Added ability to Add / Del new root nodes for some things. Added loading of items from a Json file to easily select them while editing.

This commit is contained in:
Matthew Neumann 2025-03-05 16:04:27 -08:00
parent 1a0cc36907
commit 0691e2c99e
11 changed files with 2563 additions and 87 deletions

View File

@ -9,4 +9,25 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<None Remove="beltitems.json" />
<None Remove="weaponmoditems.json" />
<None Remove="wearitems.json" />
</ItemGroup>
<ItemGroup>
<Content Include="allitems.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="beltitems.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="weaponmoditems.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="wearitems.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project> </Project>

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
@ -9,12 +10,15 @@ namespace Better_NCP_Editor
public object NewValue { get; private set; } public object NewValue { get; private set; }
private Control inputControl; private Control inputControl;
public EditValueForm(string propertyName, string currentValue, Type valueType) // Constructor now takes an extra parameter: parentNodeName.
public EditValueForm(string propertyName, string currentValue, Type valueType, List<string>? comboItems)
{ {
// Set the minimum size. // Set the minimum and default size.
this.MinimumSize = new Size(210, 150); // Example: 300x150 pixels this.MinimumSize = new Size(210, 120);
// Optionally, set the default size if you want:
this.ClientSize = new Size(210, 150); this.FormBorderStyle = FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
// Measure the width of the property name. // Measure the width of the property name.
Size keySize = TextRenderer.MeasureText(propertyName, this.Font); Size keySize = TextRenderer.MeasureText(propertyName, this.Font);
@ -27,14 +31,13 @@ namespace Better_NCP_Editor
} }
else else
{ {
inputWidth = TextRenderer.MeasureText(currentValue, this.Font).Width + 20; inputWidth = TextRenderer.MeasureText(currentValue, this.Font).Width + 40;
} }
// Determine the desired width: the maximum of the key width and input width, plus extra padding. // Determine the desired width: the maximum of the key width and input width, plus extra padding.
int desiredWidth = Math.Max(keySize.Width, inputWidth) + 40; int desiredWidth = Math.Max(keySize.Width, inputWidth) + 40;
// Set the form's client size (you can also set MinimumSize here if needed).
this.ClientSize = new Size(desiredWidth, 120); this.ClientSize = new Size(desiredWidth, 120);
this.Text = $"Edit {propertyName}"; this.Text = $"Edit {propertyName}";
this.StartPosition = FormStartPosition.CenterParent; this.StartPosition = FormStartPosition.CenterParent;
@ -47,32 +50,79 @@ namespace Better_NCP_Editor
}; };
this.Controls.Add(lblProperty); this.Controls.Add(lblProperty);
// Create the input control with width based on desiredWidth minus margins.
int controlWidth = desiredWidth - 20; int controlWidth = desiredWidth - 20;
if (valueType == typeof(bool))
// Check if we need to use a special ComboBox (for "ShortName" in "Wear items").
if (comboItems != null)
{ {
ComboBox combo = new ComboBox() ComboBox combo = new ComboBox()
{ {
Location = new Point(10, 40), Location = new Point(10, 40),
Width = controlWidth, Width = controlWidth, // initial width based on desiredWidth minus margins
DropDownStyle = ComboBoxStyle.DropDownList DropDownStyle = ComboBoxStyle.DropDownList
}; };
combo.Items.Add("true");
combo.Items.Add("false"); int maxWidth = 0;
combo.SelectedItem = currentValue; foreach (var key in comboItems)
{
combo.Items.Add(key);
// Measure each item's width using the ComboBox's font.
Size itemSize = TextRenderer.MeasureText(key, combo.Font);
if (itemSize.Width > maxWidth)
maxWidth = itemSize.Width;
}
// Optionally add some extra padding.
int paddedWidth = maxWidth + 20;
// Set the DropDownWidth to ensure items are fully visible.
combo.DropDownWidth = paddedWidth;
// Optionally, you can also adjust the combo's Width if you want it to match the dropdown width.
if (combo.Width < paddedWidth)
combo.Width = paddedWidth;
// Set selected item to currentValue if found, otherwise select the first item.
if (comboItems.Contains(currentValue))
{
combo.SelectedItem = currentValue;
}
else if (combo.Items.Count > 0)
{
combo.SelectedIndex = 0;
}
inputControl = combo; inputControl = combo;
this.Controls.Add(combo); this.Controls.Add(combo);
} }
else else
{ {
TextBox txtBox = new TextBox() // Create the input control normally.
if (valueType == typeof(bool))
{ {
Location = new Point(10, 40), ComboBox combo = new ComboBox()
Width = controlWidth, {
Text = currentValue Location = new Point(10, 40),
}; Width = controlWidth,
inputControl = txtBox; DropDownStyle = ComboBoxStyle.DropDownList
this.Controls.Add(txtBox); };
combo.Items.Add("true");
combo.Items.Add("false");
combo.SelectedItem = currentValue;
inputControl = combo;
this.Controls.Add(combo);
}
else
{
TextBox txtBox = new TextBox()
{
Location = new Point(10, 40),
Width = controlWidth,
Text = currentValue
};
inputControl = txtBox;
this.Controls.Add(txtBox);
}
} }
// OK button. // OK button.
@ -91,24 +141,31 @@ namespace Better_NCP_Editor
Button btnCancel = new Button() Button btnCancel = new Button()
{ {
Text = "Cancel", Text = "Cancel",
Location = new Point(100, 80), // Positioned to the right of OK. Location = new Point(100, 80),
Size = new Size(80, 25), Size = new Size(80, 25),
DialogResult = DialogResult.Cancel, DialogResult = DialogResult.Cancel,
Anchor = AnchorStyles.Left | AnchorStyles.Bottom Anchor = AnchorStyles.Left | AnchorStyles.Bottom
}; };
this.Controls.Add(btnCancel); this.Controls.Add(btnCancel);
this.AcceptButton = btnOk;
this.CancelButton = btnCancel;
} }
private void BtnOk_Click(object sender, EventArgs e) private void BtnOk_Click(object sender, EventArgs e)
{ {
// Determine new value based on input control. // If the input control is the special ComboBox, return the dictionary value.
if (inputControl is ComboBox combo) if (inputControl is ComboBox combo && combo.DropDownStyle == ComboBoxStyle.DropDownList &&
combo.Items.Count > 0)
{ {
NewValue = combo.SelectedItem.ToString() == "true"; NewValue = combo.SelectedItem.ToString();
}
else if (inputControl is ComboBox comboBool)
{
NewValue = comboBool.SelectedItem.ToString() == "true";
} }
else if (inputControl is TextBox txt) else if (inputControl is TextBox txt)
{ {
// Try to parse an integer; if it fails, treat it as a string.
if (int.TryParse(txt.Text, out int intValue)) if (int.TryParse(txt.Text, out int intValue))
NewValue = intValue; NewValue = intValue;
else else

View File

@ -43,9 +43,10 @@
// //
// btn_load // btn_load
// //
btn_load.Location = new Point(17, 12); btn_load.Location = new Point(15, 9);
btn_load.Margin = new Padding(3, 2, 3, 2);
btn_load.Name = "btn_load"; btn_load.Name = "btn_load";
btn_load.Size = new Size(135, 29); btn_load.Size = new Size(118, 22);
btn_load.TabIndex = 0; btn_load.TabIndex = 0;
btn_load.Text = "Load Directory"; btn_load.Text = "Load Directory";
btn_load.UseVisualStyleBackColor = true; btn_load.UseVisualStyleBackColor = true;
@ -54,9 +55,10 @@
// btn_save // btn_save
// //
btn_save.Enabled = false; btn_save.Enabled = false;
btn_save.Location = new Point(214, 12); btn_save.Location = new Point(187, 9);
btn_save.Margin = new Padding(3, 2, 3, 2);
btn_save.Name = "btn_save"; btn_save.Name = "btn_save";
btn_save.Size = new Size(93, 29); btn_save.Size = new Size(81, 22);
btn_save.TabIndex = 1; btn_save.TabIndex = 1;
btn_save.Text = "Save File"; btn_save.Text = "Save File";
btn_save.UseVisualStyleBackColor = true; btn_save.UseVisualStyleBackColor = true;
@ -64,24 +66,27 @@
// //
// dirTreeView // dirTreeView
// //
dirTreeView.Location = new Point(17, 53); dirTreeView.Location = new Point(15, 40);
dirTreeView.Margin = new Padding(3, 2, 3, 2);
dirTreeView.Name = "dirTreeView"; dirTreeView.Name = "dirTreeView";
dirTreeView.Size = new Size(290, 812); dirTreeView.Size = new Size(254, 610);
dirTreeView.TabIndex = 2; dirTreeView.TabIndex = 2;
// //
// entityTreeView // entityTreeView
// //
entityTreeView.Location = new Point(313, 53); entityTreeView.Location = new Point(274, 40);
entityTreeView.Margin = new Padding(3, 2, 3, 2);
entityTreeView.Name = "entityTreeView"; entityTreeView.Name = "entityTreeView";
entityTreeView.Size = new Size(843, 812); entityTreeView.Size = new Size(738, 610);
entityTreeView.TabIndex = 3; entityTreeView.TabIndex = 3;
// //
// btn_entity_del // btn_entity_del
// //
btn_entity_del.Enabled = false; btn_entity_del.Enabled = false;
btn_entity_del.Location = new Point(366, 12); btn_entity_del.Location = new Point(320, 9);
btn_entity_del.Margin = new Padding(3, 2, 3, 2);
btn_entity_del.Name = "btn_entity_del"; btn_entity_del.Name = "btn_entity_del";
btn_entity_del.Size = new Size(47, 29); btn_entity_del.Size = new Size(41, 22);
btn_entity_del.TabIndex = 8; btn_entity_del.TabIndex = 8;
btn_entity_del.Text = "Del"; btn_entity_del.Text = "Del";
btn_entity_del.UseVisualStyleBackColor = true; btn_entity_del.UseVisualStyleBackColor = true;
@ -90,9 +95,10 @@
// btn_entity_add // btn_entity_add
// //
btn_entity_add.Enabled = false; btn_entity_add.Enabled = false;
btn_entity_add.Location = new Point(313, 12); btn_entity_add.Location = new Point(274, 9);
btn_entity_add.Margin = new Padding(3, 2, 3, 2);
btn_entity_add.Name = "btn_entity_add"; btn_entity_add.Name = "btn_entity_add";
btn_entity_add.Size = new Size(46, 29); btn_entity_add.Size = new Size(40, 22);
btn_entity_add.TabIndex = 7; btn_entity_add.TabIndex = 7;
btn_entity_add.Text = "Add"; btn_entity_add.Text = "Add";
btn_entity_add.UseVisualStyleBackColor = true; btn_entity_add.UseVisualStyleBackColor = true;
@ -101,9 +107,10 @@
// btn_import_entityData // btn_import_entityData
// //
btn_import_entityData.Enabled = false; btn_import_entityData.Enabled = false;
btn_import_entityData.Location = new Point(493, 12); btn_import_entityData.Location = new Point(431, 9);
btn_import_entityData.Margin = new Padding(3, 2, 3, 2);
btn_import_entityData.Name = "btn_import_entityData"; btn_import_entityData.Name = "btn_import_entityData";
btn_import_entityData.Size = new Size(70, 29); btn_import_entityData.Size = new Size(61, 22);
btn_import_entityData.TabIndex = 9; btn_import_entityData.TabIndex = 9;
btn_import_entityData.Text = "Import"; btn_import_entityData.Text = "Import";
btn_import_entityData.UseVisualStyleBackColor = true; btn_import_entityData.UseVisualStyleBackColor = true;
@ -112,9 +119,10 @@
// btn_export_entityData // btn_export_entityData
// //
btn_export_entityData.Enabled = false; btn_export_entityData.Enabled = false;
btn_export_entityData.Location = new Point(569, 12); btn_export_entityData.Location = new Point(498, 9);
btn_export_entityData.Margin = new Padding(3, 2, 3, 2);
btn_export_entityData.Name = "btn_export_entityData"; btn_export_entityData.Name = "btn_export_entityData";
btn_export_entityData.Size = new Size(66, 29); btn_export_entityData.Size = new Size(58, 22);
btn_export_entityData.TabIndex = 10; btn_export_entityData.TabIndex = 10;
btn_export_entityData.Text = "Export"; btn_export_entityData.Text = "Export";
btn_export_entityData.UseVisualStyleBackColor = true; btn_export_entityData.UseVisualStyleBackColor = true;
@ -124,17 +132,18 @@
// //
statusTextbox.BorderStyle = BorderStyle.FixedSingle; statusTextbox.BorderStyle = BorderStyle.FixedSingle;
statusTextbox.ImeMode = ImeMode.NoControl; statusTextbox.ImeMode = ImeMode.NoControl;
statusTextbox.Location = new Point(660, 14); statusTextbox.Location = new Point(578, 10);
statusTextbox.Margin = new Padding(3, 2, 3, 2);
statusTextbox.Name = "statusTextbox"; statusTextbox.Name = "statusTextbox";
statusTextbox.ReadOnly = true; statusTextbox.ReadOnly = true;
statusTextbox.Size = new Size(496, 27); statusTextbox.Size = new Size(434, 23);
statusTextbox.TabIndex = 11; statusTextbox.TabIndex = 11;
// //
// Form1 // Form1
// //
AutoScaleDimensions = new SizeF(8F, 20F); AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font; AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(1176, 881); ClientSize = new Size(1028, 661);
Controls.Add(statusTextbox); Controls.Add(statusTextbox);
Controls.Add(btn_export_entityData); Controls.Add(btn_export_entityData);
Controls.Add(btn_import_entityData); Controls.Add(btn_import_entityData);
@ -144,6 +153,7 @@
Controls.Add(dirTreeView); Controls.Add(dirTreeView);
Controls.Add(btn_save); Controls.Add(btn_save);
Controls.Add(btn_load); Controls.Add(btn_load);
Margin = new Padding(3, 2, 3, 2);
Name = "Form1"; Name = "Form1";
Text = "BetterNPC Editor V1.0"; Text = "BetterNPC Editor V1.0";
Load += Form1_Load; Load += Form1_Load;

View File

@ -18,6 +18,10 @@ namespace Better_NCP_Editor
// In your Form1 class: // In your Form1 class:
private ToolTip customToolTip = new ToolTip(); private ToolTip customToolTip = new ToolTip();
private TreeNode lastHoveredNode = null; private TreeNode lastHoveredNode = null;
private List<String> _wearItems;
private List<String> _beltItems;
private List<String> _weaponModItems;
private Dictionary<String, String> _allItems;
public Form1() public Form1()
{ {
@ -27,7 +31,7 @@ namespace Better_NCP_Editor
entityTreeView.AfterSelect += entityTreeView_AfterSelect; entityTreeView.AfterSelect += entityTreeView_AfterSelect;
// Prevent the form from shrinking below its current size. // Prevent the form from shrinking below its current size.
this.MinimumSize = new Size(1194, 928); // Set minimum allowed size this.MinimumSize = new Size(1044, 700); // Set minimum allowed size
this.FormBorderStyle = FormBorderStyle.Sizable; this.FormBorderStyle = FormBorderStyle.Sizable;
// Example layout using docking: // Example layout using docking:
@ -54,8 +58,41 @@ namespace Better_NCP_Editor
entityTreeView.MouseMove += EntityTreeView_MouseMove; entityTreeView.MouseMove += EntityTreeView_MouseMove;
entityTreeView.MouseLeave += EntityTreeView_MouseLeave; entityTreeView.MouseLeave += EntityTreeView_MouseLeave;
// Load the item database.
_wearItems = LoadItemList("wearitems.json");
_allItems = LoadItemDatabase("allitems.json");
_beltItems = LoadItemList("beltitems.json");
_weaponModItems = LoadItemList("weaponmoditems.json");
} }
private List<String> LoadItemList(String filepath)
{
try
{
return JsonListLoader.Load(filepath);
}
catch (Exception ex)
{
Console.WriteLine("Error loading JSON file: " + ex.Message);
return null;
}
}
private Dictionary<String,String> LoadItemDatabase(String filepath)
{
try
{
return JsonDictionaryLoader.Load(filepath);
}
catch (Exception ex)
{
Console.WriteLine("Error loading JSON file: " + ex.Message);
return null;
}
}
private void EntityTreeView_MouseMove(object sender, MouseEventArgs e) private void EntityTreeView_MouseMove(object sender, MouseEventArgs e)
{ {
int xOffset = 15; int xOffset = 15;
@ -252,7 +289,7 @@ namespace Better_NCP_Editor
// Enable if the selected node's Tag is a JsonArray // Enable if the selected node's Tag is a JsonArray
if (e.Node.Tag is JsonArray) if (e.Node.Tag is JsonArray)
{ {
//enableButtons = true; enableAddDelButtons = true;
enableImportExportButtons = true; enableImportExportButtons = true;
} }
// Or if the selected node is an item in an array (its parent is a JsonArray) // Or if the selected node is an item in an array (its parent is a JsonArray)
@ -261,6 +298,11 @@ namespace Better_NCP_Editor
enableAddDelButtons = true; enableAddDelButtons = true;
enableImportExportButtons = true; enableImportExportButtons = true;
} }
if (e.Node.Text.Equals("Presets", StringComparison.OrdinalIgnoreCase))
{
enableAddDelButtons = false;
}
// You can also add additional conditions for a "preset" node, e.g. by checking text: // You can also add additional conditions for a "preset" node, e.g. by checking text:
// else if (e.Node.Text.StartsWith("Preset", StringComparison.OrdinalIgnoreCase)) // else if (e.Node.Text.StartsWith("Preset", StringComparison.OrdinalIgnoreCase))
// { // {
@ -302,27 +344,67 @@ namespace Better_NCP_Editor
else if (int.TryParse(currentVal, out int i)) else if (int.TryParse(currentVal, out int i))
valueType = typeof(int); valueType = typeof(int);
using (EditValueForm editForm = new EditValueForm(propName, currentVal, valueType)) String parentNodeName = "Root";
{ String grandParentNodeName = "Root";
if (editForm.ShowDialog() == DialogResult.OK) List<String> comboList = null;
{
object newVal = editForm.NewValue;
string displayVal = newVal is bool ? newVal.ToString().ToLower() : newVal.ToString();
e.Node.Text = $"{propName}: {displayVal}";
if (e.Node.Tag is JsonNode node) if (e.Node.Parent != null)
{
parentNodeName = e.Node.Parent.Text;
if (e.Node.Parent.Parent != null)
{
grandParentNodeName = e.Node.Parent.Parent.Text;
if (propName.Equals("ShortName", StringComparison.OrdinalIgnoreCase) && grandParentNodeName.Equals("Wear items"))
{ {
JsonNode newNode = JsonValue.Create(newVal); comboList = _wearItems;
node.ReplaceWith(newNode);
e.Node.Tag = newNode;
} }
// Mark the file as modified. else if (propName.Equals("ShortName", StringComparison.OrdinalIgnoreCase) && grandParentNodeName.Equals("Belt items"))
fileModified = true; {
btn_save.Enabled = true; comboList = _beltItems;
}
} }
if (parentNodeName.Equals("Mods"))
{
comboList = _weaponModItems;
}
} }
using (EditValueForm editForm = new EditValueForm(propName, currentVal, valueType, comboList))
{
if (editForm.ShowDialog() == DialogResult.OK)
{
object newVal = editForm.NewValue;
string displayVal = newVal is bool ? newVal.ToString().ToLower() : newVal.ToString();
if (_allItems.ContainsKey(displayVal))
{
newVal = _allItems[displayVal];
displayVal = _allItems[displayVal];
}
e.Node.Text = $"{propName}: {displayVal}";
if (e.Node.Tag is JsonNode node)
{
JsonNode newNode = JsonValue.Create(newVal);
node.ReplaceWith(newNode);
e.Node.Tag = newNode;
}
// Mark the file as modified.
fileModified = true;
btn_save.Enabled = true;
}
}
} }
@ -378,55 +460,124 @@ namespace Better_NCP_Editor
TreeNode selected = entityTreeView.SelectedNode; TreeNode selected = entityTreeView.SelectedNode;
if (selected == null) if (selected == null)
{ {
MessageBox.Show("Please select a node to duplicate.", "Info", MessageBoxButtons.OK, MessageBoxIcon.Information); MessageBox.Show("Please select a node to add a new item.", "Info", MessageBoxButtons.OK, MessageBoxIcon.Information);
return; return;
} }
TreeNode parent = selected.Parent; JsonArray targetArray = null;
if (parent == null) bool duplicateExisting = false;
// If the selected node itself is an array node...
if (selected.Tag is JsonArray arr)
{ {
MessageBox.Show("The selected node has no parent (cannot duplicate).", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); targetArray = arr;
return; duplicateExisting = false; // We'll add a new blank node
} }
// Otherwise, if the selected node's parent is an array...
// Ensure the parent's Tag is a JsonArray. else if (selected.Parent != null && selected.Parent.Tag is JsonArray arrParent)
if (!(parent.Tag is JsonArray parentArray))
{ {
MessageBox.Show("The selected node's parent is not an array node.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); targetArray = arrParent;
return; duplicateExisting = true; // We duplicate the selected item
} }
else
// Retrieve the underlying JsonNode for the selected node.
if (!(selected.Tag is JsonNode selectedJsonNode))
{ {
MessageBox.Show("Selected node does not contain a valid JSON element.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); MessageBox.Show("The selected node is not part of an array.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return; return;
} }
// Duplicate (deep clone) the selected JsonNode. JsonNode newNode;
JsonNode duplicate = selectedJsonNode.DeepClone(); if (!duplicateExisting)
{
// We're adding a new blank node to the array.
// Use the selected node's text as the array name.
string arrayName = selected.Text.Trim();
if (arrayName.Equals("Wear items", StringComparison.OrdinalIgnoreCase))
{
var obj = new JsonObject();
obj["ShortName"] = "CHANGE ME";
obj["SkinID (0 - default)"] = 0;
newNode = obj;
}
else if (arrayName.Equals("Belt items", StringComparison.OrdinalIgnoreCase))
{
var obj = new JsonObject();
obj["ShortName"] = "CHANGE ME";
obj["Amount"] = 1;
obj["SkinID (0 - default)"] = 0;
obj["Mods"] = new JsonArray(); // Empty array
obj["Ammo"] = "";
newNode = obj;
} else if (arrayName.Equals("List of prefabs", StringComparison.OrdinalIgnoreCase))
{
var obj = new JsonObject();
obj["Chance [0.0-100.0]"] = 100.0;
obj["The path to the prefab"] = "assets/rust.ai/agents/npcplayer/humannpc/scientist/CHANGEME";
newNode = obj;
}
else if (arrayName.Equals("List of items", StringComparison.OrdinalIgnoreCase))
{
var obj = new JsonObject();
obj["ShortName"] = "CHANGEME";
obj["Minimum"] = 1;
obj["Maximum"] = 100;
obj["Chance [0.0-100.0]"] = 50.0;
obj["Is this a blueprint? [true/false]"] = false;
obj["SkinID (0 - default)"] = 0;
obj["Name (empty - default)"] = "";
newNode = obj;
}
else if (arrayName.Equals("Mods", StringComparison.OrdinalIgnoreCase))
{
newNode = JsonValue.Create("CHANGE ME");
}
else if (arrayName.Equals("Names", StringComparison.OrdinalIgnoreCase))
{
newNode = JsonValue.Create("CHANGE ME");
}
else
{
// Default: create an empty string
newNode = JsonValue.Create("CHANGE ME");
}
}
else
{
// Duplicate the selected item.
if (!(selected.Tag is JsonNode selectedJsonNode))
{
MessageBox.Show("Selected node does not contain a valid JSON element.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
newNode = selectedJsonNode.DeepClone();
}
// Add the duplicate to the parent's array. // Add the new node to the target array.
parentArray.Add(duplicate); targetArray.Add(newNode);
// Refresh only the parent's children. // Determine the node representing the array.
parent.Nodes.Clear(); // If duplicating, the array node is the selected node's parent;
PopulateTreeRecursive((JsonNode)parent.Tag, parent); // otherwise it is the selected node.
parent.ExpandAll(); // Expand the parent and all its child nodes TreeNode arrayNode = duplicateExisting ? selected.Parent : selected;
// Refresh only the children of the array node.
arrayNode.Nodes.Clear();
PopulateTreeRecursive((JsonNode)arrayNode.Tag, arrayNode);
arrayNode.ExpandAll();
fileModified = true; fileModified = true;
btn_save.Enabled = true; btn_save.Enabled = true;
// Select the newly added item (last child in the parent's node collection). // Select the newly added item (the last child).
if (parent.Nodes.Count > 0) if (arrayNode.Nodes.Count > 0)
{ {
TreeNode newSelected = parent.Nodes[parent.Nodes.Count - 1]; TreeNode newSelected = arrayNode.Nodes[arrayNode.Nodes.Count - 1];
entityTreeView.SelectedNode = newSelected; entityTreeView.SelectedNode = newSelected;
newSelected.EnsureVisible(); newSelected.EnsureVisible();
entityTreeView.Focus(); entityTreeView.Focus();
} }
} }
private void btn_entity_del_Click(object sender, EventArgs e) private void btn_entity_del_Click(object sender, EventArgs e)
{ {
TreeNode selected = entityTreeView.SelectedNode; TreeNode selected = entityTreeView.SelectedNode;

View File

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
namespace Better_NCP_Editor
{
public static class JsonDictionaryLoader
{
public static Dictionary<string, string> Load(string filePath)
{
if (!File.Exists(filePath))
throw new FileNotFoundException("The specified JSON file was not found.", filePath);
string json = File.ReadAllText(filePath);
Dictionary<string, string> dictionary = JsonSerializer.Deserialize<Dictionary<string, string>>(json);
return dictionary;
}
}
}

View File

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
namespace Better_NCP_Editor
{
public static class JsonListLoader
{
public static List<string> Load(string filePath)
{
if (!File.Exists(filePath))
throw new FileNotFoundException("The specified JSON file was not found.", filePath);
string json = File.ReadAllText(filePath);
List<string> list = JsonSerializer.Deserialize<List<string>>(json);
return list;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,3 @@
[
"MP5A4"
]

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,3 @@
[
"Holosight"
]

View File

@ -0,0 +1,113 @@
[
"Abyss Divers Suit",
"Arctic Scientist Suit",
"Barrel Costume",
"Bandana Mask",
"Baseball Cap",
"Beenie Hat",
"Blue Jumpsuit",
"Bone Armor",
"Bone Helmet",
"Boonie Hat",
"Boots",
"Bucket Helmet",
"Bunny Hat",
"Bunny Onesie",
"Burlap Gloves",
"Burlap Headwrap",
"Burlap Shirt",
"Burlap Shoes",
"Burlap Trousers",
"Candle Hat",
"Chicken Costume",
"Clatter Helmet",
"Coffee Can Helmet",
"Crate Costume",
"Diving Fins",
"Diving Mask",
"Diving Tank",
"Dracula Cape",
"Dracula Mask",
"Dragon Mask",
"Egg Suit",
"Frankenstein Mask",
"Frog Boots",
"Frontier Suit",
"Gas Mask",
"Ghost Costume",
"Gingerbread Suit",
"Hazmat Suit",
"Heavy Plate Helmet",
"Heavy Plate Jacket",
"Heavy Plate Pants",
"Heavy Scientist Suit",
"Hide Boots",
"Hide Halterneck",
"Hide Pants",
"Hide Poncho",
"Hide Skirt",
"Hide Vest",
"Hockey Mask",
"Ice Metal Chest Plate",
"Ice Metal Facemask",
"Improvised Balaclava",
"Jacket",
"Jumpsuit",
"Knights armour cuirass",
"Knights armour helmet",
"Knights armour skirt plates",
"Leather Gloves",
"Lumberjack Hoodie",
"Lumberjack Suit",
"Metal Chest Plate",
"Metal Facemask",
"Miner Hat",
"Mummy Mask",
"Mummy Suit",
"Nest Hat",
"Night Vision Goggles",
"Ninja Suit",
"Nomad Suit",
"NVGM Scientist Suit",
"Ox Mask",
"Pants",
"Party Hat",
"Prisoner Hood",
"Purple Sunglasses",
"Rabbit Mask",
"Rat Mask",
"Reindeer Antlers",
"Riot Helmet",
"Roadsign Gloves",
"Road Sign Jacket",
"Road Sign Kilt",
"Santa Beard",
"Santa Hat",
"Scarecrow Suit",
"Scarecrow Wrap",
"Scientist Suit",
"Shirt",
"Shorts",
"Snake mask",
"Snow Jacket",
"Space Suit",
"Sunglasses",
"Sunglasses Black",
"Sunglasses Camo",
"Sunglasses Red",
"Sunglasses Black2",
"Sunglasses Chrome",
"Sunglasses Gold",
"Surgeon Scrubs",
"Tactical Gloves",
"Tank Top",
"Tiger Mask",
"T-Shirt",
"Twitch Rivals Hazmat Suit",
"Waterwell NPC Jumpsuit",
"Wetsuit",
"Wolf Headdress",
"Wood Armor Helmet",
"Wood Chestplate",
"Wooden Armor Pants"
]