using UnityEngine; using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; public enum AdRewardType { GachaCoin = 1, Avatar = 2, Count, } [DataContract(Name="KeyValueOfintint")] public sealed class KeyValueOfintint { [DataMember] public int Key; [DataMember] public int Value; public KeyValueOfintint(KeyValuePair value){ Key = value.Key; Value = value.Value; } } [DataContract] public sealed class GameData { [DataMember(Name = "Data0")] public long beginPlayTime = DateTime.UtcNow.ToBinary(); [DataMember(Name = "Data1")] public string lastPlayedVersion = Const.DefaultAppVersion; [DataMember(Name = "Data2")] public long lastPlayTime = DateTime.UtcNow.ToBinary(); // 最終プレイ日時 [DataMember(Name = "Data3")] public int dailyLoginCount = 0; [DataMember(Name = "Data4")] public int adCount; [DataMember(Name = "Data5")] public int stage = 1; // [DataMember(Name = "Data6")] // public int avatarId = Const.DefaultAvatarId; [DataMember(Name = "Data7")] private int coin; public int Coin { get => coin; set => ChangeCoin(value); } // [DataMember(Name = "Data8")] // private int[] avatarIdArray; // public List avatarIdList; // [DataMember(Name = "Data9")] // public int gachaCount; // [DataMember(Name = "Data10")] // private int[] newAvatarIdArray; // public List newAvatarIdList; // [DataMember(Name = "Data11")] // public bool isRandomAvatar; // CornField [DataMember(Name = "Data12")] public List PlantLineDataList = new List(); // [DataMember(Name = "Data13")] [DataMember(Name = "Data14")] public int MachineLevel = Const.DefaultMachineLevel; [DataMember(Name = "Data15")] public List SeedlingDataList = new List(); [DataMember(Name = "Data16")] private int cornSeed; public int CornSeed { get => cornSeed; set => ChangeCornSeed(value); } // Main // 所持レシピ [DataMember(Name = "Data17")] public int[] MyRecipes; // 所持素材 [DataMember(Name = "Data18")] public List Material; // 店頭ポップコーン在庫 [DataMember(Name = "Data19")] public List ShopStock; // タンクポップコーン在庫 [DataMember(Name = "Data20")] public List StorageTanks; [DataMember(Name = "Data21")] public int Heart = 0; [DataMember(Name = "Data22")] public int[] Pans; [DataMember(Name = "Data23")] public int ViewedShopLevel; [DataMember(Name = "Data24")] public ProductRefillMode RefillMode; [DataMember(Name = "Data25")] private int[] achievedMission; public List AchievedMission; [DataMember(Name = "Data26")] public int TastingCount; // BulkOrder [DataMember(Name = "Data27")] public int OrderIconIndex; [DataMember(Name = "Data28")] public int OrderIdInProgress; [DataMember(Name = "Data29")] public int OrderTextId; [DataMember(Name = "Data30")] public int OrderConditionCount; [DataMember(Name = "Data31")] public int CancelOrderId; [DataMember(Name = "Data32")] public int AchievedOrderLevel; [DataMember(Name = "Data33")] public List CompletedProductList; [DataMember(Name = "Data34")] private int[] deliveredOrder; public List DeliveredOrder; // reward [DataMember(Name = "Data35")] public long fertilizerTime = DateTime.UtcNow.ToBinary(); [DataMember(Name = "Data36")] public long increaseCustomerTime= DateTime.UtcNow.ToBinary(); [DataMember(Name = "Data37")] public long kitchenRefreshTime= DateTime.UtcNow.ToBinary(); // tutorial [DataMember(Name = "Data38")] public bool isFirstPlay = true; // フライパン [DataMember(Name = "Data39")] public int SelectedPanId; // 遅延追加用加算枠 [DataMember(Name = "Data40")] public int WaitAddCoin; [DataMember(Name = "Data41")] public int WaitAddHeart; // mission [DataMember(Name = "Data1001")] public int TotalAddCoin { get; private set; } [DataMember(Name = "Data1002")] public int TotalSubCoin { get; private set; } [DataMember(Name = "Data1003")] public int TotalAddCornSeed { get; private set; } [DataMember(Name = "Data1004")] public int TotalCustomerCount { get; private set; } [DataMember(Name = "Data1005")] public List TotalSalesList { get; private set; } [DataMember(Name = "Data1006")] public int BulkOrderDeliveredCount; public int TotalSales => TotalSalesList.Sum(data => data.Stock); public int RareTotalSales => TotalSalesList.Where(data => data.Rarity != ProductRarity.Normal).Sum(data => data.Stock); // daily mission [DataMember(Name = "Data2001")] public long lastDailyMissionSetTime = DateTime.MinValue.ToBinary(); [DataMember(Name = "Data2002")] public int[] dailyMissionIdArray; [DataMember(Name = "Data2003")] public int[] dailyMissionAchievedIdArray; [DataMember(Name = "Data2004")] public bool dailyMissionCompleted; // [DataMember(Name = "Data2100")] [DataMember(Name = "Data2101")] public int DailyTotalAddCoin { get; private set; } [DataMember(Name = "Data2102")] public int DailyTotalSubCoin { get; private set; } [DataMember(Name = "Data2103")] public int DailyTotalAddCornSeed { get; private set; } [DataMember(Name = "Data2104")] public int DailyTotalCustomerCount { get; private set; } [DataMember(Name = "Data2105")] public List DailyTotalSalesList { get; private set; } [DataMember(Name = "Data2106")] public int DailyRecipeGetCount; [DataMember(Name = "Data2107")] public int DailyBulkOrderDeliveredCount; public int DailyTotalSales => DailyTotalSalesList.Sum(data => data.Stock); public int DailyRareTotalSales => DailyTotalSalesList.Where(data => data.Rarity != ProductRarity.Normal).Sum(data => data.Stock); private void ChangeCornSeed(int value) { if (cornSeed < value) { TotalAddCornSeed += value - cornSeed; DailyTotalAddCornSeed += value - cornSeed; } cornSeed = value; } private void ChangeCoin(int value) { // Debug.Log($"{coin} {value}"); if (coin < value) { TotalAddCoin += value - coin; DailyTotalAddCoin += value - coin; } else { TotalSubCoin += coin - value; DailyTotalSubCoin += coin - value; } coin = value; } public void AddCustomerCount(int value) { TotalCustomerCount += value; DailyTotalCustomerCount += value; } public void AddSalesCount(int id, int stock, ProductRarity rarity) { OrderConditionCount += stock; if (!(TotalSalesList.FirstOrDefault(x => x.FlavorId == id && x.Rarity == rarity) is ProductStockData stockData)) { stockData = new ProductStockData {FlavorId = id, Rarity = rarity, Stock = 0}; TotalSalesList.Add(stockData); } stockData.Stock += stock; if (!(DailyTotalSalesList.FirstOrDefault(x => x.FlavorId == id && x.Rarity == rarity) is ProductStockData dailyStockData)) { dailyStockData = new ProductStockData {FlavorId = id, Rarity = rarity, Stock = 0}; DailyTotalSalesList.Add(dailyStockData); } dailyStockData.Stock += stock; } public int GetTotalSalesByFlavor(int id) { return TotalSalesList.Where(data => data.FlavorId == id).Sum(data => data.Stock); } public int GetTotalSalesByFlavorWithRare(int id) { return TotalSalesList.Where(data => data.FlavorId == id && data.Rarity != ProductRarity.Normal).Sum(data => data.Stock); } public int GetDailyTotalSalesByFlavor(int id) { return DailyTotalSalesList.Where(data => data.FlavorId == id).Sum(data => data.Stock); } public int GetDailyTotalSalesByFlavorWithRare(int id) { return DailyTotalSalesList.Where(data => data.FlavorId == id && data.Rarity != ProductRarity.Normal).Sum(data => data.Stock); } public void ResetNormalTotalCount() { TotalAddCoin = 0; TotalSubCoin = 0; TotalAddCornSeed = 0; TotalCustomerCount = 0; TotalSalesList.Clear(); BulkOrderDeliveredCount = 0; } public void ResetDailyTotalCount() { DailyTotalAddCoin = 0; DailyTotalSubCoin = 0; DailyTotalAddCornSeed = 0; DailyTotalCustomerCount = 0; DailyTotalSalesList.Clear(); DailyRecipeGetCount = 0; DailyBulkOrderDeliveredCount = 0; } public void AddTastingCustomerCount(int value) { TastingCount += Mathf.FloorToInt(value / 2f); } public TimeSpan GetDailyMissionLimitTime() { return DateTime.FromBinary(lastDailyMissionSetTime).ToLocalTime().AddHours(Const.DailyMissionResetOffset).Date .AddDays(1).AddHours(-Const.DailyMissionResetOffset) .Subtract(DateTime.UtcNow.ToLocalTime()); } public void FinishTutorial() { isFirstPlay = false; fertilizerTime = DateTime.UtcNow.ToBinary(); increaseCustomerTime = DateTime.UtcNow.ToBinary(); kitchenRefreshTime = DateTime.UtcNow.ToBinary(); } public void MoveCoin(int value) { // 退避していたコインを加算 WaitAddCoin -= value; Coin += value; } public void MoveHeart(int value) { WaitAddHeart -= value; Heart += value; } public void MoveAllWaitValue() { Debug.Log($"WaitAddValue:{WaitAddCoin},{WaitAddHeart}"); MoveCoin(WaitAddCoin); MoveHeart(WaitAddHeart); } // public void ChangeAvatar(AvatarData avatarData){ // newAvatarIdList.Remove(avatarData.id); // } // public void AddAvatar(AvatarData avatarData){ // if(HasAvatar(avatarData)) return ; // avatarIdList.Add(avatarData.id); // newAvatarIdList.Add(avatarData.id); // } // public bool HasAvatar(AvatarData avatarData){ // return avatarIdList.Exists(id => id == avatarData.id); // } // public void SetRandom(bool isRandom, AvatarCategory category){ // } // public bool GetRandom(AvatarCategory category){ // return false; // } // [OnDeserialized] Unityだと機能しない public void OnDeserialized(){ PreDeserialize(); // if(avatarIdList.Count < 1){ // avatarIdList.Add(Const.DefaultAvatarId); // } if (MyRecipes.Length < 1) { MyRecipes = new[] {Const.DefaultRecipeId}; } if (StorageTanks.Count < 1) { StorageTanks.Add(new StorageTank { Capacity = Const.TankCapacity, FlavorId = -1, } ); } if (Pans.Length < 1) { Pans = new[] {Const.DefaultPanId}; } // 追加した要素の初期化用 // TODO Release前にまっさらにする } public void PreDeserialize(){ // avatarIdList = avatarIdArray == null ? new List() : avatarIdArray.ToList(); // newAvatarIdList = newAvatarIdArray == null ? new List() : newAvatarIdArray.ToList(); // lastAdRewardTimeList = lastAdRewardTimeArray == null ? new List() : lastAdRewardTimeArray.ToList(); MyRecipes = MyRecipes ?? new int[0]; Material = Material ?? new List(); ShopStock = ShopStock ?? new List(); StorageTanks = StorageTanks ?? new List(); Pans = Pans ?? new int[0]; TotalSalesList = TotalSalesList ?? new List(); AchievedMission = achievedMission?.ToList() ?? new List(); CompletedProductList = CompletedProductList ?? new List(); DeliveredOrder = deliveredOrder?.ToList() ?? new List(); dailyMissionIdArray = dailyMissionIdArray ?? new int[0]; dailyMissionAchievedIdArray = dailyMissionAchievedIdArray ?? new int[0]; DailyTotalSalesList = DailyTotalSalesList ?? new List(); } private Dictionary ArrayToDictionary(KeyValueOfintint[] array){ var dictionary = new Dictionary(); if(array != null && array.Length > 0){ foreach(var kv in array){ dictionary.Add(kv.Key, kv.Value); } } return dictionary; } public void PreSerialize(){ // avatarIdArray = avatarIdList.ToArray(); // newAvatarIdArray = newAvatarIdList.ToArray(); // lastAdRewardTimeArray = lastAdRewardTimeList.ToArray(); achievedMission = AchievedMission.ToArray(); deliveredOrder = DeliveredOrder.ToArray(); } private KeyValueOfintint[] DictionaryToArray(Dictionary dictionary){ var array = new KeyValueOfintint[dictionary.Count]; int index = 0; foreach(var kvp in dictionary){ array[index++] = new KeyValueOfintint(kvp); } return array; } } public static class GameDataManager { public enum LoadState { NoFile, ExistsClashed, DeserializeFailed, Old, Succeeded, } // private static readonly int DiskFullTextId = 201; // private static readonly int UnauthorizedAccessExceptionTextId = 202; private static GameData dataCache = null; public static GameData GameData { get{ return dataCache ?? (dataCache = LoadGameData()); } } public static bool IsLoaded { get{ return dataCache != null; } } public static bool ExistsGameData(){ return UsayaStorageManager.Exists(UsayaStorageFilename.Main_Data, Const.GameDataTag) || UsayaStorageManager.ExistsBackup(UsayaStorageFilename.Main_Data, Const.GameDataTag); } private static GameData LoadGameData(){ GameData data = null; var loadState = LoadState.ExistsClashed; try{ if(UsayaStorageManager.Exists(UsayaStorageFilename.Main_Data, Const.GameDataTag)){ loadState = LoadState.DeserializeFailed; data = StringToGameData(UsayaStorageManager.Load(UsayaStorageFilename.Main_Data, Const.GameDataTag)); loadState = LoadState.Succeeded; }else{ loadState = LoadState.NoFile; } }catch(Exception){ }finally{ var backup = LoadGameDataBackup(loadState); if(loadState != LoadState.Succeeded || (data.stage < backup.stage)){ data = backup; if(loadState == LoadState.Succeeded){ // AnalyticsManager.DataLoadWarning(LoadState.Old); } } } data.OnDeserialized(); if(Application.version.CompareVersion(data.lastPlayedVersion) != 0){ data.lastPlayedVersion = Application.version; } return data; } private static GameData LoadGameDataBackup(LoadState mainLoadState){ GameData result = null; var loadState = LoadState.ExistsClashed; try{ if(UsayaStorageManager.ExistsBackup(UsayaStorageFilename.Main_Data, Const.GameDataTag)){ loadState = LoadState.DeserializeFailed; result = StringToGameData(UsayaStorageManager.LoadBackup(UsayaStorageFilename.Main_Data, Const.GameDataTag)); loadState = LoadState.Succeeded; if(mainLoadState != LoadState.Succeeded){ // AnalyticsManager.DataLoadWarning(mainLoadState); } }else{ loadState = LoadState.NoFile; } }catch(Exception){ }finally{ if(mainLoadState != LoadState.Succeeded && loadState != LoadState.NoFile){ // AnalyticsManager.DataLoadWarning(mainLoadState, loadState); } if(loadState != LoadState.Succeeded){ result = CreateData(); } } return result; } private static GameData CreateData(){ var data = new GameData(); data.lastPlayedVersion = Application.version; return data; } public static bool OverWriteGameData(string dataString){ try{ dataCache = StringToGameData(dataString); return true; }catch{ return false; } } public static void SaveGameData(){ string data = GameDataToString(GameData); bool isMainSaved = false; Exception exceptionMain = null; try{ UsayaStorageManager.Save(UsayaStorageFilename.Main_Data, Const.GameDataTag, data); isMainSaved = true; }catch(Exception e){ // TODO ログサーバが出来たらExceptionを送りたい exceptionMain = e; }finally{ bool isBackupSaved = false; Exception exceptionBackup = null; try{ UsayaStorageManager.SaveBackup(UsayaStorageFilename.Main_Data, Const.GameDataTag, data); isBackupSaved = true; }catch(Exception e){ // TODO ログサーバが出来たらExceptionを送りたい exceptionBackup = e; }finally{ if(!isMainSaved && !isBackupSaved){ // AnalyticsManager.DataSaveWarning("GameData", SaveState.MainAndBackupFailed); // SaveFailedDialogManager.ShowDialog(exceptionMain, exceptionBackup); }else if(!isMainSaved){ // AnalyticsManager.DataSaveWarning("GameData", SaveState.MainFailed); }else if(!isBackupSaved){ // AnalyticsManager.DataSaveWarning("GameData", SaveState.BackupFailed); } } } } public static string GetGameDataString(){ return GameDataToString(GameData); } private static string GameDataToString(GameData data){ data.PreSerialize(); return AvoEx.AesEncryptor.Encrypt( StringExtensions.DataToXML(data) .Replace("KeyValueOfintint", "KVOii") .Replace("KeyValueOfintlong", "KVOil") .Replace("KeyValueOfintInfiniteFloat", "KVOiIF") // .Compression() ); } private static GameData StringToGameData(string str){ str = AvoEx.AesEncryptor.DecryptString(str) // .DeCompression() .Replace("KVOii", "KeyValueOfintint") .Replace("KVOil", "KeyValueOfintlong") .Replace("KVOiIF", "KeyValueOfintInfiniteFloat"); return StringExtensions.XMLToData(str); } #if UNITY_EDITOR || DEVELOPMENT_BUILD public static void ResetData(){ dataCache = CreateData(); dataCache.OnDeserialized(); } #endif }