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; } }