mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-16 17:18:00 +00:00
02420d3e93
* Handle some nullable cases Refactor MysteryGift into a second abstract class (backed by a byte array, or fake data) Make some classes have explicit constructors instead of { } initialization * Handle bits more obviously without null * Make SaveFile.BAK explicitly readonly again * merge constructor methods to have readonly fields * Inline some properties * More nullable handling * Rearrange box actions define straightforward classes to not have any null properties * Make extrabyte reference array immutable * Move tooltip creation to designer * Rearrange some logic to reduce nesting * Cache generated fonts * Split mystery gift album purpose * Handle more tooltips * Disallow null setters * Don't capture RNG object, only type enum * Unify learnset objects Now have readonly properties which are never null don't new() empty learnsets (>800 Learnset objects no longer created, total of 2400 objects since we also new() a move & level array) optimize g1/2 reader for early abort case * Access rewrite Initialize blocks in a separate object, and get via that object removes a couple hundred "might be null" warnings since blocks are now readonly getters some block references have been relocated, but interfaces should expose all that's needed put HoF6 controls in a groupbox, and disable * Readonly personal data * IVs non nullable for mystery gift * Explicitly initialize forced encounter moves * Make shadow objects readonly & non-null Put murkrow fix in binary data resource, instead of on startup * Assign dex form fetch on constructor Fixes legality parsing edge cases also handle cxd parse for valid; exit before exception is thrown in FrameGenerator * Remove unnecessary null checks * Keep empty value until init SetPouch sets the value to an actual one during load, but whatever * Readonly team lock data * Readonly locks Put locked encounters at bottom (favor unlocked) * Mail readonly data / offset Rearrange some call flow and pass defaults Add fake classes for SaveDataEditor mocking Always party size, no need to check twice in stat editor use a fake save file as initial data for savedata editor, and for gamedata (wow i found a usage) constrain eventwork editor to struct variable types (uint, int, etc), thus preventing null assignment errors
393 lines
14 KiB
C#
393 lines
14 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Drawing;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Forms;
|
|
using PKHeX.Core;
|
|
using PKHeX.Drawing;
|
|
using PKHeX.WinForms.Properties;
|
|
using PKHeX.WinForms.Controls;
|
|
using static PKHeX.Core.MessageStrings;
|
|
|
|
namespace PKHeX.WinForms
|
|
{
|
|
public partial class SAV_MysteryGiftDB : Form
|
|
{
|
|
private readonly PKMEditor PKME_Tabs;
|
|
private readonly SaveFile SAV;
|
|
private readonly SAVEditor BoxView;
|
|
|
|
public SAV_MysteryGiftDB(PKMEditor tabs, SAVEditor sav)
|
|
{
|
|
InitializeComponent();
|
|
|
|
ToolStripMenuItem mnuView = new ToolStripMenuItem { Name = "mnuView", Text = "View", Image = Resources.other };
|
|
ToolStripMenuItem mnuSaveMG = new ToolStripMenuItem { Name = "mnuSaveMG", Text = "Save Gift", Image = Resources.gift };
|
|
ToolStripMenuItem mnuSavePK = new ToolStripMenuItem { Name = "mnuSavePK", Text = "Save PKM", Image = Resources.savePKM };
|
|
|
|
WinFormsUtil.TranslateInterface(this, Main.CurrentLanguage);
|
|
ContextMenuStrip mnu = new ContextMenuStrip();
|
|
mnu.Items.AddRange(new ToolStripItem[] { mnuView, mnuSaveMG, mnuSavePK });
|
|
|
|
SAV = sav.SAV;
|
|
BoxView = sav;
|
|
PKME_Tabs = tabs;
|
|
|
|
// Preset Filters to only show PKM available for loaded save
|
|
CB_FormatComparator.SelectedIndex = 3; // <=
|
|
|
|
pokeGrid1.InitializeGrid(6, 11);
|
|
pokeGrid1.SetBackground(Resources.box_wp_clean);
|
|
PKXBOXES = pokeGrid1.Entries.ToArray();
|
|
|
|
// Enable Scrolling when hovered over
|
|
foreach (var slot in PKXBOXES)
|
|
{
|
|
// Enable Click
|
|
slot.MouseClick += (sender, e) =>
|
|
{
|
|
if (ModifierKeys == Keys.Control)
|
|
ClickView(sender, e);
|
|
};
|
|
}
|
|
|
|
Counter = L_Count.Text;
|
|
Viewed = L_Viewed.Text;
|
|
L_Viewed.Text = string.Empty; // invis for now
|
|
var hover = new ToolTip();
|
|
L_Viewed.MouseEnter += (sender, e) => hover.SetToolTip(L_Viewed, L_Viewed.Text);
|
|
|
|
// Assign event handlers
|
|
mnuView.Click += ClickView;
|
|
mnuSaveMG.Click += ClickSaveMG;
|
|
mnuSavePK.Click += ClickSavePK;
|
|
|
|
// Assign to datagridview
|
|
foreach (PictureBox p in PKXBOXES)
|
|
p.ContextMenuStrip = mnu;
|
|
|
|
// Load Data
|
|
B_Search.Enabled = false;
|
|
L_Count.Text = "Loading...";
|
|
new Task(LoadDatabase).Start();
|
|
|
|
Menu_SearchSettings.DropDown.Closing += (sender, e) =>
|
|
{
|
|
if (e.CloseReason == ToolStripDropDownCloseReason.ItemClicked)
|
|
e.Cancel = true;
|
|
};
|
|
CB_Format.Items[0] = MsgAny;
|
|
CenterToParent();
|
|
}
|
|
|
|
private readonly PictureBox[] PKXBOXES;
|
|
private readonly string DatabasePath = Main.MGDatabasePath;
|
|
private List<MysteryGift> Results;
|
|
private List<MysteryGift> RawDB;
|
|
private int slotSelected = -1; // = null;
|
|
private Image slotColor;
|
|
private const int RES_MAX = 66;
|
|
private const int RES_MIN = 6;
|
|
private readonly string Counter;
|
|
private readonly string Viewed;
|
|
private const int MAXFORMAT = PKX.Generation;
|
|
|
|
// Important Events
|
|
private void ClickView(object sender, EventArgs e)
|
|
{
|
|
int index = GetSenderIndex(sender);
|
|
if (index < 0)
|
|
return;
|
|
PKME_Tabs.PopulateFields(Results[index].ConvertToPKM(SAV), false);
|
|
slotSelected = index;
|
|
slotColor = Resources.slotView;
|
|
UpdateSlotColor(SCR_Box.Value);
|
|
L_Viewed.Text = string.Format(Viewed, Results[index].FileName);
|
|
}
|
|
|
|
private void ClickSavePK(object sender, EventArgs e)
|
|
{
|
|
int index = GetSenderIndex(sender);
|
|
if (index < 0)
|
|
return;
|
|
var gift = Results[index];
|
|
var pk = gift.ConvertToPKM(SAV);
|
|
WinFormsUtil.SavePKMDialog(pk);
|
|
}
|
|
|
|
private void ClickSaveMG(object sender, EventArgs e)
|
|
{
|
|
int index = GetSenderIndex(sender);
|
|
if (index < 0)
|
|
return;
|
|
var gift = Results[index];
|
|
if (!(gift is DataMysteryGift g)) // e.g. WC3
|
|
{
|
|
WinFormsUtil.Alert(MsgExportWC3DataFail);
|
|
return;
|
|
}
|
|
WinFormsUtil.ExportMGDialog(g, SAV.Version);
|
|
}
|
|
|
|
private int GetSenderIndex(object sender)
|
|
{
|
|
var pb = WinFormsUtil.GetUnderlyingControl<PictureBox>(sender);
|
|
int index = Array.IndexOf(PKXBOXES, pb);
|
|
if (index >= RES_MAX)
|
|
{
|
|
System.Media.SystemSounds.Exclamation.Play();
|
|
return -1;
|
|
}
|
|
index += SCR_Box.Value*RES_MIN;
|
|
if (index >= Results.Count)
|
|
{
|
|
System.Media.SystemSounds.Exclamation.Play();
|
|
return -1;
|
|
}
|
|
return index;
|
|
}
|
|
|
|
private void PopulateComboBoxes()
|
|
{
|
|
// Set the Text
|
|
CB_HeldItem.InitializeBinding();
|
|
CB_Species.InitializeBinding();
|
|
|
|
var Any = new ComboItem(MsgAny, -1);
|
|
|
|
var DS_Species = new List<ComboItem>(GameInfo.SpeciesDataSource);
|
|
DS_Species.RemoveAt(0);
|
|
var filteredSpecies = DS_Species.Where(spec => RawDB.Any(mg => mg.Species == spec.Value)).ToList();
|
|
filteredSpecies.Insert(0, Any);
|
|
CB_Species.DataSource = filteredSpecies;
|
|
|
|
var DS_Item = new List<ComboItem>(GameInfo.ItemDataSource);
|
|
DS_Item.Insert(0, Any); CB_HeldItem.DataSource = DS_Item;
|
|
|
|
// Set the Move ComboBoxes too..
|
|
var DS_Move = new List<ComboItem>(GameInfo.MoveDataSource);
|
|
DS_Move.RemoveAt(0); DS_Move.Insert(0, Any);
|
|
{
|
|
foreach (ComboBox cb in new[] { CB_Move1, CB_Move2, CB_Move3, CB_Move4 })
|
|
{
|
|
cb.InitializeBinding();
|
|
cb.DataSource = new BindingSource(DS_Move, null);
|
|
}
|
|
}
|
|
|
|
// Trigger a Reset
|
|
ResetFilters(null, EventArgs.Empty);
|
|
B_Search.Enabled = true;
|
|
}
|
|
|
|
private void ResetFilters(object sender, EventArgs e)
|
|
{
|
|
CHK_Shiny.Checked = CHK_IsEgg.Checked = true;
|
|
CHK_Shiny.CheckState = CHK_IsEgg.CheckState = CheckState.Indeterminate;
|
|
CB_HeldItem.SelectedIndex = 0;
|
|
CB_Species.SelectedIndex = 0;
|
|
|
|
CB_Move1.SelectedIndex = CB_Move2.SelectedIndex = CB_Move3.SelectedIndex = CB_Move4.SelectedIndex = 0;
|
|
RTB_Instructions.Clear();
|
|
|
|
if (sender != null)
|
|
System.Media.SystemSounds.Asterisk.Play();
|
|
}
|
|
|
|
private void LoadDatabase()
|
|
{
|
|
RawDB = new List<MysteryGift>(EncounterEvent.GetAllEvents());
|
|
foreach (var mg in RawDB)
|
|
mg.GiftUsed = false;
|
|
BeginInvoke(new MethodInvoker(delegate
|
|
{
|
|
SetResults(RawDB);
|
|
PopulateComboBoxes();
|
|
}));
|
|
}
|
|
|
|
// IO Usage
|
|
private void OpenDB(object sender, EventArgs e)
|
|
{
|
|
if (Directory.Exists(DatabasePath))
|
|
Process.Start("explorer.exe", DatabasePath);
|
|
}
|
|
|
|
private void Menu_Export_Click(object sender, EventArgs e)
|
|
{
|
|
if (Results == null || Results.Count == 0)
|
|
{ WinFormsUtil.Alert(MsgDBCreateReportFail); return; }
|
|
|
|
if (DialogResult.Yes != WinFormsUtil.Prompt(MessageBoxButtons.YesNo, MsgDBExportResultsPrompt))
|
|
return;
|
|
|
|
using var fbd = new FolderBrowserDialog();
|
|
if (DialogResult.OK != fbd.ShowDialog())
|
|
return;
|
|
|
|
string path = fbd.SelectedPath;
|
|
Directory.CreateDirectory(path);
|
|
|
|
foreach (var gift in Results.OfType<DataMysteryGift>()) // WC3 have no data
|
|
File.WriteAllBytes(Path.Combine(path, Util.CleanFileName(gift.FileName)), gift.Data);
|
|
}
|
|
|
|
// View Updates
|
|
private void B_Search_Click(object sender, EventArgs e)
|
|
{
|
|
// Populate Search Query Result
|
|
IEnumerable<MysteryGift> res = RawDB;
|
|
|
|
int format = MAXFORMAT + 1 - CB_Format.SelectedIndex;
|
|
|
|
switch (CB_FormatComparator.SelectedIndex)
|
|
{
|
|
case 0: /* Do nothing */ break;
|
|
case 1: res = res.Where(mg => mg.Format >= format); break;
|
|
case 2: res = res.Where(mg => mg.Format == format); break;
|
|
case 3: res = res.Where(mg => mg.Format <= format); break;
|
|
}
|
|
|
|
// Primary Searchables
|
|
int species = WinFormsUtil.GetIndex(CB_Species);
|
|
int item = WinFormsUtil.GetIndex(CB_HeldItem);
|
|
if (species != -1) res = res.Where(pk => pk.Species == species);
|
|
if (item != -1) res = res.Where(pk => pk.HeldItem == item);
|
|
|
|
// Secondary Searchables
|
|
int move1 = WinFormsUtil.GetIndex(CB_Move1);
|
|
int move2 = WinFormsUtil.GetIndex(CB_Move2);
|
|
int move3 = WinFormsUtil.GetIndex(CB_Move3);
|
|
int move4 = WinFormsUtil.GetIndex(CB_Move4);
|
|
if (move1 != -1) res = res.Where(pk => pk.Moves.Contains(move1));
|
|
if (move2 != -1) res = res.Where(pk => pk.Moves.Contains(move2));
|
|
if (move3 != -1) res = res.Where(pk => pk.Moves.Contains(move3));
|
|
if (move4 != -1) res = res.Where(pk => pk.Moves.Contains(move4));
|
|
if (CHK_Shiny.CheckState == CheckState.Checked) res = res.Where(pk => pk.IsShiny);
|
|
if (CHK_Shiny.CheckState == CheckState.Unchecked) res = res.Where(pk => !pk.IsShiny);
|
|
if (CHK_IsEgg.CheckState == CheckState.Checked) res = res.Where(pk => pk.IsEgg);
|
|
if (CHK_IsEgg.CheckState == CheckState.Unchecked) res = res.Where(pk => !pk.IsEgg);
|
|
|
|
slotSelected = -1; // reset the slot last viewed
|
|
|
|
if (RTB_Instructions.Lines.Any(line => line.Length > 0))
|
|
{
|
|
var filters = StringInstruction.GetFilters(RTB_Instructions.Lines).ToArray();
|
|
BatchEditing.ScreenStrings(filters);
|
|
res = res.Where(pkm => BatchEditing.IsFilterMatch(filters, pkm)); // Compare across all filters
|
|
}
|
|
|
|
var results = res.ToArray();
|
|
if (results.Length == 0)
|
|
WinFormsUtil.Alert(MsgDBSearchNone);
|
|
|
|
SetResults(new List<MysteryGift>(results)); // updates Count Label as well.
|
|
System.Media.SystemSounds.Asterisk.Play();
|
|
}
|
|
|
|
private void UpdateScroll(object sender, ScrollEventArgs e)
|
|
{
|
|
if (e.OldValue != e.NewValue)
|
|
FillPKXBoxes(e.NewValue);
|
|
}
|
|
|
|
private void SetResults(List<MysteryGift> res)
|
|
{
|
|
Results = new List<MysteryGift>(res);
|
|
|
|
SCR_Box.Maximum = (int)Math.Ceiling((decimal)Results.Count / RES_MIN);
|
|
if (SCR_Box.Maximum > 0) SCR_Box.Maximum--;
|
|
|
|
SCR_Box.Value = 0;
|
|
FillPKXBoxes(0);
|
|
|
|
L_Count.Text = string.Format(Counter, Results.Count);
|
|
}
|
|
|
|
private void FillPKXBoxes(int start)
|
|
{
|
|
if (Results == null)
|
|
{
|
|
for (int i = 0; i < RES_MAX; i++)
|
|
PKXBOXES[i].Image = null;
|
|
return;
|
|
}
|
|
int begin = start * RES_MIN;
|
|
int end = Math.Min(RES_MAX, Results.Count - (start * RES_MIN));
|
|
for (int i = 0; i < end; i++)
|
|
PKXBOXES[i].Image = Results[i + begin].Sprite();
|
|
for (int i = end; i < RES_MAX; i++)
|
|
PKXBOXES[i].Image = null;
|
|
UpdateSlotColor(start);
|
|
}
|
|
|
|
private void UpdateSlotColor(int start)
|
|
{
|
|
for (int i = 0; i < RES_MAX; i++)
|
|
PKXBOXES[i].BackgroundImage = Resources.slotTrans;
|
|
if (slotSelected != -1 && slotSelected >= RES_MIN * start && slotSelected < (RES_MIN * start) + RES_MAX)
|
|
PKXBOXES[slotSelected - (start * RES_MIN)].BackgroundImage = slotColor ?? Resources.slotView;
|
|
}
|
|
|
|
private void Menu_SearchAdvanced_Click(object sender, EventArgs e)
|
|
{
|
|
if (!Menu_SearchAdvanced.Checked)
|
|
{
|
|
Size = MinimumSize;
|
|
RTB_Instructions.Clear();
|
|
}
|
|
else
|
|
{
|
|
Size = MaximumSize;
|
|
}
|
|
}
|
|
|
|
private void Menu_Import_Click(object sender, EventArgs e)
|
|
{
|
|
if (!BoxView.GetBulkImportSettings(out var clearAll, out var overwrite, out var noSetb))
|
|
return;
|
|
|
|
int box = BoxView.Box.CurrentBox;
|
|
int ctr = SAV.LoadBoxes(Results, out var result, box, clearAll, overwrite, noSetb);
|
|
if (ctr <= 0)
|
|
return;
|
|
|
|
BoxView.SetPKMBoxes();
|
|
BoxView.UpdateBoxViewers();
|
|
WinFormsUtil.Alert(result);
|
|
}
|
|
|
|
private void Menu_Exit_Click(object sender, EventArgs e)
|
|
{
|
|
Close();
|
|
}
|
|
|
|
protected override void OnMouseWheel(MouseEventArgs e)
|
|
{
|
|
if (!pokeGrid1.RectangleToScreen(pokeGrid1.ClientRectangle).Contains(MousePosition))
|
|
return;
|
|
int oldval = SCR_Box.Value;
|
|
int newval = oldval + (e.Delta < 0 ? 1 : -1);
|
|
if (newval >= SCR_Box.Minimum && SCR_Box.Maximum >= newval)
|
|
FillPKXBoxes(SCR_Box.Value = newval);
|
|
}
|
|
|
|
private void ChangeFormatFilter(object sender, EventArgs e)
|
|
{
|
|
if (CB_FormatComparator.SelectedIndex == 0)
|
|
{
|
|
CB_Format.Visible = false; // !any
|
|
CB_Format.SelectedIndex = 0;
|
|
}
|
|
else
|
|
{
|
|
CB_Format.Visible = true;
|
|
int index = MAXFORMAT - SAV.Generation + 1;
|
|
CB_Format.SelectedIndex = index < CB_Format.Items.Count ? index : 0; // SAV generation (offset by 1 for "Any")
|
|
}
|
|
}
|
|
}
|
|
}
|