Newer
Older
DungeonShooting / DungeonShooting_Godot / src / game / activity / role / Role.cs
  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 (IsDie)
  421. {
  422. return;
  423. }
  424. if (_rollCoolingTimer > 0)
  425. {
  426. _rollCoolingTimer -= delta;
  427. }
  428.  
  429. if (_meleeAttackTimer > 0)
  430. {
  431. _meleeAttackTimer -= delta;
  432. }
  433. //看向目标
  434. if (LookTarget != null && MountLookTarget)
  435. {
  436. Vector2 pos = LookTarget.GlobalPosition;
  437. //脸的朝向
  438. var gPos = GlobalPosition;
  439. if (pos.X > gPos.X && Face == FaceDirection.Left)
  440. {
  441. Face = FaceDirection.Right;
  442. }
  443. else if (pos.X < gPos.X && Face == FaceDirection.Right)
  444. {
  445. Face = FaceDirection.Left;
  446. }
  447. //枪口跟随目标
  448. MountPoint.SetLookAt(pos);
  449. }
  450. //检查可互动的物体
  451. bool findFlag = false;
  452. for (int i = 0; i < _interactiveItemList.Count; i++)
  453. {
  454. var item = _interactiveItemList[i];
  455. if (item == null || item.IsDestroyed)
  456. {
  457. _interactiveItemList.RemoveAt(i--);
  458. }
  459. else
  460. {
  461. //找到可互动的物体了
  462. if (!findFlag)
  463. {
  464. var result = item.CheckInteractive(this);
  465. var prev = _currentResultData;
  466. _currentResultData = result;
  467. if (result.CanInteractive) //可以互动
  468. {
  469. findFlag = true;
  470. if (InteractiveItem != item) //更改互动物体
  471. {
  472. InteractiveItem = item;
  473. ChangeInteractiveItem(prev, result);
  474. }
  475. else if (result.Type != _currentResultData.Type) //切换状态
  476. {
  477. ChangeInteractiveItem(prev, result);
  478. }
  479. }
  480. }
  481. }
  482. }
  483. //没有可互动的物体
  484. if (!findFlag && InteractiveItem != null)
  485. {
  486. var prev = _currentResultData;
  487. _currentResultData = null;
  488. InteractiveItem = null;
  489. ChangeInteractiveItem(prev, null);
  490. }
  491.  
  492. //无敌状态, 播放闪烁动画
  493. if (Invincible)
  494. {
  495. _flashingInvincibleTimer -= delta;
  496. if (_flashingInvincibleTimer <= 0)
  497. {
  498. _flashingInvincibleTimer = 0.15f;
  499. if (_flashingInvincibleFlag)
  500. {
  501. _flashingInvincibleFlag = false;
  502. SetBlendModulate(new Color(1, 1, 1, 0.7f));
  503. }
  504. else
  505. {
  506. _flashingInvincibleFlag = true;
  507. SetBlendModulate(new Color(1, 1, 1, 0));
  508. }
  509. }
  510.  
  511. _shieldRecoveryTimer = 0;
  512. }
  513. else //恢复护盾
  514. {
  515. if (Shield < MaxShield)
  516. {
  517. _shieldRecoveryTimer += delta;
  518. if (_shieldRecoveryTimer >= RoleState.ShieldRecoveryTime) //时间到, 恢复
  519. {
  520. Shield++;
  521. _shieldRecoveryTimer = 0;
  522. }
  523. }
  524. else
  525. {
  526. _shieldRecoveryTimer = 0;
  527. }
  528. }
  529.  
  530. //被动道具更新
  531. if (BuffPropPack.Count > 0)
  532. {
  533. var buffProps = BuffPropPack.ToArray();
  534. foreach (var prop in buffProps)
  535. {
  536. if (!prop.IsDestroyed)
  537. {
  538. prop.PackProcess(delta);
  539. }
  540. }
  541. }
  542. //主动道具调用更新
  543. var props = ActivePropsPack.ItemSlot;
  544. if (props.Length > 0)
  545. {
  546. props = (ActiveProp[])props.Clone();
  547. foreach (var prop in props)
  548. {
  549. if (prop != null && !prop.IsDestroyed)
  550. {
  551. prop.PackProcess(delta);
  552. }
  553. }
  554. }
  555. }
  556.  
  557. /// <summary>
  558. /// 初始化瞄准辅助线
  559. /// </summary>
  560. public void InitSubLine()
  561. {
  562. if (SubLine != null)
  563. {
  564. return;
  565. }
  566.  
  567. SubLine = AddComponent<SubLine>();
  568. }
  569. /// <summary>
  570. /// 当武器放到后背时调用, 用于设置武器位置和角度
  571. /// </summary>
  572. /// <param name="weapon">武器实例</param>
  573. /// <param name="index">放入武器背包的位置</param>
  574. public virtual void OnPutBackMount(Weapon weapon, int index)
  575. {
  576. if (index < 8)
  577. {
  578. if (index % 2 == 0)
  579. {
  580. weapon.Position = new Vector2(-4, 3);
  581. weapon.RotationDegrees = 90 - (index / 2f) * 20;
  582. weapon.Scale = new Vector2(-1, 1);
  583. }
  584. else
  585. {
  586. weapon.Position = new Vector2(4, 3);
  587. weapon.RotationDegrees = 270 + (index - 1) / 2f * 20;
  588. weapon.Scale = new Vector2(1, 1);
  589. }
  590. }
  591. else
  592. {
  593. weapon.Visible = false;
  594. }
  595. }
  596. protected override void OnAffiliationChange(AffiliationArea prevArea)
  597. {
  598. //身上的武器的所属区域也得跟着变
  599. WeaponPack.ForEach((weapon, i) =>
  600. {
  601. if (AffiliationArea != null)
  602. {
  603. AffiliationArea.InsertItem(weapon);
  604. }
  605. else if (weapon.AffiliationArea != null)
  606. {
  607. weapon.AffiliationArea.RemoveItem(weapon);
  608. }
  609. });
  610. }
  611.  
  612. /// <summary>
  613. /// 是否是满血
  614. /// </summary>
  615. public bool IsHpFull()
  616. {
  617. return Hp >= MaxHp;
  618. }
  619. /// <summary>
  620. /// 获取当前角色的中心点坐标
  621. /// </summary>
  622. public Vector2 GetCenterPosition()
  623. {
  624. return MountPoint.GlobalPosition;
  625. }
  626.  
  627. /// <summary>
  628. /// 使角色看向指定的坐标,
  629. /// 注意, 调用该函数会清空 LookTarget, 因为拥有 LookTarget 时也会每帧更新玩家视野位置
  630. /// </summary>
  631. public void LookTargetPosition(Vector2 pos)
  632. {
  633. LookTarget = null;
  634. if (MountLookTarget)
  635. {
  636. //脸的朝向
  637. var gPos = GlobalPosition;
  638. if (pos.X > gPos.X && Face == FaceDirection.Left)
  639. {
  640. Face = FaceDirection.Right;
  641. }
  642. else if (pos.X < gPos.X && Face == FaceDirection.Right)
  643. {
  644. Face = FaceDirection.Left;
  645. }
  646. //枪口跟随目标
  647. MountPoint.SetLookAt(pos);
  648. }
  649. }
  650. /// <summary>
  651. /// 判断指定坐标是否在角色视野方向
  652. /// </summary>
  653. public bool IsPositionInForward(Vector2 pos)
  654. {
  655. var gps = GlobalPosition;
  656. return (Face == FaceDirection.Left && pos.X <= gps.X) ||
  657. (Face == FaceDirection.Right && pos.X >= gps.X);
  658. }
  659.  
  660. /// <summary>
  661. /// 返回所有武器是否弹药都打光了
  662. /// </summary>
  663. public bool IsAllWeaponTotalAmmoEmpty()
  664. {
  665. foreach (var weapon in WeaponPack.ItemSlot)
  666. {
  667. if (weapon != null && !weapon.IsTotalAmmoEmpty())
  668. {
  669. return false;
  670. }
  671. }
  672.  
  673. return true;
  674. }
  675. //-------------------------------------------------------------------------------------
  676. /// <summary>
  677. /// 拾起一个武器, 返回是否成功拾起, 如果不想立刻切换到该武器, exchange 请传 false
  678. /// </summary>
  679. /// <param name="weapon">武器对象</param>
  680. /// <param name="exchange">是否立即切换到该武器, 默认 true </param>
  681. public bool PickUpWeapon(Weapon weapon, bool exchange = true)
  682. {
  683. if (WeaponPack.PickupItem(weapon, exchange) != -1)
  684. {
  685. //从可互动队列中移除
  686. _interactiveItemList.Remove(weapon);
  687. OnPickUpWeapon(weapon);
  688. return true;
  689. }
  690.  
  691. return false;
  692. }
  693.  
  694. /// <summary>
  695. /// 切换到下一个武器
  696. /// </summary>
  697. public void ExchangeNextWeapon()
  698. {
  699. var weapon = WeaponPack.ActiveItem;
  700. WeaponPack.ExchangeNext();
  701. if (WeaponPack.ActiveItem != weapon)
  702. {
  703. OnExchangeWeapon(WeaponPack.ActiveItem);
  704. }
  705. }
  706.  
  707. /// <summary>
  708. /// 切换到上一个武器
  709. /// </summary>
  710. public void ExchangePrevWeapon()
  711. {
  712. var weapon = WeaponPack.ActiveItem;
  713. WeaponPack.ExchangePrev();
  714. if (WeaponPack.ActiveItem != weapon)
  715. {
  716. OnExchangeWeapon(WeaponPack.ActiveItem);
  717. }
  718. }
  719.  
  720. /// <summary>
  721. /// 扔掉当前使用的武器, 切换到上一个武器
  722. /// </summary>
  723. public void ThrowWeapon()
  724. {
  725. ThrowWeapon(WeaponPack.ActiveIndex);
  726. }
  727.  
  728. /// <summary>
  729. /// 扔掉指定位置的武器
  730. /// </summary>
  731. /// <param name="index">武器在武器背包中的位置</param>
  732. public void ThrowWeapon(int index)
  733. {
  734. var weapon = WeaponPack.GetItem(index);
  735. if (weapon == null)
  736. {
  737. return;
  738. }
  739.  
  740. var temp = weapon.AnimatedSprite.Position;
  741. if (Face == FaceDirection.Left)
  742. {
  743. temp.Y = -temp.Y;
  744. }
  745. //var pos = GlobalPosition + temp.Rotated(weapon.GlobalRotation);
  746. WeaponPack.RemoveItem(index);
  747. //播放抛出效果
  748. weapon.ThrowWeapon(this, GlobalPosition);
  749. }
  750.  
  751. /// <summary>
  752. /// 拾起主动道具, 返回是否成功拾起, 如果不想立刻切换到该道具, exchange 请传 false
  753. /// </summary>
  754. /// <param name="activeProp">主动道具对象</param>
  755. /// <param name="exchange">是否立即切换到该道具, 默认 true </param>
  756. public bool PickUpActiveProp(ActiveProp activeProp, bool exchange = true)
  757. {
  758. if (ActivePropsPack.PickupItem(activeProp, exchange) != -1)
  759. {
  760. //从可互动队列中移除
  761. _interactiveItemList.Remove(activeProp);
  762. OnPickUpActiveProp(activeProp);
  763. return true;
  764. }
  765.  
  766. return false;
  767. }
  768. /// <summary>
  769. /// 切换到下一个武器
  770. /// </summary>
  771. public void ExchangeNextActiveProp()
  772. {
  773. var prop = ActivePropsPack.ActiveItem;
  774. ActivePropsPack.ExchangeNext();
  775. if (prop != ActivePropsPack.ActiveItem)
  776. {
  777. OnExchangeActiveProp(ActivePropsPack.ActiveItem);
  778. }
  779. }
  780.  
  781. /// <summary>
  782. /// 切换到上一个武器
  783. /// </summary>
  784. public void ExchangePrevActiveProp()
  785. {
  786. var prop = ActivePropsPack.ActiveItem;
  787. ActivePropsPack.ExchangePrev();
  788. if (prop != ActivePropsPack.ActiveItem)
  789. {
  790. OnExchangeActiveProp(ActivePropsPack.ActiveItem);
  791. }
  792. }
  793. /// <summary>
  794. /// 扔掉当前使用的道具
  795. /// </summary>
  796. public void ThrowActiveProp()
  797. {
  798. ThrowActiveProp(ActivePropsPack.ActiveIndex);
  799. }
  800. /// <summary>
  801. /// 扔掉指定位置上的主动道具
  802. /// </summary>
  803. public void ThrowActiveProp(int index)
  804. {
  805. var activeProp = ActivePropsPack.GetItem(index);
  806. if (activeProp == null)
  807. {
  808. return;
  809. }
  810.  
  811. ActivePropsPack.RemoveItem(index);
  812. OnRemoveActiveProp(activeProp);
  813. //播放抛出效果
  814. activeProp.ThrowProp(this, GlobalPosition);
  815. }
  816.  
  817. /// <summary>
  818. /// 拾起被动道具, 返回是否成功拾起
  819. /// </summary>
  820. /// <param name="buffProp">被动道具对象</param>
  821. public bool PickUpBuffProp(BuffProp buffProp)
  822. {
  823. if (BuffPropPack.Contains(buffProp))
  824. {
  825. Debug.LogError("被动道具已经在背包中了!");
  826. return false;
  827. }
  828. BuffPropPack.Add(buffProp);
  829. buffProp.Master = this;
  830. OnPickUpBuffProp(buffProp);
  831. buffProp.OnPickUpItem();
  832. return true;
  833. }
  834.  
  835. /// <summary>
  836. /// 扔掉指定的被动道具
  837. /// </summary>
  838. /// <param name="buffProp"></param>
  839. public void ThrowBuffProp(BuffProp buffProp)
  840. {
  841. var index = BuffPropPack.IndexOf(buffProp);
  842. if (index < 0)
  843. {
  844. Debug.LogError("当前道具不在角色背包中!");
  845. return;
  846. }
  847. ThrowBuffProp(index);
  848. }
  849. /// <summary>
  850. /// 扔掉指定位置上的被动道具
  851. /// </summary>
  852. public void ThrowBuffProp(int index)
  853. {
  854. if (index < 0 || index >= BuffPropPack.Count)
  855. {
  856. return;
  857. }
  858.  
  859. var buffProp = BuffPropPack[index];
  860. BuffPropPack.RemoveAt(index);
  861. buffProp.OnRemoveItem();
  862. OnRemoveBuffProp(buffProp);
  863. buffProp.Master = null;
  864. //播放抛出效果
  865. buffProp.ThrowProp(this, GlobalPosition);
  866. }
  867.  
  868. //-------------------------------------------------------------------------------------
  869. /// <summary>
  870. /// 返回是否存在可互动的物体
  871. /// </summary>
  872. public bool HasInteractive()
  873. {
  874. return InteractiveItem != null;
  875. }
  876.  
  877. /// <summary>
  878. /// 触发与碰撞的物体互动, 并返回与其互动的物体
  879. /// </summary>
  880. public ActivityObject TriggerInteractive()
  881. {
  882. if (HasInteractive())
  883. {
  884. var item = InteractiveItem;
  885. item.Interactive(this);
  886. return item;
  887. }
  888.  
  889. return null;
  890. }
  891.  
  892. /// <summary>
  893. /// 触发换弹
  894. /// </summary>
  895. public virtual void Reload()
  896. {
  897. if (WeaponPack.ActiveItem != null)
  898. {
  899. WeaponPack.ActiveItem.Reload();
  900. }
  901. }
  902.  
  903. /// <summary>
  904. /// 触发攻击
  905. /// </summary>
  906. public virtual void Attack()
  907. {
  908. if (!IsMeleeAttack && WeaponPack.ActiveItem != null)
  909. {
  910. WeaponPack.ActiveItem.Trigger(this);
  911. }
  912. }
  913.  
  914. /// <summary>
  915. /// 触发近战攻击
  916. /// </summary>
  917. public virtual void MeleeAttack()
  918. {
  919. if (IsMeleeAttack || _meleeAttackTimer > 0)
  920. {
  921. return;
  922. }
  923.  
  924. if (WeaponPack.ActiveItem != null && WeaponPack.ActiveItem.Attribute.CanMeleeAttack)
  925. {
  926. IsMeleeAttack = true;
  927. _meleeAttackTimer = RoleState.MeleeAttackTime;
  928. MountLookTarget = false;
  929. //WeaponPack.ActiveItem.TriggerMeleeAttack(this);
  930. //播放近战动画
  931. PlayAnimation_MeleeAttack(() =>
  932. {
  933. MountLookTarget = true;
  934. IsMeleeAttack = false;
  935. });
  936. }
  937. }
  938.  
  939. /// <summary>
  940. /// 触发使用道具
  941. /// </summary>
  942. public virtual void UseActiveProp()
  943. {
  944. var activeItem = ActivePropsPack.ActiveItem;
  945. if (activeItem != null)
  946. {
  947. activeItem.Use();
  948. }
  949. }
  950.  
  951. /// <summary>
  952. /// 受到伤害, 如果是在碰撞信号处理函数中调用该函数, 请使用 CallDeferred 来延时调用, 否则很有可能导致报错
  953. /// </summary>
  954. /// <param name="damage">伤害的量</param>
  955. /// <param name="angle">伤害角度(弧度制)</param>
  956. public virtual void Hurt(int damage, float angle)
  957. {
  958. //受伤闪烁, 无敌状态
  959. if (Invincible)
  960. {
  961. return;
  962. }
  963. //计算真正受到的伤害
  964. damage = OnHandlerHurt(damage);
  965. var flag = Shield > 0;
  966. if (flag)
  967. {
  968. Shield -= damage;
  969. }
  970. else
  971. {
  972. damage = RoleState.CalcHurtDamage(damage);
  973. if (damage > 0)
  974. {
  975. Hp -= damage;
  976. }
  977. //播放血液效果
  978. // var packedScene = ResourceManager.Load<PackedScene>(ResourcePath.prefab_effect_Blood_tscn);
  979. // var blood = packedScene.Instance<Blood>();
  980. // blood.GlobalPosition = GlobalPosition;
  981. // blood.Rotation = angle;
  982. // GameApplication.Instance.Node3D.GetRoot().AddChild(blood);
  983. }
  984. OnHit(damage, !flag);
  985.  
  986. //受伤特效
  987. PlayHitAnimation();
  988. //死亡判定
  989. if (Hp <= 0)
  990. {
  991. //死亡
  992. if (!IsDie)
  993. {
  994. IsDie = true;
  995. OnDie();
  996. }
  997. }
  998. }
  999.  
  1000. /// <summary>
  1001. /// 播放无敌状态闪烁动画
  1002. /// </summary>
  1003. /// <param name="time">持续时间</param>
  1004. public void PlayInvincibleFlashing(float time)
  1005. {
  1006. Invincible = true;
  1007. if (_invincibleFlashingId >= 0) //上一个还没结束
  1008. {
  1009. StopCoroutine(_invincibleFlashingId);
  1010. }
  1011.  
  1012. _invincibleFlashingId = StartCoroutine(RunInvincibleFlashing(time));
  1013. }
  1014.  
  1015. /// <summary>
  1016. /// 停止无敌状态闪烁动画
  1017. /// </summary>
  1018. public void StopInvincibleFlashing()
  1019. {
  1020. Invincible = false;
  1021. if (_invincibleFlashingId >= 0)
  1022. {
  1023. StopCoroutine(_invincibleFlashingId);
  1024. _invincibleFlashingId = -1;
  1025. }
  1026. }
  1027.  
  1028. private IEnumerator RunInvincibleFlashing(float time)
  1029. {
  1030. yield return new WaitForSeconds(time);
  1031. _invincibleFlashingId = -1;
  1032. Invincible = false;
  1033. }
  1034.  
  1035. /// <summary>
  1036. /// 设置脸的朝向
  1037. /// </summary>
  1038. private void SetFace(FaceDirection face)
  1039. {
  1040. if (_face != face)
  1041. {
  1042. _face = face;
  1043. if (face == FaceDirection.Right)
  1044. {
  1045. RotationDegrees = 0;
  1046. Scale = _startScale;
  1047. }
  1048. else
  1049. {
  1050. RotationDegrees = 180;
  1051. Scale = new Vector2(_startScale.X, -_startScale.Y);
  1052. }
  1053. }
  1054. }
  1055. /// <summary>
  1056. /// 连接信号: InteractiveArea.BodyEntered
  1057. /// 与物体碰撞
  1058. /// </summary>
  1059. private void _OnPropsEnter(Node2D other)
  1060. {
  1061. if (other is ActivityObject propObject && !propObject.CollisionWithMask(PhysicsLayer.OnHand))
  1062. {
  1063. if (!_interactiveItemList.Contains(propObject))
  1064. {
  1065. _interactiveItemList.Add(propObject);
  1066. }
  1067. }
  1068. }
  1069.  
  1070. /// <summary>
  1071. /// 连接信号: InteractiveArea.BodyExited
  1072. /// 物体离开碰撞区域
  1073. /// </summary>
  1074. private void _OnPropsExit(Node2D other)
  1075. {
  1076. if (other is ActivityObject propObject)
  1077. {
  1078. if (_interactiveItemList.Contains(propObject))
  1079. {
  1080. _interactiveItemList.Remove(propObject);
  1081. }
  1082. if (InteractiveItem == propObject)
  1083. {
  1084. var prev = _currentResultData;
  1085. _currentResultData = null;
  1086. InteractiveItem = null;
  1087. ChangeInteractiveItem(prev, null);
  1088. }
  1089. }
  1090. }
  1091.  
  1092. /// <summary>
  1093. /// 切换当前使用的武器的回调
  1094. /// </summary>
  1095. private void OnChangeActiveItem(Weapon weapon)
  1096. {
  1097. //这里处理近战区域
  1098. if (weapon != null)
  1099. {
  1100. MeleeAttackCollision.Polygon = Utils.CreateSectorPolygon(
  1101. Utils.ConvertAngle(-MeleeAttackAngle / 2f),
  1102. (weapon.GetLocalFirePosition() - weapon.GripPoint.Position).Length() * 1.2f,
  1103. MeleeAttackAngle,
  1104. 6
  1105. );
  1106. MeleeAttackArea.CollisionMask = AttackLayer | PhysicsLayer.Bullet;
  1107. }
  1108. }
  1109.  
  1110. /// <summary>
  1111. /// 近战区域碰到敌人
  1112. /// </summary>
  1113. private void OnMeleeAttackBodyEntered(Node2D body)
  1114. {
  1115. var activeWeapon = WeaponPack.ActiveItem;
  1116. if (activeWeapon == null)
  1117. {
  1118. return;
  1119. }
  1120. var activityObject = body.AsActivityObject();
  1121. if (activityObject != null)
  1122. {
  1123. if (activityObject is Role role) //攻击角色
  1124. {
  1125. var damage = Utils.Random.RandomConfigRange(activeWeapon.Attribute.MeleeAttackHarmRange);
  1126. damage = RoleState.CalcDamage(damage);
  1127. //击退
  1128. if (role is not Player) //目标不是玩家才会触发击退
  1129. {
  1130. var attr = IsAi ? activeWeapon.AiUseAttribute : activeWeapon.PlayerUseAttribute;
  1131. var repel = Utils.Random.RandomConfigRange(attr.MeleeAttackRepelRange);
  1132. var position = role.GlobalPosition - MountPoint.GlobalPosition;
  1133. var v2 = position.Normalized() * repel;
  1134. role.MoveController.AddForce(v2);
  1135. }
  1136. role.CallDeferred(nameof(Hurt), damage, (role.GetCenterPosition() - GlobalPosition).Angle());
  1137. }
  1138. else if (activityObject is Bullet bullet) //攻击子弹
  1139. {
  1140. var attackLayer = bullet.AttackLayer;
  1141. if (CollisionWithMask(attackLayer)) //是攻击玩家的子弹
  1142. {
  1143. bullet.PlayDisappearEffect();
  1144. bullet.Destroy();
  1145. }
  1146. }
  1147. }
  1148. }
  1149.  
  1150. protected override void OnDestroy()
  1151. {
  1152. //销毁道具
  1153. foreach (var buffProp in BuffPropPack)
  1154. {
  1155. buffProp.Destroy();
  1156. }
  1157. BuffPropPack.Clear();
  1158. ActivePropsPack.Destroy();
  1159. WeaponPack.Destroy();
  1160. }
  1161.  
  1162. /// <summary>
  1163. /// 翻滚结束
  1164. /// </summary>
  1165. public void OverRoll()
  1166. {
  1167. _rollCoolingTimer = RoleState.RollTime;
  1168. }
  1169.  
  1170. /// <summary>
  1171. /// 返回当前角色是否是玩家
  1172. /// </summary>
  1173. public bool IsPlayer()
  1174. {
  1175. return this == Player.Current;
  1176. }
  1177. /// <summary>
  1178. /// 是否是玩家的敌人
  1179. /// </summary>
  1180. public bool IsEnemyWithPlayer()
  1181. {
  1182. return CollisionWithMask(Player.Current.EnemyLayer);
  1183. }
  1184.  
  1185. /// <summary>
  1186. /// 将 Role 子节点的旋转角度转换为正常的旋转角度<br/>
  1187. /// 因为 Role 受到 Face 影响, 会出现转向动作, 所以需要该函数来转换旋转角度
  1188. /// </summary>
  1189. /// <param name="rotation">角度, 弧度制</param>
  1190. public float ConvertRotation(float rotation)
  1191. {
  1192. if (Face == FaceDirection.Right)
  1193. {
  1194. return rotation;
  1195. }
  1196.  
  1197. return Mathf.Pi - rotation;
  1198. }
  1199. }