EncounterCriteria: sbyte IVs

Narrow fields to reduce allocation < 16 bytes (maybe make this a struct?)
Add method to get combined IVs (gen3/4 format) for RNG purposes
use ^ method in RNG methods
Change GetExpectedLevel to be generic rather than interface, better codegen
Change Gender to enum for clarity

breaks ALM and potentially other plugins, just need to recompile if using IVs
This commit is contained in:
Kurt
2024-10-01 00:00:19 -05:00
parent 9479e8cb5f
commit e5c3c43bda
11 changed files with 53 additions and 72 deletions

View File

@@ -53,7 +53,7 @@ public interface IBattleTemplate : ISpeciesForm, IGigantamaxReadOnly, IDynamaxLe
/// <summary>
/// <see cref="PKM.HPType"/> of the Set entity.
/// </summary>
int HiddenPowerType { get; }
sbyte HiddenPowerType { get; }
/// <summary>
/// <see cref="EffortValues"/> of the Set entity.

View File

@@ -68,7 +68,7 @@ public sealed class ShowdownSet : IBattleTemplate
public int[] IVs { get; } = [31, 31, 31, 31, 31, 31];
/// <inheritdoc/>
public int HiddenPowerType { get; private set; } = -1;
public sbyte HiddenPowerType { get; private set; } = -1;
public MoveType TeraType { get; private set; } = MoveType.Any;
@@ -512,7 +512,7 @@ public ShowdownSet(PKM pk)
var moves = Moves.AsSpan();
pk.GetMoves(moves);
if (moves.Contains((ushort)Move.HiddenPower))
HiddenPowerType = HiddenPower.GetType(IVs, Context);
HiddenPowerType = (sbyte)HiddenPower.GetType(IVs, Context);
Nature = pk.StatNature;
Gender = pk.Gender < 2 ? pk.Gender : (byte)2;
@@ -719,7 +719,7 @@ private ReadOnlySpan<char> ParseLineMove(ReadOnlySpan<char> line)
if (hpVal == -1)
return hiddenPowerName;
HiddenPowerType = hpVal;
HiddenPowerType = (sbyte)hpVal;
if (!Array.TrueForAll(IVs, z => z == 31))
{
if (!HiddenPower.SetIVsForType(hpVal, IVs, Context))

View File

@@ -14,8 +14,8 @@ public sealed record EncounterCriteria : IFixedNature, IFixedAbilityNumber, IShi
public static readonly EncounterCriteria Unrestricted = new();
/// <summary> End result's gender. </summary>
/// <remarks> Leave as null to not restrict gender. </remarks>
public byte? Gender { get; init; }
/// <remarks> Leave as <see cref="Gender.Random"/> to not restrict gender. </remarks>
public Gender Gender { get; init; } = Gender.Random;
/// <summary> End result's ability numbers permitted. </summary>
/// <remarks> Leave as <see cref="Any12H"/> to not restrict ability. </remarks>
@@ -29,12 +29,12 @@ public sealed record EncounterCriteria : IFixedNature, IFixedAbilityNumber, IShi
/// <remarks> Leave as <see cref="Shiny.Random"/> to not restrict shininess. </remarks>
public Shiny Shiny { get; init; }
public int IV_HP { get; init; } = RandomIV;
public int IV_ATK { get; init; } = RandomIV;
public int IV_DEF { get; init; } = RandomIV;
public int IV_SPA { get; init; } = RandomIV;
public int IV_SPD { get; init; } = RandomIV;
public int IV_SPE { get; init; } = RandomIV;
public sbyte IV_HP { get; init; } = RandomIV;
public sbyte IV_ATK { get; init; } = RandomIV;
public sbyte IV_DEF { get; init; } = RandomIV;
public sbyte IV_SPA { get; init; } = RandomIV;
public sbyte IV_SPD { get; init; } = RandomIV;
public sbyte IV_SPE { get; init; } = RandomIV;
/// <summary>
/// If the Encounter yields variable level ranges (e.g. RNG correlation), force the minimum level instead of yielding first match.
@@ -44,7 +44,7 @@ public sealed record EncounterCriteria : IFixedNature, IFixedAbilityNumber, IShi
public sbyte TeraType { get; init; } = -1;
// unused
public int HPType { get; init; } = -1;
public sbyte HPType { get; init; } = -1;
private const int RandomIV = -1;
@@ -102,12 +102,12 @@ public static EncounterCriteria GetCriteria(IBattleTemplate s, IPersonalTable t)
public static EncounterCriteria GetCriteria(IBattleTemplate s, IPersonalInfo pi) => new()
{
Gender = GetGenderPermissions(s.Gender, pi),
IV_HP = s.IVs[0],
IV_ATK = s.IVs[1],
IV_DEF = s.IVs[2],
IV_SPE = s.IVs[3],
IV_SPA = s.IVs[4],
IV_SPD = s.IVs[5],
IV_HP = (sbyte)s.IVs[0],
IV_ATK = (sbyte)s.IVs[1],
IV_DEF = (sbyte)s.IVs[2],
IV_SPE = (sbyte)s.IVs[3],
IV_SPA = (sbyte)s.IVs[4],
IV_SPD = (sbyte)s.IVs[5],
HPType = s.HiddenPowerType,
Ability = GetAbilityPermissions(s.Ability, pi),
@@ -116,14 +116,14 @@ public static EncounterCriteria GetCriteria(IBattleTemplate s, IPersonalInfo pi)
TeraType = (sbyte)s.TeraType,
};
private static byte? GetGenderPermissions(byte? gender, IGenderDetail pi)
private static Gender GetGenderPermissions(byte? gender, IGenderDetail pi)
{
if (gender is not <= 1)
return null;
return Gender.Random;
if (pi.IsDualGender)
return gender;
return (Gender)gender;
var g = pi.FixedGender();
return g <= 1 ? g : null;
return g <= 1 ? (Gender)g : Gender.Random;
}
private static AbilityPermission GetAbilityPermissions(int ability, IPersonalAbility pi)
@@ -169,12 +169,12 @@ public Nature GetNature()
/// <summary>
/// Indicates if the <see cref="Gender"/> is specified.
/// </summary>
public bool IsGenderSpecified => Gender != null;
public bool IsGenderSpecified => Gender != Gender.Random;
/// <summary>
/// Indicates if the requested gender matches the criteria.
/// </summary>
public bool IsGenderSatisfied(byte gender) => !IsGenderSpecified || gender == Gender;
public bool IsGenderSatisfied(byte gender) => !IsGenderSpecified || (Gender)gender == Gender;
/// <summary>
/// Gets the gender to generate, random if unspecified by the template or criteria.
@@ -189,7 +189,7 @@ public byte GetGender(byte gender, IGenderDetail pkPersonalInfo)
/// <inheritdoc cref="GetGender(byte, IGenderDetail)"/>
public byte GetGender(Gender gender, IGenderDetail pkPersonalInfo)
{
if (gender == Core.Gender.Random)
if (gender == Gender.Random)
return GetGender(pkPersonalInfo);
return (byte)gender;
}
@@ -203,8 +203,8 @@ public byte GetGender(IGenderDetail pkPersonalInfo)
return pkPersonalInfo.FixedGender();
if (pkPersonalInfo.Genderless)
return 2;
if (Gender is { } request and (0 or 1))
return request;
if (Gender is not Gender.Random)
return (byte)Gender;
return pkPersonalInfo.RandomGender();
}
@@ -356,4 +356,10 @@ public bool IsCompatibleIVs(ReadOnlySpan<int> ivs)
return false;
return true;
}
public void GetCombinedIVs(out uint iv1, out uint iv2)
{
iv1 = (byte)IV_HP | (uint)IV_ATK << 5 | (uint)IV_DEF << 10;
iv2 = (byte)IV_SPE | (uint)IV_SPA << 5 | (uint)IV_SPD << 10;
}
}

