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