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 || IsDie)
  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. //禁用状态机控制器
  836. var stateController = GetComponent(typeof(IStateController));
  837. if (stateController != null)
  838. {
  839. stateController.Enable = false;
  840. }
  841.  
  842. //播放死亡动画
  843. if (AnimationPlayer.HasAnimation(AnimatorNames.Die))
  844. {
  845. StartCoroutine(DoDieWithAnimationPlayer());
  846. }
  847. else if (AnimatedSprite.SpriteFrames.HasAnimation(AnimatorNames.Die))
  848. {
  849. StartCoroutine(DoDieWithAnimatedSprite());
  850. }
  851. else
  852. {
  853. DoDieHandler();
  854. }
  855. }
  856. }
  857. }
  858.  
  859. private IEnumerator DoDieWithAnimationPlayer()
  860. {
  861. AnimationPlayer.Play(AnimatorNames.Die);
  862. yield return ToSignal(AnimationPlayer, AnimationMixer.SignalName.AnimationFinished);
  863. DoDieHandler();
  864. }
  865. private IEnumerator DoDieWithAnimatedSprite()
  866. {
  867. AnimatedSprite.Play(AnimatorNames.Die);
  868. yield return ToSignal(AnimatedSprite, AnimatedSprite2D.SignalName.AnimationFinished);
  869. DoDieHandler();
  870. }
  871.  
  872. //死亡逻辑
  873. private void DoDieHandler()
  874. {
  875. OnDie();
  876. //死亡事件
  877. World.OnRoleDie(this);
  878. }
  879.  
  880. /// <summary>
  881. /// 播放无敌状态闪烁动画
  882. /// </summary>
  883. /// <param name="time">持续时间</param>
  884. public void PlayInvincibleFlashing(float time)
  885. {
  886. Invincible = true;
  887. if (_invincibleFlashingId >= 0) //上一个还没结束
  888. {
  889. StopCoroutine(_invincibleFlashingId);
  890. }
  891.  
  892. _invincibleFlashingId = StartCoroutine(RunInvincibleFlashing(time));
  893. }
  894.  
  895. /// <summary>
  896. /// 停止无敌状态闪烁动画
  897. /// </summary>
  898. public void StopInvincibleFlashing()
  899. {
  900. Invincible = false;
  901. if (_invincibleFlashingId >= 0)
  902. {
  903. StopCoroutine(_invincibleFlashingId);
  904. _invincibleFlashingId = -1;
  905. }
  906. }
  907.  
  908. private IEnumerator RunInvincibleFlashing(float time)
  909. {
  910. yield return new WaitForSeconds(time);
  911. _invincibleFlashingId = -1;
  912. Invincible = false;
  913. }
  914.  
  915. /// <summary>
  916. /// 设置脸的朝向
  917. /// </summary>
  918. private void _SetFace(FaceDirection face)
  919. {
  920. if (_face != face)
  921. {
  922. _face = face;
  923. if (face == FaceDirection.Right)
  924. {
  925. RotationDegrees = 0;
  926. Scale = _startScale;
  927. }
  928. else
  929. {
  930. RotationDegrees = 180;
  931. Scale = new Vector2(_startScale.X, -_startScale.Y);
  932. }
  933. }
  934. }
  935. /// <summary>
  936. /// 连接信号: InteractiveArea.BodyEntered
  937. /// 与物体碰撞
  938. /// </summary>
  939. private void _OnPropsEnter(Node2D other)
  940. {
  941. if (other is ActivityObject propObject && !propObject.CollisionWithMask(PhysicsLayer.OnHand))
  942. {
  943. if (!InteractiveItemList.Contains(propObject))
  944. {
  945. InteractiveItemList.Add(propObject);
  946. }
  947. }
  948. }
  949.  
  950. /// <summary>
  951. /// 连接信号: InteractiveArea.BodyExited
  952. /// 物体离开碰撞区域
  953. /// </summary>
  954. private void _OnPropsExit(Node2D other)
  955. {
  956. if (other is ActivityObject propObject)
  957. {
  958. if (InteractiveItemList.Contains(propObject))
  959. {
  960. InteractiveItemList.Remove(propObject);
  961. }
  962. if (InteractiveItem == propObject)
  963. {
  964. var prev = _currentResultData;
  965. _currentResultData = null;
  966. InteractiveItem = null;
  967. ChangeInteractiveItem(prev, null);
  968. }
  969. }
  970. }
  971. /// <summary>
  972. /// 返回当前角色是否是玩家
  973. /// </summary>
  974. public bool IsPlayer()
  975. {
  976. return this == World.Player;
  977. }
  978. /// <summary>
  979. /// 返回指定角色是否是敌人
  980. /// </summary>
  981. public bool IsEnemy(Role other)
  982. {
  983. if (other.Camp == Camp || other.Camp == CampEnum.Peace || Camp == CampEnum.Peace)
  984. {
  985. return false;
  986. }
  987.  
  988. return true;
  989. }
  990.  
  991. /// <summary>
  992. /// 返回指定阵营是否是敌人
  993. /// </summary>
  994. public bool IsEnemy(CampEnum otherCamp)
  995. {
  996. if (otherCamp == Camp || otherCamp == CampEnum.Peace || Camp == CampEnum.Peace)
  997. {
  998. return false;
  999. }
  1000.  
  1001. return true;
  1002. }
  1003. /// <summary>
  1004. /// 是否是玩家的敌人
  1005. /// </summary>
  1006. public bool IsEnemyWithPlayer()
  1007. {
  1008. if (World.Player == null)
  1009. {
  1010. return false;
  1011. }
  1012. return IsEnemy(World.Player);
  1013. }
  1014.  
  1015. /// <summary>
  1016. /// 将 Role 子节点的旋转角度转换为正常的旋转角度<br/>
  1017. /// 因为 Role 受到 Face 影响, 会出现转向动作, 所以需要该函数来转换旋转角度
  1018. /// </summary>
  1019. /// <param name="rotation">角度, 弧度制</param>
  1020. public float ConvertRotation(float rotation)
  1021. {
  1022. if (Face == FaceDirection.Right)
  1023. {
  1024. return rotation;
  1025. }
  1026.  
  1027. return Mathf.Pi - rotation;
  1028. }
  1029.  
  1030. /// <summary>
  1031. /// 获取开火点高度
  1032. /// </summary>
  1033. public virtual float GetFirePointAltitude()
  1034. {
  1035. return -MountPoint.Position.Y;
  1036. }
  1037. /// <summary>
  1038. /// 当拾起某个武器时调用
  1039. /// </summary>
  1040. protected virtual void OnPickUpWeapon(Weapon weapon)
  1041. {
  1042. }
  1043. /// <summary>
  1044. /// 当扔掉某个武器时调用
  1045. /// </summary>
  1046. protected virtual void OnThrowWeapon(Weapon weapon)
  1047. {
  1048. }
  1049.  
  1050. /// <summary>
  1051. /// 当切换到某个武器时调用
  1052. /// </summary>
  1053. protected virtual void OnExchangeWeapon(Weapon weapon)
  1054. {
  1055. }
  1056. /// <summary>
  1057. /// 当武器放到后背时调用, 用于设置武器位置和角度
  1058. /// </summary>
  1059. /// <param name="weapon">武器实例</param>
  1060. /// <param name="index">放入武器背包的位置</param>
  1061. public virtual void OnPutBackMount(Weapon weapon, int index)
  1062. {
  1063. if (index < 8)
  1064. {
  1065. if (index % 2 == 0)
  1066. {
  1067. weapon.Position = new Vector2(-4, 3);
  1068. weapon.RotationDegrees = 90 - (index / 2f) * 20;
  1069. weapon.Scale = new Vector2(-1, 1);
  1070. }
  1071. else
  1072. {
  1073. weapon.Position = new Vector2(4, 3);
  1074. weapon.RotationDegrees = 270 + (index - 1) / 2f * 20;
  1075. weapon.Scale = new Vector2(1, 1);
  1076. }
  1077. }
  1078. else
  1079. {
  1080. weapon.Visible = false;
  1081. }
  1082. }
  1083. protected override void OnAffiliationChange(AffiliationArea prevArea)
  1084. {
  1085. //身上的武器的所属区域也得跟着变
  1086. WeaponPack.ForEach((weapon, i) =>
  1087. {
  1088. if (AffiliationArea != null)
  1089. {
  1090. AffiliationArea.InsertItem(weapon);
  1091. }
  1092. else if (weapon.AffiliationArea != null)
  1093. {
  1094. weapon.AffiliationArea.RemoveItem(weapon);
  1095. }
  1096.  
  1097. weapon.World = World;
  1098. });
  1099. }
  1100. /// <summary>
  1101. /// 调整角色的朝向, 使其看向目标点
  1102. /// </summary>
  1103. public virtual void LookTargetPosition(Vector2 pos)
  1104. {
  1105. LookPosition = pos;
  1106. if (MountLookTarget)
  1107. {
  1108. //脸的朝向
  1109. var gPos = Position;
  1110. if (pos.X > gPos.X && Face == FaceDirection.Left)
  1111. {
  1112. Face = FaceDirection.Right;
  1113. }
  1114. else if (pos.X < gPos.X && Face == FaceDirection.Right)
  1115. {
  1116. Face = FaceDirection.Left;
  1117. }
  1118. //枪口跟随目标
  1119. MountPoint.SetLookAt(pos);
  1120. }
  1121. }
  1122.  
  1123. /// <summary>
  1124. /// 返回所有武器是否弹药都打光了
  1125. /// </summary>
  1126. public virtual bool IsAllWeaponTotalAmmoEmpty()
  1127. {
  1128. foreach (var weapon in WeaponPack.ItemSlot)
  1129. {
  1130. if (weapon != null && !weapon.IsTotalAmmoEmpty())
  1131. {
  1132. return false;
  1133. }
  1134. }
  1135.  
  1136. return true;
  1137. }
  1138. //-------------------------------------------------------------------------------------
  1139. /// <summary>
  1140. /// 拾起一个武器, 返回是否成功拾起, 如果不想立刻切换到该武器, exchange 请传 false
  1141. /// </summary>
  1142. /// <param name="weapon">武器对象</param>
  1143. /// <param name="exchange">是否立即切换到该武器, 默认 true </param>
  1144. public bool PickUpWeapon(Weapon weapon, bool exchange = true)
  1145. {
  1146. if (WeaponPack.PickupItem(weapon, exchange) != -1)
  1147. {
  1148. //从可互动队列中移除
  1149. InteractiveItemList.Remove(weapon);
  1150. OnPickUpWeapon(weapon);
  1151. return true;
  1152. }
  1153.  
  1154. return false;
  1155. }
  1156.  
  1157. /// <summary>
  1158. /// 切换到下一个武器
  1159. /// </summary>
  1160. public void ExchangeNextWeapon()
  1161. {
  1162. var weapon = WeaponPack.ActiveItem;
  1163. WeaponPack.ExchangeNext();
  1164. if (WeaponPack.ActiveItem != weapon)
  1165. {
  1166. OnExchangeWeapon(WeaponPack.ActiveItem);
  1167. }
  1168. }
  1169.  
  1170. /// <summary>
  1171. /// 切换到上一个武器
  1172. /// </summary>
  1173. public void ExchangePrevWeapon()
  1174. {
  1175. var weapon = WeaponPack.ActiveItem;
  1176. WeaponPack.ExchangePrev();
  1177. if (WeaponPack.ActiveItem != weapon)
  1178. {
  1179. OnExchangeWeapon(WeaponPack.ActiveItem);
  1180. }
  1181. }
  1182.  
  1183. /// <summary>
  1184. /// 切换到指定索引到武器
  1185. /// </summary>
  1186. public void ExchangeWeaponByIndex(int index)
  1187. {
  1188. if (WeaponPack.ActiveIndex != index)
  1189. {
  1190. WeaponPack.ExchangeByIndex(index);
  1191. }
  1192. }
  1193.  
  1194. /// <summary>
  1195. /// 扔掉当前使用的武器, 切换到上一个武器
  1196. /// </summary>
  1197. public void ThrowWeapon()
  1198. {
  1199. ThrowWeapon(WeaponPack.ActiveIndex);
  1200. }
  1201.  
  1202. /// <summary>
  1203. /// 扔掉指定位置的武器
  1204. /// </summary>
  1205. /// <param name="index">武器在武器背包中的位置</param>
  1206. public void ThrowWeapon(int index)
  1207. {
  1208. var weapon = WeaponPack.GetItem(index);
  1209. if (weapon == null)
  1210. {
  1211. return;
  1212. }
  1213.  
  1214. // var temp = weapon.AnimatedSprite.Position;
  1215. // if (Face == FaceDirection.Left)
  1216. // {
  1217. // temp.Y = -temp.Y;
  1218. // }
  1219. //var pos = GlobalPosition + temp.Rotated(weapon.GlobalRotation);
  1220. WeaponPack.RemoveItem(index);
  1221. //播放抛出效果
  1222. weapon.ThrowWeapon(this, GlobalPosition);
  1223. }
  1224.  
  1225. /// <summary>
  1226. /// 从背包中移除指定武器, 不会触发投抛效果
  1227. /// </summary>
  1228. /// <param name="index">武器在武器背包中的位置</param>
  1229. public void RemoveWeapon(int index)
  1230. {
  1231. var weapon = WeaponPack.GetItem(index);
  1232. if (weapon == null)
  1233. {
  1234. return;
  1235. }
  1236. WeaponPack.RemoveItem(index);
  1237. }
  1238. /// <summary>
  1239. /// 扔掉所有武器
  1240. /// </summary>
  1241. public void ThrowAllWeapon()
  1242. {
  1243. var weapons = WeaponPack.GetAndClearItem();
  1244. for (var i = 0; i < weapons.Length; i++)
  1245. {
  1246. weapons[i].ThrowWeapon(this);
  1247. }
  1248. }
  1249. /// <summary>
  1250. /// 切换到下一个武器
  1251. /// </summary>
  1252. public void ExchangeNextActiveProp()
  1253. {
  1254. var prop = ActivePropsPack.ActiveItem;
  1255. ActivePropsPack.ExchangeNext();
  1256. if (prop != ActivePropsPack.ActiveItem)
  1257. {
  1258. OnExchangeActiveProp(ActivePropsPack.ActiveItem);
  1259. }
  1260. }
  1261.  
  1262. /// <summary>
  1263. /// 切换到上一个武器
  1264. /// </summary>
  1265. public void ExchangePrevActiveProp()
  1266. {
  1267. var prop = ActivePropsPack.ActiveItem;
  1268. ActivePropsPack.ExchangePrev();
  1269. if (prop != ActivePropsPack.ActiveItem)
  1270. {
  1271. OnExchangeActiveProp(ActivePropsPack.ActiveItem);
  1272. }
  1273. }
  1274.  
  1275. //-------------------------------------------------------------------------------------
  1276.  
  1277. /// <summary>
  1278. /// 触发换弹
  1279. /// </summary>
  1280. public virtual void Reload()
  1281. {
  1282. if (WeaponPack.ActiveItem != null)
  1283. {
  1284. WeaponPack.ActiveItem.Reload();
  1285. }
  1286. }
  1287. /// <summary>
  1288. /// 攻击函数
  1289. /// </summary>
  1290. public virtual void Attack()
  1291. {
  1292. if (MeleeAttackTimer <= 0 && WeaponPack.ActiveItem != null)
  1293. {
  1294. WeaponPack.ActiveItem.Trigger(this);
  1295. }
  1296. }
  1297.  
  1298. /// <summary>
  1299. /// 触发近战攻击
  1300. /// </summary>
  1301. public virtual void MeleeAttack()
  1302. {
  1303. if (IsAttack)
  1304. {
  1305. return;
  1306. }
  1307.  
  1308. if (WeaponPack.ActiveItem != null && !WeaponPack.ActiveItem.Reloading && WeaponPack.ActiveItem.Attribute.CanMeleeAttack)
  1309. {
  1310. MeleeAttackTimer = RoleState.MeleeAttackTime;
  1311. MountLookTarget = false;
  1312. //播放近战动画
  1313. PlayAnimation_MeleeAttack(() =>
  1314. {
  1315. MountLookTarget = true;
  1316. });
  1317. }
  1318. }
  1319. /// <summary>
  1320. /// 添加金币
  1321. /// </summary>
  1322. public virtual void AddGold(int goldCount)
  1323. {
  1324. RoleState.Gold += RoleState.CalcGetGold(goldCount);
  1325. //播放音效
  1326. SoundManager.PlaySoundByConfig(ExcelConfig.Sound_Map["gold"], Position, this);
  1327. }
  1328.  
  1329. /// <summary>
  1330. /// 使用金币
  1331. /// </summary>
  1332. public virtual void UseGold(int goldCount)
  1333. {
  1334. RoleState.Gold -= goldCount;
  1335. }
  1336.  
  1337. /// <summary>
  1338. /// 切换当前使用的武器的回调
  1339. /// </summary>
  1340. private void OnChangeActiveItem(Weapon weapon)
  1341. {
  1342. //这里处理近战区域
  1343. if (weapon != null)
  1344. {
  1345. MeleeAttackCollision.Polygon = Utils.CreateSectorPolygon(
  1346. Utils.ConvertAngle(-MeleeAttackAngle / 2f),
  1347. (weapon.GetLocalFirePosition() + weapon.GetGripPosition()).Length() * 1.1f,
  1348. MeleeAttackAngle,
  1349. 6
  1350. );
  1351. MeleeAttackArea.CollisionMask = AttackLayer | PhysicsLayer.Bullet;
  1352. }
  1353. }
  1354.  
  1355. private void OnMeleeAttackAreaEntered(Area2D area)
  1356. {
  1357. var activeWeapon = WeaponPack.ActiveItem;
  1358. if (activeWeapon == null)
  1359. {
  1360. return;
  1361. }
  1362. if (area is IHurt hurt)
  1363. {
  1364. HandlerCollision(hurt, activeWeapon);
  1365. }
  1366. }
  1367.  
  1368. /// <summary>
  1369. /// 近战区域碰到敌人
  1370. /// </summary>
  1371. private void OnMeleeAttackBodyEntered(Node2D body)
  1372. {
  1373. var activeWeapon = WeaponPack.ActiveItem;
  1374. if (activeWeapon == null)
  1375. {
  1376. return;
  1377. }
  1378. if (body is IHurt hurt)
  1379. {
  1380. HandlerCollision(hurt, activeWeapon);
  1381. }
  1382. else if (body is Bullet bullet) //攻击子弹
  1383. {
  1384. if (IsEnemy(bullet.BulletData.TriggerRole))
  1385. {
  1386. bullet.OnPlayDisappearEffect();
  1387. bullet.Destroy();
  1388. }
  1389. }
  1390. }
  1391.  
  1392. private void HandlerCollision(IHurt hurt, Weapon activeWeapon)
  1393. {
  1394. if (hurt.CanHurt(Camp))
  1395. {
  1396. var damage = Utils.Random.RandomConfigRange(activeWeapon.Attribute.MeleeAttackHarmRange);
  1397. damage = RoleState.CalcDamage(damage);
  1398.  
  1399. var o = hurt.GetActivityObject();
  1400. var pos = hurt.GetPosition();
  1401. if (o != null && o is not Player) //不是玩家才能被击退
  1402. {
  1403. var attr = IsAi ? activeWeapon.AiUseAttribute : activeWeapon.PlayerUseAttribute;
  1404. var repel = Utils.Random.RandomConfigRange(attr.MeleeAttackRepelRange);
  1405. var position = pos - MountPoint.GlobalPosition;
  1406. var v2 = position.Normalized() * repel;
  1407. o.AddRepelForce(v2);
  1408. }
  1409. hurt.Hurt(this, damage, (pos - GlobalPosition).Angle());
  1410. }
  1411. }
  1412.  
  1413. protected override void OnDestroy()
  1414. {
  1415. //销毁道具
  1416. foreach (var buffProp in BuffPropPack)
  1417. {
  1418. buffProp.Destroy();
  1419. }
  1420. BuffPropPack.Clear();
  1421. ActivePropsPack.Destroy();
  1422. WeaponPack.Destroy();
  1423. }
  1424. }