Update 22.12.01

This commit is contained in:
Kurt 2022-12-01 22:40:11 -08:00
parent f10c914eac
commit 2e67526db1
15 changed files with 354 additions and 31 deletions

View file

@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>22.11.26</Version>
<Version>22.12.01</Version>
<LangVersion>10</LangVersion>
<Nullable>enable</Nullable>
<NeutralLanguage>en</NeutralLanguage>

View file

@ -106,7 +106,8 @@ internal static class Encounters9
private static readonly EncounterTera9[] Tera = EncounterTera9.GetArray(Get("gem_paldea"));
private static readonly EncounterDist9[] Dist = EncounterDist9.GetArray(Get("dist_paldea"));
private static readonly EncounterMight9[] Might = EncounterMight9.GetArray(Get("might_paldea"));
private static readonly EncounterFixed9[] Fixed = EncounterFixed9.GetArray(Get("fixed_paldea"));
internal static readonly EncounterStatic[] StaticSL = ArrayUtil.ConcatAll<EncounterStatic>(GetEncounters(Encounter_SV, SL), Tera, Dist, Fixed);
internal static readonly EncounterStatic[] StaticVL = ArrayUtil.ConcatAll<EncounterStatic>(GetEncounters(Encounter_SV, VL), Tera, Dist, Fixed);
internal static readonly EncounterStatic[] StaticSL = ArrayUtil.ConcatAll<EncounterStatic>(GetEncounters(Encounter_SV, SL), Tera, Dist, Might, Fixed);
internal static readonly EncounterStatic[] StaticVL = ArrayUtil.ConcatAll<EncounterStatic>(GetEncounters(Encounter_SV, VL), Tera, Dist, Might, Fixed);
}

View file

