diff --git a/PKHeX.Core/Legality/Encounters/Data/EncounterUtil.cs b/PKHeX.Core/Legality/Encounters/Data/EncounterUtil.cs index 6270797e8..767ac7503 100644 --- a/PKHeX.Core/Legality/Encounters/Data/EncounterUtil.cs +++ b/PKHeX.Core/Legality/Encounters/Data/EncounterUtil.cs @@ -28,7 +28,17 @@ internal static class EncounterUtil t.Nicknames = getNames(i, strings); t.TrainerNames = getNames(i + half, strings); } - string[] getNames(int i, IEnumerable names) => names.Select(z => z.Length > i ? z[i] : string.Empty).ToArray(); + static string[] getNames(int i, IEnumerable names) => names.Select(z => z.Length > i ? z[i] : string.Empty).ToArray(); + } + + internal static void MarkEncounterTradeNicknames(T[] table, string[][] strings) where T : EncounterTrade + { + for (int i = 0; i < table.Length; i++) + { + var t = table[i]; + t.Nicknames = getNames(i, strings); + } + static string[] getNames(int i, IEnumerable names) => names.Select(z => z.Length > i ? z[i] : string.Empty).ToArray(); } } } diff --git a/PKHeX.Core/Legality/Encounters/Data/Encounters1.cs b/PKHeX.Core/Legality/Encounters/Data/Encounters1.cs index b9b2e73d8..e19816c1d 100644 --- a/PKHeX.Core/Legality/Encounters/Data/Encounters1.cs +++ b/PKHeX.Core/Legality/Encounters/Data/Encounters1.cs @@ -16,6 +16,8 @@ internal static class Encounters1 private static EncounterArea1[] Get(string name, string ident, GameVersion game) => EncounterArea1.GetAreas(BinLinker.Unpack(Util.GetBinaryResource($"encounter_{name}.pkl"), ident), game); + static Encounters1() => EncounterUtil.MarkEncounterTradeNicknames(TradeGift_RBY, TradeGift_RBY_OTs); + internal static readonly EncounterStatic1[] StaticRBY = { // GameVersion is RBY for Pokemon with the same catch rate and initial moves in all games @@ -90,71 +92,40 @@ internal static class Encounters1 new EncounterStatic1(140, 20, Stadium), // Kabuto }; - internal static readonly EncounterTrade1[] TradeGift_RBY_Common = + internal static readonly EncounterTrade1[] TradeGift_RBY = { - // Species & Minimum level (legal) possible to acquire at. - //new EncounterTrade1(122, 06, RBY), // Mr. Mime - Game Corner Abra - new EncounterTrade1(032, 02, RD), // Nidoran♂ - Wild Nidoran♀ - new EncounterTrade1(029, 02, BU), // Nidoran♀ - Wild Nidoran♂ - new EncounterTrade1(030, 16, RB), // Nidorina - Evolve Nidorino - new EncounterTrade1(030, 16, YW), // Nidorina - Evolve Nidorino (Different initial moves) - new EncounterTrade1(108, 15, RB), // Lickitung - Surf Slowbro - new EncounterTrade1(083, 02, RB), // Farfetch’d - Wild Spearow - new EncounterTrade1(101, 03, RB), // Electrode - Wild Raichu + new EncounterTrade1(122, RB, 06 ), // Mr. Mime - Abra + new EncounterTrade1(032, RB, 02 ), // Nidoran♂ - Nidoran♀ + new EncounterTrade1(030, RB, 16 ), // Nidorina - Nidorino + new EncounterTrade1(108, RB, 15 ), // Lickitung - Slowbro + new EncounterTrade1(124, RB, 15, 10), // Jynx - Poliwhirl + new EncounterTrade1(114, RB, 13, 05), // Tangela - Venonat + new EncounterTrade1(083, RB, 02 ), // Farfetch’d - Spearow + new EncounterTrade1(101, RB, 03 ), // Electrode - Raichu + new EncounterTrade1(086, RB, 28, 05), // Seel - Ponyta - new EncounterTrade1(122, 03, BU), // Mr. Mime - Wild Jigglypuff - new EncounterTrade1(060, 02, BU), // Poliwag - Wild Rattata - //new EncounterTrade1(083, 02, BU), // Farfetch’d - Wild Pidgey + new EncounterTrade1(122, YW, 08, 06), // Mr. Mime - Clefairy + new EncounterTrade1(067, YW, 16, 05) { EvolveOnTrade = true }, // Machoke - Cubone + new EncounterTrade1(051, YW, 15, 05), // Dugtrio - Lickitung + new EncounterTrade1(047, YW, 13, 05), // Parasect - Tangel + new EncounterTrade1(112, YW, 15, 10), // Rhydon - Golduck + new EncounterTrade1(087, YW, 15, 05), // Dewgong - Growlithe + new EncounterTrade1(089, YW, 25, 05), // Muk - Kangaskhan + + new EncounterTrade1(122, BU, 03 ), // Mr. Mime - Jigglypuff + new EncounterTrade1(029, BU, 02 ), // Nidoran♀ - Nidoran♂ + new EncounterTrade1(060, BU, 02 ), // Poliwag - Rattata + new EncounterTrade1(115, BU, 15, 10), // Kangaskhan - Rhydon + new EncounterTrade1(128, BU, 28, 18), // Tauros - Persian + new EncounterTrade1(093, BU, 28, 14) { EvolveOnTrade = true }, // Haunter - Machop->Machoke + new EncounterTrade1(083, BU, 02 ), // Farfetch’d - Wild Pidgey + new EncounterTrade1(075, BU, 16, 15) { EvolveOnTrade = true }, // Graveler - Abra->Kadabra + new EncounterTrade1(079, BU, 22, 05), // Slowpoke - Seel + new EncounterTrade1(098, BU, 15, 05), // Krabby - Growlithe }; - internal static readonly EncounterTrade1[] TradeGift_RBY_NoTradeback = ArrayUtil.ConcatAll(TradeGift_RBY_Common, new[] - { - new EncounterTrade1(124, 15, RB), // Jynx - Fish Poliwhirl (GSC: 10) - new EncounterTrade1(114, 13, RB), // Tangela - Wild Venonat (GSC: 5) No different moves at level 13 - new EncounterTrade1(086, 28, RB), // Seel - Wild Ponyta (GSC: 5) - - new EncounterTrade1(115, 15, BU), // Kangaskhan - Trade Rhydon (GSC: 10) - new EncounterTrade1(128, 28, BU), // Tauros - Evolve Persian (GSC: 18) - new EncounterTrade1(098, 15, BU), // Krabby - Wild Growlithe (GSC: 5) - - //new EncounterTrade1(122, 08, YW), // Mr. Mime - Wild Clefairy (GSC: 6) - new EncounterTrade1(067, 16, YW) { EvolveOnTrade = true }, // Machoke - Wild Cubone (GSC: 5) - new EncounterTrade1(112, 15, YW), // Rhydon - Surf Golduck (GSC: 10) - new EncounterTrade1(087, 15, YW), // Dewgong - Wild Growlithe (GSC: 5) - new EncounterTrade1(089, 25, YW), // Muk - Wild Kangaskhan (GSC: 5) - new EncounterTrade1(079, 22, YW), // Slowpoke - Wild Seel (GSC 5) - new EncounterTrade1(051, 15, YW), // Dugtrio - Trade Lickitung (GSC 5) - new EncounterTrade1(047, 13, YW), // Parasect - Trade Tangela (GSC 5) - - new EncounterTrade1(093, 28, BU) { EvolveOnTrade = true }, // Haunter - Evolve Machop->Machoke - new EncounterTrade1(075, 16, BU) { EvolveOnTrade = true }, // Graveler - Evolve Abra->Kadabra - }); - - internal static readonly EncounterTrade1[] TradeGift_RBY_Tradeback = ArrayUtil.ConcatAll(TradeGift_RBY_Common, new[] - { - // Trade gifts that can be obtained at a lower level due to the requested Pokémon being a lower level in GSC - new EncounterTrade1(124, 10, RB), // Jynx - Fish Poliwhirl (RBY: 15) - new EncounterTrade1(114, 05, RB), // Tangela - Wild Venonat (RBY: 13) - new EncounterTrade1(086, 05, RB), // Seel - Egg Ponyta (RBY: 28) - - new EncounterTrade1(115, 10, BU), // Kangaskhan - Trade Rhydon (RBY: 42) - new EncounterTrade1(128, 18, BU), // Tauros - Evolve Persian (RBY: 28) - new EncounterTrade1(098, 05, BU), // Krabby - Egg Growlithe (RBY: 15) - - //new EncounterTrade1(122, 08, YW), // Mr. Mime - Wild Clefairy (RBY: 6) - new EncounterTrade1(067, 05, YW) { EvolveOnTrade = true }, // Machoke - Egg Cubone (RBY: 20) - new EncounterTrade1(112, 10, YW), // Rhydon - Surf Golduck (RBY: 15) - new EncounterTrade1(087, 05, YW), // Dewgong - Egg Growlithe (RBY: 15) - new EncounterTrade1(089, 05, YW), // Muk - Egg Kangaskhan (RBY: 25) - - new EncounterTrade1(079, 05, YW), // Slowpoke - Wild Seel (GSC 5) - new EncounterTrade1(051, 05, YW), // Dugtrio - Trade Lickitung (GSC 5) - new EncounterTrade1(047, 05, YW), // Parasect - Trade Tangela (GSC 5) - - // Stadium 2 can transfer from GSC->RBY without a "Trade", thus allowing unevolved outsiders - new EncounterTrade1(093, 14, BU) { EvolveOnTrade = true, GBEra = true }, // Haunter - Evolve Machop->Machoke - new EncounterTrade1(075, 15, BU) { EvolveOnTrade = true, GBEra = true }, // Graveler - Evolve Abra->Kadabra - }); + private const string tradeRBY = "traderby"; + private static readonly string[][] TradeGift_RBY_OTs = Util.GetLanguageStrings7(tradeRBY); private static readonly int[] Flawless15 = { 15, 15, 15, 15, 15, 15 }; private static readonly int[] Yoshira = { 5, 10, 1, 12, 5, 5 }; diff --git a/PKHeX.Core/Legality/Encounters/EncounterTrade/EncounterTrade.cs b/PKHeX.Core/Legality/Encounters/EncounterTrade/EncounterTrade.cs index c5f765cbf..9c77204c8 100644 --- a/PKHeX.Core/Legality/Encounters/EncounterTrade/EncounterTrade.cs +++ b/PKHeX.Core/Legality/Encounters/EncounterTrade/EncounterTrade.cs @@ -15,7 +15,7 @@ public abstract class EncounterTrade : IEncounterable, IMoveset, ILocation, IVer public int Species { get; set; } public int Form { get; set; } public int Level { get; set; } - public int LevelMin => Level; + public virtual int LevelMin => Level; public int LevelMax => 100; public IReadOnlyList Moves { get; set; } = Array.Empty(); public abstract int Generation { get; } diff --git a/PKHeX.Core/Legality/Encounters/EncounterTrade/EncounterTrade1.cs b/PKHeX.Core/Legality/Encounters/EncounterTrade/EncounterTrade1.cs index 6fb7015e1..4a79998ed 100644 --- a/PKHeX.Core/Legality/Encounters/EncounterTrade/EncounterTrade1.cs +++ b/PKHeX.Core/Legality/Encounters/EncounterTrade/EncounterTrade1.cs @@ -1,4 +1,6 @@ -namespace PKHeX.Core +using System.Linq; + +namespace PKHeX.Core { /// /// Trade Encounter data with a fixed Catch Rate @@ -10,14 +12,22 @@ public sealed class EncounterTrade1 : EncounterTradeGB { public override int Generation => 1; - public bool GBEra { private get; set; } + public override int LevelMin => CanObtainMinGSC() ? LevelMinGSC : LevelMinRBY; - public EncounterTrade1(int species, int level, GameVersion game) : base(species, level) + private readonly int LevelMinRBY; + private readonly int LevelMinGSC; + + public EncounterTrade1(int species, GameVersion game, int rby, int gsc) : base(species, gsc) { Version = game; TrainerNames = StringConverter12.G1TradeOTName; + + LevelMinRBY = rby; + LevelMinGSC = gsc; } + public EncounterTrade1(int species, GameVersion game, int rby) : this(species, game, rby, rby) { } + public byte GetInitialCatchRate() { var pt = Version == GameVersion.YW ? PersonalTable.Y : PersonalTable.RB; @@ -31,19 +41,59 @@ protected override void ApplyDetails(ITrainerInfo sav, EncounterCriteria criteri pk1.Catch_Rate = GetInitialCatchRate(); } - internal bool IsEncounterTrade1Valid(PKM pkm) + internal bool IsNicknameValid(PKM pkm) + { + var nick = pkm.Nickname; + if (pkm.Format <= 2) + return Nicknames.Contains(nick); + + // Converted string 1/2->7 to language specific value + // Nicknames can be from any of the languages it can trade between. + return pkm.Language == 1 ? Nicknames[1] == nick : GetNicknameIndex(nick) >= 2; + } + + internal bool IsTrainerNameValid(PKM pkm) { string ot = pkm.OT_Name; if (pkm.Format <= 2) return ot == StringConverter12.G1TradeOTStr; + // Converted string 1/2->7 to language specific value - var tr = GetOT(pkm.Language); + int lang = pkm.Language; + var tr = GetOT(lang); return ot == tr; } + private int GetNicknameIndex(string nickname) + { + var nn = Nicknames; + for (int i = 0; i < nn.Count; i++) + { + if (nn[i] == nickname) + return i; + } + return -1; + } + + private bool CanObtainMinGSC() + { + if (!ParseSettings.AllowGen1Tradeback) + return false; + if (Version == GameVersion.BU && EvolveOnTrade) + return ParseSettings.AllowGBCartEra; + return true; + } + + private bool IsMatchLevel(PKM pkm, int lvl) + { + if (!(pkm is PK1)) + return lvl >= LevelMinGSC; + return lvl >= LevelMin; + } + public override bool IsMatch(PKM pkm) { - if (Level > pkm.CurrentLevel) // minimum required level + if (!IsMatchLevel(pkm, pkm.CurrentLevel)) // minimum required level return false; if (Version == GameVersion.BU) @@ -52,7 +102,7 @@ public override bool IsMatch(PKM pkm) if (!pkm.Japanese) return false; // Stadium 2 can transfer from GSC->RBY without a "Trade", thus allowing un-evolved outsiders - if (GBEra && !ParseSettings.AllowGBCartEra) + if (EvolveOnTrade && !ParseSettings.AllowGBCartEra && pkm.CurrentLevel < LevelMinRBY) return false; } @@ -62,5 +112,14 @@ public override bool IsMatch(PKM pkm) var req = GetInitialCatchRate(); return req == pk1.Catch_Rate; } + + public bool IsMatchDeferred(PKM pk) + { + if (!IsTrainerNameValid(pk)) + return true; + if (!IsNicknameValid(pk)) + return true; + return false; + } } } diff --git a/PKHeX.Core/Legality/Encounters/Generator/EncounterGenerator12.cs b/PKHeX.Core/Legality/Encounters/Generator/EncounterGenerator12.cs index c02478f68..8261662ca 100644 --- a/PKHeX.Core/Legality/Encounters/Generator/EncounterGenerator12.cs +++ b/PKHeX.Core/Legality/Encounters/Generator/EncounterGenerator12.cs @@ -33,7 +33,7 @@ private static IEnumerable GenerateRawEncounters12(PKM pkm, Game deferred.Add(t); continue; } - if (t is EncounterTrade1 t1 && !t1.IsEncounterTrade1Valid(pkm)) + if (t is EncounterTrade1 t1 && t1.IsMatchDeferred(pkm)) { deferred.Add(t); continue; @@ -154,7 +154,7 @@ private static GBEncounterPriority GetGBEncounterPriority(PKM pkm, IEncounterabl switch (Encounter) { case EncounterTrade1 t1: - return t1.IsEncounterTrade1Valid(pkm) ? GBEncounterPriority.TradeEncounterG1 : GBEncounterPriority.Least; + return !t1.IsMatchDeferred(pkm) ? GBEncounterPriority.TradeEncounterG1 : GBEncounterPriority.Least; case EncounterTrade2 _: return GBEncounterPriority.TradeEncounterG2; case EncounterStatic s: diff --git a/PKHeX.Core/Legality/Encounters/Generator/EncounterTradeGenerator.cs b/PKHeX.Core/Legality/Encounters/Generator/EncounterTradeGenerator.cs index ce57f44bf..3674014a1 100644 --- a/PKHeX.Core/Legality/Encounters/Generator/EncounterTradeGenerator.cs +++ b/PKHeX.Core/Legality/Encounters/Generator/EncounterTradeGenerator.cs @@ -68,7 +68,7 @@ private static IEnumerable GetPossibleVC(IReadOnlyList GetEncounterTradeTableVC(GameVersion gameSource) { if (GameVersion.RBY.Contains(gameSource)) - return !ParseSettings.AllowGen1Tradeback ? Encounters1.TradeGift_RBY_NoTradeback : Encounters1.TradeGift_RBY_Tradeback; + return Encounters1.TradeGift_RBY; if (GameVersion.GSC.Contains(gameSource)) return Encounters2.TradeGift_GSC; return Array.Empty(); diff --git a/PKHeX.Core/Legality/Verifiers/NicknameVerifier.cs b/PKHeX.Core/Legality/Verifiers/NicknameVerifier.cs index 53fb7cd9d..68ca82857 100644 --- a/PKHeX.Core/Legality/Verifiers/NicknameVerifier.cs +++ b/PKHeX.Core/Legality/Verifiers/NicknameVerifier.cs @@ -174,8 +174,8 @@ private void VerifyNicknameTrade(LegalityAnalysis data, EncounterTrade t) { switch (data.Info.Generation) { - case 1: - case 2: VerifyTrade12(data, t); return; + case 1: VerifyTrade12(data, t); return; + case 2: return; // already checked all relevant properties when fetching with getValidEncounterTradeVC2 case 3: VerifyTrade3(data, t); return; case 4: VerifyTrade4(data, t); return; case 5: VerifyTrade5(data, t); return; @@ -212,10 +212,10 @@ private void VerifyG1NicknameWithinBounds(LegalityAnalysis data, string str) private static void VerifyTrade12(LegalityAnalysis data, EncounterTrade t) { - if (t.TID != 0) // Gen2 Trade - return; // already checked all relevant properties when fetching with getValidEncounterTradeVC2 - - if (!((EncounterTrade1)t).IsEncounterTrade1Valid(data.pkm)) + var t1 = (EncounterTrade1)t; + if (!t1.IsNicknameValid(data.pkm)) + data.AddLine(GetInvalid(LEncTradeChangedNickname, CheckIdentifier.Nickname)); + if (!t1.IsTrainerNameValid(data.pkm)) data.AddLine(GetInvalid(LEncTradeChangedOT, CheckIdentifier.Trainer)); } diff --git a/PKHeX.Core/PKHeX.Core.csproj b/PKHeX.Core/PKHeX.Core.csproj index d625a9359..d98d299c2 100644 --- a/PKHeX.Core/PKHeX.Core.csproj +++ b/PKHeX.Core/PKHeX.Core.csproj @@ -169,6 +169,7 @@ + @@ -228,6 +229,7 @@ + @@ -287,6 +289,7 @@ + @@ -346,6 +349,7 @@ + @@ -429,6 +433,7 @@ + @@ -488,6 +493,7 @@ + @@ -1013,6 +1019,7 @@ + @@ -1072,6 +1079,7 @@ + @@ -1131,6 +1139,7 @@ + @@ -1190,6 +1199,7 @@ + @@ -1273,6 +1283,7 @@ + @@ -1332,6 +1343,7 @@ + diff --git a/PKHeX.Core/Resources/text/de/text_traderby_de.txt b/PKHeX.Core/Resources/text/de/text_traderby_de.txt new file mode 100644 index 000000000..66453a5d7 --- /dev/null +++ b/PKHeX.Core/Resources/text/de/text_traderby_de.txt @@ -0,0 +1,16 @@ +MARCEL +NICK +BOB +MARK +LILLI +PATTE +PIA +TIM +ROBBI +MICKY +PROLO +BODO +SEPP +DICKIE +ROBBIE +STINKER \ No newline at end of file diff --git a/PKHeX.Core/Resources/text/en/text_traderby_en.txt b/PKHeX.Core/Resources/text/en/text_traderby_en.txt new file mode 100644 index 000000000..ffd457b43 --- /dev/null +++ b/PKHeX.Core/Resources/text/en/text_traderby_en.txt @@ -0,0 +1,16 @@ +MARCEL +SPOT +TERRY +MARC +LOLA +DUX +DORIS +CRINKLES +SAILOR +MILES +RICKY +GURIO +SPIKE +BUFFY +CEZANNE +STICKY \ No newline at end of file diff --git a/PKHeX.Core/Resources/text/es/text_traderby_es.txt b/PKHeX.Core/Resources/text/es/text_traderby_es.txt new file mode 100644 index 000000000..e04bc3589 --- /dev/null +++ b/PKHeX.Core/Resources/text/es/text_traderby_es.txt @@ -0,0 +1,16 @@ +MARCEL +DON +BOB +MARCO +LOLA +KUMO +DORIS +RINKES +MARINERO +MILES +RICKY +GURIO +SPIKE +BUFFY +CEZANNE +STICKY \ No newline at end of file diff --git a/PKHeX.Core/Resources/text/fr/text_traderby_fr.txt b/PKHeX.Core/Resources/text/fr/text_traderby_fr.txt new file mode 100644 index 000000000..e666e1e0a --- /dev/null +++ b/PKHeX.Core/Resources/text/fr/text_traderby_fr.txt @@ -0,0 +1,16 @@ +MARCEL +FABI +BIBICHE +GLAVIOTEUR +NINI +JULIO +KOURJUS +BIGOUDI +BIBI +TAPACENBAL +LEMASTAR +BIGLEUX +JOLATEIGNE +JUMANJO +BRUNO +CRADINGUE \ No newline at end of file diff --git a/PKHeX.Core/Resources/text/it/text_traderby_it.txt b/PKHeX.Core/Resources/text/it/text_traderby_it.txt new file mode 100644 index 000000000..7bdd8cac7 --- /dev/null +++ b/PKHeX.Core/Resources/text/it/text_traderby_it.txt @@ -0,0 +1,16 @@ +MARCELLO +SPOT +BOB +MARCO +LOLA +DUCCIO +DORIS +GRINZA +MARINAIO +MILES +RICKY +GURIO +SPIKE +BUFFY +CEZANNE +STICKY \ No newline at end of file diff --git a/PKHeX.Core/Resources/text/ja/text_traderby_ja.txt b/PKHeX.Core/Resources/text/ja/text_traderby_ja.txt new file mode 100644 index 000000000..964c18da0 --- /dev/null +++ b/PKHeX.Core/Resources/text/ja/text_traderby_ja.txt @@ -0,0 +1,26 @@ +バリバリ + +テリー +なめぞう +まさこ +おしょう +おマル +リンダ +パウーン +マイム +リッキー +ぐりお +つんつん +ごんすけ +セザンヌ +ベタぼう +まさる +チャッピー +ロモたん +ロダン +ぎゅうた +ゴーすけ +アッカ +さぶろう +オスカル +どうらく \ No newline at end of file