From 2e64f0b1091ba30d7d5f3530569223ee94559e56 Mon Sep 17 00:00:00 2001 From: Kurt Date: Sat, 18 Apr 2020 11:11:25 -0700 Subject: [PATCH] Add villager flag editor + moving out state toggle --- .../Structures/Records/EventFlagVillager.cs | 72 ++++++++++ NHSE.Core/Structures/Villager/Villager.cs | 19 +++ NHSE.Parsing/GameBCSVDumper.cs | 36 +++++ NHSE.WinForms/Editor.Designer.cs | 26 ++++ NHSE.WinForms/Editor.cs | 14 ++ .../Villager/VillagerFlagEditor.Designer.cs | 132 ++++++++++++++++++ .../Subforms/Villager/VillagerFlagEditor.cs | 48 +++++++ .../Subforms/Villager/VillagerFlagEditor.resx | 120 ++++++++++++++++ 8 files changed, 467 insertions(+) create mode 100644 NHSE.Core/Structures/Records/EventFlagVillager.cs create mode 100644 NHSE.WinForms/Subforms/Villager/VillagerFlagEditor.Designer.cs create mode 100644 NHSE.WinForms/Subforms/Villager/VillagerFlagEditor.cs create mode 100644 NHSE.WinForms/Subforms/Villager/VillagerFlagEditor.resx diff --git a/NHSE.Core/Structures/Records/EventFlagVillager.cs b/NHSE.Core/Structures/Records/EventFlagVillager.cs new file mode 100644 index 0000000..3deedd3 --- /dev/null +++ b/NHSE.Core/Structures/Records/EventFlagVillager.cs @@ -0,0 +1,72 @@ +using System.Collections.Generic; +// ReSharper disable StringLiteralTypo + +namespace NHSE.Core +{ + public class EventFlagVillager + { + public readonly short Value1; + public readonly short Value2; + + public readonly ushort Index; + public readonly string Name; + + public EventFlagVillager(short v1, short v2, ushort index, string name) + { + Name = name; + Index = index; + Value1 = v1; + Value2 = v2; + } + + public static readonly IReadOnlyDictionary List = new Dictionary + { + {0x000, new EventFlagVillager(0 , 200 , 0000, "NnpcHouseLoan" )}, // ローン返済カウント + {0x001, new EventFlagVillager(0 , 1 , 0001, "NnpcHouseBuildOk" )}, // NPCテント建て替え許可 + {0x002, new EventFlagVillager(0 , 1 , 0002, "NnpcHouseBuilt" )}, // NPCの家建て替え済み + {0x003, new EventFlagVillager(0 , 1 , 0003, "CoordinateUpdate" )}, // 家具配置更新済か + {0x004, new EventFlagVillager(0 , 1 , 0004, "Is3rdNpc" )}, // 3人目のNPCか? + {0x005, new EventFlagVillager(0 , 1 , 0005, "MoveInCompletion" )}, // 引越し完了済みか + {0x006, new EventFlagVillager(0 , 10 , 0006, "LastFtrNum" )}, // 最後に配置した家具の数 + {0x007, new EventFlagVillager(10, 10 , 0007, "MoveRoomIndex" )}, // ダンボール部屋表示に使用したテンプレート番号 + {0x008, new EventFlagVillager(0 , 1 , 0008, "PlayerNamePhrase" )}, // 口癖はプレイヤーが設定したものか + {0x009, new EventFlagVillager(0 , 2 , 0009, "AbandonedHouse" )}, // 自宅を空き家状態にするか + {0x00A, new EventFlagVillager(0 , 1 , 0010, "InputPhrase" )}, // 口癖を設定した事あるか + {0x00C, new EventFlagVillager(0 , 1 , 0012, "TalkLifeStart1P1st" )}, // React_1P_Lifestart発生したか + {0x00E, new EventFlagVillager(0 , 1 , 0014, "FromSettlerQuest" )}, // 住人移住クエストで来たNPCか + {0x00F, new EventFlagVillager(0 , 1 , 0015, "ChangeFirstWall" )}, // 初期住人用壁紙を変更したか + {0x010, new EventFlagVillager(0 , 1 , 0016, "ChangeFirstFloor" )}, // 初期住人用床板を変更したか + {0x012, new EventFlagVillager(0 , 1 , 0018, "GetFirstVillagerWallFtr1" )}, // 初期住人用壁掛け家具1を入手したか + {0x013, new EventFlagVillager(0 , 1 , 0019, "GetFirstVillagerWallFtr2" )}, // 初期住人用壁掛け家具2を入手したか + {0x014, new EventFlagVillager(0 , 2 , 0020, "UseAudioType" )}, // 今日使用するオーディオ家具の種類 + {0x015, new EventFlagVillager(0 , 3 , 0021, "TalkTransitionTimes" )}, // 序盤→日常移行会話を呼んだ回数 + {0x016, new EventFlagVillager(0 , 1 , 0022, "FinishTalkTransition" )}, // 序盤→日常移行会話を聞き終えたか + {0x017, new EventFlagVillager(0 , 3 , 0023, "OutdoorCatnap" )}, // 深夜屋外でのうたた寝判定状況 + {0x018, new EventFlagVillager(0 , 1 , 0024, "ForceMoveOut" )}, // 強制的に転出させられるフラグ + {0x019, new EventFlagVillager(0 , 3 , 0025, "EarlyOrLate" )}, // 早起き・夜更かしフラグ + {0x01A, new EventFlagVillager(0 , 16 , 0026, "ContinuousNormalDay" )}, // 平常活動の連続日数 + {0x01B, new EventFlagVillager(0 , 1 , 0027, "React1stNpcPresent" )}, // React_1P_Lifestartのプレゼント装備の反応発生したか + {0x01C, new EventFlagVillager(0 , 1 , 0028, "IsReFabricSmartPhone" )}, // スマホ柄に汎用布地を使うか? + {0x01D, new EventFlagVillager(0 , 1 , 0029, "MarketBuildingSupport" )}, // お店の資材集め応援会話発生したか? + {0x020, new EventFlagVillager(0 , 1 , 0032, "NnpcHouseBuiltToday" )}, // NPCの家建て替え済み当日 + {0x023, new EventFlagVillager(0 , 1 , 0035, "AppE_Happen1st" )}, // このNPCの評判UP応援アプローチ会話発生済か? + {0x024, new EventFlagVillager(0 , 1 , 0036, "AppE_GetItem1st" )}, // このNPCの評判UP応援アイテムもらったことあるか? + {0x025, new EventFlagVillager(0 , 1 , 0037, "AppE_Rep_HappenToday" )}, // 今日このNPCの評判UP応援アプローチ会話発生済か? + {0x026, new EventFlagVillager(0 , 1 , 0038, "AppE_WelcomoMigrants" )}, // このNPCはAppE_WelcomoMigrants発生済か? + {0x028, new EventFlagVillager(0 , 1 , 0040, "AppE_WelcomeMigrantsToday" )}, // このNPCのAppE_WelcomeMigrants発生当日か? + {0x029, new EventFlagVillager(0 , 10 , 0041, "MoveInOrder" )}, // 土地売約済み状態のNPCの転入順 + {0x02A, new EventFlagVillager(0 , 1 , 0042, "EquipEasterWear" )}, // イースター用装備にする + {0x02B, new EventFlagVillager(0 , 10 , 0043, "ForceMoveOutVillagerIndex" )}, // 強制転出に指定した住人番号 + }; + + private const string Unknown = "???"; + + public static string GetFlagName(ushort index, ushort count) + { + var dict = List; + if (dict.TryGetValue(index, out var val)) + return $"{index:000} - {val.Name} = {count}"; + return $"{index:000} - {Unknown} = {count}"; + } + } +} diff --git a/NHSE.Core/Structures/Villager/Villager.cs b/NHSE.Core/Structures/Villager/Villager.cs index f2a5ce3..ed3f5d2 100644 --- a/NHSE.Core/Structures/Villager/Villager.cs +++ b/NHSE.Core/Structures/Villager/Villager.cs @@ -72,6 +72,25 @@ public IReadOnlyList Furniture set => VillagerItem.SetArray(value).CopyTo(Data, 0x105EC); } + // State Flags + public byte State2 { get => Data[0x11EFA]; set => Data[0x11EFA] = value; } + public bool MovingOut { get => (State2 & 2) == 2; set => State2 = (byte)((State2 & ~2) | (value ? 2 : 0)); } + + // EventFlagsNPCSaveParam + private const int EventFlagsSaveCount = 0x100; // Future-proof allocation! Release version used <20% of the amount allocated. + + public ushort[] GetEventFlagsSave() + { + var value = new ushort[EventFlagsSaveCount]; + Buffer.BlockCopy(Data, 0x11EFC, value, 0, sizeof(ushort) * value.Length); + return value; + } + + public void SetEventFlagsSave(ushort[] value) + { + Buffer.BlockCopy(value, 0, Data, 0x11EFC, sizeof(ushort) * value.Length); + } + public override string ToString() => InternalName; public string InternalName => VillagerUtil.GetInternalVillagerName((VillagerSpecies) Species, Variant); public int Gender => ((int)Personality / 4) & 1; // 0 = M, 1 = F diff --git a/NHSE.Parsing/GameBCSVDumper.cs b/NHSE.Parsing/GameBCSVDumper.cs index a1687fb..4eb0bba 100644 --- a/NHSE.Parsing/GameBCSVDumper.cs +++ b/NHSE.Parsing/GameBCSVDumper.cs @@ -43,6 +43,7 @@ void DumpB(string fn, byte[] bytes) DumpS("fish.txt", GetFishList(pathBCSV, itemNames)); DumpS("bugs.txt", GetInsectList(pathBCSV, itemNames)); DumpS("eventFlagPlayer.txt", GetEventFlagNames(pathBCSV)); + DumpS("eventFlagVillager.txt", GetVillagerEventFlagNames(pathBCSV)); DumpS("ItemKind.txt", GetPossibleEnum(pathBCSV, "ItemParam.bcsv", 0xFC275E86)); DumpS("PlantKind.txt", GetPossibleEnum(pathBCSV, "FgMainParam.bcsv", 0x48EF0398)); @@ -336,5 +337,40 @@ private static IEnumerable GetEventFlagNames(string pathBCSV, string fn result.Sort(); return result; } + + private static IEnumerable GetVillagerEventFlagNames(string pathBCSV, string fn = "EventFlagsNpcSaveParam.bcsv") + { + var bcsv = BCSVConverter.GetBCSV(pathBCSV, fn); + var dict = bcsv.GetFieldDictionary(); + var fv1 = dict[0x4C24F1CF]; + var fv2 = dict[0x344B17D7]; + var fname = dict[0x45F320F2]; + var findex = dict[0x54706054]; + var fcomment = dict[0x85CF1615]; + + var result = new List(); + for (int i = 0; i < bcsv.EntryCount; i++) + { + var iv1 = bcsv.ReadValue(i, fv1).Substring(2); + var iv1a = short.Parse(iv1, NumberStyles.HexNumber); + + var iv2 = bcsv.ReadValue(i, fv2).Substring(2); + var iv2a = short.Parse(iv2, NumberStyles.HexNumber); + + var iid = bcsv.ReadValue(i, findex); + var ival = (ushort)short.Parse(iid); + + var name = bcsv.ReadValue(i, fname).TrimEnd('\0'); + var comment = bcsv.ReadValue(i, fcomment).TrimEnd('\0'); + + var paddedName = $"\"{name}\"".PadRight(45, ' '); + var v = $"new {nameof(EventFlagVillager)}({iv1a,-2}, {iv2a,-4}, {ival:0000}, {paddedName})"; + var kvp = $"{{0x{ival:X3}, {v}}}, // {comment}"; + result.Add(kvp); + } + + result.Sort(); + return result; + } } } diff --git a/NHSE.WinForms/Editor.Designer.cs b/NHSE.WinForms/Editor.Designer.cs index 090dbac..fd9b7a4 100644 --- a/NHSE.WinForms/Editor.Designer.cs +++ b/NHSE.WinForms/Editor.Designer.cs @@ -92,6 +92,8 @@ private void InitializeComponent() this.B_EditTurnipExchange = new System.Windows.Forms.Button(); this.B_EditBuildings = new System.Windows.Forms.Button(); this.B_RecycleBin = new System.Windows.Forms.Button(); + this.CHK_VillagerMovingOut = new System.Windows.Forms.CheckBox(); + this.B_EditVillagerFlags = new System.Windows.Forms.Button(); this.Menu_Editor.SuspendLayout(); this.TC_Editors.SuspendLayout(); this.Tab_Players.SuspendLayout(); @@ -426,6 +428,8 @@ private void InitializeComponent() // // Tab_Villagers // + this.Tab_Villagers.Controls.Add(this.B_EditVillagerFlags); + this.Tab_Villagers.Controls.Add(this.CHK_VillagerMovingOut); this.Tab_Villagers.Controls.Add(this.B_EditFurniture); this.Tab_Villagers.Controls.Add(this.B_LoadVillager); this.Tab_Villagers.Controls.Add(this.B_DumpVillager); @@ -799,6 +803,26 @@ private void InitializeComponent() this.B_RecycleBin.UseVisualStyleBackColor = true; this.B_RecycleBin.Click += new System.EventHandler(this.B_RecycleBin_Click); // + // CHK_VillagerMovingOut + // + this.CHK_VillagerMovingOut.AutoSize = true; + this.CHK_VillagerMovingOut.Location = new System.Drawing.Point(232, 99); + this.CHK_VillagerMovingOut.Name = "CHK_VillagerMovingOut"; + this.CHK_VillagerMovingOut.Size = new System.Drawing.Size(81, 17); + this.CHK_VillagerMovingOut.TabIndex = 27; + this.CHK_VillagerMovingOut.Text = "Moving Out"; + this.CHK_VillagerMovingOut.UseVisualStyleBackColor = true; + // + // B_EditVillagerFlags + // + this.B_EditVillagerFlags.Location = new System.Drawing.Point(300, 168); + this.B_EditVillagerFlags.Name = "B_EditVillagerFlags"; + this.B_EditVillagerFlags.Size = new System.Drawing.Size(92, 40); + this.B_EditVillagerFlags.TabIndex = 28; + this.B_EditVillagerFlags.Text = "Edit Flags"; + this.B_EditVillagerFlags.UseVisualStyleBackColor = true; + this.B_EditVillagerFlags.Click += new System.EventHandler(this.B_EditVillagerFlags_Click); + // // Editor // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -904,6 +928,8 @@ private void InitializeComponent() private System.Windows.Forms.Button B_EditTerrain; private System.Windows.Forms.Button B_EditPlayerFlags; private System.Windows.Forms.Button B_EditFieldItems; + private System.Windows.Forms.CheckBox CHK_VillagerMovingOut; + private System.Windows.Forms.Button B_EditVillagerFlags; } } diff --git a/NHSE.WinForms/Editor.cs b/NHSE.WinForms/Editor.cs index 048df70..3af5665 100644 --- a/NHSE.WinForms/Editor.cs +++ b/NHSE.WinForms/Editor.cs @@ -306,6 +306,7 @@ private void LoadVillager(Villager v) NUD_Variant.Value = v.Variant; CB_Personality.SelectedIndex = (int) v.Personality; TB_Catchphrase.Text = v.CatchPhrase; + CHK_VillagerMovingOut.Checked = v.MovingOut; } private void SaveVillager(int index) @@ -316,6 +317,7 @@ private void SaveVillager(int index) v.Variant = (byte)NUD_Variant.Value; v.Personality = (VillagerPersonality)CB_Personality.SelectedIndex; v.CatchPhrase = TB_Catchphrase.Text; + v.MovingOut = CHK_VillagerMovingOut.Checked; SAV.Main.SetVillager(v, index); } @@ -463,6 +465,18 @@ private void B_EditFurniture_Click(object sender, EventArgs e) SAV.Main.SetVillager(v, VillagerIndex); } + private void B_EditVillagerFlags_Click(object sender, EventArgs e) + { + var v = SAV.Main.GetVillager(VillagerIndex); + var flags = v.GetEventFlagsSave(); + using var editor = new VillagerFlagEditor(flags); + if (editor.ShowDialog() != DialogResult.OK) + return; + + v.SetEventFlagsSave(flags); + SAV.Main.SetVillager(v, VillagerIndex); + } + private void B_DumpDesign_Click(object sender, EventArgs e) { if (ModifierKeys == Keys.Shift) diff --git a/NHSE.WinForms/Subforms/Villager/VillagerFlagEditor.Designer.cs b/NHSE.WinForms/Subforms/Villager/VillagerFlagEditor.Designer.cs new file mode 100644 index 0000000..98e021a --- /dev/null +++ b/NHSE.WinForms/Subforms/Villager/VillagerFlagEditor.Designer.cs @@ -0,0 +1,132 @@ +namespace NHSE.WinForms +{ + partial class VillagerFlagEditor + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.B_Cancel = new System.Windows.Forms.Button(); + this.B_Save = new System.Windows.Forms.Button(); + this.LB_Counts = new System.Windows.Forms.ListBox(); + this.NUD_Count = new System.Windows.Forms.NumericUpDown(); + this.L_Count = new System.Windows.Forms.Label(); + ((System.ComponentModel.ISupportInitialize)(this.NUD_Count)).BeginInit(); + this.SuspendLayout(); + // + // B_Cancel + // + this.B_Cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.B_Cancel.Location = new System.Drawing.Point(200, 226); + this.B_Cancel.Name = "B_Cancel"; + this.B_Cancel.Size = new System.Drawing.Size(72, 23); + this.B_Cancel.TabIndex = 7; + this.B_Cancel.Text = "Cancel"; + this.B_Cancel.UseVisualStyleBackColor = true; + this.B_Cancel.Click += new System.EventHandler(this.B_Cancel_Click); + // + // B_Save + // + this.B_Save.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.B_Save.Location = new System.Drawing.Point(200, 197); + this.B_Save.Name = "B_Save"; + this.B_Save.Size = new System.Drawing.Size(72, 23); + this.B_Save.TabIndex = 6; + this.B_Save.Text = "Save"; + this.B_Save.UseVisualStyleBackColor = true; + this.B_Save.Click += new System.EventHandler(this.B_Save_Click); + // + // LB_Counts + // + this.LB_Counts.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.LB_Counts.FormattingEnabled = true; + this.LB_Counts.Location = new System.Drawing.Point(12, 12); + this.LB_Counts.Name = "LB_Counts"; + this.LB_Counts.Size = new System.Drawing.Size(177, 238); + this.LB_Counts.TabIndex = 8; + this.LB_Counts.SelectedIndexChanged += new System.EventHandler(this.LB_Counts_SelectedIndexChanged); + // + // NUD_Count + // + this.NUD_Count.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.NUD_Count.Location = new System.Drawing.Point(200, 28); + this.NUD_Count.Maximum = new decimal(new int[] { + 32767, + 0, + 0, + 0}); + this.NUD_Count.Minimum = new decimal(new int[] { + 32768, + 0, + 0, + -2147483648}); + this.NUD_Count.Name = "NUD_Count"; + this.NUD_Count.Size = new System.Drawing.Size(72, 20); + this.NUD_Count.TabIndex = 9; + this.NUD_Count.ValueChanged += new System.EventHandler(this.NUD_Count_ValueChanged); + // + // L_Count + // + this.L_Count.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.L_Count.AutoSize = true; + this.L_Count.Location = new System.Drawing.Point(197, 12); + this.L_Count.Name = "L_Count"; + this.L_Count.Size = new System.Drawing.Size(37, 13); + this.L_Count.TabIndex = 10; + this.L_Count.Text = "Value:"; + // + // FlagEditor + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(284, 261); + this.Controls.Add(this.L_Count); + this.Controls.Add(this.NUD_Count); + this.Controls.Add(this.LB_Counts); + this.Controls.Add(this.B_Cancel); + this.Controls.Add(this.B_Save); + this.Icon = global::NHSE.WinForms.Properties.Resources.icon; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "FlagEditor"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "Flag Editor"; + ((System.ComponentModel.ISupportInitialize)(this.NUD_Count)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button B_Cancel; + private System.Windows.Forms.Button B_Save; + private System.Windows.Forms.ListBox LB_Counts; + private System.Windows.Forms.NumericUpDown NUD_Count; + private System.Windows.Forms.Label L_Count; + } +} \ No newline at end of file diff --git a/NHSE.WinForms/Subforms/Villager/VillagerFlagEditor.cs b/NHSE.WinForms/Subforms/Villager/VillagerFlagEditor.cs new file mode 100644 index 0000000..eb522ed --- /dev/null +++ b/NHSE.WinForms/Subforms/Villager/VillagerFlagEditor.cs @@ -0,0 +1,48 @@ +using System; +using System.Windows.Forms; +using NHSE.Core; + +namespace NHSE.WinForms +{ + public partial class VillagerFlagEditor : Form + { + private readonly ushort[] Counts; + + public VillagerFlagEditor(ushort[] counts) + { + Counts = counts; + InitializeComponent(); + for (ushort i = 0; i < counts.Length; i++) + LB_Counts.Items.Add(EventFlagVillager.GetFlagName(i, counts[i])); + DialogResult = DialogResult.Cancel; + LB_Counts.SelectedIndex = 0; + } + + private void B_Cancel_Click(object sender, EventArgs e) => Close(); + + private void B_Save_Click(object sender, EventArgs e) + { + DialogResult = DialogResult.OK; + Close(); + } + + private int Index; + + private void NUD_Count_ValueChanged(object sender, EventArgs e) + { + if (Index < 0) + return; + + Counts[Index] = (ushort) NUD_Count.Value; + LB_Counts.Items[Index] = EventFlagVillager.GetFlagName((ushort)Index, Counts[Index]); + } + + private void LB_Counts_SelectedIndexChanged(object sender, EventArgs e) + { + if (LB_Counts.SelectedIndex < 0) + return; + + NUD_Count.Value = Counts[Index = LB_Counts.SelectedIndex]; + } + } +} diff --git a/NHSE.WinForms/Subforms/Villager/VillagerFlagEditor.resx b/NHSE.WinForms/Subforms/Villager/VillagerFlagEditor.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/NHSE.WinForms/Subforms/Villager/VillagerFlagEditor.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file