Compare commits

...

15 Commits

Author SHA1 Message Date
m0_75251201
6cb89ba439 feat: 角色展示v1.2 2025-11-18 18:45:14 +08:00
m0_75251201
8fcbdc5649 feat:场景视图添加属性编辑,添加轮廓显示 2025-11-13 16:24:49 +08:00
m0_75251201
9b91218973 Feat: 场景视图mod,UI框架更换标题 2025-11-08 14:03:17 +08:00
m0_75251201
786025f720 feat: 受击音效更新类别控制 2025-11-05 21:34:21 +08:00
m0_75251201
5d69efbc3f feat: 场景 2025-11-04 17:05:34 +08:00
m0_75251201
0206a83f56 feat: 场景快照支持打印组件值 2025-11-03 18:56:20 +08:00
m0_75251201
2b7943339c feat: 添加了受击反馈mod chore: 优化了隐藏角色的代码 2025-11-02 11:43:21 +08:00
m0_75251201
2af09007f9 Merge branch 'main' of https://github.com/thisMiniBox/DuckovMods 2025-11-01 23:26:36 +08:00
m0_75251201
b3bf4a9252 fix: 修复隐藏状态下修改装备装备再次显示的bug 2025-11-01 23:26:25 +08:00
折纸的小箱子
0168f91ca9 Update README.md 2025-11-01 18:27:16 +08:00
m0_75251201
5cec8711dd feat: 添加热键自定义修改支持 2025-11-01 17:03:58 +08:00
m0_75251201
4f144bef9a chore: 添加程序说明 2025-11-01 15:53:39 +08:00
m0_75251201
67c886a904 Merge branch 'main' of https://github.com/thisMiniBox/DuckovMods 2025-11-01 15:39:27 +08:00
m0_75251201
4a3f5311cc chore: 删除之前尝试做第三人称的残留 2025-11-01 15:35:41 +08:00
折纸的小箱子
7ed03190cd Initial commit 2025-11-01 15:17:33 +08:00
300 changed files with 10464 additions and 277 deletions

46
.github/copilot-instructions.md vendored Normal file
View File

@@ -0,0 +1,46 @@
## DuckovMods — agent 快速上手说明
目标:为 AI 编码代理提供立即可用的、本仓库可执行的上下文和约定,便于快速完成修改、补丁与功能实现。
- 仓库概览
- 顶层为多个独立 mod 项目文件夹(例如 `HideCharacter/`, `HitFeedback/`, `SceneSnapshot/`, `Theme/`, `UIFrame/`)。每个模块通常包含一个 `*.csproj``ModBehaviour.cs` 与若干源文件。
- 解决方案文件:`DuckovMods.sln`。README 位于根目录,简短说明了仓库目的。
- 关键约定(必须遵循)
- 每个 mod 通过类 `ModBehaviour` 实现,且继承自 `Duckov.Modding.ModBehaviour`(示例:`Theme/ModBehaviour.cs`)。常用生命周期钩子:`OnAfterSetup()``OnBeforeDeactivate()`
- 模块间互操作通常通过反射和“安全 API 包装器”完成(示例:`HideCharacter/Api/ModConfigApi.cs`)。这些包装器:
- 在运行时用 `AppDomain.CurrentDomain.GetAssemblies()` 扫描目标程序集并反射所需类型/方法;
- 做好异常捕获并返回布尔成功标志或默认值;
- 约定配置键名为 `${modName}_${key}`(见 `SafeLoad`/`SafeSave` 用法)。
- 配置与版本兼容:一些 API 会检查目标 mod 的静态 `VERSION` 字段(例中 `ModConfig.ModBehaviour.VERSION`)。在交互前请优先调用包装器的 `Initialize()`
- 日志使用 UnityEngine 的 `Debug.Log` / `Debug.LogWarning` / `Debug.LogError`
- 构建 / 调试工作流
- 这是一个 .NET 多项目解决方案。常规命令PowerShell
- 构建(调试):`dotnet build d:\vs_project\DuckovMods\DuckovMods.sln`
- 构建(发布):`dotnet build d:\vs_project\DuckovMods\DuckovMods.sln -c Release`
- 注意:部分项目输出见 `bin/Debug/netstandard2.1/``bin/Release/net9.0/`,在修改目标框架或依赖时,请检查对应 `*.csproj``<TargetFramework>`
- 仓库内未发现独立的单元测试项目 —— 若需要添加测试,请在顶层新建 `tests/` 并使用常见测试框架xUnit/NUnit
- 常见开发任务与示例
- 添加新 mod在仓库根添加新文件夹并创建 `YourMod.csproj``ModBehaviour.cs`(继承 `Duckov.Modding.ModBehaviour`),在解决方案中加入 csproj。
- 与 ModConfig 交互:参考 `HideCharacter/Api/ModConfigApi.cs` 的模式,优先编写“安全静态封装”以免反射调用抛出未捕获异常。
- 读取/保存配置:使用 `SafeLoad<T>(modName, key, default)``SafeSave<T>(modName, key, value)`,键名需按 `${modName}_${key}` 规则。
- 代码风格与约定(可自动化检查)
- 命名:模块文件夹以 PascalCase类名以 PascalCase。
- 公共跨 mod API 放在各模块的 `Api/` 子目录(例如 `HideCharacter/Api/`),以降低命名冲突。
- 反射查找尽量包含日志输出以便定位加载顺序问题(本仓库中已有此类日志用法)。
- 易出错点agent 在修改时应特别留意)
- 直接调用其他 mod 的内部类型/方法会因加载顺序或版本不兼容而失败——请优先使用或添加“安全包装器”。
- 修改 TargetFramework 可能导致与 Unity 或已有二进制不兼容。先在本地构建并检查 `bin/` 输出。
- 不要假设存在单元测试;对行为敏感修改请手动验证或添加小型验证脚本。
- 推荐起始文件(优先阅读)
- `Theme/ModBehaviour.cs`mod 生命周期示例)
- `HideCharacter/Api/ModConfigApi.cs`(反射 + 安全包装器范例)
- `UIFrame/UIFrameAPI.cs`(公共 API 占位)
- `DuckovMods.sln` 与根 `README.md`
如果上面有遗漏或需要更详细的“如何运行/调试某个 mod”步骤请指出具体目标例如“在 Unity 中热重载某 mod”或“为 HideCharacter 添加新的 ModConfig 项”),我会把说明扩展成更精确的操作步骤。

13
.idea/.idea.DuckovMods/.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,13 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# Rider 忽略的文件
/contentModel.xml
/.idea.DuckovMods.iml
/projectSettingsUpdater.xml
/modules.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

6
.idea/.idea.DuckovMods/.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

Binary file not shown.

Binary file not shown.

BIN
.vs/DuckovMods/v17/.suo Normal file

Binary file not shown.

View File

@@ -0,0 +1,37 @@
{
"Version": 1,
"WorkspaceRootPath": "D:\\vs_project\\DuckovMods\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{623270B2-9677-44F2-9137-8EF50527E8D5}|SceneSnapshot\\SceneSnapshot.csproj|d:\\vs_project\\duckovmods\\scenesnapshot\\printtool.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{623270B2-9677-44F2-9137-8EF50527E8D5}|SceneSnapshot\\SceneSnapshot.csproj|solutionrelative:scenesnapshot\\printtool.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
}
],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": [
{
"DockedWidth": 200,
"SelectedChildIndex": 0,
"Children": [
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "PrintTool.cs",
"DocumentMoniker": "D:\\vs_project\\DuckovMods\\SceneSnapshot\\PrintTool.cs",
"RelativeDocumentMoniker": "SceneSnapshot\\PrintTool.cs",
"ToolTip": "D:\\vs_project\\DuckovMods\\SceneSnapshot\\PrintTool.cs",
"RelativeToolTip": "SceneSnapshot\\PrintTool.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAABAAAABJAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-11-01T07:31:59.689Z",
"EditorCaption": ""
}
]
}
]
}
]
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<DuckovPath>D:\steam\steamapps\common\Escape from Duckov</DuckovPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<OutputPath>D:\steam\steamapps\common\Escape from Duckov\Duckov_Data\Mods\CharacterPreview</OutputPath>
<GenerateDependencyFile>false</GenerateDependencyFile>
<DebugType>none</DebugType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<OutputPath>D:\steam\steamapps\common\Escape from Duckov\Duckov_Data\Mods\CharacterPreview</OutputPath>
<GenerateDependencyFile>false</GenerateDependencyFile>
<DebugType>none</DebugType>
</PropertyGroup>
<ItemGroup>
<Reference Include="$(DuckovPath)\Duckov_Data\Managed\TeamSoda.*" Private="False"/>
<Reference Include="$(DuckovPath)\Duckov_Data\Managed\ItemStatsSystem.dll" Private="False"/>
<Reference Include="$(DuckovPath)\Duckov_Data\Managed\Unity*" Private="False"/>
<Reference Include="$(DuckovPath)\Duckov_Data\Managed\FMODUnity.dll" Private="False"/>
<Reference Include="$(DuckovPath)\Duckov_Data\Managed\UniTask.dll" Private="False"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Lib.Harmony" Version="2.4.1"/>
</ItemGroup>
<ItemGroup>
<Compile Remove="PatchSceneLoaderNotifyPointerClick.cs" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,64 @@
using System;
using UnityEngine;
using System.IO;
namespace CharacterPreview
{
[Serializable]
public class ConfigData
{
public Vector3 modelPosition;
public Vector3 modelRotation;
public float modelScale;
public bool use = false;
public bool tip = true;
public bool hideCamera = true;
public bool showSetEditButton = true;
public bool canEdit = true;
public float editSpeed = 1;
public bool showEquipment = true;
}
[Serializable]
public class Config
{
private string configSavePath;
public ConfigData data = new ConfigData();
public Config(string savePath)
{
configSavePath = savePath;
}
public void Save()
{
data.use = true;
var json = JsonUtility.ToJson(data, true);
File.WriteAllText(configSavePath, json);
}
public static Config Load(string savePath)
{
if (!File.Exists(savePath))
{
try
{
File.Create(savePath).Dispose();
}
catch (IOException ex)
{
Debug.LogError($"Failed to create empty config file at {savePath}: {ex.Message}");
return new Config(savePath) { data = new ConfigData() };
}
}
var json = File.ReadAllText(savePath);
var loadedData = JsonUtility.FromJson<ConfigData>(json);
var config = new Config(savePath);
config.data = loadedData ?? new ConfigData();
return config;
}
}
}

View File

@@ -0,0 +1,265 @@
using System;
using TMPro;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace CharacterPreview
{
public class ControlModelMove : MonoBehaviour, IDragHandler,IPointerDownHandler,IPointerEnterHandler,IPointerExitHandler
{
private RectTransform rectTransform;
private Transform canvasRectTransform; //由滑动器自己创建的
private Vector2 lastMousePosition;
private Image image;
private TMP_Text text;
private Button editButton;
private TMP_Text editButtonText;
public bool firstClick=true;
public float Speed => ModBehaviour.config.data.editSpeed;
//防止鼠标在范围外捕获消息
private bool canEdit = false;
private void Awake()
{
SetRectTransform();
SetText();
SetColor();
if(ModBehaviour.config.data.showSetEditButton)
SetEditButton();
firstClick = ModBehaviour.config.data.tip;
if (!firstClick)
{
HideTip();
}
}
private void OnDestroy()
{
if (canvasRectTransform)
{
Destroy(canvasRectTransform.gameObject);
}
}
void Update()
{
if (!canEdit)
return;
var scroll = Input.GetAxis("Mouse ScrollWheel");
if (scroll != 0)
{
if (Input.GetMouseButton(1))
{
ModBehaviour.modelMove.RotateAroundCameraZ(Speed * scroll * 8);
}
else if(Input.GetKey(KeyCode.LeftShift))
{
ModBehaviour.modelMove.Scale(Speed*scroll/4f);
}
else
{
ModBehaviour.modelMove.Move(new Vector3(0, 0, Speed * scroll * 2));
}
}
if (Input.GetMouseButtonDown(2))
{
if (Input.GetKey(KeyCode.LeftControl))
{
ModBehaviour.modelMove.RefreshPosition();
}
else
{
ModBehaviour.modelMove.LookAtCamera();
}
}
}
public void SetColor()
{
var color = new Color(0.9f, 0.8f, 0.3f, 0.1f);
image = gameObject.GetComponent<Image>();
if (!image)
{
image = gameObject.AddComponent<Image>();
}
image.color = color;
}
public void SetText()
{
text=gameObject.GetComponentInChildren<TMP_Text>();
if (!text)
{
var textChilde = new GameObject("Text");
textChilde.transform.SetParent(gameObject.transform);
var rect = textChilde.AddComponent<RectTransform>();
text= textChilde.AddComponent<TextMeshProUGUI>();
rect.anchorMax = Vector2.one;
rect.anchorMin = Vector2.zero;
rect.offsetMin = Vector2.zero;
rect.offsetMax = Vector2.zero;
}
text.fontSize = 24;
text.alignment=TextAlignmentOptions.Center;
text.text = "在此区域可以编辑模型状态(点击区域关闭提示)\n" +
"通过鼠标左键拖动修改角色的上下左右位置\n" +
"通过鼠标滚轮修改角色的z轴位置\n" +
"通过鼠标右键控制角色旋转\n" +
"按住右键的情况下滚动滚轮可让角色转圈\n" +
"按住shift滚动滚轮可缩放角色\n" +
"点按鼠标中键可让角色朝向摄像头\n" +
"按住ctrl并点击中键则恢复默认位置";
}
public void SetRectTransform()
{
if (!rectTransform)
{
rectTransform = GetComponent<RectTransform>();
}
if (!rectTransform)
{
rectTransform = gameObject.AddComponent<RectTransform>();
}
if (!rectTransform.parent || rectTransform.parent.name != "Canvas")
{
var defaultCanvas = GameObject.Find("Canvas");
if (!defaultCanvas)
{
defaultCanvas = new GameObject("ControlModelMoveCanvas");
var canvas = defaultCanvas.AddComponent<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay; // 设置渲染模式为屏幕空间覆盖
defaultCanvas.AddComponent<CanvasScaler>();
defaultCanvas.AddComponent<GraphicRaycaster>();
canvasRectTransform = defaultCanvas.transform;
}
rectTransform.SetParent(defaultCanvas.transform);
}
rectTransform.SetAsFirstSibling();
rectTransform.anchorMax = Vector2.one;
rectTransform.anchorMin = new Vector2(0.5f, 0);
rectTransform.offsetMax = Vector2.zero;
rectTransform.offsetMin = Vector2.zero;
}
public void SetEditButton()
{
if (!editButton)
{
var buttonObj = new GameObject("EditButton");
buttonObj.transform.SetParent(transform, false);
var buttonRect= buttonObj.AddComponent<RectTransform>();
buttonRect.anchorMax=Vector2.right;
buttonRect.anchorMin=Vector2.right;
buttonRect.pivot = Vector2.right;
buttonRect.sizeDelta=new Vector2(80f,30f);
buttonRect.anchoredPosition = new Vector2(-200, 100);
var button = buttonObj.AddComponent<Button>();
var buttonImage = buttonObj.AddComponent<Image>();
buttonImage.color = Color.green;
button.image = buttonImage;
var textObj=new GameObject("Text");
var txtRect = textObj.AddComponent<RectTransform>();
txtRect.SetParent(buttonRect);
txtRect.anchorMax=Vector2.one;
txtRect.anchorMin=Vector2.zero;
txtRect.offsetMax=Vector2.zero;
txtRect.offsetMin=Vector2.zero;
var tmpText = txtRect.gameObject.AddComponent<TextMeshProUGUI>();
tmpText.text = "关闭编辑";
tmpText.color = Color.white;
tmpText.alignment = TextAlignmentOptions.Center;
tmpText.fontSize = 14;
tmpText.raycastTarget = false; // 文本RaycastTarget通常为false除非文本本身需要互动
button.AddComponent<HideSelfOnLeisure>();
editButton = button;
editButtonText = tmpText;
}
editButton.onClick.RemoveAllListeners();
editButton.onClick.AddListener(OnEditButton);
RefreshEditButton();
}
public void HideTip()
{
var oldColor = Color.black;
oldColor.a = 0.01f;
image.color = oldColor;
text.text = "";
}
public void RefreshEditButton()
{
if (editButton)
{
editButton.image.color = ModBehaviour.config.data.canEdit ? Color.green : Color.red;
editButtonText.text = ModBehaviour.config.data.canEdit ? "关闭编辑" : "开启编辑";
}
}
public void OnDrag(PointerEventData eventData)
{
var shift = eventData.position - lastMousePosition;
if (ModBehaviour.modelMove)
{
if (Input.GetMouseButton(0))
{
ModBehaviour.modelMove.Move(shift * Speed / 750);
}
else if (Input.GetMouseButton(1))
{
ModBehaviour.modelMove.Rotate(shift * Speed * 2);
}
}
lastMousePosition=eventData.position;
}
public void OnPointerDown(PointerEventData eventData)
{
lastMousePosition=eventData.position;
if (firstClick)
{
HideTip();
firstClick=false;
ModBehaviour.config.data.tip = firstClick;
}
}
public void OnPointerEnter(PointerEventData eventData)
{
canEdit = true;
}
public void OnPointerExit(PointerEventData eventData)
{
canEdit = false;
}
public void OnEditButton()
{
ModBehaviour.config.data.canEdit = !ModBehaviour.config.data.canEdit;
RefreshEditButton();
}
}
}

View File

