Newer
Older
DungeonShooting / DungeonShooting_Godot / src / game / props / weapon / Weapon.cs
@小李xl 小李xl on 24 Aug 2022 22 KB 架构调整
using Godot;
using System;

/// <summary>
/// 武器的基类
/// </summary>
public abstract class Weapon : Node2D, IProp
{

    /// <summary>
    /// 武器的唯一id
    /// </summary>
    public string Id { get; }

    /// <summary>
    /// 开火回调事件
    /// </summary>
    public event Action<Weapon> FireEvent;

    /// <summary>
    /// 属性数据
    /// </summary>
    public WeaponAttribute Attribute { get; private set; }

    /// <summary>
    /// 武器的图片
    /// </summary>
    public Sprite WeaponSprite { get; private set; }

    /// <summary>
    /// 动画播放器
    /// </summary>
    /// <value></value>
    public AnimationPlayer AnimationPlayer { get; private set; }

    /// <summary>
    /// 武器攻击的目标阵营
    /// </summary>
    public CampEnum TargetCamp { get; set; }

    /// <summary>
    /// 该武器的拥有者
    /// </summary>
    public Role Master { get; private set; }

    /// <summary>
    /// 当前弹夹弹药剩余量
    /// </summary>
    public int CurrAmmo { get; private set; }
    /// <summary>
    /// 剩余弹药量
    /// </summary>
    public int ResidueAmmo { get; private set; }

    /// <summary>
    /// 武器管的开火点
    /// </summary>
    public Position2D FirePoint { get; private set; }
    /// <summary>
    /// 武器管的原点
    /// </summary>
    public Position2D OriginPoint { get; private set; }
    /// <summary>
    /// 弹壳抛出的点
    /// </summary>
    public Position2D ShellPoint { get; private set; }
    /// <summary>
    /// 碰撞器节点
    /// </summary>
    /// <value></value>
    public CollisionShape2D CollisionShape2D { get; private set; }
    /// <summary>
    /// 武器的当前散射半径
    /// </summary>
    public float CurrScatteringRange { get; private set; } = 0;
    /// <summary>
    /// 是否在换弹中
    /// </summary>
    /// <value></value>
    public bool Reloading { get; private set; } = false;
    /// <summary>
    /// 换弹计时器
    /// </summary>
    public float ReloadTimer { get; private set; } = 0;
    /// <summary>
    /// 换弹进度 (0 - 1)
    /// </summary>
    public float ReloadProgress
    {
        get
        {
            if (Attribute.AloneReload)
            {
                var num = 1f / Attribute.AmmoCapacity;
                return num * (Attribute.AmmoCapacity - CurrAmmo - 1) + num * (ReloadTimer / Attribute.ReloadTime);
            }
            return ReloadTimer / Attribute.ReloadTime;
        }
    }

    //是否按下
    private bool triggerFlag = false;
    //扳机计时器
    private float triggerTimer = 0;
    //开火前延时时间
    private float delayedTime = 0;
    //开火间隙时间
    private float fireInterval = 0;
    //开火武器口角度
    private float fireAngle = 0;
    //攻击冷却计时
    private float attackTimer = 0;
    //攻击状态
    private bool attackFlag = false;
    //按下的时间
    private float downTimer = 0;
    //松开的时间
    private float upTimer = 0;
    //连发次数
    private float continuousCount = 0;
    //连发状态记录
    private bool continuousShootFlag = false;

    /// <summary>
    /// 根据属性创建一把武器
    /// </summary>
    /// <param name="id">武器唯一id</param>
    /// <param name="attribute">属性</param>
    public Weapon(string id, WeaponAttribute attribute)
    {
        Id = id;
        Attribute = attribute;
        //加载预制体
        var tempPrefab = ResourceManager.Load<PackedScene>(Attribute.WeaponPrefab);
        if (tempPrefab == null)
        {
            throw new Exception("WeaponAttribute中未设置'WeaponPrefab'属性!");
        }
        var tempNode = tempPrefab.Instance();
        var body = tempNode.GetChild(0);
        tempNode.RemoveChild(body);
        AddChild(body);

        WeaponSprite = GetNode<Sprite>("WeaponBody/WeaponSprite");
        FirePoint = GetNode<Position2D>("WeaponBody/FirePoint");
        OriginPoint = GetNode<Position2D>("WeaponBody/OriginPoint");
        ShellPoint = GetNode<Position2D>("WeaponBody/ShellPoint");
        AnimationPlayer = GetNode<AnimationPlayer>("WeaponBody/AnimationPlayer");
        CollisionShape2D = GetNode<CollisionShape2D>("WeaponBody/Collision");

        //更新图片
        WeaponSprite.Texture = ResourceLoader.Load<Texture>(Attribute.Sprite);
        WeaponSprite.Position = Attribute.CenterPosition;
        //开火位置
        FirePoint.Position = new Vector2(Attribute.FirePosition.x, -Attribute.FirePosition.y);
        OriginPoint.Position = new Vector2(0, -Attribute.FirePosition.y);

        //弹药量
        CurrAmmo = Attribute.AmmoCapacity;
        //剩余弹药量
        ResidueAmmo = Attribute.MaxAmmoCapacity - Attribute.AmmoCapacity;
    }

