Add special generator for Gen3 unown

Removes incorrect slots ( see 2bf963009f )
Allow Unown of any form through (unspecified in Possible generator)

Adds SlotRange get method for Gen3/4 methods.
This commit is contained in:
Kurt 2024-03-15 21:21:34 -05:00
parent ba0c4c90d2
commit 9352301b65
8 changed files with 340 additions and 18 deletions

View file

@ -368,7 +368,7 @@ public static class EncounterMovesetGenerator
return true;
if (FormInfo.IsFormChangeable(enc.Species, enc.Form, evo.Form, enc.Context, current))
return true;
if (enc is IEncounterFormRandom { IsRandomUnspecificForm: true })
if (enc is IEncounterFormRandom { IsRandomUnspecificForm: true } or { Species: (ushort)Species.Unown })
return true;
if (enc is EncounterStatic7 {IsTotem: true} && evo.Form == 0 && current.Generation() > 7) // totems get form wiped
return true;

View file

@ -1,3 +1,4 @@
using System;
using static PKHeX.Core.PIDType;
using static PKHeX.Core.SlotType3;
@ -76,27 +77,90 @@ public record EncounterSlot3(EncounterArea3 Parent, ushort Species, byte Form, b
if (Species == (int)Core.Species.Unown)
{
do
{
var seed = PIDGenerator.SetRandomWildPID4(pk, nature, ability, gender, Method_1_Unown);
var lead = MethodH.GetSeed(this, seed, lvl, false, 2, 3);
if (pk.Form != Form && lead.IsValid())
return;
ability ^= 1; // some nature-forms cannot have a certain PID-ability set, so just flip it as Unown doesn't have dual abilities.
} while (ctr++ < 10_000);
if (criteria.IsSpecifiedIVs() && SetUnownFromIVs(pk, criteria))
return;
// Generate a random Unown with the correct form and desired nature.
SetUnownRandom(pk, criteria);
return;
}
else
do
{
do
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())
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 seed = PIDGenerator.SetRandomWildPID4(pk, nature, ability, gender, Method_1);
var result = MethodH.GetSeed(this, seed, lvl, pk.E, pk.Gender, 3);
if (result.IsValid())
return;
} while (ctr++ < 10_000);
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;
}
}
}
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

View file

@ -26,6 +26,23 @@ public static class SlotMethodH
_ => Invalid,
};
/// <summary>
/// Gets the range that a given slot number is allowed to roll for a specific <see cref="SlotType3"/>.
/// </summary>
public static (byte Min, byte Max) GetRange(SlotType3 type, byte slotNumber) => type switch
{
Grass => GetRangeGrass(slotNumber),
Surf => GetRangeSurf(slotNumber),
Old_Rod => GetRangeOldRod(slotNumber),
Good_Rod => GetRangeGoodRod(slotNumber),
Super_Rod => GetRangeSuperRod(slotNumber),
Rock_Smash => GetRangeSurf(slotNumber),
SwarmFish50 => (0, 49),
SwarmGrass50 => (0, 49),
_ => (Invalid, Invalid),
};
/// <summary>
/// Calculates the encounter slot index based on the roll for a Gen3 Wild encounter.
/// </summary>
@ -91,10 +108,77 @@ public static class SlotMethodH
public static byte GetSuperRod(uint roll) => roll switch
{
< 40 => 0, // 00,39 (40%)
< 80 => 1, // 40,69 (40%)
< 95 => 2, // 70,94 (15%)
< 80 => 1, // 40,69 (30%)
< 95 => 2, // 70,94 (25%)
< 99 => 3, // 95,98 ( 4%)
99 => 4, // 99 ( 1%)
_ => Invalid,
};
/// <summary>
/// Gets the range a given slot number is allowed to roll in for a <see cref="Grass"/> encounter.
/// </summary>
public static (byte Min, byte Max) GetRangeGrass(byte slotNumber) => slotNumber switch
{
0 => (00, 19), // (20%)
1 => (20, 39), // (20%)
2 => (40, 49), // (10%)
3 => (50, 59), // (10%)
4 => (60, 69), // (10%)
5 => (70, 79), // (10%)
6 => (80, 84), // ( 5%)
7 => (85, 89), // ( 5%)
8 => (90, 93), // ( 4%)
9 => (94, 97), // ( 4%)
10=> (98, 98), // ( 1%)
11=> (99, 99), // ( 1%)
_ => (Invalid, Invalid),
};
/// <summary>
/// Gets the range a given slot number is allowed to roll in for a <see cref="Surf"/> encounter.
/// </summary>
public static (byte Min, byte Max) GetRangeSurf(byte slotNumber) => slotNumber switch
{
0 => (00, 59), // (60%)
1 => (60, 89), // (30%)
2 => (90, 94), // ( 5%)
3 => (95, 98), // ( 4%)
4 => (99, 99), // ( 1%)
_ => (Invalid, Invalid),
};
/// <summary>
/// Gets the range a given slot number is allowed to roll in for a <see cref="Old_Rod"/> encounter.
/// </summary>
public static (byte Min, byte Max) GetRangeOldRod(byte slotNumber) => slotNumber switch
{
0 => (00, 69), // (70%)
1 => (70, 99), // (30%)
_ => (Invalid, Invalid),
};
/// <summary>
/// Gets the range a given slot number is allowed to roll in for a <see cref="Good_Rod"/> encounter.
/// </summary>
public static (byte Min, byte Max) GetRangeGoodRod(byte slotNumber) => slotNumber switch
{
0 => (00, 59), // (60%)
1 => (60, 79), // (20%)
2 => (80, 99), // (20%)
_ => (Invalid, Invalid),
};
/// <summary>
/// Gets the range a given slot number is allowed to roll in for a <see cref="Super_Rod"/> encounter.
/// </summary>
public static (byte Min, byte Max) GetRangeSuperRod(byte slotNumber) => slotNumber switch
{
0 => (00, 39), // (40%)
1 => (40, 69), // (30%)
2 => (70, 94), // (25%)
3 => (95, 98), // ( 4%)
4 => (99, 99), // ( 1%)
_ => (Invalid, Invalid),
};
}

