Add pokeblock slot filtering

since there's 300 calls to rand to shuffle, I wouldn't be surprised if
there are vblanks messing up the frames

judging from my data 6 years ago (wow, still wrong, just look at the
frame offsets)

http://www.smogon.com/forums/threads/past-gen-rng-research.61090/page-35#post-4021415

looks like it happens quite erratically. may have to just override this
to return true for all gen3 safari mons as it's entirely unpredictable.
This commit is contained in:
Kurt
2017-11-26 20:07:38 -08:00
parent 187726b4af
commit be4d30bb6d
3 changed files with 85 additions and 0 deletions

View File

@@ -185,6 +185,18 @@ private static IEnumerable<Frame> FilterNatureSync(IEnumerable<SeedInfo> seeds,
foreach (var seed in seeds)
{
var s = seed.Seed;
if (info.Safari3)
{
// successful pokeblock activation
bool result = IsValidPokeBlockNature(s, info.Nature, out uint blockSeed);
if (result)
yield return info.GetFrame(blockSeed, seed.Charm3 ? LeadRequired.CuteCharm : LeadRequired.None);
// if no pokeblocks present (or failed call), fall out of the safari specific code and generate via the other scenarios
s = info.RNG.Prev(s); // wasted RNG call
}
var rand = s >> 16;
bool sync = info.AllowLeads && !seed.Charm3 && (info.DPPt ? rand >> 15 : rand & 1) == 0;
bool reg = (info.DPPt ? rand / 0xA3E : rand % 25) == info.Nature;
@@ -209,6 +221,60 @@ private static IEnumerable<Frame> FilterNatureSync(IEnumerable<SeedInfo> seeds,
}
}
}
private static bool IsValidPokeBlockNature(uint seed, uint nature, out uint natureOrigin)
{
if (nature % 6 == 0) // neutral
{
natureOrigin = 0;
return false;
}
// unroll the RNG to a stack of seeds
var stack = new Stack<uint>();
for (uint i = 0; i < 25; i++)
for (uint j = 1 + i; j < 25; j++)
stack.Push(seed = RNG.LCRNG.Prev(seed));
natureOrigin = RNG.LCRNG.Prev(stack.Peek());
if (natureOrigin >> 16 % 100 >= 80) // failed proc
return false;
// init natures
uint[] natures = new uint[25];
for (uint i = 0; i < 25; i++)
natures[i] = i;
// shuffle nature list
for (uint i = 0; i < 25; i++)
for (uint j = 1 + i; j < 25; j++)
{
var s = stack.Pop();
if ((s >> 16 & 1) == 0)
continue; // only swap if 1
var temp = natures[i];
natures[i] = natures[j];
natures[j] = temp;
}
var likes = Pokeblock.GetLikedBlockFlavor(nature);
// best case scenario is a perfect flavored pokeblock for the nature.
// has liked flavor, and all other non-disliked flavors are present.
// is it possible to skip this step?
for (int i = 0; i < 25; i++)
{
var n = natures[i];
if (n == nature)
break;
var nl = Pokeblock.GetLikedBlockFlavor(natures[i]);
if (nl == likes) // next random nature likes the same block as the desired nature
return false; // current nature is chosen instead, fail!
}
// unroll once more to hit the level calc (origin with respect for beginning the nature calcs)
natureOrigin = RNG.LCRNG.Prev(natureOrigin);
return true;
}
/// <summary>
/// Filters the input <see cref="SeedInfo"/> according to a Cute Charm frame generation pattern.

View File

@@ -10,6 +10,8 @@ public class FrameGenerator
public readonly bool AllowLeads;
public readonly FrameType FrameType = FrameType.None;
public readonly RNG RNG;
public readonly bool Safari3;
public Frame GetFrame(uint seed, LeadRequired lead) => new Frame(seed, FrameType, RNG, lead);
public Frame GetFrame(uint seed, LeadRequired lead, uint esv) => new Frame(seed, FrameType, RNG, lead) {ESV = esv};
@@ -33,6 +35,7 @@ public FrameGenerator(PIDIV pidiv, PKM pk)
DPPt = false;
FrameType = FrameType.MethodH;
RNG = pidiv.RNG;
Safari3 = pk.Ball == 5;
if (ver != GameVersion.E)
return;

View File

@@ -0,0 +1,16 @@
namespace PKHeX.Core
{
public static class Pokeblock
{
public static Flavor GetLikedBlockFlavor(uint nature) => (Flavor)(nature/5);
public static Flavor GetDislikedBlockFlavor(uint nature) => (Flavor)(nature%5);
public enum Flavor
{
Spicy,
Sour,
Sweet,
Dry,
Bitter,
}
}
}