feat: 受击音效更新类别控制
This commit is contained in:
488
HitFeedback/Api/ModConfigApi.cs
Normal file
488
HitFeedback/Api/ModConfigApi.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using UnityEngine; // 假设在Unity环境中使用Debug.LogError
|
||||
|
||||
@@ -9,78 +11,140 @@ namespace HitFeedback
|
||||
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; // 如果文件不存在,就没必要继续了
|
||||
return;
|
||||
}
|
||||
|
||||
// 清空旧的概率数据,确保每次加载都是从新开始
|
||||
probability.Clear();
|
||||
|
||||
audioDamageFeatures.Clear(); // 清空旧的音频伤害特性数据
|
||||
try
|
||||
{
|
||||
using (var sr = new StreamReader(filename))
|
||||
{
|
||||
string line;
|
||||
var lineNumber = 0; // 用于错误报告
|
||||
var lineNumber = 0;
|
||||
string currentSection = ""; // 用于解析节
|
||||
while ((line = sr.ReadLine()) != null)
|
||||
{
|
||||
lineNumber++;
|
||||
line = line.Trim(); // 移除行首尾的空白字符
|
||||
|
||||
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}'");
|
||||
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();
|
||||
|
||||
// 解析 hotKey
|
||||
if (key.Equals("hotKey", System.StringComparison.OrdinalIgnoreCase))
|
||||
if (currentSection.Equals("General", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
try
|
||||
// 解析 hotKey
|
||||
if (key.Equals("hotKey", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
hotKey = (KeyCode)System.Enum.Parse(typeof(KeyCode), valueStr, true);
|
||||
}
|
||||
catch (System.ArgumentException)
|
||||
{
|
||||
Debug.LogError($"Invalid KeyCode '{valueStr}' in config file '{filename}' at line {lineNumber}. Using default F8.");
|
||||
hotKey = KeyCode.F8; // 设置为默认值或保持不变
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 解析 probability 字典项
|
||||
else
|
||||
else if (currentSection.Equals("Probabilities", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (float.TryParse(valueStr, out var probValue))
|
||||
// 解析 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.");
|
||||
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 (System.Exception ex)
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"Error reading config file '{filename}': {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将当前配置存储到指定INI文件。
|
||||
/// </summary>
|
||||
@@ -89,17 +153,17 @@ namespace HitFeedback
|
||||
{
|
||||
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();
|
||||
sw.WriteLine("[General]");
|
||||
sw.WriteLine($"hotKey = {hotKey.ToString()}");
|
||||
sw.WriteLine();
|
||||
@@ -108,22 +172,113 @@ namespace HitFeedback
|
||||
sw.WriteLine("[Probabilities]");
|
||||
foreach (var kvp in probability)
|
||||
{
|
||||
// 使用 InvariantCulture 确保浮点数的格式在不同区域设置下都一致
|
||||
sw.WriteLine($"{kvp.Key} = {kvp.Value.ToString(System.Globalization.CultureInfo.InvariantCulture)}");
|
||||
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 (System.Exception ex)
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
69
HitFeedback/DamageFeature.cs
Normal file
69
HitFeedback/DamageFeature.cs
Normal 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,
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Duckov;
|
||||
using HitFeedback.Api;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
using UnityEngine.SceneManagement;
|
||||
@@ -11,24 +12,26 @@ using Random = UnityEngine.Random;
|
||||
|
||||
namespace HitFeedback
|
||||
{
|
||||
public class ModBehaviour:Duckov.Modding.ModBehaviour
|
||||
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> audioFilePath = new Dictionary<string,float>();
|
||||
|
||||
public Dictionary<string, float> audioProbability = new Dictionary<string, float>();
|
||||
|
||||
public Health health;
|
||||
|
||||
public Config config=new Config();
|
||||
|
||||
|
||||
public Config config = new Config();
|
||||
|
||||
public float totalWeight;
|
||||
|
||||
public const string MOD_SETTING_NAME = "受击反馈";
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (Input.GetKeyDown(KeyCode.F8))
|
||||
if (Input.GetKeyDown(config.hotKey))
|
||||
{
|
||||
PlayRandomAudioClip();
|
||||
}
|
||||
@@ -37,16 +40,16 @@ namespace HitFeedback
|
||||
protected override void OnAfterSetup()
|
||||
{
|
||||
LevelManager.OnLevelInitialized += OnSceneLoaded;
|
||||
audioFolderPath=Path.Combine(info.path,AudioFolderName);
|
||||
configFilePath=Path.Combine(info.path, ConfigFileName);
|
||||
audioFolderPath = Path.Combine(info.path, AudioFolderName);
|
||||
configFilePath = Path.Combine(info.path, ConfigFileName);
|
||||
FindWavFiles();
|
||||
config.LoadConfig(configFilePath);
|
||||
ApplyConfig();
|
||||
foreach (var f in audioFilePath)
|
||||
{
|
||||
totalWeight+=f.Value;
|
||||
}
|
||||
|
||||
InitializeSetting();
|
||||
UpdateTotalWeight();
|
||||
}
|
||||
|
||||
protected override void OnBeforeDeactivate()
|
||||
{
|
||||
LevelManager.OnLevelInitialized -= OnSceneLoaded;
|
||||
@@ -58,13 +61,22 @@ namespace HitFeedback
|
||||
SaveConfig();
|
||||
}
|
||||
|
||||
private void UpdateTotalWeight()
|
||||
{
|
||||
totalWeight = 0;
|
||||
foreach (var f in audioProbability)
|
||||
{
|
||||
totalWeight += f.Value;
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyConfig()
|
||||
{
|
||||
foreach (var f in config.probability)
|
||||
{
|
||||
if (audioFilePath.ContainsKey(f.Key))
|
||||
if (audioProbability.ContainsKey(f.Key))
|
||||
{
|
||||
audioFilePath[f.Key] = f.Value;
|
||||
audioProbability[f.Key] = f.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,28 +84,36 @@ namespace HitFeedback
|
||||
private void SaveConfig()
|
||||
{
|
||||
config.probability.Clear();
|
||||
foreach (var f in audioFilePath)
|
||||
foreach (var f in audioProbability)
|
||||
{
|
||||
config.probability.Add(f.Key, f.Value);
|
||||
}
|
||||
|
||||
config.SaveConfig(configFilePath);
|
||||
}
|
||||
|
||||
private void FindWavFiles()
|
||||
{
|
||||
audioFilePath.Clear();
|
||||
audioProbability.Clear();
|
||||
if (!Directory.Exists(audioFolderPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string[] files = Directory.GetFiles(audioFolderPath, "*.wav", SearchOption.TopDirectoryOnly);
|
||||
if (files.Length > 0)
|
||||
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 (string filePath in files)
|
||||
foreach (var filePath in audioFiles)
|
||||
{
|
||||
audioFilePath.Add(Path.GetFileName(filePath), 1);
|
||||
audioProbability.Add(Path.GetFileName(filePath), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,8 +140,10 @@ namespace HitFeedback
|
||||
{
|
||||
if (health)
|
||||
{
|
||||
|
||||
health.OnHurtEvent.RemoveListener(OnHurtEvent);
|
||||
}
|
||||
|
||||
health = CharacterMainControl.Main?.Health;
|
||||
if (health)
|
||||
{
|
||||
@@ -132,15 +154,16 @@ namespace HitFeedback
|
||||
|
||||
private void OnHurtEvent(DamageInfo damageInfo)
|
||||
{
|
||||
PlayRandomAudioClip();
|
||||
if (config.ShouldPlayAudioFeedback(damageInfo))
|
||||
PlayRandomAudioClip();
|
||||
}
|
||||
|
||||
|
||||
public void PlayRandomAudioClip()
|
||||
{
|
||||
if (audioFilePath.Count > 0)
|
||||
if (audioProbability.Count > 0)
|
||||
{
|
||||
var randomIndex = Random.Range(0, totalWeight);
|
||||
foreach (var f in audioFilePath)
|
||||
foreach (var f in audioProbability)
|
||||
{
|
||||
randomIndex -= f.Value;
|
||||
if (randomIndex <= 0)
|
||||
@@ -155,5 +178,182 @@ namespace HitFeedback
|
||||
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 "未知特性"; // 处理未定义或将来添加的特性
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ using System.Reflection;
|
||||
[assembly: System.Reflection.AssemblyCompanyAttribute("HitFeedback")]
|
||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
|
||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+0206a83f56b5a794fe2f173b4a047cc4f0d4cd90")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+5d69efbc3f80a5422cef0884e02fb27adf20b467")]
|
||||
[assembly: System.Reflection.AssemblyProductAttribute("HitFeedback")]
|
||||
[assembly: System.Reflection.AssemblyTitleAttribute("HitFeedback")]
|
||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||
|
||||
@@ -1 +1 @@
|
||||
044e9b3ac36cbf242c64f55f40a9077d40727cb97e540d335e1e29dbc7dccff1
|
||||
fa83406df4fd7108a6156ab0271b4cddce6abc73c66cbcefdb2f1419643481ef
|
||||
|
||||
Binary file not shown.
@@ -1 +1 @@
|
||||
17059da2b4ba38151f18bb4edeeed66662c124b29efddf4c48967bc01f547046
|
||||
86c7d211b447f2631e25f9656fa8ef4b9b63f5a671cedbbe00854ccaccdd00b3
|
||||
|
||||
Binary file not shown.
@@ -1 +1 @@
|
||||
17620100186005303
|
||||
17623343068138064
|
||||
Reference in New Issue
Block a user