Newer
Older
DungeonShooting / DungeonShooting_Godot / src / game / role / Role.cs
@小李xl 小李xl on 7 Jun 2023 15 KB 加载武器
  1. using System.Collections.Generic;
  2. using Godot;
  3.  
  4. /// <summary>
  5. /// 角色基类
  6. /// </summary>
  7. public abstract partial class Role : ActivityObject
  8. {
  9. /// <summary>
  10. /// 是否是 Ai
  11. /// </summary>
  12. public bool IsAi { get; protected set; } = false;
  13. /// <summary>
  14. /// 默认攻击对象层级
  15. /// </summary>
  16. public const uint DefaultAttackLayer = PhysicsLayer.Player | PhysicsLayer.Enemy | PhysicsLayer.Wall | PhysicsLayer.Props;
  17. /// <summary>
  18. /// 伤害区域
  19. /// </summary>
  20. [Export, ExportFillNode]
  21. public Area2D HurtArea { get; set; }
  22.  
  23. /// <summary>
  24. /// 移动速度
  25. /// </summary>
  26. public float MoveSpeed = 120f;
  27.  
  28. /// <summary>
  29. /// 所属阵营
  30. /// </summary>
  31. public CampEnum Camp;
  32.  
  33. /// <summary>
  34. /// 攻击目标的碰撞器所属层级, 数据源自于: PhysicsLayer
  35. /// </summary>
  36. public uint AttackLayer { get; set; } = PhysicsLayer.Wall;
  37.  
  38. // /// <summary>
  39. // /// 携带的道具包裹
  40. // /// </summary>
  41. // public List<object> PropsPack { get; } = new List<object>();
  42.  
  43. /// <summary>
  44. /// 角色携带的武器袋
  45. /// </summary>
  46. public Holster Holster { get; private set; }
  47.  
  48. /// <summary>
  49. /// 武器挂载点
  50. /// </summary>
  51. [Export, ExportFillNode]
  52. public MountRotation MountPoint { get; set; }
  53. /// <summary>
  54. /// 背后武器的挂载点
  55. /// </summary>
  56. [Export, ExportFillNode]
  57. public Marker2D BackMountPoint { get; set; }
  58.  
  59. /// <summary>
  60. /// 互动碰撞区域
  61. /// </summary>
  62. [Export, ExportFillNode]
  63. public Area2D InteractiveArea { get; set; }
  64. /// <summary>
  65. /// 脸的朝向
  66. /// </summary>
  67. public FaceDirection Face { get => _face; set => SetFace(value); }
  68. private FaceDirection _face;
  69.  
  70. /// <summary>
  71. /// 是否死亡
  72. /// </summary>
  73. public bool IsDie { get; private set; }
  74. /// <summary>
  75. /// 血量
  76. /// </summary>
  77. public int Hp
  78. {
  79. get => _hp;
  80. protected set
  81. {
  82. int temp = _hp;
  83. _hp = value;
  84. if (temp != _hp) OnChangeHp(_hp);
  85. }
  86. }
  87. private int _hp = 20;
  88.  
  89. /// <summary>
  90. /// 最大血量
  91. /// </summary>
  92. public int MaxHp
  93. {
  94. get => _maxHp;
  95. protected set
  96. {
  97. int temp = _maxHp;
  98. _maxHp = value;
  99. //护盾值改变
  100. if (temp != _maxHp) OnChangeMaxHp(_maxHp);
  101. }
  102. }
  103. private int _maxHp = 20;
  104. /// <summary>
  105. /// 当前护盾值
  106. /// </summary>
  107. public int Shield
  108. {
  109. get => _shield;
  110. protected set
  111. {
  112. int temp = _shield;
  113. _shield = value;
  114. //护盾被破坏
  115. if (temp > 0 && _shield <= 0) OnShieldDamage();
  116. //护盾值改变
  117. if (temp != _shield) OnChangeShield(_shield);
  118. }
  119. }
  120. private int _shield = 0;
  121.  
  122. /// <summary>
  123. /// 最大护盾值
  124. /// </summary>
  125. public int MaxShield
  126. {
  127. get => _maxShield;
  128. protected set
  129. {
  130. int temp = _maxShield;
  131. _maxShield = value;
  132. if (temp != _maxShield) OnChangeMaxShield(_maxShield);
  133. }
  134. }
  135. private int _maxShield = 0;
  136.  
  137. /// <summary>
  138. /// 当前角色所看向的对象, 也就是枪口指向的对象
  139. /// </summary>
  140. public ActivityObject LookTarget { get; set; }
  141.  
  142. //初始缩放
  143. private Vector2 _startScale;
  144. //所有角色碰撞的道具
  145. private readonly List<ActivityObject> _interactiveItemList = new List<ActivityObject>();
  146.  
  147. private CheckInteractiveResult _tempResultData;
  148.  
  149. /// <summary>
  150. /// 可以互动的道具
  151. /// </summary>
  152. public ActivityObject InteractiveItem { get; private set; }
  153.  
  154. /// <summary>
  155. /// 当血量改变时调用
  156. /// </summary>
  157. protected virtual void OnChangeHp(int hp)
  158. {
  159. }
  160.  
  161. /// <summary>
  162. /// 当最大血量改变时调用
  163. /// </summary>
  164. protected virtual void OnChangeMaxHp(int maxHp)
  165. {
  166. }
  167. /// <summary>
  168. /// 护盾值改变时调用
  169. /// </summary>
  170. protected virtual void OnChangeShield(int shield)
  171. {
  172. }
  173.  
  174. /// <summary>
  175. /// 最大护盾值改变时调用
  176. /// </summary>
  177. protected virtual void OnChangeMaxShield(int maxShield)
  178. {
  179. }
  180.  
  181. /// <summary>
  182. /// 当护盾被破坏时调用
  183. /// </summary>
  184. protected virtual void OnShieldDamage()
  185. {
  186. }
  187.  
  188. /// <summary>
  189. /// 当受伤时调用
  190. /// </summary>
  191. /// <param name="damage">受到的伤害</param>
  192. protected virtual void OnHit(int damage)
  193. {
  194. }
  195.  
  196. /// <summary>
  197. /// 当可互动的物体改变时调用, result 参数为 null 表示变为不可互动
  198. /// </summary>
  199. /// <param name="result">检测是否可互动时的返回值</param>
  200. protected virtual void ChangeInteractiveItem(CheckInteractiveResult result)
  201. {
  202. }
  203.  
  204. /// <summary>
  205. /// 死亡时调用
  206. /// </summary>
  207. protected virtual void OnDie()
  208. {
  209. }
  210.  
  211. public override void OnInit()
  212. {
  213. Holster = new Holster(this);
  214. _startScale = Scale;
  215. MountPoint.Master = this;
  216. HurtArea.CollisionLayer = CollisionLayer;
  217. HurtArea.CollisionMask = 0;
  218. Face = FaceDirection.Right;
  219.  
  220. //连接互动物体信号
  221. InteractiveArea.BodyEntered += _OnPropsEnter;
  222. InteractiveArea.BodyExited += _OnPropsExit;
  223. }
  224.  
  225. protected override void Process(float delta)
  226. {
  227. //看向目标
  228. if (LookTarget != null)
  229. {
  230. Vector2 pos = LookTarget.GlobalPosition;
  231. //脸的朝向
  232. var gPos = GlobalPosition;
  233. if (pos.X > gPos.X && Face == FaceDirection.Left)
  234. {
  235. Face = FaceDirection.Right;
  236. }
  237. else if (pos.X < gPos.X && Face == FaceDirection.Right)
  238. {
  239. Face = FaceDirection.Left;
  240. }
  241. //枪口跟随目标
  242. MountPoint.SetLookAt(pos);
  243. }
  244. //检查可互动的道具
  245. bool findFlag = false;
  246. for (int i = 0; i < _interactiveItemList.Count; i++)
  247. {
  248. var item = _interactiveItemList[i];
  249. if (item == null || item.IsDestroyed)
  250. {
  251. _interactiveItemList.RemoveAt(i--);
  252. }
  253. else
  254. {
  255. //找到可互动的道具了
  256. if (!findFlag)
  257. {
  258. var result = item.CheckInteractive(this);
  259. if (result.CanInteractive) //可以互动
  260. {
  261. findFlag = true;
  262. if (InteractiveItem != item) //更改互动物体
  263. {
  264. InteractiveItem = item;
  265. ChangeInteractiveItem(result);
  266. }
  267. else if (result.ShowIcon != _tempResultData.ShowIcon) //切换状态
  268. {
  269. ChangeInteractiveItem(result);
  270. }
  271. }
  272. _tempResultData = result;
  273. }
  274. }
  275. }
  276. //没有可互动的道具
  277. if (!findFlag && InteractiveItem != null)
  278. {
  279. InteractiveItem = null;
  280. ChangeInteractiveItem(null);
  281. }
  282. }
  283.  
  284. /// <summary>
  285. /// 当武器放到后背时调用, 用于设置武器位置和角度
  286. /// </summary>
  287. /// <param name="weapon">武器实例</param>
  288. /// <param name="index">放入武器袋的位置</param>
  289. public virtual void OnPutBackMount(Weapon weapon, int index)
  290. {
  291. if (index < 8)
  292. {
  293. if (index % 2 == 0)
  294. {
  295. weapon.Position = new Vector2(-4, 3);
  296. weapon.RotationDegrees = 90 - (index / 2f) * 20;
  297. weapon.Scale = new Vector2(-1, 1);
  298. }
  299. else
  300. {
  301. weapon.Position = new Vector2(4, 3);
  302. weapon.RotationDegrees = 270 + (index - 1) / 2f * 20;
  303. weapon.Scale = new Vector2(1, 1);
  304. }
  305. }
  306. else
  307. {
  308. weapon.Visible = false;
  309. }
  310. }
  311. protected override void OnAffiliationChange()
  312. {
  313. //身上的武器的所属区域也得跟着变
  314. Holster.ForEach((weapon, i) =>
  315. {
  316. if (AffiliationArea != null)
  317. {
  318. AffiliationArea.InsertItem(weapon);
  319. }
  320. else if (weapon.AffiliationArea != null)
  321. {
  322. weapon.AffiliationArea.RemoveItem(weapon);
  323. }
  324. });
  325. }
  326.  
  327. /// <summary>
  328. /// 获取当前角色的中心点坐标
  329. /// </summary>
  330. public Vector2 GetCenterPosition()
  331. {
  332. return MountPoint.GlobalPosition;
  333. }
  334.  
  335. /// <summary>
  336. /// 使角色看向指定的坐标,
  337. /// 注意, 调用该函数会清空 LookTarget, 因为拥有 LookTarget 时也会每帧更新玩家视野位置
  338. /// </summary>
  339. /// <param name="pos"></param>
  340. public void LookTargetPosition(Vector2 pos)
  341. {
  342. LookTarget = null;
  343. //脸的朝向
  344. var gPos = GlobalPosition;
  345. if (pos.X > gPos.X && Face == FaceDirection.Left)
  346. {
  347. Face = FaceDirection.Right;
  348. }
  349. else if (pos.X < gPos.X && Face == FaceDirection.Right)
  350. {
  351. Face = FaceDirection.Left;
  352. }
  353. //枪口跟随目标
  354. MountPoint.SetLookAt(pos);
  355. }
  356. /// <summary>
  357. /// 判断指定坐标是否在角色视野方向
  358. /// </summary>
  359. public bool IsPositionInForward(Vector2 pos)
  360. {
  361. var gps = GlobalPosition;
  362. return (Face == FaceDirection.Left && pos.X <= gps.X) ||
  363. (Face == FaceDirection.Right && pos.X >= gps.X);
  364. }
  365.  
  366. /// <summary>
  367. /// 返回所有武器是否弹药都打光了
  368. /// </summary>
  369. public bool IsAllWeaponTotalAmmoEmpty()
  370. {
  371. foreach (var weapon in Holster.Weapons)
  372. {
  373. if (weapon != null && !weapon.IsTotalAmmoEmpty())
  374. {
  375. return false;
  376. }
  377. }
  378.  
  379. return true;
  380. }
  381. /// <summary>
  382. /// 拾起一个武器, 返回是否成功拾取, 如果不想立刻切换到该武器, exchange 请传 false
  383. /// </summary>
  384. /// <param name="weapon">武器对象</param>
  385. /// <param name="exchange">是否立即切换到该武器, 默认 true </param>
  386. public virtual bool PickUpWeapon(Weapon weapon, bool exchange = true)
  387. {
  388. if (Holster.PickupWeapon(weapon, exchange) != -1)
  389. {
  390. //从可互动队列中移除
  391. _interactiveItemList.Remove(weapon);
  392. return true;
  393. }
  394.  
  395. return false;
  396. }
  397.  
  398. /// <summary>
  399. /// 切换到下一个武器
  400. /// </summary>
  401. public virtual void ExchangeNext()
  402. {
  403. Holster.ExchangeNext();
  404. }
  405.  
  406. /// <summary>
  407. /// 切换到上一个武器
  408. /// </summary>
  409. public virtual void ExchangePrev()
  410. {
  411. Holster.ExchangePrev();
  412. }
  413.  
  414. /// <summary>
  415. /// 扔掉当前使用的武器, 切换到上一个武器
  416. /// </summary>
  417. public virtual void ThrowWeapon()
  418. {
  419. ThrowWeapon(Holster.ActiveIndex);
  420. }
  421.  
  422. /// <summary>
  423. /// 扔掉指定位置的武器
  424. /// </summary>
  425. /// <param name="index">武器在武器袋中的位置</param>
  426. public virtual void ThrowWeapon(int index)
  427. {
  428. var weapon = Holster.GetWeapon(index);
  429. if (weapon == null)
  430. {
  431. return;
  432. }
  433.  
  434. var temp = weapon.AnimatedSprite.Position;
  435. if (Face == FaceDirection.Left)
  436. {
  437. temp.Y = -temp.Y;
  438. }
  439. var pos = GlobalPosition + temp.Rotated(weapon.GlobalRotation);
  440. Holster.RemoveWeapon(index);
  441. //播放抛出效果
  442. weapon.ThrowWeapon(this, GlobalPosition);
  443. }
  444.  
  445. /// <summary>
  446. /// 返回是否存在可互动的物体
  447. /// </summary>
  448. public bool HasInteractive()
  449. {
  450. return InteractiveItem != null;
  451. }
  452.  
  453. /// <summary>
  454. /// 触发与碰撞的物体互动, 并返回与其互动的物体
  455. /// </summary>
  456. public ActivityObject TriggerInteractive()
  457. {
  458. if (HasInteractive())
  459. {
  460. var item = InteractiveItem;
  461. item.Interactive(this);
  462. return item;
  463. }
  464.  
  465. return null;
  466. }
  467.  
  468. /// <summary>
  469. /// 触发换弹
  470. /// </summary>
  471. public virtual void Reload()
  472. {
  473. if (Holster.ActiveWeapon != null)
  474. {
  475. Holster.ActiveWeapon.Reload();
  476. }
  477. }
  478.  
  479. /// <summary>
  480. /// 触发攻击
  481. /// </summary>
  482. public virtual void Attack()
  483. {
  484. if (Holster.ActiveWeapon != null)
  485. {
  486. Holster.ActiveWeapon.Trigger();
  487. }
  488. }
  489.  
  490. /// <summary>
  491. /// 受到伤害, 如果是在碰撞信号处理函数中调用该函数, 请使用 CallDeferred 来延时调用, 否则很有可能导致报错
  492. /// </summary>
  493. /// <param name="damage">伤害的量</param>
  494. /// <param name="angle">角度</param>
  495. public virtual void Hurt(int damage, float angle)
  496. {
  497. OnHit(damage);
  498. if (Shield > 0)
  499. {
  500. Shield -= damage;
  501. }
  502. else
  503. {
  504. Hp -= damage;
  505. //播放血液效果
  506. // var packedScene = ResourceManager.Load<PackedScene>(ResourcePath.prefab_effect_Blood_tscn);
  507. // var blood = packedScene.Instance<Blood>();
  508. // blood.GlobalPosition = GlobalPosition;
  509. // blood.Rotation = angle;
  510. // GameApplication.Instance.Node3D.GetRoot().AddChild(blood);
  511. }
  512. //受伤特效
  513. PlayHitAnimation();
  514. //死亡判定
  515. if (Hp <= 0)
  516. {
  517. //死亡
  518. if (!IsDie)
  519. {
  520. IsDie = true;
  521. OnDie();
  522. }
  523. }
  524. }
  525.  
  526. /// <summary>
  527. /// 设置脸的朝向
  528. /// </summary>
  529. private void SetFace(FaceDirection face)
  530. {
  531. if (_face != face)
  532. {
  533. _face = face;
  534. if (face == FaceDirection.Right)
  535. {
  536. RotationDegrees = 0;
  537. Scale = _startScale;
  538. }
  539. else
  540. {
  541. RotationDegrees = 180;
  542. Scale = new Vector2(_startScale.X, -_startScale.Y);
  543. }
  544. }
  545. }
  546. /// <summary>
  547. /// 连接信号: InteractiveArea.BodyEntered
  548. /// 与物体碰撞
  549. /// </summary>
  550. private void _OnPropsEnter(Node2D other)
  551. {
  552. if (other is ActivityObject propObject && !propObject.CollisionWithMask(PhysicsLayer.InHand))
  553. {
  554. if (!_interactiveItemList.Contains(propObject))
  555. {
  556. _interactiveItemList.Add(propObject);
  557. }
  558. }
  559. }
  560.  
  561. /// <summary>
  562. /// 连接信号: InteractiveArea.BodyExited
  563. /// 物体离开碰撞区域
  564. /// </summary>
  565. private void _OnPropsExit(Node2D other)
  566. {
  567. if (other is ActivityObject propObject)
  568. {
  569. if (_interactiveItemList.Contains(propObject))
  570. {
  571. _interactiveItemList.Remove(propObject);
  572. }
  573. if (InteractiveItem == propObject)
  574. {
  575. InteractiveItem = null;
  576. ChangeInteractiveItem(null);
  577. }
  578. }
  579. }
  580. }