2022-06-18 18:04:24 +00:00
using System ;
2018-08-03 03:11:42 +00:00
using System.Collections.Generic ;
2022-01-03 05:35:59 +00:00
using static System . Buffers . Binary . BinaryPrimitives ;
2016-06-20 04:22:43 +00:00
2022-06-18 18:04:24 +00:00
namespace PKHeX.Core ;
/// <summary>
/// Mystery Gift Template File
/// </summary>
2023-01-22 04:02:33 +00:00
public abstract class MysteryGift : IEncounterable , IMoveset , IRelearn , ITrainerID32 , IFatefulEncounterReadOnly
2016-06-20 04:22:43 +00:00
{
2017-10-23 22:45:58 +00:00
/// <summary>
2022-06-18 18:04:24 +00:00
/// Determines whether or not the given length of bytes is valid for a mystery gift.
/// </summary>
/// <param name="len">Length, in bytes, of the data of which to determine validity.</param>
/// <returns>A boolean indicating whether or not the given length is valid for a mystery gift.</returns>
public static bool IsMysteryGift ( long len ) = > Sizes . Contains ( ( int ) len ) ;
private static readonly HashSet < int > Sizes = new ( ) { WA8 . Size , WB8 . Size , WC8 . Size , WC6Full . Size , WC6 . Size , PGF . Size , PGT . Size , PCD . Size } ;
/// <summary>
/// Converts the given data to a <see cref="MysteryGift"/>.
2017-10-23 22:45:58 +00:00
/// </summary>
2022-06-18 18:04:24 +00:00
/// <param name="data">Raw data of the mystery gift.</param>
/// <param name="ext">Extension of the file from which the <paramref name="data"/> was retrieved.</param>
/// <returns>An instance of <see cref="MysteryGift"/> representing the given data, or null if <paramref name="data"/> or <paramref name="ext"/> is invalid.</returns>
/// <remarks>This overload differs from <see cref="GetMysteryGift(byte[])"/> by checking the <paramref name="data"/>/<paramref name="ext"/> combo for validity. If either is invalid, a null reference is returned.</remarks>
2023-01-22 04:02:33 +00:00
public static DataMysteryGift ? GetMysteryGift ( byte [ ] data , ReadOnlySpan < char > ext ) = > data . Length switch
2016-06-20 04:22:43 +00:00
{
2023-01-29 03:22:31 +00:00
PGT . Size when Equals ( ext , ".pgt" ) = > new PGT ( data ) ,
PCD . Size when Equals ( ext , ".pcd" , ".wc4" ) = > new PCD ( data ) ,
PGF . Size when Equals ( ext , ".pgf" ) = > new PGF ( data ) ,
WC6 . Size when Equals ( ext , ".wc6" ) = > new WC6 ( data ) ,
WC7 . Size when Equals ( ext , ".wc7" ) = > new WC7 ( data ) ,
WB7 . Size when Equals ( ext , ".wb7" ) = > new WB7 ( data ) ,
WR7 . Size when Equals ( ext , ".wr7" ) = > new WR7 ( data ) ,
WC8 . Size when Equals ( ext , ".wc8" , ".wc8full" ) = > new WC8 ( data ) ,
WB8 . Size when Equals ( ext , ".wb8" ) = > new WB8 ( data ) ,
WA8 . Size when Equals ( ext , ".wa8" ) = > new WA8 ( data ) ,
WC9 . Size when Equals ( ext , ".wc9" ) = > new WC9 ( data ) ,
2022-06-18 18:04:24 +00:00
2023-01-29 03:22:31 +00:00
WB7 . SizeFull when Equals ( ext , ".wb7full" ) = > new WB7 ( data ) ,
WC6Full . Size when Equals ( ext , ".wc6full" ) = > new WC6Full ( data ) . Gift ,
WC7Full . Size when Equals ( ext , ".wc7full" ) = > new WC7Full ( data ) . Gift ,
2022-06-18 18:04:24 +00:00
_ = > null ,
} ;
2023-01-29 03:22:31 +00:00
private static bool Equals ( ReadOnlySpan < char > c , ReadOnlySpan < char > cmp ) = > c . Equals ( cmp , StringComparison . OrdinalIgnoreCase ) ;
private static bool Equals ( ReadOnlySpan < char > c , ReadOnlySpan < char > cmp1 , ReadOnlySpan < char > cmp2 ) = > Equals ( c , cmp1 ) | | Equals ( c , cmp2 ) ;
2022-06-18 18:04:24 +00:00
/// <summary>
/// Converts the given data to a <see cref="MysteryGift"/>.
/// </summary>
/// <param name="data">Raw data of the mystery gift.</param>
/// <returns>An instance of <see cref="MysteryGift"/> representing the given data, or null if <paramref name="data"/> is invalid.</returns>
public static DataMysteryGift ? GetMysteryGift ( byte [ ] data ) = > data . Length switch
{
PGT . Size = > new PGT ( data ) ,
PCD . Size = > new PCD ( data ) ,
PGF . Size = > new PGF ( data ) ,
WR7 . Size = > new WR7 ( data ) ,
WC8 . Size = > new WC8 ( data ) ,
WB8 . Size = > new WB8 ( data ) ,
2022-11-25 14:50:00 +00:00
// WA8/WC9: WA8 CardType >0 for wa8, 0 for wc9.
WA8 . Size = > data [ 0xF ] > 0 ? new WA8 ( data ) : new WC9 ( data ) ,
2022-06-18 18:04:24 +00:00
// WC6/WC7: Check year
WC6 . Size = > ReadUInt32LittleEndian ( data . AsSpan ( 0x4C ) ) / 10000 < 2000 ? new WC7 ( data ) : new WC6 ( data ) ,
// WC6Full/WC7Full: 0x205 has 3 * 0x46 for gen6, now only 2.
WC6Full . Size = > data [ 0x205 ] = = 0 ? new WC7Full ( data ) . Gift : new WC6Full ( data ) . Gift ,
_ = > null ,
} ;
public string Extension = > GetType ( ) . Name . ToLowerInvariant ( ) ;
public string FileName = > $"{CardHeader}.{Extension}" ;
public abstract int Generation { get ; }
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves
In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.
The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.
The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.
* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 23:15:27 +00:00
public abstract EntityContext Context { get ; }
2023-01-22 04:02:33 +00:00
public abstract bool FatefulEncounter { get ; }
2022-06-18 18:04:24 +00:00
public PKM ConvertToPKM ( ITrainerInfo tr ) = > ConvertToPKM ( tr , EncounterCriteria . Unrestricted ) ;
public abstract PKM ConvertToPKM ( ITrainerInfo tr , EncounterCriteria criteria ) ;
public abstract bool IsMatchExact ( PKM pk , EvoCriteria evo ) ;
protected abstract bool IsMatchDeferred ( PKM pk ) ;
protected abstract bool IsMatchPartial ( PKM pk ) ;
public EncounterMatchRating GetMatchRating ( PKM pk )
{
if ( IsMatchPartial ( pk ) )
return EncounterMatchRating . PartialMatch ;
if ( IsMatchDeferred ( pk ) )
return EncounterMatchRating . Deferred ;
return EncounterMatchRating . Match ;
2016-06-20 04:22:43 +00:00
}
2022-06-18 18:04:24 +00:00
/// <summary>
/// Creates a deep copy of the <see cref="MysteryGift"/> object data.
/// </summary>
public abstract MysteryGift Clone ( ) ;
/// <summary>
/// Gets a friendly name for the underlying <see cref="MysteryGift"/> type.
/// </summary>
public string Type = > GetType ( ) . Name ;
/// <summary>
/// Gets a friendly name for the underlying <see cref="MysteryGift"/> type for the <see cref="IEncounterable"/> interface.
/// </summary>
public string Name = > "Event Gift" ;
/// <summary>
/// Gets a friendly name for the underlying <see cref="MysteryGift"/> type for the <see cref="IEncounterable"/> interface.
/// </summary>
public string LongName = > $"{Name} ({Type})" ;
public virtual GameVersion Version
{
get = > GameUtil . GetVersion ( Generation ) ;
set { }
}
// Properties
2022-08-27 06:43:36 +00:00
public virtual ushort Species { get = > 0 ; set { } }
2022-06-18 18:04:24 +00:00
public abstract AbilityPermission Ability { get ; }
public abstract bool GiftUsed { get ; set ; }
public abstract string CardTitle { get ; set ; }
public abstract int CardID { get ; set ; }
public abstract bool IsItem { get ; set ; }
public abstract int ItemID { get ; set ; }
public abstract bool IsEntity { get ; set ; }
public virtual int Quantity { get = > 1 ; set { } }
public virtual bool Empty = > false ;
public virtual string CardHeader = > ( CardID > 0 ? $"Card #: {CardID:0000}" : "N/A" ) + $" - {CardTitle.Replace('\u3000',' ').Trim()}" ;
// Search Properties
2022-08-22 00:34:32 +00:00
public virtual Moveset Moves { get = > default ; set { } }
public virtual Moveset Relearn { get = > default ; set { } }
2022-06-18 18:04:24 +00:00
public virtual int [ ] IVs { get = > Array . Empty < int > ( ) ; set { } }
public virtual bool HasFixedIVs = > true ;
public virtual void GetIVs ( Span < int > value ) { }
public virtual bool IsShiny = > false ;
public virtual Shiny Shiny
{
get = > Shiny . Never ;
init = > throw new InvalidOperationException ( ) ;
}
public virtual bool IsEgg { get = > false ; set { } }
public virtual int HeldItem { get = > - 1 ; set { } }
public virtual int AbilityType { get = > - 1 ; set { } }
public abstract int Gender { get ; set ; }
2022-08-27 06:43:36 +00:00
public abstract byte Form { get ; set ; }
2023-01-22 04:02:33 +00:00
public abstract uint ID32 { get ; set ; }
public abstract ushort TID16 { get ; set ; }
public abstract ushort SID16 { get ; set ; }
2022-06-18 18:04:24 +00:00
public abstract string OT_Name { get ; set ; }
public abstract int Location { get ; set ; }
public abstract byte Level { get ; set ; }
public byte LevelMin = > Level ;
public byte LevelMax = > Level ;
public abstract int Ball { get ; set ; }
public virtual bool EggEncounter = > IsEgg ;
public abstract int EggLocation { get ; set ; }
protected virtual bool IsMatchEggLocation ( PKM pk )
{
var expect = EggEncounter ? EggLocation : pk is PB8 ? Locations . Default8bNone : 0 ;
return pk . Egg_Location = = expect ;
}
public Ball FixedBall = > ( Ball ) Ball ;
2023-01-22 04:02:33 +00:00
public TrainerIDFormat TrainerIDDisplayFormat = > this . GetTrainerIDFormat ( ) ;
public uint TrainerTID7 { get = > this . GetTrainerTID7 ( ) ; set = > this . SetTrainerTID7 ( value ) ; }
public uint TrainerSID7 { get = > this . GetTrainerSID7 ( ) ; set = > this . SetTrainerSID7 ( value ) ; }
public uint DisplayTID { get = > this . GetDisplayTID ( ) ; set = > this . SetDisplayTID ( value ) ; }
public uint DisplaySID { get = > this . GetDisplaySID ( ) ; set = > this . SetDisplaySID ( value ) ; }
2022-06-18 18:04:24 +00:00
/// <summary>
/// Checks if the <see cref="PKM"/> has the <see cref="move"/> in its current move list.
/// </summary>
2022-08-27 06:43:36 +00:00
public bool HasMove ( ushort move ) = > Moves . Contains ( move ) ;
2016-06-20 04:22:43 +00:00
}