@@ -0,0 +1,216 @@
using System.Collections.Generic;
using UnityEngine;
namespace CharacterPreview
{
public static class GameObjectUtils
{
/// <summary>
/// 在指定根对象下根据路径查找子对象,包括隐藏对象。
/// 路径示例:"Parent/Child/Grandchild" 或 "MyObject"
/// 如果路径以'/'开头,它会被视为相对于根对象。
/// </summary>
/// <param name="rootObject">起始查找的根GameObject。</param>
/// <param name="path">要查找的对象的相对路径。</param>
/// <returns>找到的GameObject如果未找到则返回null。</returns>
public static GameObject FindObjectByPath(GameObject rootObject, string path)
{
if (rootObject == null)
{
Debug.LogError("FindObjectByPath: rootObject cannot be null.");
return null;
}
if (string.IsNullOrWhiteSpace(path))
{
Debug.LogWarning("FindObjectByPath: Path is empty or whitespace. Returning the root object itself.");
return rootObject;
}
// 规范化路径,移除多余的斜杠
var pathParts = path.Split(new char[] { '/' }, System.StringSplitOptions.RemoveEmptyEntries);
var currentTransform = rootObject.transform;
foreach (var part in pathParts)
{
Transform foundChild = null;
// 迭代所有子对象(包括非激活的)
// GetChild() 只获取激活状态的子对象,甚至不会获取非激活的 Transform。
// 更好的方法是迭代所有 Transform检查 parent 是否是 currentTransform。
// 然而Unity的API并没有直接提供一个GetChildren(includeInactive: true)的方法,
// 所以我们需要遍历所有场景中的Transform或用递归查找。
// 对于层级较深或子对象较多的情况,这种遍历可能会比较慢。
//
// 另一种常见但更低效的方法是使用 FindObjectsOfTypeAll<Transform>() 然后过滤,
// 但那会遍历整个场景。
//
// 最直接且相对高效的方法是递归查找子Transform。
// 迭代当前 Transform 的所有直接子 Transform
for (var i = 0; i < currentTransform.childCount; i++)
{
var child = currentTransform.GetChild(i);
if (child.name == part)
{
foundChild = child;
break;
}
}
if (foundChild != null)
{
currentTransform = foundChild;
}
else
{
// 未找到当前路径部分对应的子对象
Debug.LogWarning(
$"FindObjectByPath: Could not find '{part}' under '{currentTransform.name}' at path '{path}'.");
return null;
}
}
return currentTransform.gameObject;
}
/// <summary>
/// (推荐使用) 更通用的方法在任意指定Transform下根据相对路径查找子Transform包括隐藏对象。
/// 路径示例:"Parent/Child/Grandchild" 或 "MyObject"
/// </summary>
/// <param name="rootTransform">起始查找的根Transform。</param>
/// <param name="relativePath">要查找的子Transform的相对路径。</param>
/// <returns>找到的Transform如果未找到则返回null。</returns>
public static Transform FindTransformByRelativePath(Transform rootTransform, string relativePath)
{
if (rootTransform == null)
{
Debug.LogError("FindTransformByRelativePath: rootTransform cannot be null.");
return null;
}
if (string.IsNullOrWhiteSpace(relativePath))
{
Debug.LogWarning(
"FindTransformByRelativePath: relativePath is empty or whitespace. Returning the rootTransform itself.");
return rootTransform;
}
var pathParts = relativePath.Split(new char[] { '/' }, System.StringSplitOptions.RemoveEmptyEntries);
var currentTransform = rootTransform;
foreach (var partName in pathParts)
{
Transform foundChild = null;
// GetChild(i) 方法返回的 Transform 即使其 GameObject.activeSelf 为 false它仍然是存在的。
// 所以这种遍历是包括非激活对象的。
for (var i = 0; i < currentTransform.childCount; i++)
{
var child = currentTransform.GetChild(i);
if (child.name == partName)
{
foundChild = child;
break;
}
}
if (foundChild != null)
{
currentTransform = foundChild;
}
else
{
Debug.LogWarning(
$"FindTransformByRelativePath: Could not find '{partName}' under '{currentTransform.name}' (Full Path Attempted: {relativePath}).");
return null;
}
}
return currentTransform;
}
/// <summary>
/// 在整个场景中根据完整路径查找GameObject包括隐藏对象。
/// 注意:如果场景中有多个同名根对象,此方法可能只返回第一个找到的。
/// 如果路径以'/'开头,它会被视为相对于场景根目录的路径。
/// 例如: "MyRootObject/Child/Grandchild"
/// </summary>
/// <param name="fullPath">要查找的对象的完整路径。</param>
/// <returns>找到的GameObject如果未找到则返回null。</returns>
public static GameObject FindObjectByFullPath(string fullPath)
{
if (string.IsNullOrWhiteSpace(fullPath))
{
Debug.LogWarning("FindObjectByFullPath: Path is empty or whitespace.");
return null;
}
// 规范化路径
var pathParts = fullPath.Split(new char[] { '/' }, System.StringSplitOptions.RemoveEmptyEntries);
if (pathParts.Length == 0)
{
return null;
}
var rootObjectName = pathParts[0];
// 查找所有根对象 (即没有父Transform的对象)
// Unity没有直接提供获取所有root GameObjects的API
// 我们可以通过迭代所有活跃的GameObject并检查其parent是否为null来找到根对象。
// 但为了包含隐藏对象,我们需要更复杂的方法。
//
// 更好的方法是使用 FindObjectsOfTypeAll<Transform>() 找到所有Transform
// 然后过滤出parent为null的作为根对象。
// 注意FindObjectsOfTypeAll 即使是静态方法,其效率也可能低于遍历已经知道的子对象。
// 在编辑器模式下Resources.FindObjectsOfTypeAll<GameObject>() 可以获取所有对象。
// 在运行时,我们通常需要先有一个起点。
// 为了在运行时找到根对象(包括非激活的),我们可以先尝试 FindObjectOfType<GameObject>()
// 或者遍历当前场景中的所有Transform然后向上追溯到根。
// 最直接的做法是假设我们知道路径的第一个部分是根对象,然后从那里开始。
// 尝试通过 GameObject.Find() 或类似的编辑器工具去查找根对象
// GameObject.Find() 只查找激活的根对象,且只通过名称查找。
// GameObject.Find(rootObjectName) 即使找到了,也可能不会是整个路径的起点。
// 为了包含隐藏对象,我们需要遍历。
// 一个更可靠的方法是先找到所有根Transform然后对每个根Transform调用 FindTransformByRelativePath。
// 获取所有根Transform的方法
var rootTransforms = new List<Transform>();
var allTransformsInScene =
Resources.FindObjectsOfTypeAll<Transform>(); // 注意此方法在Editor中有效在Build中会有些限制
// 在编辑器环境中Resources.FindObjectsOfTypeAll 很好用。
// 在运行时这个方法通常只返回在Resources文件夹中的对象或者在场景中已有的对象而不是所有已加载的对象。
// 对于运行时,更可靠的是遍历所有的场景:
for (var i = 0; i < UnityEngine.SceneManagement.SceneManager.sceneCount; i++)
{
var scene = UnityEngine.SceneManagement.SceneManager.GetSceneAt(i);
if (scene.isLoaded)
{
foreach (var go in scene.GetRootGameObjects())
{
// go 是根 GameObject它可能是激活的或非激活的。
// GetRootGameObjects() 已经包含了非激活的根对象。
rootTransforms.Add(go.transform);
}
}
}
foreach (var rootT in rootTransforms)
{
if (rootT.name == rootObjectName)
{
// 如果路径只有一个部分,就是根对象本身
if (pathParts.Length == 1)
{
return rootT.gameObject;
}
// 构建子路径(移除根对象的名称)
var relativeSubPath = string.Join("/", pathParts, 1, pathParts.Length - 1);
var result = FindTransformByRelativePath(rootT, relativeSubPath);
if (result != null)
{
return result.gameObject;
}
}
}
Debug.LogWarning(
$"FindObjectByFullPath: Could not find any root object named '{rootObjectName}' or the full path '{fullPath}'.");
return null;
}
}
}

View File

@@ -0,0 +1,64 @@
using System;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace CharacterPreview
{
public class HideSelfOnLeisure:MonoBehaviour,IPointerEnterHandler,IPointerExitHandler
{
private Image image;
private Color CurrentColor => ModBehaviour.config.data.canEdit ? Color.green : Color.red;
private Color HideColor => ModBehaviour.config.data.canEdit
? new Color(Color.green.r, Color.green.g, Color.green.b, 0.1f)
: new Color(Color.red.r, Color.red.g, Color.red.b, 0.1f);
public float hideTime = 3f;
public float animationTime = 1f;
private float timer = 0;
private float animationTimer = 0;
private void Start()
{
image = GetComponent<Image>();
// if (!image)
// gameObject.SetActive(false);
}
private void Update()
{
if (timer < hideTime)
{
timer += Time.deltaTime;
if (timer >= hideTime)
{
animationTimer = 0;
}
}
if (animationTimer < animationTime)
{
animationTimer += Time.deltaTime;
// if(animationTimer >= animationTime)
// animationTimer=animationTime;
var t = animationTimer / animationTime;
t = Mathf.Clamp01(t);
image.color = Color.Lerp(CurrentColor, HideColor, t);
}
}
public void OnPointerEnter(PointerEventData eventData)
{
if(!image)return;
timer = hideTime;
animationTimer = animationTime;
image.color = CurrentColor;
}
public void OnPointerExit(PointerEventData eventData)
{
if (!image) return;
timer = 0;
}
}
}

View File

@@ -0,0 +1,175 @@
using System;
using System.Linq;
using System.Reflection;
using UnityEngine;
namespace CharacterPreview
{
public static class Load_DuckovCustomModel
{
public static ModelMove? CreateModel()
{
Action startMethod = null;
Action midMethod = null;
Action endMethod = null;
// 尝试获取 LevelManager_OnLevelBeginInitializing
try
{
Debug.Log("\n[Main Runner] 尝试获取 LevelManager_OnLevelBeginInitializing...");
startMethod =
GetPrivateStaticVoidMethod(
"DuckovCustomModel.ModEntry.LevelManager_OnLevelBeginInitializing");
Debug.Log("[Main Runner] 成功获取 LevelManager_OnLevelBeginInitializing 委托。");
}
catch (Exception ex)
{
Debug.LogError(
$"[Main Runner] <color=red>获取 LevelManager_OnLevelBeginInitializing 时发生错误:</color> {ex.GetType().Name} - {ex.Message}");
}
// 尝试获取 LevelManager_OnLevelInitialized
try
{
Debug.Log("\n[Main Runner] 尝试获取 LevelManager_OnLevelInitialized...");
midMethod = GetPrivateStaticVoidMethod(
"DuckovCustomModel.ModEntry.LevelManager_OnLevelInitialized");
Debug.Log("[Main Runner] 成功获取 LevelManager_OnLevelInitialized 委托。");
}
catch (Exception ex)
{
Debug.LogError(
$"[Main Runner] <color=red>获取 LevelManager_OnLevelInitialized 时发生错误:</color> {ex.GetType().Name} - {ex.Message}");
}
// 尝试获取 LevelManager_OnAfterLevelInitialized
try
{
Debug.Log("\n[Main Runner] 尝试获取 LevelManager_OnAfterLevelInitialized...");
endMethod = GetPrivateStaticVoidMethod(
"DuckovCustomModel.ModEntry.LevelManager_OnAfterLevelInitialized");
Debug.Log("[Main Runner] 成功获取 LevelManager_OnAfterLevelInitialized 委托。");
}
catch (Exception ex)
{
Debug.LogError(
$"[Main Runner] <color=red>获取 LevelManager_OnAfterLevelInitialized 时发生错误:</color> {ex.GetType().Name} - {ex.Message}");
}
Debug.Log("\n[Main Runner] --- 调用获取到的方法 ---");
if (startMethod != null)
{
Debug.Log("[Main Runner] 正在调用 LevelManager_OnLevelBeginInitializing...");
startMethod.Invoke();
Debug.Log("[Main Runner] LevelManager_OnLevelBeginInitializing 调用完成。");
}
else
Debug.LogWarning("[Main Runner] LevelManager_OnLevelBeginInitializing 委托为 null跳过调用。");
if (midMethod != null)
{
Debug.Log("[Main Runner] 正在调用 LevelManager_OnLevelInitialized...");
midMethod.Invoke();
Debug.Log("[Main Runner] LevelManager_OnLevelInitialized 调用完成。");
}
else
Debug.LogWarning("[Main Runner] LevelManager_OnLevelInitialized 委托为 null跳过调用。");
if (endMethod != null)
{
Debug.Log("[Main Runner] 正在调用 LevelManager_OnAfterLevelInitialized...");
endMethod.Invoke();
Debug.Log("[Main Runner] LevelManager_OnAfterLevelInitialized 调用完成。");
}
else
Debug.LogWarning("[Main Runner] LevelManager_OnAfterLevelInitialized 委托为 null跳过调用。");
// var target = DuckovCustomModel.Core.Data.ModelTarget.Character;
//
// Debug.Log("运行替换");
// var currentModelID = DuckovCustomModel.ModEntry.UsingModel?.GetModelID(target) ?? string.Empty;
// if (string.IsNullOrEmpty(currentModelID))
// {
// // 错误日志未能获取当前模型ID
// Debug.LogError($"[DuckovCustomModel] Failed to get current model ID for target '{target}'. Current model reference might be null or GetModelID returned null/empty. Returning null.");
// return null;
// }
// // 注意out _ 用于忽略 ModelManager 返回的第一个 out 参数
// if (!DuckovCustomModel.Managers.ModelManager.FindModelByID(currentModelID, out _, out var modelInfo))
// {
// // 错误日志:未能找到指定的模型
// Debug.LogError($"[DuckovCustomModel] Model with ID '{currentModelID}' not found by ModelManager for target '{target}'. Returning null.");
// return null;
// }
// if (!modelInfo.CompatibleWithType(target))
// {
// // 错误日志:模型与目标不兼容
// Debug.LogError($"[DuckovCustomModel] Model '{currentModelID}' is not compatible with target '{target}'. Returning null.");
// return null;
// }
// Debug.Log($"inf{currentModelID}");
// // 如果前面的检查都通过,则应用模型
// DuckovCustomModel.Managers.ModelListManager.ApplyModelToTarget(target, currentModelID, true);
//
// var handlers=DuckovCustomModel.Managers.ModelManager.GetAllModelHandlers(target);
// Debug.Log($"handlers{handlers.Count}");
// Debug.Log("运行完成");
return null;
}
public static Action GetPrivateStaticVoidMethod(string fullMethodName)
{
var lastDotIndex = fullMethodName.LastIndexOf('.');
if (lastDotIndex == -1 || lastDotIndex == fullMethodName.Length - 1)
{
throw new ArgumentException($"无效的方法名格式: {fullMethodName}。应为 'Namespace.ClassName.MethodName'。");
}
var fullTypeName = fullMethodName.Substring(0, lastDotIndex);
var methodName = fullMethodName.Substring(lastDotIndex + 1);
Type targetType = null;
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
targetType = assembly.GetType(fullTypeName);
if (targetType != null)
{
break; // 找到类型后停止搜索
}
}
if (targetType == null)
{
throw new TypeLoadException($"在任何已加载的程序集中都未找到类型: {fullTypeName}。");
}
var methodInfo = targetType.GetMethod(
methodName,
BindingFlags.NonPublic | BindingFlags.Static
);
if (methodInfo == null)
{
throw new MissingMethodException(
$"在类型 '{fullTypeName}' 中未找到名为 '{methodName}' 的私有静态方法," +
$"或者该方法不满足私有、静态、无参的条件。"
);
}
if (methodInfo.GetParameters().Length != 0)
{
throw new MissingMethodException(
$"方法 '{fullMethodName}' 存在参数,不符合无参 Action 的要求。"
);
}
if (methodInfo.ReturnType != typeof(void))
{
throw new MissingMethodException(
$"方法 '{fullMethodName}' 返回值不是 void不符合 Action 的要求。"
);
}
return (Action)methodInfo.CreateDelegate(typeof(Action));
}
}
}

View File

@@ -0,0 +1,302 @@
using System;
using System.IO;
using System.Reflection;
using Cysharp.Threading.Tasks;
using Duckov.Utilities;
using ItemStatsSystem;
using Saves;
using UnityEngine;
using UnityEngine.SceneManagement;
using Object = UnityEngine.Object;
namespace CharacterPreview
{
public class ModBehaviour : Duckov.Modding.ModBehaviour
{
private static CharacterMainControl characterControl;
public static ModelMove modelMove;
private const string characterFaceSaveKey = "CustomFace_MainCharacter";
private const string characterItemSaveKey = "MainCharacterItemData";
private const string ModelName = "CharacterPreviewModel";
public static Config config;
private static GameObject cameraModelObject;
private void OnDestroy()
{
config.Save();
}
protected override void OnAfterSetup()
{
var path=Path.Combine(info.path,"config.json");
config = Config.Load(path);
SceneManager.sceneLoaded+=OnSceneLoaded;
AddListen();
}
protected override void OnBeforeDeactivate()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
RemoveModel();
}
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
if (scene.name == "MainMenu")
{
AddListen();
// _ = CreateCharacterModel();
}
}
private void AddListen()
{
var canvasObj = GameObject.Find("Canvas");
if (canvasObj == null)
{
Debug.Log("Canvas not found");
return;
}
var mainMenu=GameObjectUtils.FindObjectByPath(canvasObj, "MainMenuContainer");
if (!mainMenu)
{
Debug.Log("MainMenuContainer not found");
return;
}
var listen = mainMenu.GetComponent<ShowListen>();
if (!listen)
{
mainMenu.AddComponent<ShowListen>();
}
}
public static void RemoveModel()
{
if (characterControl)
{
Destroy(characterControl.gameObject);
characterControl = null;
modelMove = null;
}
}
public static async UniTask CreateCharacterModel()
{
if (characterControl)
{
Debug.LogWarning("[CreateCharacterModel] CharacterModel 已经被创建,跳过重复创建。");
return;
}
if (SceneManager.GetActiveScene().name != "MainMenu")
{
Debug.LogWarning(
$"[CreateCharacterModel] 当前场景为 \"{SceneManager.GetActiveScene().name}\",非主菜单界面 \"MainMenu\",无法创建角色模型。");
return;
}
Item item = await ItemSavesUtilities.LoadItem(characterItemSaveKey);
if (item == null)
{
Debug.Log($"[CreateCharacterModel] 未找到已保存的 {characterItemSaveKey},将使用默认角色模板创建新角色。");
var defaultTypeID = GameplayDataSettings.ItemAssets.DefaultCharacterItemTypeID;
item = await ItemAssetsCollection.InstantiateAsync(defaultTypeID);
if (item == null)
{
Debug.LogError("[CreateCharacterModel] 无法通过默认角色类型 ID 实例化角色物品,请确认资源是否存在且配置正确。");
return;
}
}
// 获取角色模型预制体
var model = GetCharacterModelPrefab_Reflection();
if (model == null)
{
Debug.LogError("[CreateCharacterModel] 通过反射获取角色模型预制体失败,请检查 GetCharacterModelPrefab_Reflection 方法实现。");
return;
}
characterControl = CreateCharacter(
item, model, Vector3.zero, Quaternion.identity);
if (characterControl == null)
{
Debug.LogError("[CreateCharacterModel] 角色创建失败,返回的 characterControl 为 null。");
return;
}
characterControl.enabled = false;
characterControl.gameObject.name = ModelName;
// 加载自定义面部设置
var customFaceSettingData = SavesSystem.Load<CustomFaceSettingData>(characterFaceSaveKey);
if (!customFaceSettingData.savedSetting)
{
Debug.LogWarning("[CreateCharacterModel] 未能加载有效的 CustomFaceSettingData将使用默认预设。");
if (GameplayDataSettings.CustomFaceData?.DefaultPreset?.settings == null)
{
Debug.LogError("[CreateCharacterModel] 默认面部预设也为空!无法应用面部设置。");
}
else
{
customFaceSettingData = GameplayDataSettings.CustomFaceData.DefaultPreset.settings;
}
}
if (characterControl.characterModel?.CustomFace != null)
{
characterControl.characterModel.CustomFace.LoadFromData(customFaceSettingData);
}
else
{
Debug.LogWarning("[CreateCharacterModel] 跳过面部数据加载CustomFace 组件或数据为空。");
}
// 添加移动组件
modelMove = characterControl.gameObject.GetComponent<ModelMove>();
if (modelMove == null)
{
modelMove = characterControl.gameObject.AddComponent<ModelMove>();
}
Debug.LogWarning("这个是模型的动画组件报错,由于这个是部分初始化,导致内容不完全,不影响(其实是因为没影响就懒得一个个分析了,欸嘿(*^▽^*)");
HideCamera();
// SetModelShow(false);
}
private static void SetModelShow(bool show)
{
if (characterControl)
{
characterControl.gameObject.SetActive(show);
}
}
private static void HideCamera()
{
if (!cameraModelObject)
{
var camera = FindObjectOfType<Camera>();
if (camera == null)
{
Debug.LogWarning("[CreateCharacterModel] 场景中未找到 Camera 对象。");
}
else
{
cameraModelObject = GameObjectUtils.FindObjectByPath(camera.gameObject, "Camera01_prefab");
if (cameraModelObject == null)
{
Debug.LogWarning("[CreateCharacterModel] 在 Camera 下未找到路径为 \"Camera01_prefab\" 的子对象。");
}
}
}
if (cameraModelObject)
{
if (config.data.hideCamera)
{
cameraModelObject.SetActive(false);
}
else
{
cameraModelObject.SetActive(true);
}
}
else
{
Debug.LogError("[CreateCharacterModel] 未找到摄像机模型对象,无法控制其显示状态。");
}
}
private static CharacterModel GetCharacterModelPrefab_Reflection()
{
// 获取 LevelManager 实例
var lm = GameplayDataSettings.Prefabs.LevelManagerPrefab;
if (lm == null)
{
Debug.LogError("LevelManager 实例未找到!");
return null;
}
// 使用反射获取 LevelManager 中的 characterModel 字段
var field = typeof(LevelManager).GetField("characterModel", BindingFlags.Instance | BindingFlags.NonPublic);
if (field == null)
{
Debug.LogError("characterModel 字段在 LevelManager 中未找到!");
return null;
}
// 获取字段值并转换为 CharacterModel
var modelPrefab = field.GetValue(lm) as CharacterModel;
if (modelPrefab == null)
{
Debug.LogError("characterModel 字段的值为空!");
return null;
}
return modelPrefab;
}
/// <summary>
/// 检查指定全限定类名的类型是否存在于已加载的程序集中。
/// </summary>
/// <param name="fullTypeName">完整类名,如 "DuckovCustomModel.ModBehaviour"</param>
/// <returns>如果存在返回 true否则 false</returns>
public static bool IsTypeLoaded(string fullTypeName)
{
if (string.IsNullOrWhiteSpace(fullTypeName))
return false;
// 先尝试用 Type.GetType适用于 mscorlib 和当前程序集)
var type = Type.GetType(fullTypeName, throwOnError: false, ignoreCase: false);
if (type != null)
return true;
// 遍历所有已加载的程序集
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var assembly in assemblies)
{
try
{
type = assembly.GetType(fullTypeName, throwOnError: false, ignoreCase: false);
if (type != null)
return true;
}
catch (Exception)
{
// 某些程序集可能无法读取(如动态生成、权限问题等),跳过
continue;
}
}
return false;
}
public static CharacterMainControl CreateCharacter(
Item itemInstance,
CharacterModel modelPrefab,
Vector3 pos,
Quaternion rotation)
{
var character = Instantiate(GameplayDataSettings.Prefabs.CharacterPrefab, pos, rotation);
var _characterModel = Instantiate(modelPrefab);
character.SetCharacterModel(_characterModel);
if (itemInstance == null)
{
if ((bool) (Object) character)
Destroy(character.gameObject);
return null;
}
character.SetItem(itemInstance);
return character;
}
}
}

View File

