NHSE/NHSE.WinForms/Controls/VillagerEditor.cs
Josh (vector_cmdr) 7071832476
Fix: LoadDecrypted Villager[n] (#747)
ReloadAll() wasn't loading the villager that was currently selected on load (or 0 if not in panel) leading to incorrect villager import.

Fix: Add public Reload() method to VillagerEditor to call LoadVillagers() and call it after Villagers and Origin are loaded. Reset Villager index NUD to 0 in LoadVillagers() so the slot is moved to 0 and then load occurs (so UI is refreshed and villager is loaded).
2026-02-11 23:50:25 +11:00

322 lines
11 KiB
C#

using System;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using NHSE.Core;
using NHSE.Sprites;
using NHSE.Villagers;
namespace NHSE.WinForms;
public partial class VillagerEditor : UserControl
{
public IVillager[] Villagers;
public IVillagerOrigin Origin;
private readonly HorizonSave SAV;
private int VillagerIndex = -1;
private int PlayerIndex = -1;
private bool Loading;
public VillagerEditor(IVillager[] villagers, IVillagerOrigin origin, HorizonSave sav, bool hasHouses, int playerIndex)
{
InitializeComponent();
Villagers = villagers;
Origin = origin;
SAV = sav;
PlayerIndex = playerIndex;
LoadVillagers();
B_EditHouses.Visible = hasHouses;
}
public void Save() => SaveVillager(VillagerIndex);
public void Reload() => LoadVillagers();
private void LoadVillagers()
{
CB_Personality.Items.Clear();
var personalities = Enum.GetNames<VillagerPersonality>();
CB_Personality.Items.AddRange(personalities);
VillagerIndex = -1;
NUD_Villager.Value = 0;
LoadVillager(0);
}
private void LoadVillager(object sender, EventArgs e) => LoadVillager((int)NUD_Villager.Value);
private void LoadVillager(int index)
{
if (VillagerIndex >= 0)
SaveVillager(VillagerIndex);
if (index < 0)
return;
LoadVillager(Villagers[index]);
VillagerIndex = index;
}
private void LoadVillager(IVillager v)
{
Loading = true;
NUD_Species.Value = v.Species;
NUD_Variant.Value = v.Variant;
CB_Personality.SelectedIndex = (int)v.Personality;
TB_Catchphrase.Text = v.CatchPhrase;
CHK_VillagerMovingOut.Checked = v.MovingOut;
Loading = false;
}
private void SaveVillager(int index)
{
var v = Villagers[index];
v.Species = (byte)NUD_Species.Value;
v.Variant = (byte)NUD_Variant.Value;
v.Personality = (VillagerPersonality)CB_Personality.SelectedIndex;
v.CatchPhrase = TB_Catchphrase.Text;
v.MovingOut = CHK_VillagerMovingOut.Checked;
}
private void CHK_VillagerMovingOut_CheckedChanged(object sender, EventArgs e)
{
if (Loading)
return;
if (!CHK_VillagerMovingOut.Checked)
return;
WinFormsUtil.Alert(MessageStrings.MsgMoveOut, MessageStrings.MsgMoveOutSuggest);
}
private void B_DumpVillager_Click(object sender, EventArgs e)
{
if (ModifierKeys == Keys.Shift)
{
using var fbd = new FolderBrowserDialog();
if (fbd.ShowDialog() != DialogResult.OK)
return;
var dir = Path.GetDirectoryName(fbd.SelectedPath);
if (dir == null || !Directory.Exists(dir))
return;
Villagers.Dump(fbd.SelectedPath);
return;
}
var name = L_ExternalName.Text;
using var sfd = new SaveFileDialog();
sfd.Filter = "New Horizons Villager (*.nhv)|*.nhv|" +
"New Horizons Villager (*.nhv2)|*.nhv2|" +
"All files (*.*)|*.*";
sfd.FileName = $"{name}.{Villagers[VillagerIndex].Extension}";
if (sfd.ShowDialog() != DialogResult.OK)
return;
SaveVillager(VillagerIndex);
var v = Villagers[VillagerIndex];
File.WriteAllBytes(sfd.FileName, v.Write());
}
private void B_LoadVillager_Click(object sender, EventArgs e)
{
var name = L_ExternalName.Text;
using var ofd = new OpenFileDialog();
ofd.Filter = "New Horizons Villager (*.nhv)|*.nhv|" +
"New Horizons Villager (*.nhv2)|*.nhv2|" +
"All files (*.*)|*.*";
ofd.FileName = $"{name}.{Villagers[VillagerIndex].Extension}";
if (ofd.ShowDialog() != DialogResult.OK)
return;
var path = ofd.FileName;
var expectLength = SAV.Main.Offsets.VillagerSize;
var fi = new FileInfo(path);
if (!VillagerConverter.IsCompatible((int)fi.Length, expectLength))
{
WinFormsUtil.Error(string.Format(MessageStrings.MsgDataSizeMismatchImport, fi.Length, expectLength), path);
return;
}
var data = File.ReadAllBytes(ofd.FileName);
data = VillagerConverter.GetCompatible(data, expectLength);
if (data.Length != expectLength)
{
WinFormsUtil.Error(string.Format(MessageStrings.MsgDataSizeMismatchImport, fi.Length, expectLength), path);
return;
}
var v = SAV.Main.Offsets.ReadVillager(data);
var player0 = Origin;
if (!v.IsOriginatedFrom(player0))
{
string msg = string.Format(MessageStrings.MsgDataDidNotOriginateFromHost_0, player0.PlayerName);
var result = WinFormsUtil.Prompt(MessageBoxButtons.YesNoCancel, msg, MessageStrings.MsgAskUpdateValues);
if (result == DialogResult.Cancel)
return;
if (result == DialogResult.Yes)
v.ChangeOrigins(player0, v.Write());
}
LoadVillager(Villagers[VillagerIndex] = v);
}
private void B_EditWear_Click(object sender, EventArgs e)
{
var v = Villagers[VillagerIndex];
var items = v.WearStockList;
using var editor = new PlayerItemEditor(items, 8, 3);
if (editor.ShowDialog() == DialogResult.OK)
v.WearStockList = items;
}
private void B_EditFurniture_Click(object sender, EventArgs e)
{
var v = Villagers[VillagerIndex];
var items = v.FtrStockList;
using var editor = new PlayerItemEditor(items, 8, 4);
if (editor.ShowDialog() == DialogResult.OK)
v.FtrStockList = items;
}
private void B_EditVillagerFlags_Click(object sender, EventArgs e)
{
var v = Villagers[VillagerIndex];
var flags = v.GetEventFlagsSave();
using var editor = new VillagerFlagEditor(flags);
if (editor.ShowDialog() == DialogResult.OK)
v.SetEventFlagsSave(flags);
}
private string GetCurrentVillagerInternalName() => VillagerUtil.GetInternalVillagerName((VillagerSpecies)NUD_Species.Value, (int)NUD_Variant.Value);
private void ChangeVillager(object sender, EventArgs e) => ChangeVillager();
private void ChangeVillager()
{
var name = GetCurrentVillagerInternalName();
L_InternalName.Text = name;
L_ExternalName.Text = GameInfo.Strings.GetVillager(name);
PB_Villager.Image = VillagerSprite.GetVillagerSprite(name);
}
private void B_EditHouse_Click(object sender, EventArgs e)
{
SaveVillager(VillagerIndex);
var villagers = SAV.Main.GetVillagers();
var houses = SAV.Main.GetVillagerHouses();
using var editor = new VillagerHouseEditor(houses, villagers, SAV.Main, VillagerIndex);
if (editor.ShowDialog() == DialogResult.OK)
SAV.Main.SetVillagerHouses(houses);
}
private static void ShowContextMenuBelow(ToolStripDropDown c, Control n) => c.Show(n.PointToScreen(new System.Drawing.Point(0, n.Height)));
private void B_EditVillager_Click(object sender, EventArgs e) => ShowContextMenuBelow(CM_EditVillager, B_EditVillager);
private void B_EditVillagerRoom_Click(object sender, EventArgs e)
{
var v = Villagers[VillagerIndex];
using var editor = new SaveRoomFloorWallEditor(v.Room);
if (editor.ShowDialog() == DialogResult.OK)
v.Room = editor.Entity;
}
private void B_EditVillagerDesign_Click(object sender, EventArgs e)
{
var player = SAV.Players[PlayerIndex];
var v = Villagers[VillagerIndex];
DesignPatternPRO[] tmp = [v.Design];
using var editor = new PatternEditorPRO(tmp, player);
if (editor.ShowDialog() == DialogResult.OK)
v.Design = tmp[0];
}
private void B_EditVillagerPlayerMemories_Click(object sender, EventArgs e)
{
if (ModifierKeys == Keys.Shift)
{
var prompt = WinFormsUtil.Prompt(MessageBoxButtons.YesNo, MessageStrings.MsgVillagerFriendshipMax);
if (prompt != DialogResult.Yes)
return;
foreach (var villager in Villagers)
villager.SetFriendshipAll();
System.Media.SystemSounds.Asterisk.Play();
return;
}
var v = Villagers[VillagerIndex];
using var editor = new VillagerMemoryEditor(v);
if (editor.ShowDialog() == DialogResult.OK)
{ } // editor saves our changes
}
private void B_EditDIYTimer_Click(object sender, EventArgs e)
{
var v = Villagers[VillagerIndex];
using var editor = new VillagerDIYTimerEditor(v);
if (editor.ShowDialog() == DialogResult.OK)
{ } // editor saves our changes
}
private void B_MoveOutAllVillagers_Click(object sender, EventArgs e)
{
if (Loading)
return;
var prompt = WinFormsUtil.Prompt(MessageBoxButtons.OKCancel, MessageStrings.MsgMoveOutAll);
if (prompt != DialogResult.OK)
return;
foreach (var villager in Villagers)
villager.MovingOut = true;
CHK_VillagerMovingOut.Checked = true;
System.Media.SystemSounds.Asterisk.Play();
}
private void B_SetPhraseOriginal_Click(object sender, EventArgs e)
{
var internalName = GetCurrentVillagerInternalName();
TB_Catchphrase.Text = GameInfo.Strings.GetVillagerDefaultPhrase(internalName);
}
private void B_ReplaceVillager_Click(object sender, EventArgs e)
{
if (!Clipboard.ContainsText())
{
WinFormsUtil.Error(MessageStrings.MsgVillagerReplaceNoText);
return;
}
var internalName = Clipboard.GetText().Trim();
if (!VillagerResources.IsVillagerDataKnown(internalName))
{
var clip = internalName;
internalName = GameInfo.Strings.VillagerMap.FirstOrDefault(z => string.Equals(z.Value, internalName, StringComparison.InvariantCultureIgnoreCase)).Key;
if (internalName is null)
{
WinFormsUtil.Error(string.Format(MessageStrings.MsgVillagerReplaceUnknownName, clip));
return;
}
}
var index = VillagerIndex;
var villager = Villagers[index];
if (villager is not Villager2 v2)
{
WinFormsUtil.Error(MessageStrings.MsgVillagerReplaceOutdatedSaveFormat);
return;
}
var houses = SAV.Main.GetVillagerHouses();
var houseIndex = Array.FindIndex(houses, z => z.NPC1 == index);
var exist = new VillagerInfo(v2, houses[houseIndex]);
var replace = VillagerSwap.GetReplacementVillager(exist, internalName);
var nh = new VillagerHouse1(replace.House);
SAV.Main.SetVillagerHouse(nh, houseIndex);
var nv = new Villager2(replace.Villager);
LoadVillager(Villagers[index] = nv);
System.Media.SystemSounds.Asterisk.Play();
}
}