public void Run() { // First run through and compute the effective stats of each item based // on the value of the item level consideration setting. DebugParallelForEach(mItems.Where(x => !x.Ignore), item => { byte effective_level = item.Item.Level; switch (mSettings.LevelConsideration) { case AnalyzerSettings.ItemLevelConsideration.Current: effective_level = item.Item.Level; break; case AnalyzerSettings.ItemLevelConsideration.CurrentRankMaxLevel: effective_level = item.Item.LevelMax; break; case AnalyzerSettings.ItemLevelConsideration.FullyMaxed: effective_level = StatCalculator.MaxLevel( StatCalculator.Evolve(item.Item.BaseRarity, SchemaConstants.EvolutionLevel.PlusPlus)); break; } item.NonSynergizedStats = StatCalculator.ComputeStatsForLevel(item.Item.BaseRarity, item.BaseStats, item.MaxStats, effective_level); effective_level = StatCalculator.EffectiveLevelWithSynergy(effective_level); item.SynergizedStats = StatCalculator.ComputeStatsForLevel(item.Item.BaseRarity, item.BaseStats, item.MaxStats, effective_level); }); RealmSynergy.SynergyValue[] synergy_values = RealmSynergy.Values.ToArray(); AnalyzerSettings.DefensiveStat[] defensive_stats = Enum.GetValues(typeof(AnalyzerSettings.DefensiveStat)).Cast <AnalyzerSettings.DefensiveStat>().ToArray(); AnalyzerSettings.OffensiveStat[] offensive_stats = Enum.GetValues(typeof(AnalyzerSettings.OffensiveStat)).Cast <AnalyzerSettings.OffensiveStat>().ToArray(); List <AnalysisItem>[,] best_defensive_items = new List <AnalysisItem> [synergy_values.Length, defensive_stats.Length]; List <AnalysisItem>[,] best_offensive_items = new List <AnalysisItem> [synergy_values.Length, offensive_stats.Length]; // Sort the item list MxN different ways, once for each combination of (realm,stat) DebugParallelForEach(CartesianProduct(synergy_values, defensive_stats), x => { RealmSynergy.SynergyValue synergy = x.Key; AnalyzerSettings.DefensiveStat stat = x.Value; List <AnalysisItem> sorted_items = new List <AnalysisItem>(mItems.Where(y => !y.Ignore)); sorted_items.Sort((a, b) => { EquipStats a_stats = a.GetEffectiveStats(synergy); EquipStats b_stats = b.GetEffectiveStats(synergy); short a_value = ChooseDefensiveStat(a_stats, stat); short b_value = ChooseDefensiveStat(b_stats, stat); return(-a_value.CompareTo(b_value)); }); best_defensive_items[(int)synergy.Realm + 1, (int)stat] = sorted_items; }); DebugParallelForEach(CartesianProduct(synergy_values, offensive_stats), x => { RealmSynergy.SynergyValue synergy = x.Key; AnalyzerSettings.OffensiveStat stat = x.Value; List <AnalysisItem> sorted_items = new List <AnalysisItem>(mItems.Where(y => !y.Ignore)); sorted_items.Sort((a, b) => { EquipStats a_stats = a.GetEffectiveStats(synergy); EquipStats b_stats = b.GetEffectiveStats(synergy); short a_value = ChooseOffensiveStat(a_stats, stat); short b_value = ChooseOffensiveStat(b_stats, stat); return(-a_value.CompareTo(b_value)); }); best_offensive_items[(int)synergy.Realm + 1, (int)stat] = sorted_items; }); // Compute the top N items for each character. DebugParallelForEach(mBuddies.Where(x => x.Settings.Score), buddy => { buddy.TopNDefense = new List <AnalysisItem> [synergy_values.Length, defensive_stats.Length]; buddy.TopNOffense = new List <AnalysisItem> [synergy_values.Length, offensive_stats.Length]; for (int x = 0; x < best_defensive_items.GetLength(0); ++x) { for (int y = 0; y < best_defensive_items.GetLength(1); ++y) { List <AnalysisItem> best_items = best_defensive_items[x, y]; buddy.TopNDefense[x, y] = best_items.Where(item => item.EnabledUsers.Contains(buddy)).Take(mTopN).ToList(); } } for (int x = 0; x < best_offensive_items.GetLength(0); ++x) { for (int y = 0; y < best_offensive_items.GetLength(1); ++y) { List <AnalysisItem> best_items = best_offensive_items[x, y]; buddy.TopNOffense[x, y] = best_items.Where(item => item.EnabledUsers.Contains(buddy)).Take(mTopN).ToList(); } } }); // Finally, go through each item and assign it a score based on how many times it appears in someone's Top N list. DebugParallelForEach(mItems.Where(x => !x.Ignore), item => { bool is_weapon = false; switch (item.Item.Type) { case SchemaConstants.ItemType.Weapon: is_weapon = true; break; case SchemaConstants.ItemType.Armor: is_weapon = false; break; default: return; } // In the worst case the item appears 0 times in the top N for any character and any realm. In the best // case it appears somewhere in the top N for every character who can use it, and in every single realm. // Furthermore, we weight appearances by their rank, so the max *score* is that value times the best // possible rank. int usable_by = item.EnabledUsers.Count; int max_denormalized_score = mTopN * usable_by * synergy_values.Length; int denormalized_score = 0; foreach (AnalysisBuddy buddy in mBuddies.Where(b => b.Settings.Score)) { AnalyzerSettings.PartyMemberSettings buddy_settings = mSettings[buddy.Buddy.Name]; List <AnalysisItem>[,] rank_array = is_weapon ? buddy.TopNOffense : buddy.TopNDefense; int stat_index = is_weapon ? (int)buddy_settings.OffensiveStat : (int)buddy_settings.DefensiveStat; for (int realm = 0; realm < rank_array.GetLength(0); ++realm) { List <AnalysisItem> top_n_for_stat = rank_array[realm, stat_index]; int index = top_n_for_stat.IndexOf(item); if (index == -1) { continue; } denormalized_score += top_n_for_stat.Count - index; } } System.Diagnostics.Debug.Assert(denormalized_score <= max_denormalized_score); item.Result.IsValid = true; item.Result.Score = (double)denormalized_score / (double)max_denormalized_score; item.Result.Score *= 100.0; }); }
private GridEquipStats ComputeDisplayStats(DataEquipmentInformation equip) { ViewUpgradeModeComboIndex upgrade_type = (ViewUpgradeModeComboIndex)comboBoxUpgradeMode.SelectedIndex; RealmSynergy.SynergyValue synergy = RealmSynergy.Values.ElementAt(comboBoxSynergy.SelectedIndex); bool has_synergy = equip.SeriesId == synergy.GameSeries; DataCache.Items.Key cache_key = new DataCache.Items.Key { ItemId = equip.EquipmentId }; DataCache.Items.Data cache_value; bool in_cache = FFRKProxy.Instance.Cache.Items.TryGetValue(cache_key, out cache_value); GridEquipStats result = new GridEquipStats(); if (upgrade_type == ViewUpgradeModeComboIndex.CurrentUpgradeCurrentLevel) { result.Stats.Atk = (has_synergy) ? equip.SeriesAtk : equip.Atk; result.Stats.Mag = (has_synergy) ? equip.SeriesMag : equip.Mag; result.Stats.Acc = (has_synergy) ? equip.SeriesAcc : equip.Acc; result.Stats.Def = (has_synergy) ? equip.SeriesDef : equip.Def; result.Stats.Res = (has_synergy) ? equip.SeriesRes : equip.Res; result.Stats.Eva = (has_synergy) ? equip.SeriesEva : equip.Eva; result.Stats.Mnd = (has_synergy) ? equip.SeriesMnd : equip.Mnd; result.Level = equip.Level; result.MaxLevel = equip.LevelMax; if (equip.SeriesId == synergy.GameSeries) { result.Level = StatCalculator.EffectiveLevelWithSynergy(result.Level); } } else { if (upgrade_type == ViewUpgradeModeComboIndex.CurrentUpgradeMaxLevel) { result.MaxLevel = StatCalculator.MaxLevel(equip.Rarity); } else if (upgrade_type == ViewUpgradeModeComboIndex.MaxLevelThroughExistingCombine) { // Valid candidates for combining items into this are only those items with matching // equipment id and rarity LESS THAN OR EQUAL TO current item's rarity int candidates = mEquipments.Count(x => x.EquipmentId == equip.EquipmentId && x.InstanceId != equip.InstanceId && x.Rarity <= equip.Rarity); result.MaxLevel = StatCalculator.MaxLevel(StatCalculator.EvolveAsMuchAsPossible(equip.BaseRarity, equip.Rarity, candidates)); } else { result.MaxLevel = StatCalculator.MaxLevel(StatCalculator.Evolve(equip.BaseRarity, SchemaConstants.EvolutionLevel.PlusPlus)); } result.Level = result.MaxLevel; if (has_synergy) { result.Level = StatCalculator.EffectiveLevelWithSynergy(result.Level); } if (in_cache && cache_value.AreStatsValid) { // Try to get the equipment stats from the database result.Stats.Atk = StatCalculator.ComputeStatForLevel(equip.BaseRarity, cache_value.BaseStats.Atk, cache_value.MaxStats.Atk, result.Level); result.Stats.Mag = StatCalculator.ComputeStatForLevel(equip.BaseRarity, cache_value.BaseStats.Mag, cache_value.MaxStats.Mag, result.Level); result.Stats.Acc = StatCalculator.ComputeStatForLevel(equip.BaseRarity, cache_value.BaseStats.Acc, cache_value.MaxStats.Acc, result.Level); result.Stats.Def = StatCalculator.ComputeStatForLevel(equip.BaseRarity, cache_value.BaseStats.Def, cache_value.MaxStats.Def, result.Level); result.Stats.Res = StatCalculator.ComputeStatForLevel(equip.BaseRarity, cache_value.BaseStats.Res, cache_value.MaxStats.Res, result.Level); result.Stats.Eva = StatCalculator.ComputeStatForLevel(equip.BaseRarity, cache_value.BaseStats.Eva, cache_value.MaxStats.Eva, result.Level); result.Stats.Mnd = StatCalculator.ComputeStatForLevel(equip.BaseRarity, cache_value.BaseStats.Mnd, cache_value.MaxStats.Mnd, result.Level); } else { // If they aren't there, fall back to trying to compute effective stats from the ifnormation in the JSON. This will lead to some // rounding error due to the fact that the values for Atk and SeriesAtk etc are all rounded, so the division will be less precise // than doing it over the entire range of Max stats and base stats, but it's the best we can do in this case. byte series_effective_level = StatCalculator.EffectiveLevelWithSynergy(equip.Level); result.Stats.Atk = (result.MaxLevel == equip.Level) ? ((has_synergy) ? equip.SeriesAtk : equip.Atk) : StatCalculator.ComputeStatForLevel2(equip.Atk, equip.Level, equip.SeriesAtk, series_effective_level, result.Level); result.Stats.Mag = (result.MaxLevel == equip.Level) ? ((has_synergy) ? equip.SeriesMag : equip.Mag) : StatCalculator.ComputeStatForLevel2(equip.Mag, equip.Level, equip.SeriesMag, series_effective_level, result.Level); result.Stats.Acc = (result.MaxLevel == equip.Level) ? ((has_synergy) ? equip.SeriesAcc : equip.Acc) : StatCalculator.ComputeStatForLevel2(equip.Acc, equip.Level, equip.SeriesAcc, series_effective_level, result.Level); result.Stats.Def = (result.MaxLevel == equip.Level) ? ((has_synergy) ? equip.SeriesDef : equip.Def) : StatCalculator.ComputeStatForLevel2(equip.Def, equip.Level, equip.SeriesDef, series_effective_level, result.Level); result.Stats.Res = (result.MaxLevel == equip.Level) ? ((has_synergy) ? equip.SeriesRes : equip.Res) : StatCalculator.ComputeStatForLevel2(equip.Res, equip.Level, equip.SeriesRes, series_effective_level, result.Level); result.Stats.Eva = (result.MaxLevel == equip.Level) ? ((has_synergy) ? equip.SeriesEva : equip.Eva) : StatCalculator.ComputeStatForLevel2(equip.Eva, equip.Level, equip.SeriesEva, series_effective_level, result.Level); result.Stats.Mnd = (result.MaxLevel == equip.Level) ? ((has_synergy) ? equip.SeriesMnd : equip.Mnd) : StatCalculator.ComputeStatForLevel2(equip.Mnd, equip.Level, equip.SeriesMnd, series_effective_level, result.Level); } } if (equip.AugmentStat != null && equip.Augment > 0) { double bonus = (has_synergy) ? equip.Augment * 1.5 : (double)equip.Augment; System.Reflection.FieldInfo augmentField = typeof(EquipStats).GetField(equip.AugmentStat); short val = (short)augmentField.GetValue(result.Stats); augmentField.SetValue(result.Stats, (short)Math.Ceiling(val + bonus)); } return(result); }