mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-26 14:00:21 +00:00
Minor minor perf
Small changes to reduce some allocations
This commit is contained in:
parent
e2fd292ffa
commit
c73264d4f3
50 changed files with 338 additions and 219 deletions
|
@ -120,8 +120,8 @@ public static class BallApplicator
|
|||
|
||||
static BallApplicator()
|
||||
{
|
||||
Span<Ball> exclude = stackalloc Ball[] {None, Poke};
|
||||
Span<Ball> end = stackalloc Ball[] {Poke};
|
||||
ReadOnlySpan<Ball> exclude = stackalloc Ball[] {None, Poke};
|
||||
ReadOnlySpan<Ball> end = stackalloc Ball[] {Poke};
|
||||
Span<Ball> all = stackalloc Ball[BallList.Length - exclude.Length];
|
||||
all = all[..FillExcept(all, exclude, BallList)];
|
||||
|
||||
|
|
|
@ -174,13 +174,13 @@ public static class CommonEdits
|
|||
// Under this scenario, just force the Hidden Power type.
|
||||
if (Array.IndexOf(Set.Moves, (ushort)Move.HiddenPower) != -1 && pk.HPType != Set.HiddenPowerType)
|
||||
{
|
||||
if (Array.Exists(Set.IVs, static iv => iv >= 30))
|
||||
if (Set.IVs.AsSpan().IndexOfAny(30, 31) >= 0)
|
||||
pk.SetHiddenPower(Set.HiddenPowerType);
|
||||
}
|
||||
|
||||
// In Generation 1/2 Format sets, when EVs are not specified at all, it implies maximum EVs instead!
|
||||
// Under this scenario, just apply maximum EVs (65535).
|
||||
if (Array.TrueForAll(Set.EVs, static ev => ev == 0))
|
||||
if (Set.EVs.AsSpan().IndexOfAnyExcept(0) == -1)
|
||||
gb.MaxEVs();
|
||||
else
|
||||
pk.SetEVs(Set.EVs);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
|
@ -10,22 +10,53 @@ namespace PKHeX.Core;
|
|||
public sealed class EventWork<T> : EventVar where T : struct
|
||||
{
|
||||
public T Value;
|
||||
|
||||
/// <summary>
|
||||
/// Values with known behavior. They are labeled with a humanized string.
|
||||
/// </summary>
|
||||
public readonly IList<EventWorkVal> Options = new List<EventWorkVal> { new() };
|
||||
|
||||
public EventWork(int index, EventVarType t, IReadOnlyList<string> pieces) : base(index, t, pieces[1])
|
||||
{
|
||||
if (pieces.Count < 3)
|
||||
return;
|
||||
if (pieces.Count >= 3)
|
||||
AddLabeledValueOptions(pieces[2], Options);
|
||||
}
|
||||
|
||||
var items = pieces[2]
|
||||
.Split(',')
|
||||
.Select(z => z.Split(':'))
|
||||
.Where(z => z.Length == 2);
|
||||
private const char OptionSeparator = ','; // KVP,KVP
|
||||
private const char OptionValueSeparator = ':'; // Name:Value
|
||||
|
||||
foreach (var s in items)
|
||||
/// <summary>
|
||||
/// Adds the key-value pair options to the <see cref="list"/>.
|
||||
/// </summary>
|
||||
/// <param name="text">Text line containing key-value pairs.</param>
|
||||
/// <param name="list">Object that stores the results</param>
|
||||
/// <remarks>
|
||||
/// Decodes in the format of Value:Text,Value:Text.
|
||||
/// Key-value pairs are separated by <see cref="OptionSeparator"/>.
|
||||
/// Key and value are separated by <see cref="OptionValueSeparator"/>.
|
||||
/// </remarks>
|
||||
private static void AddLabeledValueOptions(ReadOnlySpan<char> text, ICollection<EventWorkVal> list)
|
||||
{
|
||||
int i = 0;
|
||||
while (i < text.Length)
|
||||
{
|
||||
if (int.TryParse(s[0], out var value))
|
||||
Options.Add(new EventWorkVal(s[1], value));
|
||||
// Scan for the length of this key-value pair.
|
||||
int commaIndex = text[i..].IndexOf(OptionSeparator);
|
||||
if (commaIndex == -1)
|
||||
commaIndex = text.Length - i; // end of string
|
||||
|
||||
// Scan for the length of the key.
|
||||
var pair = text.Slice(i, commaIndex);
|
||||
i += commaIndex + 1; // skip to next kvp for next iteration
|
||||
int colonIndex = pair.IndexOf(OptionValueSeparator);
|
||||
if (colonIndex == -1)
|
||||
continue; // invalid kvp
|
||||
|
||||
// Split to get the key and value.
|
||||
var valueText = pair[..colonIndex];
|
||||
var display = pair[(colonIndex + 1)..];
|
||||
if (int.TryParse(valueText, out int value))
|
||||
list.Add(new EventWorkVal(display.ToString(), value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
|
@ -10,7 +9,6 @@ public abstract record EncounterArea(GameVersion Version) : IVersion
|
|||
{
|
||||
public int Location { get; protected init; }
|
||||
public SlotType Type { get; protected init; }
|
||||
protected abstract IReadOnlyList<EncounterSlot> Raw { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the slots contained in the area that match the provided data.
|
||||
|
@ -26,6 +24,9 @@ public abstract record EncounterArea(GameVersion Version) : IVersion
|
|||
/// <param name="location">Met Location ID</param>
|
||||
/// <returns>True if possibly originated from this area, false otherwise.</returns>
|
||||
public virtual bool IsMatchLocation(int location) => Location == location;
|
||||
|
||||
public bool HasSpecies(ushort species) => Raw.Any(z => z.Species == species);
|
||||
}
|
||||
|
||||
internal interface IMemorySpeciesArea
|
||||
{
|
||||
bool HasSpecies(ushort species);
|
||||
}
|
||||
|
|
|
@ -12,8 +12,6 @@ public sealed record EncounterArea1 : EncounterArea
|
|||
public readonly int Rate;
|
||||
public readonly EncounterSlot1[] Slots;
|
||||
|
||||
protected override IReadOnlyList<EncounterSlot1> Raw => Slots;
|
||||
|
||||
public static EncounterArea1[] GetAreas(BinLinkerAccessor input, GameVersion game)
|
||||
{
|
||||
var result = new EncounterArea1[input.Length];
|
||||
|
|
|
@ -18,8 +18,6 @@ public sealed record EncounterArea2 : EncounterArea
|
|||
public readonly byte[]? Rates;
|
||||
public readonly EncounterSlot2[] Slots;
|
||||
|
||||
protected override IReadOnlyList<EncounterSlot2> Raw => Slots;
|
||||
|
||||
public static EncounterArea2[] GetAreas(BinLinkerAccessor input, GameVersion game)
|
||||
{
|
||||
var result = new EncounterArea2[input.Length];
|
||||
|
|
|
@ -13,8 +13,6 @@ public sealed record EncounterArea3 : EncounterArea
|
|||
public readonly int Rate;
|
||||
public readonly EncounterSlot3[] Slots;
|
||||
|
||||
protected override IReadOnlyList<EncounterSlot3> Raw => Slots;
|
||||
|
||||
public static EncounterArea3[] GetAreas(BinLinkerAccessor input, GameVersion game)
|
||||
{
|
||||
var result = new EncounterArea3[input.Length];
|
||||
|
|
|
@ -11,8 +11,6 @@ public sealed record EncounterArea3XD : EncounterArea
|
|||
{
|
||||
public readonly EncounterSlot3PokeSpot[] Slots;
|
||||
|
||||
protected override IReadOnlyList<EncounterSlot3PokeSpot> Raw => Slots;
|
||||
|
||||
public EncounterArea3XD(int loc, ushort s0, byte l0, ushort s1, byte l1, ushort s2, byte l2) : base(GameVersion.XD)
|
||||
{
|
||||
Location = loc;
|
||||
|
|
|
@ -14,8 +14,6 @@ public sealed record EncounterArea4 : EncounterArea
|
|||
public readonly GroundTileAllowed GroundTile;
|
||||
public readonly EncounterSlot4[] Slots;
|
||||
|
||||
protected override IReadOnlyList<EncounterSlot4> Raw => Slots;
|
||||
|
||||
public static EncounterArea4[] GetAreas(BinLinkerAccessor input, GameVersion game)
|
||||
{
|
||||
var result = new EncounterArea4[input.Length];
|
||||
|
|
|
@ -12,8 +12,6 @@ public sealed record EncounterArea5 : EncounterArea
|
|||
{
|
||||
public readonly EncounterSlot5[] Slots;
|
||||
|
||||
protected override IReadOnlyList<EncounterSlot5> Raw => Slots;
|
||||
|
||||
public static EncounterArea5[] GetAreas(BinLinkerAccessor input, GameVersion game)
|
||||
{
|
||||
var result = new EncounterArea5[input.Length];
|
||||
|
|
|
@ -8,12 +8,10 @@ namespace PKHeX.Core;
|
|||
/// <summary>
|
||||
/// <see cref="GameVersion.ORAS"/> encounter area
|
||||
/// </summary>
|
||||
public sealed record EncounterArea6AO : EncounterArea
|
||||
public sealed record EncounterArea6AO : EncounterArea, IMemorySpeciesArea
|
||||
{
|
||||
public readonly EncounterSlot6AO[] Slots;
|
||||
|
||||
protected override IReadOnlyList<EncounterSlot6AO> Raw => Slots;
|
||||
|
||||
public static EncounterArea6AO[] GetAreas(BinLinkerAccessor input, GameVersion game)
|
||||
{
|
||||
var result = new EncounterArea6AO[input.Length];
|
||||
|
@ -88,4 +86,14 @@ public sealed record EncounterArea6AO : EncounterArea
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasSpecies(ushort species)
|
||||
{
|
||||
foreach (var slot in Slots)
|
||||
{
|
||||
if (slot.Species == species)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,12 +8,10 @@ namespace PKHeX.Core;
|
|||
/// <summary>
|
||||
/// <see cref="GameVersion.XY"/> encounter area
|
||||
/// </summary>
|
||||
public sealed record EncounterArea6XY : EncounterArea
|
||||
public sealed record EncounterArea6XY : EncounterArea, IMemorySpeciesArea
|
||||
{
|
||||
public readonly EncounterSlot6XY[] Slots;
|
||||
|
||||
protected override IReadOnlyList<EncounterSlot6XY> Raw => Slots;
|
||||
|
||||
public static EncounterArea6XY[] GetAreas(BinLinkerAccessor input, GameVersion game, EncounterArea6XY safari)
|
||||
{
|
||||
int count = input.Length;
|
||||
|
@ -155,4 +153,14 @@ public sealed record EncounterArea6XY : EncounterArea
|
|||
}
|
||||
return existsForm;
|
||||
}
|
||||
|
||||
public bool HasSpecies(ushort species)
|
||||
{
|
||||
foreach (var slot in Slots)
|
||||
{
|
||||
if (slot.Species == species)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,8 +12,6 @@ public sealed record EncounterArea7 : EncounterArea
|
|||
{
|
||||
public readonly EncounterSlot7[] Slots;
|
||||
|
||||
protected override IReadOnlyList<EncounterSlot7> Raw => Slots;
|
||||
|
||||
public static EncounterArea7[] GetAreas(BinLinkerAccessor input, GameVersion game)
|
||||
{
|
||||
var result = new EncounterArea7[input.Length];
|
||||
|
|
|
@ -12,8 +12,6 @@ public sealed record EncounterArea7b : EncounterArea
|
|||
{
|
||||
public readonly EncounterSlot7b[] Slots;
|
||||
|
||||
protected override IReadOnlyList<EncounterSlot7b> Raw => Slots;
|
||||
|
||||
public static EncounterArea7b[] GetAreas(BinLinkerAccessor input, GameVersion game)
|
||||
{
|
||||
var result = new EncounterArea7b[input.Length];
|
||||
|
|
|
@ -17,8 +17,6 @@ public sealed record EncounterArea7g : EncounterArea, ISpeciesForm
|
|||
public byte Form { get; }
|
||||
public readonly EncounterSlot7GO[] Slots;
|
||||
|
||||
protected override IReadOnlyList<EncounterSlot7GO> Raw => Slots;
|
||||
|
||||
private EncounterArea7g(ushort species, byte form, EncounterSlot7GO[] slots) : base(GameVersion.GO)
|
||||
{
|
||||
Species = species;
|
||||
|
|
|
@ -10,11 +10,10 @@ namespace PKHeX.Core;
|
|||
/// <summary>
|
||||
/// <see cref="GameVersion.SWSH"/> encounter area
|
||||
/// </summary>
|
||||
public sealed record EncounterArea8 : EncounterArea
|
||||
public sealed record EncounterArea8 : EncounterArea, IMemorySpeciesArea
|
||||
{
|
||||
public readonly EncounterSlot8[] Slots;
|
||||
|
||||
protected override IReadOnlyList<EncounterSlot8> Raw => Slots;
|
||||
/// <summary>
|
||||
/// Slots from this area can cross over to another area, resulting in a different met location.
|
||||
/// </summary>
|
||||
|
@ -403,6 +402,16 @@ public sealed record EncounterArea8 : EncounterArea
|
|||
|
||||
return slots;
|
||||
}
|
||||
|
||||
public bool HasSpecies(ushort species)
|
||||
{
|
||||
foreach (var slot in Slots)
|
||||
{
|
||||
if (slot.Species == species)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -13,8 +13,6 @@ public sealed record EncounterArea8a : EncounterArea
|
|||
public readonly EncounterSlot8a[] Slots;
|
||||
private readonly byte[] Locations;
|
||||
|
||||
protected override IReadOnlyList<EncounterSlot8a> Raw => Slots;
|
||||
|
||||
public override bool IsMatchLocation(int location)
|
||||
{
|
||||
return Array.IndexOf(Locations, (byte)location) != -1;
|
||||
|
|
|
@ -12,8 +12,6 @@ public sealed record EncounterArea8b : EncounterArea
|
|||
{
|
||||
public readonly EncounterSlot8b[] Slots;
|
||||
|
||||
protected override IReadOnlyList<EncounterSlot8b> Raw => Slots;
|
||||
|
||||
public static EncounterArea8b[] GetAreas(BinLinkerAccessor input, GameVersion game)
|
||||
{
|
||||
var result = new EncounterArea8b[input.Length];
|
||||
|
|
|
@ -17,8 +17,6 @@ public sealed record EncounterArea8g : EncounterArea, ISpeciesForm
|
|||
public byte Form { get; }
|
||||
public readonly EncounterSlot8GO[] Slots;
|
||||
|
||||
protected override IReadOnlyList<EncounterSlot8GO> Raw => Slots;
|
||||
|
||||
private EncounterArea8g(ushort species, byte form, EncounterSlot8GO[] slots) : base(GameVersion.GO)
|
||||
{
|
||||
Species = species;
|
||||
|
|
|
@ -14,8 +14,6 @@ public sealed record EncounterArea9 : EncounterArea
|
|||
|
||||
public ushort CrossFrom { get; }
|
||||
|
||||
protected override IReadOnlyList<EncounterSlot9> Raw => Slots;
|
||||
|
||||
public static EncounterArea9[] GetAreas(BinLinkerAccessor input, GameVersion game)
|
||||
{
|
||||
var result = new EncounterArea9[input.Length];
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace PKHeX.Core.Bulk;
|
||||
|
||||
|
@ -41,7 +40,7 @@ public sealed class BulkAnalysis
|
|||
CloneFlags = new bool[AllData.Count];
|
||||
|
||||
ScanAll();
|
||||
Valid = Parse.Count == 0 || Parse.All(z => z.Valid);
|
||||
Valid = Parse.Count == 0 || Parse.TrueForAll(static z => z.Valid);
|
||||
}
|
||||
|
||||
// Remove things that aren't actual stored data, or already flagged by legality checks.
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
|
@ -17,54 +16,58 @@ internal static class EncountersWC3
|
|||
new() { Species = 385, Level = 05, ID32 = 20043, OT_Gender = 0, Version = GameVersion.R, Method = PIDType.BACD_R, OT_Name = "WISHMKR", CardTitle = "Wishmaker Jirachi", Language = (int)LanguageID.English },
|
||||
};
|
||||
|
||||
private static IEnumerable<WC3> GetIngameCXDData()
|
||||
private static WC3[] GetIngameCXDData()
|
||||
{
|
||||
var langs = new[]{LanguageID.Japanese, LanguageID.English, LanguageID.French, LanguageID.Italian, LanguageID.German, LanguageID.Spanish};
|
||||
var langs = LanguageCXD;
|
||||
string[] h = {string.Empty, "ダニー", "HORDEL", "VOLKER", "ODINO", "HORAZ", string.Empty, "HORDEL"};
|
||||
string[] d = {string.Empty, "ギンザル", "DUKING", "DOKING", "RODRIGO", "GRAND", string.Empty, "GERMÁN"};
|
||||
string[] z = {string.Empty, "コンセント", "ZAPRONG", "ZAPRONG", "ZAPRONG", "ZAPRONG", string.Empty, "ZAPRONG"};
|
||||
|
||||
return langs.SelectMany(l => GetIngame((int)l));
|
||||
IEnumerable<WC3> GetIngame(int l)
|
||||
const int count = 5;
|
||||
var result = new WC3[count * langs.Length];
|
||||
for (int i = 0, ofs = 0; i < langs.Length; i++)
|
||||
{
|
||||
var id = (LanguageID) l;
|
||||
return new WC3[]
|
||||
{
|
||||
// Colosseum
|
||||
new() { Species = 311, Level = 13, Language = l, Location = 254, ID32 = 37149, OT_Gender = 0, OT_Name = d[l], Version = GameVersion.COLO, CardTitle = $"Special Gift ({id})", Method = PIDType.CXD, Shiny = Shiny.Never, Moves = new(045, 086, 098, 270) }, // Plusle @ Ingame Trade
|
||||
// XD
|
||||
new(true) { Species = 239, Level = 20, Language = l, Location = 164, TID16 = 41400, OT_Gender = 0, OT_Name = h[l], Version = GameVersion.XD, CardTitle = $"Trade Togepi ({id})", Method = PIDType.CXD, Moves = new(008, 007, 009, 238), Nickname = z[l] }, // Elekid @ Snagem Hideout
|
||||
new(true) { Species = 307, Level = 20, Language = l, Location = 116, TID16 = 37149, OT_Gender = 0, OT_Name = d[l], Version = GameVersion.XD, CardTitle = $"Trade Trapinch ({id})", Method = PIDType.CXD, Moves = new(223, 093, 247, 197) }, // Meditite @ Pyrite Town
|
||||
new(true) { Species = 213, Level = 20, Language = l, Location = 116, TID16 = 37149, OT_Gender = 0, OT_Name = d[l], Version = GameVersion.XD, CardTitle = $"Trade Surskit ({id})", Method = PIDType.CXD, Moves = new(092, 164, 188, 227) }, // Shuckle @ Pyrite Town
|
||||
new(true) { Species = 246, Level = 20, Language = l, Location = 116, TID16 = 37149, OT_Gender = 0, OT_Name = d[l], Version = GameVersion.XD, CardTitle = $"Trade Wooper ({id})", Method = PIDType.CXD, Moves = new(201, 349, 044, 200) }, // Larvitar @ Pyrite Town
|
||||
};
|
||||
var id = langs[i];
|
||||
var l = (int)id;
|
||||
|
||||
// Colosseum
|
||||
result[ofs++] = new() { Species = 311, Level = 13, Language = l, Location = 254, ID32 = 37149, OT_Gender = 0, OT_Name = d[l], Version = GameVersion.COLO, CardTitle = $"Special Gift ({id})", Method = PIDType.CXD, Shiny = Shiny.Never, Moves = new(045, 086, 098, 270) }; // Plusle @ Ingame Trade
|
||||
// XD
|
||||
result[ofs++] = new(true) { Species = 239, Level = 20, Language = l, Location = 164, TID16 = 41400, OT_Gender = 0, OT_Name = h[l], Version = GameVersion.XD, CardTitle = $"Trade Togepi ({id})", Method = PIDType.CXD, Moves = new(008, 007, 009, 238), Nickname = z[l] }; // Elekid @ Snagem Hideout
|
||||
result[ofs++] = new(true) { Species = 307, Level = 20, Language = l, Location = 116, TID16 = 37149, OT_Gender = 0, OT_Name = d[l], Version = GameVersion.XD, CardTitle = $"Trade Trapinch ({id})", Method = PIDType.CXD, Moves = new(223, 093, 247, 197) }; // Meditite @ Pyrite Town
|
||||
result[ofs++] = new(true) { Species = 213, Level = 20, Language = l, Location = 116, TID16 = 37149, OT_Gender = 0, OT_Name = d[l], Version = GameVersion.XD, CardTitle = $"Trade Surskit ({id})", Method = PIDType.CXD, Moves = new(092, 164, 188, 227) }; // Shuckle @ Pyrite Town
|
||||
result[ofs++] = new(true) { Species = 246, Level = 20, Language = l, Location = 116, TID16 = 37149, OT_Gender = 0, OT_Name = d[l], Version = GameVersion.XD, CardTitle = $"Trade Wooper ({id})", Method = PIDType.CXD, Moves = new(201, 349, 044, 200) }; // Larvitar @ Pyrite Town
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static IEnumerable<WC3> GetIngameCXDDataMainline()
|
||||
private static ReadOnlySpan<LanguageID> LanguageCXD => new[] { LanguageID.Japanese, LanguageID.English, LanguageID.French, LanguageID.Italian, LanguageID.German, LanguageID.Spanish };
|
||||
|
||||
private static WC3[] GetIngameCXDDataMainline()
|
||||
{
|
||||
var langs = new[] { LanguageID.Japanese, LanguageID.English, LanguageID.French, LanguageID.Italian, LanguageID.German, LanguageID.Spanish };
|
||||
var langs = LanguageCXD;
|
||||
string[] p = { string.Empty, "コロシアム", "COLOS", "COLOSSEUM", "ARENA", "COLOSSEUM", string.Empty, "CLAUDIO" };
|
||||
string[] c = { string.Empty, "アゲト", "AGATE", "SAMARAGD", "SOFO", "EMERITAE", string.Empty, "ÁGATA" };
|
||||
string[] m = { string.Empty, "バトルやま", "MATTLE", "MT BATAILL", "MONTE LOTT", "DUELLBERG", string.Empty, "ERNESTO" }; // truncated on ck3->pk3 transfer
|
||||
|
||||
return langs.SelectMany(l => GetIngame((int)l));
|
||||
IEnumerable<WC3> GetIngame(int l)
|
||||
const int count = 3;
|
||||
var result = new WC3[count * langs.Length];
|
||||
for (int i = 0, ofs = 0; i < langs.Length; i++)
|
||||
{
|
||||
var id = (LanguageID)l;
|
||||
var id = langs[i];
|
||||
var l = (int)id;
|
||||
var nd = id != LanguageID.Japanese;
|
||||
return new WC3[]
|
||||
{
|
||||
// Colosseum
|
||||
new() { Species = 025, Level = 10, Language = l, Location = 255, ID32 = 31121, OT_Gender = 0, OT_Name = p[l], Version = GameVersion.R, CardTitle = $"Colosseum Pikachu ({id})",Method = PIDType.CXD, Shiny = Shiny.Never, NotDistributed = nd }, // Colosseum Pikachu bonus gift
|
||||
new() { Species = 251, Level = 10, Language = l, Location = 255, ID32 = 31121, OT_Gender = 1, OT_Name = c[l], Version = GameVersion.R, CardTitle = $"Agate Celebi ({id})", Method = PIDType.CXD, Shiny = Shiny.Never, NotDistributed = nd }, // Ageto Celebi bonus gift
|
||||
new() { Species = 250, Level = 70, Language = l, Location = 255, ID32 = 10048, OT_Gender = 0, OT_Name = m[l], Version = GameVersion.S, CardTitle = $"Mt. Battle Ho-Oh ({id})", Method = PIDType.CXD, Shiny = Shiny.Never, Moves = new(105, 126, 241, 129) }, // Ho-oh @ Mt. Battle
|
||||
};
|
||||
|
||||
// Colosseum
|
||||
result[ofs++] = new() { Species = 025, Level = 10, Language = l, Location = 255, ID32 = 31121, OT_Gender = 0, OT_Name = p[l], Version = GameVersion.R, CardTitle = $"Colosseum Pikachu ({id})",Method = PIDType.CXD, Shiny = Shiny.Never, NotDistributed = nd }; // Colosseum Pikachu bonus gift
|
||||
result[ofs++] = new() { Species = 251, Level = 10, Language = l, Location = 255, ID32 = 31121, OT_Gender = 1, OT_Name = c[l], Version = GameVersion.R, CardTitle = $"Agate Celebi ({id})", Method = PIDType.CXD, Shiny = Shiny.Never, NotDistributed = nd }; // Ageto Celebi bonus gift
|
||||
result[ofs++] = new() { Species = 250, Level = 70, Language = l, Location = 255, ID32 = 10048, OT_Gender = 0, OT_Name = m[l], Version = GameVersion.S, CardTitle = $"Mt. Battle Ho-Oh ({id})", Method = PIDType.CXD, Shiny = Shiny.Never, Moves = new(105, 126, 241, 129) }; // Ho-oh @ Mt. Battle
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static readonly WC3[] Encounter_WC3CXD = GetIngameCXDData().ToArray();
|
||||
internal static readonly WC3[] Encounter_WC3CXDMain = GetIngameCXDDataMainline().ToArray();
|
||||
internal static readonly WC3[] Encounter_WC3CXD = GetIngameCXDData();
|
||||
internal static readonly WC3[] Encounter_WC3CXDMain = GetIngameCXDDataMainline();
|
||||
|
||||
internal static readonly WC3[] Encounter_Event3 = ArrayUtil.ConcatAll(Encounter_Event3_Special, Encounter_WC3CXD, Encounter_WC3CXDMain);
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
namespace PKHeX.Core;
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Generation 5 Static Encounter from N
|
||||
|
@ -47,8 +47,7 @@ internal sealed record EncounterStatic5N : EncounterStatic5
|
|||
pk5.IV_HP = pk5.IV_ATK = pk5.IV_DEF = pk5.IV_SPA = pk5.IV_SPD = pk5.IV_SPE = 30;
|
||||
pk5.NSparkle = NSparkle;
|
||||
pk5.OT_Name = GetOT(lang);
|
||||
pk5.TID16 = 00002;
|
||||
pk5.SID16 = 00000;
|
||||
pk5.ID32 = 00002;
|
||||
}
|
||||
|
||||
public static string GetOT(int lang) => lang == (int)LanguageID.Japanese ? "N" : "N";
|
||||
|
|
|
@ -130,7 +130,7 @@ public static class EncounterFinder
|
|||
}
|
||||
else if (pk is PK1 pk1)
|
||||
{
|
||||
var hasGen2 = Array.Exists(info.Moves, z => z.Generation is 2);
|
||||
var hasGen2 = MoveInfo.IsAnyFromGeneration(2, info.Moves);
|
||||
if (hasGen2)
|
||||
{
|
||||
if (!ParseSettings.AllowGen1Tradeback)
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using static PKHeX.Core.EncounterTypeGroup;
|
||||
|
||||
|
@ -172,7 +171,7 @@ public static class EncounterMovesetGenerator
|
|||
/// <param name="moves">Moves that the resulting <see cref="IEncounterable"/> must be able to learn.</param>
|
||||
/// <param name="version">Specific version to iterate for.</param>
|
||||
/// <returns>A consumable <see cref="IEncounterable"/> list of possible encounters.</returns>
|
||||
private static IEnumerable<IEncounterable> GenerateVersionEncounters(PKM pk, ReadOnlySpan<ushort> moves, GameVersion version)
|
||||
private static IEnumerable<IEncounterable> GenerateVersionEncounters(PKM pk, ushort[] moves, GameVersion version)
|
||||
{
|
||||
pk.Version = (int)version;
|
||||
|
||||
|
@ -185,7 +184,11 @@ public static class EncounterMovesetGenerator
|
|||
var needs = GetNeededMoves(pk, moves, version);
|
||||
var generator = EncounterGenerator.GetGenerator(version);
|
||||
|
||||
return PriorityList.SelectMany(type => GetPossibleOfType(pk, needs, version, type, chain, generator));
|
||||
foreach (var type in PriorityList)
|
||||
{
|
||||
foreach (var enc in GetPossibleOfType(pk, needs, version, type, chain, generator))
|
||||
yield return enc;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsSane(PKM pk, ReadOnlySpan<ushort> moves)
|
||||
|
|
|
@ -75,7 +75,7 @@ public static class EncounterLearn
|
|||
{
|
||||
if (species == 0)
|
||||
return Array.Empty<IEncounterable>();
|
||||
if (Array.Exists(moves, z => z == 0))
|
||||
if (moves.AsSpan().Contains<ushort>(0))
|
||||
return Array.Empty<IEncounterable>();
|
||||
|
||||
var vers = GameUtil.GameVersions;
|
||||
|
|
|
@ -29,10 +29,22 @@ public static class EncounterSuggestion
|
|||
if (s is null)
|
||||
return GetSuggestedEncounter(pk, w, loc);
|
||||
|
||||
bool isDefinitelySlot = Array.Exists(chain, z => z.Species == w.Species && z.Form == w.Form);
|
||||
bool isDefinitelyStatic = Array.Exists(chain, z => z.Species == s.Species && z.Form == s.Form);
|
||||
IEncounterable obj = (isDefinitelySlot || !isDefinitelyStatic) ? w : s;
|
||||
return GetSuggestedEncounter(pk, obj, loc);
|
||||
// Prefer the wild slot; fall back to wild slot if none are exact match.
|
||||
if (IsSpeciesFormMatch(chain, w))
|
||||
return GetSuggestedEncounter(pk, w, loc);
|
||||
if (IsSpeciesFormMatch(chain, s))
|
||||
return GetSuggestedEncounter(pk, s, loc);
|
||||
return GetSuggestedEncounter(pk, w, loc);
|
||||
}
|
||||
|
||||
private static bool IsSpeciesFormMatch(ReadOnlySpan<EvoCriteria> evos, ISpeciesForm encounter)
|
||||
{
|
||||
foreach (var evo in evos)
|
||||
{
|
||||
if (evo.Species == encounter.Species && evo.Form == encounter.Form)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static EncounterSuggestionData GetSuggestedEncounterEgg(PKM pk, int loc = -1)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
|
58
PKHeX.Core/Legality/Encounters/Information/EncounterText.cs
Normal file
58
PKHeX.Core/Legality/Encounters/Information/EncounterText.cs
Normal file
|
@ -0,0 +1,58 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Logic for creating a formatted text summary of an encounter.
|
||||
/// </summary>
|
||||
public static class EncounterText
|
||||
{
|
||||
public static IReadOnlyList<string> GetTextLines(this IEncounterInfo enc) => GetTextLines(enc, GameInfo.Strings);
|
||||
|
||||
public static IReadOnlyList<string> GetTextLines(this IEncounterInfo enc, GameStrings strings)
|
||||
{
|
||||
var lines = new List<string>();
|
||||
var str = strings.Species;
|
||||
var name = (uint)enc.Species < str.Count ? str[enc.Species] : enc.Species.ToString();
|
||||
var EncounterName = $"{(enc is IEncounterable ie ? ie.LongName : "Special")} ({name})";
|
||||
lines.Add(string.Format(L_FEncounterType_0, EncounterName));
|
||||
if (enc is MysteryGift mg)
|
||||
{
|
||||
lines.AddRange(mg.GetDescription());
|
||||
}
|
||||
else if (enc is IMoveset m)
|
||||
{
|
||||
var moves = m.Moves;
|
||||
if (moves.HasMoves)
|
||||
{
|
||||
string result = moves.GetMovesetLine(strings.movelist);
|
||||
lines.Add(result);
|
||||
}
|
||||
}
|
||||
|
||||
var el = enc as ILocation;
|
||||
var loc = el?.GetEncounterLocation(enc.Generation, (int)enc.Version);
|
||||
if (!string.IsNullOrEmpty(loc))
|
||||
lines.Add(string.Format(L_F0_1, "Location", loc));
|
||||
|
||||
var game = enc.Version.IsValidSavedVersion() ? strings.gamelist[(int)enc.Version] : enc.Version.ToString();
|
||||
lines.Add(string.Format(L_F0_1, nameof(GameVersion), game));
|
||||
lines.Add(enc.LevelMin == enc.LevelMax
|
||||
? $"Level: {enc.LevelMin}"
|
||||
: $"Level: {enc.LevelMin}-{enc.LevelMax}");
|
||||
|
||||
#if DEBUG
|
||||
// Record types! Can get a nice summary.
|
||||
// Won't work neatly for Mystery Gift types since those aren't record types.
|
||||
if (enc is not MysteryGift)
|
||||
{
|
||||
// ReSharper disable once ConstantNullCoalescingCondition
|
||||
var raw = enc.ToString() ?? throw new ArgumentNullException(nameof(enc));
|
||||
lines.AddRange(raw.Split(',', '}', '{'));
|
||||
}
|
||||
#endif
|
||||
return lines;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using static PKHeX.Core.Encounters6XY;
|
||||
using static PKHeX.Core.Encounters6AO;
|
||||
|
@ -184,7 +183,7 @@ public static class MemoryPermissions
|
|||
},
|
||||
EntityContext.Gen8 => version switch
|
||||
{
|
||||
GameVersion.Any => GetCanBeCaptured(species, SlotsSW.Concat(SlotsSH), StaticSW.Concat(StaticSH)),
|
||||
GameVersion.Any => GetCanBeCaptured(species, SlotsSW, StaticSW) || GetCanBeCaptured(species, SlotsSH, StaticSH),
|
||||
GameVersion.SW => GetCanBeCaptured(species, SlotsSW, StaticSW),
|
||||
GameVersion.SH => GetCanBeCaptured(species, SlotsSH, StaticSH),
|
||||
_ => false,
|
||||
|
@ -192,12 +191,18 @@ public static class MemoryPermissions
|
|||
_ => false,
|
||||
};
|
||||
|
||||
private static bool GetCanBeCaptured(ushort species, IEnumerable<EncounterArea> area, IEnumerable<EncounterStatic> statics)
|
||||
private static bool GetCanBeCaptured<TArea, TStatic>(ushort species, TArea[] areas, TStatic[] statics) where TArea : IMemorySpeciesArea where TStatic : IEncounterTemplate
|
||||
{
|
||||
if (area.Any(loc => loc.HasSpecies(species)))
|
||||
return true;
|
||||
if (statics.Any(enc => enc.Species == species && !enc.Gift))
|
||||
return true;
|
||||
foreach (var area in areas)
|
||||
{
|
||||
if (area.HasSpecies(species))
|
||||
return true;
|
||||
}
|
||||
foreach (var s in statics)
|
||||
{
|
||||
if (s.Species == species)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,21 +42,39 @@ public sealed class HyperTrainingVerifier : Verifier
|
|||
// already checked for 6IV, therefore we're flawed on at least one IV
|
||||
if (t.IsHyperTrainedAll())
|
||||
{
|
||||
// SV gold bottle cap applies to all IVs regardless
|
||||
// LGPE gold bottle cap applies to all IVs regardless
|
||||
var evos = data.Info.EvoChainsAllGens;
|
||||
if (evos.HasVisitedGen9 && Array.Exists(evos.Gen9, x => x.LevelMax >= 50))
|
||||
return;
|
||||
if (evos.HasVisitedLGPE && Array.Exists(evos.Gen7b, x => x.LevelMax >= 100))
|
||||
if (HasVisitedGoldBottleFlawless(data.Info.EvoChainsAllGens))
|
||||
return;
|
||||
// Otherwise, could not have hyper trained a flawless IV. Flag a flawless IV with the usual logic.
|
||||
}
|
||||
|
||||
if (IsFlawlessHyperTrained(pk, t, max))
|
||||
data.AddLine(GetInvalid(LHyperPerfectOne));
|
||||
}
|
||||
|
||||
private static bool HasVisitedGoldBottleFlawless(EvolutionHistory evos)
|
||||
{
|
||||
// S/V gold bottle cap applies to all IVs regardless
|
||||
// LGP/E gold bottle cap applies to all IVs regardless
|
||||
foreach (ref var x in evos.Gen9.AsSpan())
|
||||
{
|
||||
if (x.LevelMax >= 50)
|
||||
return true;
|
||||
}
|
||||
foreach (ref var x in evos.Gen7b.AsSpan())
|
||||
{
|
||||
if (x.LevelMax >= 100)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsFlawlessHyperTrained(PKM pk, IHyperTrain t, int max)
|
||||
{
|
||||
for (int i = 0; i < 6; i++) // Check individual IVs
|
||||
{
|
||||
if (pk.GetIV(i) != max || !t.IsHyperTrained(i))
|
||||
continue;
|
||||
data.AddLine(GetInvalid(LHyperPerfectOne));
|
||||
break;
|
||||
if (pk.GetIV(i) == max && t.IsHyperTrained(i))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
using System;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
@ -131,9 +130,9 @@ public sealed class LevelVerifier : Verifier
|
|||
var moves = data.Info.Moves;
|
||||
// Gen2 stuff can be traded between Gen2 games holding an Everstone, assuming it hasn't been transferred to Gen1 for special moves.
|
||||
if (enc.Generation == 2)
|
||||
return Array.Exists(moves, z => z.Generation == 1);
|
||||
return MoveInfo.IsAnyFromGeneration(1, moves);
|
||||
// Gen1 stuff can only be un-evolved if it was never traded from the OT.
|
||||
if (Array.Exists(moves, z => z.Generation == 2))
|
||||
if (MoveInfo.IsAnyFromGeneration(2, moves))
|
||||
return true; // traded to Gen2 for special moves
|
||||
if (pk.Format != 1)
|
||||
return true; // traded to Gen2 (current state)
|
||||
|
|
|
@ -42,20 +42,7 @@ public sealed class MemoryVerifier : Verifier
|
|||
// Actionable HM moves
|
||||
int hmIndex = MemoryContext6.MoveSpecificMemoryHM.IndexOf(memory.MemoryID);
|
||||
if (hmIndex != -1)
|
||||
{
|
||||
if (context != Gen6) // Gen8 has no HMs, so this memory can never exist.
|
||||
return GetInvalid(string.Format(LMemoryArgBadMove, memory.Handler));
|
||||
|
||||
if (pk.Species != (int)Species.Smeargle)
|
||||
{
|
||||
// All AO hidden machine permissions are super-sets of Gen 3-5 games.
|
||||
// Don't need to check the move history -- a learned HM in a prior game can still be learned in Gen6.
|
||||
var evos = info.EvoChainsAllGens.Gen6;
|
||||
var exists = Array.Exists(evos, z => PersonalTable.AO.GetFormEntry(z.Species, 0).GetIsLearnHM(hmIndex));
|
||||
if (!exists)
|
||||
return GetInvalid(string.Format(LMemoryArgBadMove, memory.Handler));
|
||||
}
|
||||
}
|
||||
return VerifyMemoryHM6(context, info, mem, memory, hmIndex);
|
||||
|
||||
if (mem.IsInvalidGeneralLocationMemoryValue(memory.MemoryID, memory.Variable, info.EncounterMatch, pk))
|
||||
return GetInvalid(string.Format(LMemoryArgBadLocation, memory.Handler));
|
||||
|
@ -71,15 +58,15 @@ public sealed class MemoryVerifier : Verifier
|
|||
|
||||
// {0} saw {2} carrying {1} on its back. {4} that {3}.
|
||||
case 21 when context != Gen6 || !PersonalTable.AO.GetFormEntry(memory.Variable, 0).GetIsLearnHM(2): // Fly
|
||||
return GetInvalid(string.Format(LMemoryArgBadMove, memory.Handler));
|
||||
return BadSpeciesMove(memory.Handler);
|
||||
|
||||
// {0} used {2} at {1}’s instruction, but it had no effect. {4} that {3}.
|
||||
// The Move Deleter that {0} met through {1} made it forget {2}. {4} that {3}.
|
||||
case 16 or 48 when !CanKnowMove(pk, memory, context, info, memory.MemoryID == 16):
|
||||
return GetInvalid(string.Format(LMemoryArgBadMove, memory.Handler));
|
||||
return BadSpeciesMove(memory.Handler);
|
||||
|
||||
case 49 when memory.Variable == 0 || !GetCanRelearnMove(pk, memory.Variable, context, info.EvoChainsAllGens, info.EncounterOriginal):
|
||||
return GetInvalid(string.Format(LMemoryArgBadMove, memory.Handler));
|
||||
return BadSpeciesMove(memory.Handler);
|
||||
|
||||
// Dynamaxing
|
||||
// {0} battled at {1}’s side against {2} that Dynamaxed. {4} that {3}.
|
||||
|
@ -92,7 +79,7 @@ public sealed class MemoryVerifier : Verifier
|
|||
// {0} studied about how to use {2} in a Box, thinking about {1}. {4} that {3}.
|
||||
// {0} practiced its cool pose for the move {2} in a Box, wishing to be praised by {1}. {4} that {3}.
|
||||
case 80 or 81 when !CanKnowMove(pk, memory, context, info):
|
||||
return Get(string.Format(LMemoryArgBadMove, memory.Handler), Severity.Invalid);
|
||||
return BadSpeciesMove(memory.Handler);
|
||||
|
||||
// Species
|
||||
// With {1}, {0} went fishing, and they caught {2}. {4} that {3}.
|
||||
|
@ -137,6 +124,31 @@ public sealed class MemoryVerifier : Verifier
|
|||
return VerifyCommonMemoryEtc(memory, mem);
|
||||
}
|
||||
|
||||
private CheckResult VerifyMemoryHM6(EntityContext context, LegalInfo info, MemoryContext mem, MemoryVariableSet memory, int hmIndex)
|
||||
{
|
||||
if (context != Gen6) // Gen8 has no HMs, so this memory can never exist.
|
||||
return BadSpeciesMove(memory.Handler);
|
||||
|
||||
if (info.EncounterMatch.Species == (int)Species.Smeargle)
|
||||
return VerifyCommonMemoryEtc(memory, mem);
|
||||
|
||||
// All AO hidden machine permissions are super-sets of Gen 3-5 games.
|
||||
// Don't need to check the move history -- a learned HM in a prior game can still be learned in Gen6.
|
||||
var pt = PersonalTable.AO;
|
||||
foreach (ref var evo in info.EvoChainsAllGens.Gen6.AsSpan())
|
||||
{
|
||||
var entry = pt[evo.Species];
|
||||
var canLearn = entry.GetIsLearnHM(hmIndex);
|
||||
if (canLearn)
|
||||
return VerifyCommonMemoryEtc(memory, mem);
|
||||
break;
|
||||
}
|
||||
|
||||
return BadSpeciesMove(memory.Handler);
|
||||
}
|
||||
|
||||
private CheckResult BadSpeciesMove(string handler) => GetInvalid(string.Format(LMemoryArgBadMove, handler));
|
||||
|
||||
private CheckResult VerifyCommonMemoryEtc(MemoryVariableSet memory, MemoryContext context)
|
||||
{
|
||||
if (!context.CanHaveIntensity(memory.MemoryID, memory.Intensity))
|
||||
|
|
|
@ -312,7 +312,7 @@ public sealed class MiscVerifier : Verifier
|
|||
|
||||
CheckResult GetWasNotTradeback(TimeCapsuleEvaluation timeCapsuleEvalution)
|
||||
{
|
||||
if (Array.Exists(data.Info.Moves, z => z.Generation == 2))
|
||||
if (MoveInfo.IsAnyFromGeneration(2, data.Info.Moves))
|
||||
return GetInvalid(LG1CatchRateItem);
|
||||
var e = data.EncounterMatch;
|
||||
if (e is EncounterStatic1E {Version: GameVersion.Stadium} or EncounterTrade1)
|
||||
|
|
|
@ -179,4 +179,14 @@ public static class MoveInfo
|
|||
return 0;
|
||||
return types[move];
|
||||
}
|
||||
|
||||
public static bool IsAnyFromGeneration(int generation, ReadOnlySpan<MoveResult> moves)
|
||||
{
|
||||
foreach (var move in moves)
|
||||
{
|
||||
if (move.Generation == generation)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace PKHeX.Core;
|
|||
/// </summary>
|
||||
public sealed class PersonalTable2 : IPersonalTable, IPersonalTable<PersonalInfo2>
|
||||
{
|
||||
private readonly PersonalInfo2[] Table; // internal to share with Gen1 tables
|
||||
private readonly PersonalInfo2[] Table;
|
||||
private const int SIZE = PersonalInfo2.SIZE;
|
||||
private const int MaxSpecies = Legal.MaxSpeciesID_2;
|
||||
public int MaxSpeciesID => MaxSpecies;
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace PKHeX.Core;
|
|||
/// </summary>
|
||||
public sealed class PersonalTable3 : IPersonalTable, IPersonalTable<PersonalInfo3>
|
||||
{
|
||||
private readonly PersonalInfo3[] Table; // internal to share with Gen1 tables
|
||||
private readonly PersonalInfo3[] Table;
|
||||
private const int SIZE = PersonalInfo3.SIZE;
|
||||
private const int MaxSpecies = Legal.MaxSpeciesID_3;
|
||||
public int MaxSpeciesID => MaxSpecies;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
|
|
@ -174,6 +174,8 @@ public abstract class SAV5 : SaveFile, ISaveBlock5BW, IEventFlag37
|
|||
set => Data[CGearSkinInfoOffset + 2] = Data[PlayerData.Offset + (this is SAV5B2W2 ? 0x6C : 0x54)] = value ? (byte)1 : (byte)0;
|
||||
}
|
||||
|
||||
private static ReadOnlySpan<byte> DLCFooter => new byte[] { 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x27, 0x00, 0x00, 0x27, 0x35, 0x05, 0x31, 0x00, 0x00 };
|
||||
|
||||
public byte[] CGearSkinData
|
||||
{
|
||||
get
|
||||
|
@ -193,8 +195,7 @@ public abstract class SAV5 : SaveFile, ISaveBlock5BW, IEventFlag37
|
|||
WriteUInt16LittleEndian(footer[2..], chk); // checksum
|
||||
WriteUInt16LittleEndian(footer[0x100..], chk); // second checksum
|
||||
|
||||
Span<byte> dlcfooter = stackalloc byte[] { 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x27, 0x00, 0x00, 0x27, 0x35, 0x05, 0x31, 0x00, 0x00 };
|
||||
dlcfooter.CopyTo(footer[0x102..]);
|
||||
DLCFooter.CopyTo(footer[0x102..]);
|
||||
|
||||
ushort skinchkval = Checksums.CRC16_CCITT(footer[0x100..0x104]);
|
||||
WriteUInt16LittleEndian(footer[0x112..], skinchkval);
|
||||
|
|
|
@ -245,27 +245,31 @@ public sealed class CGearBackground
|
|||
if (Width * Height * bpp != data.Length)
|
||||
throw new ArgumentException("Invalid image data size.");
|
||||
|
||||
var pixels = MemoryMarshal.Cast<byte, int>(data);
|
||||
var colors = GetColorData(pixels);
|
||||
var colors = GetColorData(data);
|
||||
var palette = colors.Distinct().ToArray();
|
||||
if (palette.Length > ColorCount)
|
||||
throw new ArgumentException($"Too many unique colors. Expected <= 16, got {palette.Length}");
|
||||
|
||||
var Palette = colors.Distinct().ToArray();
|
||||
if (Palette.Length > ColorCount)
|
||||
throw new ArgumentException($"Too many unique colors. Expected <= 16, got {Palette.Length}");
|
||||
|
||||
var tiles = GetTiles(colors, Palette);
|
||||
var tiles = GetTiles(colors, palette);
|
||||
GetTileList(tiles, out List<Tile> tilelist, out TileMap tm);
|
||||
if (tilelist.Count >= 0xFF)
|
||||
throw new ArgumentException($"Too many unique tiles. Expected < 256, received {tilelist.Count}.");
|
||||
|
||||
// Finished!
|
||||
return new CGearBackground(Palette, tilelist.ToArray(), tm);
|
||||
return new CGearBackground(palette, tilelist.ToArray(), tm);
|
||||
}
|
||||
|
||||
private static int[] GetColorData(ReadOnlySpan<int> pixels)
|
||||
private static int[] GetColorData(ReadOnlySpan<byte> data)
|
||||
{
|
||||
var pixels = MemoryMarshal.Cast<byte, int>(data);
|
||||
int[] colors = new int[pixels.Length];
|
||||
for (int i = 0; i < pixels.Length; i++)
|
||||
colors[i] = GetRGB555_32(pixels[i]);
|
||||
{
|
||||
var pixel = pixels[i];
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
pixel = ReverseEndianness(pixel);
|
||||
colors[i] = GetRGB555_32(pixel);
|
||||
}
|
||||
return colors;
|
||||
}
|
||||
|
||||
|
@ -327,10 +331,10 @@ public sealed class CGearBackground
|
|||
tm.Rotations[tileIndex] = 0;
|
||||
}
|
||||
|
||||
private CGearBackground(int[] Palette, Tile[] tilelist, TileMap tm)
|
||||
private CGearBackground(int[] palette, Tile[] tilelist, TileMap tm)
|
||||
{
|
||||
Map = tm;
|
||||
ColorPalette = Palette;
|
||||
ColorPalette = palette;
|
||||
Tiles = tilelist;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
using System.Collections;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
|
@ -45,10 +44,16 @@ public sealed class GoParkStorage : SaveBlock<SAV7b>, IEnumerable<GP1>
|
|||
this[i] = value[i];
|
||||
}
|
||||
|
||||
public IEnumerable<string> DumpAll(IReadOnlyList<string> speciesNames) => GetAllEntities()
|
||||
.Select((z, i) => (Entry: z, Index: i))
|
||||
.Where(z => z.Entry.Species > 0)
|
||||
.Select(z => z.Entry.Dump(speciesNames, z.Index));
|
||||
public IEnumerable<string> DumpAll(IReadOnlyList<string> speciesNames)
|
||||
{
|
||||
var arr = GetAllEntities();
|
||||
for (int i = 0; i < arr.Length; i++)
|
||||
{
|
||||
var entry = arr[i];
|
||||
if (entry.Species > 0)
|
||||
yield return entry.Dump(speciesNames, i);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<GP1> GetEnumerator() => (IEnumerator<GP1>)GetAllEntities().GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetAllEntities().GetEnumerator();
|
||||
|
|
|
@ -3,8 +3,6 @@ using System.Collections.Generic;
|
|||
using System.Windows.Forms;
|
||||
using PKHeX.Core;
|
||||
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
|
||||
namespace PKHeX.WinForms.Controls;
|
||||
|
||||
public sealed class SummaryPreviewer
|
||||
|
@ -43,7 +41,7 @@ public sealed class SummaryPreviewer
|
|||
|
||||
if (Main.Settings.Hover.HoverSlotShowText)
|
||||
{
|
||||
var lines = GetTextLines(enc);
|
||||
var lines = enc.GetTextLines(GameInfo.Strings);
|
||||
var text = string.Join(Environment.NewLine, lines);
|
||||
ShowSet.SetToolTip(pb, text);
|
||||
}
|
||||
|
@ -52,49 +50,6 @@ public sealed class SummaryPreviewer
|
|||
Cry.PlayCry(enc, enc.Context);
|
||||
}
|
||||
|
||||
public static IEnumerable<string> GetTextLines(IEncounterInfo enc)
|
||||
{
|
||||
var lines = new List<string>();
|
||||
var str = GameInfo.Strings.Species;
|
||||
var name = (uint) enc.Species < str.Count ? str[enc.Species] : enc.Species.ToString();
|
||||
var EncounterName = $"{(enc is IEncounterable ie ? ie.LongName : "Special")} ({name})";
|
||||
lines.Add(string.Format(L_FEncounterType_0, EncounterName));
|
||||
if (enc is MysteryGift mg)
|
||||
{
|
||||
lines.AddRange(mg.GetDescription());
|
||||
}
|
||||
else if (enc is IMoveset m)
|
||||
{
|
||||
var moves = m.Moves;
|
||||
if (moves.HasMoves)
|
||||
{
|
||||
string result = moves.GetMovesetLine(GameInfo.Strings.movelist);
|
||||
lines.Add(result);
|
||||
}
|
||||
}
|
||||
|
||||
var el = enc as ILocation;
|
||||
var loc = el?.GetEncounterLocation(enc.Generation, (int) enc.Version);
|
||||
if (!string.IsNullOrEmpty(loc))
|
||||
lines.Add(string.Format(L_F0_1, "Location", loc));
|
||||
lines.Add(string.Format(L_F0_1, nameof(GameVersion), enc.Version));
|
||||
lines.Add(enc.LevelMin == enc.LevelMax
|
||||
? $"Level: {enc.LevelMin}"
|
||||
: $"Level: {enc.LevelMin}-{enc.LevelMax}");
|
||||
|
||||
#if DEBUG
|
||||
// Record types! Can get a nice summary.
|
||||
// Won't work neatly for Mystery Gift types since those aren't record types.
|
||||
if (enc is not MysteryGift)
|
||||
{
|
||||
// ReSharper disable once ConstantNullCoalescingCondition
|
||||
var raw = enc.ToString() ?? throw new ArgumentNullException(nameof(enc));
|
||||
lines.AddRange(raw.Split(',', '}', '{'));
|
||||
}
|
||||
#endif
|
||||
return lines;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
ShowSet.RemoveAll();
|
||||
|
|
|
@ -1063,7 +1063,7 @@ public partial class Main : Form
|
|||
if (dr != DialogResult.Yes)
|
||||
return;
|
||||
#if DEBUG
|
||||
var enc = SummaryPreviewer.GetTextLines(la.EncounterOriginal);
|
||||
var enc = la.EncounterOriginal.GetTextLines();
|
||||
report += Environment.NewLine + Environment.NewLine + string.Join(Environment.NewLine, enc);
|
||||
#endif
|
||||
WinFormsUtil.SetClipboardText(report);
|
||||
|
|
|
@ -80,7 +80,7 @@ public partial class SAV_Encounters : Form
|
|||
return;
|
||||
|
||||
var enc = Results[index];
|
||||
pb.AccessibleDescription = string.Join(Environment.NewLine, SummaryPreviewer.GetTextLines(enc));
|
||||
pb.AccessibleDescription = string.Join(Environment.NewLine, enc.GetTextLines());
|
||||
};
|
||||
slot.ContextMenuStrip = mnu;
|
||||
if (Main.Settings.Hover.HoverSlotShowText)
|
||||
|
|
|
@ -83,7 +83,7 @@ public partial class SAV_MysteryGiftDB : Form
|
|||
return;
|
||||
|
||||
var enc = Results[index];
|
||||
pb.AccessibleDescription = string.Join(Environment.NewLine, SummaryPreviewer.GetTextLines(enc));
|
||||
pb.AccessibleDescription = string.Join(Environment.NewLine, enc.GetTextLines());
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -112,6 +112,9 @@ public partial class SAV_Misc3 : Form
|
|||
}
|
||||
#endregion
|
||||
|
||||
private const ushort ItemIDOldSeaMap = 0x178;
|
||||
private static ReadOnlySpan<ushort> TicketItemIDs => new ushort[] { 0x109, 0x113, 0x172, 0x173, ItemIDOldSeaMap }; // item IDs
|
||||
|
||||
#region Ferry
|
||||
private void B_GetTickets_Click(object sender, EventArgs e)
|
||||
{
|
||||
|
@ -123,9 +126,8 @@ public partial class SAV_Misc3 : Form
|
|||
itemlist[i] = $"(Item #{i:000})";
|
||||
}
|
||||
|
||||
const ushort oldsea = 0x178;
|
||||
Span<ushort> tickets = stackalloc ushort[] {0x109, 0x113, 0x172, 0x173, oldsea }; // item IDs
|
||||
if (!SAV.Japanese && DialogResult.Yes != WinFormsUtil.Prompt(MessageBoxButtons.YesNo, $"Non Japanese save file. Add {itemlist[oldsea]} (unreleased)?"))
|
||||
var tickets = TicketItemIDs;
|
||||
if (!SAV.Japanese && DialogResult.Yes != WinFormsUtil.Prompt(MessageBoxButtons.YesNo, $"Non Japanese save file. Add {itemlist[ItemIDOldSeaMap]} (unreleased)?"))
|
||||
tickets = tickets[..^1]; // remove old sea map
|
||||
|
||||
var p = Pouches.FirstOrDefault(z => z.Type == InventoryType.KeyItems);
|
||||
|
|
|
@ -2,7 +2,6 @@ using System;
|
|||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using PKHeX.Core;
|
||||
|
||||
|
@ -105,7 +104,7 @@ public partial class SAV_CGearSkin : Form
|
|||
private void B_Save_Click(object sender, EventArgs e)
|
||||
{
|
||||
byte[] bgdata = bg.GetSkin(SAV is SAV5B2W2);
|
||||
if (bgdata.Any(z => z != 0))
|
||||
if (bgdata.AsSpan().IndexOfAnyExcept<byte>(0) != -1)
|
||||
{
|
||||
SAV.CGearSkinData = bgdata;
|
||||
Origin.CopyChangesFrom(SAV);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using PKHeX.Core;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
@ -42,13 +42,15 @@ public partial class SAV_PokeBlockORAS : Form
|
|||
Close();
|
||||
}
|
||||
|
||||
private void B_RandomizeBerries_Click(object sender, EventArgs e)
|
||||
private static ReadOnlySpan<byte> DefaultBerryTree => new byte[] { 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x80, 0x40, 0x01, 0x00, 0x00, 0x00 };
|
||||
|
||||
private void B_RandomizeBerries_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (DialogResult.Yes != WinFormsUtil.Prompt(MessageBoxButtons.YesNo, "Repopulate all berry plots with random berries?"))
|
||||
return;
|
||||
|
||||
// Randomize the trees.
|
||||
Span<byte> tree = stackalloc byte[] { 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x80, 0x40, 0x01, 0x00, 0x00, 0x00 };
|
||||
ReadOnlySpan<byte> tree = DefaultBerryTree;
|
||||
var plantable = Legal.Pouch_Berry_XY; // 0 index is None, skip with rand
|
||||
var rnd = Util.Rand;
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ public partial class SAV_Wondercard : Form
|
|||
return;
|
||||
|
||||
var enc = mga.Gifts[index];
|
||||
pb.AccessibleDescription = string.Join(Environment.NewLine, SummaryPreviewer.GetTextLines(enc));
|
||||
pb.AccessibleDescription = string.Join(Environment.NewLine, enc.GetTextLines());
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -185,10 +185,12 @@ public static class PIDTests
|
|||
0x049F2F05, // Mawile
|
||||
};
|
||||
|
||||
private static ReadOnlySpan<int> MawileIVs => new[] {31, 30, 29, 31, 23, 27};
|
||||
|
||||
[Fact]
|
||||
public static void VerifyMawileAntishiny()
|
||||
{
|
||||
VerifyResultsAntiShiny(MawileTeamPIDs, Mawile, 12345, 51882, stackalloc[] {31, 30, 29, 31, 23, 27});
|
||||
VerifyResultsAntiShiny(MawileTeamPIDs, Mawile, 12345, 51882, MawileIVs);
|
||||
}
|
||||
|
||||
private static void VerifyResultsAntiShiny(ReadOnlySpan<uint> resultPIDs, TeamLock[] team, ushort tid, ushort sid, ReadOnlySpan<int> ivs)
|
||||
|
|
Loading…
Reference in a new issue