From e58856565767fa294efc2152b6a43e5dac6dc1b6 Mon Sep 17 00:00:00 2001 From: Kurt Date: Fri, 6 Jul 2018 17:37:07 -0700 Subject: [PATCH] Refactoring reduce pk3/ck3/xk3 logic, share AbilityBit property within _K3, and when loading, type check (favored over hardcoding individual load cases) pull some non-gui code from PKMEditor to core/etc for general data fetching --- PKHeX.Core/Game/GameStrings/GameInfo.cs | 1 + PKHeX.Core/Game/GameStrings/GameStrings.cs | 23 +++ PKHeX.Core/PKM/CK3.cs | 2 +- PKHeX.Core/PKM/PK3.cs | 3 +- PKHeX.Core/PKM/Shared/_K3.cs | 5 +- PKHeX.Core/PKM/Util/FormConverter.cs | 26 ++++ PKHeX.Core/PKM/XK3.cs | 2 +- PKHeX.WinForms/Controls/PKM Editor/EditPK3.cs | 76 +--------- .../Controls/PKM Editor/PKMEditor.cs | 131 ++++++++---------- .../Controls/PKM Editor/StatEditor.cs | 1 + 10 files changed, 119 insertions(+), 151 deletions(-) diff --git a/PKHeX.Core/Game/GameStrings/GameInfo.cs b/PKHeX.Core/Game/GameStrings/GameInfo.cs index cc95ff53d..19eb491c9 100644 --- a/PKHeX.Core/Game/GameStrings/GameInfo.cs +++ b/PKHeX.Core/Game/GameStrings/GameInfo.cs @@ -54,6 +54,7 @@ public static string[] GetStrings(string ident, string lang, string type = "text public static IReadOnlyList LegalMoveDataSource => Strings.LegalMoveDataSource; public static IReadOnlyList HaXMoveDataSource => Strings.HaXMoveDataSource; public static IReadOnlyList MoveDataSource => Strings.MoveDataSource; + public static IReadOnlyList LanguageDataSource(int gen) => GameStrings.LanguageDataSource(gen); /// diff --git a/PKHeX.Core/Game/GameStrings/GameStrings.cs b/PKHeX.Core/Game/GameStrings/GameStrings.cs index f85e0f3b5..37080bd01 100644 --- a/PKHeX.Core/Game/GameStrings/GameStrings.cs +++ b/PKHeX.Core/Game/GameStrings/GameStrings.cs @@ -482,5 +482,28 @@ public IReadOnlyList GetLocationList(GameVersion Version, int SaveFor return MetGen6; } + + private static readonly IReadOnlyList LanguageList = Util.GetUnsortedCBList("languages"); + public static IReadOnlyList LanguageDataSource(int gen) + { + var languages = LanguageList.ToList(); + Util.GetUnsortedCBList("languages"); + if (gen == 3) + languages.RemoveAll(l => l.Value >= (int)LanguageID.Korean); + else if (gen < 7) + languages.RemoveAll(l => l.Value > (int)LanguageID.Korean); + return languages; + } + + private static readonly string[] abilIdentifier = { " (1)", " (2)", " (H)" }; + public IReadOnlyList GetAbilityDataSource(IEnumerable abils) + { + return abils.Select(GetItem).ToList(); + ComboItem GetItem(int ability, int index) => new ComboItem + { + Value = ability, + Text = GameInfo.Strings.abilitylist[ability] + abilIdentifier[index] + }; + } } } \ No newline at end of file diff --git a/PKHeX.Core/PKM/CK3.cs b/PKHeX.Core/PKM/CK3.cs index 8221a8016..4a8f808c1 100644 --- a/PKHeX.Core/PKM/CK3.cs +++ b/PKHeX.Core/PKM/CK3.cs @@ -163,7 +163,7 @@ public CK3(byte[] decryptedData = null, string ident = null) public override int PKRS_Strain { get => Data[0xCA] & 0xF; set => Data[0xCA] = (byte)(value & 0xF); } public override bool IsEgg { get => Data[0xCB] == 1; set => Data[0xCB] = (byte)(value ? 1 : 0); } - public override int AbilityNumber { get => 1 << Data[0xCC]; set => Data[0xCC] = (byte)((value >> 1) & 1); } + public override bool AbilityBit { get => Data[0xCC] == 1; set => Data[0xCC] = (byte)(value ? 1 : 0); } public override bool Valid { get => Data[0xCD] == 0; set { if (value) Data[0xCD] = 0; } } // 0xCE unknown public override int MarkValue { get => SwapBits(Data[0xCF], 1, 2); protected set => Data[0xCF] = (byte)SwapBits(value, 1, 2); } diff --git a/PKHeX.Core/PKM/PK3.cs b/PKHeX.Core/PKM/PK3.cs index 1f9f81620..e42a0adc2 100644 --- a/PKHeX.Core/PKM/PK3.cs +++ b/PKHeX.Core/PKM/PK3.cs @@ -109,7 +109,7 @@ public PK3(byte[] decryptedData = null, string ident = null) public override int IV_SPA { get => (int)(IV32 >> 20) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 20)) | (uint)((value > 31 ? 31 : value) << 20)); } public override int IV_SPD { get => (int)(IV32 >> 25) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 25)) | (uint)((value > 31 ? 31 : value) << 25)); } private bool egg { get => ((IV32 >> 30) & 1) == 1; set => IV32 = (uint)((IV32 & ~0x40000000) | (uint)(value ? 0x40000000 : 0)); } - public bool AbilityBit { get => IV32 >> 31 == 1; set => IV32 = (IV32 & 0x7FFFFFFF) | (uint)(value ? 1 << 31 : 0); } + public override bool AbilityBit { get => IV32 >> 31 == 1; set => IV32 = (IV32 & 0x7FFFFFFF) | (uint)(value ? 1 << 31 : 0); } private uint RIB0 { get => BitConverter.ToUInt32(Data, 0x4C); set => BitConverter.GetBytes(value).CopyTo(Data, 0x4C); } public override int RibbonCountG3Cool { get => (int)(RIB0 >> 00) & 7; set => RIB0 = (uint)((RIB0 & ~(7 << 00)) | (uint)(value & 7) << 00); } @@ -147,7 +147,6 @@ public PK3(byte[] decryptedData = null, string ident = null) public override int Stat_SPD { get => BitConverter.ToUInt16(Data, 0x62); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x62); } // Generated Attributes - public override int AbilityNumber { get => 1 << (AbilityBit ? 1 : 0); set => AbilityBit = value > 1; } // 1/2 -> 0/1 public override bool Japanese => IsEgg || Language == (int)LanguageID.Japanese; protected override byte[] Encrypt() diff --git a/PKHeX.Core/PKM/Shared/_K3.cs b/PKHeX.Core/PKM/Shared/_K3.cs index 16054cdb7..ca5386f65 100644 --- a/PKHeX.Core/PKM/Shared/_K3.cs +++ b/PKHeX.Core/PKM/Shared/_K3.cs @@ -26,7 +26,7 @@ public abstract class _K3 : PKM, IRibbonSetEvent3, IRibbonSetCommon3, IRibbonSet public override bool WasGiftEgg => IsEgg && Met_Location == 253; // Gift Egg, indistinguible from normal eggs after hatch public override bool WasEventEgg => IsEgg && Met_Location == 255; // Event Egg, indistinguible from normal eggs after hatch - public override int Ability { get { var pi = (PersonalInfoG3)PersonalInfo; return pi.Abilities[pi.HasSecondAbility ? AbilityNumber >> 1 : 0]; } set { } } + public override int Ability { get { var pi = (PersonalInfoG3)PersonalInfo; return pi.Abilities[pi.HasSecondAbility && AbilityBit ? 1 : 0]; } set { } } public override uint EncryptionConstant { get => PID; set { } } public override int Nature { get => (int)(PID % 25); set { } } public override int AltForm { get => Species == 201 ? PKX.GetUnownForm(PID) : 0; set { } } @@ -37,6 +37,9 @@ public abstract class _K3 : PKM, IRibbonSetEvent3, IRibbonSetCommon3, IRibbonSet public override int CurrentHandler { get => 0; set { } } public override int Egg_Location { get => 0; set { } } + public override int AbilityNumber { get => 1 << (AbilityBit ? 1 : 0); set => AbilityBit = value > 1; } // [0,1]->[1,2] ; [1,x]->[0,1] + public abstract bool AbilityBit { get; set; } + public abstract bool RibbonEarth { get; set; } public abstract bool RibbonNational { get; set; } public abstract bool RibbonCountry { get; set; } diff --git a/PKHeX.Core/PKM/Util/FormConverter.cs b/PKHeX.Core/PKM/Util/FormConverter.cs index 053cf6a3f..947c69f7c 100644 --- a/PKHeX.Core/PKM/Util/FormConverter.cs +++ b/PKHeX.Core/PKM/Util/FormConverter.cs @@ -689,5 +689,31 @@ private static string[] GetFormsUnown(int generation) // AO 015, 018, 080, 208, 254, 260, 302, 319, 323, 334, 362, 373, 376, 384, 428, 475, 531, 719, }; + + /// + /// Checks if the data should have a drop-down selection visible for the value. + /// + /// Game specific personal info + /// ID + /// ID + /// True if has formes that can be provided by , otherwise false for none. + public static bool HasFormSelection(PersonalInfo pi, int species, int format) + { + if (format <= 3 && species != 201) + return false; + + if (HasFormeValuesNotIndicatedByPersonal.Contains(species)) + return true; + + int count = pi.FormeCount; + return count > 1; + } + + private static readonly HashSet HasFormeValuesNotIndicatedByPersonal = new HashSet + { + 201, // Unown + 414, // Mothim (Burmy forme carried over, not cleared) + 664, 665, // Vivillon pre-evos + }; } } diff --git a/PKHeX.Core/PKM/XK3.cs b/PKHeX.Core/PKM/XK3.cs index 1de1ff0f5..51c7277f7 100644 --- a/PKHeX.Core/PKM/XK3.cs +++ b/PKHeX.Core/PKM/XK3.cs @@ -60,7 +60,7 @@ public XK3(byte[] decryptedData = null, string ident = null) public bool UnusedFlag3 { get => (XDPKMFLAGS & (1 << 3)) == 1 << 3; set => XDPKMFLAGS = XDPKMFLAGS & ~(1 << 3) | (value ? 1 << 3 : 0); } public bool BlockTrades { get => (XDPKMFLAGS & (1 << 4)) == 1 << 4; set => XDPKMFLAGS = XDPKMFLAGS & ~(1 << 4) | (value ? 1 << 4 : 0); } public override bool Valid { get => (XDPKMFLAGS & (1 << 5)) == 0; set => XDPKMFLAGS = XDPKMFLAGS & ~(1 << 5) | (value ? 0 : 1 << 5); } // invalid flag - public override int AbilityNumber { get => 1 << ((XDPKMFLAGS >> 6) & 1); set => XDPKMFLAGS = XDPKMFLAGS & ~(1 << 6) | (((value >> 1) & 1) << 6); } + public override bool AbilityBit { get => 1 << ((XDPKMFLAGS >> 6) & 1) == 1; set => XDPKMFLAGS = XDPKMFLAGS & ~(1 << 6) | ((value ? 1 : 0) << 6); } public override bool IsEgg { get => (XDPKMFLAGS & (1 << 7)) == 1 << 7; set => XDPKMFLAGS = XDPKMFLAGS & ~(1 << 7) | (value ? 1 << 7 : 0); } // 0x1E-0x1F Unknown public override uint EXP { get => BigEndian.ToUInt32(Data, 0x20); set => BigEndian.GetBytes(value).CopyTo(Data, 0x20); } diff --git a/PKHeX.WinForms/Controls/PKM Editor/EditPK3.cs b/PKHeX.WinForms/Controls/PKM Editor/EditPK3.cs index 4097915c4..f531f84a4 100644 --- a/PKHeX.WinForms/Controls/PKM Editor/EditPK3.cs +++ b/PKHeX.WinForms/Controls/PKM Editor/EditPK3.cs @@ -6,7 +6,7 @@ public partial class PKMEditor { private void PopulateFieldsPK3() { - if (!(pkm is PK3 pk3)) + if (!(pkm is _K3 pk3)) return; LoadMisc1(pk3); @@ -14,92 +14,30 @@ private void PopulateFieldsPK3() LoadMisc3(pk3); CB_Ability.SelectedIndex = pk3.AbilityBit && CB_Ability.Items.Count > 1 ? 1 : 0; + if (pk3 is IShadowPKM s) + LoadShadow3(s); LoadPartyStats(pk3); UpdateStats(); } - private void PopulateFieldsCK3() - { - if (!(pkm is CK3 ck3)) - return; - - LoadMisc1(ck3); - LoadMisc2(ck3); - LoadMisc3(ck3); - - int abil = ck3.AbilityNumber >> 1; - CB_Ability.SelectedIndex = abil >= CB_Ability.Items.Count ? 0 : abil; - LoadShadow3(ck3); - - LoadPartyStats(ck3); - UpdateStats(); - } - private void PopulateFieldsXK3() - { - if (!(pkm is XK3 xk3)) - return; - - LoadMisc1(xk3); - LoadMisc2(xk3); - LoadMisc3(xk3); - - int abil = xk3.AbilityNumber >> 1; - CB_Ability.SelectedIndex = abil >= CB_Ability.Items.Count ? 0 : abil; - LoadShadow3(xk3); - - LoadPartyStats(xk3); - UpdateStats(); - } private PKM PreparePK3() { - if (!(pkm is PK3 pk3)) + if (!(pkm is _K3 pk3)) return null; SaveMisc1(pk3); SaveMisc2(pk3); SaveMisc3(pk3); - pk3.AbilityNumber = 1 << CB_Ability.SelectedIndex; + pk3.AbilityBit = CB_Ability.SelectedIndex != 0; + if (pkm is IShadowPKM ck3) + SaveShadow3(ck3); SavePartyStats(pk3); pk3.FixMoves(); pk3.RefreshChecksum(); return pk3; } - private PKM PrepareCK3() - { - if (!(pkm is CK3 ck3)) - return null; - - SaveMisc1(ck3); - SaveMisc2(ck3); - SaveMisc3(ck3); - - ck3.AbilityNumber = 1 << CB_Ability.SelectedIndex; // to match gen6+ - SaveShadow3(ck3); - - SavePartyStats(ck3); - ck3.FixMoves(); - ck3.RefreshChecksum(); - return ck3; - } - private PKM PrepareXK3() - { - if (!(pkm is XK3 xk3)) - return null; - - SaveMisc1(xk3); - SaveMisc2(xk3); - SaveMisc3(xk3); - - xk3.AbilityNumber = 1 << CB_Ability.SelectedIndex; // to match gen6+ - SaveShadow3(xk3); - - SavePartyStats(xk3); - xk3.FixMoves(); - xk3.RefreshChecksum(); - return xk3; - } } } diff --git a/PKHeX.WinForms/Controls/PKM Editor/PKMEditor.cs b/PKHeX.WinForms/Controls/PKM Editor/PKMEditor.cs index 25ad4771b..faabce147 100644 --- a/PKHeX.WinForms/Controls/PKM Editor/PKMEditor.cs +++ b/PKHeX.WinForms/Controls/PKM Editor/PKMEditor.cs @@ -148,23 +148,14 @@ public void SetPKMFormatMode(int Format) GetPKMfromFields = PreparePK2; break; case 3: - if (pkm is CK3) - { - GetFieldsfromPKM = PopulateFieldsCK3; - GetPKMfromFields = PrepareCK3; - extraBytes = CK3.ExtraBytes; - break; - } - if (pkm is XK3) - { - GetFieldsfromPKM = PopulateFieldsXK3; - GetPKMfromFields = PrepareXK3; - extraBytes = XK3.ExtraBytes; - break; - } GetFieldsfromPKM = PopulateFieldsPK3; GetPKMfromFields = PreparePK3; - extraBytes = PK3.ExtraBytes; + switch (pkm) + { + case CK3 _: extraBytes = CK3.ExtraBytes; break; + case XK3 _: extraBytes = XK3.ExtraBytes; break; + default: extraBytes = PK3.ExtraBytes; break; + } break; case 4: GetFieldsfromPKM = PopulateFieldsPK4; @@ -345,21 +336,16 @@ private void SetDetailsHT(ITrainerInfo SAV) if (TB_OTt2.Text.Length > 0) Label_CTGender.Text = gendersymbols[SAV.Gender & 1]; } + private void SetForms() { - int species = WinFormsUtil.GetIndex(CB_Species); - if (pkm.Format < 4 && species != 201) - { - Label_Form.Visible = CB_Form.Visible = CB_Form.Enabled = false; - return; - } - - int count = (RequestSaveFile?.Personal[species] ?? pkm.PersonalInfo).FormeCount; - bool hasForms = count > 1 || new[] { 201, 664, 665, 414 }.Contains(species); + int species = pkm.Species; + var pi = RequestSaveFile?.Personal[species] ?? pkm.PersonalInfo; + bool hasForms = FormConverter.HasFormSelection(pi, species, pkm.Format); CB_Form.Enabled = CB_Form.Visible = Label_Form.Visible = hasForms; if (HaX && pkm.Format >= 4) - Label_Form.Visible = true; + Label_Form.Visible = true; // show with value entry textbox if (!hasForms) return; @@ -367,7 +353,8 @@ private void SetForms() var ds = PKX.GetFormList(species, GameInfo.Strings.types, GameInfo.Strings.forms, gendersymbols, pkm.Format).ToList(); if (ds.Count == 1 && string.IsNullOrEmpty(ds[0])) // empty (Alolan Totems) CB_Form.Enabled = CB_Form.Visible = Label_Form.Visible = false; - else CB_Form.DataSource = ds; + else + CB_Form.DataSource = ds; } private void SetAbilityList() { @@ -388,22 +375,6 @@ private void SetAbilityList() } private static int GetSafeIndex(ComboBox cb, int index) => Math.Max(0, Math.Min(cb.Items.Count - 1, index)); - private static readonly string[] abilIdentifier = { " (1)", " (2)", " (H)" }; - private static List GetAbilityList(PKM pkm) - { - var abils = pkm.PersonalInfo.Abilities; - if (pkm.Format == 3 && abils[1] == abils[0]) - abils = new[] {abils[0]}; - - return abils.Select(GetItem).ToList(); - - ComboItem GetItem(int ability, int index) => new ComboItem - { - Value = ability, - Text = GameInfo.Strings.abilitylist[ability] + abilIdentifier[index] - }; - } - private void SetIsShiny(object sender) { if (sender == TB_PID) @@ -512,8 +483,6 @@ private void ClickGender(object sender, EventArgs e) } else if (pkm.Format <= 4) { - if (FieldsLoaded) - pkm.Species = WinFormsUtil.GetIndex(CB_Species); pkm.Version = WinFormsUtil.GetIndex(CB_GameOrigin); pkm.Nature = WinFormsUtil.GetIndex(CB_Nature); pkm.AltForm = CB_Form.SelectedIndex; @@ -1320,7 +1289,7 @@ private void Update_ID(object sender, EventArgs e) SetIsShiny(sender); UpdateSprite(); - Stats.UpdateIVs(null, null); // If the EC is changed, EC%6 (Characteristic) might be changed. + Stats.UpdateCharacteristic(); // If the EC is changed, EC%6 (Characteristic) might be changed. if (pkm.Format <= 4 && FieldsLoaded) { FieldsLoaded = false; @@ -1377,34 +1346,38 @@ private void ValidateComboBox(object sender, CancelEventArgs e) } private void ValidateComboBox2(object sender, EventArgs e) { - if (!FieldsInitialized) + if (!FieldsInitialized || !FieldsLoaded) return; + ValidateComboBox(sender, null); - if (FieldsLoaded) + if (sender == CB_Ability) { - if (sender == CB_Ability && pkm.Format >= 6) + if (pkm.Format >= 6) TB_AbilityNumber.Text = (1 << CB_Ability.SelectedIndex).ToString(); - if (sender == CB_Ability && pkm.Format <= 5 && CB_Ability.SelectedIndex < 2) // not hidden + else if (pkm.Format <= 5 && CB_Ability.SelectedIndex < 2) // Format <= 5, not hidden UpdateRandomPID(sender, e); - if (sender == CB_Nature && pkm.Format <= 4) - { - pkm.Nature = CB_Nature.SelectedIndex; - UpdateRandomPID(sender, e); - } - if (sender == CB_HeldItem || sender == CB_Ability) - UpdateLegality(); + UpdateLegality(); + } + else if (sender == CB_Nature) + { + pkm.Nature = CB_Nature.SelectedIndex; + if (pkm.Format <= 4) + UpdateRandomPID(sender, e); + UpdateNatureModification(sender, EventArgs.Empty); + Stats.UpdateIVs(null, EventArgs.Empty); // updating Nature will trigger stats to update as well + UpdateLegality(); + } + else if (sender == CB_HeldItem) + { + UpdateLegality(); } - UpdateNatureModification(sender, null); - Stats.UpdateIVs(null, null); // updating Nature will trigger stats to update as well } private void ValidateMove(object sender, EventArgs e) { - if (!FieldsInitialized) - return; - ValidateComboBox(sender); - if (!FieldsLoaded) + if (!FieldsInitialized || !FieldsLoaded) return; + ValidateComboBox(sender); if (Moves.Contains(sender)) // Move UpdatePP(sender, e); @@ -1470,18 +1443,14 @@ private void OpenHistory(object sender, EventArgs e) /// Pokémon data to edit public bool ToggleInterface(SaveFile sav, PKM pk) { - if (pk.GetType() != sav.PKMType || pkm.Format < 3) - pk = sav.BlankPKM; - pkm = pk; - - ToggleInterface(pkm.Format); - ToggleInterface(pkm.GetType()); - + pkm = GetCompatiblePKM(sav, pk); + ToggleInterface(pkm); return FinalizeInterface(sav); } - private void ToggleInterface(Type t) + private void ToggleInterface(PKM t) { - FLP_Purification.Visible = FLP_ShadowID.Visible = t == typeof(XK3) || t == typeof(CK3); + FLP_Purification.Visible = FLP_ShadowID.Visible = t is IShadowPKM; + ToggleInterface(pkm.Format); } private void ToggleInterface(int gen) { @@ -1661,12 +1630,7 @@ private void PopulateFilteredDataSources(SaveFile SAV) if (SAV.Generation > 1) CB_HeldItem.DataSource = new BindingSource(GameInfo.ItemDataSource.Where(i => i.Value <= SAV.MaxItemID).ToList(), null); - var languages = Util.GetUnsortedCBList("languages"); - if (SAV.Generation == 3) - languages.RemoveAll(l => l.Value >= (int)LanguageID.Korean); - else if (SAV.Generation < 7) - languages.RemoveAll(l => l.Value > (int)LanguageID.Korean); - CB_Language.DataSource = languages; + CB_Language.DataSource = GameInfo.LanguageDataSource(SAV.Generation); CB_Ball.DataSource = new BindingSource(GameInfo.BallDataSource.Where(b => b.Value <= SAV.MaxBallID).ToList(), null); CB_Species.DataSource = new BindingSource(GameInfo.SpeciesDataSource.Where(s => s.Value <= SAV.MaxSpeciesID).ToList(), null); @@ -1697,6 +1661,19 @@ private static List GetSuggestionMessage(PKM pkm, int level, int locatio suggestion.Add($"{MsgPKMSuggestionLevel} {minlvl}"); return suggestion; } + private static IReadOnlyList GetAbilityList(PKM pkm) + { + var abils = pkm.PersonalInfo.Abilities; + if (pkm.Format == 3 && abils[1] == abils[0]) + abils = new[] { abils[0] }; + return GameInfo.Strings.GetAbilityDataSource(abils); + } + private static PKM GetCompatiblePKM(SaveFile sav, PKM current) + { + if (current.Format < 3 || current.GetType() != sav.PKMType) + return sav.BlankPKM; + return current; + } private static void InitializeBinding(ListControl cb) { cb.DisplayMember = nameof(ComboItem.Text); diff --git a/PKHeX.WinForms/Controls/PKM Editor/StatEditor.cs b/PKHeX.WinForms/Controls/PKM Editor/StatEditor.cs index 191504e61..274a3c370 100644 --- a/PKHeX.WinForms/Controls/PKM Editor/StatEditor.cs +++ b/PKHeX.WinForms/Controls/PKM Editor/StatEditor.cs @@ -285,6 +285,7 @@ public void UpdateRandomIVs(object sender, EventArgs e) LoadIVs(pkm.SetRandomIVs()); } + public void UpdateCharacteristic() => UpdateCharacteristic(pkm.Characteristic); private void UpdateCharacteristic(int characteristic) { L_Characteristic.Visible = Label_CharacteristicPrefix.Visible = characteristic > -1;