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