View file

@ -0,0 +1,52 @@
using System;
namespace PKHeX.Core;
public static class TanobyRuins3
{
/// <summary>
/// Checks if the form is available in the location.
/// </summary>
public static bool IsFormInLocation(byte location, byte form) => IsLocationInRuins(location) && GetForms(location).Contains(form);
/// <summary>
/// If you're calling this method and know which template you are matched to, just check the form from the template directly.
/// </summary>
public static bool IsFormValid(byte location, byte form, byte slot)
{
if (!IsLocationInRuins(location))
return false;
var forms = GetForms(location);
if (form > forms.Length)
return false;
return forms[slot] == form;
}
/// <summary>
/// Gets the form for the location and slot.
/// </summary>
public static byte GetForm(byte location, byte slot)
{
if (!IsLocationInRuins(location))
return 0;
var forms = GetForms(location);
if (slot > forms.Length)
return 0;
return forms[slot];
}
private static bool IsLocationInRuins(byte location) => location is >= 188 and <= 194;
public static ReadOnlySpan<byte> GetForms(byte location) => location switch
{
188 => [00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 27], // 188 = Monean Chamber
189 => [02, 02, 02, 03, 03, 03, 07, 07, 07, 20, 20, 14], // 189 = Liptoo Chamber
190 => [13, 13, 13, 13, 18, 18, 18, 18, 08, 08, 04, 04], // 190 = Weepth Chamber
191 => [15, 15, 11, 11, 09, 09, 17, 17, 17, 16, 16, 16], // 191 = Dilford Chamber
192 => [24, 24, 19, 19, 06, 06, 06, 05, 05, 05, 10, 10], // 192 = Scufib Chamber
193 => [21, 21, 21, 22, 22, 22, 23, 23, 12, 12, 01, 01], // 193 = Rixy Chamber
194 => [25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26], // 194 = Viapois Chamber
_ => throw new ArgumentOutOfRangeException(nameof(location)),
};
}

View file

@ -25,6 +25,17 @@ public static class SlotMethodJ
};
}
/// <summary>
/// Gets the range that a given slot number is allowed to roll for a specific <see cref="SlotType4"/>.
/// </summary>
public static (byte Min, byte Max) GetRange(SlotType4 type, byte slotNumber) => type switch
{
Old_Rod or Surf => GetRangeSurf(slotNumber),
Good_Rod or Super_Rod => GetRangeSuperRod(slotNumber),
HoneyTree => (0, 99), // Fake
_ => GetRangeGrass(slotNumber),
};
/// <summary>
/// Calculates the encounter slot index based on the roll for a Gen4 Wild encounter.
/// </summary>
@ -54,4 +65,28 @@ public static class SlotMethodJ
99 => 4, // 99 ( 1%)
_ => Invalid,
};
/// <summary>
/// Gets the range a given slot number is allowed to roll in for a <see cref="Grass"/> encounter.
/// </summary>
public static (byte Min, byte Max) GetRangeGrass(byte slotNumber) => SlotMethodH.GetRangeGrass(slotNumber);
/// <summary>
/// Gets the range a given slot number is allowed to roll in for a <see cref="Surf"/> encounter.
/// </summary>
public static (byte Min, byte Max) GetRangeSurf(byte slotNumber) => SlotMethodH.GetRangeSurf(slotNumber);
/// <summary>
/// Gets the range a given slot number is allowed to roll in for a <see cref="Super_Rod"/> encounter.
/// </summary>
public static (byte Min, byte Max) GetRangeSuperRod(byte slotNumber) => slotNumber switch
{
0 => (00, 39), // (40%)
1 => (40, 79), // (40%)
2 => (80, 94), // (15%)
3 => (95, 98), // ( 4%)
4 => (99, 99), // ( 1%)
_ => (Invalid, Invalid),
};
}

