Remove extra Memory forcing

Closes #4133
Refactors most of the `Trade` methods
Fix default egg friendship to 120
Fix Version value exposed for Gen4 saves (and others)
This commit is contained in:
Kurt 2024-02-23 17:01:36 -06:00
parent 65d8ab025d
commit 034658b764
36 changed files with 347 additions and 203 deletions

View file

@ -344,7 +344,7 @@ public static class CommonEdits
return;
pk.IsEgg = false;
pk.ClearNickname();
pk.CurrentFriendship = pk.PersonalInfo.BaseFriendship;
pk.OriginalTrainerFriendship = Math.Min(pk.OriginalTrainerFriendship, EggStateLegality.GetEggHatchFriendship(pk.Context));
if (pk.IsTradedEgg)
pk.EggLocation = pk.MetLocation;
if (pk.Version == 0)

View file

@ -30,7 +30,7 @@ public sealed class FakeSaveFile : SaveFile
public override int BoxCount => 1;
public override int GetPartyOffset(int slot) => -1;
protected override void SetChecksums() { }
public override GameVersion Version { get => GameVersion.R; set { } }
public override Type PKMType => typeof(PK3);
protected override PK3 GetPKM(byte[] data) => BlankPKM;
protected override byte[] DecryptPKM(byte[] data) => data;

View file

@ -42,7 +42,7 @@ public sealed record EncounterEgg(ushort Species, byte Form, byte Level, byte Ge
var ball = FixedBall;
pk.Ball = ball is Ball.None ? (byte)Ball.Poke : (byte)ball;
pk.OriginalTrainerFriendship = pk.PersonalInfo.BaseFriendship;
pk.OriginalTrainerFriendship = EggStateLegality.GetEggHatchFriendship(Context);
SetEncounterMoves(pk, version);
pk.HealPP();
@ -51,17 +51,22 @@ public sealed record EncounterEgg(ushort Species, byte Form, byte Level, byte Ge
if (gen <= 2)
{
if (version != GameVersion.C)
var pk2 = (PK2)pk;
if (version == GameVersion.C)
{
pk.OriginalTrainerGender = 0;
// Set met data for Crystal hatch.
pk2.MetLocation = Locations.HatchLocationC;
pk2.MetLevel = 1;
pk2.MetTimeOfDay = rnd.Next(1, 4); // Morning | Day | Night
}
else
else // G/S
{
pk.MetLocation = Locations.HatchLocationC;
pk.MetLevel = 1;
((PK2)pk).MetTimeOfDay = rnd.Next(1, 4); // Morning | Day | Night
// G/S can't set any data for Trainer Gender.
pk2.OriginalTrainerGender = 0;
}
return pk;
// No other revisions needed.
return pk2;
}
SetMetData(pk);

View file

@ -153,4 +153,29 @@ public static class EggStateLegality
SL or VL => Locations.HatchLocation9,
_ => 0,
};
/// <summary>
/// Gets the initial friendship value for an egg when it is hatched.
/// </summary>
public static byte GetEggHatchFriendship(EntityContext context) => context switch
{
// From Gen2->Gen7, the value was 120.
EntityContext.Gen2 => 120,
EntityContext.Gen3 => 120,
EntityContext.Gen4 => 120,
EntityContext.Gen5 => 120,
EntityContext.Gen6 => 120,
EntityContext.Gen7 => 120,
// No eggs in LGP/E.
// Starting in SW/SH, Friendship was rescaled away from 255 (to 160-ish), so the value is lower than prior.
_ => 100,
};
/// <summary>
/// Reasonable value for the friendship of an egg when it is hatched.
/// </summary>
/// <remarks>Only use if you're trying to generalize a value for hatched eggs without checking context.</remarks>
public const byte EggHatchFriendshipGeneral = 100;
}

View file

@ -0,0 +1,19 @@
namespace PKHeX.Core;
/// <summary>
/// Interface exposing a method to adapt the entity to the Handling Trainer.
/// </summary>
public interface IHandlerUpdate
{
/// <summary>
/// Indicates if the entity belongs to the <see cref="ITrainerInfo"/>.
/// </summary>
/// <param name="tr">Trainer to check if it originally possessed the entity.</param>
bool BelongsTo(ITrainerInfo tr);
/// <summary>
/// Updates the entity to match the <see cref="ITrainerInfo"/>.
/// </summary>
/// <param name="tr">Trainer that is now in possession of the entity.</param>
void UpdateHandler(ITrainerInfo tr);
}

View file