@ -13,7 +13,7 @@ public sealed record EncounterDist9 : EncounterStatic, ITeraRaid9
public byte Index { get; private init; }
public byte Stars { get; private init; }
public byte RandRate { get; private init; } // weight chance of this encounter
public ushort RandRate0MinScarlet { get; private init; }
public ushort RandRate0MinViolet { get; private init; }
public ushort RandRate0TotalScarlet { get; private init; }
@ -138,7 +138,6 @@ public sealed record EncounterDist9 : EncounterStatic, ITeraRaid9
return false;
}
public static EncounterDist9[] GetArray(ReadOnlySpan<byte> data)
{
var count = data.Length / SerializedSize;

View file

@ -0,0 +1,289 @@
using System;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core;
public sealed record EncounterMight9 : EncounterStatic, ITeraRaid9
{
public override int Generation => 9;
public override int Location => Locations.TeraCavern9;
public override EntityContext Context => EntityContext.Gen9;
public bool IsDistribution => Index != 0;
public GemType TeraType { get; private init; }
public byte Index { get; private init; }
public byte Stars { get; private init; }
public byte RandRate { get; private init; } // weight chance of this encounter
public byte Scale { get; init; }
public ushort RandRate0MinScarlet { get; private init; }
public ushort RandRate0MinViolet { get; private init; }
public ushort RandRate0TotalScarlet { get; private init; }
public ushort RandRate0TotalViolet { get; private init; }
public ushort RandRate1MinScarlet { get; private init; }
public ushort RandRate1MinViolet { get; private init; }
public ushort RandRate1TotalScarlet { get; private init; }
public ushort RandRate1TotalViolet { get; private init; }
public ushort RandRate2MinScarlet { get; private init; }
public ushort RandRate2MinViolet { get; private init; }
public ushort RandRate2TotalScarlet { get; private init; }
public ushort RandRate2TotalViolet { get; private init; }
public ushort RandRate3MinScarlet { get; private init; }
public ushort RandRate3MinViolet { get; private init; }
public ushort RandRate3TotalScarlet { get; private init; }
public ushort RandRate3TotalViolet { get; private init; }
public ushort GetRandRateTotalScarlet(int stage) => stage switch
{
0 => RandRate0TotalScarlet,
1 => RandRate1TotalScarlet,
2 => RandRate2TotalScarlet,
3 => RandRate3TotalScarlet,
_ => throw new ArgumentOutOfRangeException(nameof(stage)),
};
public ushort GetRandRateTotalViolet(int stage) => stage switch
{
0 => RandRate0TotalViolet,
1 => RandRate1TotalViolet,
2 => RandRate2TotalViolet,
3 => RandRate3TotalViolet,
_ => throw new ArgumentOutOfRangeException(nameof(stage)),
};
public ushort GetRandRateMinScarlet(int stage) => stage switch
{
0 => RandRate0MinScarlet,
1 => RandRate1MinScarlet,
2 => RandRate2MinScarlet,
3 => RandRate3MinScarlet,
_ => throw new ArgumentOutOfRangeException(nameof(stage)),
};
public ushort GetRandRateMinViolet(int stage) => stage switch
{
0 => RandRate0MinViolet,
1 => RandRate1MinViolet,
2 => RandRate2MinViolet,
3 => RandRate3MinViolet,
_ => throw new ArgumentOutOfRangeException(nameof(stage)),
};
private const int StageCount = 4;
private const int StageNone = -1;
public bool CanBeEncountered(uint seed) => GetProgressMaximum(seed) != StageNone;
public int ProgressStageMin
{
get
{
for (int stage = 0; stage < StageCount; stage++)
{
if (GetRandRateTotalScarlet(stage) != 0 || GetRandRateTotalViolet(stage) != 0)
return stage;
}
return StageNone;
}
}
public int ProgressStageMax
{
get
{
for (int stage = StageCount - 1; stage >= 0; stage--)
{
if (GetRandRateTotalScarlet(stage) != 0 || GetRandRateTotalViolet(stage) != 0)
return stage;
}
return StageNone;
}
}
public int GetProgressMaximum(uint seed)
{
// We loop from the highest progress, since that is where the majority of samples will be from.
for (int i = StageCount - 1; i >= 0; i--)
{
if (GetIsPossibleSlot(seed, i))
return i;
}
return StageNone;
}
private bool GetIsPossibleSlot(uint seed, int stage)
{
var totalScarlet = GetRandRateTotalScarlet(stage);
if (totalScarlet != 0)
{
var rand = new Xoroshiro128Plus(seed);
_ = rand.NextInt(100);
var val = rand.NextInt(totalScarlet);
var min = GetRandRateMinScarlet(stage);
if ((uint)((int)val - min) < RandRate)
return true;
}
var totalViolet = GetRandRateTotalViolet(stage);
if (totalViolet != 0)
{
var rand = new Xoroshiro128Plus(seed);
_ = rand.NextInt(100);
var val = rand.NextInt(totalViolet);
var min = GetRandRateMinViolet(stage);
if ((uint)((int)val - min) < RandRate)
return true;
}
return false;
}
public static EncounterMight9[] GetArray(ReadOnlySpan<byte> data)
{
var count = data.Length / SerializedSize;
var result = new EncounterMight9[count];
for (int i = 0; i < result.Length; i++)
result[i] = ReadEncounter(data.Slice(i * SerializedSize, SerializedSize));
return result;
}
private EncounterMight9() : base(GameVersion.SV) { }
private const int SerializedSize = WeightStart + (sizeof(ushort) * 2 * 2 * 4) + 10;
private const int WeightStart = 0x14;
private static EncounterMight9 ReadEncounter(ReadOnlySpan<byte> data) => new()
{
Species = ReadUInt16LittleEndian(data),
Form = data[0x02],
Gender = (sbyte)(data[0x03] - 1),
Ability = GetAbility(data[0x04]),
FlawlessIVCount = data[5],
Shiny = data[0x06] switch { 0 => Shiny.Random, 1 => Shiny.Never, 2 => Shiny.Always, _ => throw new ArgumentOutOfRangeException(nameof(data)) },
Level = data[0x07],
Moves = new Moveset(
ReadUInt16LittleEndian(data[0x08..]),
ReadUInt16LittleEndian(data[0x0A..]),
ReadUInt16LittleEndian(data[0x0C..]),
ReadUInt16LittleEndian(data[0x0E..])),
TeraType = (GemType)data[0x10],
Index = data[0x11],
Stars = data[0x12],
RandRate = data[0x13],
RandRate0MinScarlet = ReadUInt16LittleEndian(data[WeightStart..]),
RandRate0MinViolet = ReadUInt16LittleEndian(data[(WeightStart + sizeof(ushort))..]),
RandRate0TotalScarlet = ReadUInt16LittleEndian(data[(WeightStart + (sizeof(ushort) * 2))..]),
RandRate0TotalViolet = ReadUInt16LittleEndian(data[(WeightStart + (sizeof(ushort) * 3))..]),
RandRate1MinScarlet = ReadUInt16LittleEndian(data[(WeightStart + (sizeof(ushort) * 4))..]),
RandRate1MinViolet = ReadUInt16LittleEndian(data[(WeightStart + (sizeof(ushort) * 5))..]),
RandRate1TotalScarlet = ReadUInt16LittleEndian(data[(WeightStart + (sizeof(ushort) * 6))..]),
RandRate1TotalViolet = ReadUInt16LittleEndian(data[(WeightStart + (sizeof(ushort) * 7))..]),
RandRate2MinScarlet = ReadUInt16LittleEndian(data[(WeightStart + (sizeof(ushort) * 8))..]),
RandRate2MinViolet = ReadUInt16LittleEndian(data[(WeightStart + (sizeof(ushort) * 9))..]),
RandRate2TotalScarlet = ReadUInt16LittleEndian(data[(WeightStart + (sizeof(ushort) * 10))..]),
RandRate2TotalViolet = ReadUInt16LittleEndian(data[(WeightStart + (sizeof(ushort) * 11))..]),
RandRate3MinScarlet = ReadUInt16LittleEndian(data[(WeightStart + (sizeof(ushort) * 12))..]),
RandRate3MinViolet = ReadUInt16LittleEndian(data[(WeightStart + (sizeof(ushort) * 13))..]),
RandRate3TotalScarlet = ReadUInt16LittleEndian(data[(WeightStart + (sizeof(ushort) * 14))..]),
RandRate3TotalViolet = ReadUInt16LittleEndian(data[(WeightStart + (sizeof(ushort) * 15))..]),
Nature = (Nature)data[0x34],
IVs = new IndividualValueSet((sbyte)data[0x35], (sbyte)data[0x36], (sbyte)data[0x37], (sbyte)data[0x38], (sbyte)data[0x39], (sbyte)data[0x3A], data[0x3B]),
Scale = data[0x3C],
};
private static AbilityPermission GetAbility(byte b) => b switch
{
0 => AbilityPermission.Any12,
1 => AbilityPermission.Any12H,
2 => AbilityPermission.OnlyFirst,
3 => AbilityPermission.OnlySecond,
4 => AbilityPermission.OnlyHidden,
_ => throw new ArgumentOutOfRangeException(nameof(b), b, null),
};
protected override EncounterMatchRating IsMatchDeferred(PKM pk)
{
if (Ability != AbilityPermission.Any12H)
{
// HA-Only is a strict match. Ability Capsule and Patch can potentially change these.
var num = pk.AbilityNumber;
if (num == 4)
{
if (Ability is not AbilityPermission.OnlyHidden && !AbilityVerifier.CanAbilityPatch(9, PersonalTable.SV.GetFormEntry(Species, Form), pk.Species))
return EncounterMatchRating.DeferredErrors;
}
else if (Ability.IsSingleValue(out int index) && 1 << index != num) // Fixed regular ability
{
if (Ability is AbilityPermission.OnlyFirst or AbilityPermission.OnlySecond && !AbilityVerifier.CanAbilityCapsule(9, PersonalTable.SV.GetFormEntry(Species, Form)))
return EncounterMatchRating.DeferredErrors;
}
}
return base.IsMatchDeferred(pk);
}
protected override bool IsMatchPartial(PKM pk)
{
switch (Shiny)
{
case Shiny.Never when pk.IsShiny:
case Shiny.Always when !pk.IsShiny:
return true;
}
if (pk is IScaledSize3 s3 && s3.Scale != Scale)
return true;
if (IVs.IsSpecified && !Legal.GetIsFixedIVSequenceValidSkipRand(IVs, pk))
return true;
var seed = Tera9RNG.GetOriginalSeed(pk);
if (pk is ITeraType t && !Tera9RNG.IsMatchTeraType(seed, TeraType, Species, Form, (byte)t.TeraTypeOriginal))
return true;
if (!CanBeEncountered(seed))
return true;
byte gender = GetGender();
var param = new GenerateParam9(gender, FlawlessIVCount, 1, 0, 0, Scale, Ability, Shiny, Nature, IVs);
if (!Encounter9RNG.IsMatch(pk, param, seed))
return true;
return base.IsMatchPartial(pk);
}
protected override void ApplyDetails(ITrainerInfo tr, EncounterCriteria criteria, PKM pk)
{
base.ApplyDetails(tr, criteria, pk);
var pk9 = (PK9)pk;
pk9.Obedience_Level = (byte)pk9.Met_Level;
pk9.RibbonMarkMightiest = true;
}
protected override void SetPINGA(PKM pk, EncounterCriteria criteria)
{
var pk9 = (PK9)pk;
const byte rollCount = 1;
const byte undefinedSize = 0;
byte gender = GetGender();
var param = new GenerateParam9(gender, FlawlessIVCount, rollCount,
undefinedSize, undefinedSize, Scale,
Ability, Shiny, Nature, IVs);
var init = Util.Rand.Rand64();
var success = this.TryApply32(pk9, init, param, criteria);
if (!success)
this.TryApply32(pk9, init, param, EncounterCriteria.Unrestricted);
}
private byte GetGender() => Gender switch
{
0 => PersonalInfo.RatioMagicMale,
1 => PersonalInfo.RatioMagicFemale,
2 => PersonalInfo.RatioMagicGenderless,
_ => (byte)PersonalTable.SV.GetFormEntry(Species, Form).Gender,
};
}

View file

@ -15,7 +15,7 @@ public static class Encounter9RNG
uint seed = (uint)rand.NextInt(uint.MaxValue);
if (!enc.CanBeEncountered(seed))
continue;
if (!GenerateData(pk, param, criteria, seed))
if (!GenerateData(pk, param, criteria, seed, param.IVs.IsSpecified))
continue;
var type = Tera9RNG.GetTeraType(seed, enc.TeraType, enc.Species, enc.Form);
@ -78,24 +78,30 @@ public static class Encounter9RNG
const int UNSET = -1;
Span<int> ivs = stackalloc[] { UNSET, UNSET, UNSET, UNSET, UNSET, UNSET };
const int MAX = 31;
for (int i = 0; i < enc.FlawlessIVs; i++)
if (enc.IVs.IsSpecified)
{
int index;
do { index = (int)rand.NextInt(6); }
while (ivs[index] != UNSET);
ivs[index] = MAX;
enc.IVs.CopyToSpeedLast(ivs);
}
else
{
const int MAX = 31;
for (int i = 0; i < enc.FlawlessIVs; i++)
{
int index;
do { index = (int)rand.NextInt(6); }
while (ivs[index] != UNSET);
ivs[index] = MAX;
}
}
if (!ignoreIVs && !criteria.IsIVsCompatible(ivs, 9))
return false;
for (int i = 0; i < 6; i++)
{
if (ivs[i] == UNSET)
ivs[i] = (int)rand.NextInt(32);
}
if (!ignoreIVs && !criteria.IsIVsCompatible(ivs, 9))
return false;
pk.IV_HP = ivs[0];
pk.IV_ATK = ivs[1];
pk.IV_DEF = ivs[2];
@ -123,7 +129,7 @@ public static class Encounter9RNG
return false;
pk.Gender = gender;
byte nature = pk.Species == (int)Species.Toxtricity
byte nature = enc.Nature != Nature.Random ? (byte)enc.Nature : pk.Species == (int)Species.Toxtricity
? ToxtricityUtil.GetRandomNature(ref rand, pk.Form)
: (byte)rand.NextInt(25);
if (criteria.Nature != Nature.Random && nature != (int)criteria.Nature)
@ -180,13 +186,20 @@ public static class Encounter9RNG
const int UNSET = -1;
Span<int> ivs = stackalloc[] { UNSET, UNSET, UNSET, UNSET, UNSET, UNSET };
const int MAX = 31;
for (int i = 0; i < enc.FlawlessIVs; i++)
if (enc.IVs.IsSpecified)
{
int index;
do { index = (int)rand.NextInt(6); }
while (ivs[index] != UNSET);
ivs[index] = MAX;
enc.IVs.CopyToSpeedLast(ivs);
}
else
{
const int MAX = 31;
for (int i = 0; i < enc.FlawlessIVs; i++)
{
int index;
do { index = (int)rand.NextInt(6); }
while (ivs[index] != UNSET);
ivs[index] = MAX;
}
}
for (int i = 0; i < 6; i++)
@ -226,7 +239,7 @@ public static class Encounter9RNG
if (pk.Gender != gender)
return false;
int nature = pk.Species == (int)Species.Toxtricity
byte nature = enc.Nature != Nature.Random ? (byte)enc.Nature : pk.Species == (int)Species.Toxtricity
? ToxtricityUtil.GetRandomNature(ref rand, pk.Form)
: (byte)rand.NextInt(25);
if (pk.Nature != nature)

View file

@ -11,5 +11,8 @@ namespace PKHeX.Core;
/// <param name="Scale">Scale value to generate. If zero, full random.</param>
/// <param name="Ability">Ability type to generate.</param>
/// <param name="Shiny">PID generation type.</param>
public readonly record struct GenerateParam9(byte GenderRatio, byte FlawlessIVs, byte RollCount, byte Height, byte Weight, byte Scale,
AbilityPermission Ability = AbilityPermission.Any12, Shiny Shiny = Shiny.Random);
/// <param name="Nature">Nature specification.</param>
/// <param name="IVs">IV specification.</param>
public readonly record struct GenerateParam9(byte GenderRatio, byte FlawlessIVs, byte RollCount, byte Height,
byte Weight, byte Scale, AbilityPermission Ability = AbilityPermission.Any12, Shiny Shiny = Shiny.Random,
Nature Nature = Nature.Random, IndividualValueSet IVs = default);

View file

@ -173,7 +173,6 @@ public sealed class MiscVerifier : Verifier
private static readonly HashSet<int> UnreleasedSV = new()
{
(int)Species.Charmander, // Charmander : distribution raids happening on Dec 1, 2022
(int)Species.Raichu | (1 << 11), // Diglett-1
(int)Species.Diglett | (1 << 11), // Diglett-1
(int)Species.Meowth | (1 << 11), // Meowth-1

View file

@ -160,7 +160,7 @@ public static class MarkRules
public static bool IsMarkPresentMightiest(IEncounterTemplate enc)
{
// 7 star raids only.
return enc is EncounterTera9 { Stars: 7 };
return enc is EncounterMight9 { Stars: 7 };
}
/// <summary>

View file

@ -312,7 +312,7 @@ public sealed class WC9 : DataMysteryGift, ILangNick, INature, ITeraType, IRibbo
}
public int GetLanguage(int redeemLanguage) => Data[GetLanguageOffset(GetLanguageIndex(redeemLanguage))];
private static int GetLanguageOffset(int index) => 0x30 + (index * 0x1C) + 0x1A;
private static int GetLanguageOffset(int index) => 0x28 + (index * 0x1C) + 0x1A;
public bool GetHasOT(int language) => ReadUInt16LittleEndian(Data.AsSpan(GetOTOffset(language))) != 0;

View file

@ -119,4 +119,5 @@ public enum TeraRaidContentType : uint
Base05,
Black6,
Distribution,
Might7,
}