    /// <summary>
    /// 当按下扳机时调用
    /// </summary>
    protected abstract void OnDownTrigger();

    /// <summary>
    /// 当松开扳机时调用
    /// </summary>
    protected abstract void OnUpTrigger();

    /// <summary>
    /// 单次开火时调用的函数
    /// </summary>
    protected abstract void OnFire();

    /// <summary>
    /// 发射子弹时调用的函数, 每发射一枚子弹调用一次,
    /// 如果做霰弹武器效果, 一次开火发射5枚子弹, 则该函数调用5次
    /// </summary>
    protected abstract void OnShoot();

    /// <summary>
    /// 当开始换弹时调用
    /// </summary>
    protected abstract void OnReload();

    /// <summary>
    /// 当换弹完成时调用
    /// </summary>
    protected abstract void OnReloadFinish();

    /// <summary>
    /// 当武器被拾起时调用
    /// </summary>
    /// <param name="master">拾起该武器的角色</param>
    protected abstract void OnPickUp(Role master);

    /// <summary>
    /// 当武器从武器袋中扔掉时调用
    /// </summary>
    protected abstract void OnThrowOut();

    /// <summary>
    /// 当武器被激活时调用, 也就是使用当武器是调用
    /// </summary>
    protected abstract void OnActive();

    /// <summary>
    /// 当武器被收起时调用
    /// </summary>
    protected abstract void OnConceal();

    public override void _Process(float delta)
    {
        if (Master == null) //这把武器被扔在地上
        {
            Reloading = false;
            ReloadTimer = 0;
            triggerTimer = triggerTimer > 0 ? triggerTimer - delta : 0;
            triggerFlag = false;
            attackFlag = false;
            attackTimer = attackTimer > 0 ? attackTimer - delta : 0;
            CurrScatteringRange = Mathf.Max(CurrScatteringRange - Attribute.ScatteringRangeBackSpeed * delta, Attribute.StartScatteringRange);
            continuousCount = 0;
            delayedTime = 0;
        }
        else if (Master.Holster.ActiveWeapon != this) //当前武器没有被使用
        {
            Reloading = false;
            ReloadTimer = 0;
            triggerTimer = triggerTimer > 0 ? triggerTimer - delta : 0;
            triggerFlag = false;
            attackFlag = false;
            attackTimer = attackTimer > 0 ? attackTimer - delta : 0;
            CurrScatteringRange = Mathf.Max(CurrScatteringRange - Attribute.ScatteringRangeBackSpeed * delta, Attribute.StartScatteringRange);
            continuousCount = 0;
            delayedTime = 0;
        }
        else //正在使用中
        {

            //换弹
            if (Reloading)
            {
                ReloadTimer -= delta;
                if (ReloadTimer <= 0)
                {
                    ReloadSuccess();
                }
            }

            if (triggerFlag)
            {
                if (upTimer > 0) //第一帧按下扳机
                {
                    upTimer = 0;
                    DownTrigger();
                }
                downTimer += delta;
            }
            else
            {
                if (downTimer > 0) //第一帧松开扳机
                {
                    downTimer = 0;
                    UpTriggern();
                }
                upTimer += delta;
            }

            // 攻击的计时器
            if (attackTimer > 0)
            {
                attackTimer -= delta;
                if (attackTimer < 0)
                {
                    delayedTime += attackTimer;
                    attackTimer = 0;
                }
            }
            else if (delayedTime > 0) //攻击延时
            {
                delayedTime -= delta;
                if (attackTimer < 0)
                {
                    delayedTime = 0;
                }
            }

            //连发判断
            if (continuousCount > 0 && delayedTime <= 0 && attackTimer <= 0)
            {
                //开火
                TriggernFire();
            }

            if (!attackFlag && attackTimer <= 0)
            {
                CurrScatteringRange = Mathf.Max(CurrScatteringRange - Attribute.ScatteringRangeBackSpeed * delta, Attribute.StartScatteringRange);
            }
            triggerTimer = triggerTimer > 0 ? triggerTimer - delta : 0;
            triggerFlag = false;
            attackFlag = false;

            //武器身回归
            Position = Position.MoveToward(Vector2.Zero, 35 * delta);
            if (fireInterval == 0)
            {
                RotationDegrees = 0;
            }
            else
            {
                RotationDegrees = Mathf.Lerp(0, fireAngle, attackTimer / fireInterval);
            }
        }
    }

