Hi there,
My game has been out for a few months on Android & iOS, and things go mostly smoothly, but some players are reporting that part of their save has been missing.
I insist on this point: only part of it has been missing, which is what has been driving me crazy for the past week because despite all my investigation, I have no clue on how to reproduce it.
In my PlayerData class (which holds all the variables I’m saving), at some point you can find Boosters, Skip’its, and a bunch of other things.
It seems like from this moment on, players are loosing their Boosters and everything that follows.
Here are some relevant reviews:
So I’ve tried to “corrupt” the save file by removing part of it, but then the game won’t load because the Deserialization won’t work (which is something I plan to correct by handling the exception thrown by the way), and the “LoadPlayer()” method will just stop the execution moving forward.
I wonder then, what could cause this behavior? Could it be something wrong happening during the LoadPlayer method, which still makes the game function, but that lets all the variables in their default states? (which would mean, boosters count = 0, skip’its tickets count = 0, etc.).
I use BinaryFormatter (I’m aware now of the security flaws it contains, I didn’t when I launched the game, but I don’t think this is what is causing the issue).
Below are some relevant parts of the code.
Save file is very small (~19ko).
I’ve found only one iOS player complaining about it, so it looks like more of an Android issue. But I also have fewer players on iOS, + the devices diversity is bigger on Android so it might be just that.
Let me know if I can provide any further information !
Thanks a lot
Here are my SavePlayer and LoadPlayer methods.
public static void SavePlayer(Player player)
{
BinaryFormatter formatter = new BinaryFormatter();
string path = Application.persistentDataPath + "/player.save";
FileStream stream = new FileStream(path, FileMode.Create);
PlayerData data = new PlayerData(player);
formatter.Serialize(stream, data);
stream.Close();
PlayerPrefs.SetInt("saveExists", 1);
}
public static PlayerData LoadPlayer()
{
string path = Application.persistentDataPath + "/player.save";
if (File.Exists(path))
{
BinaryFormatter formatter = new BinaryFormatter();
FileStream stream = new FileStream(path, FileMode.Open);
PlayerData data = formatter.Deserialize(stream) as PlayerData;
stream.Close();
return data;
}
else
{
if (PlayerPrefs.GetInt("saveExists") == 1)
{
Debug.LogError("Save file was created before but couldn't be found in: " + path);
Utility.gameManager.analyticsManager.SendEvent_SaveFileNotFound();
//TODO : Add error popup for user.
}
return LoadBlankPlayerData();
}
}
public static PlayerData LoadBlankPlayerData()
{
PlayerData data = new PlayerData(); //save par défaut
return data;
}
Which I call from my GameManager :
public void SavePlayer()
{
SaveSystem.SavePlayer(player);
isFreshSave = false;
}
public void LoadPlayer()
{
PlayerData data = SaveSystem.LoadPlayer();
ApplyPlayerLoadedValues(data);
}
public void ApplyPlayerLoadedValues(PlayerData data)
{
highestLevelUnlocked = data.highestLevelUnlocked;
scoreManager.highestScore = data.highestScore;
settingsManager.setting_SFX = data.setting_SFX;
settingsManager.setting_MUSIC = data.setting_MUSIC;
settingsManager.setting_VIBRATE = data.setting_VIBRATE;
settingsManager.setting_LEFTHANDED = data.setting_LEFTHANDED;
settingsManager.setting_LANGUAGE = (Languages)System.Enum.Parse(typeof(Languages), data.setting_LANGUAGE);
settingsManager.InitAudioChannels();
localizationManager.UpdateLanguage(settingsManager.setting_LANGUAGE);
isFreshSave = data.isFreshSave;
playerRank = data.playerRank;
playerCurrencyAmount = data.playerCurrencyAmount;
if (data.skillsEquippedDictionary != null)
skillsManager.skillsEquippedDictionary = new Dictionary<string, int>(data.skillsEquippedDictionary);
else
skillsManager.skillsEquippedDictionary = new Dictionary<string, int>(skillsManager.skillsEquippedDictionary_BLANK);
if(data.skillsLevelsDictionary != null)
skillsManager.skillsLevelsDictionary = new Dictionary<string, int>(data.skillsLevelsDictionary);
else
skillsManager.skillsLevelsDictionary = new Dictionary<string, int>(skillsManager.skillsLevelsDictionary_BLANK);
if (data.levelsPerfectlyDrawn != null)
{
int minCount = Mathf.Min(data.levelsPerfectlyDrawn.Count, levelsManager.levelsPerfectlyDrawn.Count);
for (int i = 0; i < minCount; i++)
{
levelsManager.levelsPerfectlyDrawn[i] = data.levelsPerfectlyDrawn[i];
}
}
else
levelsManager.levelsPerfectlyDrawn = new List<int>(levelsManager.levelsPerfectlyDrawn_BLANK);
if (data.levelsDangerSurvived != null)
{
int minCount = Mathf.Min(data.levelsDangerSurvived.Count, levelsManager.levelsDangerSurvived.Count);
for (int i = 0; i < minCount; i++)
{
levelsManager.levelsDangerSurvived[i] = data.levelsDangerSurvived[i];
}
}
else
levelsManager.levelsDangerSurvived = new List<int>(levelsManager.levelsDangerSurvived_BLANK);
foreach (var kvp in skillsManager.skillsEquippedDictionary)
{
skillsManager.skillsDictionary[kvp.Key].isEquipped = kvp.Value;
}
foreach (var kvp in skillsManager.skillsLevelsDictionary)
{
skillsManager.skillsDictionary[kvp.Key].skillLevel = kvp.Value;
}
//skins
Dictionary<string, int> tempSkinsDico = new Dictionary<string, int>();
foreach (var kvp in Utility.customizationManager.playerSkinsEquipped)
{
if (data.playerSkinsEquipped.ContainsKey(kvp.Key))
{
tempSkinsDico.Add(kvp.Key, data.playerSkinsEquipped[kvp.Key]);
}
else
{
tempSkinsDico.Add(kvp.Key, 0);
}
if (tempSkinsDico[kvp.Key] == 0 && (Utility.customizationManager.skinButtons_Dico[kvp.Key].skinUnlockType == SkinUnlockType.crowns && Utility.gameManager.levelsManager.GetTotalNumberOfStarsAcquired() >= Utility.customizationManager.skinButtons_Dico[kvp.Key].crownsCount))
tempSkinsDico[kvp.Key] = 1;
}
Utility.customizationManager.playerSkinsEquipped = new Dictionary<string, int>(tempSkinsDico);
tempSkinsDico.Clear();
foreach (var kvp in Utility.customizationManager.trailSkinsEquipped)
{
if (data.trailSkinsEquipped.ContainsKey(kvp.Key))
{
tempSkinsDico.Add(kvp.Key, data.trailSkinsEquipped[kvp.Key]);
}
else
{
tempSkinsDico.Add(kvp.Key, 0);
}
if (tempSkinsDico[kvp.Key] == 0 && (Utility.customizationManager.skinButtons_Dico[kvp.Key].skinUnlockType == SkinUnlockType.crowns && Utility.gameManager.levelsManager.GetTotalNumberOfStarsAcquired() >= Utility.customizationManager.skinButtons_Dico[kvp.Key].crownsCount))
tempSkinsDico[kvp.Key] = 1;
}
Utility.customizationManager.trailSkinsEquipped = new Dictionary<string, int>(tempSkinsDico);
Utility.customizationManager.playerLastSkinUnlockedIndex = data.playerLastSkinUnlockedIndex;
Utility.customizationManager.trailLastSkinUnlockedIndex = data.trailLastSkinUnlockedIndex;
Utility.customizationManager.EquipCustomization();
//===CHALLENGES
if(data.activeChallenges != null)
{
challengeManager.activeChallengesExpirationDate = data.activeChallengesExpirationDate;
challengeManager.activeChallenges = data.activeChallenges;
challengeManager.numberOfChestsOpened = data.numberOfChestsOpened;
challengeManager.hasChestBeenClaimed = data.hasChestBeenClaimed;
challengeManager.numberOfActiveChallengesGroupsGenerated = data.numberOfActiveChallengesGroupsGenerated;
if ((challengeManager.activeChallengesExpirationDate - System.DateTime.Now).Seconds <= 0)
{
if(highestLevelUnlocked >= levelsManager.challengesLevel)
{
challengeManager.isSkipPossible = true;
challengeManager.GenerateActiveChallenges();
}
}
else
{
challengeManager.InitChallengesDictionary();
challengeManager.StartChallengesTimer();
}
for (int i = 0; i < challengeManager.activeChallenges.Count; i++)
{
if(challengeManager.activeChallenges[i].description_LocID == null)
{
challengeManager.activeChallenges[i].description_LocID = challengeManager.challengesTypeDico[challengeManager.activeChallenges[i].challengeType].description_LocID;
}
}
}
else //challenges = null
{
if(highestLevelUnlocked >= levelsManager.challengesLevel)
{
challengeManager.GenerateActiveChallenges();
}
}
challengeManager.isSkipPossible = data.isSkipPossible;
//===GAME LAUNCHES
numberOfGameLaunches = data.numberOfGameLaunches;
//===REVIEW REQUESTS
settingsManager.hasAcceptedReviewRequest = data.hasAcceptedReviewRequest;
//customization ad unlock
customizationManager.playerSkin_currentAdWatch = data.playerSkin_currentAdWatch;
customizationManager.trailSkin_currentAdWatch = data.trailSkin_currentAdWatch;
Utility.gameManager.rythmManager.rythmRank = data.rythmRank;
Utility.gameManager.rythmManager.rythmContestRefreshDate = data.rythmContestRefreshDate;
Utility.gameManager.rythmManager.dualTrackSelected = data.dualTrackSelected;
Utility.gameManager.rythmManager.currentNumberOfRythmContestTriesDone = data.currentNumberOfRythmContestTriesDone;
foreach (var dualTrack in Utility.gameManager.rythmManager.dualTracks)
{
if (data.dualTracksUnlockedDico != null && data.dualTracksUnlockedDico.ContainsKey(dualTrack.name))
{
Utility.gameManager.rythmManager.dualTracksUnlockedDico[dualTrack.name] = data.dualTracksUnlockedDico[dualTrack.name];
Utility.gameManager.rythmManager.dualTracksSeenDico[dualTrack.name] = data.dualTracksSeenDico[dualTrack.name];
Utility.gameManager.rythmManager.dualTracksPlayedCountDico[dualTrack.name] = data.dualTracksPlayedCountDico[dualTrack.name];
Utility.gameManager.rythmManager.dualTracksScoresDico[dualTrack.name] = data.dualTracksScoresDico[dualTrack.name];
}
else
{
Utility.gameManager.rythmManager.dualTracksUnlockedDico[dualTrack.name] = 0;
Utility.gameManager.rythmManager.dualTracksSeenDico[dualTrack.name] = 0;
Utility.gameManager.rythmManager.dualTracksPlayedCountDico[dualTrack.name] = 0;
Utility.gameManager.rythmManager.dualTracksScoresDico[dualTrack.name] = 0;
}
if (dualTrack.unlockLevel == 0)
Utility.gameManager.rythmManager.dualTracksSeenDico[dualTrack.name] = 1; //on considère que les tracks de départ n'ont pas de "new"
}
//===CHEST ROOM
chestRoomManager.chestRoomKeysCount = data.chestRoomKeysCount;
chestRoomManager.chestRoomCompletedCount = data.chestRoomCompletedCount;
//=== BOOSTERS
boosterManager.boosterCount_Health = data.boosterCount_Health;
boosterManager.boosterCount_Cooldown = data.boosterCount_Cooldown;
boosterManager.boosterCount_Coins = data.boosterCount_Coins;
boosterManager.totalBoostersAcquiredCount = data.totalBoostersAcquiredCount;
boosterManager.skipitOrBoosterBonusEnemiesSpawnedCount = data.skipitOrBoosterBonusEnemiesSpawnedCount;
//=== SHOP
shopManager.skipItsCounts = data.skipItsCounts;
shopManager.numberOfRuneChestOpened = data.numberOfRuneChestOpened;
shopManager.runeChestRefreshDate = data.runeChestRefreshDate;
//=== ADS
adsManager.interstitialsEnabled = data.interstitialsEnabled;
adsManager.showBannerInMenus = data.showBannerInMenus;
levelsManager.levelCompletion.noThanksPressedCount = data.noThanksPressedCount;
adsManager.hasSeenSkipitsExplanationBubble = data.hasSeenSkipitsExplanationBubble;
//=== TRAINING
foreach (var shape in Utility.gameManager.trainingManager.shapesOrderedByDifficulty)
{
if (data.shapesAlreadyDiscovered != null && data.shapesAlreadyDiscovered.ContainsKey(shape.name))
{
Utility.gameManager.trainingManager.shapesAlreadyDiscovered[shape.name] = data.shapesAlreadyDiscovered[shape.name];
Utility.gameManager.trainingManager.shapesTrainingCompletion[shape.name] = data.shapesTrainingCompletion[shape.name];
Utility.gameManager.trainingManager.shapesDrawingContestScore[shape.name] = data.shapesDrawingContestScore[shape.name];
}
else
{
Utility.gameManager.trainingManager.shapesAlreadyDiscovered[shape.name] = 0;
Utility.gameManager.trainingManager.shapesTrainingCompletion[shape.name] = 0;
Utility.gameManager.trainingManager.shapesDrawingContestScore[shape.name] = 0;
}
}
trainingManager.drawingContestPlayPressedCount = data.drawingContestPlayPressedCount;
//Update of the currency module
currencyManager.InitCurrencyManager();
//Update UI according to ratio and whether there are banners or not.
touchManager.AdaptUIAccordingToScreenRatio();
}
Here is my class PlayerData, holding all the saved variables
[System.Serializable]
public class PlayerData
{
public int highestLevelUnlocked;
public int highestScore;
public bool setting_SFX;
public MusicSetting setting_MUSIC;
public bool setting_VIBRATE;
public bool setting_LEFTHANDED;
public string setting_LANGUAGE;
public bool isFreshSave;
//New since first release
public bool hqGraphics;
public int playerRank;
public int playerCurrencyAmount;
public Dictionary<string, int> skillsEquippedDictionary;
public Dictionary<string, int> skillsLevelsDictionary;
public List<int> levelsPerfectlyDrawn;
public List<int> levelsDangerSurvived;
//skins
public Dictionary<string, int> playerSkinsEquipped; //0 = LOCKED, 1 = UNLOCKED & UNEQUIPPED, 2 = EQUIPPED
public Dictionary<string, int> trailSkinsEquipped; //0 = LOCKED, 1 = UNLOCKED & UNEQUIPPED, 2 = EQUIPPED
public int playerLastSkinUnlockedIndex;
public int trailLastSkinUnlockedIndex;
//save version
public int latestSaveVersion; //Used to know which save version the player has. According to this, if player has an old save, ApplyLoadedValues can force some values in variables.
//challenges
public List<Challenge> activeChallenges;
public int numberOfChestsOpened;
public bool hasChestBeenClaimed;
public int numberOfActiveChallengesGroupsGenerated;
public System.DateTime activeChallengesExpirationDate;
public bool isSkipPossible;
//game launches
public int numberOfGameLaunches;
//review request
public bool hasAcceptedReviewRequest;
//customization ad unlock
public int playerSkin_currentAdWatch;
public int trailSkin_currentAdWatch;
//=== Rythm Contest
public int rythmRank = 0;
public System.DateTime rythmContestRefreshDate;
public string dualTrackSelected;
public Dictionary<string, int> dualTracksUnlockedDico;
public Dictionary<string, int> dualTracksSeenDico;
public Dictionary<string, int> dualTracksPlayedCountDico;
public Dictionary<string, int> dualTracksScoresDico;
public int currentNumberOfRythmContestTriesDone;
//=== CHEST ROOM
public int chestRoomKeysCount;
public int chestRoomCompletedCount;
//=== BOOSTERS
public int boosterCount_Health;
public int boosterCount_Cooldown;
public int boosterCount_Coins;
public int totalBoostersAcquiredCount;
public int skipitOrBoosterBonusEnemiesSpawnedCount;
//=== SHOP
public int skipItsCounts;
public int numberOfRuneChestOpened;
public System.DateTime runeChestRefreshDate;
//=== ADS
public bool showBannerInMenus;
public bool interstitialsEnabled;
public int noThanksPressedCount;
public bool hasSeenSkipitsExplanationBubble;
//=== TRAINING
public Dictionary<string, int> shapesAlreadyDiscovered;
public Dictionary<string, int> shapesTrainingCompletion;
public Dictionary<string, int> shapesDrawingContestScore;
public int drawingContestPlayPressedCount;
public PlayerData(Player player)
{
highestLevelUnlocked = player.gameManager.highestLevelUnlocked;
highestScore = player.gameManager.scoreManager.highestScore;
setting_SFX = player.gameManager.settingsManager.setting_SFX;
setting_MUSIC = player.gameManager.settingsManager.setting_MUSIC;
setting_VIBRATE = player.gameManager.settingsManager.setting_VIBRATE;
setting_LEFTHANDED = player.gameManager.settingsManager.setting_LEFTHANDED;
setting_LANGUAGE = player.gameManager.settingsManager.setting_LANGUAGE.ToString();
isFreshSave = false;
//New since first release
hqGraphics = player.gameManager.settingsManager.hqGraphics;
playerRank = player.gameManager.playerRank;
playerCurrencyAmount = player.gameManager.playerCurrencyAmount;
skillsEquippedDictionary = player.gameManager.skillsManager.skillsEquippedDictionary;
skillsLevelsDictionary = player.gameManager.skillsManager.skillsLevelsDictionary;
levelsPerfectlyDrawn = player.gameManager.levelsManager.levelsPerfectlyDrawn;
levelsDangerSurvived = player.gameManager.levelsManager.levelsDangerSurvived;
playerSkinsEquipped = Utility.customizationManager.playerSkinsEquipped;
trailSkinsEquipped = Utility.customizationManager.trailSkinsEquipped;
playerLastSkinUnlockedIndex = Utility.customizationManager.playerLastSkinUnlockedIndex;
trailLastSkinUnlockedIndex = Utility.customizationManager.trailLastSkinUnlockedIndex;
latestSaveVersion = Utility.gameManager.latestSaveVersion;
//challenges
activeChallenges = player.gameManager.challengeManager.activeChallenges;
numberOfChestsOpened = player.gameManager.challengeManager.numberOfChestsOpened;
hasChestBeenClaimed = player.gameManager.challengeManager.hasChestBeenClaimed;
numberOfActiveChallengesGroupsGenerated = player.gameManager.challengeManager.numberOfActiveChallengesGroupsGenerated;
activeChallengesExpirationDate = player.gameManager.challengeManager.activeChallengesExpirationDate;
isSkipPossible = player.gameManager.challengeManager.isSkipPossible;
//game launches
numberOfGameLaunches = player.gameManager.numberOfGameLaunches;
//review request
hasAcceptedReviewRequest = player.gameManager.settingsManager.hasAcceptedReviewRequest;
//customization ad unlock
playerSkin_currentAdWatch = player.gameManager.customizationManager.playerSkin_currentAdWatch;
trailSkin_currentAdWatch = player.gameManager.customizationManager.trailSkin_currentAdWatch;
//=== Rythm Contest
rythmRank = player.gameManager.rythmManager.rythmRank;
rythmContestRefreshDate = player.gameManager.rythmManager.rythmContestRefreshDate;
dualTrackSelected = player.gameManager.rythmManager.dualTrackSelected;
dualTracksUnlockedDico = player.gameManager.rythmManager.dualTracksUnlockedDico;
dualTracksSeenDico = player.gameManager.rythmManager.dualTracksSeenDico;
dualTracksPlayedCountDico = player.gameManager.rythmManager.dualTracksPlayedCountDico;
dualTracksScoresDico = player.gameManager.rythmManager.dualTracksScoresDico;
currentNumberOfRythmContestTriesDone = player.gameManager.rythmManager.currentNumberOfRythmContestTriesDone;
//=== Chest Room
chestRoomKeysCount = player.gameManager.chestRoomManager.chestRoomKeysCount;
chestRoomCompletedCount = player.gameManager.chestRoomManager.chestRoomCompletedCount;
//=== BOOSTERS
boosterCount_Health = player.gameManager.boosterManager.boosterCount_Health;
boosterCount_Cooldown = player.gameManager.boosterManager.boosterCount_Cooldown;
boosterCount_Coins = player.gameManager.boosterManager.boosterCount_Coins;
totalBoostersAcquiredCount = player.gameManager.boosterManager.totalBoostersAcquiredCount;
skipitOrBoosterBonusEnemiesSpawnedCount = player.gameManager.boosterManager.skipitOrBoosterBonusEnemiesSpawnedCount;
//=== SHOP
skipItsCounts = player.gameManager.shopManager.skipItsCounts;
numberOfRuneChestOpened = player.gameManager.shopManager.numberOfRuneChestOpened;
runeChestRefreshDate = player.gameManager.shopManager.runeChestRefreshDate;
//=== ADS
showBannerInMenus = player.gameManager.adsManager.showBannerInMenus;
interstitialsEnabled = player.gameManager.adsManager.interstitialsEnabled;
noThanksPressedCount = player.gameManager.levelsManager.levelCompletion.noThanksPressedCount;
hasSeenSkipitsExplanationBubble = player.gameManager.adsManager.hasSeenSkipitsExplanationBubble;
//=== TRAINING
shapesAlreadyDiscovered = player.gameManager.trainingManager.shapesAlreadyDiscovered;
shapesTrainingCompletion = player.gameManager.trainingManager.shapesTrainingCompletion;
shapesDrawingContestScore = player.gameManager.trainingManager.shapesDrawingContestScore;
drawingContestPlayPressedCount = player.gameManager.trainingManager.drawingContestPlayPressedCount;
}
public PlayerData()
{
highestLevelUnlocked = 0;
highestScore = 0;
setting_SFX = true;
setting_MUSIC = MusicSetting.On;
setting_VIBRATE = true;
setting_LEFTHANDED = false;
setting_LANGUAGE = GetSystemLanguage().ToString();
isFreshSave = true;
//New since first release
if (SystemInfo.systemMemorySize > 3000)
hqGraphics = true;
else
hqGraphics = false;
playerRank = 0;
playerCurrencyAmount = 0;
skillsEquippedDictionary = Utility.gameManager.skillsManager.skillsEquippedDictionary_BLANK;
skillsLevelsDictionary = Utility.gameManager.skillsManager.skillsLevelsDictionary_BLANK;
levelsPerfectlyDrawn = Utility.gameManager.levelsManager.levelsPerfectlyDrawn_BLANK;
levelsDangerSurvived = Utility.gameManager.levelsManager.levelsDangerSurvived_BLANK;
playerSkinsEquipped = Utility.customizationManager.playerSkinsEquipped_BLANK;
trailSkinsEquipped = Utility.customizationManager.trailSkinsEquipped_BLANK;
playerLastSkinUnlockedIndex = 0;
trailLastSkinUnlockedIndex = 0;
latestSaveVersion = Utility.gameManager.latestSaveVersion;
//challenges
activeChallenges = null;
numberOfChestsOpened = 0;
hasChestBeenClaimed = false;
numberOfActiveChallengesGroupsGenerated = 0;
activeChallengesExpirationDate = System.DateTime.Now.AddMinutes(-1);
isSkipPossible = true;
//game launches
numberOfGameLaunches = 0;
//review request
hasAcceptedReviewRequest = false;
//customization ad unlock
playerSkin_currentAdWatch = 0;
trailSkin_currentAdWatch = 0;
//=== Rythm Contest
rythmRank = -1;
rythmContestRefreshDate = System.DateTime.Now.AddMinutes(-1);
dualTrackSelected = null;
dualTracksUnlockedDico = Utility.gameManager.rythmManager.dualTracksUnlockedDico_BLANK;
dualTracksSeenDico = Utility.gameManager.rythmManager.dualTracksSeenDico_BLANK;
dualTracksPlayedCountDico = Utility.gameManager.rythmManager.dualTracksPlayedCountDico_BLANK;
dualTracksScoresDico = Utility.gameManager.rythmManager.dualTracksScoresDico_BLANK;
currentNumberOfRythmContestTriesDone = 0;
//=== Chest Room
chestRoomKeysCount = 0;
chestRoomCompletedCount = 0;
//=== BOOSTERS
boosterCount_Health = 0;
boosterCount_Cooldown = 0;
boosterCount_Coins = 0;
totalBoostersAcquiredCount = 0;
skipitOrBoosterBonusEnemiesSpawnedCount = 0;
//=== SHOP
skipItsCounts = 0;
numberOfRuneChestOpened = 0;
runeChestRefreshDate = System.DateTime.Now.AddMinutes(-1);
//=== ADS
showBannerInMenus = true;
interstitialsEnabled = true;
noThanksPressedCount = 0;
hasSeenSkipitsExplanationBubble = false;
//=== TRAINING
shapesAlreadyDiscovered = Utility.gameManager.trainingManager.shapesAlreadyDiscovered_BLANK;
shapesTrainingCompletion = Utility.gameManager.trainingManager.shapesTrainingCompletion_BLANK;
shapesDrawingContestScore = Utility.gameManager.trainingManager.shapesDrawingContestScore_BLANK;
drawingContestPlayPressedCount = 0;
}
public Languages GetSystemLanguage()
{
return Languages.english;
}
}
And finally my Challenges class (which is, I suspect, is where things start to go wrong … please note that players don’t mention losing their challenges because it’s a daily thing and it would be very easy to miss it if it was just reset) :
[System.Serializable]
public class Challenge
{
public ChallengeType challengeType;
public int minimumLevelUnlocked;
public int maximumLevelOffset;
//[HideInInspector]
public int maximumLevelUnlocked;
public string description;
public string description_LocID;
public int requiredAmount;
public int currentAmount;
public int reward;
public bool hasRewardBeenClaimed;
public int difficulty; // 0, 1, 2
public Challenge(Challenge challenge)
{
challengeType = challenge.challengeType;
minimumLevelUnlocked = challenge.minimumLevelUnlocked;
description = challenge.description;
description_LocID = challenge.description_LocID;
requiredAmount = challenge.requiredAmount;
currentAmount = challenge.currentAmount;
reward = challenge.reward;
hasRewardBeenClaimed = challenge.hasRewardBeenClaimed;
difficulty = challenge.difficulty;
}
public bool IsChallengeCompleted()
{
return (currentAmount >= requiredAmount);
}
public bool IsChallengeHighlighted()
{
return (currentAmount >= requiredAmount && !hasRewardBeenClaimed);
}
public bool IsChallengeCompletedAndClaimed()
{
return (currentAmount >= requiredAmount && hasRewardBeenClaimed);
}
}