2018-08-17 03:06:40 +00:00
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Linq;
|
|
|
|
|
|
|
|
namespace PKHeX.Core.Searching
|
|
|
|
{
|
|
|
|
/// <summary>
|
|
|
|
/// <see cref="PKM"/> searching utility
|
|
|
|
/// </summary>
|
|
|
|
public static class SearchUtil
|
|
|
|
{
|
|
|
|
public static IEnumerable<PKM> FilterByFormat(IEnumerable<PKM> res, int format, SearchComparison formatOperand)
|
|
|
|
{
|
|
|
|
switch (formatOperand)
|
|
|
|
{
|
|
|
|
case SearchComparison.GreaterThanEquals:
|
|
|
|
res = res.Where(pk => pk.Format >= format); break;
|
|
|
|
case SearchComparison.Equals:
|
|
|
|
res = res.Where(pk => pk.Format == format); break;
|
|
|
|
case SearchComparison.LessThanEquals:
|
|
|
|
res = res.Where(pk => pk.Format <= format); break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return res; /* Do nothing */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (format <= 2) // 1-2
|
|
|
|
return res.Where(pk => pk.Format <= 2);
|
|
|
|
if (format >= 3 && format <= 6) // 3-6
|
|
|
|
return res.Where(pk => pk.Format >= 3);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static IEnumerable<PKM> FilterByGeneration(IEnumerable<PKM> res, int generation)
|
|
|
|
{
|
2019-10-05 03:10:50 +00:00
|
|
|
return generation switch
|
2018-08-17 03:06:40 +00:00
|
|
|
{
|
2019-10-05 03:10:50 +00:00
|
|
|
1 => res.Where(pk => pk.VC || pk.Format < 3),
|
|
|
|
2 => res.Where(pk => pk.VC || pk.Format < 3),
|
|
|
|
_ => res.Where(pk => pk.GenNumber == generation)
|
|
|
|
};
|
2018-08-17 03:06:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public static IEnumerable<PKM> FilterByLVL(IEnumerable<PKM> res, SearchComparison option, int level)
|
|
|
|
{
|
|
|
|
if (level > 100)
|
|
|
|
return res;
|
|
|
|
|
2019-10-05 03:10:50 +00:00
|
|
|
return option switch
|
2018-08-17 03:06:40 +00:00
|
|
|
{
|
2019-10-05 03:10:50 +00:00
|
|
|
SearchComparison.LessThanEquals => res.Where(pk => pk.Stat_Level <= level),
|
|
|
|
SearchComparison.Equals => res.Where(pk => pk.Stat_Level == level),
|
|
|
|
SearchComparison.GreaterThanEquals => res.Where(pk => pk.Stat_Level >= level),
|
|
|
|
_ => res
|
|
|
|
};
|
2018-08-17 03:06:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public static IEnumerable<PKM> FilterByEVs(IEnumerable<PKM> res, int option)
|
|
|
|
{
|
2019-10-08 01:40:09 +00:00
|
|
|
return option switch
|
2018-08-17 03:06:40 +00:00
|
|
|
{
|
2019-10-08 01:40:09 +00:00
|
|
|
1 => res.Where(pk => pk.EVTotal == 0), // None (0)
|
|
|
|
2 => res.Where(pk => pk.EVTotal < 128), // Some (127-0)
|
|
|
|
3 => res.Where(pk => pk.EVTotal >= 128 && pk.EVTotal < 508), // Half (128-507)
|
|
|
|
4 => res.Where(pk => pk.EVTotal >= 508), // Full (508+)
|
|
|
|
_ => res
|
|
|
|
};
|
2018-08-17 03:06:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public static IEnumerable<PKM> FilterByIVs(IEnumerable<PKM> res, int option)
|
|
|
|
{
|
2019-10-08 01:40:09 +00:00
|
|
|
return option switch
|
2018-08-17 03:06:40 +00:00
|
|
|
{
|
2019-10-08 01:40:09 +00:00
|
|
|
1 => res.Where(pk => pk.IVTotal <= 90), // <= 90
|
|
|
|
2 => res.Where(pk => pk.IVTotal > 90 && pk.IVTotal <= 120), // 91-120
|
|
|
|
3 => res.Where(pk => pk.IVTotal > 120 && pk.IVTotal <= 150), // 121-150
|
|
|
|
4 => res.Where(pk => pk.IVTotal > 150 && pk.IVTotal < 180), // 151-179
|
|
|
|
5 => res.Where(pk => pk.IVTotal >= 180), // 180+
|
|
|
|
6 => res.Where(pk => pk.IVTotal == 186), // == 186
|
|
|
|
_ => res
|
|
|
|
};
|
2018-08-17 03:06:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public static IEnumerable<PKM> FilterByMoves(IEnumerable<PKM> res, IEnumerable<int> Moves)
|
|
|
|
{
|
|
|
|
var moves = new HashSet<int>(Moves);
|
|
|
|
int count = moves.Count;
|
|
|
|
return res.Where(pk =>
|
|
|
|
pk.Moves.Where(z => z > 0)
|
|
|
|
.Count(moves.Contains) == count
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static IEnumerable<PKM> FilterByBatchInstruction(IEnumerable<PKM> res, IList<string> BatchInstructions)
|
|
|
|
{
|
2020-04-12 20:20:40 +00:00
|
|
|
if (BatchInstructions.All(string.IsNullOrWhiteSpace))
|
2018-08-17 03:06:40 +00:00
|
|
|
return res; // none specified;
|
|
|
|
|
|
|
|
var lines = BatchInstructions.Where(z => !string.IsNullOrWhiteSpace(z));
|
|
|
|
var filters = StringInstruction.GetFilters(lines).ToArray();
|
|
|
|
BatchEditing.ScreenStrings(filters);
|
|
|
|
return res.Where(pkm => BatchEditing.IsFilterMatch(filters, pkm)); // Compare across all filters
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Func<PKM, string> GetCloneDetectMethod(CloneDetectionMethod Clones)
|
|
|
|
{
|
2019-10-08 01:40:09 +00:00
|
|
|
return Clones switch
|
2018-08-17 03:06:40 +00:00
|
|
|
{
|
2019-10-08 01:40:09 +00:00
|
|
|
CloneDetectionMethod.HashPID => HashByPID,
|
2020-01-07 01:50:18 +00:00
|
|
|
_ => HashByDetails,
|
2019-10-08 01:40:09 +00:00
|
|
|
};
|
2018-08-17 03:06:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public static string HashByDetails(PKM pk)
|
|
|
|
{
|
2019-10-05 03:10:50 +00:00
|
|
|
return pk.Format switch
|
2018-08-17 03:06:40 +00:00
|
|
|
{
|
2019-10-05 03:10:50 +00:00
|
|
|
1 => $"{pk.Species:000}{((PK1) pk).DV16:X4}",
|
|
|
|
2 => $"{pk.Species:000}{((PK2) pk).DV16:X4}",
|
|
|
|
_ => $"{pk.Species:000}{pk.PID:X8}{string.Join(" ", pk.IVs)}{pk.AltForm:00}"
|
|
|
|
};
|
2018-08-17 03:06:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public static string HashByPID(PKM pk)
|
|
|
|
{
|
2019-10-05 03:10:50 +00:00
|
|
|
return pk.Format switch
|
2018-08-17 03:06:40 +00:00
|
|
|
{
|
2019-10-05 03:10:50 +00:00
|
|
|
1 => $"{((PK1) pk).DV16:X4}",
|
|
|
|
2 => $"{((PK2) pk).DV16:X4}",
|
|
|
|
_ => $"{pk.PID:X8}"
|
|
|
|
};
|
2018-08-17 03:06:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public static IEnumerable<PKM> GetClones(IEnumerable<PKM> res, CloneDetectionMethod type = CloneDetectionMethod.HashDetails)
|
|
|
|
{
|
|
|
|
var method = GetCloneDetectMethod(type);
|
PKHeX.Core Nullable cleanup (#2401)
* Handle some nullable cases
Refactor MysteryGift into a second abstract class (backed by a byte array, or fake data)
Make some classes have explicit constructors instead of { } initialization
* Handle bits more obviously without null
* Make SaveFile.BAK explicitly readonly again
* merge constructor methods to have readonly fields
* Inline some properties
* More nullable handling
* Rearrange box actions
define straightforward classes to not have any null properties
* Make extrabyte reference array immutable
* Move tooltip creation to designer
* Rearrange some logic to reduce nesting
* Cache generated fonts
* Split mystery gift album purpose
* Handle more tooltips
* Disallow null setters
* Don't capture RNG object, only type enum
* Unify learnset objects
Now have readonly properties which are never null
don't new() empty learnsets (>800 Learnset objects no longer created,
total of 2400 objects since we also new() a move & level array)
optimize g1/2 reader for early abort case
* Access rewrite
Initialize blocks in a separate object, and get via that object
removes a couple hundred "might be null" warnings since blocks are now readonly getters
some block references have been relocated, but interfaces should expose all that's needed
put HoF6 controls in a groupbox, and disable
* Readonly personal data
* IVs non nullable for mystery gift
* Explicitly initialize forced encounter moves
* Make shadow objects readonly & non-null
Put murkrow fix in binary data resource, instead of on startup
* Assign dex form fetch on constructor
Fixes legality parsing edge cases
also handle cxd parse for valid; exit before exception is thrown in FrameGenerator
* Remove unnecessary null checks
* Keep empty value until init
SetPouch sets the value to an actual one during load, but whatever
* Readonly team lock data
* Readonly locks
Put locked encounters at bottom (favor unlocked)
* Mail readonly data / offset
Rearrange some call flow and pass defaults
Add fake classes for SaveDataEditor mocking
Always party size, no need to check twice in stat editor
use a fake save file as initial data for savedata editor, and for
gamedata (wow i found a usage)
constrain eventwork editor to struct variable types (uint, int, etc),
thus preventing null assignment errors
2019-10-17 01:47:31 +00:00
|
|
|
return GetClones(res, method);
|
2018-08-17 03:06:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public static IEnumerable<PKM> GetClones(IEnumerable<PKM> res, Func<PKM, string> method)
|
|
|
|
{
|
|
|
|
return res
|
|
|
|
.GroupBy(method)
|
|
|
|
.Where(grp => grp.Count() > 1)
|
|
|
|
.SelectMany(z => z);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static IEnumerable<PKM> GetExtraClones(IEnumerable<PKM> db)
|
|
|
|
{
|
|
|
|
return GetExtraClones(db, HashByDetails);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static IEnumerable<PKM> GetExtraClones(IEnumerable<PKM> db, Func<PKM, string> method)
|
|
|
|
{
|
|
|
|
return db.GroupBy(method).Where(grp => grp.Count() > 1).SelectMany(z => z.Skip(1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|