    /// <summary>
    /// 扳机函数, 调用即视为扣动扳机
    /// </summary>
    public void Trigger()
    {
        //是否第一帧按下
        var justDown = downTimer == 0;
        //是否能发射
        var flag = false;
        if (continuousCount <= 0) //不能处于连发状态下
        {
            if (Attribute.ContinuousShoot) //自动射击
            {
                if (triggerTimer > 0)
                {
                    if (continuousShootFlag)
                    {
                        flag = true;
                    }
                }
                else
                {
                    flag = true;
                    if (delayedTime <= 0 && attackTimer <= 0)
                    {
                        continuousShootFlag = true;
                    }
                }
            }
            else //半自动
            {
                if (justDown && triggerTimer <= 0)
                {
                    flag = true;
                }
            }
        }

        if (flag)
        {
            var fireFlag = true; //是否能开火
            if (Reloading) //换弹中
            {
                if (CurrAmmo > 0 && Attribute.AloneReload && Attribute.AloneReloadCanShoot) //立即停止换弹
                {
                    Reloading = false;
                    ReloadTimer = 0;
                }
                else
                {
                    fireFlag = false;
                }
            }
            else if (CurrAmmo <= 0)
            {
                fireFlag = false;
                //子弹不够
                _Reload();
            }

            if (fireFlag)
            {
                if (justDown)
                {
                    //开火前延时
                    delayedTime = Attribute.DelayedTime;
                    //扳机按下间隔
                    triggerTimer = Attribute.TriggerInterval;
                    //连发数量
                    if (!Attribute.ContinuousShoot)
                    {
                        continuousCount = MathUtils.RandRangeInt(Attribute.MinContinuousCount, Attribute.MaxContinuousCount);
                    }
                }
                if (delayedTime <= 0 && attackTimer <= 0)
                {
                    TriggernFire();
                }
                attackFlag = true;
            }

        }
        triggerFlag = true;
    }

    /// <summary>
    /// 刚按下扳机
    /// </summary>
    private void DownTrigger()
    {
        OnDownTrigger();
    }

    /// <summary>
    /// 刚松开扳机
    /// </summary>
    private void UpTriggern()
    {
        continuousShootFlag = false;
        if (delayedTime > 0)
        {
            continuousCount = 0;
        }
        OnUpTrigger();
    }

    /// <summary>
    /// 触发开火
    /// </summary>
    private void TriggernFire()
    {
        continuousCount = continuousCount > 0 ? continuousCount - 1 : 0;

        //减子弹数量
        CurrAmmo--;
        //开火间隙
        fireInterval = 60 / Attribute.StartFiringSpeed;
        //攻击冷却
        attackTimer += fireInterval;

        //触发开火函数
        OnFire();

        //开火发射的子弹数量
        var bulletCount = MathUtils.RandRangeInt(Attribute.MaxFireBulletCount, Attribute.MinFireBulletCount);
        //武器口角度
        var angle = new Vector2(GameConfig.ScatteringDistance, CurrScatteringRange).Angle();

        //创建子弹
        for (int i = 0; i < bulletCount; i++)
        {
            //先算武器口方向
            Rotation = (float)GD.RandRange(-angle, angle);
            //发射子弹
            OnShoot();
        }

        //当前的散射半径
        CurrScatteringRange = Mathf.Min(CurrScatteringRange + Attribute.ScatteringRangeAddValue, Attribute.FinalScatteringRange);
        //武器的旋转角度
        RotationDegrees -= Attribute.UpliftAngle;
        fireAngle = RotationDegrees;
        //武器身位置
        Position = new Vector2(Mathf.Max(-Attribute.MaxBacklash, Position.x - MathUtils.RandRange(Attribute.MinBacklash, Attribute.MaxBacklash)), Position.y);

        if (FireEvent != null)
        {
            FireEvent(this);
        }
    }

    /// <summary>
    /// 返回弹药是否到达上限
    /// </summary>
    public bool IsFullAmmo()
    {
        return CurrAmmo + ResidueAmmo >= Attribute.MaxAmmoCapacity;
    }