@ -8,7 +8,8 @@ namespace PKHeX.Core;
/// <summary> Generation 8 <see cref="PKM"/> format. </summary>
public sealed class PA8 : PKM, ISanityChecksum,
IGanbaru, IAlpha, INoble, ITechRecord, ISociability, IMoveShop8Mastery, IContestStats, IHyperTrain, IScaledSizeValue, IScaledSize3, IGigantamax, IFavorite, IDynamaxLevel, IHandlerLanguage, IFormArgument, IHomeTrack, IBattleVersion, ITrainerMemories, IPokerusStatus,
IRibbonIndex, IRibbonSetAffixed, IRibbonSetRibbons, IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetCommon3, IRibbonSetCommon4, IRibbonSetCommon6, IRibbonSetMemory6, IRibbonSetCommon7, IRibbonSetCommon8, IRibbonSetMarks, IRibbonSetMark8, IRibbonSetCommon9, IRibbonSetMark9
IRibbonIndex, IRibbonSetAffixed, IRibbonSetRibbons, IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetCommon3, IRibbonSetCommon4, IRibbonSetCommon6, IRibbonSetMemory6, IRibbonSetCommon7, IRibbonSetCommon8, IRibbonSetMarks, IRibbonSetMark8, IRibbonSetCommon9, IRibbonSetMark9,
IHandlerUpdate
{
public override ReadOnlySpan<ushort> ExtraBytes =>
[
@ -570,7 +571,19 @@ public sealed class PA8 : PKM, ISanityChecksum,
return 0x40 + (index >> 3);
}
public void Trade(ITrainerInfo tr)
public bool BelongsTo(ITrainerInfo tr)
{
if (tr.Version != Version)
return false;
if (tr.ID32 != ID32)
return false;
if (tr.Gender != OriginalTrainerGender)
return false;
return tr.OT == OriginalTrainerName;
}
public void UpdateHandler(ITrainerInfo tr)
{
// Process to the HT if the OT of the Pokémon does not match the SAV's OT info.
if (!TradeOT(tr))
@ -581,38 +594,27 @@ public sealed class PA8 : PKM, ISanityChecksum,
{
if (LA)
{
OriginalTrainerMemoryVariable = OriginalTrainerMemory = OriginalTrainerMemoryIntensity = OriginalTrainerMemoryFeeling = 0;
HandlingTrainerMemoryVariable = HandlingTrainerMemory = HandlingTrainerMemoryIntensity = HandlingTrainerMemoryFeeling = 0; // future inter-format conversion?
}
if (IsEgg) // No memories if is egg.
{
HandlingTrainerMemoryVariable = HandlingTrainerMemory = HandlingTrainerMemoryIntensity = HandlingTrainerMemoryFeeling = 0;
OriginalTrainerMemoryVariable = OriginalTrainerMemory = OriginalTrainerMemoryIntensity = OriginalTrainerMemoryFeeling = 0;
// Clear Handler
if (!IsTradedEgg)
{
HandlingTrainerFriendship = HandlingTrainerGender = HandlingTrainerLanguage = 0;
HandlingTrainerTrash.Clear();
}
return;
this.ClearMemoriesOT();
this.ClearMemoriesHT();
}
if (IsUntraded)
HandlingTrainerMemoryVariable = HandlingTrainerGender = HandlingTrainerFriendship = HandlingTrainerMemory = HandlingTrainerMemoryIntensity = HandlingTrainerMemoryFeeling = HandlingTrainerLanguage = 0;
var gen = Generation;
if (gen < 6)
OriginalTrainerMemoryVariable = OriginalTrainerMemory = OriginalTrainerMemoryIntensity = OriginalTrainerMemoryFeeling = 0;
// if (gen != 8) // must be transferred via HOME, and must have memories
// this.SetTradeMemoryHT8(); // not faking HOME tracker.
{
HandlingTrainerGender = HandlingTrainerFriendship = HandlingTrainerLanguage = 0;
HandlingTrainerTrash.Clear();
}
else
{
var gen = Generation;
if (gen < 6)
this.ClearMemoriesOT();
}
}
private bool TradeOT(ITrainerInfo tr)
{
// Check to see if the OT matches the SAV's OT info.
if (!(tr.ID32 == ID32 && tr.Gender == OriginalTrainerGender && tr.OT == OriginalTrainerName))
if (!BelongsTo(tr))
return false;
CurrentHandler = 0;

View file

@ -5,7 +5,8 @@ using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core;
/// <summary> Generation 7 <see cref="PKM"/> format used for <see cref="GameVersion.GG"/>. </summary>
public sealed class PB7 : G6PKM, IHyperTrain, IAwakened, IScaledSizeValue, ICombatPower, IFavorite, IFormArgument, IAppliedMarkings7
public sealed class PB7 : G6PKM, IHyperTrain, IAwakened, IScaledSizeValue, ICombatPower, IFavorite,
IFormArgument, IAppliedMarkings7, IHandlerUpdate
{
public override ReadOnlySpan<ushort> ExtraBytes =>
[
@ -336,7 +337,7 @@ public sealed class PB7 : G6PKM, IHyperTrain, IAwakened, IScaledSizeValue, IComb
protected override bool TradeOT(ITrainerInfo tr)
{
// Check to see if the OT matches the SAV's OT info.
if (!(tr.ID32 == ID32 && tr.Gender == OriginalTrainerGender && tr.OT == OriginalTrainerName))
if (!BelongsTo(tr))
return false;
CurrentHandler = 0;
@ -357,7 +358,10 @@ public sealed class PB7 : G6PKM, IHyperTrain, IAwakened, IScaledSizeValue, IComb
public void FixMemories()
{
if (IsUntraded)
HT_TextVar = HandlingTrainerFriendship = HT_Memory = HT_Intensity = HT_Feeling = 0;
{
HandlingTrainerTrash.Clear();
HandlingTrainerGender = HandlingTrainerFriendship = 0;
}
}
// Maximums

View file

@ -45,13 +45,28 @@ public sealed class PB8 : G8PKM
set => Data[0x52] = (byte)((Data[0x52] & 0xFE) | (value ? 1 : 0));
}
public void Trade(ITrainerInfo tr, int Day = 1, int Month = 1, int Year = 2015)
public bool BelongsTo(ITrainerInfo tr)
{
if (tr.Version != Version)
return false;
if (tr.ID32 != ID32)
return false;
if (tr.Gender != OriginalTrainerGender)
return false;
return tr.OT == OriginalTrainerName;
}
public void UpdateHandler(ITrainerInfo tr)
{
if (IsEgg)
{
// Apply link trade data, only if it left the OT (ignore if dumped & imported, or cloned, etc.)
if ((tr.TID16 != TID16) || (tr.SID16 != SID16) || (tr.Gender != OriginalTrainerGender) || (tr.OT != OriginalTrainerName))
SetLinkTradeEgg(Day, Month, Year, Locations.LinkTrade6NPC);
const ushort location = Locations.LinkTrade6NPC;
if (MetLocation != location && !BelongsTo(tr))
{
var date = EncounterDate.GetDate3DS();
SetLinkTradeEgg(date.Day, date.Month, date.Year, location);
}
// Unfortunately, BD/SP doesn't return if it's an egg, and can update the HT details & handler.
// Continue to the rest of the method.
@ -67,15 +82,13 @@ public sealed class PB8 : G8PKM
{
if (BDSP)
{
OriginalTrainerMemoryVariable = OriginalTrainerMemory = OriginalTrainerMemoryIntensity = OriginalTrainerMemoryFeeling = 0;
HandlingTrainerMemoryVariable = HandlingTrainerMemory = HandlingTrainerMemoryIntensity = HandlingTrainerMemoryFeeling = 0; // future inter-format conversion?
this.ClearMemoriesOT();
this.ClearMemoriesHT();
}
if (IsEgg) // No memories if is egg.
{
HandlingTrainerMemoryVariable = HandlingTrainerMemory = HandlingTrainerMemoryIntensity = HandlingTrainerMemoryFeeling = 0;
OriginalTrainerMemoryVariable = OriginalTrainerMemory = OriginalTrainerMemoryIntensity = OriginalTrainerMemoryFeeling = 0;
// Memories already cleared above.
// Clear Handler
if (!IsTradedEgg)
{
@ -86,19 +99,23 @@ public sealed class PB8 : G8PKM
}
if (IsUntraded)
HandlingTrainerMemoryVariable = HandlingTrainerGender = HandlingTrainerFriendship = HandlingTrainerMemory = HandlingTrainerMemoryIntensity = HandlingTrainerMemoryFeeling = HandlingTrainerLanguage = 0;
var gen = Generation;
if (gen < 6)
OriginalTrainerMemoryVariable = OriginalTrainerMemory = OriginalTrainerMemoryIntensity = OriginalTrainerMemoryFeeling = 0;
// if (gen != 8) // must be transferred via HOME, and must have memories
// this.SetTradeMemoryHT8(); // not faking HOME tracker.
{
// Memories already cleared above.
HandlingTrainerFriendship = HandlingTrainerGender = HandlingTrainerLanguage = 0;
HandlingTrainerTrash.Clear();
}
else
{
var gen = Generation;
if (gen < 6)
this.ClearMemoriesOT();
}
}
private bool TradeOT(ITrainerInfo tr)
{
// Check to see if the OT matches the SAV's OT info.
if (!(tr.ID32 == ID32 && tr.Gender == OriginalTrainerGender && tr.OT == OriginalTrainerName))
if (!BelongsTo(tr))
return false;
CurrentHandler = 0;

View file

@ -7,7 +7,7 @@ namespace PKHeX.Core;
/// <summary> Generation 5 <see cref="PKM"/> format. </summary>
public sealed class PK5 : PKM, ISanityChecksum,
IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetUnique3, IRibbonSetUnique4, IRibbonSetCommon3, IRibbonSetCommon4, IRibbonSetRibbons,
IContestStats, IGroundTile, IAppliedMarkings4
IContestStats, IGroundTile, IAppliedMarkings4, IHandlerUpdate
{
public override ReadOnlySpan<ushort> ExtraBytes =>
[
@ -297,14 +297,29 @@ public sealed class PK5 : PKM, ISanityChecksum,
}
// Synthetic Trading Logic
public bool Trade(string SAV_Trainer, uint savID32, int SAV_GENDER, int Day = 1, int Month = 1, int Year = 2013)
public bool BelongsTo(ITrainerInfo tr)
{
if (IsEgg && !(SAV_Trainer == OriginalTrainerName && savID32 == ID32 && SAV_GENDER == OriginalTrainerGender))
if (tr.Version != Version)
return false;
if (tr.ID32 != ID32)
return false;
if (tr.Gender != OriginalTrainerGender)
return false;
return tr.OT == OriginalTrainerName;
}
public void UpdateHandler(ITrainerInfo tr)
{
if (IsEgg)
{
SetLinkTradeEgg(Day, Month, Year, Locations.LinkTrade5);
return true;
// Don't bother updating eggs that were already traded.
const ushort location = Locations.LinkTrade5;
if (MetLocation != location && !BelongsTo(tr))
{
var date = EncounterDate.GetDateNDS();
SetLinkTradeEgg(date.Day, date.Month, date.Year, location);
}
}
return false;
}
public int MarkingCount => 6;

View file

@ -413,7 +413,7 @@ public sealed class PK6 : G6PKM, IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetC
protected override bool TradeOT(ITrainerInfo tr)
{
// Check to see if the OT matches the SAV's OT info.
if (!(tr.ID32 == ID32 && tr.Gender == OriginalTrainerGender && tr.OT == OriginalTrainerName))
if (!BelongsTo(tr))
return false;
CurrentHandler = 0;

View file

@ -477,7 +477,7 @@ public sealed class PK7 : G6PKM, IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetC
protected override bool TradeOT(ITrainerInfo tr)
{
// Check to see if the OT matches the SAV's OT info.
if (!(tr.ID32 == ID32 && tr.Gender == OriginalTrainerGender && tr.OT == OriginalTrainerName))
if (!BelongsTo(tr))
return false;
CurrentHandler = 0;

View file

@ -4,7 +4,7 @@ using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core;
/// <summary> Generation 8 <see cref="PKM"/> format. </summary>
public sealed class PK8 : G8PKM
public sealed class PK8 : G8PKM, IHandlerUpdate
{
public override ReadOnlySpan<ushort> ExtraBytes =>
[
@ -35,14 +35,30 @@ public sealed class PK8 : G8PKM
public PK8(byte[] data) : base(data) { }
public override PK8 Clone() => new((byte[])Data.Clone());
public void Trade(ITrainerInfo tr, int Day = 1, int Month = 1, int Year = 2015)
// Synthetic Trading Logic
public bool BelongsTo(ITrainerInfo tr)
{
if (tr.Version != Version)
return false;
if (tr.ID32 != ID32)
return false;
if (tr.Gender != OriginalTrainerGender)
return false;
return tr.OT == OriginalTrainerName;
}
public void UpdateHandler(ITrainerInfo tr)
{
if (IsEgg)
{
// Eggs do not have any modifications done if they are traded
// Apply link trade data, only if it left the OT (ignore if dumped & imported, or cloned, etc.)
if ((tr.TID16 != TID16) || (tr.SID16 != SID16) || (tr.Gender != OriginalTrainerGender) || (tr.OT != OriginalTrainerName))
SetLinkTradeEgg(Day, Month, Year, Locations.LinkTrade6);
const ushort location = Locations.LinkTrade6;
if (MetLocation != location && !BelongsTo(tr))
{
var date = EncounterDate.GetDateSwitch();
SetLinkTradeEgg(date.Day, date.Month, date.Year, location);
}
return;
}
@ -57,28 +73,31 @@ public sealed class PK8 : G8PKM
{
if (IsEgg) // No memories if is egg.
{
HandlingTrainerMemoryVariable = HandlingTrainerFriendship = HandlingTrainerMemory = HandlingTrainerMemoryIntensity = HandlingTrainerMemoryFeeling = HandlingTrainerLanguage = 0;
/* OriginalTrainerFriendship */ OriginalTrainerMemoryVariable = OriginalTrainerMemory = OriginalTrainerMemoryIntensity = OriginalTrainerMemoryFeeling = 0;
// Clear Handler
this.ClearMemoriesOT();
this.ClearMemoriesHT();
HandlingTrainerGender = HandlingTrainerFriendship = HandlingTrainerLanguage = 0;
HandlingTrainerTrash.Clear();
return;
}
if (IsUntraded)
HandlingTrainerMemoryVariable = HandlingTrainerFriendship = HandlingTrainerMemory = HandlingTrainerMemoryIntensity = HandlingTrainerMemoryFeeling = HandlingTrainerLanguage = 0;
var gen = Generation;
if (gen < 6)
OriginalTrainerMemoryVariable = OriginalTrainerMemory = OriginalTrainerMemoryIntensity = OriginalTrainerMemoryFeeling = 0;
if (gen != 8) // must be transferred via HOME, and must have memories
this.SetTradeMemoryHT8(); // not faking HOME tracker.
{
this.ClearMemoriesHT();
HandlingTrainerGender = HandlingTrainerFriendship = HandlingTrainerLanguage = 0;
HandlingTrainerTrash.Clear();
}
else
{
var gen = Generation;
if (gen < 6)
this.ClearMemoriesOT();
}
}
private bool TradeOT(ITrainerInfo tr)
{
// Check to see if the OT matches the SAV's OT info.
if (!(tr.ID32 == ID32 && tr.Gender == OriginalTrainerGender && tr.OT == OriginalTrainerName))
if (!BelongsTo(tr))
return false;
CurrentHandler = 0;

View file

@ -5,7 +5,7 @@ using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core;
/// <summary> Generation 9 <see cref="PKM"/> format. </summary>
public sealed class PK9 : PKM, ISanityChecksum, ITeraType, ITechRecord, IObedienceLevel,
public sealed class PK9 : PKM, ISanityChecksum, ITeraType, ITechRecord, IObedienceLevel, IHandlerUpdate,
IContestStats, IHyperTrain, IScaledSize, IScaledSize3, IFavorite, IHandlerLanguage, IFormArgument, IHomeTrack, IBattleVersion, ITrainerMemories, IAppliedMarkings7,
IRibbonIndex, IRibbonSetAffixed, IRibbonSetRibbons, IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetCommon3, IRibbonSetCommon4, IRibbonSetCommon6, IRibbonSetMemory6, IRibbonSetCommon7, IRibbonSetCommon8, IRibbonSetCommon9, IRibbonSetMarks, IRibbonSetMark8, IRibbonSetMark9
{
@ -549,34 +549,30 @@ public sealed class PK9 : PKM, ISanityChecksum, ITeraType, ITechRecord, IObedien
return 0x40 + (index >> 3);
}
public void Trade(ITrainerInfo tr, int Day = 1, int Month = 1, int Year = 2015)
// Synthetic Trading Logic
public bool BelongsTo(ITrainerInfo tr)
{
if (tr.Version != Version)
return false;
return BelongsToSkipVersion(tr);
}
public bool BelongsToSkipVersion(ITrainerInfo tr)
{
if (tr.ID32 != ID32)
return false;
if (tr.Gender != OriginalTrainerGender)
return false;
if (tr.Language != Language)
return false;
return tr.OT == OriginalTrainerName;
}
public void UpdateHandler(ITrainerInfo tr)
{
if (IsEgg)
{
if (EggLocation == 60005 && tr.Gender == OriginalTrainerGender && tr.Language == Language && tr.OT == OriginalTrainerName)
return; // Jacq gift, don't change.
// Apply link trade data, only if it left the OT (ignore if dumped & imported, or cloned, etc.)
// If not matching the trainer details, mark as a traded egg.
if (!IsTradedEgg && tr.Gender == OriginalTrainerGender && tr.Language == Language && tr.OT == OriginalTrainerName)
{
OriginalTrainerTrash.Clear();
NicknameTrash.Clear();
HandlingTrainerTrash.Clear();
CurrentHandler = 0;
Language = tr.Language;
Nickname = SpeciesName.GetEggName(tr.Language, 9);
OriginalTrainerName = tr.OT;
HandlingTrainerLanguage = 0;
}
else
{
HandlingTrainerName = tr.OT;
HandlingTrainerGender = tr.Gender;
HandlingTrainerLanguage = (byte)tr.Language;
SetLinkTradeEgg(Day, Month, Year, Locations.LinkTrade6);
CurrentHandler = 1;
}
UpdateHandlerEgg(tr);
return;
}
@ -585,33 +581,79 @@ public sealed class PK9 : PKM, ISanityChecksum, ITeraType, ITechRecord, IObedien
TradeHT(tr);
}
private void UpdateHandlerEgg(ITrainerInfo tr)
{
bool belongs = BelongsToSkipVersion(tr);
if (EggLocation == 60005 && belongs)
return; // Jacq gift, don't change.
// Apply link trade data, only if it left the OT (ignore if dumped & imported, or cloned, etc.)
// If not matching the trainer details, mark as a traded egg.
// If it's the OT's, be nice and reset the data.
if (belongs)
SetHandlerEggOT(tr);
else
SetHandlerEggTraded(tr);
}
private void SetHandlerEggOT(ITrainerInfo tr)
{
// Reset back to the OT.
OriginalTrainerTrash.Clear();
NicknameTrash.Clear();
HandlingTrainerTrash.Clear();
HandlingTrainerGender = 0;
HandlingTrainerLanguage = 0;
Nickname = SpeciesName.GetEggName(tr.Language, 9);
OriginalTrainerName = tr.OT;
CurrentHandler = 0;
}
private void SetHandlerEggTraded(ITrainerInfo tr)
{
HandlingTrainerName = tr.OT;
HandlingTrainerGender = tr.Gender;
HandlingTrainerLanguage = (byte)tr.Language;
var date = EncounterDate.GetDateSwitch();
SetLinkTradeEgg(date.Day, date.Month, date.Year, Locations.LinkTrade6);
CurrentHandler = 1;
}
public void FixMemories()
{
if (IsEgg) // No memories if is egg.
{
// HT_Language is set for eggs
HandlingTrainerMemoryVariable = HandlingTrainerFriendship = HandlingTrainerMemory = HandlingTrainerMemoryIntensity = HandlingTrainerMemoryFeeling = 0;
/* OriginalTrainerFriendship */
OriginalTrainerMemoryVariable = OriginalTrainerMemory = OriginalTrainerMemoryIntensity = OriginalTrainerMemoryFeeling = 0;
this.ClearMemoriesOT();
this.ClearMemoriesHT();
HandlingTrainerGender = 0;
HandlingTrainerFriendship = 0;
HandlingTrainerTrash.Clear();
return;
}
if (IsUntraded)
{
// HT_Language is set for gifts
// Skip clearing that.
HandlingTrainerMemoryVariable = HandlingTrainerFriendship = HandlingTrainerMemory = HandlingTrainerMemoryIntensity = HandlingTrainerMemoryFeeling = 0;
// HT_Language is set for gifts -- skip clearing that.
this.ClearMemoriesHT();
HandlingTrainerGender = 0;
HandlingTrainerFriendship = 0;
HandlingTrainerTrash.Clear();
}
else
{
var gen = Generation;
if (gen < 6)
this.ClearMemoriesOT();
}
var gen = Generation;
if (gen < 6)
OriginalTrainerMemoryVariable = OriginalTrainerMemory = OriginalTrainerMemoryIntensity = OriginalTrainerMemoryFeeling = 0;
}
private bool TradeOT(ITrainerInfo tr)
{
// Check to see if the OT matches the SAV's OT info.
if (!(tr.ID32 == ID32 && tr.Gender == OriginalTrainerGender && tr.OT == OriginalTrainerName))
if (!BelongsTo(tr))
return false;
CurrentHandler = 0;

View file

@ -797,13 +797,13 @@ public abstract class PKM : ISpeciesForm, ITrainerID32, IGeneration, IShiny, ILa
/// </summary>
/// <param name="day">Day the <see cref="PKM"/> was traded.</param>
/// <param name="month">Month the <see cref="PKM"/> was traded.</param>
/// <param name="y">Day the <see cref="PKM"/> was traded.</param>
/// <param name="year">Day the <see cref="PKM"/> was traded.</param>
/// <param name="location">Link Trade location value.</param>
protected void SetLinkTradeEgg(int day, int month, int y, ushort location)
protected void SetLinkTradeEgg(int day, int month, int year, ushort location)
{
MetDay = (byte)day;
MetMonth = (byte)month;
MetYear = (byte)(y - 2000);
MetYear = (byte)(year - 2000);
MetLocation = location;
}

View file

@ -5,7 +5,7 @@ using System.Runtime.InteropServices;
namespace PKHeX.Core;
/// <summary> Generation 4 <see cref="PKM"/> format. </summary>
public abstract class G4PKM : PKM,
public abstract class G4PKM : PKM, IHandlerUpdate,
IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetUnique3, IRibbonSetUnique4, IRibbonSetCommon3, IRibbonSetCommon4, IRibbonSetRibbons, IContestStats, IGroundTile, IAppliedMarkings4
{
protected G4PKM(byte[] data) : base(data) { }
@ -279,15 +279,29 @@ public abstract class G4PKM : PKM,
}
// Synthetic Trading Logic
public bool Trade(string SAV_Trainer, uint savID32, int SAV_GENDER, int Day = 1, int Month = 1, int Year = 2009)
public bool BelongsTo(ITrainerInfo tr)
{
// Eggs do not have any modifications done if they are traded
if (IsEgg && !(SAV_Trainer == OriginalTrainerName && savID32 == ID32 && SAV_GENDER == OriginalTrainerGender))
if (tr.Version != Version)
return false;
if (tr.ID32 != ID32)
return false;
if (tr.Gender != OriginalTrainerGender)
return false;
return tr.OT == OriginalTrainerName;
}
public void UpdateHandler(ITrainerInfo tr)
{
if (IsEgg)
{
SetLinkTradeEgg(Day, Month, Year, Locations.LinkTrade4);
return true;
// Don't bother updating eggs that were already traded.
const ushort location = Locations.LinkTrade4;
if (MetLocation != location && !BelongsTo(tr))
{
var date = EncounterDate.GetDateNDS();
SetLinkTradeEgg(date.Day, date.Month, date.Year, location);
}
}
return false;
}
// Enforce D/P content only (no Pt or HG/SS)

View file

@ -4,7 +4,7 @@ using System.Diagnostics.CodeAnalysis;
namespace PKHeX.Core;
/// <summary> Generation 6 <see cref="PKM"/> format. </summary>
public abstract class G6PKM : PKM, ISanityChecksum
public abstract class G6PKM : PKM, ISanityChecksum, IHandlerUpdate
{
public override int SIZE_PARTY => PokeCrypto.SIZE_6PARTY;
public override int SIZE_STORED => PokeCrypto.SIZE_6STORED;
@ -79,20 +79,41 @@ public abstract class G6PKM : PKM, ISanityChecksum
}
// Synthetic Trading Logic
public void Trade(ITrainerInfo tr, int Day = 1, int Month = 1, int Year = 2015)
public bool BelongsTo(ITrainerInfo tr)
{
if (tr.Version != Version)
return false;
if (tr.ID32 != ID32)
return false;
if (tr.Gender != OriginalTrainerGender)
return false;
return tr.OT == OriginalTrainerName;
}
public void UpdateHandler(ITrainerInfo tr)
{
if (IsEgg)
{
// Eggs do not have any modifications done if they are traded
// Apply link trade data, only if it left the OT (ignore if dumped & imported, or cloned, etc.)
if ((tr.TID16 != TID16) || (tr.SID16 != SID16) || (tr.Gender != OriginalTrainerGender) || (tr.OT != OriginalTrainerName))
SetLinkTradeEgg(Day, Month, Year, Locations.LinkTrade6);
const ushort location = Locations.LinkTrade6;
if (MetLocation != location && !BelongsTo(tr))
{
var date = EncounterDate.GetDate3DS();
SetLinkTradeEgg(date.Day, date.Month, date.Year, location);
}
return;
}
// Process to the HT if the OT of the Pokémon does not match the SAV's OT info.
var handler = CurrentHandler;
if (!TradeOT(tr))
TradeHT(tr);
if (handler == CurrentHandler)
return; // Logic updated Friendship
// Copy over the Friendship Value only under certain circumstances
if (HasMove((int)Move.Return) || HasMove((int)Move.Frustration))
CurrentFriendship = OppositeFriendship;
}
protected abstract bool TradeOT(ITrainerInfo tr);

View file

@ -251,7 +251,16 @@ public static class EntitySorting
var currentFriendship = pk.CurrentFriendship;
if (currentFriendship == 255)
return 255;
var baseFriendship = pk.PersonalInfo.BaseFriendship;
var baseFriendship = GetInitialFriendship(pk);
return currentFriendship - baseFriendship;
}
private static byte GetInitialFriendship(PKM pk)
{
// Don't get too intricate with this, we generally want to know if it's been raised.
if (pk.WasEgg)
return EggStateLegality.EggHatchFriendshipGeneral;
return pk.PersonalInfo.BaseFriendship;
}
}

View file

@ -41,36 +41,6 @@ public static class TrainerInfoExtensions
o.CopyRegionOrigin(tr);
}
/// <summary>
/// Copies the <see cref="ITrainerInfo"/> data to the <see cref="PKM"/> object's Handling Trainer data.
/// </summary>
/// <param name="sav">Trainer Information</param>
/// <param name="pk">Pokémon to copy to</param>
/// <param name="force">If true, will overwrite the Handling Trainer Data even if it has not been traded.</param>
public static void ApplyHandlingTrainerInfo(this ITrainerInfo sav, PKM pk, bool force = false)
{
if (pk.Format == sav.Generation && !force)
return;
pk.HandlingTrainerName = sav.OT;
pk.HandlingTrainerGender = sav.Gender;
pk.HandlingTrainerFriendship = pk.OriginalTrainerFriendship;
pk.CurrentHandler = 1;
if (pk is IHandlerLanguage h)
h.HandlingTrainerLanguage = (byte)sav.Language;
if (pk is PK6 pk6 && sav is IRegionOrigin o)
{
pk6.Geo1_Country = o.Country;
pk6.Geo1_Region = o.Region;
pk6.SetTradeMemoryHT6(true);
}
else if (pk is PK8 pk8 && PersonalTable.SWSH.IsPresentInGame(pk.Species, pk.Form))
{
pk8.SetTradeMemoryHT8();
}
}
/// <summary>
/// Checks if the <see cref="ITrainerInfo"/> data matches the <see cref="PKM"/> object's Original Trainer data.
/// </summary>
@ -82,9 +52,6 @@ public static class TrainerInfoExtensions
if (pk.IsEgg)
return tr.IsFromTrainerEgg(pk);
if (tr.Version == GameVersion.Any)
return true;
if (!IsFromTrainerNoVersion(tr, pk))
return false;

View file

@ -347,9 +347,8 @@ public abstract class SAV4 : SaveFile, IEventFlag37
{
var pk4 = (PK4)pk;
// Apply to this Save File
var now = EncounterDate.GetDateNDS();
if (pk4.Trade(OT, ID32, Gender, now.Day, now.Month, now.Year))
pk.RefreshChecksum();
pk4.UpdateHandler(this);
pk.RefreshChecksum();
}
// Daycare

View file

@ -244,9 +244,8 @@ public sealed class SAV4BR : SaveFile
{
var pk4 = (BK4)pk;
// Apply to this Save File
var now = EncounterDate.GetDateNDS();
if (pk4.Trade(OT, ID32, Gender, now.Day, now.Month, now.Year))
pk.RefreshChecksum();
pk4.UpdateHandler(this);
pk.RefreshChecksum();
}
protected override void SetPartyValues(PKM pk, bool isParty)

View file

@ -24,6 +24,7 @@ public sealed class SAV4DP : SAV4Sinnoh
public override Zukan4 Dex { get; }
protected override SAV4 CloneInternal4() => State.Exportable ? new SAV4DP((byte[])Data.Clone()) : new SAV4DP();
public override GameVersion Version { get => GameVersion.DP; set { } }
public override PersonalTable4 Personal => PersonalTable.DP;
public override ReadOnlySpan<ushort> HeldItems => Legal.HeldItems_DP;
public override int MaxItemID => Legal.MaxItemID_4_DP;

View file

@ -25,6 +25,7 @@ public sealed class SAV4HGSS : SAV4
public override Zukan4 Dex { get; }
protected override SAV4 CloneInternal4() => State.Exportable ? new SAV4HGSS((byte[])Data.Clone()) : new SAV4HGSS();
public override GameVersion Version { get => GameVersion.HGSS; set { } }
public override PersonalTable4 Personal => PersonalTable.HGSS;
public override ReadOnlySpan<ushort> HeldItems => Legal.HeldItems_HGSS;
public override int MaxItemID => Legal.MaxItemID_4_HGSS;

View file

@ -23,6 +23,7 @@ public sealed class SAV4Pt : SAV4Sinnoh
public override Zukan4 Dex { get; }
protected override SAV4 CloneInternal4() => State.Exportable ? new SAV4Pt((byte[])Data.Clone()) : new SAV4Pt();
public override GameVersion Version { get => GameVersion.Pt; set { } }
public override PersonalTable4 Personal => PersonalTable.Pt;
public override ReadOnlySpan<ushort> HeldItems => Legal.HeldItems_Pt;
public override int MaxItemID => Legal.MaxItemID_4_Pt;

View file

@ -109,9 +109,8 @@ public abstract class SAV5 : SaveFile, ISaveBlock5BW, IEventFlag37
{
var pk5 = (PK5)pk;
// Apply to this Save File
var now = EncounterDate.GetDateNDS();
if (pk5.Trade(OT, ID32, Gender, now.Day, now.Month, now.Year))
pk.RefreshChecksum();
pk5.UpdateHandler(this);
pk5.RefreshChecksum();
}
// Player Data

View file

@ -111,15 +111,7 @@ public abstract class SAV6 : SAV_BEEF, ITrainerStatRecord, ISaveBlock6Core, IReg
{
PK6 pk6 = (PK6)pk;
// Apply to this Save File
int CT = pk6.CurrentHandler;
var now = EncounterDate.GetDate3DS();
pk6.Trade(this, now.Day, now.Month, now.Year);
if (CT != pk6.CurrentHandler) // Logic updated Friendship
{
// Copy over the Friendship Value only under certain circumstances
if (pk6.HasMove((int)Move.Return) || pk6.HasMove((int)Move.Frustration))
pk6.CurrentFriendship = pk6.OppositeFriendship;
}
pk6.UpdateHandler(this);
pk6.FormArgumentElapsed = pk6.FormArgumentMaximum = 0;
pk6.FormArgumentRemain = (byte)GetFormArgument(pk, isParty);

View file

@ -169,15 +169,7 @@ public abstract class SAV7 : SAV_BEEF, ITrainerStatRecord, ISaveBlock7Main, IReg
{
PK7 pk7 = (PK7)pk;
// Apply to this Save File
int CT = pk7.CurrentHandler;
var now = EncounterDate.GetDate3DS();
pk7.Trade(this, now.Day, now.Month, now.Year);
if (CT != pk7.CurrentHandler) // Logic updated Friendship
{
// Copy over the Friendship Value only under certain circumstances
if (pk7.HasMove((int)Move.Return) || pk7.HasMove((int)Move.Frustration))
pk7.CurrentFriendship = pk7.OppositeFriendship;
}
pk7.UpdateHandler(this);
pk7.FormArgumentElapsed = pk7.FormArgumentMaximum = 0;
pk7.FormArgumentRemain = (byte)GetFormArgument(pk);

View file

@ -96,8 +96,7 @@ public sealed class SAV7b : SAV_BEEF, ISaveBlock7b, IGameSync, IEventFlagArray
{
var pb7 = (PB7)pk;
// Apply to this Save File
var now = EncounterDate.GetDateSwitch();
pb7.Trade(this, now.Day, now.Month, now.Year);
pb7.UpdateHandler(this);
pb7.RefreshChecksum();
}

View file

@ -304,8 +304,7 @@ public sealed class SAV8BS : SaveFile, ISaveFileRevision, ITrainerStatRecord, IE
{
var pb8 = (PB8)pk;
// Apply to this Save File
var now = EncounterDate.GetDateSwitch();
pb8.Trade(this, now.Day, now.Month, now.Year);
pb8.UpdateHandler(this);
pb8.RefreshChecksum();
if (SetUpdateRecords != PKMImportSetting.Skip)

View file

@ -148,7 +148,7 @@ public sealed class SAV8LA : SaveFile, ISaveBlock8LA, ISCBlockArray, ISaveFileRe
{
var pa8 = (PA8)pk;
// Apply to this Save File
pa8.Trade(this);
pa8.UpdateHandler(this);
pa8.RefreshChecksum();
}

View file

@ -191,8 +191,7 @@ public sealed class SAV8SWSH : SaveFile, ISaveBlock8SWSH, ITrainerStatRecord, IS
{
PK8 pk8 = (PK8)pk;
// Apply to this Save File
var now = EncounterDate.GetDateSwitch();
pk8.Trade(this, now.Day, now.Month, now.Year);
pk8.UpdateHandler(this);
if (FormArgumentUtil.IsFormArgumentTypeDatePair(pk8.Species, pk8.Form))
{

View file

@ -197,8 +197,7 @@ public sealed class SAV9SV : SaveFile, ISaveBlock9Main, ISCBlockArray, ISaveFile
{
PK9 pk9 = (PK9)pk;
// Apply to this Save File
var now = EncounterDate.GetDateSwitch();
pk9.Trade(this, now.Day, now.Month, now.Year);
pk9.UpdateHandler(this);
if (FormArgumentUtil.IsFormArgumentTypeDatePair(pk9.Species, pk9.Form))
{

View file

@ -61,7 +61,7 @@ public abstract class SaveFile : ITrainerInfo, IGameValueLimit, IBoxDetailWallpa
#region Metadata & Limits
public virtual string MiscSaveInfo() => string.Empty;
public virtual bool IsVersionValid() => true;
public virtual GameVersion Version { get => default; set { } }
public abstract GameVersion Version { get; set; }
public abstract bool ChecksumsValid { get; }
public abstract string ChecksumInfo { get; }
public abstract byte Generation { get; }

View file

@ -9,6 +9,7 @@ public sealed class Bank3 : BulkStorage
{
public Bank3(byte[] data) : base(data, typeof(PK3), 0) => Version = GameVersion.RS;
public override GameVersion Version { get => GameVersion.RS; set { } }
public override PersonalTable3 Personal => PersonalTable.RS;
public override ReadOnlySpan<ushort> HeldItems => Legal.HeldItems_RS;
protected override Bank3 CloneInternal() => new((byte[])Data.Clone());

View file

@ -9,6 +9,7 @@ public sealed class Bank4 : BulkStorage
{
public Bank4(byte[] data) : base(data, typeof(PK4), 0) => Version = GameVersion.HGSS;
public override GameVersion Version { get => GameVersion.HGSS; set { } }
public override PersonalTable4 Personal => PersonalTable.HGSS;
public override ReadOnlySpan<ushort> HeldItems => Legal.HeldItems_HGSS;
protected override Bank4 CloneInternal() => new((byte[])Data.Clone());

View file

@ -11,6 +11,7 @@ public sealed class Bank7 : BulkStorage
{
public Bank7(byte[] data, Type t, [ConstantExpected] int start, int slotsPerBox = 30) : base(data, t, start, slotsPerBox) => Version = GameVersion.USUM;
public override GameVersion Version { get => GameVersion.USUM; set { } }
public override PersonalTable7 Personal => PersonalTable.USUM;
public override ReadOnlySpan<ushort> HeldItems => Legal.HeldItems_SM;
protected override Bank7 CloneInternal() => new((byte[])Data.Clone(), PKMType, BoxStart, SlotsPerBox);

View file

@ -13,7 +13,6 @@ public sealed class SAV4Ranch : BulkStorage, ISaveFileRevision
protected override int SIZE_STORED => PokeCrypto.SIZE_4RSTORED;
protected override int SIZE_PARTY => PokeCrypto.SIZE_4RSTORED;
public int MaxToyID => (int) ((SaveRevision == 0) ? RanchToyType.Poke_Ball : RanchToyType.Water);
public int SaveRevision => Version == GameVersion.DP ? 0 : 1;
public string SaveRevisionString => Version == GameVersion.DP ? "-DP" : "-Pt";
@ -46,9 +45,12 @@ public sealed class SAV4Ranch : BulkStorage, ISaveFileRevision
protected override bool IsSlotSwapProtected(int box, int slot) => IsSlotOverwriteProtected(box, slot);
public override bool IsPKMPresent(ReadOnlySpan<byte> data) => EntityDetection.IsPresentSAV4Ranch(data);
private readonly GameVersion _version;
public override GameVersion Version { get => _version; set { } }
public SAV4Ranch(byte[] data) : base(data, typeof(RK4), 0)
{
Version = Data.Length == SaveUtil.SIZE_G4RANCH_PLAT ? GameVersion.Pt : GameVersion.DP;
_version = Data.Length == SaveUtil.SIZE_G4RANCH_PLAT ? GameVersion.Pt : GameVersion.DP;
OT = GetString(Data.AsSpan(0x770, 0x12));