void BuildEndGameEquipment(EquipmentJobRank[] scoredJobEquipment, JobData jobData, Saint.ClassJob sJob, bool isCombatJob) { // We only want endgame equipment for jobs, not starting classes. // Filter out DoW/M classes that don't have a parent. // Everything before the Heavensward classless jobs. if (sJob.Key < 31 && isCombatJob && sJob.ParentClassJob == sJob) { return; } // EndGame equipment is simply grouped by slot and ordered. var endGameJobEquipmentList = new JObject(); _builder.Db.EndGameEquipmentByJob[sJob.Abbreviation] = endGameJobEquipmentList; var endGameEquipmentBySlot = scoredJobEquipment // Go back 10 levels for non-combat jobs, so Lv. 60 shows prior Lv. 50 gear for comparison. .Where(e => e.EquipmentLevel >= jobData.MaxLevel - (isCombatJob ? 0 : 10)) .GroupBy(e => e.Slot); foreach (var endGameSlot in endGameEquipmentBySlot) { // Sort results in this slot. EquipmentJobRank[] orderedResults; if (isCombatJob) { orderedResults = endGameSlot.OrderByDescending(e => e.Equipment.ItemLevel.Key).ThenByDescending(e => e.Equipment.Key).ToArray(); } else { orderedResults = endGameSlot.OrderByDescending(e => e.Rank.Score).ToArray(); } // Skip slots that don't have a single piece at cap. (WHM wands and shields) if (orderedResults.Max(e => e.EquipmentLevel) < jobData.MaxLevel) { continue; } // Add references to top results. var topResults = orderedResults.Take(9).ToArray(); foreach (var result in topResults) { _builder.Db.AddReference(endGameJobEquipmentList, "item", result.Equipment.Key, false); } // Store them in the list. var results = topResults .Select(e => new JObject(new JProperty("id", e.Equipment.Key))) .ToArray(); endGameJobEquipmentList[endGameSlot.Key.Key.ToString()] = new JArray(results); // Don't predict progression on combat jobs. if (isCombatJob) { continue; } // Record end-game progression on DoH/DoL jobs. dynamic previousItem = null; double previousScore = 0; foreach (var e in orderedResults.Take(9).Reverse()) { if (e.Rank.Score == previousScore) { continue; } var item = _builder.Db.ItemsById[e.Equipment.Key]; _builder.UpgradeItem(previousItem, item); previousItem = item; previousScore = e.Rank.Score; } } }
void BuildLevelingEquipment(EquipmentJobRank[] scoredJobEquipment, JobData jobData, Saint.ClassJob sJob) { var levelingJobEquipmentArray = new JArray(); _builder.Db.LevelingEquipmentByJob[sJob.Abbreviation] = levelingJobEquipmentArray; Dictionary <Saint.EquipSlotCategory, List <EquipmentJobRank> > previousLevelingItems = null; // Find the best crafted equipment from 1-max that isn't a star recipe. for (var elvl = 1; elvl < jobData.MaxLevel; elvl++) { var currentLevelingItems = new Dictionary <Saint.EquipSlotCategory, List <EquipmentJobRank> >(); var relevantEquipment = scoredJobEquipment .Where(e => e.HasCraftingRecipe || e.HasGcVendor || e.HasGilVendor) .Where(e => e.EquipmentLevel <= elvl) .Where(e => e.Item.achievements == null) // No achievement gear. .Where(e => e.Vendors == null || !e.Vendors.Select(t => (int)t).Contains(1006004)) // No calamity salvager gear. .GroupBy(e => e.Slot); var equipmentLevelSlots = new JObject(); foreach (var scoresBySlot in relevantEquipment) { // Use the base score without melds because max overmelding on leveling equ is unrealistic. var orderedEquipment = scoresBySlot .OrderByDescending(s => s.Rank.BaseScore) .ThenBy(s => s.Equipment.Key) .ToArray(); // Find the highest scores in 3 categories: // 1. With a crafting recipe. // 2. From the GC vendor. // 3. From a regular vendor. var currentList = new List <EquipmentJobRank>(); var highestCrafted = orderedEquipment.FirstOrDefault(s => s.HasCraftingRecipe); if (highestCrafted != null) { currentList.Add(highestCrafted); } var highestGc = orderedEquipment.FirstOrDefault(s => s.HasGcVendor); var highestGil = orderedEquipment.FirstOrDefault(s => s.HasGilVendor); // Ignore GC gear if gil shop gear is better. if (highestGc != null && highestGil != null && highestGil.Rank.BaseScore > highestGc.Rank.BaseScore) { highestGc = null; } if (highestGc != null) { currentList.Add(highestGc); } if (highestGil != null) { currentList.Add(highestGil); } // No equipment - probably nothing available for this slot at this level. if (currentList.Count == 0) { continue; } currentLevelingItems[scoresBySlot.Key] = currentList; var records = new JArray(); foreach (var item in currentList.Distinct().OrderByDescending(i => i.Rank.BaseScore)) { dynamic record = new JObject(); record.id = item.Equipment.Key; if (item.HasCraftingRecipe) { record.craft = 1; } if (item.HasGcVendor) { record.gc = 1; } if (item.HasGilVendor) { record.gil = 1; } records.Add(record); _builder.Db.AddReference(levelingJobEquipmentArray, "item", item.Equipment.Key, false); } equipmentLevelSlots[scoresBySlot.Key.Key.ToString()] = records; } levelingJobEquipmentArray.Add(equipmentLevelSlots); // Link upgrades if (previousLevelingItems != null) { foreach (var pair in currentLevelingItems) { if (!previousLevelingItems.TryGetValue(pair.Key, out var previousEquipment)) { continue; } foreach (var previousItem in previousEquipment) { foreach (var currentItem in pair.Value) { // Skip identical equipment levels. The tool covers horizontal upgrades. if (currentItem.EquipmentLevel == previousItem.EquipmentLevel) { continue; } // List all classes of upgrade, regardless of type. if (currentItem.Rank.BaseScore > previousItem.Rank.BaseScore) { _builder.UpgradeItem(previousItem.Item, currentItem.Item); } } } } } previousLevelingItems = currentLevelingItems; } }