public List <Spell> Search(SpellSearchFilter filter) { //return filter.Apply(SpellList); if (filter.ClassMaxLevel == 0) { filter.ClassMinLevel = 1; } if (filter.ClassMaxLevel == 0) { filter.ClassMaxLevel = 255; } IEnumerable <Spell> query = SpellList; // if the spell text filter is an integer then just do a quick search by ID and ignore the other filters int id; if (Int32.TryParse(filter.Text, out id)) { return(query.Where(x => x.ID == id || x.GroupID == id).ToList()); } // spell name and description are checked for literal text if (!String.IsNullOrEmpty(filter.Text)) { query = query.Where(x => x.ID.ToString() == filter.Text || x.Name.IndexOf(filter.Text, StringComparison.InvariantCultureIgnoreCase) >= 0 || (x.Stacking != null && x.Stacking.Any(y => y.IndexOf(filter.Text, StringComparison.InvariantCultureIgnoreCase) >= 0)) || (x.Desc != null && x.Desc.IndexOf(filter.Text, StringComparison.InvariantCultureIgnoreCase) >= 0)); } // level filter is only used when a class is selected int levelArrayIndex = SpellParser.ParseClass(filter.Class) - 1; if (filter.Class == "Any PC") { query = query.Where(x => x.ClassesMask != 0); } else if (filter.Class == "Non PC") { //query = query.Where(x => x.ClassesMask == 0); query = query.Where(x => x.ExtLevels.All(y => y == 0)); } else if (!String.IsNullOrEmpty(filter.Class) && filter.Category != "AA") { if (levelArrayIndex >= 0) { query = query.Where(x => x.ClassesLevels != "ALL/254" && x.ExtLevels[levelArrayIndex] >= filter.ClassMinLevel && x.ExtLevels[levelArrayIndex] <= filter.ClassMaxLevel); } } if (!String.IsNullOrEmpty(filter.Category)) { if (filter.Category == "AA" && levelArrayIndex >= 0) { query = query.Where(x => x.ExtLevels[levelArrayIndex] == 254); } else if (filter.Category == "AA") { query = query.Where(x => x.ExtLevels.Any(y => y == 254)); } else { query = query.Where(x => x.Categories.Any(y => y.StartsWith(filter.Category, StringComparison.InvariantCultureIgnoreCase))); } } // effect filter can be a literal string or a regex for (int i = 0; i < filter.Effect.Length; i++) { if (!String.IsNullOrEmpty(filter.Effect[i])) { string effect = null; if (SpellSearchFilter.CommonEffects.ContainsKey(filter.Effect[i])) { effect = SpellSearchFilter.CommonEffects[filter.Effect[i]]; } if (!String.IsNullOrEmpty(effect)) { var re = new Regex(effect, RegexOptions.IgnoreCase); int?slot = filter.EffectSlot[i]; query = query.Where(x => x.HasEffect(re, slot ?? 0)); } else { string text = filter.Effect[i]; int? slot = filter.EffectSlot[i]; query = query.Where(x => x.HasEffect(text, slot ?? 0)); } } } //if (filter.Ranks == "Unranked") // query = query.Where(x => x.Rank == 0); //if (filter.Ranks == "Rank 1") // query = query.Where(x => x.Rank == 1); //if (filter.Ranks == "Rank 2") // query = query.Where(x => x.Rank == 2); //if (filter.Ranks == "Rank 3") // query = query.Where(x => x.Rank == 3); //if (filter.Ranks == "Unranked + Rank 1") // query = query.Where(x => x.Rank == 0 || x.Rank == 1); //if (filter.Ranks == "Unranked + Rank 2") // query = query.Where(x => x.Rank == 0 || x.Rank == 2); //if (filter.Ranks == "Unranked + Rank 3") // query = query.Where(x => x.Rank == 0 || x.Rank == 3); return(query.ToList()); }
/// <summary> /// Sort a spell list based on what kind of filters were used. /// </summary> public void Sort(List <Spell> list, SpellSearchFilter filter) { int cls = SpellParser.ParseClass(filter.Class) - 1; int id; Int32.TryParse(filter.Text, out id); // 1. if an effect is selected then sort by the effect strength // this is problematic since many spells have conditional effects //if (effect.Contains(@"(\d+)")) //{ // Sorting = "Results have been sorted by descending " + SearchEffect.Text + " strength."; // var re = new Regex(effect, RegexOptions.IgnoreCase); // Results.Sort((a, b) => // { // int comp = b.ScoreEffect(re) - a.ScoreEffect(re); // if (comp == 0) // comp = a.ID - b.ID; // return comp; // }); //} // 2. if a class is selected then sort by the casting levels for that class first // place castable spells before non castable effects (level == 0) if (cls >= 0) { list.Sort((a, b) => { if (a.Levels[cls] > 0 && b.Levels[cls] == 0) { return(-1); } if (b.Levels[cls] > 0 && a.Levels[cls] == 0) { return(1); } // move AA to bottom of list if (a.Levels[cls] >= 250 && b.Levels[cls] < 250) { return(1); } if (b.Levels[cls] >= 250 && a.Levels[cls] < 250) { return(-1); } // b-a = descending // a-b = ascending int comp = b.Levels[cls] - a.Levels[cls]; if (comp == 0) { comp = String.Compare(StripRank(a.Name), StripRank(b.Name)); } if (comp == 0) { comp = a.ID - b.ID; } return(comp); }); } // 3. finally sort by name if no better method is found else { list.Sort((a, b) => { int comp = String.Compare(StripRank(a.Name), StripRank(b.Name)); if (comp == 0) { comp = a.ID - b.ID; } return(comp); }); } // if searching by id, move the spell to the top of the results because it may be sorted below it's side effect spells if (id > 0) { var i = list.FindIndex(x => x.ID == id); if (i > 0) { var move = list[i]; list.RemoveAt(i); list.Insert(0, move); } } // move entries that begin with the search text to the front of the results else if (!String.IsNullOrEmpty(filter.Text)) { var move = list.FindAll(x => x.Name.StartsWith(filter.Text, StringComparison.InvariantCultureIgnoreCase)); if (move.Count > 0) { list.RemoveAll(x => x.Name.StartsWith(filter.Text, StringComparison.InvariantCultureIgnoreCase)); list.InsertRange(0, move); } } }
public void LoadSpells(string spellPath) { SpellPath = spellPath; SpellList = SpellParser.LoadFromFile(spellPath); SpellsById = SpellList.ToDictionary(x => x.ID); SpellsByGroup = SpellList.Where(x => x.GroupID != 0).ToLookup(x => x.GroupID); // fill LinksTo array for each spells - this will be used to include associated spells in search results // excluded spell IDs will be negated foreach (var spell in SpellList) { var linked = new List <int>(10); // add link for recourse spell if (spell.RecourseID != 0) { linked.Add(spell.RecourseID); } // add links for spells reference in any slots foreach (var s in spell.Slots) { if (s != null) { bool exclude = s.Desc.Contains("Exclude"); // match spell refs (works for a single ref) //var match = Spell.SpellRefExpr.Match(s.Desc); //if (match.Success) //{ // int id = Int32.Parse(match.Groups[1].Value); // linked.Add(exclude ? -id : id); //} // match spell refs (works for multiple refs on a single slot e.g. auras) var matches = Spell.SpellRefExpr.Matches(s.Desc); foreach (Match match in matches) { int id = Int32.Parse(match.Groups[1].Value); linked.Add(exclude ? -id : id); } // match spell group refs var gmatch = Spell.GroupRefExpr.Match(s.Desc); if (gmatch.Success) { int id = Int32.Parse(gmatch.Groups[1].Value); linked.AddRange(SpellsByGroup[id].Select(x => exclude ? -x.ID : x.ID)); } } } // for each spell that is referenced, update ExtLevels if the spells isn't already flagged as usable by the class foreach (int id in linked) { Spell target = null; if (SpellsById.TryGetValue(id, out target)) { target.RefCount++; // only do this for spells that aren't already assigned to a class if (target.ClassesMask != 0) { continue; } // a lot of side effect spells do not have a level on them. this will copy the level of the referring spell // onto the side effect spell so that the spell will be searchable when filtering by class. // e.g. Jolting Swings Strike has no level so it won't show up in a ranger search even though Jolting Swings will show up // we create this separate array and never display it because modifying the levels array would imply different functionality // e.g. some spells purposely don't have levels assigned so that they are not affected by focus spells for (int i = 0; i < spell.Levels.Length; i++) { if (target.ExtLevels[i] == 0 && spell.Levels[i] != 0) { target.ExtLevels[i] = spell.Levels[i]; } // apply in the reverse direction too. this will probably only be useful for including type3 augs // todo: check if this includes too many focus spells //if (spell.ExtLevels[i] == 0 && target.Levels[i] != 0) // spell.ExtLevels[i] = target.Levels[i]; } } } spell.LinksTo = linked.ToArray(); } }