View file

@ -19,8 +19,12 @@ public static class SaveUtil
public const int SIZE_G9_0a = 0x31627C; // 1.0.0 after multiplayer
public const int SIZE_G9_1 = 0x319DB3; // 1.0.1 fresh
public const int SIZE_G9_1a = 0x319DC0; // 1.0.1 after multiplayer
public const int SIZE_G9_3 = 0x319DC3; // 1.1.0 fresh
public const int SIZE_G9_1Ba = 0x319DD0; // 1.0.1 -> 1.1.0
public const int SIZE_G9_1A = 0x31A2C0; // 1.0.0 -> 1.0.1
public const int SIZE_G9_1Aa = 0x31A2CD; // 1.0.1 -> 1.0.1 after multiplayer
public const int SIZE_G9_1Aa = 0x31A2CD; // 1.0.0 -> 1.0.1 -> 1.0.1 after multiplayer
public const int SIZE_G9_1Ab = 0x31A2DD; // 1.0.0 -> 1.0.1 -> 1.0.1 after multiplayer -> 1.1.0
public const int SIZE_G9_2 = 0x31A2D0; // 1.0.0 -> 1.1.0
public const int SIZE_G8LA = 0x136DDE;
public const int SIZE_G8LA_1 = 0x13AD06;
@ -106,6 +110,8 @@ public static class SaveUtil
SIZE_G9_0, SIZE_G9_0a,
SIZE_G9_1, SIZE_G9_1a,
SIZE_G9_1A, SIZE_G9_1Aa,
SIZE_G9_1Ba, SIZE_G9_1Ab,
SIZE_G9_2, SIZE_G9_3,
};
private static readonly HashSet<int> SizesSWSH = new()

