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