mirror of
https://github.com/batfasturd/BetterNPCEditor.git
synced 2025-06-06 18:31:05 +00:00
First commit.
This commit is contained in:
commit
34e44def21
400
.gitignore
vendored
Normal file
400
.gitignore
vendored
Normal file
@ -0,0 +1,400 @@
|
|||||||
|
# ---> VisualStudio
|
||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
##
|
||||||
|
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.rsuser
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Mono auto generated files
|
||||||
|
mono_crash.*
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
[Ww][Ii][Nn]32/
|
||||||
|
[Aa][Rr][Mm]/
|
||||||
|
[Aa][Rr][Mm]64/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
[Ll]og/
|
||||||
|
[Ll]ogs/
|
||||||
|
|
||||||
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
#wwwroot/
|
||||||
|
|
||||||
|
# Visual Studio 2017 auto generated files
|
||||||
|
Generated\ Files/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
# NUnit
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
nunit-*.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# Benchmark Results
|
||||||
|
BenchmarkDotNet.Artifacts/
|
||||||
|
|
||||||
|
# .NET Core
|
||||||
|
project.lock.json
|
||||||
|
project.fragment.lock.json
|
||||||
|
artifacts/
|
||||||
|
|
||||||
|
# ASP.NET Scaffolding
|
||||||
|
ScaffoldingReadMe.txt
|
||||||
|
|
||||||
|
# StyleCop
|
||||||
|
StyleCopReport.xml
|
||||||
|
|
||||||
|
# Files built by Visual Studio
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_h.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.iobj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.ipdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*_wpftmp.csproj
|
||||||
|
*.log
|
||||||
|
*.tlog
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
*.VC.db
|
||||||
|
*.VC.VC.opendb
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
|
# Visual Studio Trace Files
|
||||||
|
*.e2e
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# AxoCover is a Code Coverage Tool
|
||||||
|
.axoCover/*
|
||||||
|
!.axoCover/settings.json
|
||||||
|
|
||||||
|
# Coverlet is a free, cross platform Code Coverage Tool
|
||||||
|
coverage*.json
|
||||||
|
coverage*.xml
|
||||||
|
coverage*.info
|
||||||
|
|
||||||
|
# Visual Studio code coverage results
|
||||||
|
*.coverage
|
||||||
|
*.coveragexml
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
|
# in these scripts will be unencrypted
|
||||||
|
PublishScripts/
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# NuGet Symbol Packages
|
||||||
|
*.snupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/[Pp]ackages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/[Pp]ackages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/[Pp]ackages/repositories.config
|
||||||
|
# NuGet v3's project.json files produces more ignorable files
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
|
||||||
|
# Microsoft Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Microsoft Azure Emulator
|
||||||
|
ecf/
|
||||||
|
rcf/
|
||||||
|
|
||||||
|
# Windows Store app package directories and files
|
||||||
|
AppPackages/
|
||||||
|
BundleArtifacts/
|
||||||
|
Package.StoreAssociation.xml
|
||||||
|
_pkginfo.txt
|
||||||
|
*.appx
|
||||||
|
*.appxbundle
|
||||||
|
*.appxupload
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!?*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.jfm
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# Including strong name files can present a security risk
|
||||||
|
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||||
|
#*.snk
|
||||||
|
|
||||||
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
|
#bower_components/
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
ServiceFabricBackup/
|
||||||
|
*.rptproj.bak
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
*.ndf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
*.rptproj.rsuser
|
||||||
|
*- [Bb]ackup.rdl
|
||||||
|
*- [Bb]ackup ([0-9]).rdl
|
||||||
|
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
*.GhostDoc.xml
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||||
|
*.vbw
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
|
||||||
|
*.vbp
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
|
||||||
|
*.dsw
|
||||||
|
*.dsp
|
||||||
|
|
||||||
|
# Visual Studio 6 technical files
|
||||||
|
*.ncb
|
||||||
|
*.aps
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
|
**/*.Server/GeneratedArtifacts
|
||||||
|
**/*.Server/ModelManifest.xml
|
||||||
|
_Pvt_Extensions
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
.paket/paket.exe
|
||||||
|
paket-files/
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
.fake/
|
||||||
|
|
||||||
|
# CodeRush personal settings
|
||||||
|
.cr/personal
|
||||||
|
|
||||||
|
# Python Tools for Visual Studio (PTVS)
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Cake - Uncomment if you are using it
|
||||||
|
# tools/**
|
||||||
|
# !tools/packages.config
|
||||||
|
|
||||||
|
# Tabs Studio
|
||||||
|
*.tss
|
||||||
|
|
||||||
|
# Telerik's JustMock configuration file
|
||||||
|
*.jmconfig
|
||||||
|
|
||||||
|
# BizTalk build output
|
||||||
|
*.btp.cs
|
||||||
|
*.btm.cs
|
||||||
|
*.odx.cs
|
||||||
|
*.xsd.cs
|
||||||
|
|
||||||
|
# OpenCover UI analysis results
|
||||||
|
OpenCover/
|
||||||
|
|
||||||
|
# Azure Stream Analytics local run output
|
||||||
|
ASALocalRun/
|
||||||
|
|
||||||
|
# MSBuild Binary and Structured Log
|
||||||
|
*.binlog
|
||||||
|
|
||||||
|
# NVidia Nsight GPU debugger configuration file
|
||||||
|
*.nvuser
|
||||||
|
|
||||||
|
# MFractors (Xamarin productivity tool) working folder
|
||||||
|
.mfractor/
|
||||||
|
|
||||||
|
# Local History for Visual Studio
|
||||||
|
.localhistory/
|
||||||
|
|
||||||
|
# Visual Studio History (VSHistory) files
|
||||||
|
.vshistory/
|
||||||
|
|
||||||
|
# BeatPulse healthcheck temp database
|
||||||
|
healthchecksdb
|
||||||
|
|
||||||
|
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||||
|
MigrationBackup/
|
||||||
|
|
||||||
|
# Ionide (cross platform F# VS Code tools) working folder
|
||||||
|
.ionide/
|
||||||
|
|
||||||
|
# Fody - auto-generated XML schema
|
||||||
|
FodyWeavers.xsd
|
||||||
|
|
||||||
|
# VS Code files for those working on multiple tools
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
*.code-workspace
|
||||||
|
|
||||||
|
# Local History for Visual Studio Code
|
||||||
|
.history/
|
||||||
|
|
||||||
|
# Windows Installer files from build outputs
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# JetBrains Rider
|
||||||
|
*.sln.iml
|
||||||
|
|
25
Better NCP Editor.sln
Normal file
25
Better NCP Editor.sln
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.13.35806.99 d17.13
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Better NCP Editor", "Better NCP Editor\Better NCP Editor.csproj", "{F8476FF1-BC01-4E29-8173-BAE156F04A7D}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{F8476FF1-BC01-4E29-8173-BAE156F04A7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{F8476FF1-BC01-4E29-8173-BAE156F04A7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{F8476FF1-BC01-4E29-8173-BAE156F04A7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{F8476FF1-BC01-4E29-8173-BAE156F04A7D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {1B1849C0-03C6-4E31-90CB-59B8DDE61F4D}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
12
Better NCP Editor/Better NCP Editor.csproj
Normal file
12
Better NCP Editor/Better NCP Editor.csproj
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<TargetFramework>net8.0-windows</TargetFramework>
|
||||||
|
<RootNamespace>Better_NCP_Editor</RootNamespace>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<UseWindowsForms>true</UseWindowsForms>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
354
Better NCP Editor/BetterNPCEntity.cs
Normal file
354
Better NCP Editor/BetterNPCEntity.cs
Normal file
@ -0,0 +1,354 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Better_NCP_Editor
|
||||||
|
{
|
||||||
|
public class BetterNPCEntity
|
||||||
|
{
|
||||||
|
// In BetterNPCEntity class
|
||||||
|
public BetterNPCEntity()
|
||||||
|
{
|
||||||
|
Enabled = false;
|
||||||
|
MonumentSize = "";
|
||||||
|
RemoveOtherNPCs = false;
|
||||||
|
Presets = new List<Preset>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonPropertyName("Enabled? [true/false]")]
|
||||||
|
public bool Enabled { get; set; }
|
||||||
|
|
||||||
|
// Marked as nullable since some JSON files may not include this key.
|
||||||
|
[JsonPropertyName("The size of the monument")]
|
||||||
|
public string? MonumentSize { get; set; }
|
||||||
|
|
||||||
|
// Marked as nullable so a missing key won’t cause issues.
|
||||||
|
[JsonPropertyName("Remove other NPCs? [true/false]")]
|
||||||
|
public bool? RemoveOtherNPCs { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Presets")]
|
||||||
|
public List<Preset> Presets { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Preset
|
||||||
|
{
|
||||||
|
// In Preset class
|
||||||
|
public Preset()
|
||||||
|
{
|
||||||
|
Enabled = true;
|
||||||
|
MinimumNumbersDay = 1;
|
||||||
|
MaximumNumbersDay = 1;
|
||||||
|
MinimumNumbersNight = 1;
|
||||||
|
MaximumNumbersNight = 1;
|
||||||
|
NPCSetting = new NPCSetting();
|
||||||
|
Economics = new EconomicsInfo();
|
||||||
|
AppearanceType = 0;
|
||||||
|
OwnLocations = new List<string>();
|
||||||
|
ReturnToAppearance = false;
|
||||||
|
NavigationGridType = 0;
|
||||||
|
CratePath = "";
|
||||||
|
LootTableType = 0;
|
||||||
|
PrefabLootTable = new PrefabLootTable();
|
||||||
|
OwnLootTable = new OwnLootTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonPropertyName("Enabled? [true/false]")]
|
||||||
|
public bool Enabled { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Minimum numbers - Day")]
|
||||||
|
public int MinimumNumbersDay { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Maximum numbers - Day")]
|
||||||
|
public int MaximumNumbersDay { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Minimum numbers - Night")]
|
||||||
|
public int MinimumNumbersNight { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Maximum numbers - Night")]
|
||||||
|
public int MaximumNumbersNight { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("NPCs setting")]
|
||||||
|
public NPCSetting NPCSetting { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("The amount of economics that is given for killing the NPC")]
|
||||||
|
public EconomicsInfo Economics { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Type of appearance (0 - random; 1 - own list) (not used for Road and Biome)")]
|
||||||
|
public int AppearanceType { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Own list of locations (not used for Road and Biome)")]
|
||||||
|
public List<string> OwnLocations { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("If the NPC ends up below ocean sea level, should the NPC return to it's place of appearance? [true/false]")]
|
||||||
|
public bool ReturnToAppearance { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Type of navigation grid (0 - used mainly on the island, 1 - used mainly under water or under land, as well as outside the map, can be used on some monuments)")]
|
||||||
|
public int NavigationGridType { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("The path to the crate that appears at the place of death (empty - not used)")]
|
||||||
|
public string CratePath { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Which loot table should the plugin use? (0 - default; 1 - own; 2 - AlphaLoot; 3 - CustomLoot; 4 - loot table of the Rust objects; 5 - combine the 1 and 4 methods)")]
|
||||||
|
public int LootTableType { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Loot table from prefabs (if the loot table type is 4 or 5)")]
|
||||||
|
public PrefabLootTable PrefabLootTable { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Own loot table (if the loot table type is 1 or 5)")]
|
||||||
|
public OwnLootTable OwnLootTable { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class NPCSetting
|
||||||
|
{
|
||||||
|
// In NPCSetting class
|
||||||
|
public NPCSetting()
|
||||||
|
{
|
||||||
|
Names = new List<string>();
|
||||||
|
Health = 100.0;
|
||||||
|
RoamRange = 0.0;
|
||||||
|
ChaseRange = 0.0;
|
||||||
|
AttackRangeMultiplier = 1.0;
|
||||||
|
SenseRange = 0.0;
|
||||||
|
TargetMemoryDuration = 0.0;
|
||||||
|
ScaleDamage = 1.0;
|
||||||
|
AimConeScale = 1.0;
|
||||||
|
DetectInVisionCone = false;
|
||||||
|
VisionCone = 0.0;
|
||||||
|
Speed = 0.0;
|
||||||
|
MinAppearanceTime = 0.0;
|
||||||
|
MaxAppearanceTime = 0.0;
|
||||||
|
DisableRadioEffects = false;
|
||||||
|
IsStationary = false;
|
||||||
|
RemoveCorpse = false;
|
||||||
|
WearItems = new List<WearItem>();
|
||||||
|
BeltItems = new List<BeltItem>();
|
||||||
|
Kits = new List<object>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonPropertyName("Names")]
|
||||||
|
public List<string> Names { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Health")]
|
||||||
|
public double Health { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Roam Range")]
|
||||||
|
public double RoamRange { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Chase Range")]
|
||||||
|
public double ChaseRange { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Attack Range Multiplier")]
|
||||||
|
public double AttackRangeMultiplier { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Sense Range")]
|
||||||
|
public double SenseRange { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Target Memory Duration [sec.]")]
|
||||||
|
public double TargetMemoryDuration { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Scale damage")]
|
||||||
|
public double ScaleDamage { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Aim Cone Scale")]
|
||||||
|
public double AimConeScale { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Detect the target only in the NPC's viewing vision cone? [true/false]")]
|
||||||
|
public bool DetectInVisionCone { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Vision Cone")]
|
||||||
|
public double VisionCone { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Speed")]
|
||||||
|
public double Speed { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Minimum time of appearance after death (not used for Events) [sec.]")]
|
||||||
|
public double MinAppearanceTime { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Maximum time of appearance after death (not used for Events) [sec.]")]
|
||||||
|
public double MaxAppearanceTime { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Disable radio effects? [true/false]")]
|
||||||
|
public bool DisableRadioEffects { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Is this a stationary NPC? [true/false]")]
|
||||||
|
public bool IsStationary { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Remove a corpse after death? (it is recommended to use the true value to improve performance) [true/false]")]
|
||||||
|
public bool RemoveCorpse { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Wear items")]
|
||||||
|
public List<WearItem> WearItems { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Belt items")]
|
||||||
|
public List<BeltItem> BeltItems { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Kits (it is recommended to use the previous 2 settings to improve performance)")]
|
||||||
|
public List<object> Kits { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WearItem
|
||||||
|
{
|
||||||
|
// In WearItem class
|
||||||
|
public WearItem()
|
||||||
|
{
|
||||||
|
ShortName = "";
|
||||||
|
SkinID = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonPropertyName("ShortName")]
|
||||||
|
public string ShortName { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("SkinID (0 - default)")]
|
||||||
|
public long SkinID { get; set; } // Changed from int to long
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BeltItem
|
||||||
|
{
|
||||||
|
// In BeltItem class
|
||||||
|
public BeltItem()
|
||||||
|
{
|
||||||
|
ShortName = "";
|
||||||
|
Amount = 0;
|
||||||
|
SkinID = 0;
|
||||||
|
Mods = new List<string>();
|
||||||
|
Ammo = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[JsonPropertyName("ShortName")]
|
||||||
|
public string ShortName { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Amount")]
|
||||||
|
public int Amount { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("SkinID (0 - default)")]
|
||||||
|
public long SkinID { get; set; } // Changed from int to long
|
||||||
|
|
||||||
|
[JsonPropertyName("Mods")]
|
||||||
|
public List<string> Mods { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Ammo")]
|
||||||
|
public string Ammo { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LootItem
|
||||||
|
{
|
||||||
|
// In LootItem class
|
||||||
|
public LootItem()
|
||||||
|
{
|
||||||
|
ShortName = "";
|
||||||
|
Minimum = 0;
|
||||||
|
Maximum = 0;
|
||||||
|
Chance = 0.0;
|
||||||
|
IsBlueprint = false;
|
||||||
|
SkinID = 0;
|
||||||
|
Name = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonPropertyName("ShortName")]
|
||||||
|
public string ShortName { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Minimum")]
|
||||||
|
public int Minimum { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Maximum")]
|
||||||
|
public int Maximum { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Chance [0.0-100.0]")]
|
||||||
|
public double Chance { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Is this a blueprint? [true/false]")]
|
||||||
|
public bool IsBlueprint { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("SkinID (0 - default)")]
|
||||||
|
public long SkinID { get; set; } // Changed from int to long
|
||||||
|
|
||||||
|
[JsonPropertyName("Name (empty - default)")]
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class EconomicsInfo
|
||||||
|
{
|
||||||
|
// In EconomicsInfo class
|
||||||
|
public EconomicsInfo()
|
||||||
|
{
|
||||||
|
Economics = 0.0;
|
||||||
|
ServerRewards = 0;
|
||||||
|
IQEconomic = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonPropertyName("Economics")]
|
||||||
|
public double Economics { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Server Rewards (minimum 1)")]
|
||||||
|
public int ServerRewards { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("IQEconomic (minimum 1)")]
|
||||||
|
public int IQEconomic { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PrefabLootTable
|
||||||
|
{
|
||||||
|
// In PrefabLootTable class
|
||||||
|
public PrefabLootTable()
|
||||||
|
{
|
||||||
|
MinPrefabs = 0;
|
||||||
|
MaxPrefabs = 0;
|
||||||
|
UseMinMax = false;
|
||||||
|
Prefabs = new List<Prefab>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[JsonPropertyName("Minimum numbers of prefabs")]
|
||||||
|
public int MinPrefabs { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Maximum numbers of prefabs")]
|
||||||
|
public int MaxPrefabs { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Use minimum and maximum values? [true/false]")]
|
||||||
|
public bool UseMinMax { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("List of prefabs")]
|
||||||
|
public List<Prefab> Prefabs { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Prefab
|
||||||
|
{
|
||||||
|
// In Prefab class
|
||||||
|
public Prefab()
|
||||||
|
{
|
||||||
|
Chance = 0.0;
|
||||||
|
Path = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonPropertyName("Chance [0.0-100.0]")]
|
||||||
|
public double Chance { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("The path to the prefab")]
|
||||||
|
public string Path { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OwnLootTable
|
||||||
|
{
|
||||||
|
// In OwnLootTable class
|
||||||
|
public OwnLootTable()
|
||||||
|
{
|
||||||
|
MinItems = 0;
|
||||||
|
MaxItems = 0;
|
||||||
|
UseMinMax = false;
|
||||||
|
Items = new List<LootItem>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonPropertyName("Minimum numbers of items")]
|
||||||
|
public int MinItems { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Maximum numbers of items")]
|
||||||
|
public int MaxItems { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Use minimum and maximum values? [true/false]")]
|
||||||
|
public bool UseMinMax { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("List of items")]
|
||||||
|
public List<LootItem> Items { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
97
Better NCP Editor/EditValueForm.cs
Normal file
97
Better NCP Editor/EditValueForm.cs
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace Better_NCP_Editor
|
||||||
|
{
|
||||||
|
public partial class EditValueForm : Form
|
||||||
|
{
|
||||||
|
public object NewValue { get; private set; }
|
||||||
|
private Control inputControl;
|
||||||
|
|
||||||
|
public EditValueForm(string propertyName, string currentValue, Type valueType)
|
||||||
|
{
|
||||||
|
// Set form properties.
|
||||||
|
this.Text = $"Edit {propertyName}";
|
||||||
|
this.StartPosition = FormStartPosition.CenterParent;
|
||||||
|
this.ClientSize = new Size(250, 120);
|
||||||
|
|
||||||
|
// Label for the property.
|
||||||
|
Label lblProperty = new Label()
|
||||||
|
{
|
||||||
|
Text = propertyName,
|
||||||
|
Location = new Point(10, 10),
|
||||||
|
AutoSize = true
|
||||||
|
};
|
||||||
|
this.Controls.Add(lblProperty);
|
||||||
|
|
||||||
|
// Create the appropriate input control.
|
||||||
|
if (valueType == typeof(bool))
|
||||||
|
{
|
||||||
|
ComboBox combo = new ComboBox()
|
||||||
|
{
|
||||||
|
Location = new Point(10, 40),
|
||||||
|
Width = 200,
|
||||||
|
DropDownStyle = ComboBoxStyle.DropDownList
|
||||||
|
};
|
||||||
|
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 = 200,
|
||||||
|
Text = currentValue
|
||||||
|
};
|
||||||
|
inputControl = txtBox;
|
||||||
|
this.Controls.Add(txtBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
// OK button.
|
||||||
|
Button btnOk = new Button()
|
||||||
|
{
|
||||||
|
Text = "OK",
|
||||||
|
Location = new Point(10, 80),
|
||||||
|
DialogResult = DialogResult.OK
|
||||||
|
};
|
||||||
|
btnOk.Click += BtnOk_Click;
|
||||||
|
this.Controls.Add(btnOk);
|
||||||
|
|
||||||
|
// Cancel button.
|
||||||
|
Button btnCancel = new Button()
|
||||||
|
{
|
||||||
|
Text = "Cancel",
|
||||||
|
Location = new Point(120, 80),
|
||||||
|
DialogResult = DialogResult.Cancel
|
||||||
|
};
|
||||||
|
this.Controls.Add(btnCancel);
|
||||||
|
|
||||||
|
this.AcceptButton = btnOk;
|
||||||
|
this.CancelButton = btnCancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BtnOk_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
// Determine new value based on input control.
|
||||||
|
if (inputControl is ComboBox combo)
|
||||||
|
{
|
||||||
|
NewValue = combo.SelectedItem.ToString() == "true";
|
||||||
|
}
|
||||||
|
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))
|
||||||
|
NewValue = intValue;
|
||||||
|
else
|
||||||
|
NewValue = txt.Text;
|
||||||
|
}
|
||||||
|
this.DialogResult = DialogResult.OK;
|
||||||
|
this.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
120
Better NCP Editor/EditValueForm.resx
Normal file
120
Better NCP Editor/EditValueForm.resx
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
</root>
|
166
Better NCP Editor/Form1.Designer.cs
generated
Normal file
166
Better NCP Editor/Form1.Designer.cs
generated
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
namespace Better_NCP_Editor
|
||||||
|
{
|
||||||
|
partial class Form1
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing && (components != null))
|
||||||
|
{
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region Windows Form Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
btn_load = new Button();
|
||||||
|
btn_save = new Button();
|
||||||
|
dirTreeView = new TreeView();
|
||||||
|
entityTreeView = new TreeView();
|
||||||
|
btn_entity_del = new Button();
|
||||||
|
btn_entity_add = new Button();
|
||||||
|
btn_import_entityData = new Button();
|
||||||
|
btn_export_entityData = new Button();
|
||||||
|
statusTextbox = new TextBox();
|
||||||
|
SuspendLayout();
|
||||||
|
//
|
||||||
|
// btn_load
|
||||||
|
//
|
||||||
|
btn_load.Location = new Point(17, 12);
|
||||||
|
btn_load.Name = "btn_load";
|
||||||
|
btn_load.Size = new Size(135, 29);
|
||||||
|
btn_load.TabIndex = 0;
|
||||||
|
btn_load.Text = "Load Directory";
|
||||||
|
btn_load.UseVisualStyleBackColor = true;
|
||||||
|
btn_load.Click += btn_load_Click;
|
||||||
|
//
|
||||||
|
// btn_save
|
||||||
|
//
|
||||||
|
btn_save.Enabled = false;
|
||||||
|
btn_save.Location = new Point(214, 12);
|
||||||
|
btn_save.Name = "btn_save";
|
||||||
|
btn_save.Size = new Size(93, 29);
|
||||||
|
btn_save.TabIndex = 1;
|
||||||
|
btn_save.Text = "Save File";
|
||||||
|
btn_save.UseVisualStyleBackColor = true;
|
||||||
|
btn_save.Click += btn_save_Click;
|
||||||
|
//
|
||||||
|
// dirTreeView
|
||||||
|
//
|
||||||
|
dirTreeView.Location = new Point(17, 53);
|
||||||
|
dirTreeView.Name = "dirTreeView";
|
||||||
|
dirTreeView.Size = new Size(290, 812);
|
||||||
|
dirTreeView.TabIndex = 2;
|
||||||
|
//
|
||||||
|
// entityTreeView
|
||||||
|
//
|
||||||
|
entityTreeView.Location = new Point(313, 53);
|
||||||
|
entityTreeView.Name = "entityTreeView";
|
||||||
|
entityTreeView.Size = new Size(843, 812);
|
||||||
|
entityTreeView.TabIndex = 3;
|
||||||
|
//
|
||||||
|
// btn_entity_del
|
||||||
|
//
|
||||||
|
btn_entity_del.Enabled = false;
|
||||||
|
btn_entity_del.Location = new Point(366, 12);
|
||||||
|
btn_entity_del.Name = "btn_entity_del";
|
||||||
|
btn_entity_del.Size = new Size(47, 29);
|
||||||
|
btn_entity_del.TabIndex = 8;
|
||||||
|
btn_entity_del.Text = "Del";
|
||||||
|
btn_entity_del.UseVisualStyleBackColor = true;
|
||||||
|
btn_entity_del.Click += btn_entity_del_Click;
|
||||||
|
//
|
||||||
|
// btn_entity_add
|
||||||
|
//
|
||||||
|
btn_entity_add.Enabled = false;
|
||||||
|
btn_entity_add.Location = new Point(313, 12);
|
||||||
|
btn_entity_add.Name = "btn_entity_add";
|
||||||
|
btn_entity_add.Size = new Size(46, 29);
|
||||||
|
btn_entity_add.TabIndex = 7;
|
||||||
|
btn_entity_add.Text = "Add";
|
||||||
|
btn_entity_add.UseVisualStyleBackColor = true;
|
||||||
|
btn_entity_add.Click += btn_entity_add_Click;
|
||||||
|
//
|
||||||
|
// btn_import_entityData
|
||||||
|
//
|
||||||
|
btn_import_entityData.Enabled = false;
|
||||||
|
btn_import_entityData.Location = new Point(493, 12);
|
||||||
|
btn_import_entityData.Name = "btn_import_entityData";
|
||||||
|
btn_import_entityData.Size = new Size(70, 29);
|
||||||
|
btn_import_entityData.TabIndex = 9;
|
||||||
|
btn_import_entityData.Text = "Import";
|
||||||
|
btn_import_entityData.UseVisualStyleBackColor = true;
|
||||||
|
btn_import_entityData.Click += btn_import_entityData_Click;
|
||||||
|
//
|
||||||
|
// btn_export_entityData
|
||||||
|
//
|
||||||
|
btn_export_entityData.Enabled = false;
|
||||||
|
btn_export_entityData.Location = new Point(569, 12);
|
||||||
|
btn_export_entityData.Name = "btn_export_entityData";
|
||||||
|
btn_export_entityData.Size = new Size(66, 29);
|
||||||
|
btn_export_entityData.TabIndex = 10;
|
||||||
|
btn_export_entityData.Text = "Export";
|
||||||
|
btn_export_entityData.UseVisualStyleBackColor = true;
|
||||||
|
btn_export_entityData.Click += btn_export_entityData_Click;
|
||||||
|
//
|
||||||
|
// statusTextbox
|
||||||
|
//
|
||||||
|
statusTextbox.BorderStyle = BorderStyle.FixedSingle;
|
||||||
|
statusTextbox.ImeMode = ImeMode.NoControl;
|
||||||
|
statusTextbox.Location = new Point(660, 14);
|
||||||
|
statusTextbox.Name = "statusTextbox";
|
||||||
|
statusTextbox.ReadOnly = true;
|
||||||
|
statusTextbox.Size = new Size(496, 27);
|
||||||
|
statusTextbox.TabIndex = 11;
|
||||||
|
//
|
||||||
|
// Form1
|
||||||
|
//
|
||||||
|
AutoScaleDimensions = new SizeF(8F, 20F);
|
||||||
|
AutoScaleMode = AutoScaleMode.Font;
|
||||||
|
ClientSize = new Size(1176, 881);
|
||||||
|
Controls.Add(statusTextbox);
|
||||||
|
Controls.Add(btn_export_entityData);
|
||||||
|
Controls.Add(btn_import_entityData);
|
||||||
|
Controls.Add(btn_entity_del);
|
||||||
|
Controls.Add(btn_entity_add);
|
||||||
|
Controls.Add(entityTreeView);
|
||||||
|
Controls.Add(dirTreeView);
|
||||||
|
Controls.Add(btn_save);
|
||||||
|
Controls.Add(btn_load);
|
||||||
|
Name = "Form1";
|
||||||
|
Text = "BetterNPC Editor V1.0";
|
||||||
|
Load += Form1_Load;
|
||||||
|
ResumeLayout(false);
|
||||||
|
PerformLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private Button btn_load;
|
||||||
|
private Button btn_save;
|
||||||
|
private TreeView dirTreeView;
|
||||||
|
private TreeView entityTreeView;
|
||||||
|
private Button btn_entity_del;
|
||||||
|
private Button btn_entity_add;
|
||||||
|
private Button btn_import_entityData;
|
||||||
|
private Button btn_export_entityData;
|
||||||
|
private TextBox statusTextbox;
|
||||||
|
}
|
||||||
|
}
|
716
Better NCP Editor/Form1.cs
Normal file
716
Better NCP Editor/Form1.cs
Normal file
@ -0,0 +1,716 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text.Encodings.Web;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using System.Text.Json.Serialization.Metadata;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace Better_NCP_Editor
|
||||||
|
{
|
||||||
|
public partial class Form1 : Form
|
||||||
|
{
|
||||||
|
private string currentJsonFilePath;
|
||||||
|
private JsonNode currentJson;
|
||||||
|
private bool fileModified = false;
|
||||||
|
private ToolTips toolTips = new ToolTips();
|
||||||
|
|
||||||
|
// In your Form1 class:
|
||||||
|
private ToolTip customToolTip = new ToolTip();
|
||||||
|
private TreeNode lastHoveredNode = null;
|
||||||
|
|
||||||
|
public Form1()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
dirTreeView.AfterSelect += DirTreeView_AfterSelect;
|
||||||
|
entityTreeView.NodeMouseDoubleClick += entityTreeView_NodeMouseDoubleClick;
|
||||||
|
entityTreeView.AfterSelect += entityTreeView_AfterSelect;
|
||||||
|
|
||||||
|
// Prevent the form from shrinking below its current size.
|
||||||
|
this.MinimumSize = new Size(1045, 700); // Set minimum allowed size
|
||||||
|
this.FormBorderStyle = FormBorderStyle.Sizable;
|
||||||
|
|
||||||
|
// Example layout using docking:
|
||||||
|
dirTreeView.Dock = DockStyle.Left;
|
||||||
|
entityTreeView.Dock = DockStyle.Fill;
|
||||||
|
|
||||||
|
// Optionally, set a fixed width for the dirTreeView:
|
||||||
|
dirTreeView.Width = 290;
|
||||||
|
|
||||||
|
// In the designer or constructor:
|
||||||
|
dirTreeView.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left;
|
||||||
|
entityTreeView.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
|
||||||
|
entityTreeView.ShowNodeToolTips = false;
|
||||||
|
|
||||||
|
// Configure the custom tooltip.
|
||||||
|
customToolTip.AutoPopDelay = 15000; // Show for 15 seconds.
|
||||||
|
customToolTip.AutomaticDelay = 1000;
|
||||||
|
customToolTip.ShowAlways = true;
|
||||||
|
customToolTip.InitialDelay = 500;
|
||||||
|
customToolTip.ReshowDelay = 500;
|
||||||
|
customToolTip.UseAnimation = true;
|
||||||
|
|
||||||
|
// Subscribe to events.
|
||||||
|
entityTreeView.MouseMove += EntityTreeView_MouseMove;
|
||||||
|
entityTreeView.MouseLeave += EntityTreeView_MouseLeave;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
private void EntityTreeView_MouseMove(object sender, MouseEventArgs e)
|
||||||
|
{
|
||||||
|
int xOffset = 15;
|
||||||
|
int yOffset = 0;
|
||||||
|
TreeNode node = entityTreeView.GetNodeAt(e.Location);
|
||||||
|
if (node != null)
|
||||||
|
{
|
||||||
|
// Measure the text size for the node using the TreeView's font.
|
||||||
|
Size textSize = TextRenderer.MeasureText(node.Text, entityTreeView.Font);
|
||||||
|
// Construct a rectangle that represents the area occupied by the text.
|
||||||
|
Rectangle textRect = new Rectangle(node.Bounds.X, node.Bounds.Y, textSize.Width, node.Bounds.Height);
|
||||||
|
|
||||||
|
if (textRect.Contains(e.Location))
|
||||||
|
{
|
||||||
|
if (node != lastHoveredNode)
|
||||||
|
{
|
||||||
|
lastHoveredNode = node;
|
||||||
|
// Use an offset so the tooltip doesn't overlap the text.
|
||||||
|
Point offsetLocation = new Point(e.Location.X + xOffset, e.Location.Y + xOffset);
|
||||||
|
customToolTip.Show(node.ToolTipText, entityTreeView, offsetLocation, customToolTip.AutoPopDelay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
customToolTip.Hide(entityTreeView);
|
||||||
|
lastHoveredNode = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
customToolTip.Hide(entityTreeView);
|
||||||
|
lastHoveredNode = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EntityTreeView_MouseLeave(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
customToolTip.Hide(entityTreeView);
|
||||||
|
lastHoveredNode = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Form1_Load(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
// Other initialization code, if needed.
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btn_load_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
using FolderBrowserDialog folderDialog = new();
|
||||||
|
folderDialog.Description = "Select a directory containing JSON files";
|
||||||
|
folderDialog.UseDescriptionForTitle = true;
|
||||||
|
folderDialog.ShowNewFolderButton = false;
|
||||||
|
|
||||||
|
if (folderDialog.ShowDialog() == DialogResult.OK)
|
||||||
|
{
|
||||||
|
string selectedPath = folderDialog.SelectedPath;
|
||||||
|
dirTreeView.Nodes.Clear();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TreeNode rootNode = new TreeNode(Path.GetFileName(selectedPath))
|
||||||
|
{
|
||||||
|
Tag = selectedPath
|
||||||
|
};
|
||||||
|
dirTreeView.Nodes.Add(rootNode);
|
||||||
|
LoadJsonFiles(rootNode, selectedPath);
|
||||||
|
rootNode.Expand();
|
||||||
|
statusTextbox.Text = $"Loaded directory: {selectedPath}";
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBox.Show($"Error loading directory: {ex.Message}", "Error",
|
||||||
|
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadJsonFiles(TreeNode parentNode, string directoryPath)
|
||||||
|
{
|
||||||
|
foreach (string filePath in Directory.GetFiles(directoryPath, "*.json"))
|
||||||
|
{
|
||||||
|
TreeNode fileNode = new TreeNode(Path.GetFileName(filePath))
|
||||||
|
{
|
||||||
|
Tag = filePath
|
||||||
|
};
|
||||||
|
parentNode.Nodes.Add(fileNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (string subDirPath in Directory.GetDirectories(directoryPath))
|
||||||
|
{
|
||||||
|
TreeNode subDirNode = new TreeNode(Path.GetFileName(subDirPath))
|
||||||
|
{
|
||||||
|
Tag = subDirPath
|
||||||
|
};
|
||||||
|
parentNode.Nodes.Add(subDirNode);
|
||||||
|
LoadJsonFiles(subDirNode, subDirPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DirTreeView_AfterSelect(object sender, TreeViewEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Node?.Tag is string filePath &&
|
||||||
|
Path.GetExtension(filePath).Equals(".json", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
LoadJsonFile(filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadJsonFile(string jsonFilePath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
currentJsonFilePath = jsonFilePath;
|
||||||
|
string json = File.ReadAllText(jsonFilePath);
|
||||||
|
currentJson = JsonNode.Parse(json);
|
||||||
|
PopulateEntityTree(currentJson);
|
||||||
|
fileModified = false;
|
||||||
|
btn_save.Enabled = false; // No changes yet
|
||||||
|
statusTextbox.Text = $"Loaded JSON file: {jsonFilePath}";
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBox.Show($"Error loading JSON file: {ex.Message}", "Error",
|
||||||
|
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Populates the entityTreeView recursively from a JsonNode.
|
||||||
|
private void PopulateEntityTree(JsonNode json)
|
||||||
|
{
|
||||||
|
entityTreeView.Nodes.Clear();
|
||||||
|
// Use the filename of the current JSON file if available; otherwise default to "JSON".
|
||||||
|
string fileName = string.IsNullOrEmpty(currentJsonFilePath) ? "JSON" : Path.GetFileName(currentJsonFilePath);
|
||||||
|
TreeNode root = new TreeNode(fileName)
|
||||||
|
{
|
||||||
|
Tag = json
|
||||||
|
};
|
||||||
|
entityTreeView.Nodes.Add(root);
|
||||||
|
PopulateTreeRecursive(json, root);
|
||||||
|
root.ExpandAll();
|
||||||
|
entityTreeView.TopNode = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PopulateTreeRecursive(JsonNode node, TreeNode treeNode)
|
||||||
|
{
|
||||||
|
if (node is JsonObject obj)
|
||||||
|
{
|
||||||
|
foreach (var kvp in obj)
|
||||||
|
{
|
||||||
|
TreeNode child;
|
||||||
|
if (kvp.Value is JsonValue)
|
||||||
|
{
|
||||||
|
child = new TreeNode($"{kvp.Key}: {kvp.Value}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
child = new TreeNode(kvp.Key);
|
||||||
|
}
|
||||||
|
child.ToolTipText = toolTips.tips.ContainsKey(kvp.Key) ? toolTips.tips[kvp.Key] : "";
|
||||||
|
child.Tag = kvp.Value;
|
||||||
|
treeNode.Nodes.Add(child);
|
||||||
|
PopulateTreeRecursive(kvp.Value, child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (node is JsonArray arr)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < arr.Count; i++)
|
||||||
|
{
|
||||||
|
JsonNode item = arr[i];
|
||||||
|
TreeNode child;
|
||||||
|
if (item is JsonValue)
|
||||||
|
{
|
||||||
|
child = new TreeNode($"[{i}]: {item}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
child = new TreeNode($"[{i}]");
|
||||||
|
}
|
||||||
|
child.Tag = item;
|
||||||
|
treeNode.Nodes.Add(child);
|
||||||
|
PopulateTreeRecursive(item, child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void entityTreeView_AfterSelect(object sender, TreeViewEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Node != null)
|
||||||
|
{
|
||||||
|
bool enableAddDelButtons = false;
|
||||||
|
bool enableImportExportButtons = false;
|
||||||
|
|
||||||
|
// Enable if the selected node's Tag is a JsonArray
|
||||||
|
if (e.Node.Tag is JsonArray)
|
||||||
|
{
|
||||||
|
//enableButtons = true;
|
||||||
|
enableImportExportButtons = true;
|
||||||
|
}
|
||||||
|
// Or if the selected node is an item in an array (its parent is a JsonArray)
|
||||||
|
else if (e.Node.Parent != null && e.Node.Parent.Tag is JsonArray)
|
||||||
|
{
|
||||||
|
enableAddDelButtons = true;
|
||||||
|
enableImportExportButtons = true;
|
||||||
|
}
|
||||||
|
// You can also add additional conditions for a "preset" node, e.g. by checking text:
|
||||||
|
// else if (e.Node.Text.StartsWith("Preset", StringComparison.OrdinalIgnoreCase))
|
||||||
|
// {
|
||||||
|
// enableButtons = true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
btn_entity_add.Enabled = enableAddDelButtons;
|
||||||
|
btn_entity_del.Enabled = enableAddDelButtons;
|
||||||
|
btn_export_entityData.Enabled = enableImportExportButtons;
|
||||||
|
btn_import_entityData.Enabled = enableImportExportButtons;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
btn_entity_add.Enabled = false;
|
||||||
|
btn_entity_del.Enabled = false;
|
||||||
|
btn_export_entityData.Enabled = false;
|
||||||
|
btn_import_entityData.Enabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// When a tree node is double-clicked, open an edit window for that value.
|
||||||
|
private void entityTreeView_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Node == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Expect node text in the form "Property: Value"
|
||||||
|
string[] parts = e.Node.Text.Split(new[] { ':' }, 2);
|
||||||
|
if (parts.Length != 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
string propName = parts[0].Trim();
|
||||||
|
string currentVal = parts[1].Trim();
|
||||||
|
|
||||||
|
// Use simple heuristics to determine the type.
|
||||||
|
Type valueType = typeof(string);
|
||||||
|
if (bool.TryParse(currentVal, out bool b))
|
||||||
|
valueType = typeof(bool);
|
||||||
|
else if (int.TryParse(currentVal, out int i))
|
||||||
|
valueType = typeof(int);
|
||||||
|
|
||||||
|
using (EditValueForm editForm = new EditValueForm(propName, currentVal, valueType))
|
||||||
|
{
|
||||||
|
if (editForm.ShowDialog() == DialogResult.OK)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
JsonNode newNode = JsonValue.Create(newVal);
|
||||||
|
node.ReplaceWith(newNode);
|
||||||
|
e.Node.Tag = newNode;
|
||||||
|
}
|
||||||
|
// Mark the file as modified.
|
||||||
|
fileModified = true;
|
||||||
|
btn_save.Enabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void btn_save_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(currentJsonFilePath) || currentJson == null)
|
||||||
|
{
|
||||||
|
MessageBox.Show("No JSON file loaded.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var options = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
WriteIndented = true,
|
||||||
|
TypeInfoResolver = new DefaultJsonTypeInfoResolver(),
|
||||||
|
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||||
|
};
|
||||||
|
|
||||||
|
string newJson = currentJson.ToJsonString(options);
|
||||||
|
File.WriteAllText(currentJsonFilePath, newJson);
|
||||||
|
statusTextbox.Text = "JSON file saved successfully.";
|
||||||
|
//MessageBox.Show("JSON file saved successfully.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
|
||||||
|
// Reset the modified flag.
|
||||||
|
fileModified = false;
|
||||||
|
btn_save.Enabled = false;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBox.Show($"Error saving JSON file: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private TreeNode FindNodeByFullPath(TreeNodeCollection nodes, string fullPath)
|
||||||
|
{
|
||||||
|
foreach (TreeNode node in nodes)
|
||||||
|
{
|
||||||
|
if (node.FullPath == fullPath)
|
||||||
|
return node;
|
||||||
|
TreeNode found = FindNodeByFullPath(node.Nodes, fullPath);
|
||||||
|
if (found != null)
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btn_entity_add_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
TreeNode selected = entityTreeView.SelectedNode;
|
||||||
|
if (selected == null)
|
||||||
|
{
|
||||||
|
MessageBox.Show("Please select a node to duplicate.", "Info", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeNode parent = selected.Parent;
|
||||||
|
if (parent == null)
|
||||||
|
{
|
||||||
|
MessageBox.Show("The selected node has no parent (cannot duplicate).", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the parent's Tag is a JsonArray.
|
||||||
|
if (!(parent.Tag is JsonArray parentArray))
|
||||||
|
{
|
||||||
|
MessageBox.Show("The selected node's parent is not an array node.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duplicate (deep clone) the selected JsonNode.
|
||||||
|
JsonNode duplicate = selectedJsonNode.DeepClone();
|
||||||
|
|
||||||
|
// Add the duplicate to the parent's array.
|
||||||
|
parentArray.Add(duplicate);
|
||||||
|
|
||||||
|
// Refresh only the parent's children.
|
||||||
|
parent.Nodes.Clear();
|
||||||
|
PopulateTreeRecursive((JsonNode)parent.Tag, parent);
|
||||||
|
parent.ExpandAll(); // Expand the parent and all its child nodes
|
||||||
|
|
||||||
|
fileModified = true;
|
||||||
|
btn_save.Enabled = true;
|
||||||
|
|
||||||
|
// Select the newly added item (last child in the parent's node collection).
|
||||||
|
if (parent.Nodes.Count > 0)
|
||||||
|
{
|
||||||
|
TreeNode newSelected = parent.Nodes[parent.Nodes.Count - 1];
|
||||||
|
entityTreeView.SelectedNode = newSelected;
|
||||||
|
newSelected.EnsureVisible();
|
||||||
|
entityTreeView.Focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btn_entity_del_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
TreeNode selected = entityTreeView.SelectedNode;
|
||||||
|
if (selected == null)
|
||||||
|
{
|
||||||
|
MessageBox.Show("Please select a node to delete.", "Info", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeNode parent = selected.Parent;
|
||||||
|
if (parent == null)
|
||||||
|
{
|
||||||
|
MessageBox.Show("The selected node has no parent (cannot delete).", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the parent's Tag is a JsonArray.
|
||||||
|
if (!(parent.Tag is JsonArray parentArray))
|
||||||
|
{
|
||||||
|
MessageBox.Show("The selected node's parent is not an array node.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the index of the selected node (assumed to be in the same order as in the array).
|
||||||
|
int indexToRemove = selected.Index;
|
||||||
|
if (indexToRemove < 0 || indexToRemove >= parentArray.Count)
|
||||||
|
{
|
||||||
|
MessageBox.Show("Selected node index is invalid.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is the last item in the array, ask for confirmation.
|
||||||
|
if (parentArray.Count == 1)
|
||||||
|
{
|
||||||
|
DialogResult confirm = MessageBox.Show("Are you sure you want to delete the last item in the array?",
|
||||||
|
"Confirm Deletion", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
|
||||||
|
if (confirm != DialogResult.Yes)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the element from the underlying JsonArray.
|
||||||
|
parentArray.RemoveAt(indexToRemove);
|
||||||
|
|
||||||
|
// Refresh only the parent's children.
|
||||||
|
parent.Nodes.Clear();
|
||||||
|
PopulateTreeRecursive((JsonNode)parent.Tag, parent);
|
||||||
|
parent.ExpandAll();
|
||||||
|
|
||||||
|
fileModified = true;
|
||||||
|
btn_save.Enabled = true;
|
||||||
|
|
||||||
|
// Select the newly last item in the array; if none, select the parent.
|
||||||
|
if (parent.Nodes.Count > 0)
|
||||||
|
{
|
||||||
|
TreeNode newSelected = parent.Nodes[parent.Nodes.Count - 1];
|
||||||
|
entityTreeView.SelectedNode = newSelected;
|
||||||
|
newSelected.EnsureVisible();
|
||||||
|
entityTreeView.Focus();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
entityTreeView.SelectedNode = parent;
|
||||||
|
parent.EnsureVisible();
|
||||||
|
entityTreeView.Focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare two JsonNode structures (objects, arrays, or values) for basic structural compatibility.
|
||||||
|
private bool CompareJsonStructure(JsonNode a, JsonNode b)
|
||||||
|
{
|
||||||
|
if (a == null || b == null)
|
||||||
|
return a == b;
|
||||||
|
|
||||||
|
// Ensure both nodes are of the same concrete type.
|
||||||
|
if (a.GetType() != b.GetType())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (a is JsonObject objA && b is JsonObject objB)
|
||||||
|
{
|
||||||
|
// They must have the same set of keys.
|
||||||
|
if (objA.Count != objB.Count)
|
||||||
|
return false;
|
||||||
|
foreach (var kvp in objA)
|
||||||
|
{
|
||||||
|
if (!objB.ContainsKey(kvp.Key))
|
||||||
|
return false;
|
||||||
|
if (!CompareJsonStructure(kvp.Value, objB[kvp.Key]))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (a is JsonArray arrA && b is JsonArray arrB)
|
||||||
|
{
|
||||||
|
// If both arrays are empty, we consider them structurally compatible.
|
||||||
|
if (arrA.Count == 0 && arrB.Count == 0)
|
||||||
|
return true;
|
||||||
|
// Otherwise, compare the structure of their first elements.
|
||||||
|
if (arrA.Count > 0 && arrB.Count > 0)
|
||||||
|
return CompareJsonStructure(arrA[0], arrB[0]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (a is JsonValue && b is JsonValue)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Compare the underlying value types.
|
||||||
|
object valA = a.GetValue<object>();
|
||||||
|
object valB = b.GetValue<object>();
|
||||||
|
return valA?.GetType() == valB?.GetType();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btn_import_entityData_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
// Ensure a node is selected.
|
||||||
|
TreeNode selected = entityTreeView.SelectedNode;
|
||||||
|
if (selected == null)
|
||||||
|
{
|
||||||
|
MessageBox.Show("Please select a node for importing data.", "Import Error", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open a file picker to select the import file.
|
||||||
|
using (OpenFileDialog openDialog = new OpenFileDialog())
|
||||||
|
{
|
||||||
|
openDialog.Filter = "JSON Files (*.json)|*.json|All Files (*.*)|*.*";
|
||||||
|
openDialog.Title = "Import JSON Data";
|
||||||
|
|
||||||
|
if (openDialog.ShowDialog() == DialogResult.OK)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string importText = File.ReadAllText(openDialog.FileName);
|
||||||
|
JsonNode importJson = JsonNode.Parse(importText);
|
||||||
|
|
||||||
|
// Determine if we are adding to an array or replacing a child.
|
||||||
|
// If the selected node's Tag is a JsonArray, we add to it.
|
||||||
|
// If the selected node's parent is a JsonArray, we replace the selected node.
|
||||||
|
JsonArray parentArray = null;
|
||||||
|
bool isAddingNewItem = false;
|
||||||
|
if (selected.Tag is JsonArray)
|
||||||
|
{
|
||||||
|
parentArray = (JsonArray)selected.Tag;
|
||||||
|
isAddingNewItem = true;
|
||||||
|
}
|
||||||
|
else if (selected.Parent != null && selected.Parent.Tag is JsonArray)
|
||||||
|
{
|
||||||
|
parentArray = (JsonArray)selected.Parent.Tag;
|
||||||
|
isAddingNewItem = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MessageBox.Show("The selected node is not part of an array. Import operation cancelled.",
|
||||||
|
"Import Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the target node for structure comparison.
|
||||||
|
// If adding a new item, compare the structure of the array's first item (if any).
|
||||||
|
// Otherwise, compare the selected node's structure.
|
||||||
|
JsonNode targetStructure = null;
|
||||||
|
if (isAddingNewItem)
|
||||||
|
{
|
||||||
|
if (parentArray.Count > 0)
|
||||||
|
targetStructure = parentArray[0];
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If the array is empty, we assume it's acceptable.
|
||||||
|
targetStructure = importJson;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
targetStructure = (JsonNode)selected.Tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CompareJsonStructure(importJson, targetStructure))
|
||||||
|
{
|
||||||
|
MessageBox.Show("The imported JSON structure does not match the target structure.",
|
||||||
|
"Import Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileModified = true;
|
||||||
|
btn_save.Enabled = true;
|
||||||
|
|
||||||
|
if (isAddingNewItem)
|
||||||
|
{
|
||||||
|
// Add the imported JSON as a new item to the array.
|
||||||
|
parentArray.Add(importJson);
|
||||||
|
// Find the TreeNode corresponding to the array (selected node).
|
||||||
|
TreeNode arrayNode = selected;
|
||||||
|
// Refresh only this node's children.
|
||||||
|
arrayNode.Nodes.Clear();
|
||||||
|
PopulateTreeRecursive((JsonNode)arrayNode.Tag, arrayNode);
|
||||||
|
arrayNode.ExpandAll();
|
||||||
|
|
||||||
|
// Select the newly added item (last child).
|
||||||
|
if (arrayNode.Nodes.Count > 0)
|
||||||
|
{
|
||||||
|
TreeNode newNode = arrayNode.Nodes[arrayNode.Nodes.Count - 1];
|
||||||
|
entityTreeView.SelectedNode = newNode;
|
||||||
|
newNode.EnsureVisible();
|
||||||
|
entityTreeView.Focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Replace the selected node's data with the imported JSON.
|
||||||
|
JsonNode oldNode = (JsonNode)selected.Tag;
|
||||||
|
oldNode.ReplaceWith(importJson);
|
||||||
|
selected.Tag = importJson;
|
||||||
|
|
||||||
|
// Refresh the parent node.
|
||||||
|
TreeNode parentNode = selected.Parent;
|
||||||
|
parentNode.Nodes.Clear();
|
||||||
|
PopulateTreeRecursive((JsonNode)parentNode.Tag, parentNode);
|
||||||
|
parentNode.ExpandAll();
|
||||||
|
|
||||||
|
// Try to reselect the replaced item (by its index).
|
||||||
|
int index = selected.Index;
|
||||||
|
if (index >= 0 && index < parentNode.Nodes.Count)
|
||||||
|
{
|
||||||
|
TreeNode newNode = parentNode.Nodes[index];
|
||||||
|
entityTreeView.SelectedNode = newNode;
|
||||||
|
newNode.EnsureVisible();
|
||||||
|
entityTreeView.Focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBox.Show($"Error importing JSON file: {ex.Message}", "Import Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btn_export_entityData_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
// Ensure a node is selected and that it contains a JsonNode.
|
||||||
|
if (entityTreeView.SelectedNode == null || !(entityTreeView.SelectedNode.Tag is JsonNode selectedJson))
|
||||||
|
{
|
||||||
|
MessageBox.Show("Please select a node with valid JSON data to export.",
|
||||||
|
"Export Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using (SaveFileDialog saveDialog = new SaveFileDialog())
|
||||||
|
{
|
||||||
|
saveDialog.Filter = "JSON Files (*.json)|*.json|All Files (*.*)|*.*";
|
||||||
|
saveDialog.Title = "Export JSON Data";
|
||||||
|
saveDialog.FileName = "exported.json";
|
||||||
|
|
||||||
|
if (saveDialog.ShowDialog() == DialogResult.OK)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var options = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
WriteIndented = true,
|
||||||
|
TypeInfoResolver = new DefaultJsonTypeInfoResolver(),
|
||||||
|
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||||
|
};
|
||||||
|
|
||||||
|
// Export the JSON subtree starting at the selected node.
|
||||||
|
string exportJson = selectedJson.ToJsonString(options);
|
||||||
|
File.WriteAllText(saveDialog.FileName, exportJson);
|
||||||
|
MessageBox.Show("Export successful!", "Export", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBox.Show($"Error exporting JSON: {ex.Message}", "Export Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
120
Better NCP Editor/Form1.resx
Normal file
120
Better NCP Editor/Form1.resx
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
</root>
|
17
Better NCP Editor/Program.cs
Normal file
17
Better NCP Editor/Program.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
namespace Better_NCP_Editor
|
||||||
|
{
|
||||||
|
internal static class Program
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The main entry point for the application.
|
||||||
|
/// </summary>
|
||||||
|
[STAThread]
|
||||||
|
static void Main()
|
||||||
|
{
|
||||||
|
// To customize application configuration such as set high DPI settings or default font,
|
||||||
|
// see https://aka.ms/applicationconfiguration.
|
||||||
|
ApplicationConfiguration.Initialize();
|
||||||
|
Application.Run(new Form1());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
78
Better NCP Editor/ToolTips.cs
Normal file
78
Better NCP Editor/ToolTips.cs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Better_NCP_Editor
|
||||||
|
{
|
||||||
|
class ToolTips
|
||||||
|
{
|
||||||
|
public Dictionary<string, string> tips { get; private set; } = new Dictionary<string, string>();
|
||||||
|
|
||||||
|
|
||||||
|
public ToolTips()
|
||||||
|
{
|
||||||
|
// General tips
|
||||||
|
tips.Add("Enabled? [true/false]", "Enables or disables NPC appearance on the map.");
|
||||||
|
tips.Add("Remove other NPCs? [true/false]", "Removes standard NPCs within the monument area.");
|
||||||
|
tips.Add("Radius", "NPC appearance radius.");
|
||||||
|
tips.Add("Use minimum and maximum values? [true/false]", "Whether to enforce minimum/maximum limits.");
|
||||||
|
|
||||||
|
// Standard Monument tips
|
||||||
|
tips.Add("The size of the monument", "Specifies the monument's length and width for random NPC placement and standard NPC removal boundaries.");
|
||||||
|
|
||||||
|
// Custom Monument tips
|
||||||
|
tips.Add("Position", "Custom monument position on the map.");
|
||||||
|
tips.Add("Rotation", "Custom monument rotation (needed for multi-map NPC placements).");
|
||||||
|
|
||||||
|
// NPC Preset tips
|
||||||
|
tips.Add("Minimum numbers - Day", "Minimum NPCs for the day preset.");
|
||||||
|
tips.Add("Maximum numbers - Day", "Maximum NPCs for the day preset.");
|
||||||
|
tips.Add("Minimum numbers - Night", "Minimum NPCs for the night preset.");
|
||||||
|
tips.Add("Maximum numbers - Night", "Maximum NPCs for the night preset.");
|
||||||
|
tips.Add("Type of appearance (0 - random; 1 - own list) (not used for Road and Biome)", "Specifies NPC appearance type: 0 for random, 1 for custom list. Not used for roads.");
|
||||||
|
tips.Add("Own list of locations (not used for Road and Biome)", "Custom list of NPC spawn locations. Ensure the list meets the maximum NPC count. Not used for roads.");
|
||||||
|
tips.Add("Which loot table should the plugin use? (0 - default; 1 - own; 2 - AlphaLoot; 3 - CustomLoot; 4 - loot table of the Rust objects; 5 - combine the 1 and 4 methods)", "Select the NPC loot table type. Type 5 combines types 1 and 4.");
|
||||||
|
tips.Add("Loot table from prefabs (if the loot table type is 4 or 5)", "Settings for the Rust loot table. See documentation for details.");
|
||||||
|
tips.Add("Own loot table (if the loot table type is 1 or 5)", "Custom NPC loot table. See documentation for details.");
|
||||||
|
|
||||||
|
// NPC Settings tips
|
||||||
|
tips.Add("Names", "List of NPC names (chosen randomly).");
|
||||||
|
tips.Add("Health", "NPC hit points.");
|
||||||
|
tips.Add("Roam Range", "Patrol distance from the spawn point.");
|
||||||
|
tips.Add("Chase Range", "Chase distance from the spawn point.");
|
||||||
|
tips.Add("Attack Range Multiplier", "Multiplier for the NPC's weapon range.");
|
||||||
|
tips.Add("Sense Range", "Target detection radius.");
|
||||||
|
tips.Add("Target Memory Duration [sec.]", "Time (in seconds) the NPC remembers a target.");
|
||||||
|
tips.Add("Scale damage", "Damage multiplier applied by the NPC.");
|
||||||
|
tips.Add("Aim Cone Scale", "Shooting spread (default in Rust is 2; non-negative only).");
|
||||||
|
tips.Add("Detect the target only in the NPC's viewing vision cone? [true/false]", "If true, detection is limited to the NPC’s vision cone; false enables 360° detection.");
|
||||||
|
tips.Add("Vision Cone", "NPC vision cone angle (20–180°). Not used if detection is 360°.");
|
||||||
|
tips.Add("Speed", "NPC movement speed (default in Rust is 5).");
|
||||||
|
tips.Add("Minimum time of appearance after death (not used for Events) [sec.]", "Minimum delay for NPC reappearance after death (not used for events).");
|
||||||
|
tips.Add("Maximum time of appearance after death (not used for Events) [sec.]", "Maximum delay for NPC reappearance after death (not used for events).");
|
||||||
|
tips.Add("Disable radio effects? [true/false]", "Toggle radio effects.");
|
||||||
|
tips.Add("Is this a stationary NPC? [true/false]", "If true, the NPC remains stationary.");
|
||||||
|
tips.Add("Remove a corpse after death? (it is recommended to use the true value to improve performance) [true/false]", "If true, NPC corpses are removed (recommended for performance).");
|
||||||
|
tips.Add("Wear items", "List of NPC clothing and armor.");
|
||||||
|
tips.Add("Belt items", "List of quick-access items (e.g., weapons, medkits, grenades).");
|
||||||
|
tips.Add("Kits (it is recommended to use the previous 2 settings to improve performance)", "List of NPC kits (leave blank if not used).");
|
||||||
|
|
||||||
|
// Rust loot table tips
|
||||||
|
tips.Add("Minimum numbers of prefabs", "Minimum number of prefabs in the loot table.");
|
||||||
|
tips.Add("Maximum numbers of prefabs", "Maximum number of prefabs in the loot table.");
|
||||||
|
|
||||||
|
tips.Add("List of prefabs", "List of Rust object prefabs with full paths and drop chances.");
|
||||||
|
|
||||||
|
// Own loot table tips
|
||||||
|
tips.Add("Minimum numbers of items", "Minimum number of items in the loot table.");
|
||||||
|
tips.Add("Maximum numbers of items", "Maximum number of items in the loot table.");
|
||||||
|
tips.Add("List of items", "List of NPC items, including blueprints and custom items.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user