    /// <summary>
    /// 返回是否弹药耗尽
    /// </summary>
    public bool IsEmptyAmmo()
    {
        return CurrAmmo + ResidueAmmo == 0;
    }

    /// <summary>
    /// 拾起的弹药数量, 如果到达容量上限, 则返回拾取完毕后剩余的弹药数量
    /// </summary>
    /// <param name="count">弹药数量</param>
    public int PickUpAmmo(int count)
    {
        var num = ResidueAmmo;
        ResidueAmmo = Mathf.Min(ResidueAmmo + count, Attribute.MaxAmmoCapacity - CurrAmmo);
        return count - ResidueAmmo + num;
    }

    /// <summary>
    /// 触发换弹
    /// </summary>
    public void _Reload()
    {
        if (CurrAmmo < Attribute.AmmoCapacity && ResidueAmmo > 0 && !Reloading)
        {
            Reloading = true;
            ReloadTimer = Attribute.ReloadTime;
            OnReload();
        }
    }

    /// <summary>
    /// 换弹计时器时间到, 执行换弹操作
    /// </summary>
    private void ReloadSuccess()
    {
        if (Attribute.AloneReload) //单独装填
        {
            if (ResidueAmmo >= Attribute.AloneReloadCount) //剩余子弹充足
            {
                if (CurrAmmo + Attribute.AloneReloadCount <= Attribute.AmmoCapacity)
                {
                    ResidueAmmo -= Attribute.AloneReloadCount;
                    CurrAmmo += Attribute.AloneReloadCount;
                }
                else //子弹满了
                {
                    var num = Attribute.AmmoCapacity - CurrAmmo;
                    CurrAmmo = Attribute.AmmoCapacity;
                    ResidueAmmo -= num;
                }
            }
            else if (ResidueAmmo != 0) //剩余子弹不足
            {
                if (ResidueAmmo + CurrAmmo <= Attribute.AmmoCapacity)
                {
                    CurrAmmo += ResidueAmmo;
                    ResidueAmmo = 0;
                }
                else //子弹满了
                {
                    var num = Attribute.AmmoCapacity - CurrAmmo;
                    CurrAmmo = Attribute.AmmoCapacity;
                    ResidueAmmo -= num;
                }
            }
            if (ResidueAmmo == 0 || CurrAmmo == Attribute.AmmoCapacity) //换弹结束
            {
                Reloading = false;
                ReloadTimer = 0;
                OnReloadFinish();
            }
            else
            {
                ReloadTimer = Attribute.ReloadTime;
                OnReload();
            }
        }
        else //换弹结束
        {
            if (ResidueAmmo >= Attribute.AmmoCapacity)
            {
                ResidueAmmo -= Attribute.AmmoCapacity - CurrAmmo;
                CurrAmmo = Attribute.AmmoCapacity;
            }
            else
            {
                CurrAmmo = ResidueAmmo;
                ResidueAmmo = 0;
            }
            Reloading = false;
            ReloadTimer = 0;
            OnReloadFinish();
        }
    }

    public CheckInteractiveResult CheckInteractive(Role master)
    {
        var result = new CheckInteractiveResult(this);
        if (Master == null)
        {
            var masterWeapon = master.Holster.ActiveWeapon;
            //查找是否有同类型武器
            var index = master.Holster.FindWeapon(Id);
            if (index != -1) //如果有这个武器
            {
                if (CurrAmmo + ResidueAmmo != 0) //子弹不为空
                {
                    var targetWeapon = master.Holster.GetWeapon(index);
                    if (!targetWeapon.IsFullAmmo()) //背包里面的武器子弹未满
                    {
                        //可以互动拾起弹药
                        result.CanInteractive = true;
                        result.Message = Attribute.Name;
                        result.ShowIcon = "res://resource/sprite/ui/icon/icon_bullet.png";
                        return result;
                    }
                }
            }
            else //没有武器
            {
                if (master.Holster.CanPickupWeapon(this)) //能拾起武器
                {
                    //可以互动, 拾起武器
                    result.CanInteractive = true;
                    result.Message = Attribute.Name;
                    result.ShowIcon = "res://resource/sprite/ui/icon/icon_pickup.png";
                    return result;
                }
                else if (masterWeapon != null && masterWeapon.Attribute.WeightType == Attribute.WeightType) //替换武器
                {
                    //可以互动, 切换武器
                    result.CanInteractive = true;
                    result.Message = Attribute.Name;
                    result.ShowIcon = "res://resource/sprite/ui/icon/icon_replace.png";
                    return result;
                }
            }
        }
        return result;
    }

