using Godot; /// <summary> /// 高级角色,可以拾起和使用武器 /// </summary> public abstract partial class AdvancedRole : Role { /// <summary> /// 角色携带的武器背包 /// </summary> public Package<Weapon, AdvancedRole> WeaponPack { get; private set; } /// <summary> /// 武器挂载点 /// </summary> [Export, ExportFillNode] public MountRotation MountPoint { get; set; } /// <summary> /// 背后武器的挂载点 /// </summary> [Export, ExportFillNode] public Marker2D BackMountPoint { get; set; } /// <summary> /// 近战碰撞检测区域 /// </summary> [Export, ExportFillNode] public Area2D MeleeAttackArea { get; set; } /// <summary> /// 近战碰撞检测区域的碰撞器 /// </summary> [Export, ExportFillNode] public CollisionPolygon2D MeleeAttackCollision { get; set; } /// <summary> /// 近战攻击时挥动武器的角度 /// </summary> [Export] public float MeleeAttackAngle { get; set; } = 120; /// <summary> /// 武器挂载点是否始终指向目标 /// </summary> public bool MountLookTarget { get; set; } = true; /// <summary> /// 是否处于近战攻击中 /// </summary> public bool IsMeleeAttack { get; private set; } //近战计时器 private float _meleeAttackTimer = 0; /// <summary> /// 当拾起某个武器时调用 /// </summary> protected virtual void OnPickUpWeapon(Weapon weapon) { } /// <summary> /// 当扔掉某个武器时调用 /// </summary> protected virtual void OnThrowWeapon(Weapon weapon) { } /// <summary> /// 当切换到某个武器时调用 /// </summary> protected virtual void OnExchangeWeapon(Weapon weapon) { } public override void OnInit() { base.OnInit(); WeaponPack = AddComponent<Package<Weapon, AdvancedRole>>(); WeaponPack.SetCapacity(4); MountPoint.Master = this; MeleeAttackCollision.Disabled = true; //切换武器回调 WeaponPack.ChangeActiveItemEvent += OnChangeActiveItem; //近战区域进入物体 MeleeAttackArea.BodyEntered += OnMeleeAttackBodyEntered; } protected override void Process(float delta) { if (IsDie) { return; } if (_meleeAttackTimer > 0) { _meleeAttackTimer -= delta; } base.Process(delta); } /// <summary> /// 当武器放到后背时调用, 用于设置武器位置和角度 /// </summary> /// <param name="weapon">武器实例</param> /// <param name="index">放入武器背包的位置</param> public virtual void OnPutBackMount(Weapon weapon, int index) { if (index < 8) { 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; } } protected override void OnAffiliationChange(AffiliationArea prevArea) { //身上的武器的所属区域也得跟着变 WeaponPack.ForEach((weapon, i) => { if (AffiliationArea != null) { AffiliationArea.InsertItem(weapon); } else if (weapon.AffiliationArea != null) { weapon.AffiliationArea.RemoveItem(weapon); } }); } public override void LookTargetPosition(Vector2 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); } } /// <summary> /// 返回所有武器是否弹药都打光了 /// </summary> public bool IsAllWeaponTotalAmmoEmpty() { foreach (var weapon in WeaponPack.ItemSlot) { if (weapon != null && !weapon.IsTotalAmmoEmpty()) { return false; } } return true; } //------------------------------------------------------------------------------------- /// <summary> /// 拾起一个武器, 返回是否成功拾起, 如果不想立刻切换到该武器, exchange 请传 false /// </summary> /// <param name="weapon">武器对象</param> /// <param name="exchange">是否立即切换到该武器, 默认 true </param> public bool PickUpWeapon(Weapon weapon, bool exchange = true) { if (WeaponPack.PickupItem(weapon, exchange) != -1) { //从可互动队列中移除 InteractiveItemList.Remove(weapon); OnPickUpWeapon(weapon); return true; } return false; } /// <summary> /// 切换到下一个武器 /// </summary> public void ExchangeNextWeapon() { var weapon = WeaponPack.ActiveItem; WeaponPack.ExchangeNext(); if (WeaponPack.ActiveItem != weapon) { OnExchangeWeapon(WeaponPack.ActiveItem); } } /// <summary> /// 切换到上一个武器 /// </summary> public void ExchangePrevWeapon() { var weapon = WeaponPack.ActiveItem; WeaponPack.ExchangePrev(); if (WeaponPack.ActiveItem != weapon) { OnExchangeWeapon(WeaponPack.ActiveItem); } } /// <summary> /// 扔掉当前使用的武器, 切换到上一个武器 /// </summary> public void ThrowWeapon() { ThrowWeapon(WeaponPack.ActiveIndex); } /// <summary> /// 扔掉指定位置的武器 /// </summary> /// <param name="index">武器在武器背包中的位置</param> 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); } /// <summary> /// 切换到下一个武器 /// </summary> public void ExchangeNextActiveProp() { var prop = ActivePropsPack.ActiveItem; ActivePropsPack.ExchangeNext(); if (prop != ActivePropsPack.ActiveItem) { OnExchangeActiveProp(ActivePropsPack.ActiveItem); } } /// <summary> /// 切换到上一个武器 /// </summary> public void ExchangePrevActiveProp() { var prop = ActivePropsPack.ActiveItem; ActivePropsPack.ExchangePrev(); if (prop != ActivePropsPack.ActiveItem) { OnExchangeActiveProp(ActivePropsPack.ActiveItem); } } //------------------------------------------------------------------------------------- /// <summary> /// 触发换弹 /// </summary> public virtual void Reload() { if (WeaponPack.ActiveItem != null) { WeaponPack.ActiveItem.Reload(); } } public override void Attack() { if (!IsMeleeAttack && WeaponPack.ActiveItem != null) { WeaponPack.ActiveItem.Trigger(this); } } /// <summary> /// 触发近战攻击 /// </summary> 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); //播放近战动画 PlayAnimation_MeleeAttack(() => { MountLookTarget = true; IsMeleeAttack = false; }); } } /// <summary> /// 切换当前使用的武器的回调 /// </summary> 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; } } /// <summary> /// 近战区域碰到敌人 /// </summary> 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(); } } } } public override float GetFirePointAltitude() { return -MountPoint.Position.Y; } protected override void OnDestroy() { base.OnDestroy(); WeaponPack.Destroy(); } }