View file

@ -1,7 +1,19 @@
PKHeX - By Kaphotics
http://projectpokemon.org/pkhex/
22/11/26 - New Update:
22/12/01 - New Update:
- Added support for Scarlet & Violet 1.1.0.
- Legality:
- - Fixed: Gen9 banned species list updated.
- - Fixed: Gen9 TR flags now check pre-evolutions if the current evolution does not have a required move flag.
- - Fixed: Ability patch reverting check updated to account for single-evolution-chain cases.
- Fixed: Showdown Set imports without a Tera Type will default to the species' first type.
- Fixed: WC9 now generates with correct SID7. Thanks @Manu098vm !
- Fixed: Associating a pk9 file to PKHeX.exe now starts up properly.
- Fixed: Form argument suggestion for Mankey & Pawniard, when evolved to max evo stage, added.
- Changed: Updated translation files. Thanks @easyworld, @Yarkis01
22/11/26 - New Update: (98776) [5755880]
- Legality:
- - Fixed: Encounter->PK9 small fixes added.
- - Added: Distribution Raids (Eevee) now recognized.

View file

@ -16,6 +16,6 @@ public class MarshalTests
Marshal.SizeOf(typeof(EvolutionMethod)).Should().Be(8);
Marshal.SizeOf(typeof(Moveset)).Should().Be(8);
Marshal.SizeOf(typeof(IndividualValueSet)).Should().BeLessOrEqualTo(8);
Marshal.SizeOf(typeof(GenerateParam9)).Should().BeLessOrEqualTo(8);
Marshal.SizeOf(typeof(GenerateParam9)).Should().BeLessOrEqualTo(16);
}
}