View file

@ -27,6 +27,22 @@ public static class SlotMethodK
_ => Invalid,
};
/// <summary>
/// Gets the range that a given slot number is allowed to roll for a specific <see cref="SlotType4"/>.
/// </summary>
public static (byte Min, byte Max) GetRange(SlotType4 type, byte slotNumber) => type switch
{
Grass => GetRangeGrass(slotNumber),
Surf => GetRangeSurf(slotNumber),
Old_Rod or Good_Rod or Super_Rod => GetRangeSuperRod(slotNumber),
Rock_Smash => GetRangeRockSmash(slotNumber),
Headbutt or HeadbuttSpecial => GetRangeHeadbutt(slotNumber),
BugContest => GetRangeBugCatchingContest(slotNumber),
Safari_Grass or Safari_Surf or
Safari_Old_Rod or Safari_Good_Rod or Safari_Super_Rod => GetRangeSafari(slotNumber),
_ => (Invalid, Invalid),
};
/// <summary>
/// Calculates the encounter slot index based on the roll for a Gen4 Wild encounter.
/// </summary>
@ -106,4 +122,75 @@ public static class SlotMethodK
<100 => 5, // 95,99 ( 5%)
_ => Invalid,
};
/// <summary>
/// Gets the range a given slot number is allowed to roll in for a <see cref="Grass"/> encounter.
/// </summary>
public static (byte Min, byte Max) GetRangeGrass(byte slotNumber) => SlotMethodH.GetRangeGrass(slotNumber);
/// <summary>
/// Gets the range a given slot number is allowed to roll in for a <see cref="Surf"/> encounter.
/// </summary>
public static (byte Min, byte Max) GetRangeSurf(byte slotNumber) => SlotMethodH.GetRangeSurf(slotNumber);
/// <summary>
/// Gets the range a given slot number is allowed to roll in for a <see cref="Super_Rod"/> encounter.
/// </summary>
public static (byte Min, byte Max) GetRangeSuperRod(byte slotNumber) => slotNumber switch
{
0 => (00, 39), // (40%)
1 => (40, 69), // (30%)
2 => (70, 84), // (15%)
3 => (85, 94), // (10%)
4 => (95, 99), // ( 5%)
_ => (Invalid, Invalid),
};
/// <summary>
/// Gets the range a given slot number is allowed to roll in for a <see cref="Rock_Smash"/> encounter.
/// </summary>
public static (byte Min, byte Max) GetRangeRockSmash(byte slotNumber) => slotNumber switch
{
0 => (00, 79), // (80%)
1 => (80, 99), // (20%)
_ => (Invalid, Invalid),
};
/// <summary>
/// Gets the range a given slot number is allowed to roll in for a <see cref="Headbutt"/> encounter.
/// </summary>
public static (byte Min, byte Max) GetRangeHeadbutt(byte slotNumber) => slotNumber switch
{
0 => (00, 49), // (50%)
1 => (50, 64), // (15%)
2 => (65, 79), // (15%)
3 => (80, 89), // (10%)
4 => (90, 94), // ( 5%)
5 => (95, 99), // ( 5%)
_ => (Invalid, Invalid),
};
/// <summary>
/// Gets the range a given slot number is allowed to roll in for a <see cref="BugContest"/> encounter.
/// </summary>
public static (byte Min, byte Max) GetRangeBugCatchingContest(byte slotNumber) => slotNumber switch
{
9 => (00, 04), // ( 5%)
8 => (05, 09), // ( 5%)
7 => (10, 14), // ( 5%)
6 => (15, 19), // ( 5%)
5 => (20, 29), // (10%)
4 => (30, 39), // (10%)
3 => (40, 49), // (10%)
2 => (50, 59), // (10%)
1 => (60, 79), // (20%)
0 => (80, 99), // (20%)
_ => (Invalid, Invalid),
};
/// <summary>
/// Gets the range a given slot number is allowed to roll in for a <see cref="Safari_Grass"/> (and other Safari Types) encounter.
/// </summary>
public static (byte Min, byte Max) GetRangeSafari(byte slotNumber) => (slotNumber, slotNumber);
}