PKHeX/PKHeX.Core/MysteryGifts/WC3.cs
Kurt d13b893351 Use PKHeX as blanksav language
Closes #3009

Make QRPK7 implement IEncounterable and remove hardcoded PKHeX strings
Let force hatching for WC3 eggs try to hatch from the savefile's language, then revert to english if failed to set an OT.
2020-10-04 08:59:33 -07:00

295 lines
10 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
namespace PKHeX.Core
{
/// <summary>
/// Generation 3 Mystery Gift Template File
/// </summary>
/// <remarks>
/// This is fabricated data built to emulate the future generation Mystery Gift objects.
/// Data here is not stored in any save file and cannot be naturally exported.
/// </remarks>
public sealed class WC3 : MysteryGift, IRibbonSetEvent3
{
public override MysteryGift Clone() => (WC3)MemberwiseClone();
/// <summary>
/// Matched <see cref="PIDIV"/> Type
/// </summary>
public PIDType Method;
public override string OT_Name { get; set; } = string.Empty;
public int OT_Gender { get; set; } = 3;
public override int TID { get; set; }
public override int SID { get; set; }
public override int Location { get; set; } = 255;
public override int EggLocation { get => 0; set {} }
public override GameVersion Version { get; set; }
public int Language { get; set; } = -1;
public override int Species { get; set; }
public override bool IsEgg { get; set; }
public override IReadOnlyList<int> Moves { get; set; } = Array.Empty<int>();
public bool NotDistributed { get; set; }
public Shiny Shiny { get; set; } = Shiny.Random;
public bool Fateful { get; set; } // Obedience Flag
// Mystery Gift Properties
public override int Format => 3;
public override int Level { get; set; }
public override int Ball { get; set; } = 4;
public override bool IsShiny => Shiny == Shiny.Always;
public bool RibbonEarth { get; set; }
public bool RibbonNational { get; set; }
public bool RibbonCountry { get; set; }
public bool RibbonChampionBattle { get; set; }
public bool RibbonChampionRegional { get; set; }
public bool RibbonChampionNational { get; set; }
// Description
public override string CardTitle { get; set; } = "Generation 3 Event";
public override string CardHeader => CardTitle;
// Unused
public override bool GiftUsed { get; set; }
public override int CardID { get; set; }
public override bool IsItem { get; set; }
public override int ItemID { get; set; }
public override bool IsPokémon { get; set; } = true;
public override bool Empty => false;
public override int Gender { get; set; }
public override int Form { get; set; }
// Synthetic
private int? _metLevel;
public int Met_Level
{
get => _metLevel ?? (IsEgg ? 0 : Level);
set => _metLevel = value;
}
public override PKM ConvertToPKM(ITrainerInfo sav, EncounterCriteria criteria)
{
PK3 pk = new PK3
{
Species = Species,
Met_Level = Met_Level,
Met_Location = Location,
Ball = 4,
// Ribbons
RibbonCountry = RibbonCountry,
RibbonNational = RibbonNational,
RibbonEarth = RibbonEarth,
RibbonChampionBattle = RibbonChampionBattle,
RibbonChampionRegional = RibbonChampionRegional,
RibbonChampionNational = RibbonChampionNational,
FatefulEncounter = Fateful,
Version = GetVersion(sav),
};
pk.EXP = Experience.GetEXP(Level, pk.PersonalInfo.EXPGrowth);
SetMoves(pk);
bool hatchedEgg = IsEgg && sav.Generation != 3;
if (hatchedEgg)
{
SetForceHatchDetails(pk, sav);
}
else
{
pk.OT_Gender = OT_Gender != 3 ? OT_Gender & 1 : sav.Gender;
pk.TID = TID;
pk.SID = SID;
pk.Language = (int)GetSafeLanguage((LanguageID)sav.Language);
pk.OT_Name = !string.IsNullOrWhiteSpace(OT_Name) ? OT_Name : sav.OT;
if (IsEgg)
pk.IsEgg = true; // lang should be set to japanese already
}
pk.Nickname = SpeciesName.GetSpeciesNameGeneration(Species, pk.Language, 3); // will be set to Egg nickname if appropriate by PK3 setter
var pi = pk.PersonalInfo;
pk.OT_Friendship = pk.IsEgg ? pi.HatchCycles : pi.BaseFriendship;
// Generate PIDIV
SetPINGA(pk, criteria);
pk.HeldItem = 0; // clear, only random for Jirachi (?), no loss
if (Version == GameVersion.XD)
pk.FatefulEncounter = true; // pk3 is already converted from xk3
pk.RefreshChecksum();
return pk;
}
private void SetForceHatchDetails(PK3 pk, ITrainerInfo sav)
{
pk.Language = (int)GetSafeLanguageNotEgg((LanguageID)sav.Language);
pk.OT_Name = sav.OT;
// ugly workaround for character table interactions
if (string.IsNullOrWhiteSpace(pk.OT_Name))
{
pk.Language = (int)LanguageID.English;
pk.OT_Name = "PKHeX";
}
pk.OT_Gender = sav.Gender;
pk.TID = sav.TID;
pk.SID = sav.SID;
pk.Met_Location = pk.FRLG ? 146 /* Four Island */ : 32; // Route 117
pk.FatefulEncounter &= pk.FRLG; // clear flag for RSE
pk.Met_Level = 0; // hatched
}
private int GetVersion(ITrainerInfo sav)
{
if (Version != 0)
return (int) GetRandomVersion(Version);
bool gen3 = sav.Game <= 15 && GameVersion.Gen3.Contains((GameVersion)sav.Game);
return gen3 ? sav.Game : (int)GameVersion.R;
}
private void SetMoves(PK3 pk)
{
if (Moves.Count == 0) // not completely defined
Moves = MoveList.GetBaseEggMoves(pk, Species, Form, (GameVersion)pk.Version, Level);
if (Moves.Count != 4)
{
var moves = Moves.ToArray();
Array.Resize(ref moves, 4);
Moves = moves;
}
pk.SetMoves(Moves);
pk.SetMaximumPPCurrent(Moves);
}
private void SetPINGA(PK3 pk, EncounterCriteria criteria)
{
var seed = Util.Rand32();
seed = GetSaneSeed(seed);
PIDGenerator.SetValuesFromSeed(pk, Method, seed);
}
private uint GetSaneSeed(uint seed)
{
return Method switch
{
PIDType.BACD_R => (seed & 0x0000FFFF),
PIDType.BACD_R_S => (seed & 0x000000FF),
_ => seed
};
}
private LanguageID GetSafeLanguage(LanguageID hatchLang)
{
if (IsEgg)
return LanguageID.Japanese;
return GetSafeLanguageNotEgg(hatchLang);
}
private LanguageID GetSafeLanguageNotEgg(LanguageID hatchLang)
{
if (Language != -1)
return (LanguageID) Language;
if (hatchLang < LanguageID.Korean && hatchLang != LanguageID.Hacked)
return hatchLang;
return LanguageID.English; // fallback
}
private static GameVersion GetRandomVersion(GameVersion version)
{
if (version <= GameVersion.CXD && version > GameVersion.Unknown) // single game
return version;
int rand = Util.Rand.Next(2); // 0 or 1
switch (version)
{
case GameVersion.FRLG:
return GameVersion.FR + rand; // or LG
case GameVersion.RSE:
case GameVersion.RS:
return GameVersion.S + rand; // or R
case GameVersion.COLO:
case GameVersion.XD:
return GameVersion.CXD;
default:
throw new Exception($"Unknown GameVersion: {version}");
}
}
protected override bool IsMatchExact(PKM pkm, DexLevel evo)
{
// Gen3 Version MUST match.
if (Version != 0 && !Version.Contains((GameVersion)pkm.Version))
return false;
bool hatchedEgg = IsEgg && !pkm.IsEgg;
if (!hatchedEgg)
{
if (SID != -1 && SID != pkm.SID) return false;
if (TID != -1 && TID != pkm.TID) return false;
if (OT_Gender < 3 && OT_Gender != pkm.OT_Gender) return false;
var wcOT = OT_Name;
if (!string.IsNullOrEmpty(wcOT))
{
if (wcOT.Length > 7) // Colosseum MATTLE Ho-Oh
{
if (!GetIsValidOTMattleHoOh(wcOT, pkm.OT_Name, pkm is CK3))
return false;
}
else if (wcOT != pkm.OT_Name)
{
return false;
}
}
}
if (Form != evo.Form && !Legal.IsFormChangeable(Species, Form, pkm.AltForm, pkm.Format))
return false;
if (Language != -1 && Language != pkm.Language) return false;
if (Ball != pkm.Ball) return false;
if (Fateful != pkm.FatefulEncounter)
{
// XD Gifts only at level 20 get flagged after transfer
if (Version == GameVersion.XD != pkm is XK3)
return false;
}
if (pkm.IsNative)
{
if (hatchedEgg)
return true; // defer egg specific checks to later.
if (Met_Level != pkm.Met_Level)
return false;
if (Location != pkm.Met_Location)
return false;
}
else
{
if (pkm.IsEgg)
return false;
if (Level > pkm.Met_Level)
return false;
}
return true;
}
private static bool GetIsValidOTMattleHoOh(string wc, string ot, bool ck3)
{
if (ck3 && ot.Length == 10)
return wc == ot;
return ot.Length == 7 && wc.StartsWith(ot);
}
protected override bool IsMatchDeferred(PKM pkm)
{
return Species != pkm.Species;
}
}
}