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