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