View File

@@ -14,7 +14,7 @@ public static class MethodCXD
public static bool SetFromIVs<T>(this T enc, G3PKM pk, EncounterCriteria criteria, PersonalInfo3 pi, bool noShiny = false) where T : IShadow3
{
var gr = pi.Gender;
(uint iv1, uint iv2) = GetCombinedIVs(criteria);
criteria.GetCombinedIVs(out var iv1, out var iv2);
Span<uint> all = stackalloc uint[XDRNG.MaxCountSeedsIV];
var count = XDRNG.GetSeedsIVs(all, iv1 << 16, iv2 << 16);
var seeds = all[..count];
@@ -52,7 +52,7 @@ public static class MethodCXD
public static bool SetFromIVsCXD(G3PKM pk, EncounterCriteria criteria, PersonalInfo3 pi, bool noShiny = true)
{
var gr = pi.Gender;
(uint iv1, uint iv2) = GetCombinedIVs(criteria);
criteria.GetCombinedIVs(out var iv1, out var iv2);
Span<uint> all = stackalloc uint[XDRNG.MaxCountSeedsIV];
var count = XDRNG.GetSeedsIVs(all, iv1 << 16, iv2 << 16);
var seeds = all[..count];
@@ -174,13 +174,6 @@ private static uint GetPIDStarterMale(ref uint seed, uint id32)
private static uint GetPIDRegular(uint a, uint b) => a << 16 | b;
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);
}
private static void SetIVs(G3PKM pk, uint iv1, uint iv2)
{
Span<int> ivs = stackalloc int[6];

View File

@@ -95,7 +95,7 @@ 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);
criteria.GetCombinedIVs(out var iv1, out var iv2);
Span<uint> all = stackalloc uint[LCRNG.MaxCountSeedsIV];
var count = LCRNGReversal.GetSeedsIVs(all, iv1 << 16, iv2 << 16);
var seeds = all[..count];
@@ -163,7 +163,7 @@ public static bool SetFromIVs<T>(this T enc, PK3 pk, EncounterCriteria criteria)
public static bool SetFromIVsUnown<T>(this T enc, PK3 pk, EncounterCriteria criteria)
where T : IEncounterSlot3
{
(uint iv1, uint iv2) = GetCombinedIVs(criteria);
criteria.GetCombinedIVs(out var iv1, out var iv2);
Span<uint> all = stackalloc uint[LCRNG.MaxCountSeedsIV];
var count = LCRNGReversal.GetSeedsIVs(all, iv1 << 16, iv2 << 16);
var seeds = all[..count];
@@ -235,11 +235,4 @@ private static void SetPIDIVSequential(PK3 pk, uint pid, uint rand)
pk.IV32 = ((iv2 & 0x7FFF) << 15) | (iv1 & 0x7FFF);
pk.RefreshAbility((int)(pid & 1));
}
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);
}
}

