2025-11-05 21:34:21 +08:00
|
|
|
|
using System;
|
2025-11-02 11:43:21 +08:00
|
|
|
|
using System.Collections.Generic;
|
2025-11-05 21:34:21 +08:00
|
|
|
|
using System.Globalization;
|
2025-11-04 17:05:34 +08:00
|
|
|
|
using System.IO;
|
|
|
|
|
|
using UnityEngine; // 假设在Unity环境中使用Debug.LogError
|
2025-11-02 11:43:21 +08:00
|
|
|
|
|
|
|
|
|
|
namespace HitFeedback
|
|
|
|
|
|
{
|
|
|
|
|
|
public class Config
|
|
|
|
|
|
{
|
2025-11-04 17:05:34 +08:00
|
|
|
|
public KeyCode hotKey = KeyCode.F8;
|
2025-11-02 11:43:21 +08:00
|
|
|
|
public Dictionary<string, float> probability = new Dictionary<string, float>();
|
2025-11-04 17:05:34 +08:00
|
|
|
|
|
2025-11-05 21:34:21 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 当伤害具有这些特性之一时,将播放音频反馈。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public HashSet<DamageFeature> audioDamageFeatures = new HashSet<DamageFeature>();
|
|
|
|
|
|
|
2025-11-04 17:05:34 +08:00
|
|
|
|
public void LoadConfig(string filename)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!File.Exists(filename))
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError($"Config file not found: {filename}");
|
2025-11-05 21:34:21 +08:00
|
|
|
|
return;
|
2025-11-04 17:05:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
probability.Clear();
|
2025-11-05 21:34:21 +08:00
|
|
|
|
audioDamageFeatures.Clear(); // 清空旧的音频伤害特性数据
|
2025-11-04 17:05:34 +08:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
using (var sr = new StreamReader(filename))
|
|
|
|
|
|
{
|
|
|
|
|
|
string line;
|
2025-11-05 21:34:21 +08:00
|
|
|
|
var lineNumber = 0;
|
|
|
|
|
|
string currentSection = ""; // 用于解析节
|
2025-11-04 17:05:34 +08:00
|
|
|
|
while ((line = sr.ReadLine()) != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
lineNumber++;
|
2025-11-05 21:34:21 +08:00
|
|
|
|
line = line.Trim();
|
2025-11-04 17:05:34 +08:00
|
|
|
|
// 忽略空行和注释行
|
|
|
|
|
|
if (string.IsNullOrEmpty(line) || line.StartsWith(";") || line.StartsWith("#"))
|
|
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-05 21:34:21 +08:00
|
|
|
|
// 处理节标题,例如 [General] 或 [AudioFeatures]
|
|
|
|
|
|
if (line.StartsWith("[") && line.EndsWith("]"))
|
|
|
|
|
|
{
|
|
|
|
|
|
currentSection = line.Substring(1, line.Length - 2).Trim();
|
|
|
|
|
|
continue; // 跳过节标题行
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-04 17:05:34 +08:00
|
|
|
|
// 查找等号
|
|
|
|
|
|
var separatorIndex = line.IndexOf('=');
|
|
|
|
|
|
if (separatorIndex == -1)
|
|
|
|
|
|
{
|
2025-11-05 21:34:21 +08:00
|
|
|
|
Debug.LogWarning(
|
|
|
|
|
|
$"Skipping malformed line in config file '{filename}' at line {lineNumber}: No '=' found. Line: '{line}'");
|
2025-11-04 17:05:34 +08:00
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var key = line.Substring(0, separatorIndex).Trim();
|
|
|
|
|
|
var valueStr = line.Substring(separatorIndex + 1).Trim();
|
2025-11-05 21:34:21 +08:00
|
|
|
|
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))
|
2025-11-04 17:05:34 +08:00
|
|
|
|
{
|
2025-11-05 21:34:21 +08:00
|
|
|
|
// 解析 probability 字典项
|
|
|
|
|
|
if (float.TryParse(valueStr, NumberStyles.Float, CultureInfo.InvariantCulture,
|
|
|
|
|
|
out var probValue))
|
2025-11-04 17:05:34 +08:00
|
|
|
|
{
|
2025-11-05 21:34:21 +08:00
|
|
|
|
probability[key] = probValue;
|
2025-11-04 17:05:34 +08:00
|
|
|
|
}
|
2025-11-05 21:34:21 +08:00
|
|
|
|
else
|
2025-11-04 17:05:34 +08:00
|
|
|
|
{
|
2025-11-05 21:34:21 +08:00
|
|
|
|
Debug.LogWarning(
|
|
|
|
|
|
$"Invalid float value '{valueStr}' for key '{key}' in config file '{filename}' at line {lineNumber}. Skipping entry.");
|
2025-11-04 17:05:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-05 21:34:21 +08:00
|
|
|
|
else if (currentSection.Equals("AudioFeatures", StringComparison.OrdinalIgnoreCase))
|
2025-11-04 17:05:34 +08:00
|
|
|
|
{
|
2025-11-05 21:34:21 +08:00
|
|
|
|
// 解析 audioDamageFeatures 集合项
|
|
|
|
|
|
// 键是 'feature' (或者可以随意定义,只要值是我们关心的)
|
|
|
|
|
|
// 值是 ExtendedDamageFeature 的枚举名称
|
|
|
|
|
|
if (key.Equals("feature", StringComparison.OrdinalIgnoreCase)) // 假设所有特征都用同一个键"feature"
|
2025-11-04 17:05:34 +08:00
|
|
|
|
{
|
2025-11-05 21:34:21 +08:00
|
|
|
|
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.");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-04 17:05:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-11-05 21:34:21 +08:00
|
|
|
|
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.");
|
|
|
|
|
|
}
|
2025-11-04 17:05:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-05 21:34:21 +08:00
|
|
|
|
// 如果存在其他节,可以在这里添加 else if 处理
|
2025-11-04 17:05:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-05 21:34:21 +08:00
|
|
|
|
catch (Exception ex)
|
2025-11-04 17:05:34 +08:00
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError($"Error reading config file '{filename}': {ex.Message}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-05 21:34:21 +08:00
|
|
|
|
|
2025-11-04 17:05:34 +08:00
|
|
|
|
/// <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);
|
|
|
|
|
|
}
|
2025-11-05 21:34:21 +08:00
|
|
|
|
|
2025-11-04 17:05:34 +08:00
|
|
|
|
using (var sw = new StreamWriter(filename))
|
|
|
|
|
|
{
|
|
|
|
|
|
sw.WriteLine("; HitFeedback Configuration File");
|
|
|
|
|
|
sw.WriteLine("; Generated by HitFeedback.Config class");
|
2025-11-05 21:34:21 +08:00
|
|
|
|
sw.WriteLine();
|
2025-11-04 17:05:34 +08:00
|
|
|
|
sw.WriteLine("[General]");
|
|
|
|
|
|
sw.WriteLine($"hotKey = {hotKey.ToString()}");
|
|
|
|
|
|
sw.WriteLine();
|
|
|
|
|
|
if (probability.Count > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
sw.WriteLine("[Probabilities]");
|
|
|
|
|
|
foreach (var kvp in probability)
|
|
|
|
|
|
{
|
2025-11-05 21:34:21 +08:00
|
|
|
|
sw.WriteLine($"{kvp.Key} = {kvp.Value.ToString(CultureInfo.InvariantCulture)}");
|
2025-11-04 17:05:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
sw.WriteLine("; No probabilities currently configured.");
|
|
|
|
|
|
}
|
2025-11-05 21:34:21 +08:00
|
|
|
|
|
|
|
|
|
|
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.");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-04 17:05:34 +08:00
|
|
|
|
sw.WriteLine();
|
|
|
|
|
|
}
|
2025-11-05 21:34:21 +08:00
|
|
|
|
|
2025-11-04 17:05:34 +08:00
|
|
|
|
Debug.Log($"Config saved to: {filename}");
|
|
|
|
|
|
}
|
2025-11-05 21:34:21 +08:00
|
|
|
|
catch (Exception ex)
|
2025-11-04 17:05:34 +08:00
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError($"Error saving config file '{filename}': {ex.Message}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-05 21:34:21 +08:00
|
|
|
|
/// <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;
|
|
|
|
|
|
}
|
2025-11-02 11:43:21 +08:00
|
|
|
|
}
|
2025-11-04 17:05:34 +08:00
|
|
|
|
}
|