DS-Pokemon-Rom-Editor/DS_Map/Editors/TradeEditor.cs
2025-07-28 14:23:32 +02:00

547 lines
22 KiB
C#

using DSPRE.ROMFiles;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace DSPRE.Editors
{
public partial class TradeEditor : Form
{
private TradeData curTradeData;
private TextArchive tradeArchive;
// Track if any changes have been made
private bool tradeDirty = false;
private bool textDirty = false;
#region Enums
public enum OriginLang
{
NONE = 0,
JAPANESE = 1,
ENGLISH = 2,
FRENCH = 3,
ITALIAN = 4,
GERMAN = 5,
UNUSED = 6,
SPANISH = 7,
KOREAN = 8
}
#endregion
public TradeEditor()
{
Helpers.DisableHandlers();
AppLogger.Debug("TradeEditor: Initializing Trade Editor.");
InitializeComponent();
InitLimits();
InitDataRanges();
SetToolTips();
RegisterMarkDirtyHandlers();
if (TradeData.GetTradeCount() == 0)
{
MessageBox.Show("No trades could be found! The narc may have failed to unpack.", "Loading Trades Failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
AppLogger.Error("TradeEditor: Loading trades failed.");
return;
}
if (RomInfo.gameFamily != RomInfo.GameFamilies.HGSS)
{
unknownNumericUpDown.Enabled = false;
}
// PLAT: Overlay 6, 0x8050-0x8053: dc f5 f0 fb -> 00 00 00 00 to dummy out ASSERT
// Disable buttons until trade expansion is implemented
addFileButton.Enabled = false;
removeLastButton.Enabled = false;
tradeArchive = new TextArchive(GetTextBankIndex());
LoadFromFile(0);
}
private void InitLimits()
{
// IVs: 0-31
hpIVNumericUpDown.Minimum = 0;
hpIVNumericUpDown.Maximum = 31;
atkIVNumericUpDown.Minimum = 0;
atkIVNumericUpDown.Maximum = 31;
defIVNumericUpDown.Minimum = 0;
defIVNumericUpDown.Maximum = 31;
speIVNumericUpDown.Minimum = 0;
speIVNumericUpDown.Maximum = 31;
spaIVNumericUpDown.Minimum = 0;
spaIVNumericUpDown.Maximum = 31;
spdIVNumericUpDown.Minimum = 0;
spdIVNumericUpDown.Maximum = 31;
// Contest stats: 0-255
coolNumericUpDown.Minimum = 0;
coolNumericUpDown.Maximum = 255;
beautyNumericUpDown.Minimum = 0;
beautyNumericUpDown.Maximum = 255;
cuteNumericUpDown.Minimum = 0;
cuteNumericUpDown.Maximum = 255;
smartNumericUpDown.Minimum = 0;
smartNumericUpDown.Maximum = 255;
toughNumericUpDown.Minimum = 0;
toughNumericUpDown.Maximum = 255;
// PID: (32-bit unsigned)
pidNumericUpDown.Minimum = 0;
pidNumericUpDown.Maximum = int.MaxValue;
// Sheen: 0-255
sheenNumericUpDown.Minimum = 0;
sheenNumericUpDown.Maximum = 255;
// Unknown: (32-bit unsigned)
unknownNumericUpDown.Minimum = 0;
unknownNumericUpDown.Maximum = int.MaxValue;
// Trade ID: (dynamic based on loaded data)
tradeIDNumericUpDown.Minimum = 0;
tradeIDNumericUpDown.Maximum = TradeData.GetTradeCount() - 1;
// OT ID: (32-bit unsigned)
otIDNumericUpDown.Minimum = 0;
otIDNumericUpDown.Maximum = int.MaxValue;
// Text fields
otNameTextBox.MaxLength = 7;
nicknameTextBox.MaxLength = 10;
}
private void InitDataRanges()
{
// Species Names
speciesComboBox.DataSource = RomInfo.GetPokemonNames();
speciesComboBox.AutoCompleteSource = AutoCompleteSource.ListItems;
requestedComboBox.DataSource = RomInfo.GetPokemonNames();
requestedComboBox.AutoCompleteSource = AutoCompleteSource.ListItems;
// Abilities
abilityComboBox.DataSource = RomInfo.GetAbilityNames();
abilityComboBox.AutoCompleteSource = AutoCompleteSource.ListItems;
// Held Items
heldItemComboBox.DataSource = RomInfo.GetItemNames();
heldItemComboBox.AutoCompleteSource = AutoCompleteSource.ListItems;
// OT Gender
otGenderComboBox.DataSource = new string[] { "Male", "Female" };
// Languages
langComboBox.DataSource = Enum.GetNames(typeof(OriginLang));
langComboBox.AutoCompleteSource = AutoCompleteSource.ListItems;
}
private void SetToolTips()
{
SetToolTipsForControls(new Control[] { tradeIDNumericUpDown, tradeIDLabel }, "The Trade ID to edit.");
SetToolTipsForControls(new Control[] { speciesComboBox, speciesLabel }, "The species of the Pokémon being traded.");
SetToolTipsForControls(new Control[] { IVGroupBox }, "Individual Values (IVs)");
SetToolTipsForControls(new Control[] { abilityComboBox, abilityLabel }, "The ability of the Pokémon.\nThis value is unused and will have no effect!");
SetToolTipsForControls(new Control[] { otIDNumericUpDown, otIDLabel }, "The Original Trainer ID of the Pokémon.");
SetToolTipsForControls(new Control[] { contestGroupBox }, "Contest stats for the Pokémon.");
SetToolTipsForControls(new Control[] { pidNumericUpDown, pidLabel }, "The PID of the Pokémon.\nDetermines ability and nature.");
SetToolTipsForControls(new Control[] { heldItemComboBox, heldItemLabel }, "The held item of the Pokémon.");
SetToolTipsForControls(new Control[] { otGenderComboBox, otGenderLabel }, "The gender of the Original Trainer (OT).\nThe OT name is determined by the text bank.");
SetToolTipsForControls(new Control[] { langComboBox, langLabel }, "The language of origin of the traded Pokémon.");
SetToolTipsForControls(new Control[] { requestedComboBox, requestedLabel }, "The species of the requested Pokémon.\nWhether the Pokémon selected by the player matches this species or not is checked via scripting.");
SetToolTipsForControls(new Control[] { unknownNumericUpDown, unknownLabel }, "An unknown value, present in HGSS only.\nAppears to be unused.");
SetToolTipsForControls(new Control[] { otNameTextBox, otNameLabel },
$"The Original Trainer's name.\nStored in text bank {GetTextBankIndex()}.\nAt most {otNameTextBox.MaxLength} characters.");
SetToolTipsForControls(new Control[] { nicknameTextBox, nicknameLabel },
$"The nickname of the Pokémon.\nStored in text bank {GetTextBankIndex()}.\nAt most {nicknameTextBox.MaxLength} characters.");
SetToolTipsForControls(new Control[] { saveTradeButton }, "Save the current trade data to file.");
SetToolTipsForControls(new Control[] { saveTextDataButton }, "Save the current text data to file.");
SetToolTipsForControls(new Control[] { saveAllButton }, "Save both trade data and text data to file.");
// ToDo: Remove these tooltips when the features are implemented
SetToolTipsForControls(new Control[] { addFileButton, removeLastButton }, "Coming soon!\nThis feature is not yet implemented.\nIt will allow you to add or remove trades from the trade data file.");
}
private void SetToolTipsForControls(IEnumerable<Control> controls, string text)
{
foreach (var control in controls)
{
toolTip.SetToolTip(control, text);
}
}
public void LoadFromFile(int tradeID)
{
Helpers.DisableHandlers();
curTradeData = new TradeData(tradeID);
tradeArchive = new TextArchive(GetTextBankIndex());
tradeIDNumericUpDown.Value = tradeID;
speciesComboBox.SelectedIndex = curTradeData.species;
hpIVNumericUpDown.Value = curTradeData.hpIV;
atkIVNumericUpDown.Value = curTradeData.atkIV;
defIVNumericUpDown.Value = curTradeData.defIV;
speIVNumericUpDown.Value = curTradeData.speedIV;
spaIVNumericUpDown.Value = curTradeData.spAtkIV;
spdIVNumericUpDown.Value = curTradeData.spDefIV;
abilityComboBox.SelectedIndex = curTradeData.ability;
otIDNumericUpDown.Value = curTradeData.otID;
coolNumericUpDown.Value = curTradeData.cool;
beautyNumericUpDown.Value = curTradeData.beauty;
cuteNumericUpDown.Value = curTradeData.cute;
smartNumericUpDown.Value = curTradeData.smart;
toughNumericUpDown.Value = curTradeData.tough;
pidNumericUpDown.Value = curTradeData.pid;
heldItemComboBox.SelectedIndex = curTradeData.heldItem;
otGenderComboBox.SelectedIndex = curTradeData.otGender;
sheenNumericUpDown.Value = curTradeData.sheen;
langComboBox.SelectedIndex = curTradeData.language;
requestedComboBox.SelectedIndex = curTradeData.requestedSpecies;
unknownNumericUpDown.Value = curTradeData.unknown;
otNameTextBox.Text = GetOTName(tradeID);
nicknameTextBox.Text = GetMonNickname(tradeID);
tradeDataGroupBox.Text = "Trade Data";
textDataGroupBox.Text = "Text Data";
this.Text = "Trade Editor";
tradeDirty = false;
textDirty = false;
Helpers.EnableHandlers();
}
private void SaveTradeToFile()
{
Helpers.DisableHandlers();
curTradeData.species = speciesComboBox.SelectedIndex;
curTradeData.hpIV = (int)hpIVNumericUpDown.Value;
curTradeData.atkIV = (int)atkIVNumericUpDown.Value;
curTradeData.defIV = (int)defIVNumericUpDown.Value;
curTradeData.speedIV = (int)speIVNumericUpDown.Value;
curTradeData.spAtkIV = (int)spaIVNumericUpDown.Value;
curTradeData.spDefIV = (int)spdIVNumericUpDown.Value;
curTradeData.ability = abilityComboBox.SelectedIndex;
curTradeData.otID = (int)otIDNumericUpDown.Value;
curTradeData.cool = (int)coolNumericUpDown.Value;
curTradeData.beauty = (int)beautyNumericUpDown.Value;
curTradeData.cute = (int)cuteNumericUpDown.Value;
curTradeData.smart = (int)smartNumericUpDown.Value;
curTradeData.tough = (int)toughNumericUpDown.Value;
curTradeData.pid = (int)pidNumericUpDown.Value;
curTradeData.heldItem = heldItemComboBox.SelectedIndex;
curTradeData.otGender = otGenderComboBox.SelectedIndex;
curTradeData.sheen = (int)sheenNumericUpDown.Value;
curTradeData.language = langComboBox.SelectedIndex;
curTradeData.requestedSpecies = requestedComboBox.SelectedIndex;
if (RomInfo.gameFamily == RomInfo.GameFamilies.HGSS)
{
curTradeData.unknown = (int)unknownNumericUpDown.Value;
}
else
{
curTradeData.unknown = 0;
}
curTradeData.SaveToFileDefaultDir((int)tradeIDNumericUpDown.Value, false);
tradeDataGroupBox.Text = "Trade Data";
tradeDirty = false;
if (!textDirty)
{
this.Text = "Trade Editor";
}
AppLogger.Debug($"TradeEditor: Saved trade data for ID {curTradeData.id}.");
Helpers.EnableHandlers();
}
private void SaveTextDataToFile()
{
Helpers.DisableHandlers();
int tradeID = (int)tradeIDNumericUpDown.Value;
if (tradeID < 0 || tradeID + TradeData.GetTradeCount() > tradeArchive.messages.Count)
{
MessageBox.Show("Can't save to text bank. Index is out of range.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
AppLogger.Error("TradeEditor: Can't save to text bank. Index is out of range.");
Helpers.EnableHandlers();
return;
}
// Nickname
tradeArchive.messages[tradeID] = nicknameTextBox.Text;
// OT Name
tradeArchive.messages[tradeID + TradeData.GetTradeCount()] = otNameTextBox.Text;
tradeArchive.SaveToFileDefaultDir(GetTextBankIndex(), false);
// Reset the text data group box and dirty flags
textDataGroupBox.Text = "Text Data";
textDirty = false;
if (!tradeDirty)
{
this.Text = "Trade Editor";
}
AppLogger.Debug($"TradeEditor: Saved trade text data to message bank {GetTextBankIndex()}");
Helpers.EnableHandlers();
}
private int GetTextBankIndex()
{
switch (RomInfo.gameFamily)
{
case RomInfo.GameFamilies.DP:
switch (RomInfo.gameLanguage)
{
case RomInfo.GameLanguages.Japanese:
return 324;
default:
return 326;
}
case RomInfo.GameFamilies.Plat:
switch (RomInfo.gameLanguage)
{
case RomInfo.GameLanguages.Japanese:
return 369;
default:
return 370;
}
case RomInfo.GameFamilies.HGSS:
switch (RomInfo.gameLanguage)
{
case RomInfo.GameLanguages.Japanese:
return 198;
default:
return 200;
}
default:
AppLogger.Error("TradeEditor: Invalid game family for text bank index retrieval.");
MessageBox.Show("Invalid game family for text bank index retrieval.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return 0;
}
}
private string GetMonNickname(int tradeID)
{
string msg = tradeArchive.messages[tradeID];
if (msg.Length > nicknameTextBox.MaxLength)
{
msg = msg.Substring(0, nicknameTextBox.MaxLength);
}
return msg;
}
private string GetOTName(int tradeID)
{
int index = tradeID + TradeData.GetTradeCount();
string msg = tradeArchive.messages[index];
if (msg.Length > otNameTextBox.MaxLength)
{
msg = msg.Substring(0, otNameTextBox.MaxLength);
}
return msg;
}
private void RegisterMarkDirtyHandlers()
{
// ComboBoxes
speciesComboBox.SelectedIndexChanged += MarkDirty;
abilityComboBox.SelectedIndexChanged += MarkDirty;
heldItemComboBox.SelectedIndexChanged += MarkDirty;
otGenderComboBox.SelectedIndexChanged += MarkDirty;
langComboBox.SelectedIndexChanged += MarkDirty;
requestedComboBox.SelectedIndexChanged += MarkDirty;
// NumericUpDowns
hpIVNumericUpDown.ValueChanged += MarkDirty;
atkIVNumericUpDown.ValueChanged += MarkDirty;
defIVNumericUpDown.ValueChanged += MarkDirty;
speIVNumericUpDown.ValueChanged += MarkDirty;
spaIVNumericUpDown.ValueChanged += MarkDirty;
spdIVNumericUpDown.ValueChanged += MarkDirty;
otIDNumericUpDown.ValueChanged += MarkDirty;
coolNumericUpDown.ValueChanged += MarkDirty;
beautyNumericUpDown.ValueChanged += MarkDirty;
cuteNumericUpDown.ValueChanged += MarkDirty;
smartNumericUpDown.ValueChanged += MarkDirty;
toughNumericUpDown.ValueChanged += MarkDirty;
pidNumericUpDown.ValueChanged += MarkDirty;
sheenNumericUpDown.ValueChanged += MarkDirty;
unknownNumericUpDown.ValueChanged += MarkDirty;
// TextBoxes
otNameTextBox.TextChanged += MarkDirty;
nicknameTextBox.TextChanged += MarkDirty;
}
#region Handlers
private void MarkDirty(object sender, EventArgs e)
{
if (Helpers.HandlersDisabled)
{
return;
}
Helpers.DisableHandlers();
if (sender.Equals(otNameTextBox) || sender.Equals(nicknameTextBox))
{
textDataGroupBox.Text = "Text Data*";
this.Text = "Trade Editor*";
textDirty = true;
}
else
{
tradeDataGroupBox.Text = "Trade Data*";
this.Text = "Trade Editor*";
tradeDirty = true;
}
Helpers.EnableHandlers();
}
private void tradeIDNumericUpDown_ValueChanged(object sender, EventArgs e)
{
if (Helpers.HandlersDisabled)
{
return;
}
Helpers.DisableHandlers();
if (tradeDirty || textDirty)
{
DialogResult result = MessageBox.Show("You have unsaved changes. Do you want to save before changing the Trade ID?", "Unsaved Changes", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning);
if (result == DialogResult.Yes)
{
SaveTradeToFile();
SaveTextDataToFile();
}
else if (result == DialogResult.Cancel)
{
// Revert the change
tradeIDNumericUpDown.Value = curTradeData.id;
Helpers.EnableHandlers();
return;
}
}
int tradeID = (int)tradeIDNumericUpDown.Value;
if (tradeID < 0 || tradeID >= TradeData.GetTradeCount())
{
MessageBox.Show("Invalid Trade ID selected.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
LoadFromFile(tradeID);
Helpers.EnableHandlers();
}
private void saveTradeButton_Click(object sender, EventArgs e)
{
SaveTradeToFile();
}
private void saveTextDataButton_Click(object sender, EventArgs e)
{
SaveTextDataToFile();
}
private void saveAllButton_Click(object sender, EventArgs e)
{
SaveTradeToFile();
SaveTextDataToFile();
}
private void TradeEditor_FormClosing(object sender, FormClosingEventArgs e)
{
if (tradeDirty || textDirty)
{
DialogResult result = MessageBox.Show("You have unsaved changes. Do you want to save before closing?", "Unsaved Changes", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning);
if (result == DialogResult.Yes)
{
SaveTradeToFile();
SaveTextDataToFile();
}
else if (result == DialogResult.Cancel)
{
e.Cancel = true; // Cancel the closing
}
}
}
private void addFileButton_Click(object sender, EventArgs e)
{
TradeData newTrade = new TradeData(TradeData.GetTradeCount());
newTrade.SaveToFileDefaultDir(newTrade.id, false);
tradeIDNumericUpDown.Maximum = TradeData.GetTradeCount() - 1; // Update the maximum value
// Adjust text archive to accommodate the new trade
int lastNicknameIndex = TradeData.GetTradeCount() - 1;
tradeArchive.messages.Insert(lastNicknameIndex, "Nickname");
tradeArchive.messages.Add("OT Name");
tradeArchive.SaveToFileDefaultDir(GetTextBankIndex(), false);
MessageBox.Show($"Added new trade with id {newTrade.id}", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
private void removeLastButton_Click(object sender, EventArgs e)
{
DialogResult result = MessageBox.Show("Are you sure you want to remove the last trade? This action cannot be undone.", "Confirm Removal", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
if (result != DialogResult.Yes)
{
return; // User cancelled the removal
}
if (TradeData.GetTradeCount() <= 1)
{
MessageBox.Show("Cannot remove the last trade.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
int lastTradeID = TradeData.GetTradeCount() - 1;
try
{
File.Delete(RomInfo.gameDirs[RomInfo.DirNames.tradeData].unpackedDir + "\\" + lastTradeID.ToString("D4"));
}
catch (Exception ex)
{
MessageBox.Show($"Failed to delete trade file: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
AppLogger.Error($"TradeEditor: Failed to delete trade file for ID {lastTradeID}: {ex.Message}");
return;
}
tradeIDNumericUpDown.Maximum = lastTradeID - 1; // Update the maximum value
// Adjust text archive to remove the last trade's text
if (tradeArchive.messages.Count > lastTradeID)
{
tradeArchive.messages.RemoveAt(lastTradeID); // Remove Nickname
tradeArchive.messages.RemoveAt(tradeArchive.messages.Count - 1); // Remove OT Name
tradeArchive.SaveToFileDefaultDir(GetTextBankIndex(), false);
}
}
#endregion
}
}