2017-05-12 16:41:21 +00:00
using System.Collections.Generic ;
namespace PKHeX.Core
{
2017-05-14 21:42:18 +00:00
public static class FrameFinder
2017-05-12 16:41:21 +00:00
{
2017-05-14 19:42:27 +00:00
/// <summary>
/// Checks a <see cref="PIDIV"/> to see if any encounter frames can generate the spread. Requires further filtering against matched Encounter Slots and generation patterns.
/// </summary>
/// <param name="pidiv">Matched <see cref="PIDIV"/> containing <see cref="PIDIV.RNG"/> info and <see cref="PIDIV.OriginSeed"/>.</param>
/// <param name="pk"><see cref="PKM"/> object containing various accessible information required for the encounter.</param>
2017-05-14 21:42:18 +00:00
/// <returns><see cref="IEnumerable{Frame}"/> to yield possible encounter details for further filtering</returns>
2017-06-18 01:37:19 +00:00
public static IEnumerable < Frame > GetFrames ( PIDIV pidiv , PKM pk )
2017-05-12 16:41:21 +00:00
{
2017-11-25 21:53:39 +00:00
if ( pidiv . RNG = = null )
yield break ;
2017-05-15 06:21:34 +00:00
FrameGenerator info = new FrameGenerator ( pidiv , pk ) ;
if ( info . FrameType = = FrameType . None )
2017-05-14 19:42:27 +00:00
yield break ;
2017-05-12 16:41:21 +00:00
2017-05-15 06:21:34 +00:00
info . Nature = pk . EncryptionConstant % 25 ;
// gather possible nature determination seeds until a same-nature PID breaks the unrolling
2017-06-18 01:37:19 +00:00
IEnumerable < SeedInfo > seeds = SeedInfo . GetSeedsUntilNature ( pidiv , info ) ;
2017-05-15 06:21:34 +00:00
var frames = pidiv . Type = = PIDType . CuteCharm
2017-06-18 01:37:19 +00:00
? FilterCuteCharm ( seeds , pidiv , info )
: FilterNatureSync ( seeds , pidiv , info ) ;
2017-05-15 06:21:34 +00:00
2017-06-18 01:37:19 +00:00
var refined = RefineFrames ( frames , info ) ;
2017-05-15 06:21:34 +00:00
foreach ( var z in refined )
yield return z ;
}
2017-06-18 01:37:19 +00:00
private static IEnumerable < Frame > RefineFrames ( IEnumerable < Frame > frames , FrameGenerator info )
2017-05-15 06:21:34 +00:00
{
return info . FrameType = = FrameType . MethodH
2017-06-18 01:37:19 +00:00
? RefineFrames3 ( frames , info )
: RefineFrames4 ( frames , info ) ;
2017-05-15 06:21:34 +00:00
}
2017-05-14 21:42:18 +00:00
2017-06-18 01:37:19 +00:00
private static IEnumerable < Frame > RefineFrames3 ( IEnumerable < Frame > frames , FrameGenerator info )
2017-05-15 06:21:34 +00:00
{
2017-11-26 22:48:55 +00:00
// ESV
// Level
// Nature
// Current Seed of the frame is the Level Calc (frame before nature)
2017-05-15 06:21:34 +00:00
var list = new List < Frame > ( ) ;
foreach ( var f in frames )
2017-05-12 16:41:21 +00:00
{
2017-05-15 06:21:34 +00:00
// Current Seed of the frame is the Level Calc
var prev = info . RNG . Prev ( f . Seed ) ; // ESV
var rand = prev > > 16 ;
{
f . ESV = rand ;
yield return f ;
}
if ( f . Lead ! = LeadRequired . None | | ! info . AllowLeads ) // Emerald
continue ;
// Generate frames for other slots after the regular slots
list . Add ( f ) ;
2017-05-12 16:41:21 +00:00
}
2017-11-26 22:48:55 +00:00
// Check leads -- none in list if leads are not allowed
// Certain leads inject a RNG call
// 3 different rand places
2017-05-15 06:21:34 +00:00
foreach ( var f in list )
{
2017-11-26 22:48:55 +00:00
LeadRequired lead ;
var prev0 = f . Seed ; // 0
var prev1 = info . RNG . Prev ( f . Seed ) ; // -1
var prev2 = info . RNG . Prev ( prev1 ) ; // -2
// Modify Call values
var p0 = prev0 > > 16 ;
var p1 = prev1 > > 16 ;
var p2 = prev2 > > 16 ;
// Pressure, Hustle, Vital Spirit = Force Maximum Level from slot
// -2 ESV
// -1 Level
// 0 LevelMax proc (Random() & 1)
// 1 Nature
bool max = p0 % 2 = = 1 ;
lead = max ? LeadRequired . PressureHustleSpirit : LeadRequired . PressureHustleSpiritFail ;
yield return info . GetFrame ( prev2 , lead , p2 ) ;
// Keen Eye, Intimidate
// -2 ESV
// -1 Level
// 0 Level Adequate Check !(Random() % 2 == 1) rejects -- rand%2==1 is adequate
// 1 Nature
// Note: if this check fails, the encounter generation routine is aborted.
if ( max ) // same result as above, no need to recalculate
{
lead = LeadRequired . IntimidateKeenEye ;
yield return info . GetFrame ( prev2 , lead , p2 ) ;
}
2017-05-15 06:21:34 +00:00
2017-11-26 22:48:55 +00:00
// Cute Charm
// -2 ESV
// -1 CC Proc (Random() % 3 != 0)
// 0 Level
// 1 Nature
bool cc = p1 % 3 ! = 0 ;
lead = cc ? LeadRequired . CuteCharm : LeadRequired . CuteCharmFail ;
yield return info . GetFrame ( prev2 , lead , p2 ) ;
// Static or Magnet Pull
// -2 SlotProc (Random % 2 == 0)
// -1 ESV (select slot)
// 0 Level
// 1 Nature
bool force = p2 % 2 = = 0 ;
if ( force )
{
// Since a failed proc is indistinguishable from the default frame calls, only generate if it succeeds.
lead = LeadRequired . StaticMagnet ;
yield return info . GetFrame ( prev2 , lead , p1 ) ;
}
2017-05-15 06:21:34 +00:00
}
}
2017-06-18 01:37:19 +00:00
private static IEnumerable < Frame > RefineFrames4 ( IEnumerable < Frame > frames , FrameGenerator info )
2017-05-15 06:21:34 +00:00
{
var list = new List < Frame > ( ) ;
foreach ( var f in frames )
{
// Current Seed of the frame is the ESV.
var rand = f . Seed > > 16 ;
{
f . ESV = rand ;
yield return f ;
}
if ( f . Lead ! = LeadRequired . None )
continue ;
// Generate frames for other slots after the regular slots
list . Add ( f ) ;
}
foreach ( var f in list )
{
// Level Modifiers between ESV and Nature
var prev = info . RNG . Prev ( f . Seed ) ;
var p16 = prev > > 16 ;
2017-11-26 22:48:55 +00:00
yield return info . GetFrame ( prev , LeadRequired . IntimidateKeenEye , p16 ) ;
yield return info . GetFrame ( prev , LeadRequired . PressureHustleSpirit , p16 ) ;
2017-05-15 06:21:34 +00:00
// Slot Modifiers before ESV
var force = ( info . DPPt ? p16 > > 15 : p16 & 1 ) = = 1 ;
if ( ! force )
continue ;
var rand = f . Seed > > 16 ;
2017-11-26 22:48:55 +00:00
yield return info . GetFrame ( prev , LeadRequired . StaticMagnet , rand ) ;
2017-05-15 06:21:34 +00:00
}
2017-05-12 16:41:21 +00:00
}
2017-05-14 19:42:27 +00:00
/// <summary>
/// Filters the input <see cref="SeedInfo"/> according to a Nature Lock frame generation pattern.
/// </summary>
/// <param name="seeds">Seed Information for the frame</param>
/// <param name="pidiv">PIDIV Info for the frame</param>
/// <param name="info">Search Info for the frame</param>
/// <returns>Possible matches to the Nature Lock frame generation pattern</returns>
2017-06-18 01:37:19 +00:00
private static IEnumerable < Frame > FilterNatureSync ( IEnumerable < SeedInfo > seeds , PIDIV pidiv , FrameGenerator info )
2017-05-12 16:41:21 +00:00
{
2017-05-14 19:42:27 +00:00
foreach ( var seed in seeds )
2017-05-12 16:41:21 +00:00
{
2017-05-14 19:42:27 +00:00
var s = seed . Seed ;
2017-05-12 16:41:21 +00:00
var rand = s > > 16 ;
2017-05-15 06:21:34 +00:00
bool sync = info . AllowLeads & & ! seed . Charm3 & & ( info . DPPt ? rand > > 15 : rand & 1 ) = = 0 ;
2017-05-14 19:42:27 +00:00
bool reg = ( info . DPPt ? rand / 0xA3E : rand % 25 ) = = info . Nature ;
2017-05-12 16:41:21 +00:00
if ( ! sync & & ! reg ) // doesn't generate nature frame
continue ;
uint prev = pidiv . RNG . Prev ( s ) ;
2017-05-15 06:21:34 +00:00
if ( info . AllowLeads & & reg ) // check for failed sync
2017-05-12 16:41:21 +00:00
{
2017-05-14 19:42:27 +00:00
var failsync = ( info . DPPt ? prev > > 31 : ( prev > > 16 ) & 1 ) ! = 1 ;
2017-05-12 16:41:21 +00:00
if ( failsync )
2017-05-14 21:42:18 +00:00
yield return info . GetFrame ( pidiv . RNG . Prev ( prev ) , LeadRequired . SynchronizeFail ) ;
2017-05-12 16:41:21 +00:00
}
if ( sync )
2017-05-14 21:42:18 +00:00
yield return info . GetFrame ( prev , LeadRequired . Synchronize ) ;
2017-05-12 16:41:21 +00:00
if ( reg )
2017-05-14 21:42:18 +00:00
{
if ( seed . Charm3 )
yield return info . GetFrame ( prev , LeadRequired . CuteCharm ) ;
else
yield return info . GetFrame ( prev , LeadRequired . None ) ;
}
2017-05-12 16:41:21 +00:00
}
}
2017-05-14 19:42:27 +00:00
/// <summary>
/// Filters the input <see cref="SeedInfo"/> according to a Cute Charm frame generation pattern.
/// </summary>
/// <param name="seeds">Seed Information for the frame</param>
/// <param name="pidiv">PIDIV Info for the frame</param>
/// <param name="info">Search Info for the frame</param>
/// <returns>Possible matches to the Cute Charm frame generation pattern</returns>
2017-06-18 01:37:19 +00:00
private static IEnumerable < Frame > FilterCuteCharm ( IEnumerable < SeedInfo > seeds , PIDIV pidiv , FrameGenerator info )
2017-05-12 16:41:21 +00:00
{
2017-05-14 19:42:27 +00:00
foreach ( var seed in seeds )
2017-05-12 16:41:21 +00:00
{
2017-05-14 19:42:27 +00:00
var s = seed . Seed ;
2017-05-12 16:41:21 +00:00
var rand = s > > 16 ;
2017-05-14 19:42:27 +00:00
var nature = info . DPPt ? rand / 0xA3E : rand % 25 ;
if ( nature ! = info . Nature )
continue ;
var prev = pidiv . RNG . Prev ( s ) ;
var proc = prev > > 16 ;
2017-11-26 22:48:55 +00:00
bool charmProc = ( info . DPPt ? proc / 0x5556 : proc % 3 ) ! = 0 ; // 2/3 odds
2017-05-14 19:42:27 +00:00
if ( ! charmProc )
continue ;
2017-05-14 21:42:18 +00:00
yield return info . GetFrame ( prev , LeadRequired . CuteCharm ) ;
2017-05-12 16:41:21 +00:00
}
}
}
}