2020-12-25 00:36:50 +00:00
using System ;
using System.Collections.Generic ;
2020-06-27 18:06:28 +00:00
using System.Linq ;
namespace PKHeX.Core
{
2020-12-25 00:36:50 +00:00
public static class MoveListSuggest
2020-06-27 18:06:28 +00:00
{
2022-05-08 01:29:36 +00:00
private static int [ ] GetSuggestedMoves ( PKM pkm , EvoCriteria [ ] [ ] evoChains , MoveSourceType types , IEncounterTemplate enc )
2020-06-27 18:06:28 +00:00
{
if ( pkm . IsEgg & & pkm . Format < = 5 ) // pre relearn
return MoveList . GetBaseEggMoves ( pkm , pkm . Species , 0 , ( GameVersion ) pkm . Version , pkm . CurrentLevel ) ;
2021-03-14 23:16:55 +00:00
if ( types ! = MoveSourceType . None )
return GetValidMoves ( pkm , evoChains , types ) . Skip ( 1 ) . ToArray ( ) ; // skip move 0
// try to give current moves
if ( enc . Generation < = 2 )
2020-06-27 18:06:28 +00:00
{
2021-03-14 23:16:55 +00:00
var lvl = pkm . Format > = 7 ? pkm . Met_Level : pkm . CurrentLevel ;
var ver = enc . Version ;
return MoveLevelUp . GetEncounterMoves ( enc . Species , 0 , lvl , ver ) ;
}
2020-06-27 18:06:28 +00:00
2021-03-14 23:16:55 +00:00
if ( pkm . Species = = enc . Species )
{
return MoveLevelUp . GetEncounterMoves ( pkm . Species , pkm . Form , pkm . CurrentLevel , ( GameVersion ) pkm . Version ) ;
2020-06-27 18:06:28 +00:00
}
2020-12-22 05:24:16 +00:00
return GetValidMoves ( pkm , evoChains , types ) . Skip ( 1 ) . ToArray ( ) ; // skip move 0
2020-06-27 18:06:28 +00:00
}
2022-05-08 01:29:36 +00:00
private static IEnumerable < int > GetValidMoves ( PKM pkm , EvoCriteria [ ] [ ] evoChains , MoveSourceType types = MoveSourceType . ExternalSources , bool RemoveTransferHM = true )
2020-06-27 18:06:28 +00:00
{
GameVersion version = ( GameVersion ) pkm . Version ;
2021-11-24 06:58:41 +00:00
if ( ! pkm . IsMovesetRestricted ( ) )
2020-06-27 18:06:28 +00:00
version = GameVersion . Any ;
2021-07-23 04:25:15 +00:00
return GetValidMoves ( pkm , version , evoChains , types : types , RemoveTransferHM : RemoveTransferHM ) ;
2020-06-27 18:06:28 +00:00
}
2022-05-08 01:29:36 +00:00
private static IEnumerable < int > GetValidMoves ( PKM pkm , GameVersion version , EvoCriteria [ ] [ ] evoChains , MoveSourceType types = MoveSourceType . Reminder , bool RemoveTransferHM = true )
2020-06-27 18:06:28 +00:00
{
var r = new List < int > { 0 } ;
2020-12-22 05:24:16 +00:00
if ( types . HasFlagFast ( MoveSourceType . RelearnMoves ) & & pkm . Format > = 6 )
2020-06-27 18:06:28 +00:00
r . AddRange ( pkm . RelearnMoves ) ;
2020-12-11 04:42:30 +00:00
int start = pkm . Generation ;
2020-06-27 18:06:28 +00:00
if ( start < 0 )
start = pkm . Format ; // be generous instead of returning nothing
if ( pkm is IBattleVersion b )
2021-01-29 17:56:31 +00:00
start = Math . Max ( 0 , b . GetMinGeneration ( ) ) ;
2020-06-27 18:06:28 +00:00
for ( int generation = start ; generation < = pkm . Format ; generation + + )
{
var chain = evoChains [ generation ] ;
2022-05-08 01:29:36 +00:00
if ( chain . Length = = 0 )
2020-06-27 18:06:28 +00:00
continue ;
2021-07-23 04:25:15 +00:00
r . AddRange ( MoveList . GetValidMoves ( pkm , version , chain , generation , types : types , RemoveTransferHM : RemoveTransferHM ) ) ;
2020-06-27 18:06:28 +00:00
}
return r . Distinct ( ) ;
}
2020-12-25 00:36:50 +00:00
private static IEnumerable < int > AllSuggestedMoves ( this LegalityAnalysis analysis )
{
if ( ! analysis . Parsed )
return new int [ 4 ] ;
return analysis . GetSuggestedCurrentMoves ( ) ;
}
private static IEnumerable < int > AllSuggestedRelearnMoves ( this LegalityAnalysis analysis )
{
if ( ! analysis . Parsed )
return new int [ 4 ] ;
var pkm = analysis . pkm ;
var enc = analysis . EncounterMatch ;
return MoveList . GetValidRelearn ( pkm , enc . Species , enc . Form , ( GameVersion ) pkm . Version ) . ToArray ( ) ;
}
public static int [ ] GetSuggestedMovesAndRelearn ( this LegalityAnalysis analysis )
{
if ( ! analysis . Parsed )
return new int [ 4 ] ;
return analysis . AllSuggestedMoves ( ) . Concat ( analysis . AllSuggestedRelearnMoves ( ) ) . ToArray ( ) ;
}
/// <summary>
/// Gets four moves which can be learned depending on the input arguments.
/// </summary>
/// <param name="analysis">Parse information to generate a moveset for.</param>
/// <param name="types">Allowed move sources for populating the result array</param>
public static int [ ] GetSuggestedCurrentMoves ( this LegalityAnalysis analysis , MoveSourceType types = MoveSourceType . All )
{
if ( ! analysis . Parsed )
return new int [ 4 ] ;
var pkm = analysis . pkm ;
if ( pkm . IsEgg & & pkm . Format > = 6 )
return pkm . RelearnMoves ;
if ( pkm . IsEgg )
types = types . ClearNonEggSources ( ) ;
var info = analysis . Info ;
return GetSuggestedMoves ( pkm , info . EvoChainsAllGens , types , info . EncounterOriginal ) ;
}
2021-03-31 01:51:53 +00:00
/// <summary>
/// Gets the current <see cref="PKM.RelearnMoves"/> array of four moves that might be legal.
/// </summary>
2021-04-08 22:58:09 +00:00
/// <remarks>Use <see cref="GetSuggestedRelearnMovesFromEncounter"/> instead of calling directly; this method just puts default values in without considering the final moveset.</remarks>
2021-04-18 03:09:02 +00:00
public static IReadOnlyList < int > GetSuggestedRelearn ( this IEncounterTemplate enc , PKM pkm )
2021-03-31 01:51:53 +00:00
{
2021-04-17 20:13:03 +00:00
if ( VerifyRelearnMoves . ShouldNotHaveRelearnMoves ( enc , pkm ) )
2021-04-05 01:30:01 +00:00
return Empty ;
2021-03-31 01:51:53 +00:00
2021-04-08 22:58:09 +00:00
return GetSuggestedRelearnInternal ( enc , pkm ) ;
2021-03-31 01:51:53 +00:00
}
2021-04-08 22:58:09 +00:00
// Invalid encounters won't be recognized as an EncounterEgg; check if it *should* be a bred egg.
private static IReadOnlyList < int > GetSuggestedRelearnInternal ( this IEncounterTemplate enc , PKM pkm ) = > enc switch
{
IRelearn s when s . Relearn . Count > 0 = > s . Relearn ,
EncounterEgg or EncounterInvalid { EggEncounter : true } = > MoveBreed . GetExpectedMoves ( pkm . RelearnMoves , enc ) ,
_ = > Empty ,
} ;
2021-04-05 01:30:01 +00:00
private static readonly IReadOnlyList < int > Empty = new int [ 4 ] ;
2020-12-25 00:36:50 +00:00
/// <summary>
/// Gets the current <see cref="PKM.RelearnMoves"/> array of four moves that might be legal.
/// </summary>
2021-04-10 18:03:21 +00:00
public static IReadOnlyList < int > GetSuggestedRelearnMovesFromEncounter ( this LegalityAnalysis analysis , IEncounterTemplate ? enc = null )
2020-12-25 00:36:50 +00:00
{
var info = analysis . Info ;
2021-04-10 18:03:21 +00:00
enc ? ? = info . EncounterOriginal ;
2021-04-08 22:58:09 +00:00
var pkm = analysis . pkm ;
2021-04-17 20:13:03 +00:00
if ( VerifyRelearnMoves . ShouldNotHaveRelearnMoves ( enc , pkm ) )
2021-04-08 22:58:09 +00:00
return Empty ;
if ( enc is EncounterEgg or EncounterInvalid { EggEncounter : true } )
2021-04-10 18:03:21 +00:00
return enc . GetSuggestedRelearnEgg ( info . Moves , pkm ) ;
2021-04-08 22:58:09 +00:00
return enc . GetSuggestedRelearnInternal ( pkm ) ;
2020-12-25 00:36:50 +00:00
}
2021-04-08 22:58:09 +00:00
2021-04-10 18:03:21 +00:00
private static IReadOnlyList < int > GetSuggestedRelearnEgg ( this IEncounterTemplate enc , IReadOnlyList < CheckMoveResult > parse , PKM pkm )
{
var result = enc . GetEggRelearnMoves ( parse , pkm ) ;
2021-04-18 03:09:02 +00:00
int generation = enc . Generation ;
2021-09-07 22:40:14 +00:00
if ( generation < = 5 ) // gen2 does not have splitbreed, <=5 do not have relearn moves and shouldn't even be here.
return result ;
// Split-breed species like Budew & Roselia may be legal for one, and not the other.
// If we're not a split-breed or are already legal, return.
2021-04-18 03:09:02 +00:00
var split = Breeding . GetSplitBreedGeneration ( generation ) ;
2021-09-07 22:40:14 +00:00
if ( ! split . Contains ( enc . Species ) )
2021-04-10 18:03:21 +00:00
return result ;
var tmp = pkm . Clone ( ) ;
tmp . SetRelearnMoves ( result ) ;
var la = new LegalityAnalysis ( tmp ) ;
if ( la . Info . Moves . All ( z = > z . Valid ) )
return result ;
// Try again with the other split-breed species if possible.
2021-04-18 03:09:02 +00:00
var incense = EncounterEggGenerator . GenerateEggs ( tmp , generation ) . FirstOrDefault ( ) ;
2021-04-10 18:03:21 +00:00
if ( incense is null | | incense . Species = = enc . Species )
return result ;
2021-09-07 22:40:14 +00:00
return incense . GetEggRelearnMoves ( parse , pkm ) ;
2021-04-10 18:03:21 +00:00
}
2021-04-08 22:58:09 +00:00
private static IReadOnlyList < int > GetEggRelearnMoves ( this IEncounterTemplate enc , IReadOnlyList < CheckMoveResult > parse , PKM pkm )
{
// Extract a list of the moves that should end up in the relearn move list.
int ctr = 0 ;
var moves = new int [ 4 ] ;
for ( var i = 0 ; i < parse . Count ; i + + )
{
var m = parse [ i ] ;
if ( ! m . ShouldBeInRelearnMoves ( ) )
continue ;
moves [ ctr + + ] = pkm . GetMove ( i ) ;
}
// Swap Volt Tackle to the end of the list.
int volt = Array . IndexOf ( moves , ( int ) Move . VoltTackle , 0 , ctr ) ;
if ( volt ! = - 1 )
{
var dest = ctr - 1 ;
moves [ volt ] = moves [ dest ] ;
moves [ dest ] = ( int ) Move . VoltTackle ;
}
return MoveBreed . GetExpectedMoves ( moves , enc ) ;
}
2020-06-27 18:06:28 +00:00
}
}