2021-04-21 22:20:16 +00:00
using System.Linq ;
using static PKHeX . Core . LegalityCheckStrings ;
2020-04-06 23:32:23 +00:00
namespace PKHeX.Core
{
/// <summary>
/// Verifies the <see cref="RibbonIndex"/> values for markings.
/// </summary>
public sealed class MarkVerifier : Verifier
{
protected override CheckIdentifier Identifier = > CheckIdentifier . RibbonMark ;
public override void Verify ( LegalityAnalysis data )
{
var pkm = data . pkm ;
2020-12-22 01:12:39 +00:00
if ( pkm is not IRibbonIndex m )
2020-04-06 23:32:23 +00:00
return ;
2021-04-21 22:20:16 +00:00
if ( data . Info . Generation ! = 8 | | ( pkm . Species = = ( int ) Species . Shedinja & & data . EncounterOriginal . Species is not ( int ) Species . Shedinja ) ) // Shedinja doesn't copy Ribbons or Marks
2020-04-06 23:32:23 +00:00
VerifyNoMarksPresent ( data , m ) ;
else
VerifyMarksPresent ( data , m ) ;
2020-09-28 01:19:10 +00:00
VerifyAffixedRibbonMark ( data , m ) ;
2020-04-06 23:32:23 +00:00
}
private void VerifyNoMarksPresent ( LegalityAnalysis data , IRibbonIndex m )
{
2021-01-23 05:17:41 +00:00
for ( var x = RibbonIndex . MarkLunchtime ; x < = RibbonIndex . MarkSlump ; x + + )
{
if ( m . GetRibbon ( ( int ) x ) )
data . AddLine ( GetInvalid ( string . Format ( LRibbonMarkingFInvalid_0 , x ) ) ) ;
}
2020-04-06 23:32:23 +00:00
}
private void VerifyMarksPresent ( LegalityAnalysis data , IRibbonIndex m )
{
bool hasOne = false ;
for ( var mark = RibbonIndex . MarkLunchtime ; mark < = RibbonIndex . MarkSlump ; mark + + )
{
2021-07-26 21:28:05 +00:00
bool has = m . GetRibbon ( ( int ) mark ) ;
2020-04-06 23:32:23 +00:00
if ( ! has )
continue ;
if ( hasOne )
{
2022-05-31 04:43:52 +00:00
data . AddLine ( GetInvalid ( string . Format ( LRibbonMarkingFInvalid_0 , GetRibbonNameSafe ( mark ) ) ) ) ;
2020-04-06 23:32:23 +00:00
return ;
}
2020-11-04 20:58:56 +00:00
bool result = IsMarkValid ( mark , data . pkm , data . EncounterMatch ) ;
2020-04-06 23:32:23 +00:00
if ( ! result )
{
2022-05-31 04:43:52 +00:00
data . AddLine ( GetInvalid ( string . Format ( LRibbonMarkingFInvalid_0 , GetRibbonNameSafe ( mark ) ) ) ) ;
2020-04-06 23:32:23 +00:00
return ;
}
hasOne = true ;
}
}
2022-05-31 04:43:52 +00:00
private static string GetRibbonNameSafe ( RibbonIndex index )
{
if ( index > = RibbonIndex . MAX_COUNT )
return index . ToString ( ) ;
var expect = $"Ribbon{index}" ;
return RibbonStrings . GetName ( expect ) ;
}
2021-07-14 00:33:23 +00:00
public static bool IsMarkValid ( RibbonIndex mark , PKM pk , IEncounterTemplate enc )
2020-04-06 23:32:23 +00:00
{
2020-11-04 20:58:56 +00:00
return IsMarkAllowedAny ( enc ) & & IsMarkAllowedSpecific ( mark , pk , enc ) ;
}
2021-07-14 00:33:23 +00:00
public static bool IsMarkAllowedSpecific ( RibbonIndex mark , PKM pk , IEncounterTemplate x ) = > mark switch
2020-11-04 20:58:56 +00:00
{
2021-02-15 05:24:31 +00:00
RibbonIndex . MarkCurry when ! IsMarkAllowedCurry ( pk , x ) = > false ,
2021-02-15 06:25:59 +00:00
RibbonIndex . MarkFishing when ! IsMarkAllowedFishing ( x ) = > false ,
2021-08-06 21:54:32 +00:00
RibbonIndex . MarkMisty when pk . Met_Level < EncounterArea8 . BoostLevel & & EncounterArea8 . IsBoostedArea60Fog ( pk . Met_Location ) = > false ,
2021-01-02 01:08:49 +00:00
RibbonIndex . MarkDestiny = > false ,
2021-07-26 21:28:05 +00:00
> = RibbonIndex . MarkCloudy and < = RibbonIndex . MarkMisty = > IsWeatherPermitted ( mark , x ) ,
2021-08-20 20:49:20 +00:00
_ = > true ,
2021-01-02 01:08:49 +00:00
} ;
2020-11-04 20:58:56 +00:00
2021-07-26 21:28:05 +00:00
private static bool IsWeatherPermitted ( RibbonIndex mark , IEncounterTemplate enc )
{
var permit = mark . GetWeather8 ( ) ;
// Encounter slots check location weather, while static encounters check weather per encounter.
return enc switch
{
EncounterSlot8 w = > IsSlotWeatherPermitted ( permit , w ) ,
EncounterStatic8 s = > s . Weather . HasFlag ( permit ) ,
_ = > false ,
} ;
}
private static bool IsSlotWeatherPermitted ( AreaWeather8 permit , EncounterSlot8 s )
{
var location = s . Location ;
// If it's not in the main table, it can only have Normal weather.
if ( ! EncounterArea8 . WeatherbyArea . TryGetValue ( location , out var weather ) )
weather = AreaWeather8 . Normal ;
if ( weather . HasFlag ( permit ) )
return true ;
// Valid tree/fishing weathers should have returned with main area weather.
if ( ( s . Weather & ( AreaWeather8 . Shaking_Trees | AreaWeather8 . Fishing ) ) ! = 0 )
return false ;
// Check bleed conditions otherwise.
return EncounterArea8 . IsWeatherBleedPossible ( s . SlotType , permit , location ) ;
}
2021-07-14 00:33:23 +00:00
public static bool IsMarkAllowedAny ( IEncounterTemplate enc ) = > enc . Generation = = 8 & & enc switch
2020-11-04 20:58:56 +00:00
{
2021-02-02 04:34:04 +00:00
// Gen 8
2022-05-03 02:27:22 +00:00
EncounterSlot8 or EncounterStatic8 { Gift : false , ScriptedNoMarks : false } = > true ,
_ = > false ,
2021-01-02 01:08:49 +00:00
} ;
2020-11-04 20:58:56 +00:00
2021-07-14 00:33:23 +00:00
public static bool IsMarkAllowedCurry ( PKM pkm , IEncounterTemplate enc )
2020-11-04 20:58:56 +00:00
{
2021-02-15 06:25:59 +00:00
// Curry are only encounter slots, from the hidden table (not symbol). Slots taken from area's current weather(?).
2022-05-03 02:27:22 +00:00
if ( enc is not EncounterSlot8 { CanEncounterViaCurry : true } )
2021-02-15 06:25:59 +00:00
return false ;
2021-02-15 05:24:31 +00:00
var ball = pkm . Ball ;
return ( uint ) ( ball - 2 ) < = 2 ;
2020-04-06 23:32:23 +00:00
}
2020-09-28 01:19:10 +00:00
2021-07-14 00:33:23 +00:00
public static bool IsMarkAllowedFishing ( IEncounterTemplate enc )
2021-02-15 06:25:59 +00:00
{
2022-05-03 02:27:22 +00:00
return enc is EncounterSlot8 { CanEncounterViaFishing : true } ;
2021-02-15 06:25:59 +00:00
}
2020-09-28 01:19:10 +00:00
private void VerifyAffixedRibbonMark ( LegalityAnalysis data , IRibbonIndex m )
{
2022-05-31 04:43:52 +00:00
if ( m is not IRibbonSetAffixed a )
2020-09-28 01:19:10 +00:00
return ;
2022-05-31 04:43:52 +00:00
var affix = a . AffixedRibbon ;
2020-09-28 01:19:10 +00:00
if ( affix = = - 1 ) // None
return ;
2022-05-31 04:43:52 +00:00
if ( ( byte ) affix > ( int ) RibbonIndex . MarkSlump ) // SW/SH cannot affix anything higher.
2021-04-21 22:20:16 +00:00
{
2022-05-31 04:43:52 +00:00
data . AddLine ( GetInvalid ( string . Format ( LRibbonMarkingAffixedF_0 , GetRibbonNameSafe ( ( RibbonIndex ) affix ) ) ) ) ;
2021-04-21 22:20:16 +00:00
return ;
}
2022-05-31 04:43:52 +00:00
if ( m is not PKM pk )
return ;
if ( pk . Species = = ( int ) Species . Shedinja & & data . EncounterOriginal . Species is not ( int ) Species . Shedinja )
2021-04-21 22:20:16 +00:00
{
2022-05-31 04:43:52 +00:00
VerifyShedinjaAffixed ( data , affix , pk , m ) ;
2021-04-21 22:20:16 +00:00
return ;
}
2022-05-31 04:43:52 +00:00
EnsureHasRibbon ( data , m , affix ) ;
2021-04-21 22:20:16 +00:00
}
2022-05-31 04:43:52 +00:00
private void VerifyShedinjaAffixed ( LegalityAnalysis data , sbyte affix , PKM pk , IRibbonIndex r )
2021-04-21 22:20:16 +00:00
{
// Does not copy ribbons or marks, but retains the Affixed Ribbon value.
// Try re-verifying to see if it could have had the Ribbon/Mark.
2021-04-22 02:12:50 +00:00
var enc = data . EncounterOriginal ;
2021-04-21 22:20:16 +00:00
if ( ( byte ) affix > = ( int ) RibbonIndex . MarkLunchtime )
{
2022-05-31 04:43:52 +00:00
if ( ! IsMarkValid ( ( RibbonIndex ) affix , pk , enc ) )
data . AddLine ( GetInvalid ( string . Format ( LRibbonMarkingAffixedF_0 , GetRibbonNameSafe ( ( RibbonIndex ) affix ) ) ) ) ;
2021-04-21 22:20:16 +00:00
return ;
}
2022-05-31 04:43:52 +00:00
if ( enc . Generation < = 4 & & ( pk . Ball ! = ( int ) Ball . Poke | | IsMoveSetEvolvedShedinja ( pk ) ) )
2021-04-21 22:20:16 +00:00
{
// Evolved in a prior generation.
2022-05-31 04:43:52 +00:00
EnsureHasRibbon ( data , r , affix ) ;
2021-04-21 22:20:16 +00:00
return ;
}
2022-05-31 04:43:52 +00:00
var clone = pk . Clone ( ) ;
2021-04-21 22:20:16 +00:00
clone . Species = ( int ) Species . Nincada ;
( ( IRibbonIndex ) clone ) . SetRibbon ( affix ) ;
2021-11-20 02:23:49 +00:00
var parse = RibbonVerifier . GetRibbonResults ( clone , data . Info . EvoChainsAllGens , enc ) ;
2022-05-31 04:43:52 +00:00
var name = GetRibbonNameSafe ( ( RibbonIndex ) affix ) ;
2021-04-21 22:20:16 +00:00
bool invalid = parse . FirstOrDefault ( z = > z . Name = = name ) ? . Invalid = = true ;
var severity = invalid ? Severity . Invalid : Severity . Fishy ;
2022-05-31 04:43:52 +00:00
data . AddLine ( Get ( string . Format ( LRibbonMarkingAffixedF_0 , name ) , severity ) ) ;
2021-04-21 22:20:16 +00:00
}
2022-05-31 04:43:52 +00:00
private static bool IsMoveSetEvolvedShedinja ( PKM pk )
2021-04-21 22:20:16 +00:00
{
2021-04-22 02:12:50 +00:00
// Check for gen3/4 exclusive moves that are Ninjask glitch only.
2022-05-31 04:43:52 +00:00
if ( pk . HasMove ( ( int ) Move . Screech ) )
2021-04-21 22:20:16 +00:00
return true ;
2022-05-31 04:43:52 +00:00
if ( pk . HasMove ( ( int ) Move . SwordsDance ) )
2021-04-21 22:20:16 +00:00
return true ;
2022-05-31 04:43:52 +00:00
if ( pk . HasMove ( ( int ) Move . Slash ) )
2021-04-21 22:20:16 +00:00
return true ;
2022-05-31 04:43:52 +00:00
if ( pk . HasMove ( ( int ) Move . BatonPass ) )
2021-04-21 22:20:16 +00:00
return true ;
2022-05-31 04:43:52 +00:00
return pk . HasMove ( ( int ) Move . Agility ) & & pk is PK8 pk8 & & ! pk8 . GetMoveRecordFlag ( 12 ) ; // TR12 (Agility)
2021-04-21 22:20:16 +00:00
}
2022-05-31 04:43:52 +00:00
private void EnsureHasRibbon ( LegalityAnalysis data , IRibbonIndex m , sbyte affix )
2021-04-21 22:20:16 +00:00
{
2022-05-31 04:43:52 +00:00
var hasRibbon = m . GetRibbonIndex ( ( RibbonIndex ) affix ) ;
2021-04-21 22:20:16 +00:00
if ( ! hasRibbon )
2022-05-31 04:43:52 +00:00
data . AddLine ( GetInvalid ( string . Format ( LRibbonMarkingAffixedF_0 , GetRibbonNameSafe ( ( RibbonIndex ) affix ) ) ) ) ;
2020-09-28 01:19:10 +00:00
}
2020-04-06 23:32:23 +00:00
}
}