mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-22 12:03:10 +00:00
Add specialized PIDIV generator for gen4 slots
extract gen3's to a static class
This commit is contained in:
parent
3dd60890fb
commit
bc7118b493
10 changed files with 517 additions and 105 deletions
|
@ -1,4 +1,3 @@
|
|||
using System;
|
||||
using static PKHeX.Core.PIDType;
|
||||
using static PKHeX.Core.SlotType3;
|
||||
|
||||
|
@ -69,98 +68,20 @@ public record EncounterSlot3(EncounterArea3 Parent, ushort Species, byte Form, b
|
|||
|
||||
private void SetPINGA(PK3 pk, EncounterCriteria criteria, PersonalInfo3 pi)
|
||||
{
|
||||
var gender = criteria.GetGender(pi);
|
||||
var nature = criteria.GetNature();
|
||||
var ability = criteria.GetAbilityFromNumber(Ability);
|
||||
var lvl = new SingleLevelRange(LevelMin);
|
||||
int ctr = 0;
|
||||
|
||||
if (Species == (int)Core.Species.Unown)
|
||||
if (Species != (int)Core.Species.Unown)
|
||||
{
|
||||
if (criteria.IsSpecifiedIVs() && SetUnownFromIVs(pk, criteria))
|
||||
if (criteria.IsSpecifiedIVs() && this.SetFromIVs(pk, criteria))
|
||||
return;
|
||||
|
||||
// Generate a random Unown with the correct form and desired nature.
|
||||
SetUnownRandom(pk, criteria);
|
||||
return;
|
||||
this.SetRandom(pk, pi, criteria, Util.Rand32());
|
||||
}
|
||||
|
||||
do
|
||||
else
|
||||
{
|
||||
var seed = PIDGenerator.SetRandomWildPID4(pk, nature, ability, gender, Method_1);
|
||||
var result = MethodH.GetSeed(this, seed, lvl, pk.E, pk.Gender, 3);
|
||||
if (result.IsValid())
|
||||
if (criteria.IsSpecifiedIVs() && this.SetFromIVsUnown(pk, criteria))
|
||||
return;
|
||||
} while (ctr++ < 10_000);
|
||||
}
|
||||
|
||||
private void SetUnownRandom(PK3 pk, EncounterCriteria criteria)
|
||||
{
|
||||
//bool checkForm = forms.Contains(criteria.Form); // not a property :(
|
||||
var (min, max) = SlotMethodH.GetRangeGrass(SlotNumber);
|
||||
var seed = Util.Rand32() & 0x7FFF_FFFF;
|
||||
// Can't game the seed with % 100 increments as Unown's form calculation is based on the PID.
|
||||
|
||||
while (true)
|
||||
{
|
||||
var esv = LCRNG.Next16(ref seed) % 100;
|
||||
if (esv < min || esv > max)
|
||||
continue;
|
||||
// Skip the level roll, always results in the same level value.
|
||||
var rand = LCRNG.Next2(seed);
|
||||
if (criteria.Nature != Nature.Random && (rand >> 16) % 25 != (byte)criteria.Nature)
|
||||
continue;
|
||||
|
||||
while (true)
|
||||
{
|
||||
var a = LCRNG.Next16(ref rand);
|
||||
var b = LCRNG.Next16(ref rand);
|
||||
var pid = a << 16 | b;
|
||||
var form = EntityPID.GetUnownForm3(pid);
|
||||
if (form != Form)
|
||||
continue;
|
||||
|
||||
pk.PID = pid;
|
||||
var iv1 = LCRNG.Next16(ref rand);
|
||||
var iv2 = LCRNG.Next16(ref rand);
|
||||
pk.IV32 = ((iv2 & 0x7FFF) << 15) | (iv1 & 0x7FFF);
|
||||
return;
|
||||
}
|
||||
this.SetRandomUnown(pk, criteria);
|
||||
}
|
||||
}
|
||||
|
||||
private bool SetUnownFromIVs(PK3 pk, EncounterCriteria criteria)
|
||||
{
|
||||
Span<uint> seeds = stackalloc uint[LCRNG.MaxCountSeedsIV];
|
||||
var count = LCRNGReversal.GetSeedsIVs(seeds, (byte)criteria.IV_HP, (byte)criteria.IV_ATK, (byte)criteria.IV_DEF, (byte)criteria.IV_SPA, (byte)criteria.IV_SPD, (byte)criteria.IV_SPE);
|
||||
seeds = seeds[..count];
|
||||
foreach (ref var seed in seeds)
|
||||
{
|
||||
seed = LCRNG.Prev2(seed);
|
||||
var s = seed;
|
||||
|
||||
var a = LCRNG.Next16(ref s);
|
||||
var b = LCRNG.Next16(ref s);
|
||||
var pid = a << 16 | b;
|
||||
if (criteria.Nature != Nature.Random && (Nature)(pid % 25) != criteria.Nature)
|
||||
continue;
|
||||
var form = EntityPID.GetUnownForm3(pid);
|
||||
if (form != Form)
|
||||
continue;
|
||||
var lead = MethodH.GetSeed(this, seed, this, false, 2, 3);
|
||||
if (!lead.IsValid()) // Verifies the slot and nature loop; if it passes, apply the details.
|
||||
continue;
|
||||
|
||||
pk.PID = pid;
|
||||
var iv1 = LCRNG.Next16(ref s);
|
||||
var iv2 = LCRNG.Next16(ref s);
|
||||
pk.IV32 = ((iv2 & 0x7FFF) << 15) | (iv1 & 0x7FFF);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected virtual void SetEncounterMoves(PKM pk) => EncounterUtil.SetEncounterMoves(pk, Version, LevelMin);
|
||||
#endregion
|
||||
|
||||
|
|
|
@ -84,21 +84,20 @@ public sealed record EncounterSlot4(EncounterArea4 Parent, ushort Species, byte
|
|||
|
||||
private void SetPINGA(PK4 pk, EncounterCriteria criteria, PersonalInfo4 pi)
|
||||
{
|
||||
var gender = criteria.GetGender(pi);
|
||||
var nature = criteria.GetNature();
|
||||
var ability = criteria.GetAbilityFromNumber(Ability);
|
||||
var lvl = new SingleLevelRange(LevelMin);
|
||||
bool hgss = pk.HGSS;
|
||||
int ctr = 0;
|
||||
do
|
||||
uint seed;
|
||||
if (hgss)
|
||||
{
|
||||
var seed = PIDGenerator.SetRandomWildPID4(pk, nature, ability, gender, PIDType.Method_1);
|
||||
if (!LeadFinder.TryGetLeadInfo4(this, lvl, hgss, seed, 4, out _))
|
||||
continue;
|
||||
if (Species == (int)Core.Species.Unown)
|
||||
pk.Form = GetUnownForm(seed, hgss);
|
||||
break;
|
||||
} while (ctr++ < 10_000);
|
||||
if (!criteria.IsSpecifiedIVs() || !this.SetFromIVsK(pk, pi, criteria, out seed))
|
||||
seed = this.SetRandomK(pk, pi, criteria, Util.Rand32());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!criteria.IsSpecifiedIVs() || !this.SetFromIVsJ(pk, pi, criteria, out seed))
|
||||
seed = this.SetRandomJ(pk, pi, criteria, Util.Rand32());
|
||||
}
|
||||
if (Species == (int)Core.Species.Unown)
|
||||
pk.Form = GetUnownForm(seed, hgss);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
232
PKHeX.Core/Legality/RNG/ClassicEra/Gen3/GenerateMethodH.cs
Normal file
232
PKHeX.Core/Legality/RNG/ClassicEra/Gen3/GenerateMethodH.cs
Normal file
|
@ -0,0 +1,232 @@
|
|||
using System;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Generator methods for Generation 3.
|
||||
/// </summary>
|
||||
public static class GenerateMethodH
|
||||
{
|
||||
public static void SetRandom<T>(this T enc, PK3 pk, PersonalInfo3 pi, EncounterCriteria criteria, uint seed)
|
||||
where T : IEncounterSlot3
|
||||
{
|
||||
var gr = pi.Gender;
|
||||
var ability = criteria.GetAbilityFromNumber(AbilityPermission.Any12);
|
||||
var (min, max) = SlotMethodH.GetRange(enc.Type, enc.SlotNumber);
|
||||
|
||||
// Generate Method H correlated PID and IVs, no lead (keep things simple).
|
||||
while (true)
|
||||
{
|
||||
var esv = LCRNG.Next16(ref seed) % 100;
|
||||
if (esv < min || esv > max)
|
||||
continue;
|
||||
var lv = LCRNG.Next16(ref seed);
|
||||
var nature = LCRNG.Next16(ref seed) % 25;
|
||||
if (criteria.IsSpecifiedNature() && nature != (byte)criteria.Nature)
|
||||
continue;
|
||||
|
||||
while (true)
|
||||
{
|
||||
var a = LCRNG.Next16(ref seed);
|
||||
var b = LCRNG.Next16(ref seed);
|
||||
var pid = GetPIDRegular(a, b);
|
||||
if (pid % 25 != nature)
|
||||
continue;
|
||||
if ((pid & 1) != ability)
|
||||
break; // try again
|
||||
var gender = EntityGender.GetFromPIDAndRatio(pid, gr);
|
||||
if (!criteria.IsGenderSatisfied(gender))
|
||||
break; // try again
|
||||
|
||||
pk.MetLevel = pk.CurrentLevel = (byte)((lv % (enc.LevelMax - enc.LevelMin + 1)) + enc.LevelMin);
|
||||
SetPIDIVSequential(pk, pid, seed);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetRandomUnown<T>(this T enc, PK3 pk, EncounterCriteria criteria)
|
||||
where T : INumberedSlot, ISpeciesForm
|
||||
{
|
||||
//bool checkForm = forms.Contains(criteria.Form); // not a property :(
|
||||
var (min, max) = SlotMethodH.GetRangeGrass(enc.SlotNumber);
|
||||
var seed = Util.Rand32();
|
||||
// Can't game the seed with % 100 increments as Unown's form calculation is based on the PID.
|
||||
|
||||
while (true)
|
||||
{
|
||||
var esv = LCRNG.Next16(ref seed) % 100;
|
||||
if (esv < min || esv > max)
|
||||
continue;
|
||||
// Skip the level roll, always results in the same level value.
|
||||
seed = LCRNG.Next2(seed);
|
||||
var nature = (seed >> 16) % 25;
|
||||
if (criteria.IsSpecifiedNature() && nature != (byte)criteria.Nature)
|
||||
continue;
|
||||
|
||||
while (true)
|
||||
{
|
||||
var a = LCRNG.Next16(ref seed);
|
||||
var b = LCRNG.Next16(ref seed);
|
||||
var pid = a << 16 | b;
|
||||
if (pid % 25 != nature)
|
||||
continue;
|
||||
var form = EntityPID.GetUnownForm3(pid);
|
||||
if (form != enc.Form)
|
||||
continue;
|
||||
|
||||
SetPIDIVSequential(pk, pid, seed);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool SetFromIVs<T>(this T enc, PK3 pk, EncounterCriteria criteria)
|
||||
where T : IEncounterSlot3
|
||||
{
|
||||
var gr = pk.PersonalInfo.Gender;
|
||||
(uint iv1, uint iv2) = GetCombinedIVs(criteria);
|
||||
Span<uint> all = stackalloc uint[LCRNG.MaxCountSeedsIV];
|
||||
var count = LCRNGReversal.GetSeedsIVs(all, iv1 << 16, iv2 << 16);
|
||||
var seeds = all[..count];
|
||||
foreach (ref var seed in seeds)
|
||||
{
|
||||
seed = LCRNG.Prev2(seed);
|
||||
var s = seed;
|
||||
|
||||
var a = LCRNG.Next16(ref s);
|
||||
var b = LCRNG.Next16(ref s);
|
||||
var pid = GetPIDRegular(a, b);
|
||||
if (criteria.IsSpecifiedNature() && (Nature)(pid % 25) != criteria.Nature)
|
||||
{
|
||||
// Try again as Method 2 (BA-DE)
|
||||
var o = s >> 16;
|
||||
pid = GetPIDRegular(o, a);
|
||||
if (criteria.IsSpecifiedNature() && (Nature)(pid % 25) != criteria.Nature)
|
||||
continue;
|
||||
seed = LCRNG.Prev(seed);
|
||||
}
|
||||
|
||||
var gender = EntityGender.GetFromPIDAndRatio(pid, gr);
|
||||
if (!criteria.IsGenderSatisfied(gender))
|
||||
continue;
|
||||
var lead = MethodH.GetSeed(enc, seed, enc, pk.E, gender, 3);
|
||||
if (!lead.IsValid()) // Verifies the slot, (min) level, and nature loop; if it passes, apply the details.
|
||||
continue;
|
||||
|
||||
pk.PID = pid;
|
||||
pk.IV32 = ((iv2 & 0x7FFF) << 15) | (iv1 & 0x7FFF);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Try again as Method 4 (BAC-E)
|
||||
count = LCRNGReversalSkip.GetSeedsIVs(all, iv1 << 16, iv2 << 16);
|
||||
seeds = all[..count];
|
||||
foreach (ref var seed in seeds)
|
||||
{
|
||||
seed = LCRNG.Prev2(seed);
|
||||
var s = seed;
|
||||
|
||||
var a = LCRNG.Next16(ref s);
|
||||
var b = LCRNG.Next16(ref s);
|
||||
var pid = GetPIDRegular(a, b);
|
||||
if (criteria.IsSpecifiedNature() && (Nature)(pid % 25) != criteria.Nature)
|
||||
continue;
|
||||
|
||||
var gender = EntityGender.GetFromPIDAndRatio(pid, gr);
|
||||
if (!criteria.IsGenderSatisfied(gender))
|
||||
continue;
|
||||
var lead = MethodH.GetSeed(enc, seed, enc, pk.E, gender, 3);
|
||||
if (!lead.IsValid()) // Verifies the slot and nature loop; if it passes, apply the details.
|
||||
continue;
|
||||
|
||||
pk.PID = pid;
|
||||
pk.IV32 = ((iv2 & 0x7FFF) << 15) | (iv1 & 0x7FFF);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool SetFromIVsUnown<T>(this T enc, PK3 pk, EncounterCriteria criteria)
|
||||
where T : IEncounterSlot3
|
||||
{
|
||||
(uint iv1, uint iv2) = GetCombinedIVs(criteria);
|
||||
Span<uint> all = stackalloc uint[LCRNG.MaxCountSeedsIV];
|
||||
var count = LCRNGReversal.GetSeedsIVs(all, iv1 << 16, iv2 << 16);
|
||||
var seeds = all[..count];
|
||||
foreach (ref var seed in seeds)
|
||||
{
|
||||
seed = LCRNG.Prev2(seed);
|
||||
var s = seed;
|
||||
|
||||
var a = LCRNG.Next16(ref s);
|
||||
var b = LCRNG.Next16(ref s);
|
||||
var pid = GetPIDUnown(a, b);
|
||||
if ((criteria.IsSpecifiedNature() && (Nature)(pid % 25) != criteria.Nature) || EntityPID.GetUnownForm3(pid) != enc.Form)
|
||||
{
|
||||
// Try again as Method 2 (BA-DE)
|
||||
var o = s >> 16;
|
||||
pid = GetPIDUnown(o, a);
|
||||
if (criteria.IsSpecifiedNature() && (Nature)(pid % 25) != criteria.Nature)
|
||||
continue;
|
||||
var form = EntityPID.GetUnownForm3(pid);
|
||||
if (form != enc.Form)
|
||||
continue;
|
||||
seed = LCRNG.Prev(seed);
|
||||
}
|
||||
var lead = MethodH.GetSeed(enc, seed, enc, false, 2, 3);
|
||||
if (!lead.IsValid()) // Verifies the slot and nature loop; if it passes, apply the details.
|
||||
continue;
|
||||
|
||||
pk.PID = pid;
|
||||
pk.IV32 = ((iv2 & 0x7FFF) << 15) | (iv1 & 0x7FFF);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Try again as Method 4 (BAC-E)
|
||||
count = LCRNGReversalSkip.GetSeedsIVs(all, iv1 << 16, iv2 << 16);
|
||||
seeds = all[..count];
|
||||
foreach (ref var seed in seeds)
|
||||
{
|
||||
seed = LCRNG.Prev2(seed);
|
||||
var s = seed;
|
||||
|
||||
var a = LCRNG.Next16(ref s);
|
||||
var b = LCRNG.Next16(ref s);
|
||||
var pid = GetPIDUnown(a, b);
|
||||
if (criteria.IsSpecifiedNature() && (Nature)(pid % 25) != criteria.Nature)
|
||||
continue;
|
||||
var form = EntityPID.GetUnownForm3(pid);
|
||||
if (form != enc.Form)
|
||||
continue;
|
||||
var lead = MethodH.GetSeed(enc, seed, enc, false, 2, 3);
|
||||
if (!lead.IsValid()) // Verifies the slot and nature loop; if it passes, apply the details.
|
||||
continue;
|
||||
|
||||
pk.PID = pid;
|
||||
pk.IV32 = ((iv2 & 0x7FFF) << 15) | (iv1 & 0x7FFF);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static uint GetPIDUnown(uint a, uint b) => a << 16 | b;
|
||||
public static uint GetPIDRegular(uint a, uint b) => b << 16 | a;
|
||||
|
||||
private static void SetPIDIVSequential(PK3 pk, uint pid, uint rand)
|
||||
{
|
||||
pk.PID = pid;
|
||||
var iv1 = LCRNG.Next16(ref rand);
|
||||
var iv2 = LCRNG.Next16(ref rand);
|
||||
pk.IV32 = ((iv2 & 0x7FFF) << 15) | (iv1 & 0x7FFF);
|
||||
}
|
||||
|
||||
private static (uint iv1, uint iv2) GetCombinedIVs(EncounterCriteria criteria)
|
||||
{
|
||||
uint iv1 = (uint)criteria.IV_HP | (uint)criteria.IV_ATK << 5 | (uint)criteria.IV_DEF << 10;
|
||||
uint iv2 = (uint)criteria.IV_SPE | (uint)criteria.IV_SPA << 5 | (uint)criteria.IV_SPD << 10;
|
||||
return (iv1, iv2);
|
||||
}
|
||||
}
|
129
PKHeX.Core/Legality/RNG/ClassicEra/Gen4/GenerateMethodJ.cs
Normal file
129
PKHeX.Core/Legality/RNG/ClassicEra/Gen4/GenerateMethodJ.cs
Normal file
|
@ -0,0 +1,129 @@
|
|||
using System;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Generator methods for Generation 4.
|
||||
/// </summary>
|
||||
public static class GenerateMethodJ
|
||||
{
|
||||
/// <summary>
|
||||
/// Applies a random PID and IVs to the entity, based on the <see cref="PersonalInfo4"/> and <see cref="EncounterCriteria"/>.
|
||||
/// </summary>
|
||||
/// <param name="enc">Encounter slot to generate for</param>
|
||||
/// <param name="pk">Entity to modify</param>
|
||||
/// <param name="pi">Personal Info of the entity</param>
|
||||
/// <param name="criteria">Criteria to match</param>
|
||||
/// <param name="seed">Initial seed to use; will be modified during the loop if the first seed fails</param>
|
||||
/// <returns>Method 1 origin seed applied</returns>
|
||||
public static uint SetRandomJ<T>(this T enc, PK4 pk, PersonalInfo4 pi, EncounterCriteria criteria, uint seed)
|
||||
where T : IEncounterSlot4
|
||||
{
|
||||
var gr = pi.Gender;
|
||||
var ability = criteria.GetAbilityFromNumber(AbilityPermission.Any12);
|
||||
var (min, max) = SlotMethodJ.GetRange(enc.Type, enc.SlotNumber);
|
||||
bool randLevel = MethodJ.IsLevelRand(enc);
|
||||
|
||||
// Generate Method J correlated PID and IVs, no lead (keep things simple).
|
||||
while (true)
|
||||
{
|
||||
var esv = LCRNG.Next16(ref seed) / 656;
|
||||
if (esv < min || esv > max)
|
||||
continue;
|
||||
var lv = randLevel ? LCRNG.Next16(ref seed) : 0;
|
||||
var nature = LCRNG.Next16(ref seed) % 25;
|
||||
if (criteria.IsSpecifiedNature() && nature != (byte)criteria.Nature)
|
||||
continue;
|
||||
|
||||
while (true)
|
||||
{
|
||||
var a = LCRNG.Next16(ref seed);
|
||||
var b = LCRNG.Next16(ref seed);
|
||||
var pid = GetPIDRegular(a, b);
|
||||
if (pid % 25 != nature)
|
||||
continue;
|
||||
if ((pid & 1) != ability)
|
||||
break; // try again
|
||||
var gender = EntityGender.GetFromPIDAndRatio(pid, gr);
|
||||
if (!criteria.IsGenderSatisfied(gender))
|
||||
break; // try again
|
||||
|
||||
if (randLevel)
|
||||
{
|
||||
var lvl = enc.Type is SlotType4.HoneyTree
|
||||
? MethodJ.GetHoneyTreeLevel(lv)
|
||||
: ((lv % (enc.LevelMax - enc.LevelMin + 1)) + enc.LevelMin);
|
||||
pk.MetLevel = pk.CurrentLevel = (byte)lvl;
|
||||
}
|
||||
SetPIDIVSequential(pk, pid, seed);
|
||||
pk.Gender = gender;
|
||||
pk.Ability = (pid & 1) == 0 ? pi.Ability1 : pi.Ability2;
|
||||
return LCRNG.Prev4(seed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the IVs from the <see cref="EncounterCriteria"/>, assuming the IVs are possible and a Method 1 spread is possible.
|
||||
/// </summary>
|
||||
/// <param name="enc">Encounter slot to generate for</param>
|
||||
/// <param name="pk">Entity to modify</param>
|
||||
/// <param name="pi">Personal Info of the entity</param>
|
||||
/// <param name="criteria">Criteria to match</param>
|
||||
/// <param name="origin">Method 1 origin seed applied</param>
|
||||
/// <returns>True if the PID/IV was valid & applied to the entity.</returns>
|
||||
public static bool SetFromIVsJ<T>(this T enc, PK4 pk, PersonalInfo4 pi, EncounterCriteria criteria, out uint origin)
|
||||
where T : IEncounterSlot4
|
||||
{
|
||||
var gr = pi.Gender;
|
||||
(uint iv1, uint iv2) = GetCombinedIVs(criteria);
|
||||
Span<uint> all = stackalloc uint[LCRNG.MaxCountSeedsIV];
|
||||
var count = LCRNGReversal.GetSeedsIVs(all, iv1 << 16, iv2 << 16);
|
||||
var seeds = all[..count];
|
||||
foreach (ref var seed in seeds)
|
||||
{
|
||||
seed = LCRNG.Prev2(seed);
|
||||
var s = seed;
|
||||
|
||||
var a = LCRNG.Next16(ref s);
|
||||
var b = LCRNG.Next16(ref s);
|
||||
var pid = GetPIDRegular(a, b);
|
||||
if (criteria.IsSpecifiedNature() && (Nature)(pid % 25) != criteria.Nature)
|
||||
continue;
|
||||
|
||||
var gender = EntityGender.GetFromPIDAndRatio(pid, gr);
|
||||
if (!criteria.IsGenderSatisfied(gender))
|
||||
continue;
|
||||
var lead = MethodJ.GetSeed(enc, seed, enc, 4);
|
||||
if (!lead.IsValid()) // Verifies the slot, (min) level, and nature loop; if it passes, apply the details.
|
||||
continue;
|
||||
|
||||
pk.PID = pid;
|
||||
pk.IV32 = ((iv2 & 0x7FFF) << 15) | (iv1 & 0x7FFF);
|
||||
pk.Gender = gender;
|
||||
pk.Ability = (pid & 1) == 0 ? pi.Ability1 : pi.Ability2;
|
||||
origin = seed;
|
||||
return true;
|
||||
}
|
||||
|
||||
origin = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static uint GetPIDRegular(uint a, uint b) => b << 16 | a;
|
||||
|
||||
private static void SetPIDIVSequential(PK4 pk, uint pid, uint rand)
|
||||
{
|
||||
pk.PID = pid;
|
||||
var iv1 = LCRNG.Next16(ref rand);
|
||||
var iv2 = LCRNG.Next16(ref rand);
|
||||
pk.IV32 = ((iv2 & 0x7FFF) << 15) | (iv1 & 0x7FFF);
|
||||
}
|
||||
|
||||
private static (uint iv1, uint iv2) GetCombinedIVs(EncounterCriteria criteria)
|
||||
{
|
||||
uint iv1 = (uint)criteria.IV_HP | (uint)criteria.IV_ATK << 5 | (uint)criteria.IV_DEF << 10;
|
||||
uint iv2 = (uint)criteria.IV_SPE | (uint)criteria.IV_SPA << 5 | (uint)criteria.IV_SPD << 10;
|
||||
return (iv1, iv2);
|
||||
}
|
||||
}
|
127
PKHeX.Core/Legality/RNG/ClassicEra/Gen4/GenerateMethodK.cs
Normal file
127
PKHeX.Core/Legality/RNG/ClassicEra/Gen4/GenerateMethodK.cs
Normal file
|
@ -0,0 +1,127 @@
|
|||
using System;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Generator methods for Generation 4.
|
||||
/// </summary>
|
||||
public static class GenerateMethodK
|
||||
{
|
||||
/// <summary>
|
||||
/// Applies a random PID and IVs to the entity, based on the <see cref="PersonalInfo4"/> and <see cref="EncounterCriteria"/>.
|
||||
/// </summary>
|
||||
/// <param name="enc">Encounter slot to generate for</param>
|
||||
/// <param name="pk">Entity to modify</param>
|
||||
/// <param name="pi">Personal Info of the entity</param>
|
||||
/// <param name="criteria">Criteria to match</param>
|
||||
/// <param name="seed">Initial seed to use; will be modified during the loop if the first seed fails</param>
|
||||
/// <returns>Method 1 origin seed applied</returns>
|
||||
public static uint SetRandomK<T>(this T enc, PK4 pk, PersonalInfo4 pi, EncounterCriteria criteria, uint seed)
|
||||
where T : IEncounterSlot4
|
||||
{
|
||||
var gr = pi.Gender;
|
||||
var ability = criteria.GetAbilityFromNumber(AbilityPermission.Any12);
|
||||
var (min, max) = SlotMethodK.GetRange(enc.Type, enc.SlotNumber);
|
||||
bool randLevel = MethodK.IsLevelRand(enc);
|
||||
var modulo = enc.Type.IsSafari() ? 10 : 100;
|
||||
|
||||
// Generate Method K correlated PID and IVs, no lead (keep things simple).
|
||||
while (true)
|
||||
{
|
||||
var esv = LCRNG.Next16(ref seed) % modulo;
|
||||
if (esv < min || esv > max)
|
||||
continue;
|
||||
var lv = randLevel ? LCRNG.Next16(ref seed) : 0;
|
||||
var nature = LCRNG.Next16(ref seed) % 25;
|
||||
if (criteria.IsSpecifiedNature() && nature != (byte)criteria.Nature)
|
||||
continue;
|
||||
|
||||
while (true)
|
||||
{
|
||||
var a = LCRNG.Next16(ref seed);
|
||||
var b = LCRNG.Next16(ref seed);
|
||||
var pid = GetPIDRegular(a, b);
|
||||
if (pid % 25 != nature)
|
||||
continue;
|
||||
if ((pid & 1) != ability)
|
||||
break; // try again
|
||||
var gender = EntityGender.GetFromPIDAndRatio(pid, gr);
|
||||
if (!criteria.IsGenderSatisfied(gender))
|
||||
break; // try again
|
||||
|
||||
if (randLevel)
|
||||
pk.MetLevel = pk.CurrentLevel = (byte)((lv % (enc.LevelMax - enc.LevelMin + 1)) + enc.LevelMin);
|
||||
pk.PID = pid;
|
||||
var iv1 = LCRNG.Next16(ref seed);
|
||||
var iv2 = LCRNG.Next16(ref seed);
|
||||
pk.IV32 = ((iv2 & 0x7FFF) << 15) | (iv1 & 0x7FFF);
|
||||
|
||||
if (enc.Type is SlotType4.BugContest && !MethodK.IsAny31(iv1) && !MethodK.IsAny31(iv2))
|
||||
break; // try again
|
||||
pk.Gender = gender;
|
||||
pk.Ability = (pid & 1) == 0 ? pi.Ability1 : pi.Ability2;
|
||||
return LCRNG.Prev4(seed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the IVs from the <see cref="EncounterCriteria"/>, assuming the IVs are possible and a Method 1 spread is possible.
|
||||
/// </summary>
|
||||
/// <param name="enc">Encounter slot to generate for</param>
|
||||
/// <param name="pk">Entity to modify</param>
|
||||
/// <param name="pi">Personal Info of the entity</param>
|
||||
/// <param name="criteria">Criteria to match</param>
|
||||
/// <param name="origin">Method 1 origin seed applied</param>
|
||||
/// <returns>True if the PID/IV was valid & applied to the entity.</returns>
|
||||
public static bool SetFromIVsK<T>(this T enc, PK4 pk, PersonalInfo4 pi, EncounterCriteria criteria, out uint origin)
|
||||
where T : IEncounterSlot4
|
||||
{
|
||||
var gr = pi.Gender;
|
||||
(uint iv1, uint iv2) = GetCombinedIVs(criteria);
|
||||
Span<uint> all = stackalloc uint[LCRNG.MaxCountSeedsIV];
|
||||
var count = LCRNGReversal.GetSeedsIVs(all, iv1 << 16, iv2 << 16);
|
||||
var seeds = all[..count];
|
||||
foreach (ref var seed in seeds)
|
||||
{
|
||||
seed = LCRNG.Prev2(seed);
|
||||
var s = seed;
|
||||
|
||||
var a = LCRNG.Next16(ref s);
|
||||
var b = LCRNG.Next16(ref s);
|
||||
var pid = GetPIDRegular(a, b);
|
||||
if (criteria.IsSpecifiedNature() && (Nature)(pid % 25) != criteria.Nature)
|
||||
continue;
|
||||
|
||||
var gender = EntityGender.GetFromPIDAndRatio(pid, gr);
|
||||
if (!criteria.IsGenderSatisfied(gender))
|
||||
continue;
|
||||
var lead = MethodK.GetSeed(enc, seed, enc, 4);
|
||||
if (!lead.IsValid()) // Verifies the slot, (min) level, and nature loop; if it passes, apply the details.
|
||||
continue;
|
||||
|
||||
pk.PID = pid;
|
||||
pk.IV32 = ((iv2 & 0x7FFF) << 15) | (iv1 & 0x7FFF);
|
||||
pk.Gender = gender;
|
||||
pk.Ability = (pid & 1) == 0 ? pi.Ability1 : pi.Ability2;
|
||||
origin = seed;
|
||||
return true;
|
||||
}
|
||||
|
||||
origin = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static uint GetPIDRegular(uint a, uint b) => b << 16 | a;
|
||||
|
||||
private static void SetPIDIVSequential(PK4 pk, uint pid, uint rand)
|
||||
{
|
||||
}
|
||||
|
||||
private static (uint iv1, uint iv2) GetCombinedIVs(EncounterCriteria criteria)
|
||||
{
|
||||
uint iv1 = (uint)criteria.IV_HP | (uint)criteria.IV_ATK << 5 | (uint)criteria.IV_DEF << 10;
|
||||
uint iv2 = (uint)criteria.IV_SPE | (uint)criteria.IV_SPA << 5 | (uint)criteria.IV_SPD << 10;
|
||||
return (iv1, iv2);
|
||||
}
|
||||
}
|
|
@ -317,7 +317,7 @@ public static class MethodJ
|
|||
result = default; return false;
|
||||
}
|
||||
|
||||
private static bool IsLevelRand<T>(T enc) where T : IEncounterSlot4 => enc.Type.IsLevelRandDPPt();
|
||||
public static bool IsLevelRand<T>(T enc) where T : IEncounterSlot4 => enc.Type.IsLevelRandDPPt();
|
||||
|
||||
private static bool IsSlotValidFrom1Skip<T>(FrameCheckDetails<T> ctx, out uint result)
|
||||
where T : IEncounterSlot4
|
||||
|
|
|
@ -243,8 +243,12 @@ public static class MethodK
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if any IV component value is 31.
|
||||
/// </summary>
|
||||
/// <remarks>BCC re-rolls 3x if none are 31.</remarks>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool IsAny31(uint iv16)
|
||||
public static bool IsAny31(uint iv16)
|
||||
=> IsLow5Bits31(iv16)
|
||||
|| IsLow5Bits31(iv16 >> 5)
|
||||
|| IsLow5Bits31(iv16 >> 10);
|
||||
|
@ -336,7 +340,7 @@ public static class MethodK
|
|||
result = default; return false;
|
||||
}
|
||||
|
||||
private static bool IsLevelRand<T>(T enc) where T : IEncounterSlot4 => enc.Type.IsLevelRandHGSS();
|
||||
public static bool IsLevelRand<T>(T enc) where T : IEncounterSlot4 => enc.Type.IsLevelRandHGSS();
|
||||
|
||||
private static bool IsSlotValidFrom1Skip<T>(FrameCheckDetails<T> ctx, out uint result)
|
||||
where T : IEncounterSlot4
|
||||
|
|
|
@ -188,7 +188,7 @@ public partial class SAV_Encounters : Form
|
|||
var set = new ShowdownSet(editor);
|
||||
var criteria = EncounterCriteria.GetCriteria(set, editor.PersonalInfo);
|
||||
if (!isInChain)
|
||||
criteria = criteria with { Gender = FixedGenderUtil.GenderRandom }; // Genderless tabs and a gendered enc -> let's play safe.
|
||||
criteria = criteria with { Gender = default }; // Genderless tabs and a gendered enc -> let's play safe.
|
||||
return criteria;
|
||||
}
|
||||
|
||||
|
|
|
@ -621,10 +621,10 @@ public partial class SAV_Misc5 : Form
|
|||
source.Remove(slot);
|
||||
s.Species = slot.Species;
|
||||
s.Form = slot.Form;
|
||||
s.Gender = slot.Gender == FixedGenderUtil.GenderRandom ? PersonalTable.B2W2[slot.Species].RandomGender() : slot.Gender;
|
||||
s.Gender = !((IFixedGender)slot).IsFixedGender ? PersonalTable.B2W2[slot.Species].RandomGender() : slot.Gender;
|
||||
|
||||
slot.Moves.CopyTo(moves);
|
||||
var count = moves.Length - moves.Count((ushort)0);
|
||||
var count = moves.Length - moves.Count<ushort>(0);
|
||||
s.Move = count == 0 ? (ushort)0 : moves[rnd.Next(count)];
|
||||
}
|
||||
ChangeArea(this, EventArgs.Empty); // refresh
|
||||
|
|
|
@ -87,6 +87,6 @@ public class BreedTests
|
|||
// fixed order should be different now.
|
||||
expected.SequenceEqual(moves).Should().BeFalse();
|
||||
// nonzero move count should be same
|
||||
expected.Count((ushort)0).Should().Be(moves.Count((ushort)0));
|
||||
expected.Count<ushort>(0).Should().Be(moves.Count<ushort>(0));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue