Newer
Older
DungeonShooting / DungeonShooting_Godot / src / game / activity / role / AdvancedRole.cs
  1. using Godot;
  2.  
  3. /// <summary>
  4. /// 高级角色,可以拾起和使用武器
  5. /// </summary>
  6. public abstract partial class AdvancedRole : Role
  7. {
  8. /// <summary>
  9. /// 角色携带的武器背包
  10. /// </summary>
  11. public Package<Weapon, AdvancedRole> WeaponPack { get; private set; }
  12.  
  13. /// <summary>
  14. /// 武器挂载点
  15. /// </summary>
  16. [Export, ExportFillNode]
  17. public MountRotation MountPoint { get; set; }
  18. /// <summary>
  19. /// 背后武器的挂载点
  20. /// </summary>
  21. [Export, ExportFillNode]
  22. public Marker2D BackMountPoint { get; set; }
  23. /// <summary>
  24. /// 近战碰撞检测区域
  25. /// </summary>
  26. [Export, ExportFillNode]
  27. public Area2D MeleeAttackArea { get; set; }
  28. /// <summary>
  29. /// 近战碰撞检测区域的碰撞器
  30. /// </summary>
  31. [Export, ExportFillNode]
  32. public CollisionPolygon2D MeleeAttackCollision { get; set; }
  33.  
  34. /// <summary>
  35. /// 近战攻击时挥动武器的角度
  36. /// </summary>
  37. [Export]
  38. public float MeleeAttackAngle { get; set; } = 120;
  39.  
  40. /// <summary>
  41. /// 武器挂载点是否始终指向目标
  42. /// </summary>
  43. public bool MountLookTarget { get; set; } = true;
  44.  
  45. /// <summary>
  46. /// 是否处于近战攻击中
  47. /// </summary>
  48. public bool IsMeleeAttack { get; private set; }
  49. /// <summary>
  50. /// 当前角色所看向的对象, 也就是枪口指向的对象
  51. /// </summary>
  52. public ActivityObject LookTarget { get; set; }
  53. //近战计时器
  54. private float _meleeAttackTimer = 0;
  55.  
  56. /// <summary>
  57. /// 当拾起某个武器时调用
  58. /// </summary>
  59. protected virtual void OnPickUpWeapon(Weapon weapon)
  60. {
  61. }
  62. /// <summary>
  63. /// 当扔掉某个武器时调用
  64. /// </summary>
  65. protected virtual void OnThrowWeapon(Weapon weapon)
  66. {
  67. }
  68.  
  69. /// <summary>
  70. /// 当切换到某个武器时调用
  71. /// </summary>
  72. protected virtual void OnExchangeWeapon(Weapon weapon)
  73. {
  74. }
  75.  
  76. public override void OnInit()
  77. {
  78. base.OnInit();
  79. WeaponPack = AddComponent<Package<Weapon, AdvancedRole>>();
  80. WeaponPack.SetCapacity(4);
  81. MountPoint.Master = this;
  82. MeleeAttackCollision.Disabled = true;
  83. //切换武器回调
  84. WeaponPack.ChangeActiveItemEvent += OnChangeActiveItem;
  85. //近战区域进入物体
  86. MeleeAttackArea.BodyEntered += OnMeleeAttackBodyEntered;
  87. }
  88.  
  89. protected override void Process(float delta)
  90. {
  91. if (IsDie)
  92. {
  93. return;
  94. }
  95.  
  96. if (_meleeAttackTimer > 0)
  97. {
  98. _meleeAttackTimer -= delta;
  99. }
  100. //看向目标
  101. if (LookTarget != null && MountLookTarget)
  102. {
  103. var pos = LookTarget.Position;
  104. //脸的朝向
  105. var gPos = Position;
  106. if (pos.X > gPos.X && Face == FaceDirection.Left)
  107. {
  108. Face = FaceDirection.Right;
  109. }
  110. else if (pos.X < gPos.X && Face == FaceDirection.Right)
  111. {
  112. Face = FaceDirection.Left;
  113. }
  114. //枪口跟随目标
  115. MountPoint.SetLookAt(pos);
  116. }
  117. base.Process(delta);
  118. }
  119. /// <summary>
  120. /// 当武器放到后背时调用, 用于设置武器位置和角度
  121. /// </summary>
  122. /// <param name="weapon">武器实例</param>
  123. /// <param name="index">放入武器背包的位置</param>
  124. public virtual void OnPutBackMount(Weapon weapon, int index)
  125. {
  126. if (index < 8)
  127. {
  128. if (index % 2 == 0)
  129. {
  130. weapon.Position = new Vector2(-4, 3);
  131. weapon.RotationDegrees = 90 - (index / 2f) * 20;
  132. weapon.Scale = new Vector2(-1, 1);
  133. }
  134. else
  135. {
  136. weapon.Position = new Vector2(4, 3);
  137. weapon.RotationDegrees = 270 + (index - 1) / 2f * 20;
  138. weapon.Scale = new Vector2(1, 1);
  139. }
  140. }
  141. else
  142. {
  143. weapon.Visible = false;
  144. }
  145. }
  146. protected override void OnAffiliationChange(AffiliationArea prevArea)
  147. {
  148. //身上的武器的所属区域也得跟着变
  149. WeaponPack.ForEach((weapon, i) =>
  150. {
  151. if (AffiliationArea != null)
  152. {
  153. AffiliationArea.InsertItem(weapon);
  154. }
  155. else if (weapon.AffiliationArea != null)
  156. {
  157. weapon.AffiliationArea.RemoveItem(weapon);
  158. }
  159. });
  160. }
  161. public override Vector2 GetCenterPosition()
  162. {
  163. return MountPoint.GlobalPosition;
  164. }
  165. public override void LookTargetPosition(Vector2 pos)
  166. {
  167. LookTarget = null;
  168. if (MountLookTarget)
  169. {
  170. //脸的朝向
  171. var gPos = GlobalPosition;
  172. if (pos.X > gPos.X && Face == FaceDirection.Left)
  173. {
  174. Face = FaceDirection.Right;
  175. }
  176. else if (pos.X < gPos.X && Face == FaceDirection.Right)
  177. {
  178. Face = FaceDirection.Left;
  179. }
  180. //枪口跟随目标
  181. MountPoint.SetLookAt(pos);
  182. }
  183. }
  184.  
  185. /// <summary>
  186. /// 返回所有武器是否弹药都打光了
  187. /// </summary>
  188. public bool IsAllWeaponTotalAmmoEmpty()
  189. {
  190. foreach (var weapon in WeaponPack.ItemSlot)
  191. {
  192. if (weapon != null && !weapon.IsTotalAmmoEmpty())
  193. {
  194. return false;
  195. }
  196. }
  197.  
  198. return true;
  199. }
  200. //-------------------------------------------------------------------------------------
  201. /// <summary>
  202. /// 拾起一个武器, 返回是否成功拾起, 如果不想立刻切换到该武器, exchange 请传 false
  203. /// </summary>
  204. /// <param name="weapon">武器对象</param>
  205. /// <param name="exchange">是否立即切换到该武器, 默认 true </param>
  206. public bool PickUpWeapon(Weapon weapon, bool exchange = true)
  207. {
  208. if (WeaponPack.PickupItem(weapon, exchange) != -1)
  209. {
  210. //从可互动队列中移除
  211. InteractiveItemList.Remove(weapon);
  212. OnPickUpWeapon(weapon);
  213. return true;
  214. }
  215.  
  216. return false;
  217. }
  218.  
  219. /// <summary>
  220. /// 切换到下一个武器
  221. /// </summary>
  222. public void ExchangeNextWeapon()
  223. {
  224. var weapon = WeaponPack.ActiveItem;
  225. WeaponPack.ExchangeNext();
  226. if (WeaponPack.ActiveItem != weapon)
  227. {
  228. OnExchangeWeapon(WeaponPack.ActiveItem);
  229. }
  230. }
  231.  
  232. /// <summary>
  233. /// 切换到上一个武器
  234. /// </summary>
  235. public void ExchangePrevWeapon()
  236. {
  237. var weapon = WeaponPack.ActiveItem;
  238. WeaponPack.ExchangePrev();
  239. if (WeaponPack.ActiveItem != weapon)
  240. {
  241. OnExchangeWeapon(WeaponPack.ActiveItem);
  242. }
  243. }
  244.  
  245. /// <summary>
  246. /// 扔掉当前使用的武器, 切换到上一个武器
  247. /// </summary>
  248. public void ThrowWeapon()
  249. {
  250. ThrowWeapon(WeaponPack.ActiveIndex);
  251. }
  252.  
  253. /// <summary>
  254. /// 扔掉指定位置的武器
  255. /// </summary>
  256. /// <param name="index">武器在武器背包中的位置</param>
  257. public void ThrowWeapon(int index)
  258. {
  259. var weapon = WeaponPack.GetItem(index);
  260. if (weapon == null)
  261. {
  262. return;
  263. }
  264.  
  265. var temp = weapon.AnimatedSprite.Position;
  266. if (Face == FaceDirection.Left)
  267. {
  268. temp.Y = -temp.Y;
  269. }
  270. //var pos = GlobalPosition + temp.Rotated(weapon.GlobalRotation);
  271. WeaponPack.RemoveItem(index);
  272. //播放抛出效果
  273. weapon.ThrowWeapon(this, GlobalPosition);
  274. }
  275. /// <summary>
  276. /// 切换到下一个武器
  277. /// </summary>
  278. public void ExchangeNextActiveProp()
  279. {
  280. var prop = ActivePropsPack.ActiveItem;
  281. ActivePropsPack.ExchangeNext();
  282. if (prop != ActivePropsPack.ActiveItem)
  283. {
  284. OnExchangeActiveProp(ActivePropsPack.ActiveItem);
  285. }
  286. }
  287.  
  288. /// <summary>
  289. /// 切换到上一个武器
  290. /// </summary>
  291. public void ExchangePrevActiveProp()
  292. {
  293. var prop = ActivePropsPack.ActiveItem;
  294. ActivePropsPack.ExchangePrev();
  295. if (prop != ActivePropsPack.ActiveItem)
  296. {
  297. OnExchangeActiveProp(ActivePropsPack.ActiveItem);
  298. }
  299. }
  300.  
  301. //-------------------------------------------------------------------------------------
  302.  
  303. /// <summary>
  304. /// 触发换弹
  305. /// </summary>
  306. public virtual void Reload()
  307. {
  308. if (WeaponPack.ActiveItem != null)
  309. {
  310. WeaponPack.ActiveItem.Reload();
  311. }
  312. }
  313. public override void Attack()
  314. {
  315. if (!IsMeleeAttack && WeaponPack.ActiveItem != null)
  316. {
  317. WeaponPack.ActiveItem.Trigger(this);
  318. }
  319. }
  320.  
  321. /// <summary>
  322. /// 触发近战攻击
  323. /// </summary>
  324. public virtual void MeleeAttack()
  325. {
  326. if (IsMeleeAttack || _meleeAttackTimer > 0)
  327. {
  328. return;
  329. }
  330.  
  331. if (WeaponPack.ActiveItem != null && WeaponPack.ActiveItem.Attribute.CanMeleeAttack)
  332. {
  333. IsMeleeAttack = true;
  334. _meleeAttackTimer = RoleState.MeleeAttackTime;
  335. MountLookTarget = false;
  336. //WeaponPack.ActiveItem.TriggerMeleeAttack(this);
  337. //播放近战动画
  338. PlayAnimation_MeleeAttack(() =>
  339. {
  340. MountLookTarget = true;
  341. IsMeleeAttack = false;
  342. });
  343. }
  344. }
  345.  
  346. /// <summary>
  347. /// 切换当前使用的武器的回调
  348. /// </summary>
  349. private void OnChangeActiveItem(Weapon weapon)
  350. {
  351. //这里处理近战区域
  352. if (weapon != null)
  353. {
  354. MeleeAttackCollision.Polygon = Utils.CreateSectorPolygon(
  355. Utils.ConvertAngle(-MeleeAttackAngle / 2f),
  356. (weapon.GetLocalFirePosition() - weapon.GripPoint.Position).Length() * 1.2f,
  357. MeleeAttackAngle,
  358. 6
  359. );
  360. MeleeAttackArea.CollisionMask = AttackLayer | PhysicsLayer.Bullet;
  361. }
  362. }
  363.  
  364. /// <summary>
  365. /// 近战区域碰到敌人
  366. /// </summary>
  367. private void OnMeleeAttackBodyEntered(Node2D body)
  368. {
  369. var activeWeapon = WeaponPack.ActiveItem;
  370. if (activeWeapon == null)
  371. {
  372. return;
  373. }
  374. var activityObject = body.AsActivityObject();
  375. if (activityObject != null)
  376. {
  377. if (activityObject is AdvancedRole role) //攻击角色
  378. {
  379. var damage = Utils.Random.RandomConfigRange(activeWeapon.Attribute.MeleeAttackHarmRange);
  380. damage = RoleState.CalcDamage(damage);
  381. //击退
  382. if (role is not Player) //目标不是玩家才会触发击退
  383. {
  384. var attr = IsAi ? activeWeapon.AiUseAttribute : activeWeapon.PlayerUseAttribute;
  385. var repel = Utils.Random.RandomConfigRange(attr.MeleeAttackRepelRange);
  386. var position = role.GlobalPosition - MountPoint.GlobalPosition;
  387. var v2 = position.Normalized() * repel;
  388. role.MoveController.AddForce(v2);
  389. }
  390. role.CallDeferred(nameof(Hurt), damage, (role.GetCenterPosition() - GlobalPosition).Angle());
  391. }
  392. else if (activityObject is Bullet bullet) //攻击子弹
  393. {
  394. var attackLayer = bullet.AttackLayer;
  395. if (CollisionWithMask(attackLayer)) //是攻击玩家的子弹
  396. {
  397. bullet.PlayDisappearEffect();
  398. bullet.Destroy();
  399. }
  400. }
  401. }
  402. }
  403.  
  404. protected override void OnDestroy()
  405. {
  406. base.OnDestroy();
  407. WeaponPack.Destroy();
  408. }
  409. }