@@ -0,0 +1,299 @@
using System;
using UnityEngine;
namespace CharacterPreview
{
public class ModelMove : MonoBehaviour
{
// 摄像机属性
private Camera _camera;
public Camera CurrentCamera
{
get
{
if (!_camera)
{
_camera = Camera.main;
if (!_camera)
{
_camera = FindObjectOfType<Camera>();
}
}
return _camera;
}
}
public Vector2 horizontalMoveRange = new Vector2(-1f, 2f); // X轴范围
public Vector2 verticalMoveRange = new Vector2(-2f, 2f); // Y轴范围
public Vector2 depthMoveRange = new Vector2(-1, 10f); // Z轴范围 (深度)
public Vector2 scaleRange = new Vector2(0.5f, 3f); // 缩放范围 (x=min, y=max)
// 初始状态变量
private Vector3 _initialScale = Vector3.one;
private Quaternion _initialRotation = Quaternion.identity;
// 模型相对于摄像机的初始局部位置偏移量
private Vector3 _initialCameraLocalOffset;
// 用于Input输入的控制器对象
private GameObject _controlModelMoveGameObject;
public bool CanFix => ModBehaviour.config.data.canEdit;
// 模型在摄像机局部空间中的默认位置偏移量 (用于Reset)
public static readonly Vector3 DefaultCameraLocalOffset = new Vector3(0.38f, -0.92f, 2.00f);
private void Awake()
{
EnsureControlModelMoveGameObject();
}
private void Start()
{
if (ModBehaviour.config.data.use)
{
ApplyConfig();
}
else
{
RefreshPosition();
}
}
private void Update()
{
// // 调试功能,按下 "-" 打印模型相对于摄像机的局部位置
// if (Input.GetKeyDown(KeyCode.Minus))
// {
// if (CurrentCamera)
// {
// var localPositionRelativeToCamera =
// CurrentCamera.transform.InverseTransformPoint(transform.position);
// Debug.Log($"模型相对于摄像机的局部位置: {localPositionRelativeToCamera}");
// }
// else
// {
// Debug.LogWarning("找不到摄像机,无法计算局部位置。模型的全局位置为: " + transform.position);
// }
// }
}
private void OnDestroy()
{
// 销毁时清理生成的控制器GameObject
if (_controlModelMoveGameObject)
{
Destroy(_controlModelMoveGameObject);
_controlModelMoveGameObject = null;
}
}
/// <summary>
/// 确保ControlModelMove GameObject存在并绑定脚本。
/// </summary>
private void EnsureControlModelMoveGameObject()
{
if (_controlModelMoveGameObject == null)
{
// 尝试查找场景中已有的ControlModelMove
_controlModelMoveGameObject = GameObject.Find("ControlModelMove");
if (_controlModelMoveGameObject == null)
{
// 如果没有,则创建新的
_controlModelMoveGameObject = new GameObject("ControlModelMove");
}
// 确保ControlModelMove组件已添加
if (_controlModelMoveGameObject.GetComponent<ControlModelMove>() == null)
{
_controlModelMoveGameObject.AddComponent<ControlModelMove>();
}
}
}
/// <summary>
/// 将模型朝向当前摄像机。
/// </summary>
public void LookAtCamera()
{
if(!CanFix)
return;
if (CurrentCamera)
{
transform.LookAt(CurrentCamera.transform);
}
else
{
Debug.LogWarning("ModelMove.LookAtCamera: 找不到摄像机,无法使模型朝向摄像机。");
}
SaveDataToConfig();
}
/// <summary>
/// 重置模型的位置、旋转和缩放
/// </summary>
public void RefreshPosition()
{
if(!CanFix)
return;
// 重置缩放和旋转到初始状态
transform.localScale = _initialScale;
transform.rotation = _initialRotation;
if (CurrentCamera)
{
// 计算模型在世界空间中相对于摄像机的默认位置
var worldPos = CameraLocalToWorld(CurrentCamera, DefaultCameraLocalOffset);
transform.position = worldPos;
// 使模型朝向摄像机
LookAtCamera();
// 记录模型重置后的相对于摄像机的局部偏移量,作为后续移动的基准
_initialCameraLocalOffset = CurrentCamera.transform.InverseTransformPoint(transform.position);
}
else
{
Debug.LogWarning("ModelMove.RefreshPosition: 找不到摄像机。模型将保持其当前世界位置,且相机相对移动功能将无法正常工作。");
_initialCameraLocalOffset = transform.position;
transform.position = new Vector3(8, 8, -16);
}
SaveDataToConfig();
}
/// <summary>
/// 移动模型
/// </summary>
/// <param name="shift">相对于当前总偏移量的位移增量(在摄像机局部坐标系下)</param>
public void Move(Vector3 shift)
{
if(!CanFix)
return;
if (!CurrentCamera)
{
Debug.LogWarning("ModelMove.Move: 找不到摄像机,无法执行相机相对移动。");
return;
}
var currentLocalPos = CurrentCamera.transform.InverseTransformPoint(transform.position);
var currentOffsetFromInitial = currentLocalPos - _initialCameraLocalOffset;
var targetOffsetFromInitial = currentOffsetFromInitial + shift;
targetOffsetFromInitial.x = Mathf.Clamp(targetOffsetFromInitial.x, horizontalMoveRange.x, horizontalMoveRange.y);
targetOffsetFromInitial.y = Mathf.Clamp(targetOffsetFromInitial.y, verticalMoveRange.x, verticalMoveRange.y);
targetOffsetFromInitial.z = Mathf.Clamp(targetOffsetFromInitial.z, depthMoveRange.x, depthMoveRange.y);
var newLocalPos = _initialCameraLocalOffset + targetOffsetFromInitial;
transform.position = CurrentCamera.transform.TransformPoint(newLocalPos);
SaveDataToConfig();
}
/// <summary>
/// 等比例缩放模型
/// </summary>
/// <param name="increment">缩放增量通常来自鼠标滚轮或UI滑块</param>
public void Scale(float increment)
{
if(!CanFix)
return;
// 假设是等比例缩放所以只取x轴的值来计算
var currentScale = transform.localScale.x;
var newScale = currentScale + increment;
// 将新的缩放值限制在预设范围内
newScale = Mathf.Clamp(newScale, scaleRange.x, scaleRange.y);
// 应用新的等比例缩放值
SaveDataToConfig();
transform.localScale = new Vector3(newScale, newScale, newScale);
}
/// <summary>
/// 旋转模型
/// </summary>
/// <param name="rotationAmount">旋转量,通常来自鼠标拖拽的偏移量(delta)</param>
public void Rotate(Vector2 rotationAmount)
{
if(!CanFix)
return;
// 左右拖拽rotationAmount.x围绕世界坐标的Y轴旋转使模型保持直立
transform.Rotate(Vector3.up, -rotationAmount.x * Time.deltaTime, Space.World);
if (CurrentCamera)
{
// 上下拖拽rotationAmount.y围绕摄像机的右方向轴旋转感觉更自然
transform.Rotate(CurrentCamera.transform.right, rotationAmount.y * Time.deltaTime, Space.World);
}
else
{
// 如果找不到相机则退而求其次围绕世界坐标的X轴旋转
Debug.LogWarning("ModelMove.Rotate: 找不到摄像机上下旋转将使用世界X轴。");
transform.Rotate(Vector3.right, rotationAmount.y * Time.deltaTime, Space.World);
}
SaveDataToConfig();
}
/// <summary>
/// 沿摄像机的Z轴朝向旋转模型实现“滚动”或“倾斜”效果。
/// </summary>
/// <param name="angle">要旋转的角度。正值通常为顺时针,负值为逆时针。</param>
public void RotateAroundCameraZ(float angle)
{
if(!CanFix)
return;
if (CurrentCamera == null)
{
Debug.LogWarning("ModelMove.RotateAroundCameraZ: 无法执行围绕相机Z轴的旋转因为找不到相机。");
return;
}
var rotationAxis = CurrentCamera.transform.forward;
transform.Rotate(rotationAxis, angle, Space.World);
SaveDataToConfig();
}
public void SaveDataToConfig()
{
var configData = ModBehaviour.config.data;
configData.modelPosition = CurrentCamera.transform.InverseTransformPoint(transform.position);
configData.modelRotation = transform.eulerAngles;
configData.modelScale = CurrentCamera.transform.localScale.x;
}
public void ApplyConfig()
{
var configData = ModBehaviour.config.data;
transform.position = CameraLocalToWorld(CurrentCamera, configData.modelPosition);
transform.eulerAngles = configData.modelRotation;
transform.localScale = new Vector3(configData.modelScale, configData.modelScale, configData.modelScale);
}
/// <summary>
/// 将摄像机局部空间中的点转换为世界空间中的点。
/// </summary>
/// <param name="camera">参考摄像机。</param>
/// <param name="localPoint">摄像机局部空间中的点。</param>
/// <returns>世界空间中的点。</returns>
public static Vector3 CameraLocalToWorld(Camera camera, Vector3 localPoint)
{
if (camera == null)
throw new ArgumentNullException(nameof(camera),
"Camera cannot be null for CameraLocalToWorld conversion.");
var camTransform = camera.transform;
return camTransform.position + camTransform.rotation * localPoint;
}
}
}

View File

@@ -0,0 +1,13 @@
// using HarmonyLib;
//
// namespace CharacterPreview
// {
// [HarmonyPatch(typeof(SceneLoader), "NotifyPointerClick")]
// public class PatchSceneLoaderNotifyPointerClick
// {
// public static void Postfix(SceneLoader __instance)
// {
// ModBehaviour.CreateCharacterModel();
// }
// }
// }

View File

@@ -0,0 +1,21 @@
using System;
using Cysharp.Threading.Tasks;
using UnityEngine;
namespace CharacterPreview
{
public class ShowListen:MonoBehaviour
{
private void OnEnable()
{
try
{
_ = ModBehaviour.CreateCharacterModel();
}
catch (Exception ex)
{
Debug.LogError($"创建角色模型失败: {ex}");
}
}
}
}

View File

@@ -0,0 +1,79 @@
{
"format": 1,
"restore": {
"D:\\vs_project\\DuckovMods\\CharacterPreview\\CharacterPreview.csproj": {}
},
"projects": {
"D:\\vs_project\\DuckovMods\\CharacterPreview\\CharacterPreview.csproj": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "D:\\vs_project\\DuckovMods\\CharacterPreview\\CharacterPreview.csproj",
"projectName": "CharacterPreview",
"projectPath": "D:\\vs_project\\DuckovMods\\CharacterPreview\\CharacterPreview.csproj",
"packagesPath": "C:\\Users\\Lenovo\\.nuget\\packages\\",
"outputPath": "D:\\vs_project\\DuckovMods\\CharacterPreview\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"D:\\vsShare\\NuGetPackages"
],
"configFilePaths": [
"C:\\Users\\Lenovo\\AppData\\Roaming\\NuGet\\NuGet.Config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
],
"originalTargetFrameworks": [
"netstandard2.1"
],
"sources": {
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
"https://api.nuget.org/v3/index.json": {}
},
"frameworks": {
"netstandard2.1": {
"targetAlias": "netstandard2.1",
"projectReferences": {}
}
},
"warningProperties": {
"warnAsError": [
"NU1605"
]
},
"restoreAuditProperties": {
"enableAudit": "true",
"auditLevel": "low",
"auditMode": "direct"
},
"SdkAnalysisLevel": "9.0.300"
},
"frameworks": {
"netstandard2.1": {
"targetAlias": "netstandard2.1",
"dependencies": {
"Lib.Harmony": {
"target": "Package",
"version": "[2.4.1, )"
}
},
"imports": [
"net461",
"net462",
"net47",
"net471",
"net472",
"net48",
"net481"
],
"assetTargetFallback": true,
"warn": true,
"frameworkReferences": {
"NETStandard.Library": {
"privateAssets": "all"
}
},
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.306\\RuntimeIdentifierGraph.json"
}
}
}
}
}

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\Lenovo\.nuget\packages\;D:\vsShare\NuGetPackages</NuGetPackageFolders>
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.14.0</NuGetToolVersion>
</PropertyGroup>
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<SourceRoot Include="C:\Users\Lenovo\.nuget\packages\" />
<SourceRoot Include="D:\vsShare\NuGetPackages\" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" />

View File

@@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]

View File

@@ -0,0 +1,22 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("CharacterPreview")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+8fcbdc5649e0b93fd1b771001f53cdbb81da2c78")]
[assembly: System.Reflection.AssemblyProductAttribute("CharacterPreview")]
[assembly: System.Reflection.AssemblyTitleAttribute("CharacterPreview")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
// 由 MSBuild WriteCodeFragment 类生成。

View File

@@ -0,0 +1 @@
a35a52a5782dcfa53768ea1eb0acd5bd17c1e3d2dfc5d618e23c884a1a098d9e

View File

@@ -0,0 +1,8 @@
is_global = true
build_property.RootNamespace = CharacterPreview
build_property.ProjectDir = D:\vs_project\DuckovMods\CharacterPreview\
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =
build_property.CsWinRTUseWindowsUIXamlProjections = false
build_property.EffectiveAnalysisLevelStyle =
build_property.EnableCodeStyleSeverity =

View File

@@ -0,0 +1 @@
9ceb1c845a1b18d2def0bf1eea585e3dc69eb9cff9872b77a69643b5e6fc7903

View File

@@ -0,0 +1,7 @@
D:\steam\steamapps\common\Escape from Duckov\Duckov_Data\Mods\CharacterPreview\CharacterPreview.dll
D:\vs_project\DuckovMods\CharacterPreview\obj\Debug\CharacterPreview.csproj.AssemblyReference.cache
D:\vs_project\DuckovMods\CharacterPreview\obj\Debug\CharacterPreview.GeneratedMSBuildEditorConfig.editorconfig
D:\vs_project\DuckovMods\CharacterPreview\obj\Debug\CharacterPreview.AssemblyInfoInputs.cache
D:\vs_project\DuckovMods\CharacterPreview\obj\Debug\CharacterPreview.AssemblyInfo.cs
D:\vs_project\DuckovMods\CharacterPreview\obj\Debug\CharacterPreview.csproj.CoreCompileInputs.cache
D:\vs_project\DuckovMods\CharacterPreview\obj\Debug\CharacterPreview.dll

Binary file not shown.

View File

@@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]

View File

@@ -0,0 +1,22 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("CharacterPreview")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+8fcbdc5649e0b93fd1b771001f53cdbb81da2c78")]
[assembly: System.Reflection.AssemblyProductAttribute("CharacterPreview")]
[assembly: System.Reflection.AssemblyTitleAttribute("CharacterPreview")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
// 由 MSBuild WriteCodeFragment 类生成。

View File

@@ -0,0 +1 @@
2dcee58ea3d10e33f69ccfa06e1fe3a4fe22d6ce0e9a2c07a40e5dff5a0c39dd

View File

@@ -0,0 +1,8 @@
is_global = true
build_property.RootNamespace = CharacterPreview
build_property.ProjectDir = D:\vs_project\DuckovMods\CharacterPreview\
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =
build_property.CsWinRTUseWindowsUIXamlProjections = false
build_property.EffectiveAnalysisLevelStyle =
build_property.EnableCodeStyleSeverity =

View File

@@ -0,0 +1 @@
c0134ee1afbfeb7d9ea2fc05c8c4e57fa94629a377c1dde9e9b9bbb913bac608

View File

@@ -0,0 +1,7 @@
D:\steam\steamapps\common\Escape from Duckov\Duckov_Data\Mods\CharacterPreview\CharacterPreview.dll
D:\vs_project\DuckovMods\CharacterPreview\obj\Release\CharacterPreview.csproj.AssemblyReference.cache
D:\vs_project\DuckovMods\CharacterPreview\obj\Release\CharacterPreview.GeneratedMSBuildEditorConfig.editorconfig
D:\vs_project\DuckovMods\CharacterPreview\obj\Release\CharacterPreview.AssemblyInfoInputs.cache
D:\vs_project\DuckovMods\CharacterPreview\obj\Release\CharacterPreview.AssemblyInfo.cs
D:\vs_project\DuckovMods\CharacterPreview\obj\Release\CharacterPreview.csproj.CoreCompileInputs.cache
D:\vs_project\DuckovMods\CharacterPreview\obj\Release\CharacterPreview.dll

Binary file not shown.

View File

@@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]

View File

@@ -0,0 +1,22 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("CharacterPreview")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+9b9121897387fa7e2a57d76690a2a9ae848b1705")]
[assembly: System.Reflection.AssemblyProductAttribute("CharacterPreview")]
[assembly: System.Reflection.AssemblyTitleAttribute("CharacterPreview")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
// 由 MSBuild WriteCodeFragment 类生成。

View File

@@ -0,0 +1 @@
cdbee0654e5bd2c9896d77407e3d7ce5ae6183e3f37c36106e534970cbdd063c

View File

@@ -0,0 +1,8 @@
is_global = true
build_property.RootNamespace = CharacterPreview
build_property.ProjectDir = D:\vs_project\DuckovMods\CharacterPreview\
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =
build_property.CsWinRTUseWindowsUIXamlProjections = false
build_property.EffectiveAnalysisLevelStyle =
build_property.EnableCodeStyleSeverity =

View File

@@ -0,0 +1,239 @@
{
"version": 3,
"targets": {
".NETStandard,Version=v2.1": {
"Lib.Harmony/2.4.1": {
"type": "package",
"dependencies": {
"Lib.Harmony.Ref": "2.4.1"
},
"compile": {
"lib/netstandard2.0/_._": {}
},
"runtime": {
"lib/netstandard2.0/_._": {}
}
},
"Lib.Harmony.Ref/2.4.1": {
"type": "package",
"dependencies": {
"System.Reflection.Emit": "4.7.0"
},
"compile": {
"ref/netstandard2.0/0Harmony.dll": {
"related": ".xml"
}
}
},
"System.Reflection.Emit/4.7.0": {
"type": "package",
"compile": {
"ref/netstandard2.1/_._": {}
},
"runtime": {
"lib/netstandard2.1/_._": {}
}
}
}
},
"libraries": {
"Lib.Harmony/2.4.1": {
"sha512": "iLTZi/kKKB18jYEIwReZSx2xXyVUh4J1swReMgvYBBBn4tzA1Nd0PJlVyntY5BDdSiXSxzmvjc/3OYfFs0YwFg==",
"type": "package",
"path": "lib.harmony/2.4.1",
"files": [
".nupkg.metadata",
".signature.p7s",
"HarmonyLogo.png",
"LICENSE",
"README.md",
"lib.harmony.2.4.1.nupkg.sha512",
"lib.harmony.nuspec",
"lib/net35/0Harmony.dll",
"lib/net35/0Harmony.pdb",
"lib/net35/0Harmony.xml",
"lib/net452/0Harmony.dll",
"lib/net452/0Harmony.pdb",
"lib/net452/0Harmony.xml",
"lib/net472/0Harmony.dll",
"lib/net472/0Harmony.pdb",
"lib/net472/0Harmony.xml",
"lib/net48/0Harmony.dll",
"lib/net48/0Harmony.pdb",
"lib/net48/0Harmony.xml",
"lib/net5.0/0Harmony.dll",
"lib/net5.0/0Harmony.pdb",
"lib/net5.0/0Harmony.xml",
"lib/net6.0/0Harmony.dll",
"lib/net6.0/0Harmony.pdb",
"lib/net6.0/0Harmony.xml",
"lib/net7.0/0Harmony.dll",
"lib/net7.0/0Harmony.pdb",
"lib/net7.0/0Harmony.xml",
"lib/net8.0/0Harmony.dll",
"lib/net8.0/0Harmony.pdb",
"lib/net8.0/0Harmony.xml",
"lib/net9.0/0Harmony.dll",
"lib/net9.0/0Harmony.pdb",
"lib/net9.0/0Harmony.xml",
"lib/netcoreapp3.0/0Harmony.dll",
"lib/netcoreapp3.0/0Harmony.pdb",
"lib/netcoreapp3.0/0Harmony.xml",
"lib/netcoreapp3.1/0Harmony.dll",
"lib/netcoreapp3.1/0Harmony.pdb",
"lib/netcoreapp3.1/0Harmony.xml",
"lib/netstandard2.0/_._"
]
},
"Lib.Harmony.Ref/2.4.1": {
"sha512": "+u1y2Qd6OlSUQ8JtrsrSo3adnAsrXMJ2YPYtbW+FAmdPI5yw34M9VX4bKl8ZwRuUzaGzZIz+oGMbn/yS4fWtZw==",
"type": "package",
"path": "lib.harmony.ref/2.4.1",
"files": [
".nupkg.metadata",
".signature.p7s",
"HarmonyLogo.png",
"LICENSE",
"README.md",
"lib.harmony.ref.2.4.1.nupkg.sha512",
"lib.harmony.ref.nuspec",
"ref/netstandard2.0/0Harmony.dll",
"ref/netstandard2.0/0Harmony.xml"
]
},
"System.Reflection.Emit/4.7.0": {
"sha512": "VR4kk8XLKebQ4MZuKuIni/7oh+QGFmZW3qORd1GvBq/8026OpW501SzT/oypwiQl4TvT8ErnReh/NzY9u+C6wQ==",
"type": "package",
"path": "system.reflection.emit/4.7.0",
"files": [
".nupkg.metadata",
".signature.p7s",
"LICENSE.TXT",
"THIRD-PARTY-NOTICES.TXT",
"lib/MonoAndroid10/_._",
"lib/MonoTouch10/_._",
"lib/net45/_._",
"lib/netcore50/System.Reflection.Emit.dll",
"lib/netcoreapp2.0/_._",
"lib/netstandard1.1/System.Reflection.Emit.dll",
"lib/netstandard1.1/System.Reflection.Emit.xml",
"lib/netstandard1.3/System.Reflection.Emit.dll",
"lib/netstandard2.0/System.Reflection.Emit.dll",
"lib/netstandard2.0/System.Reflection.Emit.xml",
"lib/netstandard2.1/_._",
"lib/xamarinios10/_._",
"lib/xamarinmac20/_._",
"lib/xamarintvos10/_._",
"lib/xamarinwatchos10/_._",
"ref/MonoAndroid10/_._",
"ref/MonoTouch10/_._",
"ref/net45/_._",
"ref/netcoreapp2.0/_._",
"ref/netstandard1.1/System.Reflection.Emit.dll",
"ref/netstandard1.1/System.Reflection.Emit.xml",
"ref/netstandard1.1/de/System.Reflection.Emit.xml",
"ref/netstandard1.1/es/System.Reflection.Emit.xml",
"ref/netstandard1.1/fr/System.Reflection.Emit.xml",
"ref/netstandard1.1/it/System.Reflection.Emit.xml",
"ref/netstandard1.1/ja/System.Reflection.Emit.xml",
"ref/netstandard1.1/ko/System.Reflection.Emit.xml",
"ref/netstandard1.1/ru/System.Reflection.Emit.xml",
"ref/netstandard1.1/zh-hans/System.Reflection.Emit.xml",
"ref/netstandard1.1/zh-hant/System.Reflection.Emit.xml",
"ref/netstandard2.0/System.Reflection.Emit.dll",
"ref/netstandard2.0/System.Reflection.Emit.xml",
"ref/netstandard2.1/_._",
"ref/xamarinios10/_._",
"ref/xamarinmac20/_._",
"ref/xamarintvos10/_._",
"ref/xamarinwatchos10/_._",
"runtimes/aot/lib/netcore50/System.Reflection.Emit.dll",
"runtimes/aot/lib/netcore50/System.Reflection.Emit.xml",
"system.reflection.emit.4.7.0.nupkg.sha512",
"system.reflection.emit.nuspec",
"useSharedDesignerContext.txt",
"version.txt"
]
}
},
"projectFileDependencyGroups": {
".NETStandard,Version=v2.1": [
"Lib.Harmony >= 2.4.1"
]
},
"packageFolders": {
"C:\\Users\\Lenovo\\.nuget\\packages\\": {},
"D:\\vsShare\\NuGetPackages": {}
},
"project": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "D:\\vs_project\\DuckovMods\\CharacterPreview\\CharacterPreview.csproj",
"projectName": "CharacterPreview",
"projectPath": "D:\\vs_project\\DuckovMods\\CharacterPreview\\CharacterPreview.csproj",
"packagesPath": "C:\\Users\\Lenovo\\.nuget\\packages\\",
"outputPath": "D:\\vs_project\\DuckovMods\\CharacterPreview\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"D:\\vsShare\\NuGetPackages"
],
"configFilePaths": [
"C:\\Users\\Lenovo\\AppData\\Roaming\\NuGet\\NuGet.Config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
],
"originalTargetFrameworks": [
"netstandard2.1"
],
"sources": {
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
"https://api.nuget.org/v3/index.json": {}
},
"frameworks": {
"netstandard2.1": {
"targetAlias": "netstandard2.1",
"projectReferences": {}
}
},
"warningProperties": {
"warnAsError": [
"NU1605"
]
},
"restoreAuditProperties": {
"enableAudit": "true",
"auditLevel": "low",
"auditMode": "direct"
},
"SdkAnalysisLevel": "9.0.300"
},
"frameworks": {
"netstandard2.1": {
"targetAlias": "netstandard2.1",
"dependencies": {
"Lib.Harmony": {
"target": "Package",
"version": "[2.4.1, )"
}
},
"imports": [
"net461",
"net462",
"net47",
"net471",
"net472",
"net48",
"net481"
],
"assetTargetFallback": true,
"warn": true,
"frameworkReferences": {
"NETStandard.Library": {
"privateAssets": "all"
}
},
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.306\\RuntimeIdentifierGraph.json"
}
}
}
}

