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 )
{
data . AddLine ( GetInvalid ( string . Format ( LRibbonMarkingFInvalid_0 , mark ) ) ) ;
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 )
{
data . AddLine ( GetInvalid ( string . Format ( LRibbonMarkingFInvalid_0 , mark ) ) ) ;
return ;
}
hasOne = true ;
}
}
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
WC8 or EncounterEgg or EncounterTrade or EncounterSlot8GO
2021-01-02 01:08:49 +00:00
or EncounterStatic8U or EncounterStatic8N or EncounterStatic8ND or EncounterStatic8NC
or EncounterStatic8 { Gift : true }
or EncounterStatic8 { ScriptedNoMarks : true }
= > false ,
_ = > true ,
} ;
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(?).
2021-08-03 06:54:44 +00:00
if ( enc is not EncounterSlot8 s | | ! s . SlotType . CanEncounterViaCurry ( ) )
2021-02-15 06:25:59 +00:00
return false ;
var weather = s . Weather ;
if ( ( weather & AreaWeather8 . All ) = = 0 )
return false ;
if ( EncounterArea8 . IsWildArea ( s . Location ) )
2020-11-04 20:58:56 +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
{
// Fishing are only encounter slots, from the hidden table (not symbol).
if ( enc is not EncounterSlot8 s )
return false ;
2021-08-03 06:54:44 +00:00
return s . SlotType . CanEncounterViaFishing ( s . Weather ) ;
2021-02-15 06:25:59 +00:00
}
2020-09-28 01:19:10 +00:00
private void VerifyAffixedRibbonMark ( LegalityAnalysis data , IRibbonIndex m )
{
2020-12-22 01:12:39 +00:00
if ( m is not PK8 pk8 )
2020-09-28 01:19:10 +00:00
return ;
var affix = pk8 . AffixedRibbon ;
if ( affix = = - 1 ) // None
return ;
if ( ( byte ) affix > ( int ) RibbonIndex . MarkSlump )
2021-04-21 22:20:16 +00:00
{
2020-09-28 01:19:10 +00:00
data . AddLine ( GetInvalid ( string . Format ( LRibbonMarkingAffixedF_0 , affix ) ) ) ;
2021-04-21 22:20:16 +00:00
return ;
}
if ( pk8 . Species = = ( int ) Species . Shedinja & & data . EncounterOriginal . Species is not ( int ) Species . Shedinja )
{
VerifyShedinjaAffixed ( data , affix , pk8 ) ;
return ;
}
EnsureHasRibbon ( data , pk8 , affix ) ;
}
private void VerifyShedinjaAffixed ( LegalityAnalysis data , sbyte affix , PK8 pk8 )
{
// 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 )
{
2021-04-22 02:12:50 +00:00
if ( ! IsMarkValid ( ( RibbonIndex ) affix , pk8 , enc ) )
2021-04-21 22:20:16 +00:00
data . AddLine ( GetInvalid ( string . Format ( LRibbonMarkingAffixedF_0 , ( RibbonIndex ) affix ) ) ) ;
return ;
}
if ( enc . Generation < = 4 & & ( pk8 . Ball ! = ( int ) Ball . Poke | | IsMoveSetEvolvedShedinja ( pk8 ) ) )
{
// Evolved in a prior generation.
EnsureHasRibbon ( data , pk8 , affix ) ;
return ;
}
var clone = pk8 . Clone ( ) ;
clone . Species = ( int ) Species . Nincada ;
( ( IRibbonIndex ) clone ) . SetRibbon ( affix ) ;
2021-04-22 02:12:50 +00:00
var parse = RibbonVerifier . GetRibbonResults ( clone , enc ) ;
2021-04-21 22:20:16 +00:00
var expect = $"Ribbon{(RibbonIndex) affix}" ;
var name = RibbonStrings . GetName ( expect ) ;
bool invalid = parse . FirstOrDefault ( z = > z . Name = = name ) ? . Invalid = = true ;
var severity = invalid ? Severity . Invalid : Severity . Fishy ;
data . AddLine ( Get ( string . Format ( LRibbonMarkingAffixedF_0 , affix ) , severity ) ) ;
}
private static bool IsMoveSetEvolvedShedinja ( PK8 pk8 )
{
2021-04-22 02:12:50 +00:00
// Check for gen3/4 exclusive moves that are Ninjask glitch only.
2021-04-21 22:20:16 +00:00
if ( pk8 . HasMove ( ( int ) Move . Screech ) )
return true ;
if ( pk8 . HasMove ( ( int ) Move . SwordsDance ) )
return true ;
if ( pk8 . HasMove ( ( int ) Move . Slash ) )
return true ;
if ( pk8 . HasMove ( ( int ) Move . BatonPass ) )
return true ;
2021-04-22 02:12:50 +00:00
return pk8 . HasMove ( ( int ) Move . Agility ) & & ! pk8 . GetMoveRecordFlag ( 12 ) ; // TR12 (Agility)
2021-04-21 22:20:16 +00:00
}
2021-04-22 02:12:50 +00:00
private void EnsureHasRibbon ( LegalityAnalysis data , IRibbonIndex pk8 , sbyte affix )
2021-04-21 22:20:16 +00:00
{
var hasRibbon = pk8 . GetRibbonIndex ( ( RibbonIndex ) affix ) ;
if ( ! hasRibbon )
data . AddLine ( GetInvalid ( string . Format ( LRibbonMarkingAffixedF_0 , ( RibbonIndex ) affix ) ) ) ;
2020-09-28 01:19:10 +00:00
}
2020-04-06 23:32:23 +00:00
}
}