diff --git a/DungeonShooting_Godot/src/framework/activity/ActivityObject.cs b/DungeonShooting_Godot/src/framework/activity/ActivityObject.cs index b4bfe60..eef5518 100644 --- a/DungeonShooting_Godot/src/framework/activity/ActivityObject.cs +++ b/DungeonShooting_Godot/src/framework/activity/ActivityObject.cs @@ -1735,6 +1735,6 @@ /// public Vector2 GetCenterPosition() { - return AnimatedSprite.Position + Position; + return AnimatedSprite.GlobalPosition + AnimatedSprite.Offset; } } \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/activity/role/AdvancedRole.cs b/DungeonShooting_Godot/src/game/activity/role/AdvancedRole.cs index c1350c1..ceaad7e 100644 --- a/DungeonShooting_Godot/src/game/activity/role/AdvancedRole.cs +++ b/DungeonShooting_Godot/src/game/activity/role/AdvancedRole.cs @@ -326,7 +326,7 @@ //WeaponPack.ActiveItem.TriggerMeleeAttack(this); //播放近战动画 - PlayAnimation_MeleeAttack(() => + this.PlayAnimation_MeleeAttack(() => { MountLookTarget = true; IsMeleeAttack = false; @@ -399,6 +399,21 @@ return -MountPoint.Position.Y; } + public override float GetAttackRotation() + { + return MountPoint.RealRotation; + } + + public override Vector2 GetMountPosition() + { + return MountPoint.GlobalPosition; + } + + public override Node2D GetMountNode() + { + return MountPoint; + } + protected override void OnDestroy() { base.OnDestroy(); diff --git a/DungeonShooting_Godot/src/game/activity/role/AdvancedRole_Animation.cs b/DungeonShooting_Godot/src/game/activity/role/AdvancedRole_Animation.cs deleted file mode 100644 index e829a1f..0000000 --- a/DungeonShooting_Godot/src/game/activity/role/AdvancedRole_Animation.cs +++ /dev/null @@ -1,78 +0,0 @@ - -using System; -using Godot; -using Vector2 = Godot.Vector2; - -public partial class AdvancedRole -{ - /// - /// 播放近战攻击动画 - /// - public virtual void PlayAnimation_MeleeAttack(Action finish) - { - var r = MountPoint.RotationDegrees; - //var gp = MountPoint.GlobalPosition; - var p1 = MountPoint.Position; - var p2 = p1 + new Vector2(6, 0).Rotated(Mathf.DegToRad(r - MeleeAttackAngle / 2f)); - var p3 = p1 + new Vector2(6, 0).Rotated(Mathf.DegToRad(r + MeleeAttackAngle / 2f)); - - var tween = CreateTween(); - tween.SetParallel(); - - tween.TweenProperty(MountPoint, "rotation_degrees", r - MeleeAttackAngle / 2f, 0.1); - tween.TweenProperty(MountPoint, "position", p2, 0.1); - tween.TweenProperty(MountPoint, "position", p2, 0.1); - tween.Chain(); - - tween.TweenCallback(Callable.From(() => - { - MountPoint.RotationDegrees = r + MeleeAttackAngle / 2f; - MountPoint.Position = p3; - //重新计算武器阴影位置 - var activeItem = WeaponPack.ActiveItem; - activeItem.CalcShadowTransform(); - //创建屏幕抖动 - if (Face == FaceDirection.Right) - { - //GameCamera.Main.DirectionalShake(Vector2.FromAngle(Mathf.DegToRad(r - 90)) * 5); - GameCamera.Main.DirectionalShake(Vector2.FromAngle(Mathf.DegToRad(r - 180)) * 6); - } - else - { - //GameCamera.Main.DirectionalShake(Vector2.FromAngle(Mathf.DegToRad(270 - r)) * 5); - GameCamera.Main.DirectionalShake(Vector2.FromAngle(Mathf.DegToRad(-r)) * 6); - } - //播放特效 - var effect = ObjectManager.GetPoolItem(ResourcePath.prefab_effect_weapon_MeleeAttack1_tscn); - var sprite = (Node2D)effect; - var localFirePosition = activeItem.GetLocalFirePosition() - activeItem.GripPoint.Position; - localFirePosition *= 0.9f; - sprite.Position = p1 + localFirePosition.Rotated(Mathf.DegToRad(r)); - sprite.RotationDegrees = r; - AddChild(sprite); - effect.PlayEffect(); - - //启用近战碰撞区域 - MeleeAttackCollision.Disabled = false; - })); - tween.Chain(); - - tween.TweenInterval(0.1f); - tween.Chain(); - - tween.TweenCallback(Callable.From(() => - { - //关闭近战碰撞区域 - MeleeAttackCollision.Disabled = true; - })); - tween.TweenProperty(MountPoint, "rotation_degrees", r, 0.2); - tween.TweenProperty(MountPoint, "position", p1, 0.2); - tween.Chain(); - - tween.TweenCallback(Callable.From(() => - { - finish(); - })); - tween.Play(); - } -} \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/activity/role/Role.cs b/DungeonShooting_Godot/src/game/activity/role/Role.cs index fcea8b0..a63cc35 100644 --- a/DungeonShooting_Godot/src/game/activity/role/Role.cs +++ b/DungeonShooting_Godot/src/game/activity/role/Role.cs @@ -737,7 +737,15 @@ // blood.Rotation = angle; // GameApplication.Instance.Node3D.GetRoot().AddChild(blood); } - OnHit(target, damage, angle, !flag); + + if (target == null || target.IsDestroyed) + { + OnHit(null, damage, angle, !flag); + } + else + { + OnHit(target, damage, angle, !flag); + } //受伤特效 PlayHitAnimation(); @@ -886,6 +894,30 @@ { return -AnimatedSprite.Position.Y; } + + /// + /// 获取攻击标角度, 弧度制 + /// + public virtual float GetAttackRotation() + { + return GetCenterPosition().AngleTo(LookPosition); + } + + /// + /// 获取物品挂载点 + /// + public virtual Vector2 GetMountPosition() + { + return AnimatedSprite.Position; + } + + /// + /// 获取物品挂载节点对象 + /// + public virtual Node2D GetMountNode() + { + return AnimatedSprite; + } protected override void OnDestroy() { diff --git a/DungeonShooting_Godot/src/game/activity/role/RoleAnimation.cs b/DungeonShooting_Godot/src/game/activity/role/RoleAnimation.cs new file mode 100644 index 0000000..227fb3f --- /dev/null +++ b/DungeonShooting_Godot/src/game/activity/role/RoleAnimation.cs @@ -0,0 +1,149 @@ + +using System; +using Godot; +using Vector2 = Godot.Vector2; + +public static class RoleAnimation +{ + /// + /// 播放近战攻击动画 + /// + public static void PlayAnimation_MeleeAttack(this AdvancedRole role, Action finish) + { + var r = role.MountPoint.RotationDegrees; + //var gp = MountPoint.GlobalPosition; + var p1 = role.MountPoint.Position; + var p2 = p1 + new Vector2(6, 0).Rotated(Mathf.DegToRad(r - role.MeleeAttackAngle / 2f)); + var p3 = p1 + new Vector2(6, 0).Rotated(Mathf.DegToRad(r + role.MeleeAttackAngle / 2f)); + + var tween = role.CreateTween(); + tween.SetParallel(); + + tween.TweenProperty(role.MountPoint, "rotation_degrees", r - role.MeleeAttackAngle / 2f, 0.1); + tween.TweenProperty(role.MountPoint, "position", p2, 0.1); + tween.TweenProperty(role.MountPoint, "position", p2, 0.1); + tween.Chain(); + + tween.TweenCallback(Callable.From(() => + { + role.MountPoint.RotationDegrees = r + role.MeleeAttackAngle / 2f; + role.MountPoint.Position = p3; + //重新计算武器阴影位置 + var activeItem = role.WeaponPack.ActiveItem; + activeItem.CalcShadowTransform(); + //创建屏幕抖动 + if (role.Face == FaceDirection.Right) + { + //GameCamera.Main.DirectionalShake(Vector2.FromAngle(Mathf.DegToRad(r - 90)) * 5); + GameCamera.Main.DirectionalShake(Vector2.FromAngle(Mathf.DegToRad(r - 180)) * 6); + } + else + { + //GameCamera.Main.DirectionalShake(Vector2.FromAngle(Mathf.DegToRad(270 - r)) * 5); + GameCamera.Main.DirectionalShake(Vector2.FromAngle(Mathf.DegToRad(-r)) * 6); + } + //播放特效 + var effect = ObjectManager.GetPoolItem(ResourcePath.prefab_effect_weapon_MeleeAttack1_tscn); + var sprite = (Node2D)effect; + var localFirePosition = activeItem.GetLocalFirePosition() - activeItem.GripPoint.Position; + localFirePosition *= 0.9f; + sprite.Position = p1 + localFirePosition.Rotated(Mathf.DegToRad(r)); + sprite.RotationDegrees = r; + role.AddChild(sprite); + effect.PlayEffect(); + + //启用近战碰撞区域 + role.MeleeAttackCollision.Disabled = false; + })); + tween.Chain(); + + tween.TweenInterval(0.1f); + tween.Chain(); + + tween.TweenCallback(Callable.From(() => + { + //关闭近战碰撞区域 + role.MeleeAttackCollision.Disabled = true; + })); + tween.TweenProperty(role.MountPoint, "rotation_degrees", r, 0.2); + tween.TweenProperty(role.MountPoint, "position", p1, 0.2); + tween.Chain(); + + tween.TweenCallback(Callable.From(() => + { + finish(); + })); + tween.Play(); + } + + /// + /// 播放近战攻击动画 + /// + public static void PlayAnimation_MeleeAttack(this AdvancedEnemy role, Action finish) + { + var r = role.MountPoint.RotationDegrees; + //var gp = MountPoint.GlobalPosition; + var p1 = role.MountPoint.Position; + var p2 = p1 + new Vector2(6, 0).Rotated(Mathf.DegToRad(r - role.MeleeAttackAngle / 2f)); + var p3 = p1 + new Vector2(6, 0).Rotated(Mathf.DegToRad(r + role.MeleeAttackAngle / 2f)); + + var tween = role.CreateTween(); + tween.SetParallel(); + + tween.TweenProperty(role.MountPoint, "rotation_degrees", r - role.MeleeAttackAngle / 2f, 0.1); + tween.TweenProperty(role.MountPoint, "position", p2, 0.1); + tween.TweenProperty(role.MountPoint, "position", p2, 0.1); + tween.Chain(); + + tween.TweenCallback(Callable.From(() => + { + role.MountPoint.RotationDegrees = r + role.MeleeAttackAngle / 2f; + role.MountPoint.Position = p3; + //重新计算武器阴影位置 + var activeItem = role.WeaponPack.ActiveItem; + activeItem.CalcShadowTransform(); + //创建屏幕抖动 + if (role.Face == FaceDirection.Right) + { + //GameCamera.Main.DirectionalShake(Vector2.FromAngle(Mathf.DegToRad(r - 90)) * 5); + GameCamera.Main.DirectionalShake(Vector2.FromAngle(Mathf.DegToRad(r - 180)) * 6); + } + else + { + //GameCamera.Main.DirectionalShake(Vector2.FromAngle(Mathf.DegToRad(270 - r)) * 5); + GameCamera.Main.DirectionalShake(Vector2.FromAngle(Mathf.DegToRad(-r)) * 6); + } + //播放特效 + var effect = ObjectManager.GetPoolItem(ResourcePath.prefab_effect_weapon_MeleeAttack1_tscn); + var sprite = (Node2D)effect; + var localFirePosition = activeItem.GetLocalFirePosition() - activeItem.GripPoint.Position; + localFirePosition *= 0.9f; + sprite.Position = p1 + localFirePosition.Rotated(Mathf.DegToRad(r)); + sprite.RotationDegrees = r; + role.AddChild(sprite); + effect.PlayEffect(); + + //启用近战碰撞区域 + role.MeleeAttackCollision.Disabled = false; + })); + tween.Chain(); + + tween.TweenInterval(0.1f); + tween.Chain(); + + tween.TweenCallback(Callable.From(() => + { + //关闭近战碰撞区域 + role.MeleeAttackCollision.Disabled = true; + })); + tween.TweenProperty(role.MountPoint, "rotation_degrees", r, 0.2); + tween.TweenProperty(role.MountPoint, "position", p1, 0.2); + tween.Chain(); + + tween.TweenCallback(Callable.From(() => + { + finish(); + })); + tween.Play(); + } +} \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/AdvancedEnemy.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/AdvancedEnemy.cs index b2d184d..75f7900 100644 --- a/DungeonShooting_Godot/src/game/activity/role/enemy/AdvancedEnemy.cs +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/AdvancedEnemy.cs @@ -14,66 +14,93 @@ using System; using AdvancedState; using Godot; +using NnormalState; +using AiAstonishedState = AdvancedState.AiAstonishedState; +using AiAttackState = AdvancedState.AiAttackState; +using AiFollowUpState = AdvancedState.AiFollowUpState; +using AiLeaveForState = AdvancedState.AiLeaveForState; +using AiNormalState = AdvancedState.AiNormalState; +using AiNotifyState = AdvancedState.AiNotifyState; +using AiSurroundState = AdvancedState.AiSurroundState; +using AiTailAfterState = AdvancedState.AiTailAfterState; /// /// 高级敌人,可以携带武器 /// [Tool] -public partial class AdvancedEnemy : AdvancedRole +public partial class AdvancedEnemy : Enemy { /// - /// 目标是否在视野内 + /// 角色携带的武器背包 /// - public bool TargetInView { get; set; } = true; + public Package WeaponPack { get; private set; } /// - /// 敌人身上的状态机控制器 - /// - public StateController StateController { get; private set; } - - /// - /// 视野半径, 单位像素, 发现玩家后改视野范围可以穿墙 - /// - public float ViewRange { get; set; } = 250; - - /// - /// 发现玩家后的视野半径 - /// - public float TailAfterViewRange { get; set; } = 400; - - /// - /// 背后的视野半径, 单位像素 - /// - public float BackViewRange { get; set; } = 50; - - /// - /// 视野检测射线, 朝玩家打射线, 检测是否碰到墙 + /// 武器挂载点 /// [Export, ExportFillNode] - public RayCast2D ViewRay { get; private set; } - - /// - /// 导航代理 - /// - [Export, ExportFillNode] - public NavigationAgent2D NavigationAgent2D { get; private set; } - - /// - /// 导航代理中点 - /// - [Export, ExportFillNode] - public Marker2D NavigationPoint { get; private set; } + public MountRotation MountPoint { get; set; } /// - /// 当前敌人所看向的对象, 也就是枪口指向的对象 + /// 近战碰撞检测区域 /// - public ActivityObject LookTarget { get; set; } + [Export, ExportFillNode] + public Area2D MeleeAttackArea { get; set; } /// - /// 锁定目标已经走过的时间 + /// 近战碰撞检测区域的碰撞器 /// - public float LockTargetTime { get; set; } = 0; + [Export, ExportFillNode] + public CollisionPolygon2D MeleeAttackCollision { get; set; } + /// + /// 近战攻击时挥动武器的角度 + /// + [Export] + public float MeleeAttackAngle { get; set; } = 120; + + /// + /// 武器挂载点是否始终指向目标 + /// + public bool MountLookTarget { get; set; } = true; + + /// + /// 背后武器的挂载点 + /// + [Export, ExportFillNode] + public Marker2D BackMountPoint { get; set; } + + /// + /// 是否处于近战攻击中 + /// + public bool IsMeleeAttack { get; private set; } + + //近战计时器 + private float _meleeAttackTimer = 0; + + + /// + /// 当拾起某个武器时调用 + /// + protected virtual void OnPickUpWeapon(Weapon weapon) + { + } + + /// + /// 当扔掉某个武器时调用 + /// + protected virtual void OnThrowWeapon(Weapon weapon) + { + } + + /// + /// 当切换到某个武器时调用 + /// + protected virtual void OnExchangeWeapon(Weapon weapon) + { + } + + public override void OnInit() { base.OnInit(); @@ -180,15 +207,32 @@ //拾起武器操作 EnemyPickUpWeapon(); } - - protected override void OnHit(ActivityObject target, int damage, float angle, bool realHarm) + + /// + /// 当武器放到后背时调用, 用于设置武器位置和角度 + /// + /// 武器实例 + /// 放入武器背包的位置 + public virtual void OnPutBackMount(Weapon weapon, int index) { - //受到伤害 - var state = StateController.CurrState; - if (state == AIAdvancedStateEnum.AiNormal || state == AIAdvancedStateEnum.AiLeaveFor) //|| state == AiStateEnum.AiProbe + if (index < 8) { - LookTarget = target; - StateController.ChangeState(AIAdvancedStateEnum.AiTailAfter); + if (index % 2 == 0) + { + weapon.Position = new Vector2(-4, 3); + weapon.RotationDegrees = 90 - (index / 2f) * 20; + weapon.Scale = new Vector2(-1, 1); + } + else + { + weapon.Position = new Vector2(4, 3); + weapon.RotationDegrees = 270 + (index - 1) / 2f * 20; + weapon.Scale = new Vector2(1, 1); + } + } + else + { + weapon.Visible = false; } } @@ -297,60 +341,7 @@ return 0; } - - /// - /// 返回目标点是否在视野范围内 - /// - public bool IsInViewRange(Vector2 target) - { - var isForward = IsPositionInForward(target); - if (isForward) - { - if (GlobalPosition.DistanceSquaredTo(target) <= ViewRange * ViewRange) //没有超出视野半径 - { - return true; - } - } - - return false; - } - - /// - /// 返回目标点是否在跟随状态下的视野半径内 - /// - public bool IsInTailAfterViewRange(Vector2 target) - { - var isForward = IsPositionInForward(target); - if (isForward) - { - if (GlobalPosition.DistanceSquaredTo(target) <= TailAfterViewRange * TailAfterViewRange) //没有超出视野半径 - { - return true; - } - } - - return false; - } - - /// - /// 调用视野检测, 如果被墙壁和其它物体遮挡, 则返回true - /// - public bool TestViewRayCast(Vector2 target) - { - ViewRay.Enabled = true; - ViewRay.TargetPosition = ViewRay.ToLocal(target); - ViewRay.ForceRaycastUpdate(); - return ViewRay.IsColliding(); - } - - /// - /// 调用视野检测完毕后, 需要调用 TestViewRayCastOver() 来关闭视野检测射线 - /// - public void TestViewRayCastOver() - { - ViewRay.Enabled = false; - } - + /// /// AI 拾起武器操作 /// @@ -358,7 +349,7 @@ { //这几个状态不需要主动拾起武器操作 var state = StateController.CurrState; - if (state == AIAdvancedStateEnum.AiNormal) + if (state == AINormalStateEnum.AiNormal) { return; } @@ -410,7 +401,7 @@ /// /// 获取锁定目标的剩余时间 /// - public float GetLockRemainderTime() + public override float GetLockRemainderTime() { var weapon = WeaponPack.ActiveItem; if (weapon == null) @@ -420,48 +411,263 @@ return weapon.Attribute.AiAttackAttr.LockingTime - LockTargetTime; } - /// - /// 强制设置锁定目标时间 - /// - public void SetLockTargetTime(float time) - { - LockTargetTime = time; - } - public override void LookTargetPosition(Vector2 pos) { LookTarget = null; - base.LookTargetPosition(pos); + LookPosition = pos; + if (MountLookTarget) + { + //脸的朝向 + var gPos = Position; + if (pos.X > gPos.X && Face == FaceDirection.Left) + { + Face = FaceDirection.Right; + } + else if (pos.X < gPos.X && Face == FaceDirection.Right) + { + Face = FaceDirection.Left; + } + //枪口跟随目标 + MountPoint.SetLookAt(pos); + } } /// - /// 执行移动操作 + /// 返回所有武器是否弹药都打光了 /// - public void DoMove() + public bool IsAllWeaponTotalAmmoEmpty() { - AnimatedSprite.Play(AnimatorNames.Run); - //计算移动 - var nextPos = NavigationAgent2D.GetNextPathPosition(); - BasisVelocity = (nextPos - Position - NavigationPoint.Position).Normalized() * RoleState.MoveSpeed; + foreach (var weapon in WeaponPack.ItemSlot) + { + if (weapon != null && !weapon.IsTotalAmmoEmpty()) + { + return false; + } + } + + return true; + } + + public override float GetAttackRotation() + { + return MountPoint.RealRotation; + } + + public override Vector2 GetMountPosition() + { + return MountPoint.GlobalPosition; + } + + public override Node2D GetMountNode() + { + return MountPoint; } /// - /// 执行站立操作 + /// 拾起一个武器, 返回是否成功拾起, 如果不想立刻切换到该武器, exchange 请传 false /// - public void DoIdle() + /// 武器对象 + /// 是否立即切换到该武器, 默认 true + public bool PickUpWeapon(Weapon weapon, bool exchange = true) { - AnimatedSprite.Play(AnimatorNames.Idle); - BasisVelocity = Vector2.Zero; + if (WeaponPack.PickupItem(weapon, exchange) != -1) + { + //从可互动队列中移除 + InteractiveItemList.Remove(weapon); + OnPickUpWeapon(weapon); + return true; + } + + return false; + } + + /// + /// 切换到下一个武器 + /// + public void ExchangeNextWeapon() + { + var weapon = WeaponPack.ActiveItem; + WeaponPack.ExchangeNext(); + if (WeaponPack.ActiveItem != weapon) + { + OnExchangeWeapon(WeaponPack.ActiveItem); + } + } + + /// + /// 切换到上一个武器 + /// + public void ExchangePrevWeapon() + { + var weapon = WeaponPack.ActiveItem; + WeaponPack.ExchangePrev(); + if (WeaponPack.ActiveItem != weapon) + { + OnExchangeWeapon(WeaponPack.ActiveItem); + } + } + + /// + /// 扔掉当前使用的武器, 切换到上一个武器 + /// + public void ThrowWeapon() + { + ThrowWeapon(WeaponPack.ActiveIndex); + } + + /// + /// 扔掉指定位置的武器 + /// + /// 武器在武器背包中的位置 + public void ThrowWeapon(int index) + { + var weapon = WeaponPack.GetItem(index); + if (weapon == null) + { + return; + } + + var temp = weapon.AnimatedSprite.Position; + if (Face == FaceDirection.Left) + { + temp.Y = -temp.Y; + } + //var pos = GlobalPosition + temp.Rotated(weapon.GlobalRotation); + WeaponPack.RemoveItem(index); + //播放抛出效果 + weapon.ThrowWeapon(this, GlobalPosition); } /// - /// 更新房间中标记的目标位置 + /// 切换到下一个武器 /// - public void UpdateMarkTargetPosition() + public void ExchangeNextActiveProp() { - if (LookTarget != null) + var prop = ActivePropsPack.ActiveItem; + ActivePropsPack.ExchangeNext(); + if (prop != ActivePropsPack.ActiveItem) { - AffiliationArea.RoomInfo.MarkTargetPosition[LookTarget.Id] = LookTarget.Position; + OnExchangeActiveProp(ActivePropsPack.ActiveItem); + } + } + + /// + /// 切换到上一个武器 + /// + public void ExchangePrevActiveProp() + { + var prop = ActivePropsPack.ActiveItem; + ActivePropsPack.ExchangePrev(); + if (prop != ActivePropsPack.ActiveItem) + { + OnExchangeActiveProp(ActivePropsPack.ActiveItem); + } + } + + //------------------------------------------------------------------------------------- + + /// + /// 触发换弹 + /// + public virtual void Reload() + { + if (WeaponPack.ActiveItem != null) + { + WeaponPack.ActiveItem.Reload(); + } + } + + public override void Attack() + { + if (!IsMeleeAttack && WeaponPack.ActiveItem != null) + { + WeaponPack.ActiveItem.Trigger(this); + } + } + + /// + /// 触发近战攻击 + /// + public virtual void MeleeAttack() + { + if (IsMeleeAttack || _meleeAttackTimer > 0) + { + return; + } + + if (WeaponPack.ActiveItem != null && WeaponPack.ActiveItem.Attribute.CanMeleeAttack) + { + IsMeleeAttack = true; + _meleeAttackTimer = RoleState.MeleeAttackTime; + MountLookTarget = false; + + //WeaponPack.ActiveItem.TriggerMeleeAttack(this); + //播放近战动画 + this.PlayAnimation_MeleeAttack(() => + { + MountLookTarget = true; + IsMeleeAttack = false; + }); + } + } + + /// + /// 切换当前使用的武器的回调 + /// + private void OnChangeActiveItem(Weapon weapon) + { + //这里处理近战区域 + if (weapon != null) + { + MeleeAttackCollision.Polygon = Utils.CreateSectorPolygon( + Utils.ConvertAngle(-MeleeAttackAngle / 2f), + (weapon.GetLocalFirePosition() - weapon.GripPoint.Position).Length() * 1.2f, + MeleeAttackAngle, + 6 + ); + MeleeAttackArea.CollisionMask = AttackLayer | PhysicsLayer.Bullet; + } + } + + /// + /// 近战区域碰到敌人 + /// + private void OnMeleeAttackBodyEntered(Node2D body) + { + var activeWeapon = WeaponPack.ActiveItem; + if (activeWeapon == null) + { + return; + } + var activityObject = body.AsActivityObject(); + if (activityObject != null) + { + if (activityObject is AdvancedRole role) //攻击角色 + { + var damage = Utils.Random.RandomConfigRange(activeWeapon.Attribute.MeleeAttackHarmRange); + damage = RoleState.CalcDamage(damage); + + //击退 + if (role is not Player) //目标不是玩家才会触发击退 + { + var attr = IsAi ? activeWeapon.AiUseAttribute : activeWeapon.PlayerUseAttribute; + var repel = Utils.Random.RandomConfigRange(attr.MeleeAttackRepelRange); + var position = role.GlobalPosition - MountPoint.GlobalPosition; + var v2 = position.Normalized() * repel; + role.MoveController.AddForce(v2); + } + + role.CallDeferred(nameof(Hurt), this, damage, (role.GetCenterPosition() - GlobalPosition).Angle()); + } + else if (activityObject is Bullet bullet) //攻击子弹 + { + var attackLayer = bullet.AttackLayer; + if (CollisionWithMask(attackLayer)) //是攻击玩家的子弹 + { + bullet.PlayDisappearEffect(); + bullet.Destroy(); + } + } } } } diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/Enemy.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/Enemy.cs index 1513d7c..e711869 100644 --- a/DungeonShooting_Godot/src/game/activity/role/enemy/Enemy.cs +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/Enemy.cs @@ -75,12 +75,15 @@ public ActivityObject LookTarget { get; set; } /// + /// 锁定目标已经走过的时间 + /// + public float LockTargetTime { get; set; } = 0; + + /// /// Ai 攻击属性 /// public ExcelConfig.AiAttackAttr AttackAttribute { get; private set; } - //锁定目标时间 - private float _lockTargetTime = 0; //攻击冷却计时器 private float _attackTimer = 0; @@ -171,11 +174,11 @@ var currState = StateController.CurrState; if (currState == AINormalStateEnum.AiAttack && _attackTimer <= 0) //必须在可以开火时记录时间 { - _lockTargetTime += delta; + LockTargetTime += delta; } else { - _lockTargetTime = 0; + LockTargetTime = 0; } } @@ -183,14 +186,11 @@ { //受到伤害 var state = StateController.CurrState; - // if (state == AINormalStateEnum.AiNormal) - // { - // StateController.ChangeState(AINormalStateEnum.AiLeaveFor, target); - // } - // else if (state == AINormalStateEnum.AiLeaveFor) - // { - // - // } + if (target != null && state == AINormalStateEnum.AiNormal || state == AINormalStateEnum.AiLeaveFor) //|| state == AiStateEnum.AiProbe + { + LookTarget = target; + StateController.ChangeState(AINormalStateEnum.AiTailAfter); + } } protected override void OnDie() @@ -268,29 +268,13 @@ { ViewRay.Enabled = false; } - - /// - /// 获取锁定目标的时间 - /// - public float GetLockTime() - { - return _lockTargetTime; - } /// /// 获取锁定目标的剩余时间 /// - public float GetLockRemainderTime() + public virtual float GetLockRemainderTime() { - return AttackAttribute.LockingTime - _lockTargetTime; - } - - /// - /// 强制设置锁定目标时间 - /// - public void SetLockTargetTime(float time) - { - _lockTargetTime = time; + return AttackAttribute.LockingTime - LockTargetTime; } /// @@ -321,6 +305,17 @@ } /// + /// 更新房间中标记的目标位置 + /// + public void UpdateMarkTargetPosition() + { + if (LookTarget != null) + { + AffiliationArea.RoomInfo.MarkTargetPosition[LookTarget.Id] = LookTarget.Position; + } + } + + /// /// 执行移动操作 /// public void DoMove() diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AINormalStateEnum.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AINormalStateEnum.cs index 35e56dd..f731d72 100644 --- a/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AINormalStateEnum.cs +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AINormalStateEnum.cs @@ -11,7 +11,7 @@ /// AiNotify, /// - /// 发现目标, 惊讶状态 + /// 惊讶状态 /// AiAstonished, /// @@ -31,7 +31,11 @@ /// AiSurround, /// - /// 攻击状态 + /// Ai 寻找弹药 + /// + AiFindAmmo, + /// + /// Ai攻击 /// AiAttack, } \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/activity/weapon/Weapon.cs b/DungeonShooting_Godot/src/game/activity/weapon/Weapon.cs index 9a2fd78..e39f771 100644 --- a/DungeonShooting_Godot/src/game/activity/weapon/Weapon.cs +++ b/DungeonShooting_Godot/src/game/activity/weapon/Weapon.cs @@ -6,7 +6,7 @@ /// /// 武器的基类 /// -public abstract partial class Weapon : ActivityObject, IPackageItem +public abstract partial class Weapon : ActivityObject, IPackageItem { /// /// 武器使用的属性数据, 该属性会根据是否是玩家使用武器, 如果是Ai使用武器, 则会返回 AiUseAttribute 的属性对象 @@ -32,7 +32,7 @@ /// public CampEnum TargetCamp { get; set; } - public AdvancedRole Master { get; set; } + public Role Master { get; set; } public int PackageIndex { get; set; } = -1; @@ -127,7 +127,19 @@ /// /// 返回是否真正使用该武器 /// - public bool IsActive => Master != null && Master.WeaponPack.ActiveItem == this; + public bool IsActive + { + get + { + if (Master == null) + return false; + else if (Master is AdvancedRole advancedRole) + return advancedRole.WeaponPack.ActiveItem == this; + else if (Master is AdvancedEnemy advancedEnemy) + return advancedEnemy.WeaponPack.ActiveItem == this; + return false; + } + } /// /// 动画播放器 @@ -148,7 +160,7 @@ /// /// 上一次触发改武器开火的角色, 可能为 null /// - public AdvancedRole TriggerRole { get; private set; } + public Role TriggerRole { get; private set; } /// /// 上一次触发改武器开火的触发开火攻击的层级, 数据源自于: @@ -397,7 +409,7 @@ /// 当武器被拾起时调用 /// /// 拾起该武器的角色 - protected virtual void OnPickUp(AdvancedRole master) + protected virtual void OnPickUp(Role master) { } @@ -405,7 +417,7 @@ /// 当武器从武器背包中移除时调用 /// /// 移除该武器的角色 - protected virtual void OnRemove(AdvancedRole master) + protected virtual void OnRemove(Role master) { } @@ -452,7 +464,7 @@ //这把武器被扔在地上, 或者当前武器没有被使用 //if (Master == null || Master.WeaponPack.ActiveItem != this) - if ((Master != null && Master.WeaponPack.ActiveItem != this) || !_triggerRoleFlag) //在背上, 或者被扔出去了 + if (!IsActive || !_triggerRoleFlag) //在背上, 或者被扔出去了 { //_triggerTimer _triggerTimer = _triggerTimer > 0 ? _triggerTimer - delta : 0; @@ -695,7 +707,7 @@ /// 扳机函数, 调用即视为按下扳机 /// /// 按下扳机的角色, 如果传 null, 则视为走火 - public void Trigger(AdvancedRole triggerRole) + public void Trigger(Role triggerRole) { //不能触发扳机 if (!NoMasterCanTrigger && Master == null) return; @@ -1041,7 +1053,7 @@ if (Master != null) { bulletCount = Master.RoleState.CalcBulletCount(bulletCount); - fireRotation += Master.MountPoint.RealRotation; + fireRotation += Master.GetAttackRotation(); } else { @@ -1105,7 +1117,7 @@ /// /// 根据触扳机的角色对象判断该角色使用的武器数据 /// - public ExcelConfig.WeaponBase GetUseAttribute(AdvancedRole triggerRole) + public ExcelConfig.WeaponBase GetUseAttribute(Role triggerRole) { if (triggerRole == null || !triggerRole.IsAi) { @@ -1734,14 +1746,14 @@ /// public float GetRealGlobalRotation() { - return Mathf.DegToRad(Master.MountPoint.RealRotationDegrees) + Rotation; + return Master.GetAttackRotation() + Rotation; } /// /// 触发扔掉武器时抛出的效果, 并不会管武器是否在武器背包中 /// /// 触发扔掉该武器的的角色 - public void ThrowWeapon(AdvancedRole master) + public void ThrowWeapon(Role master) { ThrowWeapon(master, master.GlobalPosition); } @@ -1751,7 +1763,7 @@ /// /// 触发扔掉该武器的的角色 /// 投抛起始位置 - public void ThrowWeapon(AdvancedRole master, Vector2 startPosition) + public void ThrowWeapon(Role master, Vector2 startPosition) { //阴影偏移 ShadowOffset = new Vector2(0, 2); @@ -1761,11 +1773,29 @@ Scale *= new Vector2(1, -1); } - var rotation = master.MountPoint.GlobalRotation; + float rotation; + float startHeight; + if (master is AdvancedRole advancedRole) + { + var mountPoint = advancedRole.MountPoint; + rotation = mountPoint.GlobalRotation; + startHeight = -mountPoint.Position.Y; + } + else if (master is AdvancedEnemy advancedEnemy) + { + var mountPoint = advancedEnemy.MountPoint; + rotation = mountPoint.GlobalRotation; + startHeight = -mountPoint.Position.Y; + } + else + { + rotation = master.AnimatedSprite.GlobalRotation; + startHeight = -GetCenterPosition().Y; + } + GlobalRotation = rotation; startPosition -= GripPoint.Position.Rotated(rotation); - var startHeight = -master.MountPoint.Position.Y; var velocity = new Vector2(20, 0).Rotated(rotation); var yf = Utils.Random.RandomRangeInt(50, 70); Throw(startPosition, startHeight, yf, velocity, 0); @@ -1799,7 +1829,19 @@ { //调整阴影 //ShadowOffset = new Vector2(0, Master.GlobalPosition.Y - GlobalPosition.Y); - ShadowOffset = new Vector2(0, -Master.MountPoint.Position.Y); + if (Master is AdvancedRole advancedRole) + { + ShadowOffset = new Vector2(0, -advancedRole.MountPoint.Position.Y); + } + else if (Master is AdvancedEnemy advancedEnemy) + { + ShadowOffset = new Vector2(0, -advancedEnemy.MountPoint.Position.Y); + } + else + { + ShadowOffset = new Vector2(0, -Master.GetCenterPosition().Y); + } + //枪口默认抬起角度 RotationDegrees = -Attribute.DefaultAngle; ShowShadowSprite(); @@ -1863,16 +1905,33 @@ public void OnActiveItem() { - //更改父节点 - var parent = GetParentOrNull(); - if (parent == null) + if (Master is AdvancedRole advancedRole) { - Master.MountPoint.AddChild(this); + //更改父节点 + var parent = GetParentOrNull(); + if (parent == null) + { + advancedRole.MountPoint.AddChild(this); + } + else if (parent != advancedRole.MountPoint) + { + parent.RemoveChild(this); + advancedRole.MountPoint.AddChild(this); + } } - else if (parent != Master.MountPoint) + else if (Master is AdvancedEnemy advancedEnemy) { - parent.RemoveChild(this); - Master.MountPoint.AddChild(this); + //更改父节点 + var parent = GetParentOrNull(); + if (parent == null) + { + advancedEnemy.MountPoint.AddChild(this); + } + else if (parent != advancedEnemy.MountPoint) + { + parent.RemoveChild(this); + advancedEnemy.MountPoint.AddChild(this); + } } Position = Vector2.Zero; @@ -1884,19 +1943,41 @@ public void OnConcealItem() { - var tempParent = GetParentOrNull(); - if (tempParent != null) + if (Master is AdvancedRole advancedRole) { - tempParent.RemoveChild(this); - Master.BackMountPoint.AddChild(this); - Master.OnPutBackMount(this, PackageIndex); - Conceal(); + var tempParent = GetParentOrNull(); + if (tempParent != null) + { + tempParent.RemoveChild(this); + advancedRole.BackMountPoint.AddChild(this); + advancedRole.OnPutBackMount(this, PackageIndex); + Conceal(); + } } + else if (Master is AdvancedEnemy advancedEnemy) + { + var tempParent = GetParentOrNull(); + if (tempParent != null) + { + tempParent.RemoveChild(this); + advancedEnemy.BackMountPoint.AddChild(this); + advancedEnemy.OnPutBackMount(this, PackageIndex); + Conceal(); + } + } + } public void OnOverflowItem() { - Master.ThrowWeapon(PackageIndex); + if (Master is AdvancedRole advancedRole) + { + advancedRole.ThrowWeapon(PackageIndex); + } + else if (Master is AdvancedEnemy advancedEnemy) + { + advancedEnemy.ThrowWeapon(PackageIndex); + } } /// @@ -1925,12 +2006,13 @@ AiAttackEnum flag; if (IsTotalAmmoEmpty()) //当前武器弹药打空 { + var enemy = (AdvancedEnemy)Master; //切换到有子弹的武器 - var index = Master.WeaponPack.FindIndex((we, i) => !we.IsTotalAmmoEmpty()); + var index = enemy.WeaponPack.FindIndex((we, i) => !we.IsTotalAmmoEmpty()); if (index != -1) { flag = AiAttackEnum.ExchangeWeapon; - Master.WeaponPack.ExchangeByIndex(index); + enemy.WeaponPack.ExchangeByIndex(index); } else //所有子弹打光 { @@ -1977,7 +2059,7 @@ enemy.Attack(); if (_attackFlag) { - enemy.SetLockTargetTime(0); + enemy.LockTargetTime = 0; } } else @@ -1988,7 +2070,7 @@ enemy.Attack(); if (_attackFlag) { - enemy.SetLockTargetTime(0); + enemy.LockTargetTime = 0; } } else //单发 @@ -1997,7 +2079,7 @@ enemy.Attack(); if (_attackFlag) { - enemy.SetLockTargetTime(0); + enemy.LockTargetTime = 0; } } } diff --git a/DungeonShooting_Godot/src/game/activity/weapon/knife/Knife.cs b/DungeonShooting_Godot/src/game/activity/weapon/knife/Knife.cs index b5db0d6..d32b28d 100644 --- a/DungeonShooting_Godot/src/game/activity/weapon/knife/Knife.cs +++ b/DungeonShooting_Godot/src/game/activity/weapon/knife/Knife.cs @@ -50,7 +50,7 @@ if (IsActive) { //让碰撞节点与武器挂载节点位置保持一致, 而不跟着武器走 - _hitArea.GlobalPosition = Master.MountPoint.GlobalPosition; + _hitArea.GlobalPosition = Master.GetMountPosition(); } } @@ -85,8 +85,8 @@ SpecialEffectManager.Play( Master, ResourcePath.resource_spriteFrames_effect_KnifeHit1_tres, "default", - Master.MountPoint.Position, - Master.MountPoint.Rotation + Mathf.DegToRad(Attribute.UpliftAngle + 60), + Master.GetMountPosition(), + Master.GetAttackRotation() + Mathf.DegToRad(Attribute.UpliftAngle + 60), AnimatedSprite.Scale, new Vector2(17, 4), 1 ); @@ -95,7 +95,7 @@ if (Master == Player.Current) { - var r = Master.MountPoint.RotationDegrees; + var r = Mathf.RadToDeg(Master.GetAttackRotation()); //创建屏幕抖动 if (Master.Face == FaceDirection.Right) { @@ -148,7 +148,7 @@ Vector2 position; if (TriggerRole != null) { - position = role.GlobalPosition - TriggerRole.MountPoint.GlobalPosition; + position = role.GlobalPosition - TriggerRole.GetMountPosition(); } else { diff --git a/DungeonShooting_Godot/src/game/room/DungeonManager.cs b/DungeonShooting_Godot/src/game/room/DungeonManager.cs index 3cf058f..ab4560a 100644 --- a/DungeonShooting_Godot/src/game/room/DungeonManager.cs +++ b/DungeonShooting_Godot/src/game/room/DungeonManager.cs @@ -566,23 +566,9 @@ //不与玩家处于同一个房间 if (!enemy.IsDestroyed && enemy.AffiliationArea != playerAffiliationArea) { - if (enemy is Enemy e) + if (enemy.StateController.CurrState != AINormalStateEnum.AiNormal) { - if (e.StateController.CurrState != AINormalStateEnum.AiNormal) - { - e.StateController.ChangeState(AINormalStateEnum.AiNormal); - } - } - else if (enemy is AdvancedEnemy ae) - { - if (ae.StateController.CurrState != AIAdvancedStateEnum.AiNormal) - { - ae.StateController.ChangeState(AIAdvancedStateEnum.AiNormal); - } - } - else - { - throw new Exception("World.Enemy_InstanceList 混入了非 Enemy 和 AdvancedEnemy 类型的对象!"); + enemy.StateController.ChangeState(AINormalStateEnum.AiNormal); } } } diff --git a/DungeonShooting_Godot/src/game/room/World.cs b/DungeonShooting_Godot/src/game/room/World.cs index 1ed2eab..1285582 100644 --- a/DungeonShooting_Godot/src/game/room/World.cs +++ b/DungeonShooting_Godot/src/game/room/World.cs @@ -63,7 +63,7 @@ /// /// 记录所有存活的敌人 /// - public List Enemy_InstanceList { get; } = new List(); + public List Enemy_InstanceList { get; } = new List(); private bool _pause = false; private List _coroutineList;