using System; using System.Collections; using System.Collections.Generic; using System.Reflection; using System.Text.RegularExpressions; using Godot; /// <summary> /// 物体生成标记 /// </summary> [Tool] public partial class ActivityMark : Node2D { /// <summary> /// 物体类型 /// </summary> [Export] public ActivityIdPrefix.ActivityPrefixType Type = ActivityIdPrefix.ActivityPrefixType.NonePrefix; /// <summary> /// 创建物体的表达式, 该表达式计算出的id会自动加上 Type 前缀 /// 例如: 0001(w:100,ca:15,ra:30);0002(w:120,ca:10,ra:20) /// </summary> [Export(PropertyHint.Expression), ActivityExpression] public string ItemExpression; /// <summary> /// 所在层级 /// </summary> [Export] public RoomLayerEnum Layer = RoomLayerEnum.NormalLayer; /// <summary> /// 该标记在第几波调用 BeReady, /// 一个房间内所以敌人清完即可进入下一波 /// </summary> [Export] public int WaveNumber = 1; /// <summary> /// 延时执行时间,单位:秒 /// </summary> [Export] public float DelayTime = 0; /// <summary> /// 物体会在该矩形区域内随机位置生成 /// </summary> [Export] public Vector2I BirthRect = Vector2I.Zero; /// <summary> /// 绘制的颜色 /// </summary> [Export] public Color DrawColor = new Color(1, 1, 1, 1); /// <summary> /// 物体初始海拔高度 /// </summary> [ExportGroup("Vertical")] [Export(PropertyHint.Range, "0, 128")] public int Altitude = 0; /// <summary> /// 物体初始纵轴速度 /// </summary> [Export(PropertyHint.Range, "-1000,1000,0.1")] public float VerticalSpeed = 0; /// <summary> /// 当前标记所在Tile节点 /// </summary> public TileMap TileRoot; //是否已经结束 private bool _isOver = true; private float _overTimer = 0; private float _timer = 0; private RoomInfo _tempRoom; //绘制的字体 private static Font _drawFont; //已经计算好要生成的物体 private Dictionary<string, ActivityExpressionData> _currentExpression = new Dictionary<string, ActivityExpressionData>(); //存储所有 ActivityMark 和子类中被 [ActivityExpression] 标记的字段名称 private static Dictionary<Type, List<string>> _activityExpressionMap = new Dictionary<Type, List<string>>(); /// <summary> /// 对生成的物体执行后续操作 /// </summary> public virtual void Doing(ActivityObject activityObject, ActivityExpressionData expressionData, RoomInfo roomInfo) { } public ActivityMark() { //扫描所有 ActivityExpression var type = GetType(); if (!_activityExpressionMap.ContainsKey(type)) { // 获取类型信息 var fieldInfos = new List<string>(); var tempList = type.GetFields(BindingFlags.Instance | BindingFlags.Public); foreach (var s in tempList) { if (s.GetCustomAttribute<ActivityExpression>() != null) { fieldInfos.Add(s.Name); } } _activityExpressionMap.Add(type, fieldInfos); } } public override void _Process(double delta) { #if TOOLS if (Engine.IsEditorHint()) { QueueRedraw(); return; } #endif if (_isOver) { _overTimer += (float)delta; if (_overTimer >= 1) { SetActive(false); } } else { if (DelayTime > 0) { _timer += (float)delta; if (_timer >= DelayTime) { Doing(_tempRoom); _tempRoom = null; _isOver = true; } } } } /// <summary> /// 标记准备好了 /// </summary> public void BeReady(RoomInfo roomInfo) { if (_currentExpression == null) { return; } _isOver = false; _overTimer = 0; SetActive(true); if (DelayTime <= 0) { Doing(roomInfo); _isOver = true; } else { _timer = 0; _tempRoom = roomInfo; } } /// <summary> /// 是否已经结束 /// </summary> public bool IsOver() { return _isOver && _overTimer >= 1; } private void Doing(RoomInfo roomInfo) { var activityObject = CreateActivityObjectFromExpression(Type, nameof(ItemExpression)); if (activityObject == null) { return; } activityObject.VerticalSpeed = VerticalSpeed; activityObject.Altitude = Altitude; var pos = Position; if (BirthRect != Vector2I.Zero) { activityObject.Position = new Vector2( Utils.RandomRangeInt((int)pos.X - BirthRect.X / 2, (int)pos.X + BirthRect.X / 2), Utils.RandomRangeInt((int)pos.Y - BirthRect.Y / 2, (int)pos.Y + BirthRect.Y / 2) ); } else { activityObject.Position = pos; } activityObject.StartCoroutine(OnActivityObjectBirth(activityObject)); activityObject.PutDown(Layer); Doing(activityObject, _currentExpression[nameof(ItemExpression)], roomInfo); } /// <summary> /// 根据预制表达式创建物体并返回 /// </summary> /// <param name="type">物体类型</param> /// <param name="expressionFieldName">预制表达式字段名称, 注意是字段名称, 而不是内容</param> public ActivityObject CreateActivityObjectFromExpression(ActivityIdPrefix.ActivityPrefixType type, string expressionFieldName) { if (_currentExpression.TryGetValue(expressionFieldName, out var expressionData)) { var id = ActivityIdPrefix.GetNameByPrefixType(type) + expressionData.Id; return ActivityObject.Create(id); } return null; } /// <summary> /// 生成 ActivityObject 时调用, 用于出生时的动画效果 /// </summary> private IEnumerator OnActivityObjectBirth(ActivityObject instance) { var a = 1.0f; instance.SetBlendColor(Colors.White); //禁用自定义行为 instance.EnableCustomBehavior = false; //禁用下坠 instance.EnableVerticalMotion = false; for (var i = 0; i < 10; i++) { instance.SetBlendSchedule(a); yield return 0; } while (a > 0) { instance.SetBlendSchedule(a); a -= 0.05f; yield return 0; } //启用自定义行为 instance.EnableCustomBehavior = true; //启用下坠 instance.EnableVerticalMotion = true; } #if TOOLS public override void _Draw() { if (Engine.IsEditorHint() || GameApplication.Instance.Debug) { var drawColor = DrawColor; //如果在编辑器中选中了该节点, 则绘更改绘制颜色的透明度 var selectedNodes = Plugin.Plugin.Instance?.GetEditorInterface()?.GetSelection()?.GetSelectedNodes(); if (selectedNodes != null && selectedNodes.Contains(this)) { drawColor.A = 1f; } else { drawColor.A = 0.5f; } DrawLine(new Vector2(-2, -2), new Vector2(2, 2), drawColor, 1f); DrawLine(new Vector2(-2, 2), new Vector2(2, -2), drawColor, 1f); if (BirthRect != Vector2.Zero) { DrawRect(new Rect2(-BirthRect / 2, BirthRect), drawColor, false, 0.5f); } if (_drawFont == null) { _drawFont = ResourceManager.Load<Font>(ResourcePath.Silver_ttf); } DrawString(_drawFont, new Vector2(-14, 12), WaveNumber.ToString(), HorizontalAlignment.Center, 28, 14); } } #endif /// <summary> /// 设置当前节点是否是活动状态 /// </summary> private void SetActive(bool flag) { // SetProcess(flag); // SetPhysicsProcess(flag); // SetProcessInput(flag); // Visible = flag; var parent = GetParent(); if (flag) { if (parent == null) { TileRoot.AddChild(this); } else if (parent != TileRoot) { parent.RemoveChild(this); TileRoot.AddChild(this); } Owner = TileRoot; } else { if (parent != null) { parent.RemoveChild(this); Owner = null; } } } //----------------------------------------------------------------------------------------------------- /// <summary> /// 执行预处理操作 /// </summary> public void Pretreatment() { if (_activityExpressionMap.TryGetValue(GetType(), out var list)) { foreach (var field in list) { Pretreatment(field); } } } private void Pretreatment(string field) { var expressionStr = GetType().GetField(field)?.GetValue(this) as string; if (string.IsNullOrEmpty(expressionStr)) { return; } var activityExpression = Parse(expressionStr); if (activityExpression.Count > 0) { //权重列表 var list = new List<int>(); for (var i = 0; i < activityExpression.Count; i++) { var item = activityExpression[i]; if (item.Args.TryGetValue("weight", out var weight)) //获取自定义权重值 { list.Add(int.Parse(weight)); } else //默认权重100 { item.Args.Add("weight", "100"); list.Add(100); } } //根据权重随机值 var index = Utils.RandomWeight(list); _currentExpression.Add(field, activityExpression[index]); } } private List<ActivityExpressionData> Parse(string str) { var list = new List<ActivityExpressionData>(); var exps = str.Split(';'); for (var i = 0; i < exps.Length; i++) { var exp = exps[i]; //去除空格 exp = Regex.Replace(exp, "\\s", ""); if (string.IsNullOrEmpty(exp)) { continue; } //验证语法 if (Regex.IsMatch(exp, "^\\w+(\\((\\w+:\\w+)*(,\\w+:\\w+)*\\))?$")) { if (!exp.Contains('(')) //没有参数 { list.Add(new ActivityExpressionData(exp)); } else { var name = Regex.Match(exp, "^\\w+").Value; var activityExpression = new ActivityExpressionData(name); var paramsResult = Regex.Matches(exp, "\\w+:\\w+"); if (paramsResult.Count > 0) { foreach (Match result in paramsResult) { var valSplit = result.Value.Split(':'); activityExpression.Args.Add(valSplit[0], valSplit[1]); } } list.Add(activityExpression); } } else //语法异常 { throw new Exception("表达式语法错误: " + exp); } } return list; } }