private void UpdateGrid() { if (Recipe.numRecipes != recipeSlots.Count) { recipeSlots.Clear(); for (int i = 0; i < Recipe.numRecipes; i++) { recipeSlots.Add(new UIRecipeSlot(i)); } tileChooserGrid.Clear(); var tileUsageCounts = new Dictionary <int, int>(); int currentCount; for (int i = 0; i < Recipe.numRecipes; i++) { for (int j = 0; j < 15; j++) { if (Main.recipe[i].requiredTile[j] == -1) { break; } tileUsageCounts.TryGetValue(Main.recipe[i].requiredTile[j], out currentCount); tileUsageCounts[Main.recipe[i].requiredTile[j]] = currentCount + 1; } } // sort var sorted = tileUsageCounts.OrderBy(kvp => kvp.Value); foreach (var tileUsage in sorted) { var tileSlot = new UITileSlot(tileUsage.Key, tileUsage.Value); tileChooserGrid.Add(tileSlot); tileSlots.Add(tileSlot); } craftingTiles = tileUsageCounts.Select(x => x.Key).ToList(); RecipeBrowserUI.instance.UpdateFavoritedPanel(); } if (!RecipeBrowserUI.instance.ShowRecipeBrowser || RecipeBrowserUI.instance.CurrentPanel != RecipeBrowserUI.RecipeCatalogue) { return; } if (slowUpdateNeeded > 0) { slowUpdateNeeded--; if (slowUpdateNeeded == 0) { updateNeeded = true; } } if (!updateNeeded) { return; } updateNeeded = false; slowUpdateNeeded = 0; List <int> groups = new List <int>(); if (queryItem.item.stack > 0) { int type = queryItem.item.type; foreach (var group in RecipeGroup.recipeGroups) { if (group.Value.ValidItems.Contains(type)) { groups.Add(group.Key); } } } lootSourceGrid.Clear(); if (queryLootItem != null) { //var jsonitem = new JSONItem(queryLootItem.modItem?.mod.Name ?? "Terraria", Lang.GetItemNameValue(queryLootItem.type), queryLootItem.modItem != null ? 0 : queryLootItem.type); var jsonitem = new JSONItem(queryLootItem.modItem?.mod.Name ?? "Terraria", queryLootItem.modItem?.Name ?? Lang.GetItemNameValue(queryLootItem.type), queryLootItem.modItem != null ? 0 : queryLootItem.type); List <JSONNPC> npcsthatdropme; if (LootCache.instance.lootInfos.TryGetValue(jsonitem, out npcsthatdropme)) { foreach (var dropper in npcsthatdropme) { int id = dropper.GetID(); if (id == 0) { continue; } /*int id = dropper.id; * if (id == 0) * { * //it's a * Mod m = ModLoader.GetMod(dropper.mod); * if (m == null) continue; * id = m.NPCType(dropper.name); * }*/ NPC npc = new NPC(); npc.SetDefaults(id); var slot = new UINPCSlot(npc); //lootSourceGrid.Add(slot); lootSourceGrid._items.Add(slot); lootSourceGrid._innerList.Append(slot); } } } lootSourceGrid.UpdateOrder(); lootSourceGrid._innerList.Recalculate(); //if (SharedUI.instance.ObtainableFilter.button.selected) //{ // Main.NewText("Warning, Extended Craftable Filter will cause lag."); //} recipeGrid.Clear(); //int craftPathsCalculatedCount = 0; for (int i = 0; i < Recipe.numRecipes; i++) { //if (recipeSlots[i].craftPathsCalculated) // craftPathsCalculatedCount++; if (PassRecipeFilters(recipeSlots[i], Main.recipe[i], groups)) // all the filters //if (Main.projName[i].ToLower().IndexOf(searchFilter.Text, StringComparison.OrdinalIgnoreCase) != -1) { var box = recipeSlots[i]; // if (newestItem > 0) { Recipe recipe = Main.recipe[i]; box.recentlyDiscovered = false; if (recipe.requiredItem.Any(x => x.type == newestItem)) { box.recentlyDiscovered = true; } } recipeGrid._items.Add(box); recipeGrid._innerList.Append(box); } } //Main.NewText($"craftPathsCalculated: {craftPathsCalculatedCount}/{Recipe.numRecipes}"); recipeGrid.UpdateOrder(); recipeGrid._innerList.Recalculate(); }
internal static void Setup(Mod recipeBrowserMod) { // Save format: /* * .. hmm, vanilla item drops...do I have to recalculate each time? New Set of mods => complete refresh? * * RecipeBrowserVersion // So if we add more features we can ignore this file * [ of Mod Info * -> ModName * -> Version * -> [ of npc drop info? or item info? * --> Tuple<JSONNPC, LootItem[]> or * --> JSONNPC<T> * --> * ---> OR * --> Item Info * ---> ["NPCInfo" with droprate] * --> * -> ] * ] * * hmm, instead of mod info, just a list of npc? just a list of Item? * * <Mod, Version>[] --> For knowing which have updated....for now, any update or unaccounted mod, recalculate allll * ItemInfo[] JSONNPC and array of JSONNPC that drop it. (forget droprates for now?) * * * item to npc or npc to item.... */ string json; string filename = "LootCache.json"; string folder = Path.Combine(Main.SavePath, "Mods", "Cache"); string path = Path.Combine(folder, filename); LootCache li = new LootCache(); bool needsRecalculate = true; LootCacheManagerActive = true; List <string> modsThatNeedRecalculate = new List <string>(); if (File.Exists(path)) { using (StreamReader r = new StreamReader(path)) { json = r.ReadToEnd(); li = JsonConvert.DeserializeObject <LootCache>(json, new JsonSerializerSettings { Converters = { new Newtonsoft.Json.Converters.VersionConverter() } }); needsRecalculate = false; } } // New Recipe Browser version, assume total reset needed (adjust this logic next update.) if (li.recipeBrowserVersion != recipeBrowserMod.Version) { li.lootInfos.Clear(); li.cachedMods.Clear(); li.recipeBrowserVersion = recipeBrowserMod.Version; needsRecalculate = true; } // If we aren't up to date on each mod... foreach (var mod in ModLoader.GetLoadedMods()) { if (/*mod == "ModLoader" || */ mod == "RecipeBrowser") { continue; } Mod m = ModLoader.GetMod(mod); string modName = m.Name == "ModLoader" ? "Terraria" : m.Name; if (!li.cachedMods.Any(x => x.Key == modName && x.Value == m.Version)) // if this mod is either updated or doesn't exist yet //if (li.cachedMods.ContainsKey(modName) && li.cachedMods[modName] == m.Version) { needsRecalculate = true; // Remove mod from list li.cachedMods.Remove(modName); // Remove items from this mod var toRemove = li.lootInfos.Where(pair => pair.Key.mod == modName) // Can't detect if a vanilla npc dropping a vanilla is because of mod. It's fine .Select(pair => pair.Key) .ToList(); foreach (var key in toRemove) { li.lootInfos.Remove(key); } // Remove npc from items. foreach (var itemToNPCs in li.lootInfos) { itemToNPCs.Value.RemoveAll(x => x.mod == modName); } modsThatNeedRecalculate.Add(modName); li.cachedMods[modName] = m.Version; // (new Tuple<string, Version>(modName, m.Version)); } } //li.cachedMods.Add(new Tuple<string, Version>(m.Name, m.Version)); if (needsRecalculate) { // Temp variables float soundVolume = Main.soundVolume; Main.soundVolume = 0f; if (!Main.dedServ) { Reflect(); } setLoadProgressText?.Invoke("Recipe Browser: Rebuilding Loot Cache"); setLoadProgressProgress?.Invoke(0f); // expert drops? for (int playernum = 0; playernum < 256; playernum++) { Main.player[playernum] = new Player(); } //Main.player[0].active = true; int oldMx = Main.maxTilesX; Main.maxTilesX = 2100; int oldMy = Main.maxTilesY; Main.maxTilesY = 600; for (int x = 0; x < Main.maxTilesX; x++) { for (int y = 0; y < Main.maxTilesY; y++) { Main.tile[x, y] = new Tile(); Main.tile[x, y].type = 0; if (y > Main.maxTilesY * 0.3f) { Main.tile[x, y].active(true); } } } Main.worldSurface = 200; //Main.netMode = 1; // hope this doesn't do anything weird NPC npc = new NPC(); Item item = new Item(); loots = new HashSet <int>(); string lastMod = ""; var watch = Stopwatch.StartNew(); var oldRand = Main.rand; if (Main.rand == null) { Main.rand = new Terraria.Utilities.UnifiedRandom(); } for (int i = 1; i < NPCLoader.NPCCount; i++) // for every npc... { npc.SetDefaults(i); npc.value = 0; string currentMod = npc.modNPC?.mod.Name ?? "Terraria"; if (!modsThatNeedRecalculate.Contains(currentMod)) { continue; } if (lastMod != currentMod) { lastMod = currentMod; setLoadSubProgressText?.Invoke(lastMod); } setLoadProgressProgress?.Invoke((float)i / NPCLoader.NPCCount); JSONNPC jsonNPC = new JSONNPC(npc.modNPC?.mod.Name ?? "Terraria", npc.modNPC?.Name ?? npc.TypeName, npc.modNPC != null ? 0 : i); loots.Clear(); CalculateLoot(npc); // ...calculate drops foreach (var loot in loots) { if (ignoreItemIDS.Contains(loot)) { continue; } item.SetDefaults(loot, true); //JSONItem jsonitem = new JSONItem(item.modItem?.mod.Name ?? "Terraria", Lang.GetItemNameValue(loot), item.modItem != null ? 0 : loot); JSONItem jsonitem = new JSONItem(item.modItem?.mod.Name ?? "Terraria", item.modItem?.Name ?? Lang.GetItemNameValue(loot), item.modItem != null ? 0 : loot); List <JSONNPC> npcsthatdropme; if (!li.lootInfos.TryGetValue(jsonitem, out npcsthatdropme)) { li.lootInfos.Add(jsonitem, npcsthatdropme = new List <JSONNPC>()); } npcsthatdropme.Add(jsonNPC); } } loots.Clear(); // Reset temp values Main.rand = oldRand; // value 8 seconds. // don't value to 0 and ignore.contains: 5 seconds. // value to 0, contains, 4 seconds. .6 seconds without contains. Main.maxTilesX = oldMx; Main.maxTilesY = oldMy; Main.soundVolume = soundVolume; // Save json: watch.Stop(); var elapsedMs = watch.ElapsedMilliseconds; //li.iterations = MaxNumberLootExperiments; li.lastUpdateTime = elapsedMs; Directory.CreateDirectory(folder); json = JsonConvert.SerializeObject(li, Formatting.Indented, new JsonSerializerSettings { Converters = { new Newtonsoft.Json.Converters.VersionConverter() } }); File.WriteAllText(path, json); // Reset Load Mods Progress bar setLoadSubProgressText?.Invoke(""); setLoadProgressText?.Invoke("Adding Recipes"); setLoadProgressProgress?.Invoke(0f); } LootCacheManagerActive = false; LootCache.instance = li; }