Newer
Older
DungeonShooting / DungeonShooting_Godot / src / game / activity / role / Role.cs
@小李xl 小李xl on 29 Oct 2023 32 KB 激光武器,开发中
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using Godot;
  4.  
  5. /// <summary>
  6. /// 角色基类
  7. /// </summary>
  8. public abstract partial class Role : ActivityObject
  9. {
  10. /// <summary>
  11. /// 是否是 Ai
  12. /// </summary>
  13. public bool IsAi { get; protected set; } = false;
  14.  
  15. /// <summary>
  16. /// 角色属性
  17. /// </summary>
  18. public RoleState RoleState { get; } = new RoleState();
  19. /// <summary>
  20. /// 默认攻击对象层级
  21. /// </summary>
  22. public const uint DefaultAttackLayer = PhysicsLayer.Player | PhysicsLayer.Enemy | PhysicsLayer.Wall;
  23. /// <summary>
  24. /// 伤害区域
  25. /// </summary>
  26. [Export, ExportFillNode]
  27. public Area2D HurtArea { get; set; }
  28. /// <summary>
  29. /// 伤害区域碰撞器
  30. /// </summary>
  31. [Export, ExportFillNode]
  32. public CollisionShape2D HurtCollision { get; set; }
  33.  
  34. /// <summary>
  35. /// 所属阵营
  36. /// </summary>
  37. public CampEnum Camp;
  38.  
  39. /// <summary>
  40. /// 攻击目标的碰撞器所属层级, 数据源自于: <see cref="PhysicsLayer"/>
  41. /// </summary>
  42. public uint AttackLayer { get; set; } = PhysicsLayer.Wall;
  43. /// <summary>
  44. /// 该角色敌对目标的碰撞器所属层级, 数据源自于: <see cref="PhysicsLayer"/>
  45. /// </summary>
  46. public uint EnemyLayer { get; set; } = PhysicsLayer.Enemy;
  47.  
  48. /// <summary>
  49. /// 携带的被动道具列表
  50. /// </summary>
  51. public List<BuffProp> BuffPropPack { get; } = new List<BuffProp>();
  52.  
  53. /// <summary>
  54. /// 携带的主动道具包裹
  55. /// </summary>
  56. public Package<ActiveProp> ActivePropsPack { get; private set; }
  57.  
  58. /// <summary>
  59. /// 角色携带的武器背包
  60. /// </summary>
  61. public Package<Weapon> WeaponPack { get; private set; }
  62.  
  63. /// <summary>
  64. /// 武器挂载点
  65. /// </summary>
  66. [Export, ExportFillNode]
  67. public MountRotation MountPoint { get; set; }
  68. /// <summary>
  69. /// 背后武器的挂载点
  70. /// </summary>
  71. [Export, ExportFillNode]
  72. public Marker2D BackMountPoint { get; set; }
  73.  
  74. /// <summary>
  75. /// 互动碰撞区域
  76. /// </summary>
  77. [Export, ExportFillNode]
  78. public Area2D InteractiveArea { get; set; }
  79. /// <summary>
  80. /// 互动区域碰撞器
  81. /// </summary>
  82. [Export, ExportFillNode]
  83. public CollisionShape2D InteractiveCollision { get; set; }
  84. /// <summary>
  85. /// 近战碰撞检测区域
  86. /// </summary>
  87. [Export, ExportFillNode]
  88. public Area2D MeleeAttackArea { get; set; }
  89. /// <summary>
  90. /// 近战碰撞检测区域的碰撞器
  91. /// </summary>
  92. [Export, ExportFillNode]
  93. public CollisionPolygon2D MeleeAttackCollision { get; set; }
  94.  
  95. /// <summary>
  96. /// 近战攻击时挥动武器的角度
  97. /// </summary>
  98. [Export]
  99. public float MeleeAttackAngle { get; set; } = 120;
  100.  
  101. /// <summary>
  102. /// 武器挂载点是否始终指向目标
  103. /// </summary>
  104. public bool MountLookTarget { get; set; } = true;
  105. /// <summary>
  106. /// 脸的朝向
  107. /// </summary>
  108. public FaceDirection Face { get => _face; set => SetFace(value); }
  109. private FaceDirection _face;
  110.  
  111. /// <summary>
  112. /// 是否死亡
  113. /// </summary>
  114. public bool IsDie { get; private set; }
  115. /// <summary>
  116. /// 血量
  117. /// </summary>
  118. public int Hp
  119. {
  120. get => _hp;
  121. set
  122. {
  123. int temp = _hp;
  124. _hp = Mathf.Clamp(value, 0, _maxHp);
  125. if (temp != _hp)
  126. {
  127. OnChangeHp(_hp);
  128. }
  129. }
  130. }
  131. private int _hp = 20;
  132.  
  133. /// <summary>
  134. /// 最大血量
  135. /// </summary>
  136. public int MaxHp
  137. {
  138. get => _maxHp;
  139. set
  140. {
  141. int temp = _maxHp;
  142. _maxHp = value;
  143. //最大血量值改变
  144. if (temp != _maxHp)
  145. {
  146. OnChangeMaxHp(_maxHp);
  147. }
  148. //调整血量
  149. if (Hp > _maxHp)
  150. {
  151. Hp = _maxHp;
  152. }
  153. }
  154. }
  155. private int _maxHp = 20;
  156. /// <summary>
  157. /// 当前护盾值
  158. /// </summary>
  159. public int Shield
  160. {
  161. get => _shield;
  162. set
  163. {
  164. int temp = _shield;
  165. _shield = value;
  166. //护盾被破坏
  167. if (temp > 0 && _shield <= 0 && _maxShield > 0)
  168. {
  169. OnShieldDestroy();
  170. }
  171. //护盾值改变
  172. if (temp != _shield)
  173. {
  174. OnChangeShield(_shield);
  175. }
  176. }
  177. }
  178. private int _shield = 0;
  179.  
  180. /// <summary>
  181. /// 最大护盾值
  182. /// </summary>
  183. public int MaxShield
  184. {
  185. get => _maxShield;
  186. set
  187. {
  188. int temp = _maxShield;
  189. _maxShield = value;
  190. //最大护盾值改变
  191. if (temp != _maxShield)
  192. {
  193. OnChangeMaxShield(_maxShield);
  194. }
  195. //调整护盾值
  196. if (Shield > _maxShield)
  197. {
  198. Shield = _maxShield;
  199. }
  200. }
  201. }
  202. private int _maxShield = 0;
  203.  
  204. /// <summary>
  205. /// 无敌状态
  206. /// </summary>
  207. public bool Invincible
  208. {
  209. get => _invincible;
  210. set
  211. {
  212. if (_invincible != value)
  213. {
  214. if (value) //无敌状态
  215. {
  216. HurtArea.CollisionLayer = _currentLayer;
  217. _flashingInvincibleTimer = -1;
  218. _flashingInvincibleFlag = false;
  219. }
  220. else //正常状态
  221. {
  222. HurtArea.CollisionLayer = _currentLayer;
  223. SetBlendModulate(new Color(1, 1, 1, 1));
  224. }
  225. }
  226.  
  227. _invincible = value;
  228. }
  229. }
  230.  
  231. private bool _invincible = false;
  232. /// <summary>
  233. /// 当前角色所看向的对象, 也就是枪口指向的对象
  234. /// </summary>
  235. public ActivityObject LookTarget { get; set; }
  236. /// <summary>
  237. /// 当前可以互动的物体
  238. /// </summary>
  239. public ActivityObject InteractiveItem { get; private set; }
  240.  
  241. /// <summary>
  242. /// 是否可以翻滚
  243. /// </summary>
  244. public bool CanRoll => _rollCoolingTimer <= 0;
  245.  
  246. /// <summary>
  247. /// 是否处于近战攻击中
  248. /// </summary>
  249. public bool IsMeleeAttack { get; private set; }
  250.  
  251. /// <summary>
  252. /// 瞄准辅助线, 需要手动调用 InitSubLine() 初始化
  253. /// </summary>
  254. public SubLine SubLine { get; private set; }
  255. //翻滚冷却计时器
  256. private float _rollCoolingTimer = 0;
  257. //初始缩放
  258. private Vector2 _startScale;
  259. //所有角色碰撞的物体
  260. private readonly List<ActivityObject> _interactiveItemList = new List<ActivityObject>();
  261. //当前可互动的物体
  262. private CheckInteractiveResult _currentResultData;
  263. private uint _currentLayer;
  264. //闪烁计时器
  265. private float _flashingInvincibleTimer = -1;
  266. //闪烁状态
  267. private bool _flashingInvincibleFlag = false;
  268. //闪烁动画协程id
  269. private long _invincibleFlashingId = -1;
  270. //护盾恢复计时器
  271. private float _shieldRecoveryTimer = 0;
  272. //近战计时器
  273. private float _meleeAttackTimer = 0;
  274.  
  275. /// <summary>
  276. /// 当血量改变时调用
  277. /// </summary>
  278. protected virtual void OnChangeHp(int hp)
  279. {
  280. }
  281.  
  282. /// <summary>
  283. /// 当最大血量改变时调用
  284. /// </summary>
  285. protected virtual void OnChangeMaxHp(int maxHp)
  286. {
  287. }
  288. /// <summary>
  289. /// 护盾值改变时调用
  290. /// </summary>
  291. protected virtual void OnChangeShield(int shield)
  292. {
  293. }
  294.  
  295. /// <summary>
  296. /// 最大护盾值改变时调用
  297. /// </summary>
  298. protected virtual void OnChangeMaxShield(int maxShield)
  299. {
  300. }
  301.  
  302. /// <summary>
  303. /// 当护盾被破坏时调用
  304. /// </summary>
  305. protected virtual void OnShieldDestroy()
  306. {
  307. }
  308.  
  309. /// <summary>
  310. /// 当受伤时调用
  311. /// </summary>
  312. /// <param name="damage">受到的伤害</param>
  313. /// <param name="realHarm">是否受到真实伤害, 如果为false, 则表示该伤害被互动格挡掉了</param>
  314. protected virtual void OnHit(int damage, bool realHarm)
  315. {
  316. }
  317.  
  318. /// <summary>
  319. /// 受到伤害时调用, 用于改变受到的伤害值
  320. /// </summary>
  321. /// <param name="damage">受到的伤害</param>
  322. protected virtual int OnHandlerHurt(int damage)
  323. {
  324. return damage;
  325. }
  326. /// <summary>
  327. /// 当可互动的物体改变时调用, result 参数为 null 表示变为不可互动
  328. /// </summary>
  329. /// <param name="prev">上一个互动的物体</param>
  330. /// <param name="result">当前物体, 检测是否可互动时的返回值</param>
  331. protected virtual void ChangeInteractiveItem(CheckInteractiveResult prev, CheckInteractiveResult result)
  332. {
  333. }
  334.  
  335. /// <summary>
  336. /// 死亡时调用
  337. /// </summary>
  338. protected virtual void OnDie()
  339. {
  340. }
  341.  
  342. /// <summary>
  343. /// 当拾起某个武器时调用
  344. /// </summary>
  345. protected virtual void OnPickUpWeapon(Weapon weapon)
  346. {
  347. }
  348. /// <summary>
  349. /// 当扔掉某个武器时调用
  350. /// </summary>
  351. protected virtual void OnThrowWeapon(Weapon weapon)
  352. {
  353. }
  354.  
  355. /// <summary>
  356. /// 当切换到某个武器时调用
  357. /// </summary>
  358. protected virtual void OnExchangeWeapon(Weapon weapon)
  359. {
  360. }
  361.  
  362. /// <summary>
  363. /// 当拾起某个主动道具时调用
  364. /// </summary>
  365. protected virtual void OnPickUpActiveProp(ActiveProp activeProp)
  366. {
  367. }
  368.  
  369. /// <summary>
  370. /// 当移除某个主动道具时调用
  371. /// </summary>
  372. protected virtual void OnRemoveActiveProp(ActiveProp activeProp)
  373. {
  374. }
  375. /// <summary>
  376. /// 当切换到某个主动道具时调用
  377. /// </summary>
  378. protected virtual void OnExchangeActiveProp(ActiveProp activeProp)
  379. {
  380. }
  381. /// <summary>
  382. /// 当拾起某个被动道具时调用
  383. /// </summary>
  384. protected virtual void OnPickUpBuffProp(BuffProp buffProp)
  385. {
  386. }
  387.  
  388. /// <summary>
  389. /// 当移除某个被动道具时调用
  390. /// </summary>
  391. protected virtual void OnRemoveBuffProp(BuffProp buffProp)
  392. {
  393. }
  394.  
  395. public override void OnInit()
  396. {
  397. ActivePropsPack = AddComponent<Package<ActiveProp>>();
  398. ActivePropsPack.SetCapacity(1);
  399. WeaponPack = AddComponent<Package<Weapon>>();
  400. WeaponPack.SetCapacity(4);
  401.  
  402. _startScale = Scale;
  403. MountPoint.Master = this;
  404. HurtArea.CollisionLayer = CollisionLayer;
  405. HurtArea.CollisionMask = 0;
  406. _currentLayer = HurtArea.CollisionLayer;
  407. Face = FaceDirection.Right;
  408. //连接互动物体信号
  409. InteractiveArea.BodyEntered += _OnPropsEnter;
  410. InteractiveArea.BodyExited += _OnPropsExit;
  411. MeleeAttackCollision.Disabled = true;
  412. //切换武器回调
  413. WeaponPack.ChangeActiveItemEvent += OnChangeActiveItem;
  414. //近战区域进入物体
  415. MeleeAttackArea.BodyEntered += OnMeleeAttackBodyEntered;
  416. }
  417.  
  418. protected override void Process(float delta)
  419. {
  420. if (_rollCoolingTimer > 0)
  421. {
  422. _rollCoolingTimer -= delta;
  423. }
  424.  
  425. if (_meleeAttackTimer > 0)
  426. {
  427. _meleeAttackTimer -= delta;
  428. }
  429. //看向目标
  430. if (LookTarget != null && MountLookTarget)
  431. {
  432. Vector2 pos = LookTarget.GlobalPosition;
  433. //脸的朝向
  434. var gPos = GlobalPosition;
  435. if (pos.X > gPos.X && Face == FaceDirection.Left)
  436. {
  437. Face = FaceDirection.Right;
  438. }
  439. else if (pos.X < gPos.X && Face == FaceDirection.Right)
  440. {
  441. Face = FaceDirection.Left;
  442. }
  443. //枪口跟随目标
  444. MountPoint.SetLookAt(pos);
  445. }
  446. //检查可互动的物体
  447. bool findFlag = false;
  448. for (int i = 0; i < _interactiveItemList.Count; i++)
  449. {
  450. var item = _interactiveItemList[i];
  451. if (item == null || item.IsDestroyed)
  452. {
  453. _interactiveItemList.RemoveAt(i--);
  454. }
  455. else
  456. {
  457. //找到可互动的物体了
  458. if (!findFlag)
  459. {
  460. var result = item.CheckInteractive(this);
  461. var prev = _currentResultData;
  462. _currentResultData = result;
  463. if (result.CanInteractive) //可以互动
  464. {
  465. findFlag = true;
  466. if (InteractiveItem != item) //更改互动物体
  467. {
  468. InteractiveItem = item;
  469. ChangeInteractiveItem(prev, result);
  470. }
  471. else if (result.Type != _currentResultData.Type) //切换状态
  472. {
  473. ChangeInteractiveItem(prev, result);
  474. }
  475. }
  476. }
  477. }
  478. }
  479. //没有可互动的物体
  480. if (!findFlag && InteractiveItem != null)
  481. {
  482. var prev = _currentResultData;
  483. _currentResultData = null;
  484. InteractiveItem = null;
  485. ChangeInteractiveItem(prev, null);
  486. }
  487.  
  488. //无敌状态, 播放闪烁动画
  489. if (Invincible)
  490. {
  491. _flashingInvincibleTimer -= delta;
  492. if (_flashingInvincibleTimer <= 0)
  493. {
  494. _flashingInvincibleTimer = 0.15f;
  495. if (_flashingInvincibleFlag)
  496. {
  497. _flashingInvincibleFlag = false;
  498. SetBlendModulate(new Color(1, 1, 1, 0.7f));
  499. }
  500. else
  501. {
  502. _flashingInvincibleFlag = true;
  503. SetBlendModulate(new Color(1, 1, 1, 0));
  504. }
  505. }
  506.  
  507. _shieldRecoveryTimer = 0;
  508. }
  509. else //恢复护盾
  510. {
  511. if (Shield < MaxShield)
  512. {
  513. _shieldRecoveryTimer += delta;
  514. if (_shieldRecoveryTimer >= RoleState.ShieldRecoveryTime) //时间到, 恢复
  515. {
  516. Shield++;
  517. _shieldRecoveryTimer = 0;
  518. }
  519. }
  520. else
  521. {
  522. _shieldRecoveryTimer = 0;
  523. }
  524. }
  525.  
  526. //被动道具更新
  527. if (BuffPropPack.Count > 0)
  528. {
  529. var buffProps = BuffPropPack.ToArray();
  530. foreach (var prop in buffProps)
  531. {
  532. if (!prop.IsDestroyed)
  533. {
  534. prop.PackProcess(delta);
  535. }
  536. }
  537. }
  538. //主动道具调用更新
  539. var props = (Prop[])ActivePropsPack.ItemSlot.Clone();
  540. foreach (var prop in props)
  541. {
  542. if (prop != null && !prop.IsDestroyed)
  543. {
  544. prop.PackProcess(delta);
  545. }
  546. }
  547. }
  548.  
  549. /// <summary>
  550. /// 初始化瞄准辅助线
  551. /// </summary>
  552. public void InitSubLine()
  553. {
  554. if (SubLine != null)
  555. {
  556. return;
  557. }
  558.  
  559. SubLine = AddComponent<SubLine>();
  560. }
  561. /// <summary>
  562. /// 当武器放到后背时调用, 用于设置武器位置和角度
  563. /// </summary>
  564. /// <param name="weapon">武器实例</param>
  565. /// <param name="index">放入武器背包的位置</param>
  566. public virtual void OnPutBackMount(Weapon weapon, int index)
  567. {
  568. if (index < 8)
  569. {
  570. if (index % 2 == 0)
  571. {
  572. weapon.Position = new Vector2(-4, 3);
  573. weapon.RotationDegrees = 90 - (index / 2f) * 20;
  574. weapon.Scale = new Vector2(-1, 1);
  575. }
  576. else
  577. {
  578. weapon.Position = new Vector2(4, 3);
  579. weapon.RotationDegrees = 270 + (index - 1) / 2f * 20;
  580. weapon.Scale = new Vector2(1, 1);
  581. }
  582. }
  583. else
  584. {
  585. weapon.Visible = false;
  586. }
  587. }
  588. protected override void OnAffiliationChange(AffiliationArea prevArea)
  589. {
  590. //身上的武器的所属区域也得跟着变
  591. WeaponPack.ForEach((weapon, i) =>
  592. {
  593. if (AffiliationArea != null)
  594. {
  595. AffiliationArea.InsertItem(weapon);
  596. }
  597. else if (weapon.AffiliationArea != null)
  598. {
  599. weapon.AffiliationArea.RemoveItem(weapon);
  600. }
  601. });
  602. }
  603.  
  604. /// <summary>
  605. /// 是否是满血
  606. /// </summary>
  607. public bool IsHpFull()
  608. {
  609. return Hp >= MaxHp;
  610. }
  611. /// <summary>
  612. /// 获取当前角色的中心点坐标
  613. /// </summary>
  614. public Vector2 GetCenterPosition()
  615. {
  616. return MountPoint.GlobalPosition;
  617. }
  618.  
  619. /// <summary>
  620. /// 使角色看向指定的坐标,
  621. /// 注意, 调用该函数会清空 LookTarget, 因为拥有 LookTarget 时也会每帧更新玩家视野位置
  622. /// </summary>
  623. public void LookTargetPosition(Vector2 pos)
  624. {
  625. LookTarget = null;
  626. if (MountLookTarget)
  627. {
  628. //脸的朝向
  629. var gPos = GlobalPosition;
  630. if (pos.X > gPos.X && Face == FaceDirection.Left)
  631. {
  632. Face = FaceDirection.Right;
  633. }
  634. else if (pos.X < gPos.X && Face == FaceDirection.Right)
  635. {
  636. Face = FaceDirection.Left;
  637. }
  638. //枪口跟随目标
  639. MountPoint.SetLookAt(pos);
  640. }
  641. }
  642. /// <summary>
  643. /// 判断指定坐标是否在角色视野方向
  644. /// </summary>
  645. public bool IsPositionInForward(Vector2 pos)
  646. {
  647. var gps = GlobalPosition;
  648. return (Face == FaceDirection.Left && pos.X <= gps.X) ||
  649. (Face == FaceDirection.Right && pos.X >= gps.X);
  650. }
  651.  
  652. /// <summary>
  653. /// 返回所有武器是否弹药都打光了
  654. /// </summary>
  655. public bool IsAllWeaponTotalAmmoEmpty()
  656. {
  657. foreach (var weapon in WeaponPack.ItemSlot)
  658. {
  659. if (weapon != null && !weapon.IsTotalAmmoEmpty())
  660. {
  661. return false;
  662. }
  663. }
  664.  
  665. return true;
  666. }
  667. //-------------------------------------------------------------------------------------
  668. /// <summary>
  669. /// 拾起一个武器, 返回是否成功拾起, 如果不想立刻切换到该武器, exchange 请传 false
  670. /// </summary>
  671. /// <param name="weapon">武器对象</param>
  672. /// <param name="exchange">是否立即切换到该武器, 默认 true </param>
  673. public bool PickUpWeapon(Weapon weapon, bool exchange = true)
  674. {
  675. if (WeaponPack.PickupItem(weapon, exchange) != -1)
  676. {
  677. //从可互动队列中移除
  678. _interactiveItemList.Remove(weapon);
  679. OnPickUpWeapon(weapon);
  680. return true;
  681. }
  682.  
  683. return false;
  684. }
  685.  
  686. /// <summary>
  687. /// 切换到下一个武器
  688. /// </summary>
  689. public void ExchangeNextWeapon()
  690. {
  691. var weapon = WeaponPack.ActiveItem;
  692. WeaponPack.ExchangeNext();
  693. if (WeaponPack.ActiveItem != weapon)
  694. {
  695. OnExchangeWeapon(WeaponPack.ActiveItem);
  696. }
  697. }
  698.  
  699. /// <summary>
  700. /// 切换到上一个武器
  701. /// </summary>
  702. public void ExchangePrevWeapon()
  703. {
  704. var weapon = WeaponPack.ActiveItem;
  705. WeaponPack.ExchangePrev();
  706. if (WeaponPack.ActiveItem != weapon)
  707. {
  708. OnExchangeWeapon(WeaponPack.ActiveItem);
  709. }
  710. }
  711.  
  712. /// <summary>
  713. /// 扔掉当前使用的武器, 切换到上一个武器
  714. /// </summary>
  715. public void ThrowWeapon()
  716. {
  717. ThrowWeapon(WeaponPack.ActiveIndex);
  718. }
  719.  
  720. /// <summary>
  721. /// 扔掉指定位置的武器
  722. /// </summary>
  723. /// <param name="index">武器在武器背包中的位置</param>
  724. public void ThrowWeapon(int index)
  725. {
  726. var weapon = WeaponPack.GetItem(index);
  727. if (weapon == null)
  728. {
  729. return;
  730. }
  731.  
  732. var temp = weapon.AnimatedSprite.Position;
  733. if (Face == FaceDirection.Left)
  734. {
  735. temp.Y = -temp.Y;
  736. }
  737. //var pos = GlobalPosition + temp.Rotated(weapon.GlobalRotation);
  738. WeaponPack.RemoveItem(index);
  739. //播放抛出效果
  740. weapon.ThrowWeapon(this, GlobalPosition);
  741. }
  742.  
  743. /// <summary>
  744. /// 拾起主动道具, 返回是否成功拾起, 如果不想立刻切换到该道具, exchange 请传 false
  745. /// </summary>
  746. /// <param name="activeProp">主动道具对象</param>
  747. /// <param name="exchange">是否立即切换到该道具, 默认 true </param>
  748. public bool PickUpActiveProp(ActiveProp activeProp, bool exchange = true)
  749. {
  750. if (ActivePropsPack.PickupItem(activeProp, exchange) != -1)
  751. {
  752. //从可互动队列中移除
  753. _interactiveItemList.Remove(activeProp);
  754. OnPickUpActiveProp(activeProp);
  755. return true;
  756. }
  757.  
  758. return false;
  759. }
  760. /// <summary>
  761. /// 切换到下一个武器
  762. /// </summary>
  763. public void ExchangeNextActiveProp()
  764. {
  765. var prop = ActivePropsPack.ActiveItem;
  766. ActivePropsPack.ExchangeNext();
  767. if (prop != ActivePropsPack.ActiveItem)
  768. {
  769. OnExchangeActiveProp(ActivePropsPack.ActiveItem);
  770. }
  771. }
  772.  
  773. /// <summary>
  774. /// 切换到上一个武器
  775. /// </summary>
  776. public void ExchangePrevActiveProp()
  777. {
  778. var prop = ActivePropsPack.ActiveItem;
  779. ActivePropsPack.ExchangePrev();
  780. if (prop != ActivePropsPack.ActiveItem)
  781. {
  782. OnExchangeActiveProp(ActivePropsPack.ActiveItem);
  783. }
  784. }
  785. /// <summary>
  786. /// 扔掉当前使用的道具
  787. /// </summary>
  788. public void ThrowActiveProp()
  789. {
  790. ThrowActiveProp(ActivePropsPack.ActiveIndex);
  791. }
  792. /// <summary>
  793. /// 扔掉指定位置上的主动道具
  794. /// </summary>
  795. public void ThrowActiveProp(int index)
  796. {
  797. var activeProp = ActivePropsPack.GetItem(index);
  798. if (activeProp == null)
  799. {
  800. return;
  801. }
  802.  
  803. ActivePropsPack.RemoveItem(index);
  804. OnRemoveActiveProp(activeProp);
  805. //播放抛出效果
  806. activeProp.ThrowProp(this, GlobalPosition);
  807. }
  808.  
  809. /// <summary>
  810. /// 拾起被动道具, 返回是否成功拾起
  811. /// </summary>
  812. /// <param name="buffProp">被动道具对象</param>
  813. public bool PickUpBuffProp(BuffProp buffProp)
  814. {
  815. if (BuffPropPack.Contains(buffProp))
  816. {
  817. Debug.LogError("被动道具已经在背包中了!");
  818. return false;
  819. }
  820. BuffPropPack.Add(buffProp);
  821. buffProp.Master = this;
  822. OnPickUpBuffProp(buffProp);
  823. buffProp.OnPickUpItem();
  824. return true;
  825. }
  826.  
  827. /// <summary>
  828. /// 扔掉指定的被动道具
  829. /// </summary>
  830. /// <param name="buffProp"></param>
  831. public void ThrowBuffProp(BuffProp buffProp)
  832. {
  833. var index = BuffPropPack.IndexOf(buffProp);
  834. if (index < 0)
  835. {
  836. Debug.LogError("当前道具不在角色背包中!");
  837. return;
  838. }
  839. ThrowBuffProp(index);
  840. }
  841. /// <summary>
  842. /// 扔掉指定位置上的被动道具
  843. /// </summary>
  844. public void ThrowBuffProp(int index)
  845. {
  846. if (index < 0 || index >= BuffPropPack.Count)
  847. {
  848. return;
  849. }
  850.  
  851. var buffProp = BuffPropPack[index];
  852. BuffPropPack.RemoveAt(index);
  853. buffProp.OnRemoveItem();
  854. OnRemoveBuffProp(buffProp);
  855. buffProp.Master = null;
  856. //播放抛出效果
  857. buffProp.ThrowProp(this, GlobalPosition);
  858. }
  859.  
  860. //-------------------------------------------------------------------------------------
  861. /// <summary>
  862. /// 返回是否存在可互动的物体
  863. /// </summary>
  864. public bool HasInteractive()
  865. {
  866. return InteractiveItem != null;
  867. }
  868.  
  869. /// <summary>
  870. /// 触发与碰撞的物体互动, 并返回与其互动的物体
  871. /// </summary>
  872. public ActivityObject TriggerInteractive()
  873. {
  874. if (HasInteractive())
  875. {
  876. var item = InteractiveItem;
  877. item.Interactive(this);
  878. return item;
  879. }
  880.  
  881. return null;
  882. }
  883.  
  884. /// <summary>
  885. /// 触发换弹
  886. /// </summary>
  887. public virtual void Reload()
  888. {
  889. if (WeaponPack.ActiveItem != null)
  890. {
  891. WeaponPack.ActiveItem.Reload();
  892. }
  893. }
  894.  
  895. /// <summary>
  896. /// 触发攻击
  897. /// </summary>
  898. public virtual void Attack()
  899. {
  900. if (!IsMeleeAttack && WeaponPack.ActiveItem != null)
  901. {
  902. WeaponPack.ActiveItem.Trigger(this);
  903. }
  904. }
  905.  
  906. /// <summary>
  907. /// 触发近战攻击
  908. /// </summary>
  909. public virtual void MeleeAttack()
  910. {
  911. if (IsMeleeAttack || _meleeAttackTimer > 0)
  912. {
  913. return;
  914. }
  915.  
  916. if (WeaponPack.ActiveItem != null && WeaponPack.ActiveItem.Attribute.CanMeleeAttack)
  917. {
  918. IsMeleeAttack = true;
  919. _meleeAttackTimer = RoleState.MeleeAttackTime;
  920. MountLookTarget = false;
  921. //WeaponPack.ActiveItem.TriggerMeleeAttack(this);
  922. //播放近战动画
  923. PlayAnimation_MeleeAttack(() =>
  924. {
  925. MountLookTarget = true;
  926. IsMeleeAttack = false;
  927. });
  928. }
  929. }
  930.  
  931. /// <summary>
  932. /// 触发使用道具
  933. /// </summary>
  934. public virtual void UseActiveProp()
  935. {
  936. var activeItem = ActivePropsPack.ActiveItem;
  937. if (activeItem != null)
  938. {
  939. activeItem.Use();
  940. }
  941. }
  942.  
  943. /// <summary>
  944. /// 受到伤害, 如果是在碰撞信号处理函数中调用该函数, 请使用 CallDeferred 来延时调用, 否则很有可能导致报错
  945. /// </summary>
  946. /// <param name="damage">伤害的量</param>
  947. /// <param name="angle">角度</param>
  948. public virtual void Hurt(int damage, float angle)
  949. {
  950. //受伤闪烁, 无敌状态
  951. if (Invincible)
  952. {
  953. return;
  954. }
  955. //计算真正受到的伤害
  956. damage = OnHandlerHurt(damage);
  957. var flag = Shield > 0;
  958. if (flag)
  959. {
  960. Shield -= damage;
  961. }
  962. else
  963. {
  964. damage = RoleState.CallCalcHurtDamageEvent(damage);
  965. if (damage > 0)
  966. {
  967. Hp -= damage;
  968. }
  969. //播放血液效果
  970. // var packedScene = ResourceManager.Load<PackedScene>(ResourcePath.prefab_effect_Blood_tscn);
  971. // var blood = packedScene.Instance<Blood>();
  972. // blood.GlobalPosition = GlobalPosition;
  973. // blood.Rotation = angle;
  974. // GameApplication.Instance.Node3D.GetRoot().AddChild(blood);
  975. }
  976. OnHit(damage, !flag);
  977.  
  978. //受伤特效
  979. PlayHitAnimation();
  980. //死亡判定
  981. if (Hp <= 0)
  982. {
  983. //死亡
  984. if (!IsDie)
  985. {
  986. IsDie = true;
  987. OnDie();
  988. }
  989. }
  990. }
  991.  
  992. /// <summary>
  993. /// 播放无敌状态闪烁动画
  994. /// </summary>
  995. /// <param name="time">持续时间</param>
  996. public void PlayInvincibleFlashing(float time)
  997. {
  998. Invincible = true;
  999. if (_invincibleFlashingId >= 0) //上一个还没结束
  1000. {
  1001. StopCoroutine(_invincibleFlashingId);
  1002. }
  1003.  
  1004. _invincibleFlashingId = StartCoroutine(RunInvincibleFlashing(time));
  1005. }
  1006.  
  1007. /// <summary>
  1008. /// 停止无敌状态闪烁动画
  1009. /// </summary>
  1010. public void StopInvincibleFlashing()
  1011. {
  1012. Invincible = false;
  1013. if (_invincibleFlashingId >= 0)
  1014. {
  1015. StopCoroutine(_invincibleFlashingId);
  1016. _invincibleFlashingId = -1;
  1017. }
  1018. }
  1019.  
  1020. private IEnumerator RunInvincibleFlashing(float time)
  1021. {
  1022. yield return new WaitForSeconds(time);
  1023. _invincibleFlashingId = -1;
  1024. Invincible = false;
  1025. }
  1026.  
  1027. /// <summary>
  1028. /// 设置脸的朝向
  1029. /// </summary>
  1030. private void SetFace(FaceDirection face)
  1031. {
  1032. if (_face != face)
  1033. {
  1034. _face = face;
  1035. if (face == FaceDirection.Right)
  1036. {
  1037. RotationDegrees = 0;
  1038. Scale = _startScale;
  1039. }
  1040. else
  1041. {
  1042. RotationDegrees = 180;
  1043. Scale = new Vector2(_startScale.X, -_startScale.Y);
  1044. }
  1045. }
  1046. }
  1047. /// <summary>
  1048. /// 连接信号: InteractiveArea.BodyEntered
  1049. /// 与物体碰撞
  1050. /// </summary>
  1051. private void _OnPropsEnter(Node2D other)
  1052. {
  1053. if (other is ActivityObject propObject && !propObject.CollisionWithMask(PhysicsLayer.OnHand))
  1054. {
  1055. if (!_interactiveItemList.Contains(propObject))
  1056. {
  1057. _interactiveItemList.Add(propObject);
  1058. }
  1059. }
  1060. }
  1061.  
  1062. /// <summary>
  1063. /// 连接信号: InteractiveArea.BodyExited
  1064. /// 物体离开碰撞区域
  1065. /// </summary>
  1066. private void _OnPropsExit(Node2D other)
  1067. {
  1068. if (other is ActivityObject propObject)
  1069. {
  1070. if (_interactiveItemList.Contains(propObject))
  1071. {
  1072. _interactiveItemList.Remove(propObject);
  1073. }
  1074. if (InteractiveItem == propObject)
  1075. {
  1076. var prev = _currentResultData;
  1077. _currentResultData = null;
  1078. InteractiveItem = null;
  1079. ChangeInteractiveItem(prev, null);
  1080. }
  1081. }
  1082. }
  1083.  
  1084. /// <summary>
  1085. /// 切换当前使用的武器的回调
  1086. /// </summary>
  1087. private void OnChangeActiveItem(Weapon weapon)
  1088. {
  1089. //这里处理近战区域
  1090. if (weapon != null)
  1091. {
  1092. MeleeAttackCollision.Polygon = Utils.CreateSectorPolygon(
  1093. Utils.ConvertAngle(-MeleeAttackAngle / 2f),
  1094. (weapon.GetLocalFirePosition() - weapon.GripPoint.Position).Length() * 1.2f,
  1095. MeleeAttackAngle,
  1096. 6
  1097. );
  1098. MeleeAttackArea.CollisionMask = AttackLayer | PhysicsLayer.Bullet;
  1099. }
  1100. }
  1101.  
  1102. /// <summary>
  1103. /// 近战区域碰到敌人
  1104. /// </summary>
  1105. private void OnMeleeAttackBodyEntered(Node2D body)
  1106. {
  1107. var activeWeapon = WeaponPack.ActiveItem;
  1108. if (activeWeapon == null)
  1109. {
  1110. return;
  1111. }
  1112. var activityObject = body.AsActivityObject();
  1113. if (activityObject != null)
  1114. {
  1115. if (activityObject is Role role) //攻击角色
  1116. {
  1117. var damage = Utils.Random.RandomConfigRange(activeWeapon.Attribute.MeleeAttackHarmRange);
  1118. damage = RoleState.CallCalcDamageEvent(damage);
  1119. //击退
  1120. if (role is not Player) //目标不是玩家才会触发击退
  1121. {
  1122. var attr = IsAi ? activeWeapon.AiUseAttribute : activeWeapon.PlayerUseAttribute;
  1123. var repel = Utils.Random.RandomConfigRange(attr.MeleeAttackRepelRnage);
  1124. var position = role.GlobalPosition - MountPoint.GlobalPosition;
  1125. var v2 = position.Normalized() * repel;
  1126. role.MoveController.AddForce(v2, repel * 2);
  1127. }
  1128. role.CallDeferred(nameof(Hurt), damage, (role.GetCenterPosition() - GlobalPosition).Angle());
  1129. }
  1130. else if (activityObject is Bullet bullet) //攻击子弹
  1131. {
  1132. var attackLayer = bullet.AttackLayer;
  1133. if (CollisionWithMask(attackLayer)) //是攻击玩家的子弹
  1134. {
  1135. bullet.PlayDisappearEffect();
  1136. bullet.Destroy();
  1137. }
  1138. }
  1139. }
  1140. }
  1141.  
  1142. protected override void OnDestroy()
  1143. {
  1144. //销毁道具
  1145. foreach (var buffProp in BuffPropPack)
  1146. {
  1147. buffProp.Destroy();
  1148. }
  1149. BuffPropPack.Clear();
  1150. ActivePropsPack.Destroy();
  1151. WeaponPack.Destroy();
  1152. }
  1153.  
  1154. /// <summary>
  1155. /// 翻滚结束
  1156. /// </summary>
  1157. public void OverRoll()
  1158. {
  1159. _rollCoolingTimer = RoleState.RollTime;
  1160. }
  1161.  
  1162. /// <summary>
  1163. /// 返回当前角色是否是玩家
  1164. /// </summary>
  1165. public bool IsPlayer()
  1166. {
  1167. return this == Player.Current;
  1168. }
  1169. /// <summary>
  1170. /// 是否是玩家的敌人
  1171. /// </summary>
  1172. public bool IsEnemyWithPlayer()
  1173. {
  1174. return CollisionWithMask(Player.Current.EnemyLayer);
  1175. }
  1176.  
  1177. /// <summary>
  1178. /// 将 Role 子节点的旋转角度转换为正常的旋转角度<br/>
  1179. /// 因为 Role 受到 Face 影响, 会出现转向动作, 所以需要该函数来转换旋转角度
  1180. /// </summary>
  1181. /// <param name="rotation">角度, 弧度制</param>
  1182. public float ConvertRotation(float rotation)
  1183. {
  1184. if (Face == FaceDirection.Right)
  1185. {
  1186. return rotation;
  1187. }
  1188.  
  1189. return Mathf.Pi - rotation;
  1190. }
  1191. }