Newer
Older
DungeonShooting / DungeonShooting_Godot / src / game / activity / role / Role.cs
@lijincheng lijincheng on 4 Jul 2023 20 KB 将 Holster 抽成通用背包 Package
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using Godot;
  4.  
  5. /// <summary>
  6. /// 角色基类
  7. /// </summary>
  8. public abstract partial class Role : ActivityObject
  9. {
  10. /// <summary>
  11. /// 是否是 Ai
  12. /// </summary>
  13. public bool IsAi { get; protected set; } = false;
  14.  
  15. /// <summary>
  16. /// 角色属性
  17. /// </summary>
  18. public RoleState RoleState { get; } = new RoleState();
  19. /// <summary>
  20. /// 默认攻击对象层级
  21. /// </summary>
  22. public const uint DefaultAttackLayer = PhysicsLayer.Player | PhysicsLayer.Enemy | PhysicsLayer.Wall | PhysicsLayer.Prop;
  23. /// <summary>
  24. /// 伤害区域
  25. /// </summary>
  26. [Export, ExportFillNode]
  27. public Area2D HurtArea { get; set; }
  28.  
  29. /// <summary>
  30. /// 所属阵营
  31. /// </summary>
  32. public CampEnum Camp;
  33.  
  34. /// <summary>
  35. /// 攻击目标的碰撞器所属层级, 数据源自于: PhysicsLayer
  36. /// </summary>
  37. public uint AttackLayer { get; set; } = PhysicsLayer.Wall;
  38.  
  39. /// <summary>
  40. /// 携带的道具包裹
  41. /// </summary>
  42. public List<Prop> PropsPack { get; } = new List<Prop>();
  43.  
  44. /// <summary>
  45. /// 角色携带的武器袋
  46. /// </summary>
  47. public Package<Weapon> Holster { get; private set; }
  48.  
  49. /// <summary>
  50. /// 武器挂载点
  51. /// </summary>
  52. [Export, ExportFillNode]
  53. public MountRotation MountPoint { get; set; }
  54. /// <summary>
  55. /// 背后武器的挂载点
  56. /// </summary>
  57. [Export, ExportFillNode]
  58. public Marker2D BackMountPoint { get; set; }
  59.  
  60. /// <summary>
  61. /// 互动碰撞区域
  62. /// </summary>
  63. [Export, ExportFillNode]
  64. public Area2D InteractiveArea { get; set; }
  65. /// <summary>
  66. /// 脸的朝向
  67. /// </summary>
  68. public FaceDirection Face { get => _face; set => SetFace(value); }
  69. private FaceDirection _face;
  70.  
  71. /// <summary>
  72. /// 是否死亡
  73. /// </summary>
  74. public bool IsDie { get; private set; }
  75. /// <summary>
  76. /// 血量
  77. /// </summary>
  78. public int Hp
  79. {
  80. get => _hp;
  81. set
  82. {
  83. int temp = _hp;
  84. _hp = value;
  85. if (temp != _hp)
  86. {
  87. OnChangeHp(_hp);
  88. }
  89. }
  90. }
  91. private int _hp = 20;
  92.  
  93. /// <summary>
  94. /// 最大血量
  95. /// </summary>
  96. public int MaxHp
  97. {
  98. get => _maxHp;
  99. set
  100. {
  101. int temp = _maxHp;
  102. _maxHp = value;
  103. //最大血量值改变
  104. if (temp != _maxHp)
  105. {
  106. OnChangeMaxHp(_maxHp);
  107. }
  108. //调整血量
  109. if (Hp > _maxHp)
  110. {
  111. Hp = _maxHp;
  112. }
  113. }
  114. }
  115. private int _maxHp = 20;
  116. /// <summary>
  117. /// 当前护盾值
  118. /// </summary>
  119. public int Shield
  120. {
  121. get => _shield;
  122. set
  123. {
  124. int temp = _shield;
  125. _shield = value;
  126. //护盾被破坏
  127. if (temp > 0 && _shield <= 0 && _maxShield > 0)
  128. {
  129. OnShieldDestroy();
  130. }
  131. //护盾值改变
  132. if (temp != _shield)
  133. {
  134. OnChangeShield(_shield);
  135. }
  136. }
  137. }
  138. private int _shield = 0;
  139.  
  140. /// <summary>
  141. /// 最大护盾值
  142. /// </summary>
  143. public int MaxShield
  144. {
  145. get => _maxShield;
  146. set
  147. {
  148. int temp = _maxShield;
  149. _maxShield = value;
  150. //最大护盾值改变
  151. if (temp != _maxShield)
  152. {
  153. OnChangeMaxShield(_maxShield);
  154. }
  155. //调整护盾值
  156. if (Shield > _maxShield)
  157. {
  158. Shield = _maxShield;
  159. }
  160. }
  161. }
  162. private int _maxShield = 0;
  163.  
  164. /// <summary>
  165. /// 无敌状态
  166. /// </summary>
  167. public bool Invincible
  168. {
  169. get => _invincible;
  170. set
  171. {
  172. if (_invincible != value)
  173. {
  174. if (value) //无敌状态
  175. {
  176. HurtArea.CollisionLayer = _currentLayer;
  177. _flashingInvincibleTimer = -1;
  178. _flashingInvincibleFlag = false;
  179. }
  180. else //正常状态
  181. {
  182. HurtArea.CollisionLayer = _currentLayer;
  183. SetBlendAlpha(1);
  184. }
  185. }
  186.  
  187. _invincible = value;
  188. }
  189. }
  190.  
  191. private bool _invincible = false;
  192. /// <summary>
  193. /// 当前角色所看向的对象, 也就是枪口指向的对象
  194. /// </summary>
  195. public ActivityObject LookTarget { get; set; }
  196. /// <summary>
  197. /// 当前可以互动的物体
  198. /// </summary>
  199. public ActivityObject InteractiveItem { get; private set; }
  200.  
  201. //初始缩放
  202. private Vector2 _startScale;
  203. //所有角色碰撞的物体
  204. private readonly List<ActivityObject> _interactiveItemList = new List<ActivityObject>();
  205. //当前可互动的物体
  206. private CheckInteractiveResult _currentResultData;
  207. private uint _currentLayer;
  208. //闪烁计时器
  209. private float _flashingInvincibleTimer = -1;
  210. //闪烁状态
  211. private bool _flashingInvincibleFlag = false;
  212. //闪烁动画协程id
  213. private long _invincibleFlashingId = -1;
  214. //护盾恢复计时器
  215. private float _shieldRecoveryTimer = 0;
  216.  
  217. /// <summary>
  218. /// 当血量改变时调用
  219. /// </summary>
  220. protected virtual void OnChangeHp(int hp)
  221. {
  222. }
  223.  
  224. /// <summary>
  225. /// 当最大血量改变时调用
  226. /// </summary>
  227. protected virtual void OnChangeMaxHp(int maxHp)
  228. {
  229. }
  230. /// <summary>
  231. /// 护盾值改变时调用
  232. /// </summary>
  233. protected virtual void OnChangeShield(int shield)
  234. {
  235. }
  236.  
  237. /// <summary>
  238. /// 最大护盾值改变时调用
  239. /// </summary>
  240. protected virtual void OnChangeMaxShield(int maxShield)
  241. {
  242. }
  243.  
  244. /// <summary>
  245. /// 当护盾被破坏时调用
  246. /// </summary>
  247. protected virtual void OnShieldDestroy()
  248. {
  249. }
  250.  
  251. /// <summary>
  252. /// 当受伤时调用
  253. /// </summary>
  254. /// <param name="damage">受到的伤害</param>
  255. /// <param name="realHarm">是否受到真实伤害, 如果为false, 则表示该伤害被互动格挡掉了</param>
  256. protected virtual void OnHit(int damage, bool realHarm)
  257. {
  258. }
  259.  
  260. /// <summary>
  261. /// 受到伤害时调用, 用于改变受到的伤害值
  262. /// </summary>
  263. /// <param name="damage">受到的伤害</param>
  264. protected virtual int OnHandlerHurt(int damage)
  265. {
  266. return damage;
  267. }
  268. /// <summary>
  269. /// 当可互动的物体改变时调用, result 参数为 null 表示变为不可互动
  270. /// </summary>
  271. /// <param name="prev">上一个互动的物体</param>
  272. /// <param name="result">当前物体, 检测是否可互动时的返回值</param>
  273. protected virtual void ChangeInteractiveItem(CheckInteractiveResult prev, CheckInteractiveResult result)
  274. {
  275. }
  276.  
  277. /// <summary>
  278. /// 死亡时调用
  279. /// </summary>
  280. protected virtual void OnDie()
  281. {
  282. }
  283.  
  284. /// <summary>
  285. /// 当往背包中添加道具时调用
  286. /// </summary>
  287. protected virtual void OnPushProp(Prop prop)
  288. {
  289. }
  290.  
  291. /// <summary>
  292. /// 当从背包中移除道具时调用
  293. /// </summary>
  294. protected virtual void OnRemoveProp(Prop prop)
  295. {
  296. }
  297.  
  298. public override void OnInit()
  299. {
  300. Holster = new Package<Weapon>(this);
  301. _startScale = Scale;
  302. MountPoint.Master = this;
  303. HurtArea.CollisionLayer = CollisionLayer;
  304. HurtArea.CollisionMask = 0;
  305. _currentLayer = HurtArea.CollisionLayer;
  306. Face = FaceDirection.Right;
  307. //连接互动物体信号
  308. InteractiveArea.BodyEntered += _OnPropsEnter;
  309. InteractiveArea.BodyExited += _OnPropsExit;
  310. }
  311.  
  312. protected override void Process(float delta)
  313. {
  314. //看向目标
  315. if (LookTarget != null)
  316. {
  317. Vector2 pos = LookTarget.GlobalPosition;
  318. //脸的朝向
  319. var gPos = GlobalPosition;
  320. if (pos.X > gPos.X && Face == FaceDirection.Left)
  321. {
  322. Face = FaceDirection.Right;
  323. }
  324. else if (pos.X < gPos.X && Face == FaceDirection.Right)
  325. {
  326. Face = FaceDirection.Left;
  327. }
  328. //枪口跟随目标
  329. MountPoint.SetLookAt(pos);
  330. }
  331. //检查可互动的物体
  332. bool findFlag = false;
  333. for (int i = 0; i < _interactiveItemList.Count; i++)
  334. {
  335. var item = _interactiveItemList[i];
  336. if (item == null || item.IsDestroyed)
  337. {
  338. _interactiveItemList.RemoveAt(i--);
  339. }
  340. else
  341. {
  342. //找到可互动的物体了
  343. if (!findFlag)
  344. {
  345. var result = item.CheckInteractive(this);
  346. var prev = _currentResultData;
  347. _currentResultData = result;
  348. if (result.CanInteractive) //可以互动
  349. {
  350. findFlag = true;
  351. if (InteractiveItem != item) //更改互动物体
  352. {
  353. InteractiveItem = item;
  354. ChangeInteractiveItem(prev, result);
  355. }
  356. else if (result.ShowIcon != _currentResultData.ShowIcon) //切换状态
  357. {
  358. ChangeInteractiveItem(prev, result);
  359. }
  360. }
  361. }
  362. }
  363. }
  364. //没有可互动的物体
  365. if (!findFlag && InteractiveItem != null)
  366. {
  367. var prev = _currentResultData;
  368. _currentResultData = null;
  369. InteractiveItem = null;
  370. ChangeInteractiveItem(prev, null);
  371. }
  372.  
  373. //无敌状态, 播放闪烁动画
  374. if (Invincible)
  375. {
  376. _flashingInvincibleTimer -= delta;
  377. if (_flashingInvincibleTimer <= 0)
  378. {
  379. _flashingInvincibleTimer = 0.15f;
  380. if (_flashingInvincibleFlag)
  381. {
  382. _flashingInvincibleFlag = false;
  383. SetBlendAlpha(0.7f);
  384. }
  385. else
  386. {
  387. _flashingInvincibleFlag = true;
  388. SetBlendAlpha(0);
  389. }
  390. }
  391.  
  392. _shieldRecoveryTimer = 0;
  393. }
  394. else //恢复护盾
  395. {
  396. if (Shield < MaxShield)
  397. {
  398. _shieldRecoveryTimer += delta;
  399. if (_shieldRecoveryTimer >= RoleState.ShieldRecoveryTime) //时间到, 恢复
  400. {
  401. Shield++;
  402. _shieldRecoveryTimer = 0;
  403. }
  404. }
  405. else
  406. {
  407. _shieldRecoveryTimer = 0;
  408. }
  409. }
  410.  
  411. //道具调用更新
  412. var props = PropsPack.ToArray();
  413. foreach (var prop in props)
  414. {
  415. prop.PackProcess(delta);
  416. }
  417. }
  418.  
  419. /// <summary>
  420. /// 当武器放到后背时调用, 用于设置武器位置和角度
  421. /// </summary>
  422. /// <param name="weapon">武器实例</param>
  423. /// <param name="index">放入武器袋的位置</param>
  424. public virtual void OnPutBackMount(Weapon weapon, int index)
  425. {
  426. if (index < 8)
  427. {
  428. if (index % 2 == 0)
  429. {
  430. weapon.Position = new Vector2(-4, 3);
  431. weapon.RotationDegrees = 90 - (index / 2f) * 20;
  432. weapon.Scale = new Vector2(-1, 1);
  433. }
  434. else
  435. {
  436. weapon.Position = new Vector2(4, 3);
  437. weapon.RotationDegrees = 270 + (index - 1) / 2f * 20;
  438. weapon.Scale = new Vector2(1, 1);
  439. }
  440. }
  441. else
  442. {
  443. weapon.Visible = false;
  444. }
  445. }
  446. protected override void OnAffiliationChange()
  447. {
  448. //身上的武器的所属区域也得跟着变
  449. Holster.ForEach((weapon, i) =>
  450. {
  451. if (AffiliationArea != null)
  452. {
  453. AffiliationArea.InsertItem(weapon);
  454. }
  455. else if (weapon.AffiliationArea != null)
  456. {
  457. weapon.AffiliationArea.RemoveItem(weapon);
  458. }
  459. });
  460. }
  461.  
  462. /// <summary>
  463. /// 获取当前角色的中心点坐标
  464. /// </summary>
  465. public Vector2 GetCenterPosition()
  466. {
  467. return MountPoint.GlobalPosition;
  468. }
  469.  
  470. /// <summary>
  471. /// 使角色看向指定的坐标,
  472. /// 注意, 调用该函数会清空 LookTarget, 因为拥有 LookTarget 时也会每帧更新玩家视野位置
  473. /// </summary>
  474. /// <param name="pos"></param>
  475. public void LookTargetPosition(Vector2 pos)
  476. {
  477. LookTarget = null;
  478. //脸的朝向
  479. var gPos = GlobalPosition;
  480. if (pos.X > gPos.X && Face == FaceDirection.Left)
  481. {
  482. Face = FaceDirection.Right;
  483. }
  484. else if (pos.X < gPos.X && Face == FaceDirection.Right)
  485. {
  486. Face = FaceDirection.Left;
  487. }
  488. //枪口跟随目标
  489. MountPoint.SetLookAt(pos);
  490. }
  491. /// <summary>
  492. /// 判断指定坐标是否在角色视野方向
  493. /// </summary>
  494. public bool IsPositionInForward(Vector2 pos)
  495. {
  496. var gps = GlobalPosition;
  497. return (Face == FaceDirection.Left && pos.X <= gps.X) ||
  498. (Face == FaceDirection.Right && pos.X >= gps.X);
  499. }
  500.  
  501. /// <summary>
  502. /// 返回所有武器是否弹药都打光了
  503. /// </summary>
  504. public bool IsAllWeaponTotalAmmoEmpty()
  505. {
  506. foreach (var weapon in Holster.ItemSlot)
  507. {
  508. if (weapon != null && !weapon.IsTotalAmmoEmpty())
  509. {
  510. return false;
  511. }
  512. }
  513.  
  514. return true;
  515. }
  516. /// <summary>
  517. /// 拾起一个武器, 返回是否成功拾取, 如果不想立刻切换到该武器, exchange 请传 false
  518. /// </summary>
  519. /// <param name="weapon">武器对象</param>
  520. /// <param name="exchange">是否立即切换到该武器, 默认 true </param>
  521. public virtual bool PickUpWeapon(Weapon weapon, bool exchange = true)
  522. {
  523. if (Holster.PickupItem(weapon, exchange) != -1)
  524. {
  525. //从可互动队列中移除
  526. _interactiveItemList.Remove(weapon);
  527. return true;
  528. }
  529.  
  530. return false;
  531. }
  532.  
  533. /// <summary>
  534. /// 切换到下一个武器
  535. /// </summary>
  536. public virtual void ExchangeNext()
  537. {
  538. Holster.ExchangeNext();
  539. }
  540.  
  541. /// <summary>
  542. /// 切换到上一个武器
  543. /// </summary>
  544. public virtual void ExchangePrev()
  545. {
  546. Holster.ExchangePrev();
  547. }
  548.  
  549. /// <summary>
  550. /// 扔掉当前使用的武器, 切换到上一个武器
  551. /// </summary>
  552. public virtual void ThrowWeapon()
  553. {
  554. ThrowWeapon(Holster.ActiveIndex);
  555. }
  556.  
  557. /// <summary>
  558. /// 扔掉指定位置的武器
  559. /// </summary>
  560. /// <param name="index">武器在武器袋中的位置</param>
  561. public virtual void ThrowWeapon(int index)
  562. {
  563. var weapon = Holster.GetItem(index);
  564. if (weapon == null)
  565. {
  566. return;
  567. }
  568.  
  569. var temp = weapon.AnimatedSprite.Position;
  570. if (Face == FaceDirection.Left)
  571. {
  572. temp.Y = -temp.Y;
  573. }
  574. //var pos = GlobalPosition + temp.Rotated(weapon.GlobalRotation);
  575. Holster.RemoveItem(index);
  576. //播放抛出效果
  577. weapon.ThrowWeapon(this, GlobalPosition);
  578. }
  579.  
  580. /// <summary>
  581. /// 返回是否存在可互动的物体
  582. /// </summary>
  583. public bool HasInteractive()
  584. {
  585. return InteractiveItem != null;
  586. }
  587.  
  588. /// <summary>
  589. /// 触发与碰撞的物体互动, 并返回与其互动的物体
  590. /// </summary>
  591. public ActivityObject TriggerInteractive()
  592. {
  593. if (HasInteractive())
  594. {
  595. var item = InteractiveItem;
  596. item.Interactive(this);
  597. return item;
  598. }
  599.  
  600. return null;
  601. }
  602.  
  603. /// <summary>
  604. /// 触发换弹
  605. /// </summary>
  606. public virtual void Reload()
  607. {
  608. if (Holster.ActiveItem != null)
  609. {
  610. Holster.ActiveItem.Reload();
  611. }
  612. }
  613.  
  614. /// <summary>
  615. /// 触发攻击
  616. /// </summary>
  617. public virtual void Attack()
  618. {
  619. if (Holster.ActiveItem != null)
  620. {
  621. Holster.ActiveItem.Trigger();
  622. }
  623. }
  624.  
  625. /// <summary>
  626. /// 受到伤害, 如果是在碰撞信号处理函数中调用该函数, 请使用 CallDeferred 来延时调用, 否则很有可能导致报错
  627. /// </summary>
  628. /// <param name="damage">伤害的量</param>
  629. /// <param name="angle">角度</param>
  630. public virtual void Hurt(int damage, float angle)
  631. {
  632. //受伤闪烁, 无敌状态
  633. if (Invincible)
  634. {
  635. return;
  636. }
  637. //计算真正受到的伤害
  638. damage = OnHandlerHurt(damage);
  639. var flag = Shield > 0;
  640. if (flag)
  641. {
  642. Shield -= damage;
  643. }
  644. else
  645. {
  646. damage = RoleState.CallCalcHurtDamageEvent(damage);
  647. if (damage < 0)
  648. {
  649. return;
  650. }
  651. Hp -= damage;
  652. //播放血液效果
  653. // var packedScene = ResourceManager.Load<PackedScene>(ResourcePath.prefab_effect_Blood_tscn);
  654. // var blood = packedScene.Instance<Blood>();
  655. // blood.GlobalPosition = GlobalPosition;
  656. // blood.Rotation = angle;
  657. // GameApplication.Instance.Node3D.GetRoot().AddChild(blood);
  658. }
  659. OnHit(damage, !flag);
  660.  
  661. //受伤特效
  662. PlayHitAnimation();
  663. //死亡判定
  664. if (Hp <= 0)
  665. {
  666. //死亡
  667. if (!IsDie)
  668. {
  669. IsDie = true;
  670. OnDie();
  671. }
  672. }
  673. }
  674.  
  675. /// <summary>
  676. /// 播放无敌状态闪烁动画
  677. /// </summary>
  678. /// <param name="time">持续时间</param>
  679. public void PlayInvincibleFlashing(float time)
  680. {
  681. Invincible = true;
  682. if (_invincibleFlashingId >= 0) //上一个还没结束
  683. {
  684. StopCoroutine(_invincibleFlashingId);
  685. }
  686.  
  687. _invincibleFlashingId = StartCoroutine(RunInvincibleFlashing(time));
  688. }
  689.  
  690. /// <summary>
  691. /// 停止无敌状态闪烁动画
  692. /// </summary>
  693. public void StopInvincibleFlashing()
  694. {
  695. Invincible = false;
  696. if (_invincibleFlashingId >= 0)
  697. {
  698. StopCoroutine(_invincibleFlashingId);
  699. _invincibleFlashingId = -1;
  700. }
  701. }
  702.  
  703. private IEnumerator RunInvincibleFlashing(float time)
  704. {
  705. yield return new WaitForSeconds(time);
  706. _invincibleFlashingId = -1;
  707. Invincible = false;
  708. }
  709.  
  710. /// <summary>
  711. /// 设置脸的朝向
  712. /// </summary>
  713. private void SetFace(FaceDirection face)
  714. {
  715. if (_face != face)
  716. {
  717. _face = face;
  718. if (face == FaceDirection.Right)
  719. {
  720. RotationDegrees = 0;
  721. Scale = _startScale;
  722. }
  723. else
  724. {
  725. RotationDegrees = 180;
  726. Scale = new Vector2(_startScale.X, -_startScale.Y);
  727. }
  728. }
  729. }
  730. /// <summary>
  731. /// 连接信号: InteractiveArea.BodyEntered
  732. /// 与物体碰撞
  733. /// </summary>
  734. private void _OnPropsEnter(Node2D other)
  735. {
  736. if (other is ActivityObject propObject && !propObject.CollisionWithMask(PhysicsLayer.OnHand))
  737. {
  738. if (!_interactiveItemList.Contains(propObject))
  739. {
  740. _interactiveItemList.Add(propObject);
  741. }
  742. }
  743. }
  744.  
  745. /// <summary>
  746. /// 连接信号: InteractiveArea.BodyExited
  747. /// 物体离开碰撞区域
  748. /// </summary>
  749. private void _OnPropsExit(Node2D other)
  750. {
  751. if (other is ActivityObject propObject)
  752. {
  753. if (_interactiveItemList.Contains(propObject))
  754. {
  755. _interactiveItemList.Remove(propObject);
  756. }
  757. if (InteractiveItem == propObject)
  758. {
  759. var prev = _currentResultData;
  760. _currentResultData = null;
  761. InteractiveItem = null;
  762. ChangeInteractiveItem(prev, null);
  763. }
  764. }
  765. }
  766.  
  767. /// <summary>
  768. /// 添加道具
  769. /// </summary>
  770. public void PushProp(Prop prop)
  771. {
  772. if (PropsPack.Contains(prop))
  773. {
  774. GD.PrintErr("道具已经在包裹中了!");
  775. return;
  776. }
  777. PropsPack.Add(prop);
  778. OnPushProp(prop);
  779. }
  780.  
  781. /// <summary>
  782. /// 移除道具
  783. /// </summary>
  784. public bool RemoveProp(Prop prop)
  785. {
  786. if (PropsPack.Remove(prop))
  787. {
  788. OnRemoveProp(prop);
  789. return true;
  790. }
  791. GD.PrintErr("当前道具不在角色包裹中!");
  792. return false;
  793. }
  794. }