Newer
Older
DungeonShooting / DungeonShooting_Godot / src / framework / common / NodeExtend.cs
using System;
using System.Collections;
using Godot;

/// <summary>
/// 该类为 node 节点通用扩展函数类
/// </summary>
public static class NodeExtend
{
    /// <summary>
    /// 获取 IHurt 绑定的 ActivityObject, 没有则返回 null
    /// </summary>
    /// <param name="hurt"></param>
    /// <returns></returns>
    public static ActivityObject GetActivityObject(this IHurt hurt)
    {
        if (hurt is ActivityObject activityObject)
        {
            return activityObject;
        }

        if (hurt is HurtArea hurtArea)
        {
            return hurtArea.Master;
        }

        return null;
    }

    /// <summary>
    /// 获取 IHurt 节点的坐标
    /// </summary>
    public static Vector2 GetPosition(this IHurt hurt)
    {
        if (hurt is ActivityObject role)
        {
            return role.GetCenterPosition();
        }

        if (hurt is Node2D node2D)
        {
            return node2D.GlobalPosition;
        }
        
        return Vector2.Zero;
    }
    
    /// <summary>
    /// 将节点插入的房间物体根节点
    /// </summary>
    /// <param name="node">实例</param>
    /// <param name="layer">放入的层</param>
    public static void AddToActivityRoot(this Node2D node, RoomLayerEnum layer)
    {
        GameApplication.Instance.DungeonManager.CurrWorld.GetRoomLayer(layer).AddChild(node);
    }
    
    /// <summary>
    /// 将节点插入的房间物体根节点,延时调用
    /// </summary>
    /// <param name="node">实例</param>
    /// <param name="layer">放入的层</param>
    public static void AddToActivityRootDeferred(this Node2D node, RoomLayerEnum layer)
    {
        World.Current.GetRoomLayer(layer).CallDeferred(Node.MethodName.AddChild, node);
    }

    /// <summary>
    /// 设置Ui布局方式是否横向扩展, 如果为 true, 则 GridContainer 的宽度会撑满父物体
    /// </summary>
    public static void SetHorizontalExpand(this Control control, bool flag)
    {
        if (flag)
        {
            control.SizeFlagsHorizontal |= Control.SizeFlags.Expand;
        }
        else if ((control.SizeFlagsHorizontal & Control.SizeFlags.Expand) != 0)
        {
            control.SizeFlagsHorizontal ^= Control.SizeFlags.Expand;
        }
    }

    /// <summary>
    /// 获取Ui布局方式是否横向扩展
    /// </summary>
    public static bool GetHorizontalExpand(this Control control)
    {
        return (control.SizeFlagsHorizontal & Control.SizeFlags.Expand) != 0;
    }
    
    /// <summary>
    /// 返回鼠标是否在Ui矩形内
    /// </summary>
    public static bool IsMouseInRect(this Control control, float border = 0)
    {
        var pos = control.GetLocalMousePosition();
        if (pos.X < border || pos.Y < border)
        {
            return false;
        }

        var size = control.Size;
        return pos.X <= size.X - border && pos.Y <= size.Y - border;
    }

    /// <summary>
    /// 设置是否启用节点
    /// </summary>
    public static void SetActive(this Node node, bool value)
    {
        if (node is CanvasItem canvasItem)
        {
            canvasItem.Visible = value;
        }
        node.SetProcess(value);
        node.SetPhysicsProcess(value);
        node.SetProcessInput(value);
        node.SetPhysicsProcessInternal(value);
        node.SetProcessInput(value);
    }
    
    /// <summary>
    /// 延时指定时间调用一个回调函数
    /// </summary>
    public static void CallDelay(this ICoroutine coroutine, float delayTime, Action cb)
    {
        coroutine.StartCoroutine(_CallDelay(delayTime, cb));
    }
    
    /// <summary>
    /// 延时指定时间调用一个回调函数
    /// </summary>
    public static void CallDelay<T1>(this ICoroutine coroutine, float delayTime, Action<T1> cb, T1 arg1)
    {
        coroutine.StartCoroutine(_CallDelay(delayTime, cb, arg1));
    }
    
    /// <summary>
    /// 延时指定时间调用一个回调函数
    /// </summary>
    public static void CallDelay<T1, T2>(this ICoroutine coroutine, float delayTime, Action<T1, T2> cb, T1 arg1, T2 arg2)
    {
        coroutine.StartCoroutine(_CallDelay(delayTime, cb, arg1, arg2));
    }
    
    /// <summary>
    /// 延时指定时间调用一个回调函数
    /// </summary>
    public static void CallDelay<T1, T2, T3>(this ICoroutine coroutine, float delayTime, Action<T1, T2, T3> cb, T1 arg1, T2 arg2, T3 arg3)
    {
        coroutine.StartCoroutine(_CallDelay(delayTime, cb, arg1, arg2, arg3));
    }
    
    //---------------------------
    
    /// <summary>
    /// 延时指定时间调用一个回调函数
    /// </summary>
    public static void CallDelayInNode(this Node node, float delayTime, Action cb)
    {
        GameApplication.Instance.StartCoroutine(_CallDelay(delayTime, cb));
    }
    
