Exemplo n.º 1
0
        public void Run()
        {
            GameDataLists gameDataLists = new GameDataLists();

            gameDataLists.Load(@"..\..\..\datasource\export\exd-all\");

            bool result = true;

            result = result && ValidateCardIds(gameDataLists);
            result = result && ValidateNpcIds(gameDataLists);
            result = result && gameDataLists.Link();
            if (result)
            {
                var mapRuleByCodeName     = BuildRuleNameMap();
                var mapCardTypeByCodeName = BuildCardTypes();

                result = result && ExportRuleNames(gameDataLists.rules, mapRuleByCodeName);
                result = result && ExportCardTypes(gameDataLists.cardTypes, mapCardTypeByCodeName);
                result = result && UpdateCards(gameDataLists, mapCardTypeByCodeName);
                result = result && UpdateNpcs(gameDataLists, mapRuleByCodeName);
                result = result && UpdateTournaments(gameDataLists, mapRuleByCodeName);
            }

            if (result)
            {
                LocalizationDB.Get().Save();
                TriadCardDB.Get().Save();
                TriadNpcDB.Get().Save();
                TriadTournamentDB.Get().Save();
            }

            Logger.WriteLine(result ? "Done." : "Aborted");
        }
Exemplo n.º 2
0
        public static void RunSolverStressTest()
        {
            int numIterations = 1000 * 1000;

            Logger.WriteLine("Solver testing start, numIterations:" + numIterations);

            Stopwatch timer = new Stopwatch();

            timer.Start();

            TriadDeck testDeck   = new TriadDeck(new int[] { 10, 20, 30, 40, 50 });
            TriadNpc  testNpc    = TriadNpcDB.Get().Find("Garima");
            Random    randStream = new Random();

            TriadGameSession solver = new TriadGameSession();

            solver.modifiers.AddRange(testNpc.Rules);
            solver.UpdateSpecialRules();

            for (int Idx = 0; Idx < numIterations; Idx++)
            {
                TriadGameData testData    = solver.StartGame(testDeck, testNpc.Deck, ETriadGameState.InProgressBlue);
                Random        sessionRand = new Random(randStream.Next());
                solver.SolverPlayRandomGame(testData, sessionRand);
            }

            timer.Stop();
            Logger.WriteLine("Solver testing finished, time taken:" + timer.ElapsedMilliseconds + "ms");

            if (Debugger.IsAttached)
            {
                Debugger.Break();
            }
        }
Exemplo n.º 3
0
        public static void RunSolverStressTest()
        {
#if DEBUG
            int numIterations = 1000 * 100;
            Logger.WriteLine("Solver testing start, numIterations:" + numIterations);
            perfStats = new SolverPerfStats();

            Stopwatch timer = new Stopwatch();
            timer.Start();

            TriadDeck testDeck   = new TriadDeck(new int[] { 10, 20, 30, 40, 50 });
            TriadNpc  testNpc    = TriadNpcDB.Get().Find("Garima");
            Random    randStream = new Random();

            TriadGameSession solver = new TriadGameSession();
            solver.modifiers.AddRange(testNpc.Rules);
            solver.UpdateSpecialRules();

            for (int Idx = 0; Idx < numIterations; Idx++)
            {
                TriadGameData testData    = solver.StartGame(testDeck, testNpc.Deck, ETriadGameState.InProgressBlue);
                Random        sessionRand = new Random(randStream.Next());
                solver.SolverPlayRandomGame(testData, sessionRand);
            }

            timer.Stop();
            Logger.WriteLine("Solver testing finished, time taken:" + timer.ElapsedMilliseconds + "ms");

            float TicksToMs = 1000.0f / Stopwatch.Frequency;
            Logger.WriteLine(">> PlayRandomTurn.SelectSpot: " + (perfStats.PlayRandomTurnSelectSpot * TicksToMs) + "ms");
            Logger.WriteLine(">> PlayRandomTurn.SelectCard: " + (perfStats.PlayRandomTurnSelectCard * TicksToMs) + "ms");
            Logger.WriteLine(">> PlayRandomTurn.PlaceCard: " + (perfStats.PlayRandomTurnPlaceCard * TicksToMs) + "ms");
            Logger.WriteLine("  >> PlaceCard.OnPlaced: " + (perfStats.PlaceCardOnPlaced * TicksToMs) + "ms");
            Logger.WriteLine("  >> PlaceCard.OnPlacedMods: " + (perfStats.PlaceCardOnPlacedMods * TicksToMs) + "ms");
            Logger.WriteLine("  >> PlaceCard.Captures: " + (perfStats.PlaceCardCaptures * TicksToMs) + "ms");
            Logger.WriteLine("  >> PlaceCard.CapturesCombo: " + (perfStats.PlaceCardCapturesCombo * TicksToMs) + "ms");
            Logger.WriteLine("  >> PlaceCard.PostCaptures: " + (perfStats.PlaceCardPostCaptures * TicksToMs) + "ms");
            Logger.WriteLine("  >> PlaceCard.AllPlaced: " + (perfStats.PlaceCardAllPlaced * TicksToMs) + "ms");
#endif // DEBUG
        }
Exemplo n.º 4
0
        private bool UpdateNpcs(GameDataLists gameDataLists, Dictionary <string, TriadGameModifier> mapRuleNames)
        {
            Logger.WriteLine("Updating npc list...");

            TriadCardDB cardDB       = TriadCardDB.Get();
            TriadNpcDB  npcDB        = TriadNpcDB.Get();
            var         validDeckIds = new List <string>();

            foreach (var npcData in gameDataLists.npcs)
            {
                if (npcData.LinkedNpcId == null)
                {
                    continue;
                }

                if (npcData.LinkedNpcId.LinkedName == null || npcData.LinkedNpcId.LinkedLocation == null)
                {
                    continue;
                }

                TriadDeck npcDataDeck = new TriadDeck();
                foreach (var cardData in npcData.LinkedCardsFixed)
                {
                    npcDataDeck.knownCards.Add(cardDB.cards[cardData.Id]);
                }
                foreach (var cardData in npcData.LinkedCardsVariable)
                {
                    npcDataDeck.unknownCardPool.Add(cardDB.cards[cardData.Id]);
                }
                npcDataDeck.UpdateDeckId();
                validDeckIds.Add(npcDataDeck.deckId);

                // mistakes were made...
                TriadNpc npcOb = npcDB.FindByDeckId(npcDataDeck.deckId);
                int      npcId = (npcOb == null) ? npcDB.npcs.Count : npcOb.Id;

                if (npcOb != null)
                {
                    // ensure decks are the same
                    if (!npcOb.Deck.Equals(npcDataDeck))
                    {
                        Logger.WriteLine("FAILED npc update, id:{0} name:{1} is not matching cards!", npcId, npcData.LinkedNpcId.LinkedName.Name.GetCodeName());
                        return(false);
                    }
                }
                else
                {
                    while (npcDB.npcs.Count <= npcId)
                    {
                        npcDB.npcs.Add(null);
                    }

                    var listMods = new List <TriadGameModifier>();
                    foreach (var ruleData in npcData.LinkedRules)
                    {
                        listMods.Add(mapRuleNames[ruleData.Name.GetCodeName()]);
                    }

                    var listRewards = new List <TriadCard>();
                    foreach (var rewardData in npcData.LinkedRewards)
                    {
                        listRewards.Add(cardDB.cards[rewardData.LinkedCard.Id]);
                    }

                    npcOb = new TriadNpc(npcId, listMods, listRewards, npcDataDeck);

                    Logger.WriteLine(">> adding new npc: " + npcOb.ToString());
                    npcDB.npcs[npcId] = npcOb;
                }

                var linkedLoc = npcData.LinkedNpcId.LinkedLocation;
                linkedLoc.LinkedMap.GetCoords(linkedLoc.ScaledPosX, linkedLoc.ScaledPosZ, out npcOb.LocationX, out npcOb.LocationY);

                npcOb.Name.Text        = npcData.LinkedNpcId.LinkedName.Name.Text;
                npcOb.LocationMap.Text = npcData.LinkedNpcId.LinkedLocation.LinkedMap.LinkedPlace.Name.Text;
            }

            for (int idx = 0; idx < npcDB.npcs.Count; idx++)
            {
                TriadNpc npcOb = npcDB.npcs[idx];
                if (npcOb != null)
                {
                    if (!validDeckIds.Contains(npcOb.Deck.deckId))
                    {
                        Logger.WriteLine(">> removing npc: " + npcOb.ToString() + ", deck:" + npcOb.Deck.ToString());
                        npcDB.npcs[idx] = null;
                        continue;
                    }

                    if (npcOb.Id != idx)
                    {
                        Logger.WriteLine("FAILED npc update, index mismatch for npc[{0}].Id:{1}, Name:{2}", idx, npcOb.Id, npcOb.Name.GetCodeName());
                        return(false);
                    }
                }
            }

            return(true);
        }
Exemplo n.º 5
0
        private bool UpdateNpcs(Dictionary <int, TriadNpc> npcMap)
        {
            TriadNpcDB npcDB      = TriadNpcDB.Get();
            int        newNpcId   = npcDB.npcs[npcDB.npcs.Count - 1].Id + 1;
            bool       hasChanges = false;

            foreach (KeyValuePair <int, TriadNpc> kvp in npcMap)
            {
                if (kvp.Key >= newNpcId)
                {
                    kvp.Value.Id = newNpcId;
                    newNpcId++;

                    while (npcDB.npcs.Count < newNpcId)
                    {
                        npcDB.npcs.Add(null);
                    }
                    npcDB.npcs[kvp.Value.Id] = kvp.Value;

                    Logger.WriteLine("Newly added npc: " + kvp.Value.ToString());
                    hasChanges = true;
                    continue;
                }

                TriadNpc npcMatch = npcDB.Find(kvp.Value.Name);
                if (npcMatch == null)
                {
                    foreach (TriadNpc testNpc in npcDB.npcs)
                    {
                        if (testNpc != null &&
                            HasMatchingRewards(testNpc.Rewards, kvp.Value.Rewards) &&
                            HasMatchingRules(testNpc.Rules, kvp.Value.Rules) &&
                            testNpc.Deck.Equals(kvp.Value.Deck))
                        {
                            Logger.WriteLine("Npc name remap: " + testNpc.ToString() + " => " + kvp.Value.Name);
                            npcMatch   = testNpc;
                            hasChanges = true;
                            break;
                        }
                    }
                }

                if (npcMatch != null)
                {
                    kvp.Value.Id             = npcMatch.Id;
                    npcDB.npcs[kvp.Value.Id] = kvp.Value;
                }
                else
                {
                    Logger.WriteLine("Newly added npc: " + kvp.Value.ToString());

                    kvp.Value.Id = newNpcId;
                    newNpcId++;
                    hasChanges = true;

                    while (npcDB.npcs.Count < newNpcId)
                    {
                        npcDB.npcs.Add(null);
                    }
                    npcDB.npcs[kvp.Value.Id] = kvp.Value;
                }
            }

            if (hasChanges)
            {
                npcDB.Save();
            }

            return(hasChanges);
        }
Exemplo n.º 6
0
        public bool LoadFromXmlStream(Stream stream)
        {
            TriadCardDB cardDB = TriadCardDB.Get();
            TriadNpcDB  npcDB  = TriadNpcDB.Get();

            try
            {
                XmlDocument xdoc = new XmlDocument();
                xdoc.Load(stream);

                foreach (XmlNode testNode in xdoc.DocumentElement.ChildNodes)
                {
                    try
                    {
                        XmlElement testElem = (XmlElement)testNode;
                        if (testElem != null && testElem.Name == "ui")
                        {
                            int autoScanNum = int.Parse(testElem.GetAttribute("autoScan"));
                            useAutoScan = (autoScanNum == 1);
                        }
                        else if (testElem != null && testElem.Name == "cloud")
                        {
                            int useNum = int.Parse(testElem.GetAttribute("use"));
                            useCloudStorage = (useNum == 1);
                            cloudToken      = testElem.HasAttribute("token") ? testElem.GetAttribute("token") : null;
                        }
                        else if (testElem != null && testElem.Name == "card")
                        {
                            int cardId = int.Parse(testElem.GetAttribute("id"));
                            ownedCards.Add(cardDB.cards[cardId]);
                        }
                        else if (testElem != null && testElem.Name == "npc")
                        {
                            int npcId = int.Parse(testElem.GetAttribute("id"));
                            completedNpcs.Add(npcDB.npcs[npcId]);
                        }
                        else if (testElem != null && testElem.Name == "deck")
                        {
                            int      npcId = int.Parse(testElem.GetAttribute("id"));
                            TriadNpc npc   = TriadNpcDB.Get().npcs[npcId];
                            if (npc != null)
                            {
                                TriadDeck deckCards = new TriadDeck();
                                foreach (XmlAttribute attr in testElem.Attributes)
                                {
                                    if (attr.Name.StartsWith("card"))
                                    {
                                        string cardNumStr = attr.Name.Substring(4);
                                        int    cardNum    = int.Parse(cardNumStr);
                                        while (deckCards.knownCards.Count < (cardNum + 1))
                                        {
                                            deckCards.knownCards.Add(null);
                                        }

                                        int cardId = int.Parse(attr.Value);
                                        deckCards.knownCards[cardNum] = TriadCardDB.Get().cards[cardId];
                                    }
                                }

                                lastDeck.Add(npc, deckCards);
                            }
                        }
                        else
                        {
                            ImageHashData customHash = ImageHashDB.Get().LoadHashEntry(testElem);
                            if (customHash != null)
                            {
                                customHashes.Add(customHash);
                            }
                            else
                            {
                                ImagePatternDigit customDigit = ImageHashDB.Get().LoadDigitEntry(testElem);
                                if (customDigit.Value > 0)
                                {
                                    customDigits.Add(customDigit);
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Logger.WriteLine("Loading failed! Exception:" + ex);
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.WriteLine("Loading failed! Exception:" + ex);
            }

            return(ownedCards.Count > 0);
        }
Exemplo n.º 7
0
        public bool LoadFromJson(string jsonStr)
        {
            TriadCardDB cardDB = TriadCardDB.Get();
            TriadNpcDB  npcDB  = TriadNpcDB.Get();

            try
            {
                JsonParser.ObjectValue jsonOb = JsonParser.ParseJson(jsonStr);

                JsonParser.ObjectValue uiOb = (JsonParser.ObjectValue)jsonOb["ui", null];
                if (uiOb != null)
                {
                    useAutoScan = (JsonParser.BoolValue)uiOb["autoScan", JsonParser.BoolValue.Empty];
                }

                JsonParser.ObjectValue cloudOb = (JsonParser.ObjectValue)jsonOb["cloud", null];
                if (cloudOb != null)
                {
                    useCloudStorage = (JsonParser.BoolValue)cloudOb["use", JsonParser.BoolValue.Empty];
                    cloudToken      = (JsonParser.StringValue)cloudOb["token", null];
                }

                JsonParser.ArrayValue cardsArr = (JsonParser.ArrayValue)jsonOb["cards", JsonParser.ArrayValue.Empty];
                foreach (JsonParser.Value value in cardsArr.entries)
                {
                    int cardId = (JsonParser.IntValue)value;
                    ownedCards.Add(cardDB.cards[cardId]);
                }

                JsonParser.ArrayValue npcsArr = (JsonParser.ArrayValue)jsonOb["npcs", JsonParser.ArrayValue.Empty];
                foreach (JsonParser.Value value in npcsArr.entries)
                {
                    int npcId = (JsonParser.IntValue)value;
                    completedNpcs.Add(npcDB.npcs[npcId]);
                }

                JsonParser.ArrayValue decksArr = (JsonParser.ArrayValue)jsonOb["decks", JsonParser.ArrayValue.Empty];
                foreach (JsonParser.Value value in decksArr.entries)
                {
                    JsonParser.ObjectValue deckOb = (JsonParser.ObjectValue)value;
                    int npcId = (JsonParser.IntValue)deckOb["id"];

                    TriadNpc npc = TriadNpcDB.Get().npcs[npcId];
                    if (npc != null)
                    {
                        TriadDeck deckCards = new TriadDeck();

                        cardsArr = (JsonParser.ArrayValue)deckOb["cards", JsonParser.ArrayValue.Empty];
                        foreach (JsonParser.Value cardValue in cardsArr.entries)
                        {
                            int cardId = (JsonParser.IntValue)cardValue;
                            deckCards.knownCards.Add(cardDB.cards[cardId]);
                        }

                        lastDeck.Add(npc, deckCards);
                    }
                }

                JsonParser.ArrayValue favDecksArr = (JsonParser.ArrayValue)jsonOb["favDecks", JsonParser.ArrayValue.Empty];
                foreach (JsonParser.Value value in favDecksArr.entries)
                {
                    JsonParser.ObjectValue deckOb    = (JsonParser.ObjectValue)value;
                    TriadDeckNamed         deckCards = new TriadDeckNamed();

                    cardsArr = (JsonParser.ArrayValue)deckOb["cards", JsonParser.ArrayValue.Empty];
                    foreach (JsonParser.Value cardValue in cardsArr.entries)
                    {
                        int cardId = (JsonParser.IntValue)cardValue;
                        deckCards.knownCards.Add(cardDB.cards[cardId]);
                    }

                    if (deckCards.knownCards.Count > 0)
                    {
                        deckCards.Name = deckOb["name", JsonParser.StringValue.Empty];
                        favDecks.Add(deckCards);
                    }
                }

                JsonParser.ObjectValue imageHashesOb = (JsonParser.ObjectValue)jsonOb["images", null];
                if (imageHashesOb != null)
                {
                    customHashes = ImageHashDB.Get().LoadImageHashes(imageHashesOb);
                }

                JsonParser.ArrayValue digitHashesArr = (JsonParser.ArrayValue)jsonOb["digits", null];
                if (digitHashesArr != null)
                {
                    customDigits = ImageHashDB.Get().LoadDigitHashes(digitHashesArr);
                }
            }
            catch (Exception ex)
            {
                Logger.WriteLine("Loading failed! Exception:" + ex);
            }

            return(ownedCards.Count > 0);
        }
Exemplo n.º 8
0
        public static void RunTest(string configPath, bool debugMode)
        {
            string testName = System.IO.Path.GetFileNameWithoutExtension(configPath);

            string configText = System.IO.File.ReadAllText(configPath);

            JsonParser.ObjectValue rootOb = JsonParser.ParseJson(configText);
            if (rootOb["type"] != "Screen")
            {
                return;
            }

            ScannerTriad.VerifyConfig configData = new ScannerTriad.VerifyConfig();
            configData.Load(rootOb);

            // setup npc & modifiers
            TriadNpc testNpc = TriadNpcDB.Get().Find(configData.npc);

            if (testNpc == null)
            {
                string exceptionMsg = string.Format("Test {0} failed! Can't find npc: {1}", testName, configData.npc);
                throw new Exception(exceptionMsg);
            }

            ScannerTriad.GameState screenGame = new ScannerTriad.GameState();
            if (mapValidationRules == null)
            {
                mapValidationRules = new Dictionary <string, TriadGameModifier>();
                foreach (TriadGameModifier mod in ImageHashDB.Get().modObjects)
                {
                    mapValidationRules.Add(mod.GetCodeName(), mod);
                }
            }

            foreach (string modName in configData.rules)
            {
                screenGame.mods.Add(mapValidationRules[modName]);
            }

            Func <ScannerTriad.VerifyCard, TriadCard> ConvertToTriadCard = configCard =>
            {
                if (configCard.state == ScannerTriad.ECardState.None)
                {
                    return(null);
                }
                if (configCard.state == ScannerTriad.ECardState.Hidden)
                {
                    return(TriadCardDB.Get().hiddenCard);
                }

                TriadCard matchingCard = !string.IsNullOrEmpty(configCard.name) ?
                                         TriadCardDB.Get().Find(configCard.name) :
                                         TriadCardDB.Get().Find(configCard.sides[0], configCard.sides[1], configCard.sides[2], configCard.sides[3]);

                if (matchingCard == null)
                {
                    string exceptionMsg = string.Format("Test {0} failed! Can't match validation card: '{1}' [{2},{3},{4},{5}]", testName,
                                                        configCard.name, configCard.sides[0], configCard.sides[1], configCard.sides[2], configCard.sides[3]);
                    throw new Exception(exceptionMsg);
                }

                return(matchingCard);
            };

            bool needsLockedBlue = false;

            for (int idx = 0; idx < 5; idx++)
            {
                screenGame.blueDeck[idx] = ConvertToTriadCard(configData.deckBlue[idx]);
                screenGame.redDeck[idx]  = ConvertToTriadCard(configData.deckRed[idx]);

                if (configData.deckBlue[idx].state == ScannerTriad.ECardState.Locked)
                {
                    needsLockedBlue = true;
                }
            }

            if (needsLockedBlue)
            {
                for (int idx = 0; idx < 5; idx++)
                {
                    if (configData.deckBlue[idx].state == ScannerTriad.ECardState.Visible)
                    {
                        screenGame.forcedBlueCard = screenGame.blueDeck[idx];
                        break;
                    }
                }
            }

            for (int idx = 0; idx < 9; idx++)
            {
                screenGame.board[idx]      = ConvertToTriadCard(configData.board[idx]);
                screenGame.boardOwner[idx] =
                    configData.board[idx].state == ScannerTriad.ECardState.PlacedBlue ? ETriadCardOwner.Blue :
                    configData.board[idx].state == ScannerTriad.ECardState.PlacedRed ? ETriadCardOwner.Red :
                    ETriadCardOwner.Unknown;
            }

            TriadGameScreenMemory screenMemory = new TriadGameScreenMemory {
                logScan = false
            };

            screenMemory.OnNewScan(screenGame, testNpc);
            screenMemory.gameSession.SolverFindBestMove(screenMemory.gameState, out int solverBoardPos, out TriadCard solverTriadCard, out TriadGameResultChance bestChance);

            if (bestChance.expectedResult == ETriadGameState.BlueLost && bestChance.winChance <= 0.0f && bestChance.drawChance <= 0.0f)
            {
                string exceptionMsg = string.Format("Test {0} failed! Can't find move!", testName);
                throw new Exception(exceptionMsg);
            }
        }
Exemplo n.º 9
0
        public bool LoadFromJson(string jsonStr)
        {
            TriadCardDB cardDB = TriadCardDB.Get();
            TriadNpcDB  npcDB  = TriadNpcDB.Get();

            ownedCards.Clear();
            completedNpcs.Clear();
            lastDeck.Clear();
            favDecks.Clear();

            try
            {
                JsonParser.ObjectValue jsonOb = JsonParser.ParseJson(jsonStr);

                JsonParser.ObjectValue uiOb = (JsonParser.ObjectValue)jsonOb["ui", null];
                if (uiOb != null)
                {
                    JsonParser.Value BoolTrue  = new JsonParser.BoolValue(true);
                    JsonParser.Value BoolFalse = new JsonParser.BoolValue(false);

                    useXInput      = (JsonParser.BoolValue)uiOb["xInput", BoolTrue];
                    alwaysOnTop    = (JsonParser.BoolValue)uiOb["onTop", BoolFalse];
                    forcedLanguage = (JsonParser.StringValue)uiOb["lang", null];

                    TryGettingFloatValue(uiOb, "fontSize", ref fontSize);
                    TryGettingFloatValue(uiOb, "markerCard", ref markerDurationCard);
                    TryGettingFloatValue(uiOb, "markerSwap", ref markerDurationSwap);
                    TryGettingFloatValue(uiOb, "markerCactpot", ref markerDurationCactpot);

                    TryGettingIntValue(uiOb, "lastNpcId", ref lastNpcId);
                    TryGettingFloatValue(uiOb, "lastWidth", ref lastWidth);
                    TryGettingFloatValue(uiOb, "lastHeight", ref lastHeight);

                    fontSize = Math.Min(Math.Max(fontSize, 10), 40);
                }

                JsonParser.ObjectValue cloudOb = (JsonParser.ObjectValue)jsonOb["cloud", null];
                if (cloudOb != null)
                {
                    useCloudStorage = (JsonParser.BoolValue)cloudOb["use", JsonParser.BoolValue.Empty];
                    cloudToken      = (JsonParser.StringValue)cloudOb["token", null];
                }

                JsonParser.ArrayValue cardsArr = (JsonParser.ArrayValue)jsonOb["cards", JsonParser.ArrayValue.Empty];
                foreach (JsonParser.Value value in cardsArr.entries)
                {
                    int cardId = (JsonParser.IntValue)value;
                    ownedCards.Add(cardDB.cards[cardId]);
                }

                JsonParser.ArrayValue npcsArr = (JsonParser.ArrayValue)jsonOb["npcs", JsonParser.ArrayValue.Empty];
                foreach (JsonParser.Value value in npcsArr.entries)
                {
                    int npcId = (JsonParser.IntValue)value;
                    completedNpcs.Add(npcDB.npcs[npcId]);
                }

                JsonParser.ArrayValue decksArr = (JsonParser.ArrayValue)jsonOb["decks", JsonParser.ArrayValue.Empty];
                foreach (JsonParser.Value value in decksArr.entries)
                {
                    JsonParser.ObjectValue deckOb = (JsonParser.ObjectValue)value;
                    int npcId = (JsonParser.IntValue)deckOb["id"];

                    TriadNpc npc = TriadNpcDB.Get().npcs[npcId];
                    if (npc != null)
                    {
                        TriadDeck deckCards = new TriadDeck();

                        cardsArr = (JsonParser.ArrayValue)deckOb["cards", JsonParser.ArrayValue.Empty];
                        foreach (JsonParser.Value cardValue in cardsArr.entries)
                        {
                            int cardId = (JsonParser.IntValue)cardValue;
                            deckCards.knownCards.Add(cardDB.cards[cardId]);
                        }

                        lastDeck.Add(npc, deckCards);
                    }
                }

                JsonParser.ArrayValue favDecksArr = (JsonParser.ArrayValue)jsonOb["favDecks", JsonParser.ArrayValue.Empty];
                foreach (JsonParser.Value value in favDecksArr.entries)
                {
                    JsonParser.ObjectValue deckOb    = (JsonParser.ObjectValue)value;
                    TriadDeckNamed         deckCards = new TriadDeckNamed();

                    cardsArr = (JsonParser.ArrayValue)deckOb["cards", JsonParser.ArrayValue.Empty];
                    foreach (JsonParser.Value cardValue in cardsArr.entries)
                    {
                        int cardId = (JsonParser.IntValue)cardValue;
                        deckCards.knownCards.Add(cardDB.cards[cardId]);
                    }

                    if (deckCards.knownCards.Count > 0)
                    {
                        deckCards.Name = deckOb["name", JsonParser.StringValue.Empty];
                        favDecks.Add(deckCards);
                    }
                }

                JsonParser.ObjectValue imageHashesOb = (JsonParser.ObjectValue)jsonOb["images", null];
                if (imageHashesOb != null)
                {
                    customHashes = ImageHashDB.Get().LoadImageHashes(imageHashesOb);
                    ImageHashDB.Get().hashes.AddRange(customHashes);
                }
            }
            catch (Exception ex)
            {
                Logger.WriteLine("Loading failed! Exception:" + ex);
            }

            Logger.WriteLine("Loaded player cards: " + ownedCards.Count + ", npcs: " + completedNpcs.Count + ", hashes: " + customHashes.Count);
            return(ownedCards.Count > 0);
        }