View File

@@ -564,10 +564,11 @@ private static bool IsOriginalLevelValid(byte min, byte max, byte format, uint l
return LevelRangeExtensions.IsLevelWithinRange((int)level, min, max);
}
private static uint GetExpectedLevel(ILevelRange enc, uint u16LevelRand)
private static uint GetExpectedLevel<T>(T enc, uint u16LevelRand) where T : ILevelRange
{
uint mod = 1u + enc.LevelMax - enc.LevelMin;
return (u16LevelRand % mod) + enc.LevelMin;
var min = enc.LevelMin;
uint mod = 1u + enc.LevelMax - min;
return (u16LevelRand % mod) + min;
}
private static bool IsRockSmashPossible(byte areaRate, ref uint seed)

View File

@@ -89,7 +89,7 @@ public static bool SetFromIVsJ<T>(this T enc, PK4 pk, PersonalInfo4 pi, Encounte
where T : IEncounterSlot4
{
var gr = pi.Gender;
(uint iv1, uint iv2) = GetCombinedIVs(criteria);
criteria.GetCombinedIVs(out var iv1, out var iv2);
Span<uint> all = stackalloc uint[LCRNG.MaxCountSeedsIV];
var count = LCRNGReversal.GetSeedsIVs(all, iv1 << 16, iv2 << 16);
var seeds = all[..count];
@@ -124,11 +124,4 @@ public static bool SetFromIVsJ<T>(this T enc, PK4 pk, PersonalInfo4 pi, Encounte
}
public static uint GetPIDRegular(uint a, uint b) => b << 16 | a;
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);
}
}

View File

@@ -88,7 +88,7 @@ public static bool SetFromIVsK<T>(this T enc, PK4 pk, PersonalInfo4 pi, Encounte
where T : IEncounterSlot4
{
var gr = pi.Gender;
(uint iv1, uint iv2) = GetCombinedIVs(criteria);
criteria.GetCombinedIVs(out var iv1, out var iv2);
Span<uint> all = stackalloc uint[LCRNG.MaxCountSeedsIV];
var count = LCRNGReversal.GetSeedsIVs(all, iv1 << 16, iv2 << 16);
var seeds = all[..count];
@@ -123,11 +123,4 @@ public static bool SetFromIVsK<T>(this T enc, PK4 pk, PersonalInfo4 pi, Encounte
}
public static uint GetPIDRegular(uint a, uint b) => b << 16 | a;
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);
}
}

View File

@@ -456,10 +456,11 @@ private static bool IsOriginalLevelValid(byte min, byte max, byte format, uint l
return LevelRangeExtensions.IsLevelWithinRange((int)level, min, max);
}
private static uint GetExpectedLevel(ILevelRange enc, uint u16LevelRand)
private static uint GetExpectedLevel<T>(T enc, uint u16LevelRand) where T : ILevelRange
{
uint mod = 1u + enc.LevelMax - enc.LevelMin;
return (u16LevelRand % mod) + enc.LevelMin;
var min = enc.LevelMin;
uint mod = 1u + enc.LevelMax - min;
return (u16LevelRand % mod) + min;
}
/// <summary>

View File

@@ -482,10 +482,11 @@ private static bool IsOriginalLevelValid(byte min, byte max, byte format, uint l
return LevelRangeExtensions.IsLevelWithinRange((int)level, min, max);
}
private static uint GetExpectedLevel(ILevelRange enc, uint u16LevelRand)
private static uint GetExpectedLevel<T>(T enc, uint u16LevelRand) where T : ILevelRange
{
uint mod = 1u + enc.LevelMax - enc.LevelMin;
return (u16LevelRand % mod) + enc.LevelMin;
var min = enc.LevelMin;
uint mod = 1u + enc.LevelMax - min;
return (u16LevelRand % mod) + min;
}
private static bool IsBugContestPossibleDeadlock(uint areaRate, ref uint result)

View File

@@ -187,7 +187,7 @@ private EncounterCriteria GetCriteria(ISpeciesForm enc, EncounterDatabaseSetting
var set = new ShowdownSet(editor);
var criteria = EncounterCriteria.GetCriteria(set, editor.PersonalInfo);
if (!isInChain)
criteria = criteria with { Gender = default }; // Genderless tabs and a gendered enc -> let's play safe.
criteria = criteria with { Gender = Gender.Random }; // Genderless tabs and a gendered enc -> let's play safe.
return criteria;
}