    /// <summary>
    /// 延时指定时间调用一个回调函数
    /// </summary>
    public static void CallDelayInNode<T1>(this Node node, float delayTime, Action<T1> cb, T1 arg1)
    {
        GameApplication.Instance.StartCoroutine(_CallDelay(delayTime, cb, arg1));
    }
    
    /// <summary>
    /// 延时指定时间调用一个回调函数
    /// </summary>
    public static void CallDelayInNode<T1, T2>(this Node node, float delayTime, Action<T1, T2> cb, T1 arg1, T2 arg2)
    {
        GameApplication.Instance.StartCoroutine(_CallDelay(delayTime, cb, arg1, arg2));
    }
    
    /// <summary>
    /// 延时指定时间调用一个回调函数
    /// </summary>
    public static void CallDelayInNode<T1, T2, T3>(this Node node, float delayTime, Action<T1, T2, T3> cb, T1 arg1, T2 arg2, T3 arg3)
    {
        GameApplication.Instance.StartCoroutine(_CallDelay(delayTime, cb, arg1, arg2, arg3));
    }
    
    private static IEnumerator _CallDelay(float delayTime, Action cb)
    {
        yield return new WaitForSeconds(delayTime);
        cb();
    }
    
    private static IEnumerator _CallDelay<T1>(float delayTime, Action<T1> cb, T1 arg1)
    {
        yield return new WaitForSeconds(delayTime);
        cb(arg1);
    }
    
    private static IEnumerator _CallDelay<T1, T2>(float delayTime, Action<T1, T2> cb, T1 arg1, T2 arg2)
    {
        yield return new WaitForSeconds(delayTime);
        cb(arg1, arg2);
    }
    
    private static IEnumerator _CallDelay<T1, T2, T3>(float delayTime, Action<T1, T2, T3> cb, T1 arg1, T2 arg2, T3 arg3)
    {
        yield return new WaitForSeconds(delayTime);
        cb(arg1,arg2, arg3);
    }

    /// <summary>
    /// 给Ui节点添加拖拽事件
    /// </summary>
    /// <param name="control">需要绑定事件的节点对象</param>
    /// <param name="callback">拖拽回调函数</param>
    public static UiEventBinder AddDragListener(this Control control, Action<DragState, Vector2> callback)
    {
        return AddDragListener(control, DragButtonEnum.Left, callback);
    }

    /// <summary>
    /// 给Ui节点添加拖拽事件
    /// </summary>
    /// <param name="control">需要绑定拖拽的节点对象</param>
    /// <param name="triggerButton">可触发拖拽的按钮</param>
    /// <param name="callback">拖拽回调函数</param>
    public static UiEventBinder AddDragListener(this Control control, DragButtonEnum triggerButton, Action<DragState, Vector2> callback)
    {
        var dragFlag = false;
        Control.GuiInputEventHandler handler = (ev) =>
        {
            if (!dragFlag) //未开始拖拽
            {
                if (ev is InputEventMouseButton mouseButton && mouseButton.Pressed &&
                    CheckDragButton(mouseButton.ButtonIndex, triggerButton)) //按下按钮
                {
                    control.AcceptEvent();
                    dragFlag = true;
                    callback(DragState.DragStart, Vector2.Zero);
                }
            }
            else //拖拽中
            {
                if (ev is InputEventMouseButton mouseButton)
                {
                    if (!mouseButton.Pressed && CheckDragButton(mouseButton.ButtonIndex, triggerButton)) //松开按钮
                    {
                        control.AcceptEvent();
                        dragFlag = false;
                        callback(DragState.DragEnd, Vector2.Zero);
                    }
                } else if (ev is InputEventMouseMotion mouseMotion) //拖拽中
                {
                    control.AcceptEvent();
                    var delta = mouseMotion.Relative;
                    if (delta != Vector2.Zero)
                    {
                        callback(DragState.DragMove, mouseMotion.Relative);
                    }
                }
            }
        };
        control.GuiInput += handler;
        return new UiEventBinder(control, handler);
    }

    private static bool CheckDragButton(MouseButton button, DragButtonEnum triggerButton)
    {
        DragButtonEnum buttonEnum;
        switch (button)
        {
            case MouseButton.Left:
                buttonEnum = DragButtonEnum.Left;
                break;
            case MouseButton.Right:
                buttonEnum = DragButtonEnum.Right;
                break;
            case MouseButton.Middle:
                buttonEnum = DragButtonEnum.Middle;
                break;
            default: return false;
        }

        return (buttonEnum & triggerButton) != 0;
    }

    /// <summary>
    /// 给Ui节点添加鼠标滚轮事件
    /// </summary>
    /// <param name="control">需要绑定事件的节点对象</param>
    /// <param name="callback">滚轮回调, 参数 -1 表示滚轮向下滚动, 1 表示滚轮向上滚动</param>
    public static UiEventBinder AddMouseWheelListener(this Control control, Action<int> callback)
    {
        Control.GuiInputEventHandler handler = (ev) =>
        {
            if (ev is InputEventMouseButton mouseButton)
            {
                if (mouseButton.ButtonIndex == MouseButton.WheelDown)
                {
                    control.AcceptEvent();
                    callback(-1);
                }
                else if (mouseButton.ButtonIndex == MouseButton.WheelUp)
                {
                    control.AcceptEvent();
                    callback(1);
                }
            }
        };
        control.GuiInput += handler;
        return new UiEventBinder(control, handler);
    }
}