Newer
Older
DungeonShooting / DungeonShooting_Godot / src / game / activity / role / Role.cs
@小李xl 小李xl on 28 Jun 2023 19 KB 制作被动道具
  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<object> PropsPack { get; } = new List<object>();
  43.  
  44. /// <summary>
  45. /// 角色携带的武器袋
  46. /// </summary>
  47. public Holster 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.  
  197. //初始缩放
  198. private Vector2 _startScale;
  199. //所有角色碰撞的道具
  200. private readonly List<ActivityObject> _interactiveItemList = new List<ActivityObject>();
  201.  
  202. private CheckInteractiveResult _tempResultData;
  203. private uint _currentLayer;
  204. //闪烁计时器
  205. private float _flashingInvincibleTimer = -1;
  206. //闪烁状态
  207. private bool _flashingInvincibleFlag = false;
  208. //闪烁动画协程id
  209. private long _invincibleFlashingId = -1;
  210. //护盾恢复计时器
  211. private float _shieldRecoveryTimer = 0;
  212.  
  213. /// <summary>
  214. /// 可以互动的道具
  215. /// </summary>
  216. public ActivityObject InteractiveItem { get; private set; }
  217.  
  218. /// <summary>
  219. /// 当血量改变时调用
  220. /// </summary>
  221. protected virtual void OnChangeHp(int hp)
  222. {
  223. }
  224.  
  225. /// <summary>
  226. /// 当最大血量改变时调用
  227. /// </summary>
  228. protected virtual void OnChangeMaxHp(int maxHp)
  229. {
  230. }
  231. /// <summary>
  232. /// 护盾值改变时调用
  233. /// </summary>
  234. protected virtual void OnChangeShield(int shield)
  235. {
  236. }
  237.  
  238. /// <summary>
  239. /// 最大护盾值改变时调用
  240. /// </summary>
  241. protected virtual void OnChangeMaxShield(int maxShield)
  242. {
  243. }
  244.  
  245. /// <summary>
  246. /// 当护盾被破坏时调用
  247. /// </summary>
  248. protected virtual void OnShieldDestroy()
  249. {
  250. }
  251.  
  252. /// <summary>
  253. /// 当受伤时调用
  254. /// </summary>
  255. /// <param name="damage">受到的伤害</param>
  256. /// <param name="realHarm">是否受到真实伤害, 如果为false, 则表示该伤害被互动格挡掉了</param>
  257. protected virtual void OnHit(int damage, bool realHarm)
  258. {
  259. }
  260.  
  261. /// <summary>
  262. /// 受到伤害时调用, 用于改变受到的伤害值
  263. /// </summary>
  264. /// <param name="damage">受到的伤害</param>
  265. protected virtual int OnHandlerHurt(int damage)
  266. {
  267. return damage;
  268. }
  269. /// <summary>
  270. /// 当可互动的物体改变时调用, result 参数为 null 表示变为不可互动
  271. /// </summary>
  272. /// <param name="result">检测是否可互动时的返回值</param>
  273. protected virtual void ChangeInteractiveItem(CheckInteractiveResult result)
  274. {
  275. }
  276.  
  277. /// <summary>
  278. /// 死亡时调用
  279. /// </summary>
  280. protected virtual void OnDie()
  281. {
  282. }
  283.  
  284. public override void OnInit()
  285. {
  286. Holster = new Holster(this);
  287. _startScale = Scale;
  288. MountPoint.Master = this;
  289. HurtArea.CollisionLayer = CollisionLayer;
  290. HurtArea.CollisionMask = 0;
  291. _currentLayer = HurtArea.CollisionLayer;
  292. Face = FaceDirection.Right;
  293. //连接互动物体信号
  294. InteractiveArea.BodyEntered += _OnPropsEnter;
  295. InteractiveArea.BodyExited += _OnPropsExit;
  296. }
  297.  
  298. protected override void Process(float delta)
  299. {
  300. //看向目标
  301. if (LookTarget != null)
  302. {
  303. Vector2 pos = LookTarget.GlobalPosition;
  304. //脸的朝向
  305. var gPos = GlobalPosition;
  306. if (pos.X > gPos.X && Face == FaceDirection.Left)
  307. {
  308. Face = FaceDirection.Right;
  309. }
  310. else if (pos.X < gPos.X && Face == FaceDirection.Right)
  311. {
  312. Face = FaceDirection.Left;
  313. }
  314. //枪口跟随目标
  315. MountPoint.SetLookAt(pos);
  316. }
  317. //检查可互动的道具
  318. bool findFlag = false;
  319. for (int i = 0; i < _interactiveItemList.Count; i++)
  320. {
  321. var item = _interactiveItemList[i];
  322. if (item == null || item.IsDestroyed)
  323. {
  324. _interactiveItemList.RemoveAt(i--);
  325. }
  326. else
  327. {
  328. //找到可互动的道具了
  329. if (!findFlag)
  330. {
  331. var result = item.CheckInteractive(this);
  332. if (result.CanInteractive) //可以互动
  333. {
  334. findFlag = true;
  335. if (InteractiveItem != item) //更改互动物体
  336. {
  337. InteractiveItem = item;
  338. ChangeInteractiveItem(result);
  339. }
  340. else if (result.ShowIcon != _tempResultData.ShowIcon) //切换状态
  341. {
  342. ChangeInteractiveItem(result);
  343. }
  344. }
  345. _tempResultData = result;
  346. }
  347. }
  348. }
  349. //没有可互动的道具
  350. if (!findFlag && InteractiveItem != null)
  351. {
  352. InteractiveItem = null;
  353. ChangeInteractiveItem(null);
  354. }
  355.  
  356. //无敌状态, 播放闪烁动画
  357. if (Invincible)
  358. {
  359. _flashingInvincibleTimer -= delta;
  360. if (_flashingInvincibleTimer <= 0)
  361. {
  362. _flashingInvincibleTimer = 0.15f;
  363. if (_flashingInvincibleFlag)
  364. {
  365. _flashingInvincibleFlag = false;
  366. SetBlendAlpha(0.7f);
  367. }
  368. else
  369. {
  370. _flashingInvincibleFlag = true;
  371. SetBlendAlpha(0);
  372. }
  373. }
  374.  
  375. _shieldRecoveryTimer = 0;
  376. }
  377. else //恢复护盾
  378. {
  379. if (Shield < MaxShield)
  380. {
  381. _shieldRecoveryTimer += delta;
  382. if (_shieldRecoveryTimer >= RoleState.ShieldRecoveryTime) //时间到, 恢复
  383. {
  384. Shield++;
  385. _shieldRecoveryTimer = 0;
  386. }
  387. }
  388. else
  389. {
  390. _shieldRecoveryTimer = 0;
  391. }
  392. }
  393. }
  394.  
  395. /// <summary>
  396. /// 当武器放到后背时调用, 用于设置武器位置和角度
  397. /// </summary>
  398. /// <param name="weapon">武器实例</param>
  399. /// <param name="index">放入武器袋的位置</param>
  400. public virtual void OnPutBackMount(Weapon weapon, int index)
  401. {
  402. if (index < 8)
  403. {
  404. if (index % 2 == 0)
  405. {
  406. weapon.Position = new Vector2(-4, 3);
  407. weapon.RotationDegrees = 90 - (index / 2f) * 20;
  408. weapon.Scale = new Vector2(-1, 1);
  409. }
  410. else
  411. {
  412. weapon.Position = new Vector2(4, 3);
  413. weapon.RotationDegrees = 270 + (index - 1) / 2f * 20;
  414. weapon.Scale = new Vector2(1, 1);
  415. }
  416. }
  417. else
  418. {
  419. weapon.Visible = false;
  420. }
  421. }
  422. protected override void OnAffiliationChange()
  423. {
  424. //身上的武器的所属区域也得跟着变
  425. Holster.ForEach((weapon, i) =>
  426. {
  427. if (AffiliationArea != null)
  428. {
  429. AffiliationArea.InsertItem(weapon);
  430. }
  431. else if (weapon.AffiliationArea != null)
  432. {
  433. weapon.AffiliationArea.RemoveItem(weapon);
  434. }
  435. });
  436. }
  437.  
  438. /// <summary>
  439. /// 获取当前角色的中心点坐标
  440. /// </summary>
  441. public Vector2 GetCenterPosition()
  442. {
  443. return MountPoint.GlobalPosition;
  444. }
  445.  
  446. /// <summary>
  447. /// 使角色看向指定的坐标,
  448. /// 注意, 调用该函数会清空 LookTarget, 因为拥有 LookTarget 时也会每帧更新玩家视野位置
  449. /// </summary>
  450. /// <param name="pos"></param>
  451. public void LookTargetPosition(Vector2 pos)
  452. {
  453. LookTarget = null;
  454. //脸的朝向
  455. var gPos = GlobalPosition;
  456. if (pos.X > gPos.X && Face == FaceDirection.Left)
  457. {
  458. Face = FaceDirection.Right;
  459. }
  460. else if (pos.X < gPos.X && Face == FaceDirection.Right)
  461. {
  462. Face = FaceDirection.Left;
  463. }
  464. //枪口跟随目标
  465. MountPoint.SetLookAt(pos);
  466. }
  467. /// <summary>
  468. /// 判断指定坐标是否在角色视野方向
  469. /// </summary>
  470. public bool IsPositionInForward(Vector2 pos)
  471. {
  472. var gps = GlobalPosition;
  473. return (Face == FaceDirection.Left && pos.X <= gps.X) ||
  474. (Face == FaceDirection.Right && pos.X >= gps.X);
  475. }
  476.  
  477. /// <summary>
  478. /// 返回所有武器是否弹药都打光了
  479. /// </summary>
  480. public bool IsAllWeaponTotalAmmoEmpty()
  481. {
  482. foreach (var weapon in Holster.Weapons)
  483. {
  484. if (weapon != null && !weapon.IsTotalAmmoEmpty())
  485. {
  486. return false;
  487. }
  488. }
  489.  
  490. return true;
  491. }
  492. /// <summary>
  493. /// 拾起一个武器, 返回是否成功拾取, 如果不想立刻切换到该武器, exchange 请传 false
  494. /// </summary>
  495. /// <param name="weapon">武器对象</param>
  496. /// <param name="exchange">是否立即切换到该武器, 默认 true </param>
  497. public virtual bool PickUpWeapon(Weapon weapon, bool exchange = true)
  498. {
  499. if (Holster.PickupWeapon(weapon, exchange) != -1)
  500. {
  501. //从可互动队列中移除
  502. _interactiveItemList.Remove(weapon);
  503. return true;
  504. }
  505.  
  506. return false;
  507. }
  508.  
  509. /// <summary>
  510. /// 切换到下一个武器
  511. /// </summary>
  512. public virtual void ExchangeNext()
  513. {
  514. Holster.ExchangeNext();
  515. }
  516.  
  517. /// <summary>
  518. /// 切换到上一个武器
  519. /// </summary>
  520. public virtual void ExchangePrev()
  521. {
  522. Holster.ExchangePrev();
  523. }
  524.  
  525. /// <summary>
  526. /// 扔掉当前使用的武器, 切换到上一个武器
  527. /// </summary>
  528. public virtual void ThrowWeapon()
  529. {
  530. ThrowWeapon(Holster.ActiveIndex);
  531. }
  532.  
  533. /// <summary>
  534. /// 扔掉指定位置的武器
  535. /// </summary>
  536. /// <param name="index">武器在武器袋中的位置</param>
  537. public virtual void ThrowWeapon(int index)
  538. {
  539. var weapon = Holster.GetWeapon(index);
  540. if (weapon == null)
  541. {
  542. return;
  543. }
  544.  
  545. var temp = weapon.AnimatedSprite.Position;
  546. if (Face == FaceDirection.Left)
  547. {
  548. temp.Y = -temp.Y;
  549. }
  550. //var pos = GlobalPosition + temp.Rotated(weapon.GlobalRotation);
  551. Holster.RemoveWeapon(index);
  552. //播放抛出效果
  553. weapon.ThrowWeapon(this, GlobalPosition);
  554. }
  555.  
  556. /// <summary>
  557. /// 返回是否存在可互动的物体
  558. /// </summary>
  559. public bool HasInteractive()
  560. {
  561. return InteractiveItem != null;
  562. }
  563.  
  564. /// <summary>
  565. /// 触发与碰撞的物体互动, 并返回与其互动的物体
  566. /// </summary>
  567. public ActivityObject TriggerInteractive()
  568. {
  569. if (HasInteractive())
  570. {
  571. var item = InteractiveItem;
  572. item.Interactive(this);
  573. return item;
  574. }
  575.  
  576. return null;
  577. }
  578.  
  579. /// <summary>
  580. /// 触发换弹
  581. /// </summary>
  582. public virtual void Reload()
  583. {
  584. if (Holster.ActiveWeapon != null)
  585. {
  586. Holster.ActiveWeapon.Reload();
  587. }
  588. }
  589.  
  590. /// <summary>
  591. /// 触发攻击
  592. /// </summary>
  593. public virtual void Attack()
  594. {
  595. if (Holster.ActiveWeapon != null)
  596. {
  597. Holster.ActiveWeapon.Trigger();
  598. }
  599. }
  600.  
  601. /// <summary>
  602. /// 受到伤害, 如果是在碰撞信号处理函数中调用该函数, 请使用 CallDeferred 来延时调用, 否则很有可能导致报错
  603. /// </summary>
  604. /// <param name="damage">伤害的量</param>
  605. /// <param name="angle">角度</param>
  606. public virtual void Hurt(int damage, float angle)
  607. {
  608. //受伤闪烁, 无敌状态
  609. if (Invincible)
  610. {
  611. return;
  612. }
  613. //计算真正受到的伤害
  614. damage = OnHandlerHurt(damage);
  615. var flag = Shield > 0;
  616. if (flag)
  617. {
  618. Shield -= damage;
  619. }
  620. else
  621. {
  622. damage = RoleState.CallCalcHurtDamageEvent(damage);
  623. if (damage < 0)
  624. {
  625. return;
  626. }
  627. Hp -= damage;
  628. //播放血液效果
  629. // var packedScene = ResourceManager.Load<PackedScene>(ResourcePath.prefab_effect_Blood_tscn);
  630. // var blood = packedScene.Instance<Blood>();
  631. // blood.GlobalPosition = GlobalPosition;
  632. // blood.Rotation = angle;
  633. // GameApplication.Instance.Node3D.GetRoot().AddChild(blood);
  634. }
  635. OnHit(damage, !flag);
  636. //受伤特效
  637. PlayHitAnimation();
  638. //死亡判定
  639. if (Hp <= 0)
  640. {
  641. //死亡
  642. if (!IsDie)
  643. {
  644. IsDie = true;
  645. OnDie();
  646. }
  647. }
  648. }
  649.  
  650. /// <summary>
  651. /// 播放无敌状态闪烁动画
  652. /// </summary>
  653. /// <param name="time">持续时间</param>
  654. public void PlayInvincibleFlashing(float time)
  655. {
  656. Invincible = true;
  657. if (_invincibleFlashingId >= 0) //上一个还没结束
  658. {
  659. StopCoroutine(_invincibleFlashingId);
  660. }
  661.  
  662. _invincibleFlashingId = StartCoroutine(RunInvincibleFlashing(time));
  663. }
  664.  
  665. /// <summary>
  666. /// 停止无敌状态闪烁动画
  667. /// </summary>
  668. public void StopInvincibleFlashing()
  669. {
  670. Invincible = false;
  671. if (_invincibleFlashingId >= 0)
  672. {
  673. StopCoroutine(_invincibleFlashingId);
  674. _invincibleFlashingId = -1;
  675. }
  676. }
  677.  
  678. private IEnumerator RunInvincibleFlashing(float time)
  679. {
  680. yield return new WaitForSeconds(time);
  681. _invincibleFlashingId = -1;
  682. Invincible = false;
  683. }
  684.  
  685. /// <summary>
  686. /// 设置脸的朝向
  687. /// </summary>
  688. private void SetFace(FaceDirection face)
  689. {
  690. if (_face != face)
  691. {
  692. _face = face;
  693. if (face == FaceDirection.Right)
  694. {
  695. RotationDegrees = 0;
  696. Scale = _startScale;
  697. }
  698. else
  699. {
  700. RotationDegrees = 180;
  701. Scale = new Vector2(_startScale.X, -_startScale.Y);
  702. }
  703. }
  704. }
  705. /// <summary>
  706. /// 连接信号: InteractiveArea.BodyEntered
  707. /// 与物体碰撞
  708. /// </summary>
  709. private void _OnPropsEnter(Node2D other)
  710. {
  711. if (other is ActivityObject propObject && !propObject.CollisionWithMask(PhysicsLayer.OnHand))
  712. {
  713. if (!_interactiveItemList.Contains(propObject))
  714. {
  715. _interactiveItemList.Add(propObject);
  716. }
  717. }
  718. }
  719.  
  720. /// <summary>
  721. /// 连接信号: InteractiveArea.BodyExited
  722. /// 物体离开碰撞区域
  723. /// </summary>
  724. private void _OnPropsExit(Node2D other)
  725. {
  726. if (other is ActivityObject propObject)
  727. {
  728. if (_interactiveItemList.Contains(propObject))
  729. {
  730. _interactiveItemList.Remove(propObject);
  731. }
  732. if (InteractiveItem == propObject)
  733. {
  734. InteractiveItem = null;
  735. ChangeInteractiveItem(null);
  736. }
  737. }
  738. }
  739.  
  740. /// <summary>
  741. /// 添加被动
  742. /// </summary>
  743. /// <param name="buff"></param>
  744. public void PushBuff(Buff buff)
  745. {
  746. }
  747. }