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