PKHeX/PKHeX.WinForms/Subforms/Save Editors/SAV_Wondercard.cs

647 lines
22 KiB
C#
Raw Normal View History

using PKHeX.Core;
2018-07-29 23:39:15 +00:00
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
2014-06-28 21:22:05 +00:00
using System.Windows.Forms;
using PKHeX.Drawing.Misc;
using PKHeX.WinForms.Controls;
using static PKHeX.Core.MessageStrings;
2014-06-28 21:22:05 +00:00
namespace PKHeX.WinForms
2014-06-28 21:22:05 +00:00
{
public partial class SAV_Wondercard : Form
{
private readonly SaveFile Origin;
private readonly SaveFile SAV;
private readonly SummaryPreviewer Summary = new();
2018-07-29 23:39:15 +00:00
public SAV_Wondercard(SaveFile sav, DataMysteryGift? g = null)
2014-06-28 21:22:05 +00:00
{
InitializeComponent();
WinFormsUtil.TranslateInterface(this, Main.CurrentLanguage);
2018-04-03 03:36:13 +00:00
SAV = (Origin = sav).Clone();
mga = SAV.GiftAlbum;
2018-05-12 15:13:39 +00:00
2020-12-24 01:14:38 +00:00
pba = SAV.Generation switch
{
2020-12-24 01:14:38 +00:00
4 => PopulateViewGiftsG4().ToArray(),
5 or 6 or 7 => PopulateViewGiftsG567().ToArray(),
_ => throw new ArgumentOutOfRangeException(nameof(SAV.Generation), "Game not supported."),
2020-12-24 01:14:38 +00:00
};
foreach (var pb in pba)
{
pb.AllowDrop = true;
pb.DragDrop += BoxSlot_DragDrop;
pb.DragEnter += BoxSlot_DragEnter;
pb.MouseDown += BoxSlot_MouseDown;
pb.ContextMenuStrip = mnuVSD;
pb.MouseHover += (_, _) => Summary.Show(pb, mga.Gifts[pba.IndexOf(pb)]);
}
SetGiftBoxes();
GetReceivedFlags();
2018-05-12 15:13:39 +00:00
2014-06-28 21:22:05 +00:00
if (LB_Received.Items.Count > 0)
2014-12-10 23:24:34 +00:00
LB_Received.SelectedIndex = 0;
if (mga.Gifts[0] is WR7) // giftused is not a valid prop
B_UnusedAll.Visible = B_UsedAll.Visible = L_QR.Visible = false;
DragEnter += Main_DragEnter;
DragDrop += Main_DragDrop;
2015-04-01 00:58:23 +00:00
if (g == null)
ClickView(pba[0], EventArgs.Empty);
else
ViewGiftData(g);
2014-06-28 21:22:05 +00:00
}
2018-07-29 23:39:15 +00:00
private readonly MysteryGiftAlbum mga;
private DataMysteryGift? mg;
2020-12-24 01:14:38 +00:00
private readonly IList<PictureBox> pba;
2014-06-28 21:22:05 +00:00
// Repopulation Functions
private void SetBackground(int index, Image bg)
{
for (int i = 0; i < mga.Gifts.Length; i++)
pba[i].BackgroundImage = index == i ? bg : null;
}
2018-07-29 23:39:15 +00:00
private void SetGiftBoxes()
2014-06-28 21:22:05 +00:00
{
for (int i = 0; i < mga.Gifts.Length; i++)
2014-06-28 21:22:05 +00:00
{
MysteryGift m = mga.Gifts[i];
pba[i].Image = m.Sprite();
2014-06-28 21:22:05 +00:00
}
}
2018-07-29 23:39:15 +00:00
PKHeX.Core Nullable cleanup (#2401) * 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
2019-10-17 01:47:31 +00:00
private void ViewGiftData(DataMysteryGift g)
2014-06-28 21:22:05 +00:00
{
2014-12-11 06:50:40 +00:00
try
2014-06-28 21:22:05 +00:00
{
// only check if the form is visible (not opening)
if (Visible && g.GiftUsed && DialogResult.Yes == WinFormsUtil.Prompt(MessageBoxButtons.YesNo, MsgMsyteryGiftUsedAlert, MsgMysteryGiftUsedFix))
g.GiftUsed = false;
2018-04-21 16:55:27 +00:00
RTB.Lines = g.GetDescription().ToArray();
PB_Preview.Image = g.Sprite();
mg = g;
2014-06-28 21:22:05 +00:00
}
// Some user input mystery gifts can have out-of-bounds values. Just swallow any exception.
catch (Exception e)
{
WinFormsUtil.Error(MsgMysteryGiftParseTypeUnknown, e);
RTB.Clear();
2014-06-28 21:22:05 +00:00
}
}
2018-07-29 23:39:15 +00:00
private void GetReceivedFlags()
2014-06-28 21:22:05 +00:00
{
LB_Received.Items.Clear();
for (int i = 1; i < mga.Flags.Length; i++)
2018-07-29 23:39:15 +00:00
{
if (mga.Flags[i])
2014-06-28 21:22:05 +00:00
LB_Received.Items.Add(i.ToString("0000"));
2018-07-29 23:39:15 +00:00
}
if (LB_Received.Items.Count > 0)
LB_Received.SelectedIndex = 0;
2014-06-28 21:22:05 +00:00
}
2018-07-29 23:39:15 +00:00
private void SetCardID(int cardID)
{
2020-12-25 20:30:26 +00:00
if (cardID is <= 0 or >= 0x100 * 8)
return;
2014-06-28 21:22:05 +00:00
string card = cardID.ToString("0000");
if (!LB_Received.Items.Contains(card))
LB_Received.Items.Add(card);
LB_Received.SelectedIndex = LB_Received.Items.IndexOf(card);
}
// Mystery Gift IO (.file<->window)
2014-06-28 21:22:05 +00:00
private void B_Import_Click(object sender, EventArgs e)
{
using var import = new OpenFileDialog {Filter = WinFormsUtil.GetMysterGiftFilter(SAV.Generation, SAV.Version) };
if (import.ShowDialog() != DialogResult.OK) return;
string path = import.FileName;
PKHeX.Core Nullable cleanup (#2401) * 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
2019-10-17 01:47:31 +00:00
var g = MysteryGift.GetMysteryGift(File.ReadAllBytes(path), Path.GetExtension(path));
if (g == null)
2014-06-28 21:22:05 +00:00
{
WinFormsUtil.Error(MsgMysteryGiftInvalid, path);
return;
2014-06-28 21:22:05 +00:00
}
ViewGiftData(g);
2014-06-28 21:22:05 +00:00
}
2018-07-29 23:39:15 +00:00
2014-06-28 21:22:05 +00:00
private void B_Output_Click(object sender, EventArgs e)
{
if (mg == null)
return;
WinFormsUtil.ExportMGDialog(mg, SAV.Version);
2014-06-28 21:22:05 +00:00
}
private static int GetLastUnfilledByType(MysteryGift Gift, MysteryGiftAlbum Album)
{
for (int i = 0; i < Album.Gifts.Length; i++)
{
if (!Album.Gifts[i].Empty)
continue;
if (Album.Gifts[i].Type != Gift.Type)
continue;
return i;
}
return -1;
}
// Mystery Gift RW (window<->sav)
private void ClickView(object sender, EventArgs e)
2014-06-28 21:22:05 +00:00
{
var pb = WinFormsUtil.GetUnderlyingControl<PictureBox>(sender);
2020-12-24 01:14:38 +00:00
if (pb == null)
return;
int index = pba.IndexOf(pb);
SetBackground(index, Drawing.PokeSprite.Properties.Resources.slotView);
ViewGiftData(mga.Gifts[index]);
2014-06-28 21:22:05 +00:00
}
2018-07-29 23:39:15 +00:00
private void ClickSet(object sender, EventArgs e)
2014-06-28 21:22:05 +00:00
{
2022-02-15 05:32:52 +00:00
if (mg is not { } gift)
return;
2022-02-15 05:32:52 +00:00
if (!gift.IsCardCompatible(SAV, out var msg))
2018-04-21 16:55:27 +00:00
{
WinFormsUtil.Alert(MsgMysteryGiftSlotFail, msg);
return;
2018-04-21 16:55:27 +00:00
}
var pb = WinFormsUtil.GetUnderlyingControl<PictureBox>(sender);
2020-12-24 01:14:38 +00:00
if (pb == null)
return;
int index = pba.IndexOf(pb);
// Hijack to the latest unfilled slot if index creates interstitial empty slots.
2022-02-15 05:32:52 +00:00
int lastUnfilled = GetLastUnfilledByType(gift, mga);
if (lastUnfilled > -1 && lastUnfilled < index)
index = lastUnfilled;
2022-02-15 05:32:52 +00:00
if (gift is PCD pcd && mga.Gifts[index] is PGT)
2018-07-29 23:39:15 +00:00
{
2022-02-15 05:32:52 +00:00
gift = pcd.Gift;
2018-07-29 23:39:15 +00:00
}
2022-02-15 05:32:52 +00:00
else if (gift.Type != mga.Gifts[index].Type)
{
2022-02-15 05:32:52 +00:00
WinFormsUtil.Alert(MsgMysteryGiftSlotFail, $"{gift.Type} != {mga.Gifts[index].Type}");
return;
}
SetBackground(index, Drawing.PokeSprite.Properties.Resources.slotSet);
2022-02-15 05:32:52 +00:00
mga.Gifts[index] = (DataMysteryGift)gift.Clone();
SetGiftBoxes();
2022-02-15 05:32:52 +00:00
SetCardID(gift.CardID);
2014-06-28 21:22:05 +00:00
}
2018-07-29 23:39:15 +00:00
private void ClickDelete(object sender, EventArgs e)
2014-06-28 21:22:05 +00:00
{
var pb = WinFormsUtil.GetUnderlyingControl<PictureBox>(sender);
2020-12-24 01:14:38 +00:00
if (pb == null)
return;
int index = pba.IndexOf(pb);
PKHeX.Core Nullable cleanup (#2401) * 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
2019-10-17 01:47:31 +00:00
var arr = mga.Gifts[index].Data;
Array.Clear(arr, 0, arr.Length);
// Shuffle blank card down
int i = index;
while (i < mga.Gifts.Length - 1)
{
if (mga.Gifts[i+1].Empty)
break;
if (mga.Gifts[i+1].Type != mga.Gifts[i].Type)
break;
i++;
var mg1 = mga.Gifts[i];
var mg2 = mga.Gifts[i-1];
mga.Gifts[i-1] = mg1;
mga.Gifts[i] = mg2;
}
SetBackground(i, Drawing.PokeSprite.Properties.Resources.slotDel);
SetGiftBoxes();
2014-06-28 21:22:05 +00:00
}
// Close Window
private void B_Cancel_Click(object sender, EventArgs e)
{
Close();
}
2018-07-29 23:39:15 +00:00
2014-06-28 21:22:05 +00:00
private void B_Save_Click(object sender, EventArgs e)
{
// Make sure all of the Received Flags are flipped!
bool[] flags = new bool[mga.Flags.Length];
foreach (var o in LB_Received.Items)
{
var value = o?.ToString();
if (value == null)
continue;
var flag = Util.ToUInt32(value);
flags[flag] = true;
}
2014-06-28 21:22:05 +00:00
PKHeX.Core Nullable cleanup (#2401) * 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
2019-10-17 01:47:31 +00:00
flags.CopyTo(mga.Flags, 0);
SAV.GiftAlbum = mga;
Origin.CopyChangesFrom(SAV);
2014-06-28 21:22:05 +00:00
Close();
}
// Delete Received Flag
private void ClearReceivedFlag(object sender, EventArgs e)
2014-06-28 21:22:05 +00:00
{
if (LB_Received.SelectedIndex < 0)
return;
if (LB_Received.SelectedIndices.Count > 1)
{
for (int i = LB_Received.SelectedIndices.Count - 1; i >= 0; i--)
LB_Received.Items.RemoveAt(LB_Received.SelectedIndices[i]);
}
else if (LB_Received.SelectedIndices.Count == 1)
{
int lastIndex = LB_Received.SelectedIndex;
LB_Received.Items.RemoveAt(lastIndex);
if (LB_Received.Items.Count == 0)
return;
if (lastIndex == LB_Received.Items.Count)
lastIndex--;
LB_Received.SelectedIndex = lastIndex;
}
2014-06-28 21:22:05 +00:00
}
2014-12-10 23:24:34 +00:00
// Drag & Drop Wonder Cards
2021-12-05 06:29:51 +00:00
private static void Main_DragEnter(object? sender, DragEventArgs? e)
2014-12-10 23:24:34 +00:00
{
2021-12-05 06:29:51 +00:00
if (e?.Data is null)
return;
if (e.Data.GetDataPresent(DataFormats.FileDrop))
e.Effect = DragDropEffects.Copy;
2014-12-10 23:24:34 +00:00
}
2018-07-29 23:39:15 +00:00
2021-12-05 06:29:51 +00:00
private void Main_DragDrop(object? sender, DragEventArgs? e)
2014-12-10 23:24:34 +00:00
{
2021-12-05 06:29:51 +00:00
if (e?.Data?.GetData(DataFormats.FileDrop) is not string[] { Length: not 0 } files)
return;
2021-12-05 06:29:51 +00:00
var first = files[0];
2015-01-31 23:59:23 +00:00
// Check for multiple wondercards
2021-12-05 06:29:51 +00:00
if (Directory.Exists(first))
files = Directory.GetFiles(first, "*", SearchOption.AllDirectories);
if (files.Length == 1 && !Directory.Exists(files[0]))
2014-12-10 23:24:34 +00:00
{
2015-01-31 23:59:23 +00:00
string path = files[0]; // open first D&D
2018-07-29 23:39:15 +00:00
if (!MysteryGift.IsMysteryGift(new FileInfo(path).Length)) // arbitrary
2015-01-31 23:59:23 +00:00
{
WinFormsUtil.Alert(MsgMysteryGiftInvalid, path);
2015-01-31 23:59:23 +00:00
return;
}
2018-07-29 23:39:15 +00:00
var gift = MysteryGift.GetMysteryGift(File.ReadAllBytes(path), Path.GetExtension(path));
if (gift == null)
{
WinFormsUtil.Error(MsgMysteryGiftInvalid, path);
return;
}
2018-07-29 23:39:15 +00:00
ViewGiftData(gift);
return;
}
SetGiftBoxes();
2014-12-10 23:24:34 +00:00
}
2018-05-12 15:13:39 +00:00
private void ClickQR(object sender, EventArgs e)
{
if (ModifierKeys == Keys.Alt)
{
string url = Clipboard.GetText();
if (!string.IsNullOrWhiteSpace(url))
{
ImportQRToView(url);
return;
}
}
ExportQRFromView();
}
2018-07-29 23:39:15 +00:00
private void ExportQRFromView()
{
if (mg == null)
return;
PKHeX.Core Nullable cleanup (#2401) * 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
2019-10-17 01:47:31 +00:00
if (mg.Empty)
{
WinFormsUtil.Alert(MsgMysteryGiftSlotNone);
return;
}
if (SAV.Generation == 6 && mg.ItemID == 726 && mg.IsItem)
{
WinFormsUtil.Alert(MsgMysteryGiftQREonTicket, MsgMysteryGiftQREonTicketAdvice);
return;
}
Image qr = QREncode.GenerateQRCode(mg);
2018-04-21 16:55:27 +00:00
string desc = $"({mg.Type}) {string.Join(Environment.NewLine, mg.GetDescription())}";
using var form = new QR(qr, PB_Preview.Image, desc + Environment.NewLine + "PKHeX Wonder Card @ ProjectPokemon.org");
form.ShowDialog();
}
2018-07-29 23:39:15 +00:00
private void ImportQRToView(string url)
{
var msg = QRDecode.GetQRData(url, out var data);
if (msg != 0)
{
WinFormsUtil.Alert(msg.ConvertMsg());
return;
}
if (data.Length == 0)
return;
string[] types = mga.Gifts.Select(g => g.Type).Distinct().ToArray();
PKHeX.Core Nullable cleanup (#2401) * 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
2019-10-17 01:47:31 +00:00
var gift = MysteryGift.GetMysteryGift(data);
if (gift == null)
return;
string giftType = gift.Type;
if (mga.Gifts.All(card => card.Data.Length != data.Length))
WinFormsUtil.Alert(MsgMysteryGiftQRTypeLength, string.Format(MsgQRDecodeSize, $"0x{data.Length:X}"));
else if (types.All(type => type != giftType))
WinFormsUtil.Alert(MsgMysteryGiftTypeIncompatible, $"{MsgMysteryGiftQRReceived} {gift.Type}{Environment.NewLine}{MsgMysteryGiftTypeUnexpected} {string.Join(", ", types)}");
else if (!SAV.CanReceiveGift(gift))
WinFormsUtil.Alert(MsgMysteryGiftTypeDetails);
else
ViewGiftData(gift);
}
private void BoxSlot_MouseDown(object? sender, MouseEventArgs e)
{
if (sender == null)
return;
switch (ModifierKeys)
{
case Keys.Control: ClickView(sender, e); return;
case Keys.Shift: ClickSet(sender, e); return;
case Keys.Alt: ClickDelete(sender, e); return;
}
2018-07-29 23:39:15 +00:00
var pb = sender as PictureBox;
if (pb?.Image == null)
return;
if (e.Button != MouseButtons.Left || e.Clicks != 1) return;
2020-12-24 01:14:38 +00:00
int index = pba.IndexOf(pb);
2015-12-19 06:17:01 +00:00
wc_slot = index;
// Create Temp File to Drag
Cursor.Current = Cursors.Hand;
// Make File
2018-07-29 23:39:15 +00:00
var gift = mga.Gifts[index];
string newfile = Path.Combine(Path.GetTempPath(), Util.CleanFileName(gift.FileName));
try
{
File.WriteAllBytes(newfile, gift.Write());
DoDragDrop(new DataObject(DataFormats.FileDrop, new[] { newfile }), DragDropEffects.Move);
}
// Sometimes the drag-drop is canceled or ends up at a bad location. Don't bother recovering from an exception; just display a safe error message.
catch (Exception x)
{ WinFormsUtil.Error("Drag & Drop Error", x); }
File.Delete(newfile);
2015-12-19 06:17:01 +00:00
wc_slot = -1;
}
2018-07-29 23:39:15 +00:00
2021-12-05 06:29:51 +00:00
private void BoxSlot_DragDrop(object? sender, DragEventArgs? e)
{
2020-12-24 01:14:38 +00:00
if (mg == null || sender is not PictureBox pb)
return;
2020-12-24 01:14:38 +00:00
int index = pba.IndexOf(pb);
// Hijack to the latest unfilled slot if index creates interstitial empty slots.
int lastUnfilled = GetLastUnfilledByType(mg, mga);
if (lastUnfilled > -1 && lastUnfilled < index && mga.Gifts[lastUnfilled].Type == mga.Gifts[index].Type)
index = lastUnfilled;
2018-05-12 15:13:39 +00:00
if (wc_slot == -1) // dropped
2015-12-19 06:17:01 +00:00
{
2021-12-05 06:29:51 +00:00
if (e?.Data?.GetData(DataFormats.FileDrop) is not string[] {Length: not 0} files)
2015-12-19 06:17:01 +00:00
return;
2018-05-12 15:13:39 +00:00
var first = files[0];
var fi = new FileInfo(first);
if (!MysteryGift.IsMysteryGift(fi.Length))
{ WinFormsUtil.Alert(MsgFileUnsupported, first); return; }
byte[] data = File.ReadAllBytes(first);
var gift = MysteryGift.GetMysteryGift(data, fi.Extension);
if (gift == null)
{ WinFormsUtil.Alert(MsgFileUnsupported, first); return; }
2018-05-13 02:07:32 +00:00
if (gift is PCD pcd && mga.Gifts[index] is PGT)
2018-07-29 23:39:15 +00:00
{
2018-05-13 02:07:32 +00:00
gift = pcd.Gift;
2018-07-29 23:39:15 +00:00
}
else if (gift.Type != mga.Gifts[index].Type)
{
WinFormsUtil.Alert(MsgMysteryGiftSlotFail, $"{gift.Type} != {mga.Gifts[index].Type}");
return;
}
SetBackground(index, Drawing.PokeSprite.Properties.Resources.slotSet);
PKHeX.Core Nullable cleanup (#2401) * 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
2019-10-17 01:47:31 +00:00
mga.Gifts[index] = (DataMysteryGift)gift.Clone();
2018-05-12 15:13:39 +00:00
SetCardID(mga.Gifts[index].CardID);
ViewGiftData(mga.Gifts[index]);
2015-12-19 06:17:01 +00:00
}
else // Swap Data
{
PKHeX.Core Nullable cleanup (#2401) * 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
2019-10-17 01:47:31 +00:00
DataMysteryGift s1 = mga.Gifts[index];
DataMysteryGift s2 = mga.Gifts[wc_slot];
if (s2 is PCD && s1 is PGT)
{
// set the PGT to the PGT slot instead
ViewGiftData(s2);
ClickSet(pba[index], EventArgs.Empty);
{ WinFormsUtil.Alert(string.Format(MsgMysteryGiftSlotAlternate, s2.Type, s1.Type)); return; }
}
if (s1.Type != s2.Type)
{ WinFormsUtil.Alert(string.Format(MsgMysteryGiftSlotFailSwap, s2.Type, s1.Type)); return; }
mga.Gifts[wc_slot] = s1;
mga.Gifts[index] = s2;
if (mga.Gifts[wc_slot].Empty) // empty slot created, slide down
{
int i = wc_slot;
while (i < index)
{
if (mga.Gifts[i + 1].Empty)
break;
if (mga.Gifts[i + 1].Type != mga.Gifts[i].Type)
break;
i++;
var mg1 = mga.Gifts[i];
var mg2 = mga.Gifts[i - 1];
mga.Gifts[i - 1] = mg1;
mga.Gifts[i] = mg2;
}
index = i-1;
}
2015-12-19 06:17:01 +00:00
}
SetBackground(index, Drawing.PokeSprite.Properties.Resources.slotView);
SetGiftBoxes();
}
2018-07-29 23:39:15 +00:00
2021-12-05 06:29:51 +00:00
private static void BoxSlot_DragEnter(object? sender, DragEventArgs e)
{
if (e.AllowedEffect == (DragDropEffects.Copy | DragDropEffects.Link)) // external file
e.Effect = DragDropEffects.Copy;
else if (e.Data != null) // within
e.Effect = DragDropEffects.Move;
}
2018-07-29 23:39:15 +00:00
2015-12-19 06:17:01 +00:00
private int wc_slot = -1;
// UI Generation
private List<PictureBox> PopulateViewGiftsG4()
{
List<PictureBox> pb = new();
// Row 1
var f1 = GetFlowLayoutPanel();
f1.Controls.Add(GetLabel($"{nameof(PGT)} 1-6"));
for (int i = 0; i < 6; i++)
{
var p = GetPictureBox();
f1.Controls.Add(p);
pb.Add(p);
}
// Row 2
var f2 = GetFlowLayoutPanel();
f2.Controls.Add(GetLabel($"{nameof(PGT)} 7-8"));
for (int i = 6; i < 8; i++)
{
var p = GetPictureBox();
f2.Controls.Add(p);
pb.Add(p);
}
// Row 3
var f3 = GetFlowLayoutPanel();
f3.Margin = new Padding(0, f3.Height, 0, 0);
f3.Controls.Add(GetLabel($"{nameof(PCD)} 1-3"));
for (int i = 8; i < 11; i++)
{
var p = GetPictureBox();
f3.Controls.Add(p);
pb.Add(p);
}
FLP_Gifts.Controls.Add(f1);
FLP_Gifts.Controls.Add(f2);
FLP_Gifts.Controls.Add(f3);
return pb;
}
2018-07-29 23:39:15 +00:00
private List<PictureBox> PopulateViewGiftsG567()
{
2018-07-29 23:39:15 +00:00
var pb = new List<PictureBox>();
const int cellsPerRow = 6;
int rows = (int)Math.Ceiling(mga.Gifts.Length / (decimal)cellsPerRow);
int countRemaining = mga.Gifts.Length;
for (int i = 0; i < rows; i++)
{
var row = GetFlowLayoutPanel();
int count = cellsPerRow >= countRemaining ? countRemaining : cellsPerRow;
countRemaining -= count;
int start = (i * cellsPerRow) + 1;
row.Controls.Add(GetLabel($"{start}-{start + count - 1}"));
for (int j = 0; j < count; j++)
{
var p = GetPictureBox();
row.Controls.Add(p);
pb.Add(p);
}
FLP_Gifts.Controls.Add(row);
}
return pb;
}
2018-07-29 23:39:15 +00:00
2021-12-05 01:56:56 +00:00
private static FlowLayoutPanel GetFlowLayoutPanel() => new()
{
2021-12-05 01:56:56 +00:00
Width = 480,
Height = 60,
Padding = new Padding(0),
Margin = new Padding(0),
};
2018-07-29 23:39:15 +00:00
2021-12-05 01:56:56 +00:00
private static Label GetLabel(string text) => new()
{
2021-12-05 01:56:56 +00:00
Size = new Size(40, 60),
AutoSize = false,
TextAlign = ContentAlignment.MiddleRight,
Text = text,
Padding = new Padding(0),
Margin = new Padding(0),
};
private static PictureBox GetPictureBox() => new()
{
2021-12-05 01:56:56 +00:00
Size = new Size(70, 58),
SizeMode = PictureBoxSizeMode.CenterImage,
BorderStyle = BorderStyle.FixedSingle,
BackColor = Color.Transparent,
Padding = new Padding(0),
Margin = new Padding(1),
};
private void B_ModifyAll_Click(object sender, EventArgs e)
{
foreach (var g in mga.Gifts)
g.GiftUsed = sender == B_UsedAll;
SetGiftBoxes();
System.Media.SystemSounds.Asterisk.Play();
}
private void LB_Received_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Delete)
{
if (LB_Received.SelectedIndices.Count > 1)
{
for (int i = LB_Received.SelectedIndices.Count - 1; i >= 0; i--)
LB_Received.Items.RemoveAt(LB_Received.SelectedIndices[i]);
}
else if (LB_Received.SelectedIndices.Count == 1)
{
int lastIndex = LB_Received.SelectedIndex;
LB_Received.Items.RemoveAt(lastIndex);
if (LB_Received.Items.Count == 0)
return;
if (lastIndex == LB_Received.Items.Count)
lastIndex--;
LB_Received.SelectedIndex = lastIndex;
}
}
}
}
}