mirror of
http://47.107.252.169:3000/Roguelite-Game-Developing-Team/Gen_Hack-and-Slash-Roguelite.git
synced 2025-11-20 04:27:13 +08:00
(client) feat:健康给予,路径优化,结算界面,商店界面 (#60)
Co-authored-by: m0_75251201 <m0_75251201@noreply.gitcode.com> Reviewed-on: http://47.107.252.169:3000/Roguelite-Game-Developing-Team/Gen_Hack-and-Slash-Roguelite/pulls/60
This commit is contained in:
@@ -10,13 +10,13 @@ namespace Parsing
|
||||
public static class ConditionDelegateFactory
|
||||
{
|
||||
// 正则表达式用于解析函数名和参数
|
||||
private static readonly Regex _methodCallRegex = new Regex(
|
||||
private static readonly Regex _methodCallRegex = new(
|
||||
@"^(?<methodName>[a-zA-Z_][a-zA-Z0-9_]*)(?:\((?<args>.*)\))?$",
|
||||
RegexOptions.Compiled
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// 解析条件字符串并创建 Func<Entity.Entity, bool> 委托。
|
||||
/// 解析条件字符串并创建 Func<Entity.Entity, bool> 委托。
|
||||
/// </summary>
|
||||
/// <param name="conditionString">条件字符串,如 "EntityHealth(20)" 或 "IsTargetAlive"。</param>
|
||||
/// <param name="entityType">实体类型,通常是 typeof(Entity.Entity)。</param>
|
||||
@@ -38,10 +38,8 @@ namespace Parsing
|
||||
|
||||
var match = _methodCallRegex.Match(conditionString);
|
||||
if (!match.Success)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"条件字符串 '{conditionString}' 格式不正确。期望格式: MethodName 或 MethodName(arg1, arg2)。");
|
||||
}
|
||||
|
||||
var methodName = match.Groups["methodName"].Value;
|
||||
var argsString = match.Groups["args"].Success ? match.Groups["args"].Value : null;
|
||||
@@ -146,47 +144,37 @@ namespace Parsing
|
||||
|
||||
// 如果没有额外参数,直接创建委托以获得最佳性能
|
||||
if (parsedArgValues.Count == 0)
|
||||
{
|
||||
return (Func<Entity.Entity, bool>)Delegate.CreateDelegate(typeof(Func<Entity.Entity, bool>),
|
||||
bestMatchMethod);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 创建一个闭包来绑定解析后的参数
|
||||
// 闭包捕获 finalInvokeArgs,并在运行时填充第一个参数 (entity)
|
||||
return (entity) =>
|
||||
{
|
||||
// 复制一份 finalInvokeArgs,因为 Invoke 会修改数组内容(如果参数是 ref/out)
|
||||
// 并且需要将 entity 放入第一个位置
|
||||
var invokeArgs = new object[finalInvokeArgs.Length];
|
||||
invokeArgs[0] = entity;
|
||||
for (var i = 1; i < finalInvokeArgs.Length; i++)
|
||||
{
|
||||
invokeArgs[i] = finalInvokeArgs[i];
|
||||
}
|
||||
|
||||
return (bool)bestMatchMethod.Invoke(null, invokeArgs);
|
||||
};
|
||||
}
|
||||
// 创建一个闭包来绑定解析后的参数
|
||||
// 闭包捕获 finalInvokeArgs,并在运行时填充第一个参数 (entity)
|
||||
return entity =>
|
||||
{
|
||||
// 复制一份 finalInvokeArgs,因为 Invoke 会修改数组内容(如果参数是 ref/out)
|
||||
// 并且需要将 entity 放入第一个位置
|
||||
var invokeArgs = new object[finalInvokeArgs.Length];
|
||||
invokeArgs[0] = entity;
|
||||
for (var i = 1; i < finalInvokeArgs.Length; i++) invokeArgs[i] = finalInvokeArgs[i];
|
||||
|
||||
return (bool)bestMatchMethod.Invoke(null, invokeArgs);
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 辅助方法:拆分参数字符串。
|
||||
/// 这是一个更健壮的实现,能够正确处理包含逗号的带引号字符串和嵌套括号。
|
||||
/// 辅助方法:拆分参数字符串。
|
||||
/// 这是一个更健壮的实现,能够正确处理包含逗号的带引号字符串和嵌套括号。
|
||||
/// </summary>
|
||||
/// <param name="argsString">待拆分的参数字符串。</param>
|
||||
/// <returns>拆分后的参数列表。</returns>
|
||||
private static IEnumerable<string> SplitArguments(string argsString)
|
||||
{
|
||||
if (string.IsNullOrEmpty(argsString))
|
||||
{
|
||||
return Enumerable.Empty<string>();
|
||||
}
|
||||
if (string.IsNullOrEmpty(argsString)) return Enumerable.Empty<string>();
|
||||
|
||||
var arguments = new List<string>();
|
||||
var currentArgument = new StringBuilder();
|
||||
var inQuote = false; // 跟踪是否在双引号内部
|
||||
var parenLevel = 0; // 跟踪括号的嵌套层级
|
||||
var parenLevel = 0; // 跟踪括号的嵌套层级
|
||||
|
||||
for (var i = 0; i < argsString.Length; i++)
|
||||
{
|
||||
@@ -211,10 +199,7 @@ namespace Parsing
|
||||
{
|
||||
// 发现一个顶级的逗号分隔符:不在引号内,也不在任何括号内
|
||||
var arg = currentArgument.ToString().Trim();
|
||||
if (!string.IsNullOrEmpty(arg))
|
||||
{
|
||||
arguments.Add(arg);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(arg)) arguments.Add(arg);
|
||||
currentArgument.Clear(); // 重置,开始收集下一个参数
|
||||
}
|
||||
else
|
||||
@@ -226,21 +211,18 @@ namespace Parsing
|
||||
|
||||
// 循环结束后,添加最后一个参数(如果有的话)
|
||||
var lastArg = currentArgument.ToString().Trim();
|
||||
if (!string.IsNullOrEmpty(lastArg))
|
||||
{
|
||||
arguments.Add(lastArg);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(lastArg)) arguments.Add(lastArg);
|
||||
|
||||
return arguments;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 辅助方法:检查一个值是否可以转换为目标类型。
|
||||
/// 辅助方法:检查一个值是否可以转换为目标类型。
|
||||
/// </summary>
|
||||
private static bool CanConvert(object value, Type targetType)
|
||||
{
|
||||
// 逻辑修改:新增辅助方法,用于检查类型转换可行性
|
||||
if (value == null) return !targetType.IsValueType || (Nullable.GetUnderlyingType(targetType) != null);
|
||||
if (value == null) return !targetType.IsValueType || Nullable.GetUnderlyingType(targetType) != null;
|
||||
if (targetType.IsInstanceOfType(value)) return true;
|
||||
|
||||
try
|
||||
@@ -256,8 +238,8 @@ namespace Parsing
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解析字符串字面量为对应的对象和类型。
|
||||
/// 支持 int, long, float, double, bool, string。
|
||||
/// 解析字符串字面量为对应的对象和类型。
|
||||
/// 支持 int, long, float, double, bool, string。
|
||||
/// </summary>
|
||||
/// <param name="literalString">要解析的字面量字符串。</param>
|
||||
/// <returns>包含解析后的值和类型的元组。</returns>
|
||||
@@ -267,40 +249,23 @@ namespace Parsing
|
||||
// 顺序很重要:先尝试更窄的类型,再尝试更宽的类型,以避免不必要的类型提升。
|
||||
|
||||
// 尝试解析为 int
|
||||
if (int.TryParse(literalString, out var intValue))
|
||||
{
|
||||
return (intValue, typeof(int));
|
||||
}
|
||||
if (int.TryParse(literalString, out var intValue)) return (intValue, typeof(int));
|
||||
|
||||
// 尝试解析为 long
|
||||
if (long.TryParse(literalString, out var longValue))
|
||||
{
|
||||
return (longValue, typeof(long));
|
||||
}
|
||||
if (long.TryParse(literalString, out var longValue)) return (longValue, typeof(long));
|
||||
|
||||
// 尝试解析为 float
|
||||
if (float.TryParse(literalString, out var floatValue))
|
||||
{
|
||||
return (floatValue, typeof(float));
|
||||
}
|
||||
if (float.TryParse(literalString, out var floatValue)) return (floatValue, typeof(float));
|
||||
|
||||
// 尝试解析为 double
|
||||
if (double.TryParse(literalString, out var doubleValue))
|
||||
{
|
||||
return (doubleValue, typeof(double));
|
||||
}
|
||||
if (double.TryParse(literalString, out var doubleValue)) return (doubleValue, typeof(double));
|
||||
|
||||
// 尝试解析为 bool
|
||||
if (bool.TryParse(literalString, out var boolValue))
|
||||
{
|
||||
return (boolValue, typeof(bool));
|
||||
}
|
||||
if (bool.TryParse(literalString, out var boolValue)) return (boolValue, typeof(bool));
|
||||
|
||||
// 尝试解析为 string (如果被双引号包围)
|
||||
if (literalString.StartsWith("\"") && literalString.EndsWith("\"") && literalString.Length > 1)
|
||||
{
|
||||
return (literalString.Substring(1, literalString.Length - 2), typeof(string));
|
||||
}
|
||||
|
||||
// 默认作为字符串处理
|
||||
return (literalString, typeof(string));
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Data;
|
||||
using Managers;
|
||||
|
||||
namespace Parsing
|
||||
{
|
||||
@@ -11,7 +12,7 @@ namespace Parsing
|
||||
|
||||
public static bool HasEnemyInSight(Entity.Entity entity)
|
||||
{
|
||||
return Managers.EntityManager.Instance.ExistsHostile(entity.currentDimensionId, entity.entityPrefab);
|
||||
return EntityManager.Instance.ExistsHostile(entity.currentDimensionId, entity.entityPrefab);
|
||||
}
|
||||
|
||||
public static bool HasWeapon(Entity.Entity entity)
|
||||
@@ -24,6 +25,7 @@ namespace Parsing
|
||||
var weapon = entity.GetCurrentWeapon();
|
||||
return weapon is { Type: WeaponType.Ranged };
|
||||
}
|
||||
|
||||
public static bool HasMeleeWeapon(Entity.Entity entity)
|
||||
{
|
||||
var weapon = entity.GetCurrentWeapon();
|
||||
|
||||
85
Client/Assets/Scripts/Parsing/HediffCompParsing.cs
Normal file
85
Client/Assets/Scripts/Parsing/HediffCompParsing.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using HediffComps;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Parsing
|
||||
{
|
||||
public static class HediffCompParsing
|
||||
{
|
||||
/// <summary>
|
||||
/// 在当前应用程序域的加载程序集中查找指定名称的类型。
|
||||
/// </summary>
|
||||
/// <param name="typeFullName">要查找的类型的完整名称。</param>
|
||||
/// <param name="baseType">期望的基类 Type。</param>
|
||||
/// <returns>如果找到符合条件的类型则返回其 Type 对象;否则返回 null。</returns>
|
||||
private static Type FindTypeInAssemblies(string typeFullName, Type baseType)
|
||||
{
|
||||
// 遍历所有已加载的程序集
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
// 尝试从程序集中获取类型,不抛出异常,忽略大小写
|
||||
var foundType = assembly.GetType(typeFullName, false, true);
|
||||
// 检查找到的类型是否符合条件:
|
||||
// 1. 类型不为 null
|
||||
// 2. 是基类或其子类
|
||||
// 3. 不是抽象类(可实例化)
|
||||
// 4. 具有公共的无参数构造函数
|
||||
if (foundType != null &&
|
||||
baseType.IsAssignableFrom(foundType) &&
|
||||
!foundType.IsAbstract &&
|
||||
foundType.GetConstructor(Type.EmptyTypes) != null)
|
||||
return foundType;
|
||||
}
|
||||
|
||||
return null; // 未找到符合条件的类型
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据给定的字符串名称解析并返回 HediffComps.HediffComp 的实例化子类。
|
||||
/// </summary>
|
||||
/// <param name="hediffCompName">HediffComp 子类的名称,可能包含命名空间。</param>
|
||||
/// <returns>实例化后的 HediffComps.HediffComp 子类对象;如果找不到或无法实例化,则返回 null。</returns>
|
||||
public static HediffComp ParseHediffComp(string hediffCompName)
|
||||
{
|
||||
// 如果输入名称为空或空白,直接返回 null
|
||||
if (string.IsNullOrWhiteSpace(hediffCompName)) return null;
|
||||
|
||||
// 获取 HediffComps.HediffComp 的 Type 对象,作为查找的基准
|
||||
var baseHediffCompType = typeof(HediffComp);
|
||||
Type targetType = null;
|
||||
|
||||
// 1. 如果 hediffCompName 包含点号 '.',则尝试将其作为完整的类型名称(包含命名空间)查找。
|
||||
if (hediffCompName.Contains(".")) targetType = FindTypeInAssemblies(hediffCompName, baseHediffCompType);
|
||||
|
||||
// 2. 如果之前未找到,或者 hediffCompName 不包含点号,则尝试默认命名空间查找。
|
||||
if (targetType == null)
|
||||
{
|
||||
// 尝试在 HediffComps 命名空间下查找
|
||||
// 构建完整的类型名称,例如 "HediffComps.MyHediffComp"
|
||||
var hediffCompsNamespaceName = baseHediffCompType.Namespace + "." + hediffCompName;
|
||||
targetType = FindTypeInAssemblies(hediffCompsNamespaceName, baseHediffCompType);
|
||||
}
|
||||
|
||||
// 3. 如果前两种尝试仍未找到,则尝试在无命名空间(全局或根命名空间)下查找。
|
||||
if (targetType == null) targetType = FindTypeInAssemblies(hediffCompName, baseHediffCompType);
|
||||
|
||||
// 如果成功找到一个符合条件的类型,则尝试实例化它。
|
||||
if (targetType != null)
|
||||
try
|
||||
{
|
||||
// 使用 Activator.CreateInstance 调用类型的无参数构造函数创建实例
|
||||
return (HediffComp)Activator.CreateInstance(targetType);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// <color=red>实例化 HediffComp '{hediffCompName}' (Type: '{targetType.FullName}') 时出错: {ex.Message}</color>
|
||||
Debug.LogError(
|
||||
$"<color=red>实例化 HediffComp '{hediffCompName}' (Type: '{targetType.FullName}') 时出错: {ex.Message}</color>");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 如果所有查找和实例化尝试都失败,返回 null
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Client/Assets/Scripts/Parsing/HediffCompParsing.cs.meta
Normal file
3
Client/Assets/Scripts/Parsing/HediffCompParsing.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5106a6da4b914d21ae3811dff1e2c35f
|
||||
timeCreated: 1759750471
|
||||
@@ -7,23 +7,22 @@ namespace Parsing
|
||||
public static class Resolver
|
||||
{
|
||||
/// <summary>
|
||||
/// 将字符串表达式解析为一个谓词函数,该函数可以用于筛选实体对象。
|
||||
/// 将字符串表达式解析为一个谓词函数,该函数可以用于筛选实体对象。
|
||||
/// </summary>
|
||||
/// <param name="expression">表示条件的字符串表达式。格式示例:"entity.Id > 10" 或 "entity.Name == 'John'"。</param>
|
||||
/// <returns>返回一个 Func<Entity.Entity, bool> 类型的委托,表示解析后的谓词函数。</returns>
|
||||
/// <exception cref="FormatException">当输入表达式的格式不正确时抛出此异常。</exception>
|
||||
/// <exception cref="NotSupportedException">当表达式中包含不支持的操作符或数据类型时抛出此异常。</exception>
|
||||
/// <remarks>
|
||||
/// 表达式的格式必须符合以下规则:
|
||||
/// - 表达式由三部分组成:属性路径、操作符和值,用空格分隔。
|
||||
/// - 属性路径格式为 "entity.PropertyName",其中 PropertyName 是实体类中的一个公共属性或字段。
|
||||
/// - 操作符可以是以下之一:">", "<", ">=", "<=", "==", "!="。
|
||||
/// - 值的类型必须与属性的类型匹配,并且支持以下类型:string, int, long, float, double, decimal, bool, DateTime, Guid 和枚举类型。
|
||||
///
|
||||
/// 注意事项:
|
||||
/// - 字符串值需要用单引号或双引号括起来,例如 'John' 或 "John"。
|
||||
/// - 对于可为空类型(Nullable),会自动处理其底层类型的转换。
|
||||
/// - 字符串比较默认使用不区分大小写的 Equals 方法。
|
||||
/// 表达式的格式必须符合以下规则:
|
||||
/// - 表达式由三部分组成:属性路径、操作符和值,用空格分隔。
|
||||
/// - 属性路径格式为 "entity.PropertyName",其中 PropertyName 是实体类中的一个公共属性或字段。
|
||||
/// - 操作符可以是以下之一:">", "<", ">=", "<=", "==", "!="。
|
||||
/// - 值的类型必须与属性的类型匹配,并且支持以下类型:string, int, long, float, double, decimal, bool, DateTime, Guid 和枚举类型。
|
||||
/// 注意事项:
|
||||
/// - 字符串值需要用单引号或双引号括起来,例如 'John' 或 "John"。
|
||||
/// - 对于可为空类型(Nullable),会自动处理其底层类型的转换。
|
||||
/// - 字符串比较默认使用不区分大小写的 Equals 方法。
|
||||
/// </remarks>
|
||||
public static Func<Entity.Entity, bool> ParsePredicate(string expression)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user