View File

@@ -0,0 +1,12 @@
{
"version": 2,
"dgSpecHash": "7zqX1jgBbf8=",
"success": true,
"projectFilePath": "D:\\vs_project\\DuckovMods\\CharacterPreview\\CharacterPreview.csproj",
"expectedPackageFiles": [
"C:\\Users\\Lenovo\\.nuget\\packages\\lib.harmony\\2.4.1\\lib.harmony.2.4.1.nupkg.sha512",
"C:\\Users\\Lenovo\\.nuget\\packages\\lib.harmony.ref\\2.4.1\\lib.harmony.ref.2.4.1.nupkg.sha512",
"C:\\Users\\Lenovo\\.nuget\\packages\\system.reflection.emit\\4.7.0\\system.reflection.emit.4.7.0.nupkg.sha512"
],
"logs": []
}

View File

@@ -0,0 +1 @@
"restore":{"projectUniqueName":"D:\\vs_project\\DuckovMods\\CharacterPreview\\CharacterPreview.csproj","projectName":"CharacterPreview","projectPath":"D:\\vs_project\\DuckovMods\\CharacterPreview\\CharacterPreview.csproj","outputPath":"D:\\vs_project\\DuckovMods\\CharacterPreview\\obj\\","projectStyle":"PackageReference","fallbackFolders":["D:\\vsShare\\NuGetPackages"],"originalTargetFrameworks":["netstandard2.1"],"sources":{"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\":{},"https://api.nuget.org/v3/index.json":{}},"frameworks":{"netstandard2.1":{"targetAlias":"netstandard2.1","projectReferences":{}}},"warningProperties":{"warnAsError":["NU1605"]},"restoreAuditProperties":{"enableAudit":"true","auditLevel":"low","auditMode":"direct"},"SdkAnalysisLevel":"9.0.300"}"frameworks":{"netstandard2.1":{"targetAlias":"netstandard2.1","dependencies":{"Lib.Harmony":{"target":"Package","version":"[2.4.1, )"}},"imports":["net461","net462","net47","net471","net472","net48","net481"],"assetTargetFallback":true,"warn":true,"frameworkReferences":{"NETStandard.Library":{"privateAssets":"all"}},"runtimeIdentifierGraphPath":"C:\\Program Files\\dotnet\\sdk\\9.0.306\\RuntimeIdentifierGraph.json"}}

View File

@@ -0,0 +1 @@
17627872868371582

View File

@@ -0,0 +1 @@
17627872868371582

View File

@@ -1,24 +1,28 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36603.0 d17.14
VisualStudioVersion = 17.14.36603.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThirdPersonCamera", "ThirdPersonCamera\ThirdPersonCamera.csproj", "{B35A0F60-1B8F-4CC5-9F92-3B177DFB4078}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SceneSnapshot", "SceneSnapshot\SceneSnapshot.csproj", "{623270B2-9677-44F2-9137-8EF50527E8D5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HideCharacter", "HideCharacter\HideCharacter.csproj", "{809F6CA1-E757-46CD-B8AB-89522D166140}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HitFeedback", "HitFeedback\HitFeedback.csproj", "{FEA30679-27B6-4413-91A5-EB08FCD9F02B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UIFrame", "UIFrame\UIFrame.csproj", "{78BAF98F-64B2-41EB-BA04-2381E1D37AC8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Theme", "Theme\Theme.csproj", "{7CCF2612-AF15-42F0-B472-8C8F8CAB9B20}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SceneView", "SceneView\SceneView.csproj", "{87AA16B9-C7F9-456E-8F57-CE05F393D91F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CharacterPreview", "CharacterPreview\CharacterPreview.csproj", "{4D2577DB-5915-40AC-869B-68C37D28AC8F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B35A0F60-1B8F-4CC5-9F92-3B177DFB4078}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B35A0F60-1B8F-4CC5-9F92-3B177DFB4078}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B35A0F60-1B8F-4CC5-9F92-3B177DFB4078}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B35A0F60-1B8F-4CC5-9F92-3B177DFB4078}.Release|Any CPU.Build.0 = Release|Any CPU
{623270B2-9677-44F2-9137-8EF50527E8D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{623270B2-9677-44F2-9137-8EF50527E8D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{623270B2-9677-44F2-9137-8EF50527E8D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -27,6 +31,26 @@ Global
{809F6CA1-E757-46CD-B8AB-89522D166140}.Debug|Any CPU.Build.0 = Debug|Any CPU
{809F6CA1-E757-46CD-B8AB-89522D166140}.Release|Any CPU.ActiveCfg = Release|Any CPU
{809F6CA1-E757-46CD-B8AB-89522D166140}.Release|Any CPU.Build.0 = Release|Any CPU
{FEA30679-27B6-4413-91A5-EB08FCD9F02B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FEA30679-27B6-4413-91A5-EB08FCD9F02B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FEA30679-27B6-4413-91A5-EB08FCD9F02B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FEA30679-27B6-4413-91A5-EB08FCD9F02B}.Release|Any CPU.Build.0 = Release|Any CPU
{78BAF98F-64B2-41EB-BA04-2381E1D37AC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{78BAF98F-64B2-41EB-BA04-2381E1D37AC8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{78BAF98F-64B2-41EB-BA04-2381E1D37AC8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{78BAF98F-64B2-41EB-BA04-2381E1D37AC8}.Release|Any CPU.Build.0 = Release|Any CPU
{7CCF2612-AF15-42F0-B472-8C8F8CAB9B20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7CCF2612-AF15-42F0-B472-8C8F8CAB9B20}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7CCF2612-AF15-42F0-B472-8C8F8CAB9B20}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7CCF2612-AF15-42F0-B472-8C8F8CAB9B20}.Release|Any CPU.Build.0 = Release|Any CPU
{87AA16B9-C7F9-456E-8F57-CE05F393D91F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{87AA16B9-C7F9-456E-8F57-CE05F393D91F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{87AA16B9-C7F9-456E-8F57-CE05F393D91F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{87AA16B9-C7F9-456E-8F57-CE05F393D91F}.Release|Any CPU.Build.0 = Release|Any CPU
{4D2577DB-5915-40AC-869B-68C37D28AC8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4D2577DB-5915-40AC-869B-68C37D28AC8F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4D2577DB-5915-40AC-869B-68C37D28AC8F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4D2577DB-5915-40AC-869B-68C37D28AC8F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -1,24 +1,124 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/AddReferences/RecentPaths/=D_003A_005Csteam_005Csteamapps_005Ccommon_005CEscape_0020from_0020Duckov_005CDuckov_005FData_005CManaged_005CEflatun_002ESceneReference_002Edll/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/AddReferences/RecentPaths/=D_003A_005Csteam_005Csteamapps_005Ccommon_005CEscape_0020from_0020Duckov_005CDuckov_005FData_005CManaged_005CFMODUnity_002Edll/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/AddReferences/RecentPaths/=D_003A_005Csteam_005Csteamapps_005Ccommon_005CEscape_0020from_0020Duckov_005CDuckov_005FData_005CManaged_005CSodaLocalization_002Edll/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/AddReferences/RecentPaths/=D_003A_005Csteam_005Csteamapps_005Cworkshop_005Ccontent_005C3167020_005C3589088839_005CHarmonyLoadMod_002Edll/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/AddReferences/RecentPaths/=D_003A_005Csteam_005Csteamapps_005Cworkshop_005Ccontent_005C3167020_005C3590674339_005CModConfig_002Edll/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/AddReferences/RecentPaths/=D_003A_005Csteam_005Csteamapps_005Cworkshop_005Ccontent_005C3167020_005C3591672666_005Cqingye_002Edll/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/AddReferences/RecentPaths/=D_003A_005Csteam_005Csteamapps_005Cworkshop_005Ccontent_005C3167020_005C3596659565_005CCustomQuack_002Edll/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/AddReferences/RecentPaths/=D_003A_005Csteam_005Csteamapps_005Cworkshop_005Ccontent_005C3167020_005C3599429514_005CCharacterIzuna_002Edll/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/AddReferences/RecentPaths/=D_003A_005Csteam_005Csteamapps_005Cworkshop_005Ccontent_005C3167020_005C3600560151_005CDuckovCustomModel_002ECore_002Edll/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/AddReferences/RecentPaths/=D_003A_005Csteam_005Csteamapps_005Cworkshop_005Ccontent_005C3167020_005C3600560151_005CDuckovCustomModel_002Edll/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/AddReferences/RecentPaths/=D_003A_005Csteam_005Csteamapps_005Cworkshop_005Ccontent_005C3167020_005C3600560151_005CDuckovCustomModel_002EGameModules_002Edll/@EntryIndexedValue">True</s:Boolean>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAIMainBrain_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F7c82da41edbe4051b5c92d726719f58b199200_003F9a_003F06cede9f_003FAIMainBrain_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAIMainBrain_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F52_003F7c426cea_003FAIMainBrain_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAimTargetFinder_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F492b6cef9eac4cd497c1ffcbfcb9161f197200_003Fa9_003F4ac92f25_003FAimTargetFinder_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAssembly_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F2bc5ed240d5c402c8b6abd14ffcee26791db88_003F31_003Fe5abc737_003FAssembly_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAudioManager_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F95_003F5092a809_003FAudioManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABehaviour_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fc39a522eee05469b8171a6cfeb646c591543b0_003Fc1_003Faa1a31b2_003FBehaviour_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABlackScreen_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F7c82da41edbe4051b5c92d726719f58b199200_003F62_003Fd4fcffde_003FBlackScreen_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABlackScreen_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F83_003Fdd84d8e1_003FBlackScreen_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABuffsDisplay_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F72_003F6940c184_003FBuffsDisplay_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABuff_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fc9_003F95f738f9_003FBuff_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AButton_005FLoadMainMenu_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fae_003F1c447fe1_003FButton_005FLoadMainMenu_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACameraArmControl_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F492b6cef9eac4cd497c1ffcbfcb9161f197200_003Ff5_003F4622470d_003FCameraArmControl_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACameraArm_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F492b6cef9eac4cd497c1ffcbfcb9161f197200_003Fdc_003Fc73e8a9f_003FCameraArm_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACameraArm_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Ff6_003F5678008c_003FCameraArm_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACharacterAnimationControl_005FMagicBlend_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F7c82da41edbe4051b5c92d726719f58b199200_003F69_003F48d1fd4f_003FCharacterAnimationControl_005FMagicBlend_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACharacterCreator_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F492b6cef9eac4cd497c1ffcbfcb9161f197200_003F06_003Ff3fe5490_003FCharacterCreator_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACharacterCreator_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F7c82da41edbe4051b5c92d726719f58b199200_003F2d_003Fdafe66b2_003FCharacterCreator_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACharacterCreator_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fd3_003F2e360998_003FCharacterCreator_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACharacterEquipmentController_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F35_003F0c1fbbe5_003FCharacterEquipmentController_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACharacterMainControl_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F492b6cef9eac4cd497c1ffcbfcb9161f197200_003F2c_003Fe5626d7e_003FCharacterMainControl_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACharacterMainControl_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F7c82da41edbe4051b5c92d726719f58b199200_003F3e_003Fdd1453e6_003FCharacterMainControl_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACharacterMainControl_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F4c_003Faffc257a_003FCharacterMainControl_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACharacterModel_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F7c82da41edbe4051b5c92d726719f58b199200_003F59_003F9266675b_003FCharacterModel_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACharacterRandomPreset_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F7c82da41edbe4051b5c92d726719f58b199200_003Fc9_003F606976e3_003FCharacterRandomPreset_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACharacterRandomPreset_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fd5_003Fb696ff66_003FCharacterRandomPreset_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AConfigManager_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F4b1e96d83e40723ba3f28167886c0a7f741af767cb5e1875bed6340c9fdf3a_003FConfigManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AConstant_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F30a5524d7f498d1bec81b5e5f58e3aa440082ae311a90213325fe8f44084_003FConstant_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACopyTextButton_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fc4_003F9995fb11_003FCopyTextButton_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACustomFaceInstance_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F7c82da41edbe4051b5c92d726719f58b199200_003F5d_003F39ead7d8_003FCustomFaceInstance_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACustomFaceInstance_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Ff5_003F7e299618_003FCustomFaceInstance_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACustomFaceManager_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F7c82da41edbe4051b5c92d726719f58b199200_003Ffe_003F78a87e79_003FCustomFaceManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADamageInfo_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F0b_003F36368d2b_003FDamageInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADamageTypes_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Faf_003Fe7c272fb_003FDamageTypes_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADebugManager_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fb02e352b027d497b8571da05e536c2f682000_003Fe6_003F0a55a8d7_003FDebugManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADuckovScreenCapturer_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F492b6cef9eac4cd497c1ffcbfcb9161f197200_003F59_003F92754824_003FDuckovScreenCapturer_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEventSystem_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fc5cfaab0e1d24cfeb3c73b55e9a4f72b40c00_003F99_003F879c7f70_003FEventSystem_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AFadeElement_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F9b_003Fbe78cf8b_003FFadeElement_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AFadeGroup_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F4e_003F69f8fcae_003FFadeGroup_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AFogOfWarManager_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fd3_003Fb21152a5_003FFogOfWarManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AGameCamera_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F492b6cef9eac4cd497c1ffcbfcb9161f197200_003Fd2_003F4160411a_003FGameCamera_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AGameManager_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F7c82da41edbe4051b5c92d726719f58b199200_003F92_003F37e04f57_003FGameManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AGameManager_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F94_003Fdd33b16b_003FGameManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AGameObject_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fc39a522eee05469b8171a6cfeb646c591543b0_003F52_003F998640fe_003FGameObject_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AGameplayDataSettings_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F492b6cef9eac4cd497c1ffcbfcb9161f197200_003Fa4_003Ffea6779d_003FGameplayDataSettings_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AGameplayDataSettings_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F7c82da41edbe4051b5c92d726719f58b199200_003F55_003F21969a4b_003FGameplayDataSettings_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AGameplayDataSettings_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F1f_003F8b814bde_003FGameplayDataSettings_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AGameplayUIManager_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fcc_003Fe767eb5a_003FGameplayUIManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AGameRulesManager_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fb1_003Ff82c3168_003FGameRulesManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AHarmonyLoader_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003Fea4edbee2cb3c8675e39a67ad559e461525f6ee7235c385e25a68da4aa9f3_003FHarmonyLoader_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AHarmonyPatch_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F8e06e6c4d12e4db29fecbb8a1ed2864918400_003F11_003F77145916_003FHarmonyPatch_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AInputManager_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F7c82da41edbe4051b5c92d726719f58b199200_003F26_003F732d33cb_003FInputManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AInventoryData_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F71a3d79537274de2b084d0fd207549d617200_003F5e_003F6152b6b7_003FInventoryData_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AItemDisplay_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F73_003Fa6db3a6c_003FItemDisplay_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AItemSavesUtilities_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F7c82da41edbe4051b5c92d726719f58b199200_003Fe6_003F83a39c27_003FItemSavesUtilities_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ALevelConfig_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F492b6cef9eac4cd497c1ffcbfcb9161f197200_003Fcc_003Fcfd90334_003FLevelConfig_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ALevelManager_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F7c82da41edbe4051b5c92d726719f58b199200_003Ff1_003F866b116e_003FLevelManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ALevelManager_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fe0_003F6f24caa1_003FLevelManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ALocalizationDataModel_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F85f08cf721894642b6034bb4eee4d75b4000_003F79_003F52786d7e_003FLocalizationDataModel_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ALocalizationManager_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F85f08cf721894642b6034bb4eee4d75b4000_003F2c_003Ff9202561_003FLocalizationManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ALocalization_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F3e16ded3f21451a1c36f19b36e4848a2a6c664ab5a459ec66f4f663fc6f2b6_003FLocalization_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMainCharacterStatValueDisplay_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F08_003Fd04928fd_003FMainCharacterStatValueDisplay_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMainMenuCamera_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F7c82da41edbe4051b5c92d726719f58b199200_003Fdc_003F4aa81a69_003FMainMenuCamera_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMainMenu_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F7c82da41edbe4051b5c92d726719f58b199200_003F72_003F2cdb714d_003FMainMenu_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMainMenu_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F21_003F0ec534a6_003FMainMenu_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AModBehaviour_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F15eaac0daac842bca117926c0c9be2781a00_003F37_003Fa09d99b2_003FModBehaviour_002Ecs_002Fz_003A4_002D3/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AModBehaviour_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F2576bc5c328e49adb3eb5fc3c914a4544600_003Fb4_003Fa1f05223_003FModBehaviour_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AModBehaviour_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F4784c8b601ed4e3c93b990a18b8bd7a4a800_003F3e_003Fb80788cb_003FModBehaviour_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AModBehaviour_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Ff3_003Ff5f2b19a_003FModBehaviour_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AModBehaviour_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Faf3c3aa7ce14487c914dade28d96504e70a00_003F26_003Fe998e749_003FModBehaviour_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AModBehaviour_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fe495fb8cdd21493497febf8ce397a8d71400_003F4a_003F0c719bc6_003FModBehaviour_002Ecs_002Fz_003A2_002D1/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AModBehaviour_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003Ff38c631c761d815c597f4ac13b877f5ac5a9341f8932d31d2dc7f87ddfd159_003FModBehaviour_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AModelHandler_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F3efbeee73bde1c365962611930da5fbdc1162b791aa563e78e6d49901f2b_003FModelHandler_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AModelInfo_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F33793f39d20317172713cd91860358cfd5e2efeae28cbd2b3b87a37b4cb2_003FModelInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AModelListManager_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F41d02c4a2db7d9141abf815ad27e337e6f7e6edf3816bcc2a6797ca5cdf_003FModelListManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AModelTarget_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F238e312a614ee6d46bfb2a691ae5d2db9d90d429faa73e880624e28e2c0922b_003FModelTarget_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AModEntry_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F5193603f30474f129ab2e32f9421b04e2f400_003F32_003F20a650c9_003FModEntry_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AModEntry_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F5193603f30474f129ab2e32f9421b04e2f400_003F32_003F20a650c9_003FModEntry_002Ecs_002Fz_003A2_002D1/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AModEntry_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003Fd88ffd86a8668ed6682fd44eb522fc9d48a4c236bb72c18388222a8fb390a8_003FModEntry_002Ecs_002Fz_003A2_002D1/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AModInfo_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F3c_003F9c6fe4cd_003FModInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AModLoader_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F8cc7d3b3c55e278feec015d8a76d68a7d4671624d0ccfb9e3563ea43d6d5a7a_003FModLoader_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AModLogger_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F15e3577ebca54bf713c697676a81da8c5a3cf88bdd27bfa8ae3f468b644d13_003FModLogger_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AModManager_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F7c82da41edbe4051b5c92d726719f58b199200_003F4c_003F3b41863d_003FModManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AModManager_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fe5_003Ff729e34a_003FModManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMonoBehaviour_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fc39a522eee05469b8171a6cfeb646c591543b0_003F02_003F65608a5e_003FMonoBehaviour_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMultiInteractionMenu_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003F88_003F0d34c6e2_003FMultiInteractionMenu_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMultiInteraction_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Ff8_003F995da332_003FMultiInteraction_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AObject_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fc39a522eee05469b8171a6cfeb646c591543b0_003F4b_003F927bbb51_003FObject_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AOcclusionFadeChecker_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F492b6cef9eac4cd497c1ffcbfcb9161f197200_003F05_003F7aa60d94_003FOcclusionFadeChecker_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AOcclusionFadeChecker_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fa8_003Fd12e1cf7_003FOcclusionFadeChecker_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AOcclusionFadeManager_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F492b6cef9eac4cd497c1ffcbfcb9161f197200_003F70_003F6aba3c97_003FOcclusionFadeManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AOnPointerClick_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F7c82da41edbe4051b5c92d726719f58b199200_003F25_003Fba6ddb49_003FOnPointerClick_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AOptionsPanel_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fe1_003Fac8e1feb_003FOptionsPanel_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AOutline_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fc5cfaab0e1d24cfeb3c73b55e9a4f72b40c00_003F22_003F77407fec_003FOutline_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003APauseMenu_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F7c82da41edbe4051b5c92d726719f58b199200_003Fbe_003Faca9be37_003FPauseMenu_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003APrefabPool_00601_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F2fb2e03687cc4f6182420d823a41eca07200_003F05_003F8982f5f5_003FPrefabPool_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASceneLoader_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F7c82da41edbe4051b5c92d726719f58b199200_003Fdd_003Fa610281f_003FSceneLoader_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASceneLoadingContext_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fbf_003F2b3732af_003FSceneLoadingContext_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AScriptableObject_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fc39a522eee05469b8171a6cfeb646c591543b0_003F4d_003F1a33cb14_003FScriptableObject_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASlot_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F891999c761c24c49afb16405089f08e817400_003Fa1_003F8574f295_003FSlot_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASpriteRendererLocalizor_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F85f08cf721894642b6034bb4eee4d75b4000_003F8f_003F9f8025e8_003FSpriteRendererLocalizor_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASystemLanguage_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fc39a522eee05469b8171a6cfeb646c591543b0_003F5f_003Ffb4a10f4_003FSystemLanguage_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATextLocalizor_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F85f08cf721894642b6034bb4eee4d75b4000_003Fce_003F46f8a570_003FTextLocalizor_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATitle_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fe8_003F42124cdd_003FTitle_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AUIInputManager_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F7c82da41edbe4051b5c92d726719f58b199200_003F20_003Fd367484d_003FUIInputManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AUIPrefabsReference_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fae7b55f2e79e4a30b19151f53aa9af29197600_003Fb5_003F49d2159e_003FUIPrefabsReference_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AUsingModel_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F5193603f30474f129ab2e32f9421b04e2f400_003F9b_003F1fe92a0a_003FUsingModel_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AVolumeComponent_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fb02e352b027d497b8571da05e536c2f682000_003F36_003F74cee2c4_003FVolumeComponent_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AVolume_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fb02e352b027d497b8571da05e536c2f682000_003Ffa_003F02e0206c_003FVolume_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003A_005F003CModule_005F003E_002Ecs_002Fl_003AC_0021_003FUsers_003FLenovo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Faf3c3aa7ce14487c914dade28d96504e70a00_003F9c_003Ff3cdecda_003F_005F003CModule_005F003E_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/Environment/AssemblyExplorer/XmlDocument/@EntryValue">&lt;AssemblyExplorer&gt;&#xD;
&lt;Assembly Path="D:\steam\steamapps\common\Escape from Duckov\Duckov_Data\Managed\ItemStatsSystem.dll" /&gt;&#xD;
&lt;/AssemblyExplorer&gt;</s:String></wpf:ResourceDictionary>

View File

@@ -0,0 +1,488 @@
using System;
using System.Linq;
using System.Reflection;
using UnityEngine;
//替换为你的mod命名空间, 防止多个同名ModConfigAPI冲突
namespace HideCharacter.Api {
/// <summary>
/// ModConfig 安全接口封装类 - 提供不抛异常的静态接口
/// ModConfig Safe API Wrapper Class - Provides non-throwing static interfaces
/// </summary>
public static class ModConfigAPI
{
public static string ModConfigName = "ModConfig";
//Ensure this match the number of ModConfig.ModBehaviour.VERSION
//这里确保版本号与ModConfig.ModBehaviour.VERSION匹配
private const int ModConfigVersion = 1;
private static string TAG = $"ModConfig_v{ModConfigVersion}";
private static Type modBehaviourType;
private static Type optionsManagerType;
public static bool isInitialized = false;
private static bool versionChecked = false;
private static bool isVersionCompatible = false;
/// <summary>
/// 检查版本兼容性
/// Check version compatibility
/// </summary>
private static bool CheckVersionCompatibility()
{
if (versionChecked)
return isVersionCompatible;
try
{
// 尝试获取 ModConfig 的版本号
// Try to get ModConfig version number
FieldInfo versionField = modBehaviourType.GetField("VERSION", BindingFlags.Public | BindingFlags.Static);
if (versionField != null && versionField.FieldType == typeof(int))
{
int modConfigVersion = (int)versionField.GetValue(null);
isVersionCompatible = (modConfigVersion == ModConfigVersion);
if (!isVersionCompatible)
{
Debug.LogError($"[{TAG}] 版本不匹配API版本: {ModConfigVersion}, ModConfig版本: {modConfigVersion}");
return false;
}
Debug.Log($"[{TAG}] 版本检查通过: {ModConfigVersion}");
versionChecked = true;
return true;
}
else
{
// 如果找不到版本字段,发出警告但继续运行(向后兼容)
// If version field not found, warn but continue (backward compatibility)
Debug.LogWarning($"[{TAG}] 未找到版本信息字段,跳过版本检查");
isVersionCompatible = true;
versionChecked = true;
return true;
}
}
catch (Exception ex)
{
Debug.LogError($"[{TAG}] 版本检查失败: {ex.Message}");
isVersionCompatible = false;
versionChecked = true;
return false;
}
}
/// <summary>
/// 初始化 ModConfigAPI检查必要的函数是否存在
/// Initialize ModConfigAPI, check if necessary functions exist
/// </summary>
public static bool Initialize()
{
try
{
if (isInitialized)
return true;
// 获取 ModBehaviour 类型
// Get ModBehaviour type
modBehaviourType = FindTypeInAssemblies("ModConfig.ModBehaviour");
if (modBehaviourType == null)
{
Debug.LogWarning($"[{TAG}] ModConfig.ModBehaviour 类型未找到ModConfig 可能未加载");
return false;
}
// 获取 OptionsManager_Mod 类型
// Get OptionsManager_Mod type
optionsManagerType = FindTypeInAssemblies("ModConfig.OptionsManager_Mod");
if (optionsManagerType == null)
{
Debug.LogWarning($"[{TAG}] ModConfig.OptionsManager_Mod 类型未找到");
return false;
}
// 检查版本兼容性
// Check version compatibility
if (!CheckVersionCompatibility())
{
Debug.LogWarning($"[{TAG}] ModConfig version mismatch!!!");
return false;
}
// 检查必要的静态方法是否存在
// Check if necessary static methods exist
string[] requiredMethods = {
"AddDropdownList",
"AddInputWithSlider",
"AddBoolDropdownList",
"AddOnOptionsChangedDelegate",
"RemoveOnOptionsChangedDelegate",
};
foreach (string methodName in requiredMethods)
{
MethodInfo method = modBehaviourType.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static);
if (method == null)
{
Debug.LogError($"[{TAG}] 必要方法 {methodName} 未找到");
return false;
}
}
isInitialized = true;
Debug.Log($"[{TAG}] ModConfigAPI 初始化成功");
return true;
}
catch (Exception ex)
{
Debug.LogError($"[{TAG}] 初始化失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 在所有已加载的程序集中查找类型
/// </summary>
private static Type FindTypeInAssemblies(string typeName)
{
try
{
// 获取当前域中的所有程序集
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies)
{
try
{
// 检查程序集名称是否包含 ModConfig
if (assembly.FullName.Contains("ModConfig"))
{
Debug.Log($"[{TAG}] 找到 ModConfig 相关程序集: {assembly.FullName}");
}
// 尝试在该程序集中查找类型
Type type = assembly.GetType(typeName);
if (type != null)
{
Debug.Log($"[{TAG}] 在程序集 {assembly.FullName} 中找到类型 {typeName}");
return type;
}
}
catch (Exception ex)
{
// 忽略单个程序集的查找错误
continue;
}
}
// 记录所有已加载的程序集用于调试
Debug.LogWarning($"[{TAG}] 在所有程序集中未找到类型 {typeName},已加载程序集数量: {assemblies.Length}");
foreach (var assembly in assemblies.Where(a => a.FullName.Contains("ModConfig")))
{
Debug.Log($"[{TAG}] ModConfig 相关程序集: {assembly.FullName}");
}
return null;
}
catch (Exception ex)
{
Debug.LogError($"[{TAG}] 程序集扫描失败: {ex.Message}");
return null;
}
}
/// <summary>
/// 安全地添加选项变更事件委托
/// Safely add options changed event delegate
/// </summary>
/// <param name="action">事件处理委托,参数为变更的选项键名</param>
/// <returns>是否成功添加</returns>
public static bool SafeAddOnOptionsChangedDelegate(Action<string> action)
{
if (!Initialize())
return false;
if (action == null)
{
Debug.LogWarning($"[{TAG}] 不能添加空的事件委托");
return false;
}
try
{
MethodInfo method = modBehaviourType.GetMethod("AddOnOptionsChangedDelegate", BindingFlags.Public | BindingFlags.Static);
method.Invoke(null, new object[] { action });
Debug.Log($"[{TAG}] 成功添加选项变更事件委托");
return true;
}
catch (Exception ex)
{
Debug.LogError($"[{TAG}] 添加选项变更事件委托失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 安全地移除选项变更事件委托
/// Safely remove options changed event delegate
/// </summary>
/// <param name="action">要移除的事件处理委托</param>
/// <returns>是否成功移除</returns>
public static bool SafeRemoveOnOptionsChangedDelegate(Action<string> action)
{
if (!Initialize())
return false;
if (action == null)
{
Debug.LogWarning($"[{TAG}] 不能移除空的事件委托");
return false;
}
try
{
MethodInfo method = modBehaviourType.GetMethod("RemoveOnOptionsChangedDelegate", BindingFlags.Public | BindingFlags.Static);
method.Invoke(null, new object[] { action });
Debug.Log($"[{TAG}] 成功移除选项变更事件委托");
return true;
}
catch (Exception ex)
{
Debug.LogError($"[{TAG}] 移除选项变更事件委托失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 安全地添加下拉列表配置项
/// Safely add dropdown list configuration item
/// </summary>
public static bool SafeAddDropdownList(string modName, string key, string description, System.Collections.Generic.SortedDictionary<string, object> options, Type valueType, object defaultValue)
{
key = $"{modName}_{key}";
if (!Initialize())
return false;
try
{
MethodInfo method = modBehaviourType.GetMethod("AddDropdownList", BindingFlags.Public | BindingFlags.Static);
method.Invoke(null, new object[] { modName, key, description, options, valueType, defaultValue });
Debug.Log($"[{TAG}] 成功添加下拉列表: {modName}.{key}");
return true;
}
catch (Exception ex)
{
Debug.LogError($"[{TAG}] 添加下拉列表失败 {modName}.{key}: {ex.Message}");
return false;
}
}
/// <summary>
/// 安全地添加带滑条的输入框配置项
/// Safely add input box with slider configuration item
/// </summary>
public static bool SafeAddInputWithSlider(string modName, string key, string description, Type valueType, object defaultValue, UnityEngine.Vector2? sliderRange = null)
{
key = $"{modName}_{key}";
if (!Initialize())
return false;
try
{
MethodInfo method = modBehaviourType.GetMethod("AddInputWithSlider", BindingFlags.Public | BindingFlags.Static);
// 处理可空参数
// Handle nullable parameters
object[] parameters = sliderRange.HasValue ?
new object[] { modName, key, description, valueType, defaultValue, sliderRange.Value } :
new object[] { modName, key, description, valueType, defaultValue, null };
method.Invoke(null, parameters);
Debug.Log($"[{TAG}] 成功添加滑条输入框: {modName}.{key}");
return true;
}
catch (Exception ex)
{
Debug.LogError($"[{TAG}] 添加滑条输入框失败 {modName}.{key}: {ex.Message}");
return false;
}
}
/// <summary>
/// 安全地添加布尔下拉列表配置项
/// Safely add boolean dropdown list configuration item
/// </summary>
public static bool SafeAddBoolDropdownList(string modName, string key, string description, bool defaultValue)
{
key = $"{modName}_{key}";
if (!Initialize())
return false;
try
{
MethodInfo method = modBehaviourType.GetMethod("AddBoolDropdownList", BindingFlags.Public | BindingFlags.Static);
method.Invoke(null, new object[] { modName, key, description, defaultValue });
Debug.Log($"[{TAG}] 成功添加布尔下拉列表: {modName}.{key}");
return true;
}
catch (Exception ex)
{
Debug.LogError($"[{TAG}] 添加布尔下拉列表失败 {modName}.{key}: {ex.Message}");
return false;
}
}
/// <summary>
/// 安全地加载配置值
/// Safely load configuration value
/// </summary>
/// <typeparam name="T">值的类型</typeparam>
/// <param name="key">配置键</param>
/// <param name="defaultValue">默认值</param>
/// <returns>加载的值或默认值</returns>
public static T SafeLoad<T>(string mod_name, string key, T defaultValue = default(T))
{
key = $"{mod_name}_{key}";
if (!Initialize())
return defaultValue;
if (string.IsNullOrEmpty(key))
{
Debug.LogWarning($"[{TAG}] 配置键不能为空");
return defaultValue;
}
try
{
MethodInfo loadMethod = optionsManagerType.GetMethod("Load", BindingFlags.Public | BindingFlags.Static);
if (loadMethod == null)
{
Debug.LogError($"[{TAG}] 未找到 OptionsManager_Mod.Load 方法");
return defaultValue;
}
// 获取泛型方法
MethodInfo genericLoadMethod = loadMethod.MakeGenericMethod(typeof(T));
object result = genericLoadMethod.Invoke(null, new object[] { key, defaultValue });
Debug.Log($"[{TAG}] 成功加载配置: {key} = {result}");
return (T)result;
}
catch (Exception ex)
{
Debug.LogError($"[{TAG}] 加载配置失败 {key}: {ex.Message}");
return defaultValue;
}
}
/// <summary>
/// 安全地保存配置值
/// Safely save configuration value
/// </summary>
/// <typeparam name="T">值的类型</typeparam>
/// <param name="key">配置键</param>
/// <param name="value">要保存的值</param>
/// <returns>是否保存成功</returns>
public static bool SafeSave<T>(string mod_name, string key, T value)
{
key = $"{mod_name}_{key}";
if (!Initialize())
return false;
if (string.IsNullOrEmpty(key))
{
Debug.LogWarning($"[{TAG}] 配置键不能为空");
return false;
}
try
{
MethodInfo saveMethod = optionsManagerType.GetMethod("Save", BindingFlags.Public | BindingFlags.Static);
if (saveMethod == null)
{
Debug.LogError($"[{TAG}] 未找到 OptionsManager_Mod.Save 方法");
return false;
}
// 获取泛型方法
MethodInfo genericSaveMethod = saveMethod.MakeGenericMethod(typeof(T));
genericSaveMethod.Invoke(null, new object[] { key, value });
Debug.Log($"[{TAG}] 成功保存配置: {key} = {value}");
return true;
}
catch (Exception ex)
{
Debug.LogError($"[{TAG}] 保存配置失败 {key}: {ex.Message}");
return false;
}
}
/// <summary>
/// 检查 ModConfig 是否可用
/// Check if ModConfig is available
/// </summary>
public static bool IsAvailable()
{
return Initialize();
}
/// <summary>
/// 获取 ModConfig 版本信息(如果存在)
/// Get ModConfig version information (if exists)
/// </summary>
public static string GetVersionInfo()
{
if (!Initialize())
return "ModConfig 未加载 | ModConfig not loaded";
try
{
// 尝试获取版本信息(如果 ModBehaviour 有相关字段或属性)
// Try to get version information (if ModBehaviour has related fields or properties)
FieldInfo versionField = modBehaviourType.GetField("VERSION", BindingFlags.Public | BindingFlags.Static);
if (versionField != null && versionField.FieldType == typeof(int))
{
int modConfigVersion = (int)versionField.GetValue(null);
string compatibility = (modConfigVersion == ModConfigVersion) ? "兼容" : "不兼容";
return $"ModConfig v{modConfigVersion} (API v{ModConfigVersion}, {compatibility})";
}
PropertyInfo versionProperty = modBehaviourType.GetProperty("VERSION", BindingFlags.Public | BindingFlags.Static);
if (versionProperty != null)
{
object versionValue = versionProperty.GetValue(null);
return versionValue?.ToString() ?? "未知版本 | Unknown version";
}
return "ModConfig 已加载(版本信息不可用) | ModConfig loaded (version info unavailable)";
}
catch
{
return "ModConfig 已加载(版本检查失败) | ModConfig loaded (version check failed)";
}
}
/// <summary>
/// 检查版本兼容性
/// Check version compatibility
/// </summary>
public static bool IsVersionCompatible()
{
if (!Initialize())
return false;
return isVersionCompatible;
}
}
}

View File

@@ -5,14 +5,23 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Authors>折纸的小箱子</Authors>
<AssemblyVersion>1.0.0</AssemblyVersion>
<AssemblyVersion>1.0.1</AssemblyVersion>
<DuckovPath>D:\steam\steamapps\common\Escape from Duckov</DuckovPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<ImplicitUsings>disable</ImplicitUsings>
<Version>1.0.1</Version>
<Title>隐藏角色</Title>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<OutputPath>D:\steam\steamapps\common\Escape from Duckov\Duckov_Data\Mods\HideCharacter</OutputPath>
<GenerateDependencyFile>false</GenerateDependencyFile>
<DebugType>none</DebugType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<OutputPath>D:\steam\steamapps\common\Escape from Duckov\Duckov_Data\Mods\HideCharacter</OutputPath>
<GenerateDependencyFile>false</GenerateDependencyFile>
<DebugType>none</DebugType>
</PropertyGroup>
<ItemGroup>
@@ -40,4 +49,8 @@
<PackageReference Include="Lib.Harmony" Version="2.4.1"/>
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
</ItemGroup>
<ItemGroup>
<Folder Include="Api\" />
</ItemGroup>
</Project>

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Duckov.Utilities;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.SceneManagement;
@@ -11,10 +12,9 @@ namespace HideCharacter
public class HideCharacterComponent : MonoBehaviour
{
public HideList? hideList = new HideList();
private bool hide = false;
public bool hide { get; private set; } = false;
private List<Renderer> rendererList = new List<Renderer>();
private bool needRefresh = true;
private GameObject?
bodyPartObject,
tail,
@@ -27,72 +27,77 @@ namespace HideCharacter
thighLeft,
thighRight,
weapon,
healthBar;
healthBar,
helmet,
headTip,
glasses,
armor,
backpack;
private void OnEnable()
{
SceneManager.sceneLoaded += OnSceneLoaded;
var dllDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var configFilePath = Path.Combine(dllDirectory, "config.json");
if (File.Exists(configFilePath))
{
try
{
var jsonString = File.ReadAllText(configFilePath);
hideList = JsonConvert.DeserializeObject<HideList>(jsonString);
}
catch (JsonSerializationException ex) // 捕获 Newtonsoft.Json 特有的异常
{
Debug.LogError($"JSON 反序列化错误 (Newtonsoft.Json): {ex.Message}");
}
catch (IOException ex)
{
Debug.LogError($"文件读取错误: {ex.Message}");
}
catch (Exception ex)
{
Debug.LogError($"加载配置文件时发生未知错误: {ex.Message}");
}
}
else
{
Debug.LogWarning($"配置文件 '{configFilePath}' 不存在。将使用默认设置。");
try
{
var jsonString = JsonConvert.SerializeObject(hideList, Formatting.Indented);
File.WriteAllText(configFilePath, jsonString);
}
catch (IOException ex)
{
Debug.LogError($"创建配置文件时发生错误: {ex.Message}");
}
catch (Exception ex)
{
Debug.LogError($"创建配置文件时发生未知错误: {ex.Message}");
}
}
LevelManager.OnLevelInitialized+=OnSceneLoaded;
SceneManager.sceneUnloaded += OnSceneUnloaded;
}
private void OnDisable()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
LevelManager.OnLevelInitialized-=OnSceneLoaded;
SceneManager.sceneUnloaded -= OnSceneUnloaded;
}
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
private void OnSceneLoaded()
{
rendererList.Clear();
hide = false;
var obj = GameObject.Find("ModelRoot");
if (!obj) return;
//角色的模型是最先加载的,就直接先查找了,避免找到其他实体的身体
bodyPartObject = GameObject.Find("Pelvis");
healthBar=GameObject.Find("HealthBars");
needRefresh = true;
//防止无法正常恢复身体
healthBar = GameObject.Find("HealthBars");
//身体的SkinnedMeshRenderer如果不隐藏会发现身体无法恢复
foreach (var skinnedMeshRenderer in obj.GetComponentsInChildren<SkinnedMeshRenderer>())
{
rendererList.Add(skinnedMeshRenderer);
}
Refresh();
}
private void OnSceneUnloaded(Scene scene)
{
hide = false;
SetCharacterHide(false);
}
public void Refresh()
{
tail = null;
eye = null;
eyebrow = null;
mouth = null;
hair = null;
armLeft = null;
armRight = null;
thighLeft = null;
thighRight = null;
weapon = null;
healthBar = null;
helmet = null;
glasses = null;
headTip = null;
armor = null;
backpack = null;
if (bodyPartObject != null)
FindChildObjectsRecursively(bodyPartObject.transform);
}
/// <summary>
/// 查找身体部件不使用对Meshderer的隐藏是因为测试的时候发现没有正确隐藏
/// 可能是测试逻辑错了,就先这样写了
/// </summary>
/// <param name="parentTransform"></param>
void FindChildObjectsRecursively(Transform parentTransform)
{
foreach (Transform child in parentTransform)
@@ -123,6 +128,21 @@ namespace HideCharacter
case "RightHandSocket":
weapon = child.gameObject;
break;
case "HelmatSocket":
helmet= child.gameObject;
break;
case "FaceMaskSocket":
glasses = child.gameObject;
break;
case "HeadTip":
headTip= child.gameObject;
break;
case "ArmorSocket":
armor= child.gameObject;
break;
case "BackpackSocket":
backpack= child.gameObject;
break;
default:
if (child.gameObject.name.Contains("EyePart"))
{
@@ -146,23 +166,18 @@ namespace HideCharacter
private void Update()
{
if (Input.GetKeyDown(KeyCode.F5))
if (Input.GetKeyDown(ModBehaviour.hideList?.hotkey ?? KeyCode.F5))
{
hide = !hide;
SetCharacterHide(hide);
}
}
private void SetCharacterHide(bool hide)
public void SetCharacterHide(bool hide)
{
var hideList = ModBehaviour.hideList;
if (hideList != null)
{
if (needRefresh)
{
if (bodyPartObject != null)
FindChildObjectsRecursively(bodyPartObject.transform);
needRefresh=false;
}
tail?.SetActive(!(hide && hideList.hideTail));
eye?.SetActive(!(hide && hideList.hideEyes));
eyebrow?.SetActive(!(hide && hideList.hideEyebrow));
@@ -174,8 +189,12 @@ namespace HideCharacter
thighRight?.SetActive(!(hide && hideList.hideThighRight));
weapon?.SetActive(!(hide && hideList.hideWeapon));
healthBar?.SetActive(!(hide && hideList.hideHealthBar));
helmet?.SetActive(!(hide && hideList.hideHelmet));
glasses?.SetActive(!(hide && hideList.hideGlasses));
headTip?.SetActive(!(hide && hideList.hideHeadTip));
armor?.SetActive(!(hide && hideList.hideArmor));
backpack?.SetActive(!(hide && hideList.hideBackpack));
}
foreach (var o in rendererList)

View File

@@ -1,8 +1,14 @@
using UnityEngine;
namespace HideCharacter
{
public class HideList
{
public KeyCode hotkey = KeyCode.F5;
public bool hideHelmet = true;
public bool hideGlasses = true;
public bool hideHeadTip = true;
public bool hideTail = true;
public bool hideEyes = true;
public bool hideEyebrow = true;
@@ -14,5 +20,9 @@ namespace HideCharacter
public bool hideThighRight = true;
public bool hideWeapon = false;
public bool hideHealthBar = true;
public bool hideArmor = true;
public bool hideBackpack = true;
}
}

View File

@@ -1,4 +1,9 @@

using System;
using System.IO;
using System.Reflection;
using HarmonyLib;
using Newtonsoft.Json;
using UnityEngine;
using Object = UnityEngine.Object; // 确保引入 UnityEngine 命名空间
@@ -6,33 +11,108 @@ namespace HideCharacter
{
public class ModBehaviour : Duckov.Modding.ModBehaviour
{
private GameObject? _hideCharacterManagerGameObject=null;
public static HideCharacterComponent? hideHideCharacterManager=null;
private const string CHILD_GAMEOBJECT_NAME = "HideCharacterManager";
public const string MOD_ID = "HideCharacter";
public const string MOD_NAME = "隐藏角色设置";
private Harmony _harmony;
public bool loadedConfigApi = false;
public static HideList? hideList = new HideList();
private void Awake()
{
loadedConfigApi = Api.ModConfigAPI.Initialize();
}
protected override void OnAfterSetup()
{
AddHideComponent();
if (_harmony == null)
{
_harmony=new Harmony(MOD_ID);
_harmony.PatchAll();
}
var dllDirectory = info.path;
var configFilePath = Path.Combine(dllDirectory, "config.json");
if (File.Exists(configFilePath))
{
try
{
var jsonString = File.ReadAllText(configFilePath);
hideList = JsonConvert.DeserializeObject<HideList>(jsonString);
}
catch (JsonSerializationException ex) // 捕获 Newtonsoft.Json 特有的异常
{
Debug.LogError($"JSON 反序列化错误 (Newtonsoft.Json): {ex.Message}");
}
catch (IOException ex)
{
Debug.LogError($"文件读取错误: {ex.Message}");
}
catch (Exception ex)
{
Debug.LogError($"加载配置文件时发生未知错误: {ex.Message}");
}
}
else
{
Debug.LogWarning($"配置文件 '{configFilePath}' 不存在。将使用默认设置。");
try
{
var jsonString = JsonConvert.SerializeObject(hideList, Formatting.Indented);
File.WriteAllText(configFilePath, jsonString);
}
catch (IOException ex)
{
Debug.LogError($"创建配置文件时发生错误: {ex.Message}");
}
catch (Exception ex)
{
Debug.LogError($"创建配置文件时发生未知错误: {ex.Message}");
}
}
if(Api.ModConfigAPI.IsAvailable())
AddConfigSetting();
}
protected override void OnBeforeDeactivate()
{
RemoveHideComponent();
if (_harmony != null)
{
_harmony.UnpatchAll(MOD_ID);
_harmony = null;
}
}
private void AddHideComponent()
{
var childTransform = this.transform.Find(CHILD_GAMEOBJECT_NAME);
if (childTransform) return;
_hideCharacterManagerGameObject = new GameObject(CHILD_GAMEOBJECT_NAME);
_hideCharacterManagerGameObject.transform.SetParent(this.transform);
_hideCharacterManagerGameObject.AddComponent<HideCharacterComponent>();
var hideCharacterManagerGameObject = new GameObject(CHILD_GAMEOBJECT_NAME);
hideCharacterManagerGameObject.transform.SetParent(this.transform);
hideHideCharacterManager = hideCharacterManagerGameObject.AddComponent<HideCharacterComponent>();
}
private void RemoveHideComponent()
{
if (_hideCharacterManagerGameObject)
Destroy(_hideCharacterManagerGameObject);
if (hideHideCharacterManager)
Destroy(hideHideCharacterManager?.gameObject);
}
private void AddConfigSetting()
{
Api.ModConfigAPI.SafeAddBoolDropdownList(MOD_NAME,"hideArmor","隐藏装备",hideList.hideArmor);
}
private void OnConfigChange()
{
}
}
}

View File

@@ -0,0 +1,18 @@
using HarmonyLib;
namespace HideCharacter
{
[HarmonyPatch(typeof(CharacterEquipmentController), "ChangeEquipmentModel")]
public class PatchCharacterEquipmentControllerChangeEquipmentModel
{
public static void Postfix(CharacterEquipmentController __instance)
{
var manage = ModBehaviour.hideHideCharacterManager;
if (manage!=null)
{
if(manage.hide)
manage.SetCharacterHide(manage.hide);
}
}
}
}

View File

@@ -1,11 +1,12 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v9.0",
"name": ".NETStandard,Version=v2.1/",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v9.0": {
".NETStandard,Version=v2.1": {},
".NETStandard,Version=v2.1/": {
"HideCharacter/1.0.0": {
"dependencies": {
"Lib.Harmony": "2.4.1",
@@ -17,24 +18,23 @@
},
"Lib.Harmony/2.4.1": {
"dependencies": {
"System.Text.Json": "9.0.1"
},
"runtime": {
"lib/net9.0/0Harmony.dll": {
"assemblyVersion": "2.4.1.0",
"fileVersion": "2.4.1.0"
}
"Lib.Harmony.Ref": "2.4.1"
}
},
"Lib.Harmony.Ref/2.4.1": {
"dependencies": {
"System.Reflection.Emit": "4.7.0"
}
},
"Newtonsoft.Json/13.0.4": {
"runtime": {
"lib/net6.0/Newtonsoft.Json.dll": {
"lib/netstandard2.0/Newtonsoft.Json.dll": {
"assemblyVersion": "13.0.0.0",
"fileVersion": "13.0.4.30916"
}
}
},
"System.Text.Json/9.0.1": {}
"System.Reflection.Emit/4.7.0": {}
}
},
"libraries": {
@@ -50,6 +50,13 @@
"path": "lib.harmony/2.4.1",
"hashPath": "lib.harmony.2.4.1.nupkg.sha512"
},
"Lib.Harmony.Ref/2.4.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-+u1y2Qd6OlSUQ8JtrsrSo3adnAsrXMJ2YPYtbW+FAmdPI5yw34M9VX4bKl8ZwRuUzaGzZIz+oGMbn/yS4fWtZw==",
"path": "lib.harmony.ref/2.4.1",
"hashPath": "lib.harmony.ref.2.4.1.nupkg.sha512"
},
"Newtonsoft.Json/13.0.4": {
"type": "package",
"serviceable": true,
@@ -57,12 +64,12 @@
"path": "newtonsoft.json/13.0.4",
"hashPath": "newtonsoft.json.13.0.4.nupkg.sha512"
},
"System.Text.Json/9.0.1": {
"System.Reflection.Emit/4.7.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-eqWHDZqYPv1PvuvoIIx5pF74plL3iEOZOl/0kQP+Y0TEbtgNnM2W6k8h8EPYs+LTJZsXuWa92n5W5sHTWvE3VA==",
"path": "system.text.json/9.0.1",
"hashPath": "system.text.json.9.0.1.nupkg.sha512"
"sha512": "sha512-VR4kk8XLKebQ4MZuKuIni/7oh+QGFmZW3qORd1GvBq/8026OpW501SzT/oypwiQl4TvT8ErnReh/NzY9u+C6wQ==",
"path": "system.reflection.emit/4.7.0",
"hashPath": "system.reflection.emit.4.7.0.nupkg.sha512"
}
}
}

View File

@@ -12,11 +12,11 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("折纸的小箱子")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.1")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.1+8fcbdc5649e0b93fd1b771001f53cdbb81da2c78")]
[assembly: System.Reflection.AssemblyProductAttribute("HideCharacter")]
[assembly: System.Reflection.AssemblyTitleAttribute("HideCharacter")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.1")]
// 由 MSBuild WriteCodeFragment 类生成。

View File

@@ -1 +1 @@
dae6dbab2d5a234d9270a5b902ffce8a75e41587e20f8a2b6bca7c468f962706
2a2aab917c9869a2c45025a35579f7c6e03404be00ce574a62d12c32df38a826

View File

@@ -1,6 +1,6 @@
is_global = true
build_property.RootNamespace = HideCharacter
build_property.ProjectDir = d:\vs_project\DuckovMods\HideCharacter\
build_property.ProjectDir = D:\vs_project\DuckovMods\HideCharacter\
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =
build_property.CsWinRTUseWindowsUIXamlProjections = false

View File

@@ -1 +1 @@
25b4fbc72e02fa26f694d23ed2f2e54f063851d37f15458c1d186fb4218ff1a3
bfbf8f24eac374a808530bfd826b4ac67fd68b16390e779378456c151d4baf40

View File

@@ -10,3 +10,13 @@ D:\vs_project\ThirdPersonCamera\HideCharacter\obj\Debug\HideCharacter.dll
D:\vs_project\ThirdPersonCamera\HideCharacter\obj\Debug\refint\HideCharacter.dll
D:\vs_project\ThirdPersonCamera\HideCharacter\obj\Debug\HideCharacter.pdb
D:\vs_project\ThirdPersonCamera\HideCharacter\obj\Debug\ref\HideCharacter.dll
D:\vs_project\DuckovMods\HideCharacter\bin\Debug\HideCharacter.deps.json
D:\vs_project\DuckovMods\HideCharacter\bin\Debug\HideCharacter.dll
D:\vs_project\DuckovMods\HideCharacter\bin\Debug\HideCharacter.pdb
D:\vs_project\DuckovMods\HideCharacter\obj\Debug\HideCharacter.csproj.AssemblyReference.cache
D:\vs_project\DuckovMods\HideCharacter\obj\Debug\HideCharacter.GeneratedMSBuildEditorConfig.editorconfig
D:\vs_project\DuckovMods\HideCharacter\obj\Debug\HideCharacter.AssemblyInfoInputs.cache
D:\vs_project\DuckovMods\HideCharacter\obj\Debug\HideCharacter.AssemblyInfo.cs
D:\vs_project\DuckovMods\HideCharacter\obj\Debug\HideCharacter.csproj.CoreCompileInputs.cache
D:\vs_project\DuckovMods\HideCharacter\obj\Debug\HideCharacter.dll
D:\steam\steamapps\common\Escape from Duckov\Duckov_Data\Mods\HideCharacter\HideCharacter.dll

View File

@@ -1,17 +1,17 @@
{
"format": 1,
"restore": {
"D:\\vs_project\\ThirdPersonCamera\\HideCharacter\\HideCharacter.csproj": {}
"D:\\vs_project\\DuckovMods\\HideCharacter\\HideCharacter.csproj": {}
},
"projects": {
"D:\\vs_project\\ThirdPersonCamera\\HideCharacter\\HideCharacter.csproj": {
"version": "1.0.0",
"D:\\vs_project\\DuckovMods\\HideCharacter\\HideCharacter.csproj": {
"version": "1.0.1",
"restore": {
"projectUniqueName": "D:\\vs_project\\ThirdPersonCamera\\HideCharacter\\HideCharacter.csproj",
"projectUniqueName": "D:\\vs_project\\DuckovMods\\HideCharacter\\HideCharacter.csproj",
"projectName": "HideCharacter",
"projectPath": "D:\\vs_project\\ThirdPersonCamera\\HideCharacter\\HideCharacter.csproj",
"projectPath": "D:\\vs_project\\DuckovMods\\HideCharacter\\HideCharacter.csproj",
"packagesPath": "C:\\Users\\Lenovo\\.nuget\\packages\\",
"outputPath": "D:\\vs_project\\ThirdPersonCamera\\HideCharacter\\obj\\",
"outputPath": "D:\\vs_project\\DuckovMods\\HideCharacter\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"D:\\vsShare\\NuGetPackages"

View File

@@ -12,11 +12,11 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("折纸的小箱子")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.1")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.1+8fcbdc5649e0b93fd1b771001f53cdbb81da2c78")]
[assembly: System.Reflection.AssemblyProductAttribute("HideCharacter")]
[assembly: System.Reflection.AssemblyTitleAttribute("HideCharacter")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.1")]
// 由 MSBuild WriteCodeFragment 类生成。

View File

@@ -1 +1 @@
437a77986ec6f3a8f9a36a1880ef78a303cf6060d04e3fed2480735ab9210d4a
5947386f6162b5b02c7300baa77b21c77fa4ed061a138d3fc81f970ad239b319

View File

@@ -1,6 +1,6 @@
is_global = true
build_property.RootNamespace = HideCharacter
build_property.ProjectDir = D:\vs_project\ThirdPersonCamera\HideCharacter\
build_property.ProjectDir = D:\vs_project\DuckovMods\HideCharacter\
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =
build_property.CsWinRTUseWindowsUIXamlProjections = false

View File

@@ -1 +1 @@
b9443e15139f8eafd545941efac2b184f22ba6bbbc52bcbe6e7426113697aef7
cd9d2ad4bfd553f20993642bd029f6938ef1781f8d488fae23bb311c819125fb

View File

@@ -1,4 +1,3 @@
D:\steam\steamapps\common\Escape from Duckov\Duckov_Data\Mods\HideCharacter\HideCharacter.deps.json
D:\steam\steamapps\common\Escape from Duckov\Duckov_Data\Mods\HideCharacter\HideCharacter.dll
D:\vs_project\ThirdPersonCamera\HideCharacter\obj\Release\HideCharacter.csproj.AssemblyReference.cache
D:\vs_project\ThirdPersonCamera\HideCharacter\obj\Release\HideCharacter.GeneratedMSBuildEditorConfig.editorconfig
@@ -6,5 +5,10 @@ D:\vs_project\ThirdPersonCamera\HideCharacter\obj\Release\HideCharacter.Assembly
D:\vs_project\ThirdPersonCamera\HideCharacter\obj\Release\HideCharacter.AssemblyInfo.cs
D:\vs_project\ThirdPersonCamera\HideCharacter\obj\Release\HideCharacter.csproj.CoreCompileInputs.cache
D:\vs_project\ThirdPersonCamera\HideCharacter\obj\Release\HideCharacter.dll
D:\steam\steamapps\common\Escape from Duckov\Duckov_Data\Mods\HideCharacter\HideCharacter.pdb
D:\vs_project\ThirdPersonCamera\HideCharacter\obj\Release\HideCharacter.pdb
D:\vs_project\DuckovMods\HideCharacter\obj\Release\HideCharacter.csproj.AssemblyReference.cache
D:\vs_project\DuckovMods\HideCharacter\obj\Release\HideCharacter.GeneratedMSBuildEditorConfig.editorconfig
D:\vs_project\DuckovMods\HideCharacter\obj\Release\HideCharacter.AssemblyInfoInputs.cache
D:\vs_project\DuckovMods\HideCharacter\obj\Release\HideCharacter.AssemblyInfo.cs
D:\vs_project\DuckovMods\HideCharacter\obj\Release\HideCharacter.csproj.CoreCompileInputs.cache
D:\vs_project\DuckovMods\HideCharacter\obj\Release\HideCharacter.dll

View File

@@ -210,13 +210,13 @@
"D:\\vsShare\\NuGetPackages": {}
},
"project": {
"version": "1.0.0",
"version": "1.0.1",
"restore": {
"projectUniqueName": "D:\\vs_project\\ThirdPersonCamera\\HideCharacter\\HideCharacter.csproj",
"projectUniqueName": "D:\\vs_project\\DuckovMods\\HideCharacter\\HideCharacter.csproj",
"projectName": "HideCharacter",
"projectPath": "D:\\vs_project\\ThirdPersonCamera\\HideCharacter\\HideCharacter.csproj",
"projectPath": "D:\\vs_project\\DuckovMods\\HideCharacter\\HideCharacter.csproj",
"packagesPath": "C:\\Users\\Lenovo\\.nuget\\packages\\",
"outputPath": "D:\\vs_project\\ThirdPersonCamera\\HideCharacter\\obj\\",
"outputPath": "D:\\vs_project\\DuckovMods\\HideCharacter\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"D:\\vsShare\\NuGetPackages"

View File

@@ -1,8 +1,8 @@
{
"version": 2,
"dgSpecHash": "0fgzUvttKoU=",
"dgSpecHash": "PGmptqDdy6g=",
"success": true,
"projectFilePath": "D:\\vs_project\\ThirdPersonCamera\\HideCharacter\\HideCharacter.csproj",
"projectFilePath": "D:\\vs_project\\DuckovMods\\HideCharacter\\HideCharacter.csproj",
"expectedPackageFiles": [
"C:\\Users\\Lenovo\\.nuget\\packages\\lib.harmony\\2.4.1\\lib.harmony.2.4.1.nupkg.sha512",
"C:\\Users\\Lenovo\\.nuget\\packages\\lib.harmony.ref\\2.4.1\\lib.harmony.ref.2.4.1.nupkg.sha512",

View File

@@ -1 +1 @@
"restore":{"projectUniqueName":"D:\\vs_project\\ThirdPersonCamera\\HideCharacter\\HideCharacter.csproj","projectName":"HideCharacter","projectPath":"D:\\vs_project\\ThirdPersonCamera\\HideCharacter\\HideCharacter.csproj","outputPath":"D:\\vs_project\\ThirdPersonCamera\\HideCharacter\\obj\\","projectStyle":"PackageReference","fallbackFolders":["D:\\vsShare\\NuGetPackages"],"originalTargetFrameworks":["netstandard2.1"],"sources":{"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\":{},"https://api.nuget.org/v3/index.json":{}},"frameworks":{"netstandard2.1":{"targetAlias":"netstandard2.1","projectReferences":{}}},"warningProperties":{"warnAsError":["NU1605"]},"restoreAuditProperties":{"enableAudit":"true","auditLevel":"low","auditMode":"direct"},"SdkAnalysisLevel":"9.0.300"}"frameworks":{"netstandard2.1":{"targetAlias":"netstandard2.1","dependencies":{"Lib.Harmony":{"target":"Package","version":"[2.4.1, )"},"Newtonsoft.Json":{"target":"Package","version":"[13.0.4, )"}},"imports":["net461","net462","net47","net471","net472","net48","net481"],"assetTargetFallback":true,"warn":true,"frameworkReferences":{"NETStandard.Library":{"privateAssets":"all"}},"runtimeIdentifierGraphPath":"C:\\Program Files\\dotnet\\sdk\\9.0.306\\RuntimeIdentifierGraph.json"}}
"restore":{"projectUniqueName":"D:\\vs_project\\DuckovMods\\HideCharacter\\HideCharacter.csproj","projectName":"HideCharacter","projectPath":"D:\\vs_project\\DuckovMods\\HideCharacter\\HideCharacter.csproj","outputPath":"D:\\vs_project\\DuckovMods\\HideCharacter\\obj\\","projectStyle":"PackageReference","fallbackFolders":["D:\\vsShare\\NuGetPackages"],"originalTargetFrameworks":["netstandard2.1"],"sources":{"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\":{},"https://api.nuget.org/v3/index.json":{}},"frameworks":{"netstandard2.1":{"targetAlias":"netstandard2.1","projectReferences":{}}},"warningProperties":{"warnAsError":["NU1605"]},"restoreAuditProperties":{"enableAudit":"true","auditLevel":"low","auditMode":"direct"},"SdkAnalysisLevel":"9.0.300"}"frameworks":{"netstandard2.1":{"targetAlias":"netstandard2.1","dependencies":{"Lib.Harmony":{"target":"Package","version":"[2.4.1, )"},"Newtonsoft.Json":{"target":"Package","version":"[13.0.4, )"}},"imports":["net461","net462","net47","net471","net472","net48","net481"],"assetTargetFallback":true,"warn":true,"frameworkReferences":{"NETStandard.Library":{"privateAssets":"all"}},"runtimeIdentifierGraphPath":"C:\\Program Files\\dotnet\\sdk\\9.0.306\\RuntimeIdentifierGraph.json"}}

View File

@@ -1 +1 @@
17619695316822345
17623343068138064

View File

@@ -1 +1 @@
17619695316822345
17623343068138064

View File

@@ -0,0 +1,488 @@
using System;
using System.Linq;
using System.Reflection;
using UnityEngine;
//替换为你的mod命名空间, 防止多个同名ModConfigAPI冲突
namespace HitFeedback.Api {
/// <summary>
/// ModConfig 安全接口封装类 - 提供不抛异常的静态接口
/// ModConfig Safe API Wrapper Class - Provides non-throwing static interfaces
/// </summary>
public static class ModConfigAPI
{
public static string ModConfigName = "ModConfig";
//Ensure this match the number of ModConfig.ModBehaviour.VERSION
//这里确保版本号与ModConfig.ModBehaviour.VERSION匹配
private const int ModConfigVersion = 1;
private static string TAG = $"ModConfig_v{ModConfigVersion}";
private static Type modBehaviourType;
private static Type optionsManagerType;
public static bool isInitialized = false;
private static bool versionChecked = false;
private static bool isVersionCompatible = false;
/// <summary>
/// 检查版本兼容性
/// Check version compatibility
/// </summary>
private static bool CheckVersionCompatibility()
{
if (versionChecked)
return isVersionCompatible;
try
{
// 尝试获取 ModConfig 的版本号
// Try to get ModConfig version number
var versionField = modBehaviourType.GetField("VERSION", BindingFlags.Public | BindingFlags.Static);
if (versionField != null && versionField.FieldType == typeof(int))
{
var modConfigVersion = (int)versionField.GetValue(null);
isVersionCompatible = (modConfigVersion == ModConfigVersion);
if (!isVersionCompatible)
{
Debug.LogError($"[{TAG}] 版本不匹配API版本: {ModConfigVersion}, ModConfig版本: {modConfigVersion}");
return false;
}
Debug.Log($"[{TAG}] 版本检查通过: {ModConfigVersion}");
versionChecked = true;
return true;
}
else
{
// 如果找不到版本字段,发出警告但继续运行(向后兼容)
// If version field not found, warn but continue (backward compatibility)
Debug.LogWarning($"[{TAG}] 未找到版本信息字段,跳过版本检查");
isVersionCompatible = true;
versionChecked = true;
return true;
}
}
catch (Exception ex)
{
Debug.LogError($"[{TAG}] 版本检查失败: {ex.Message}");
isVersionCompatible = false;
versionChecked = true;
return false;
}
}
/// <summary>
/// 初始化 ModConfigAPI检查必要的函数是否存在
/// Initialize ModConfigAPI, check if necessary functions exist
/// </summary>
public static bool Initialize()
{
try
{
if (isInitialized)
return true;
// 获取 ModBehaviour 类型
// Get ModBehaviour type
modBehaviourType = FindTypeInAssemblies("ModConfig.ModBehaviour");
if (modBehaviourType == null)
{
Debug.LogWarning($"[{TAG}] ModConfig.ModBehaviour 类型未找到ModConfig 可能未加载");
return false;
}
// 获取 OptionsManager_Mod 类型
// Get OptionsManager_Mod type
optionsManagerType = FindTypeInAssemblies("ModConfig.OptionsManager_Mod");
if (optionsManagerType == null)
{
Debug.LogWarning($"[{TAG}] ModConfig.OptionsManager_Mod 类型未找到");
return false;
}
// 检查版本兼容性
// Check version compatibility
if (!CheckVersionCompatibility())
{
Debug.LogWarning($"[{TAG}] ModConfig version mismatch!!!");
return false;
}
// 检查必要的静态方法是否存在
// Check if necessary static methods exist
string[] requiredMethods = {
"AddDropdownList",
"AddInputWithSlider",
"AddBoolDropdownList",
"AddOnOptionsChangedDelegate",
"RemoveOnOptionsChangedDelegate",
};
foreach (var methodName in requiredMethods)
{
var method = modBehaviourType.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static);
if (method == null)
{
Debug.LogError($"[{TAG}] 必要方法 {methodName} 未找到");
return false;
}
}
isInitialized = true;
Debug.Log($"[{TAG}] ModConfigAPI 初始化成功");
return true;
}
catch (Exception ex)
{
Debug.LogError($"[{TAG}] 初始化失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 在所有已加载的程序集中查找类型
/// </summary>
private static Type FindTypeInAssemblies(string typeName)
{
try
{
// 获取当前域中的所有程序集
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var assembly in assemblies)
{
try
{
// 检查程序集名称是否包含 ModConfig
if (assembly.FullName.Contains("ModConfig"))
{
Debug.Log($"[{TAG}] 找到 ModConfig 相关程序集: {assembly.FullName}");
}
// 尝试在该程序集中查找类型
var type = assembly.GetType(typeName);
if (type != null)
{
Debug.Log($"[{TAG}] 在程序集 {assembly.FullName} 中找到类型 {typeName}");
return type;
}
}
catch (Exception ex)
{
// 忽略单个程序集的查找错误
continue;
}
}
// 记录所有已加载的程序集用于调试
Debug.LogWarning($"[{TAG}] 在所有程序集中未找到类型 {typeName},已加载程序集数量: {assemblies.Length}");
foreach (var assembly in assemblies.Where(a => a.FullName.Contains("ModConfig")))
{
Debug.Log($"[{TAG}] ModConfig 相关程序集: {assembly.FullName}");
}
return null;
}
catch (Exception ex)
{
Debug.LogError($"[{TAG}] 程序集扫描失败: {ex.Message}");
return null;
}
}
/// <summary>
/// 安全地添加选项变更事件委托
/// Safely add options changed event delegate
/// </summary>
/// <param name="action">事件处理委托,参数为变更的选项键名</param>
/// <returns>是否成功添加</returns>
public static bool SafeAddOnOptionsChangedDelegate(Action<string> action)
{
if (!Initialize())
return false;
if (action == null)
{
Debug.LogWarning($"[{TAG}] 不能添加空的事件委托");
return false;
}
try
{
var method = modBehaviourType.GetMethod("AddOnOptionsChangedDelegate", BindingFlags.Public | BindingFlags.Static);
method.Invoke(null, new object[] { action });
Debug.Log($"[{TAG}] 成功添加选项变更事件委托");
return true;
}
catch (Exception ex)
{
Debug.LogError($"[{TAG}] 添加选项变更事件委托失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 安全地移除选项变更事件委托
/// Safely remove options changed event delegate
/// </summary>
/// <param name="action">要移除的事件处理委托</param>
/// <returns>是否成功移除</returns>
public static bool SafeRemoveOnOptionsChangedDelegate(Action<string> action)
{
if (!Initialize())
return false;
if (action == null)
{
Debug.LogWarning($"[{TAG}] 不能移除空的事件委托");
return false;
}
try
{
var method = modBehaviourType.GetMethod("RemoveOnOptionsChangedDelegate", BindingFlags.Public | BindingFlags.Static);
method.Invoke(null, new object[] { action });
Debug.Log($"[{TAG}] 成功移除选项变更事件委托");
return true;
}
catch (Exception ex)
{
Debug.LogError($"[{TAG}] 移除选项变更事件委托失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 安全地添加下拉列表配置项
/// Safely add dropdown list configuration item
/// </summary>
public static bool SafeAddDropdownList(string modName, string key, string description, System.Collections.Generic.SortedDictionary<string, object> options, Type valueType, object defaultValue)
{
key = $"{modName}_{key}";
if (!Initialize())
return false;
try
{
var method = modBehaviourType.GetMethod("AddDropdownList", BindingFlags.Public | BindingFlags.Static);
method.Invoke(null, new object[] { modName, key, description, options, valueType, defaultValue });
Debug.Log($"[{TAG}] 成功添加下拉列表: {modName}.{key}");
return true;
}
catch (Exception ex)
{
Debug.LogError($"[{TAG}] 添加下拉列表失败 {modName}.{key}: {ex.Message}");
return false;
}
}
/// <summary>
/// 安全地添加带滑条的输入框配置项
/// Safely add input box with slider configuration item
/// </summary>
public static bool SafeAddInputWithSlider(string modName, string key, string description, Type valueType, object defaultValue, UnityEngine.Vector2? sliderRange = null)
{
key = $"{modName}_{key}";
if (!Initialize())
return false;
try
{
var method = modBehaviourType.GetMethod("AddInputWithSlider", BindingFlags.Public | BindingFlags.Static);
// 处理可空参数
// Handle nullable parameters
var parameters = sliderRange.HasValue ?
new object[] { modName, key, description, valueType, defaultValue, sliderRange.Value } :
new object[] { modName, key, description, valueType, defaultValue, null };
method.Invoke(null, parameters);
Debug.Log($"[{TAG}] 成功添加滑条输入框: {modName}.{key}");
return true;
}
catch (Exception ex)
{
Debug.LogError($"[{TAG}] 添加滑条输入框失败 {modName}.{key}: {ex.Message}");
return false;
}
}
/// <summary>
/// 安全地添加布尔下拉列表配置项
/// Safely add boolean dropdown list configuration item
/// </summary>
public static bool SafeAddBoolDropdownList(string modName, string key, string description, bool defaultValue)
{
key = $"{modName}_{key}";
if (!Initialize())
return false;
try
{
var method = modBehaviourType.GetMethod("AddBoolDropdownList", BindingFlags.Public | BindingFlags.Static);
method.Invoke(null, new object[] { modName, key, description, defaultValue });
Debug.Log($"[{TAG}] 成功添加布尔下拉列表: {modName}.{key}");
return true;
}
catch (Exception ex)
{
Debug.LogError($"[{TAG}] 添加布尔下拉列表失败 {modName}.{key}: {ex.Message}");
return false;
}
}
/// <summary>
/// 安全地加载配置值
/// Safely load configuration value
/// </summary>
/// <typeparam name="T">值的类型</typeparam>
/// <param name="key">配置键</param>
/// <param name="defaultValue">默认值</param>
/// <returns>加载的值或默认值</returns>
public static T SafeLoad<T>(string mod_name, string key, T defaultValue = default(T))
{
key = $"{mod_name}_{key}";
if (!Initialize())
return defaultValue;
if (string.IsNullOrEmpty(key))
{
Debug.LogWarning($"[{TAG}] 配置键不能为空");
return defaultValue;
}
try
{
var loadMethod = optionsManagerType.GetMethod("Load", BindingFlags.Public | BindingFlags.Static);
if (loadMethod == null)
{
Debug.LogError($"[{TAG}] 未找到 OptionsManager_Mod.Load 方法");
return defaultValue;
}
// 获取泛型方法
var genericLoadMethod = loadMethod.MakeGenericMethod(typeof(T));
var result = genericLoadMethod.Invoke(null, new object[] { key, defaultValue });
Debug.Log($"[{TAG}] 成功加载配置: {key} = {result}");
return (T)result;
}
catch (Exception ex)
{
Debug.LogError($"[{TAG}] 加载配置失败 {key}: {ex.Message}");
return defaultValue;
}
}
/// <summary>
/// 安全地保存配置值
/// Safely save configuration value
/// </summary>
/// <typeparam name="T">值的类型</typeparam>
/// <param name="key">配置键</param>
/// <param name="value">要保存的值</param>
/// <returns>是否保存成功</returns>
public static bool SafeSave<T>(string mod_name, string key, T value)
{
key = $"{mod_name}_{key}";
if (!Initialize())
return false;
if (string.IsNullOrEmpty(key))
{
Debug.LogWarning($"[{TAG}] 配置键不能为空");
return false;
}
try
{
var saveMethod = optionsManagerType.GetMethod("Save", BindingFlags.Public | BindingFlags.Static);
if (saveMethod == null)
{
Debug.LogError($"[{TAG}] 未找到 OptionsManager_Mod.Save 方法");
return false;
}
// 获取泛型方法
var genericSaveMethod = saveMethod.MakeGenericMethod(typeof(T));
genericSaveMethod.Invoke(null, new object[] { key, value });
Debug.Log($"[{TAG}] 成功保存配置: {key} = {value}");
return true;
}
catch (Exception ex)
{
Debug.LogError($"[{TAG}] 保存配置失败 {key}: {ex.Message}");
return false;
}
}
/// <summary>
/// 检查 ModConfig 是否可用
/// Check if ModConfig is available
/// </summary>
public static bool IsAvailable()
{
return Initialize();
}
/// <summary>
/// 获取 ModConfig 版本信息(如果存在)
/// Get ModConfig version information (if exists)
/// </summary>
public static string GetVersionInfo()
{
if (!Initialize())
return "ModConfig 未加载 | ModConfig not loaded";
try
{
// 尝试获取版本信息(如果 ModBehaviour 有相关字段或属性)
// Try to get version information (if ModBehaviour has related fields or properties)
var versionField = modBehaviourType.GetField("VERSION", BindingFlags.Public | BindingFlags.Static);
if (versionField != null && versionField.FieldType == typeof(int))
{
var modConfigVersion = (int)versionField.GetValue(null);
var compatibility = (modConfigVersion == ModConfigVersion) ? "兼容" : "不兼容";
return $"ModConfig v{modConfigVersion} (API v{ModConfigVersion}, {compatibility})";
}
var versionProperty = modBehaviourType.GetProperty("VERSION", BindingFlags.Public | BindingFlags.Static);
if (versionProperty != null)
{
var versionValue = versionProperty.GetValue(null);
return versionValue?.ToString() ?? "未知版本 | Unknown version";
}
return "ModConfig 已加载(版本信息不可用) | ModConfig loaded (version info unavailable)";
}
catch
{
return "ModConfig 已加载(版本检查失败) | ModConfig loaded (version check failed)";
}
}
/// <summary>
/// 检查版本兼容性
/// Check version compatibility
/// </summary>
public static bool IsVersionCompatible()
{
if (!Initialize())
return false;
return isVersionCompatible;
}
}
}

284
HitFeedback/Config.cs Normal file
View File

@@ -0,0 +1,284 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using UnityEngine; // 假设在Unity环境中使用Debug.LogError
namespace HitFeedback
{
public class Config
{
public KeyCode hotKey = KeyCode.F8;
public Dictionary<string, float> probability = new Dictionary<string, float>();
/// <summary>
/// 当伤害具有这些特性之一时,将播放音频反馈。
/// </summary>
public HashSet<DamageFeature> audioDamageFeatures = new HashSet<DamageFeature>();
public void LoadConfig(string filename)
{
if (!File.Exists(filename))
{
Debug.LogError($"Config file not found: {filename}");
return;
}
probability.Clear();
audioDamageFeatures.Clear(); // 清空旧的音频伤害特性数据
try
{
using (var sr = new StreamReader(filename))
{
string line;
var lineNumber = 0;
string currentSection = ""; // 用于解析节
while ((line = sr.ReadLine()) != null)
{
lineNumber++;
line = line.Trim();
// 忽略空行和注释行
if (string.IsNullOrEmpty(line) || line.StartsWith(";") || line.StartsWith("#"))
{
continue;
}
// 处理节标题,例如 [General] 或 [AudioFeatures]
if (line.StartsWith("[") && line.EndsWith("]"))
{
currentSection = line.Substring(1, line.Length - 2).Trim();
continue; // 跳过节标题行
}
// 查找等号
var separatorIndex = line.IndexOf('=');
if (separatorIndex == -1)
{
Debug.LogWarning(
$"Skipping malformed line in config file '{filename}' at line {lineNumber}: No '=' found. Line: '{line}'");
continue;
}
var key = line.Substring(0, separatorIndex).Trim();
var valueStr = line.Substring(separatorIndex + 1).Trim();
if (currentSection.Equals("General", StringComparison.OrdinalIgnoreCase))
{
// 解析 hotKey
if (key.Equals("hotKey", StringComparison.OrdinalIgnoreCase))
{
try
{
hotKey = (KeyCode)Enum.Parse(typeof(KeyCode), valueStr, true);
}
catch (ArgumentException)
{
Debug.LogError(
$"Invalid KeyCode '{valueStr}' in config file '{filename}' at line {lineNumber}. Using default F8.");
hotKey = KeyCode.F8;
}
}
}
else if (currentSection.Equals("Probabilities", StringComparison.OrdinalIgnoreCase))
{
// 解析 probability 字典项
if (float.TryParse(valueStr, NumberStyles.Float, CultureInfo.InvariantCulture,
out var probValue))
{
probability[key] = probValue;
}
else
{
Debug.LogWarning(
$"Invalid float value '{valueStr}' for key '{key}' in config file '{filename}' at line {lineNumber}. Skipping entry.");
}
}
else if (currentSection.Equals("AudioFeatures", StringComparison.OrdinalIgnoreCase))
{
// 解析 audioDamageFeatures 集合项
// 键是 'feature' (或者可以随意定义,只要值是我们关心的)
// 值是 ExtendedDamageFeature 的枚举名称
if (key.Equals("feature", StringComparison.OrdinalIgnoreCase)) // 假设所有特征都用同一个键"feature"
{
foreach (var featureName in valueStr.Split(new char[] { ',', '|' },
StringSplitOptions.RemoveEmptyEntries))
{
try
{
// 确保 ExtendedDamageFeature 是 [Flags] 枚举
var feature = (DamageFeature)Enum.Parse(typeof(DamageFeature),
featureName.Trim(), true);
audioDamageFeatures.Add(feature);
}
catch (ArgumentException)
{
Debug.LogWarning(
$"Invalid ExtendedDamageFeature '{featureName.Trim()}' in config file '{filename}' at line {lineNumber}. Skipping entry.");
}
}
}
else
{
try
{
var feature =
(DamageFeature)Enum.Parse(typeof(DamageFeature), key, true);
if (bool.TryParse(valueStr, out bool includeFeature) && includeFeature)
{
audioDamageFeatures.Add(feature);
}
// 如果是 false则不添加到集合或者可以从集合中移除如果默认是都包含
}
catch (ArgumentException)
{
Debug.LogWarning(
$"Invalid ExtendedDamageFeature key '{key}' in config file '{filename}' at line {lineNumber}. Skipping entry.");
}
}
}
// 如果存在其他节,可以在这里添加 else if 处理
}
}
}
catch (Exception ex)
{
Debug.LogError($"Error reading config file '{filename}': {ex.Message}");
}
}
/// <summary>
/// 将当前配置存储到指定INI文件。
/// </summary>
/// <param name="filename">要保存的INI文件的路径。</param>
public void SaveConfig(string filename)
{
try
{
var directory = Path.GetDirectoryName(filename);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
using (var sw = new StreamWriter(filename))
{
sw.WriteLine("; HitFeedback Configuration File");
sw.WriteLine("; Generated by HitFeedback.Config class");
sw.WriteLine();
sw.WriteLine("[General]");
sw.WriteLine($"hotKey = {hotKey.ToString()}");
sw.WriteLine();
if (probability.Count > 0)
{
sw.WriteLine("[Probabilities]");
foreach (var kvp in probability)
{
sw.WriteLine($"{kvp.Key} = {kvp.Value.ToString(CultureInfo.InvariantCulture)}");
}
}
else
{
sw.WriteLine("; No probabilities currently configured.");
}
sw.WriteLine();
if (audioDamageFeatures.Count > 0)
{
sw.WriteLine("[AudioFeatures]");
// 假设每个特性一行,值为 true
foreach (var feature in audioDamageFeatures)
{
sw.WriteLine($"{feature.ToString()} = True");
}
// 或者如果你想用一个键存储所有特性(用逗号或竖线分隔)
// sw.WriteLine($"features = {string.Join(",", audioDamageFeatures.Select(f => f.ToString()))}");
}
else
{
sw.WriteLine("; No audio damage features currently configured.");
}
sw.WriteLine();
}
Debug.Log($"Config saved to: {filename}");
}
catch (Exception ex)
{
Debug.LogError($"Error saving config file '{filename}': {ex.Message}");
}
}
/// <summary>
/// 根据DamageInfo的特性判断是否应该播放音频反馈。
/// 如果DamageInfo的任何一个特性在audioDamageFeatures集合中则返回true。
/// </summary>
/// <param name="damageInfo">要检查的DamageInfo对象。</param>
/// <returns>如果应该播放音频反馈则为true否则为false。</returns>
public bool ShouldPlayAudioFeedback(DamageInfo damageInfo)
{
if (audioDamageFeatures.Count == 0)
{
return false;
}
// 将DamageInfo的各种布尔属性/条件转换为DamageFeature组合
DamageFeature currentDamageFeatures = DamageFeature.Undefined;
if (damageInfo.damageType == DamageTypes.normal)
{
currentDamageFeatures |= DamageFeature.NormalDamage;
}
else if (damageInfo.damageType == DamageTypes.realDamage)
{
currentDamageFeatures |= DamageFeature.RealDamage;
}
if (damageInfo.isFromBuffOrEffect)
{
currentDamageFeatures |= DamageFeature.BuffOrEffectDamage;
}
if (damageInfo.ignoreArmor)
{
currentDamageFeatures |= DamageFeature.ArmorIgnoringDamage;
}
if (damageInfo.crit > 0) // crit > 0 表示是暴击
{
currentDamageFeatures |= DamageFeature.CriticalDamage;
}
if (damageInfo.armorPiercing > 0)
{
currentDamageFeatures |= DamageFeature.ArmorPiercingDamage;
}
if (damageInfo.isExplosion)
{
currentDamageFeatures |= DamageFeature.ExplosionDamage;
}
if (damageInfo.armorBreak > 0)
{
currentDamageFeatures |= DamageFeature.ArmorBreakingDamage;
}
if (damageInfo.elementFactors != null && damageInfo.elementFactors.Count > 0)
{
currentDamageFeatures |= DamageFeature.ElementalDamage;
}
if (damageInfo.buffChance > 0 || damageInfo.buff != null)
{
currentDamageFeatures |= DamageFeature.OnHitBuffApply;
}
if (damageInfo.bleedChance > 0)
{
currentDamageFeatures |= DamageFeature.OnHitBleed;
}
foreach (var configuredFeature in audioDamageFeatures)
{
if (configuredFeature == DamageFeature.Undefined)
{
continue;
}
if ((currentDamageFeatures & configuredFeature) == configuredFeature)
{
return true;
}
}
return false;
}
}
}

View File

@@ -0,0 +1,69 @@
using System;
namespace HitFeedback
{
[Flags]
public enum DamageFeature
{
/// <summary>
/// 未指定或未分类的伤害特性。
/// </summary>
Undefined = 0,
/// <summary>
/// 普通物理伤害或其他未特别定义的伤害(对应 DamageTypes.normal
/// </summary>
NormalDamage = 1,
/// <summary>
/// 真实伤害,不受护甲或其他减伤效果影响(对应 DamageTypes.realDamage
/// </summary>
RealDamage = 2,
/// <summary>
/// 伤害来自增益Buff或持续效果。
/// (基于 DamageInfo.isFromBuffOrEffect)
/// </summary>
BuffOrEffectDamage = 4,
/// <summary>
/// 伤害无视目标的护甲。
/// (基于 DamageInfo.ignoreArmor)
/// </summary>
ArmorIgnoringDamage = 8,
/// <summary>
/// 伤害是暴击伤害。
/// (基于 DamageInfo.crit > 0)
/// </summary>
CriticalDamage = 16,
/// <summary>
/// 伤害包含护甲穿透效果。
/// (基于 DamageInfo.armorPiercing > 0)
/// </summary>
ArmorPiercingDamage = 32,
/// <summary>
/// 伤害是爆炸类型。
/// (基于 DamageInfo.isExplosion)
/// </summary>
ExplosionDamage = 64,
/// <summary>
/// 伤害具有护甲破坏效果。
/// (基于 DamageInfo.armorBreak > 0)
/// </summary>
ArmorBreakingDamage = 128,
/// <summary>
/// 伤害可能带有元素效果(如果有 elementFactors 存在且非空)。
/// </summary>
ElementalDamage = 256,
/// <summary>
/// 伤害可能附带Buff效果。
/// (基于 DamageInfo.buffChance > 0 或 buff != null)
/// </summary>
/// <remarks>
/// 注意:这个可以与 BuffOrEffectDamage 区分BuffOrEffectDamage 是伤害本身来自Buff
/// 而นี้是伤害造成时附带施加Buff的效果。
/// </remarks>
OnHitBuffApply = 512,
/// <summary>
/// 伤害可能附带流血效果。
/// (基于 DamageInfo.bleedChance > 0)
/// </summary>
OnHitBleed = 1024,
}
}

View File

@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<DuckovPath>D:\steam\steamapps\common\Escape from Duckov</DuckovPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<OutputPath>D:\steam\steamapps\common\Escape from Duckov\Duckov_Data\Mods\HitFeedback</OutputPath>
<GenerateDependencyFile>false</GenerateDependencyFile>
<DebugType>none</DebugType>
</PropertyGroup>
<ItemGroup>
<Reference Include="$(DuckovPath)\Duckov_Data\Managed\TeamSoda.*" Private="False" />
<Reference Include="$(DuckovPath)\Duckov_Data\Managed\ItemStatsSystem.dll" Private="False" />
<Reference Include="$(DuckovPath)\Duckov_Data\Managed\Unity*" Private="False" />
<Reference Include="$(DuckovPath)\Duckov_Data\Managed\FMODUnity.dll" Private="False" />
</ItemGroup>
</Project>

359
HitFeedback/ModBehaviour.cs Normal file
View File

@@ -0,0 +1,359 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using Duckov;
using HitFeedback.Api;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
using Object = UnityEngine.Object;
using Random = UnityEngine.Random;
namespace HitFeedback
{
public class ModBehaviour : Duckov.Modding.ModBehaviour
{
public const string AudioFolderName = "audio";
public const string ConfigFileName = "config.ini";
public string audioFolderPath;
public string configFilePath;
public Dictionary<string, float> audioProbability = new Dictionary<string, float>();
public Health health;
public Config config = new Config();
public float totalWeight;
public const string MOD_SETTING_NAME = "受击反馈";
private void Update()
{
if (Input.GetKeyDown(config.hotKey))
{
PlayRandomAudioClip();
}
}
protected override void OnAfterSetup()
{
LevelManager.OnLevelInitialized += OnSceneLoaded;
audioFolderPath = Path.Combine(info.path, AudioFolderName);
configFilePath = Path.Combine(info.path, ConfigFileName);
FindWavFiles();
config.LoadConfig(configFilePath);
ApplyConfig();
InitializeSetting();
UpdateTotalWeight();
}
protected override void OnBeforeDeactivate()
{
LevelManager.OnLevelInitialized -= OnSceneLoaded;
SaveConfig();
}
private void OnDestroy()
{
SaveConfig();
}
private void UpdateTotalWeight()
{
totalWeight = 0;
foreach (var f in audioProbability)
{
totalWeight += f.Value;
}
}
private void ApplyConfig()
{
foreach (var f in config.probability)
{
if (audioProbability.ContainsKey(f.Key))
{
audioProbability[f.Key] = f.Value;
}
}
}
private void SaveConfig()
{
config.probability.Clear();
foreach (var f in audioProbability)
{
config.probability.Add(f.Key, f.Value);
}
config.SaveConfig(configFilePath);
}
private void FindWavFiles()
{
audioProbability.Clear();
if (!Directory.Exists(audioFolderPath))
{
return;
}
try
{
var audioFiles = new List<string>();
string[] wavFiles = Directory.GetFiles(audioFolderPath, "*.wav", SearchOption.TopDirectoryOnly);
audioFiles.AddRange(wavFiles);
string[] mp3Files = Directory.GetFiles(audioFolderPath, "*.mp3", SearchOption.TopDirectoryOnly);
audioFiles.AddRange(mp3Files);
string[] oggFiles = Directory.GetFiles(audioFolderPath, "*.ogg", SearchOption.TopDirectoryOnly);
audioFiles.AddRange(oggFiles);
if (audioFiles.Count > 0)
{
foreach (var filePath in audioFiles)
{
audioProbability.Add(Path.GetFileName(filePath), 1);
}
}
}
catch (UnauthorizedAccessException ex)
{
Debug.LogError($"Error: Access to '{audioFolderPath}' is denied. {ex.Message}");
}
catch (DirectoryNotFoundException ex)
{
Debug.LogError($"Error: Directory '{audioFolderPath}' not found. {ex.Message}");
}
catch (Exception ex)
{
Debug.LogError($"An unexpected error occurred: {ex.Message}");
}
}
private void OnSceneLoaded()
{
TryAddListener();
}
private void TryAddListener()
{
if (health)
{
health.OnHurtEvent.RemoveListener(OnHurtEvent);
}
health = CharacterMainControl.Main?.Health;
if (health)
{
health.OnHurtEvent.AddListener(OnHurtEvent);
}
}
private void OnHurtEvent(DamageInfo damageInfo)
{
if (config.ShouldPlayAudioFeedback(damageInfo))
PlayRandomAudioClip();
}
public void PlayRandomAudioClip()
{
if (audioProbability.Count > 0)
{
var randomIndex = Random.Range(0, totalWeight);
foreach (var f in audioProbability)
{
randomIndex -= f.Value;
if (randomIndex <= 0)
{
AudioManager.PostCustomSFX(Path.Combine(audioFolderPath, f.Key));
return;
}
}
}
else
{
Debug.LogWarning("Mod Feedback: No audio clips loaded to play.");
}
}
public void InitializeSetting()
{
if (!Api.ModConfigAPI.Initialize())
{
return;
}
foreach (var audio in audioProbability)
{
ModConfigAPI.SafeAddInputWithSlider(MOD_SETTING_NAME, audio.Key, $"音频\"{audio.Key}\"播放概率",
typeof(float), audio.Value, new Vector2(0, 100));
}
foreach (DamageFeature value in Enum.GetValues(typeof(DamageFeature)))
{
ModConfigAPI.SafeAddBoolDropdownList(MOD_SETTING_NAME, value.ToString(),
$"受到{ToSingleFeatureChineseString(value)}时触发",
config.audioDamageFeatures.Contains(value));
}
var hotkeyOptions = GenerateCommonKeyCodeOptions();
ModConfigAPI.SafeAddDropdownList(
MOD_SETTING_NAME,
"hotKey",
"主动触发的热键",
hotkeyOptions,
typeof(int),
config.hotKey
);
ModConfigAPI.SafeAddOnOptionsChangedDelegate(OnConfigChange);
}
private void OnConfigChange(string key)
{
key = key[(MOD_SETTING_NAME.Length + 1)..];
if (key == "hotKey")
{
config.hotKey = (KeyCode)ModConfigAPI.SafeLoad(MOD_SETTING_NAME, key, (int)(config.hotKey));
return;
}
if (audioProbability.ContainsKey(key))
{
var value = ModConfigAPI.SafeLoad(MOD_SETTING_NAME, key, audioProbability[key]);
audioProbability[key] = value;
}
if (Enum.TryParse(key, out DamageFeature damageInfo))
{
var current=config.audioDamageFeatures.Contains(damageInfo);
if (ModConfigAPI.SafeLoad(MOD_SETTING_NAME, key, current))
{
config.audioDamageFeatures.Add(damageInfo);
}
else if (current)
{
config.audioDamageFeatures.Remove(damageInfo);
}
}
UpdateTotalWeight();
}
/// <summary>
/// 生成包含常用 KeyCode 的 SortedDictionary。
/// </summary>
/// <returns>一个 SortedDictionary键是 KeyCode 的字符串表示,值是 KeyCode 枚举本身。</returns>
private static SortedDictionary<string, object> GenerateCommonKeyCodeOptions()
{
var options = new SortedDictionary<string, object>();
// 字母键
for (var c = 'A'; c <= 'Z'; c++)
{
var keyCode = (int)(KeyCode)Enum.Parse(typeof(KeyCode), c.ToString());
options.Add(c.ToString(), keyCode);
}
// 数字键(主键盘)
for (var i = 0; i <= 9; i++)
{
var keyCode = (int)(KeyCode)Enum.Parse(typeof(KeyCode), "Alpha" + i.ToString());
options.Add(i.ToString(), keyCode);
}
// 数字键盘
for (var i = 0; i <= 9; i++)
{
var keyCode = (int)(KeyCode)Enum.Parse(typeof(KeyCode), "Keypad" + i.ToString());
options.Add($"Num_{i}", keyCode); // 加前缀区分主键盘数字
}
// 功能键
for (var i = 1; i <= 12; i++)
{
var keyCode = (int)(KeyCode)Enum.Parse(typeof(KeyCode), "F" + i.ToString());
options.Add($"F{i}", keyCode);
}
// 常用控制键
options.Add("空格", (int)KeyCode.Space);
options.Add("回车", (int)KeyCode.Return);
options.Add("Esc", (int)KeyCode.Escape);
options.Add("Shift (左)", (int)KeyCode.LeftShift);
options.Add("Shift (右)", (int)KeyCode.RightShift);
options.Add("Ctrl (左)", (int)KeyCode.LeftControl);
options.Add("Ctrl (右)", (int)KeyCode.RightControl);
options.Add("Alt (左)", (int)KeyCode.LeftAlt);
options.Add("Alt (右)", (int)KeyCode.RightAlt);
options.Add("Tab", (int)KeyCode.Tab);
options.Add("Backspace", (int)KeyCode.Backspace);
options.Add("Delete", (int)KeyCode.Delete);
options.Add("Home", (int)KeyCode.Home);
options.Add("End", (int)KeyCode.End);
options.Add("PageUp", (int)KeyCode.PageUp);
options.Add("PageDown", (int)KeyCode.PageDown);
options.Add("插入", (int)KeyCode.Insert);
// 方向键
options.Add("向上", (int)KeyCode.UpArrow);
options.Add("向下", (int)KeyCode.DownArrow);
options.Add("向左", (int)KeyCode.LeftArrow);
options.Add("向右", (int)KeyCode.RightArrow);
// 鼠标按键
options.Add("鼠标左键", (int)KeyCode.Mouse0);
options.Add("鼠标右键", (int)KeyCode.Mouse1);
options.Add("鼠标中键", (int)KeyCode.Mouse2);
// 其他一些常用键
options.Add("~", (int)KeyCode.BackQuote);
options.Add("-", (int)KeyCode.Minus);
options.Add("=", (int)KeyCode.Equals);
options.Add("[", (int)KeyCode.LeftBracket);
options.Add("]", (int)KeyCode.RightBracket);
options.Add("\\", (int)KeyCode.Backslash);
options.Add(";", (int)KeyCode.Semicolon);
options.Add("'", (int)KeyCode.Quote);
options.Add(",", (int)KeyCode.Comma);
options.Add(".", (int)KeyCode.Period);
options.Add("/", (int)KeyCode.Slash);
return options;
}
private static string ToSingleFeatureChineseString(DamageFeature feature)
{
switch (feature)
{
case DamageFeature.Undefined:
return "未指定特性";
case DamageFeature.NormalDamage:
return "普通伤害";
case DamageFeature.RealDamage:
return "真实伤害";
case DamageFeature.BuffOrEffectDamage:
return "增益/效果伤害";
case DamageFeature.ArmorIgnoringDamage:
return "无视护甲伤害";
case DamageFeature.CriticalDamage:
return "暴击伤害";
case DamageFeature.ArmorPiercingDamage:
return "护甲穿透伤害";
case DamageFeature.ExplosionDamage:
return "爆炸伤害";
case DamageFeature.ArmorBreakingDamage:
return "护甲破坏伤害";
case DamageFeature.ElementalDamage:
return "元素伤害";
case DamageFeature.OnHitBuffApply:
return "命中附加增益";
case DamageFeature.OnHitBleed:
return "命中附加流血";
default:
return "未知特性"; // 处理未定义或将来添加的特性
}
}
}
}

View File

@@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]

View File

@@ -0,0 +1,22 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("HitFeedback")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+8fcbdc5649e0b93fd1b771001f53cdbb81da2c78")]
[assembly: System.Reflection.AssemblyProductAttribute("HitFeedback")]
[assembly: System.Reflection.AssemblyTitleAttribute("HitFeedback")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
// 由 MSBuild WriteCodeFragment 类生成。

View File

@@ -0,0 +1 @@
443c8a898256e461af19611af44d76c4c5616c60885973a9cda418947ee407d3

View File

@@ -0,0 +1,8 @@
is_global = true
build_property.RootNamespace = HitFeedback
build_property.ProjectDir = D:\vs_project\DuckovMods\HitFeedback\
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =
build_property.CsWinRTUseWindowsUIXamlProjections = false
build_property.EffectiveAnalysisLevelStyle =
build_property.EnableCodeStyleSeverity =

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More