mirror of
https://github.com/kwsch/PKHeX.git
synced 2026-08-25 11:25:30 -05:00
125 lines
4.5 KiB
C#
125 lines
4.5 KiB
C#
using System.Collections.Generic;
|
|
using static PKHeX.Core.CheckIdentifier;
|
|
|
|
namespace PKHeX.Core.Bulk;
|
|
|
|
/// <summary>
|
|
/// Checks for duplicate Trainer IDs among Pokémon in a bulk legality analysis.
|
|
/// </summary>
|
|
public sealed class DuplicateTrainerChecker : IBulkAnalyzer
|
|
{
|
|
/// <summary>
|
|
/// Analyzes the provided <see cref="BulkAnalysis"/> for duplicate Trainer IDs.
|
|
/// </summary>
|
|
/// <param name="input">The bulk analysis data to check.</param>
|
|
public void Analyze(BulkAnalysis input) => CheckIDReuse(input);
|
|
|
|
private static void CheckIDReuse(BulkAnalysis input)
|
|
{
|
|
var dict = new Dictionary<uint, CombinedReference>();
|
|
for (int i = 0; i < input.AllData.Count; i++)
|
|
{
|
|
if (input.GetIsClone(i))
|
|
continue; // already flagged
|
|
var cs = input.AllData[i];
|
|
var ca = input.AllAnalysis[i];
|
|
var cr = new CombinedReference(cs, ca, i);
|
|
Verify(input, dict, cr);
|
|
}
|
|
}
|
|
|
|
private static void Verify(BulkAnalysis input, Dictionary<uint, CombinedReference> dict, CombinedReference cr)
|
|
{
|
|
var ca = cr.Analysis;
|
|
var cs = cr.Slot;
|
|
var id = cs.Entity.ID32;
|
|
|
|
if (!dict.TryGetValue(id, out var pr))
|
|
{
|
|
dict.Add(id, cr);
|
|
return;
|
|
}
|
|
|
|
var pa = pr.Analysis;
|
|
// ignore GB era collisions
|
|
// a 16bit TID16 can reasonably occur for multiple trainers, and versions
|
|
if (ca.Info.Generation <= 2 && pa.Info.Generation <= 2)
|
|
return;
|
|
|
|
if (VerifyIDReuse(input, pr, cr))
|
|
return;
|
|
|
|
// egg encounters can be traded before hatching
|
|
// store the current loop pk if it's a better reference
|
|
var ps = pr.Slot;
|
|
if (ps.Entity.WasTradedEgg && !ca.Entity.WasTradedEgg)
|
|
dict[id] = cr;
|
|
}
|
|
|
|
private static bool VerifyIDReuse(BulkAnalysis input, CombinedReference pr, CombinedReference cr)
|
|
{
|
|
var pa = pr.Analysis;
|
|
var ps = pr.Slot;
|
|
var ca = cr.Analysis;
|
|
var cs = cr.Slot;
|
|
if (IsNotPlayerDetails(pa.EncounterMatch) || IsNotPlayerDetails(ca.EncounterMatch))
|
|
return false;
|
|
|
|
const CheckIdentifier ident = Trainer;
|
|
|
|
var pp = ps.Entity;
|
|
var cp = cs.Entity;
|
|
|
|
// 32bit ID-SID16 should only occur for one generation
|
|
// Trainer-ID-SID16 should only occur for one version
|
|
if (IsSharedVersion(pp, pa, cp, ca))
|
|
{
|
|
input.AddLine(ps, cs, ident, pr.Index, cr.Index, LegalityCheckResultCode.BulkSharingTrainerVersion);
|
|
return true;
|
|
}
|
|
|
|
// ID-SID16 should only occur for one Trainer name
|
|
if (pp.OriginalTrainerName != cp.OriginalTrainerName)
|
|
{
|
|
var severity = ca.Info.Generation == 4 ? Severity.Fishy : Severity.Invalid;
|
|
input.AddLine(ps, cs, ident, pr.Index, cr.Index, LegalityCheckResultCode.BulkSharingTrainerIDs, s: severity);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static bool IsNotPlayerDetails(IEncounterTemplate enc) => enc switch
|
|
{
|
|
IFixedTrainer { IsFixedTrainer: true } => true,
|
|
MysteryGift { IsEgg: false } => true,
|
|
_ => false,
|
|
};
|
|
|
|
private static bool IsSharedVersion(PKM pp, LegalityAnalysis pa, PKM cp, LegalityAnalysis ca)
|
|
{
|
|
if (pp.Version == cp.Version || pp.Version == 0 || cp.Version == 0)
|
|
return false;
|
|
|
|
// Traded eggs retain the original version ID, only on the same generation
|
|
if (pa.Info.Generation != ca.Info.Generation)
|
|
return false;
|
|
|
|
// Gen3/4 traded eggs do not have an Egg Location, and do not update the Version upon hatch.
|
|
// These eggs can obtain another trainer's TID16/SID16/OT and be valid with a different version ID.
|
|
if (pa.EncounterMatch.IsEgg && IsTradedEggVersionNoUpdate(pp, pa))
|
|
return false; // version doesn't update on trade
|
|
if (ca.EncounterMatch.IsEgg && IsTradedEggVersionNoUpdate(cp, ca))
|
|
return false; // version doesn't update on trade
|
|
|
|
static bool IsTradedEggVersionNoUpdate(PKM pk, LegalityAnalysis la) => la.Info.Generation switch
|
|
{
|
|
2 => true, // No version stored, just ignore.
|
|
3 => true, // No egg location, assume can be traded. Doesn't update version upon hatch.
|
|
4 => pk.WasTradedEgg, // Gen4 traded eggs do not update version upon hatch.
|
|
_ => false, // Gen5+ eggs have an egg location, and update the version upon hatch.
|
|
};
|
|
|
|
return true;
|
|
}
|
|
}
|