    public void Interactive(Role master)
    {
        //查找是否有同类型武器
        var index = master.Holster.FindWeapon(Id);
        if (index != -1) //如果有这个武器
        {
            if (CurrAmmo + ResidueAmmo == 0) //没有子弹了
            {
                return;
            }
            var weapon = master.Holster.GetWeapon(index);
            //子弹上限
            var maxCount = Attribute.MaxAmmoCapacity;
            //是否捡到子弹
            var flag = false;
            if (ResidueAmmo > 0 && weapon.CurrAmmo + weapon.ResidueAmmo < maxCount)
            {
                var count = weapon.PickUpAmmo(ResidueAmmo);
                if (count != ResidueAmmo)
                {
                    ResidueAmmo = count;
                    flag = true;
                }
            }
            if (CurrAmmo > 0 && weapon.CurrAmmo + weapon.ResidueAmmo < maxCount)
            {
                var count = weapon.PickUpAmmo(CurrAmmo);
                if (count != CurrAmmo)
                {
                    CurrAmmo = count;
                    flag = true;
                }
            }
            //播放互动效果
            if (flag)
            {
                this.StartThrow<ThrowWeapon>(new Vector2(20, 20), GlobalPosition, 0, 0,
                    MathUtils.RandRangeInt(-20, 20), MathUtils.RandRangeInt(20, 50),
                    MathUtils.RandRangeInt(-180, 180), WeaponSprite);
            }
        }
        else //没有武器
        {
            if (master.Holster.PickupWeapon(this) == -1)
            {
                var slot = master.Holster.SlotList[master.Holster.ActiveIndex];
                if (slot.Type == Attribute.WeightType)
                {
                    var weapon = master.Holster.RmoveWeapon(master.Holster.ActiveIndex);
                    weapon.StartThrowWeapon(master);
                    master.PickUpWeapon(this);
                }
            }
        }
    }

    public Vector2 GetItemPosition()
    {
        return GlobalPosition;
    }

    /// <summary>
    /// 触发落到地面
    /// </summary>
    public void _FallToGround()
    {
        //启用碰撞
        CollisionShape2D.Disabled = false;
    }

    /// <summary>
    /// 触发拾起
    /// </summary>
    public void _PickUpWeapon(Role master)
    {
        Master = master;
        //握把位置
        WeaponSprite.Position = Attribute.HoldPosition;
        //清除泛白效果
        ShaderMaterial sm = WeaponSprite.Material as ShaderMaterial;
        sm.SetShaderParam("schedule", 0);
        //停止动画
        AnimationPlayer.Stop();
        ZIndex = 0;
        //禁用碰撞
        CollisionShape2D.Disabled = true;
        OnPickUp(master);
    }

    /// <summary>
    /// 触发抛出
    /// </summary>a
    public void _ThrowOutWeapon()
    {
        Master = null;
        WeaponSprite.Position = Attribute.CenterPosition;
        AnimationPlayer.Play("Floodlight");
        OnThrowOut();
    }

    /// <summary>
    /// 触发启用武器
    /// </summary>
    public void _Active()
    {
        OnActive();
    }

    /// <summary>
    /// 触发收起武器
    /// </summary>
    public void _Conceal()
    {
        OnConceal();
    }

    /// <summary>
    /// 实例化并返回子弹对象
    /// </summary>
    /// <param name="bulletPack">子弹的预制体</param>
    protected T CreateBullet<T>(PackedScene bulletPack, Vector2 globalPostion, float globalRotation, Node parent = null) where T : Node2D, IBullet
    {
        return (T)CreateBullet(bulletPack, globalPostion, globalRotation, parent);
    }

    /// <summary>
    /// 实例化并返回子弹对象
    /// </summary>
    /// <param name="bulletPack">子弹的预制体</param>
    protected IBullet CreateBullet(PackedScene bulletPack, Vector2 globalPostion, float globalRotation, Node parent = null)
    {
        // 实例化子弹
        Node2D bullet = bulletPack.Instance<Node2D>();
        // 设置坐标
        bullet.GlobalPosition = globalPostion;
        // 旋转角度
        bullet.GlobalRotation = globalRotation;
        if (parent == null)
        {
            RoomManager.Current.SortRoot.AddChild(bullet);
        }
        else
        {
            parent.AddChild(bullet);
        }
        // 调用初始化
        IBullet result = (IBullet)bullet;
        result.Init(TargetCamp, this, null);
        return result;
    }
}