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