Newer
Older
DungeonShooting / DungeonShooting_Godot / src / game / activity / weapon / Weapon.cs
@小李xl 小李xl on 26 Jan 2024 62 KB 0010武器制作中
  1. using Godot;
  2. using System;
  3. using System.Collections.Generic;
  4. using Config;
  5.  
  6. /// <summary>
  7. /// 武器的基类
  8. /// </summary>
  9. public abstract partial class Weapon : ActivityObject, IPackageItem<Role>
  10. {
  11. /// <summary>
  12. /// 武器使用的属性数据, 该属性会根据是否是玩家使用武器, 如果是Ai使用武器, 则会返回 AiUseAttribute 的属性对象
  13. /// </summary>
  14. public ExcelConfig.WeaponBase Attribute => _weaponAttribute;
  15.  
  16. /// <summary>
  17. /// Ai使用该武器的属性
  18. /// </summary>
  19. public ExcelConfig.WeaponBase AiUseAttribute => _aiWeaponAttribute;
  20.  
  21. /// <summary>
  22. /// 玩家使用该武器的属性
  23. /// </summary>
  24. public ExcelConfig.WeaponBase PlayerUseAttribute => _playerWeaponAttribute;
  25. private ExcelConfig.WeaponBase _weaponAttribute;
  26. private ExcelConfig.WeaponBase _playerWeaponAttribute;
  27. private ExcelConfig.WeaponBase _aiWeaponAttribute;
  28.  
  29. /// <summary>
  30. /// 武器攻击的目标阵营
  31. /// </summary>
  32. public CampEnum TargetCamp { get; set; }
  33.  
  34. public Role Master { get; set; }
  35.  
  36. public int PackageIndex { get; set; } = -1;
  37.  
  38. /// <summary>
  39. /// 当前弹夹弹药剩余量
  40. /// </summary>
  41. public int CurrAmmo { get; private set; }
  42.  
  43. /// <summary>
  44. /// 剩余弹药量(备用弹药)
  45. /// </summary>
  46. public int ResidueAmmo { get; private set; }
  47.  
  48. /// <summary>
  49. /// 武器管的开火点
  50. /// </summary>
  51. [Export, ExportFillNode]
  52. public Marker2D FirePoint { get; set; }
  53.  
  54. /// <summary>
  55. /// 弹壳抛出的点
  56. /// </summary>
  57. [Export, ExportFillNode]
  58. public Marker2D ShellPoint { get; set; }
  59. /// <summary>
  60. /// 武器的当前散射半径
  61. /// </summary>
  62. public float CurrScatteringRange { get; private set; } = 0;
  63.  
  64. /// <summary>
  65. /// 是否在换弹中
  66. /// </summary>
  67. /// <value></value>
  68. public bool Reloading { get; private set; } = false;
  69.  
  70. /// <summary>
  71. /// 换弹进度 (从 0 到 1)
  72. /// </summary>
  73. public float ReloadProgress
  74. {
  75. get
  76. {
  77. if (!Reloading)
  78. {
  79. return 1;
  80. }
  81.  
  82. if (Attribute.AloneReload)
  83. {
  84. //总时间
  85. var total = Attribute.AloneReloadBeginIntervalTime + (Attribute.ReloadTime * Attribute.AmmoCapacity) + Attribute.AloneReloadFinishIntervalTime;
  86. //当前时间
  87. float current;
  88. if (_aloneReloadState == 1)
  89. {
  90. current = (Attribute.AloneReloadBeginIntervalTime - _reloadTimer) + Attribute.ReloadTime * CurrAmmo;
  91. }
  92. else if (_aloneReloadState == 2)
  93. {
  94. current = Attribute.AloneReloadBeginIntervalTime + (Attribute.ReloadTime * (CurrAmmo + (1 - _reloadTimer / Attribute.ReloadTime)));
  95. }
  96. else
  97. {
  98. current = Attribute.AloneReloadBeginIntervalTime + (Attribute.ReloadTime * CurrAmmo) + (Attribute.AloneReloadFinishIntervalTime - _reloadTimer);
  99. }
  100.  
  101. return current / total;
  102. }
  103.  
  104. return 1 - _reloadTimer / Attribute.ReloadTime;
  105. }
  106. }
  107.  
  108. /// <summary>
  109. /// 返回是否在蓄力中,
  110. /// 注意, 属性仅在 Attribute.LooseShoot == false 时有正确的返回值, 否则返回 false
  111. /// </summary>
  112. public bool IsCharging => _looseShootFlag;
  113.  
  114. /// <summary>
  115. /// 返回武器是否在武器背包中
  116. /// </summary>
  117. public bool IsInHolster => Master != null;
  118.  
  119. /// <summary>
  120. /// 返回是否真正使用该武器
  121. /// </summary>
  122. public bool IsActive => Master != null && Master.WeaponPack.ActiveItem == this;
  123. /// <summary>
  124. /// 动画播放器
  125. /// </summary>
  126. [Export, ExportFillNode]
  127. public AnimationPlayer AnimationPlayer { get; set; }
  128.  
  129. /// <summary>
  130. /// 是否自动播放 SpriteFrames 的动画
  131. /// </summary>
  132. public bool IsAutoPlaySpriteFrames { get; set; } = true;
  133.  
  134. /// <summary>
  135. /// 在没有所属 Master 的时候是否可以触发扳机
  136. /// </summary>
  137. public bool NoMasterCanTrigger { get; set; } = true;
  138. /// <summary>
  139. /// 上一次触发改武器开火的角色, 可能为 null
  140. /// </summary>
  141. public Role TriggerRole { get; private set; }
  142. /// <summary>
  143. /// 上一次触发改武器开火的触发开火攻击的层级, 数据源自于: <see cref="PhysicsLayer"/>
  144. /// </summary>
  145. public long TriggerRoleAttackLayer { get; private set; }
  146. /// <summary>
  147. /// 武器当前射速
  148. /// </summary>
  149. public float CurrentFiringSpeed { get; private set; }
  150. //--------------------------------------------------------------------------------------------
  151.  
  152. //用于记录是否有角色操作过这把武器
  153. private bool _triggerRoleFlag = false;
  154. //是否按下
  155. private bool _triggerFlag = false;
  156.  
  157. //扳机计时器
  158. private float _triggerTimer = 0;
  159.  
  160. //开火前延时时间
  161. private float _delayedTime = 0;
  162.  
  163. //开火间隙时间
  164. private float _fireInterval = 0;
  165.  
  166. //开火武器口角度
  167. private float _fireAngle = 0;
  168.  
  169. //攻击冷却计时
  170. private float _attackTimer = 0;
  171.  
  172. //攻击状态
  173. private bool _attackFlag = false;
  174. //多久没开火了
  175. private float _noAttackTime = 0;
  176.  
  177. //按下的时间
  178. private float _downTimer = 0;
  179.  
  180. //松开的时间
  181. private float _upTimer = 0;
  182.  
  183. //连发次数
  184. private int _continuousCount = 0;
  185.  
  186. //连发状态记录
  187. private bool _continuousShootFlag = false;
  188.  
  189. //松开扳机是否开火
  190. private bool _looseShootFlag = false;
  191.  
  192. //蓄力攻击时长
  193. private float _chargeTime = 0;
  194.  
  195. //是否需要重置武器数据
  196. private bool _dirtyFlag = false;
  197.  
  198. //当前后坐力导致的偏移长度
  199. private float _currBacklashLength = 0;
  200.  
  201. //握把位置
  202. private Vector2 _gripPoint;
  203.  
  204. //持握时 Sprite 偏移
  205. private Vector2 _gripOffset;
  206.  
  207. //碰撞器位置
  208. private Vector2 _collPoint;
  209.  
  210. //换弹计时器
  211. private float _reloadTimer = 0;
  212. //单独换弹设置下的换弹状态, 0: 未换弹, 1: 装第一颗子弹之前, 2: 单独装弹中, 3: 单独装弹完成
  213. private byte _aloneReloadState = 3;
  214.  
  215. //单独换弹状态下是否强制结束换弹过程
  216. private bool _aloneReloadStop = false;
  217. //本次换弹已用时间
  218. private float _reloadUseTime = 0;
  219.  
  220. //是否播放过换弹完成音效
  221. private bool _playReloadFinishSoundFlag = false;
  222.  
  223. //上膛状态,-1: 等待执行自动上膛 , 0: 未上膛, 1: 上膛中, 2: 已经完成上膛
  224. private sbyte _beLoadedState = 2;
  225.  
  226. //上膛计时器
  227. private float _beLoadedStateTimer = -1;
  228.  
  229. //换弹投抛弹壳记录
  230. private bool _reloadShellFlag = false;
  231.  
  232. // ----------------------------------------------
  233. private uint _tempLayer;
  234.  
  235. private static bool _init = false;
  236. private static Dictionary<string, ExcelConfig.WeaponBase> _weaponAttributeMap =
  237. new Dictionary<string, ExcelConfig.WeaponBase>();
  238.  
  239. /// <summary>
  240. /// 初始化武器属性数据
  241. /// </summary>
  242. public static void InitWeaponAttribute()
  243. {
  244. if (_init)
  245. {
  246. return;
  247. }
  248.  
  249. _init = true;
  250. foreach (var weaponAttr in ExcelConfig.WeaponBase_List)
  251. {
  252. if (weaponAttr.Activity != null)
  253. {
  254. if (!_weaponAttributeMap.TryAdd(weaponAttr.Activity.Id, weaponAttr))
  255. {
  256. Debug.LogError("发现重复注册的武器属性: " + weaponAttr.Id);
  257. }
  258. }
  259. }
  260. }
  261. /// <summary>
  262. /// 根据 ActivityBase.Id 获取对应武器的属性数据
  263. /// </summary>
  264. public static ExcelConfig.WeaponBase GetWeaponAttribute(string itemId)
  265. {
  266. if (itemId == null)
  267. {
  268. return null;
  269. }
  270. if (_weaponAttributeMap.TryGetValue(itemId, out var attr))
  271. {
  272. return attr;
  273. }
  274.  
  275. throw new Exception($"武器'{itemId}'没有在 WeaponBase 表中配置属性数据!");
  276. }
  277. public override void OnInit()
  278. {
  279. InitWeapon(GetWeaponAttribute(ActivityBase.Id).Clone());
  280. AnimatedSprite.AnimationFinished += OnAnimatedSpriteFinished;
  281. AnimationPlayer.AnimationFinished += OnAnimationPlayerFinished;
  282. _gripPoint = AnimatedSprite.Position;
  283. _gripOffset = AnimatedSprite.Offset;
  284. _collPoint = Collision.Position;
  285. AnimatedSprite.Position = Vector2.Zero;
  286. AnimatedSprite.Offset = Vector2.Zero;
  287. Collision.Position = Vector2.Zero;
  288. }
  289.  
  290. /// <summary>
  291. /// 初始化武器属性
  292. /// </summary>
  293. private void InitWeapon(ExcelConfig.WeaponBase attribute)
  294. {
  295. _playerWeaponAttribute = attribute;
  296. SetCurrentWeaponAttribute(attribute);
  297. if (ExcelConfig.WeaponBase_Map.TryGetValue(attribute.Id + "_ai", out var aiAttr))
  298. {
  299. _aiWeaponAttribute = aiAttr;
  300. }
  301. else
  302. {
  303. Debug.LogError("警告: 未找到 AI 武器属性: " + attribute.Id);
  304. _aiWeaponAttribute = attribute;
  305. }
  306.  
  307. if (Attribute.AmmoCapacity > Attribute.MaxAmmoCapacity)
  308. {
  309. Attribute.AmmoCapacity = Attribute.MaxAmmoCapacity;
  310. Debug.LogError("弹夹的容量不能超过弹药上限, 武器id: " + ActivityBase.Id);
  311. }
  312. //弹药量
  313. CurrAmmo = Attribute.AmmoCapacity;
  314. //剩余弹药量
  315. ResidueAmmo = Mathf.Min(Attribute.StandbyAmmoCapacity + CurrAmmo, Attribute.MaxAmmoCapacity) - CurrAmmo;
  316. ThrowCollisionSize = attribute.ThrowCollisionSize.AsVector2();
  317. }
  318.  
  319. /// <summary>
  320. /// 单次开火时调用的函数
  321. /// </summary>
  322. protected abstract void OnFire();
  323.  
  324. /// <summary>
  325. /// 发射子弹时调用的函数, 每发射一枚子弹调用一次,
  326. /// 如果做霰弹武器效果, 一次开火发射5枚子弹, 则该函数调用5次
  327. /// </summary>
  328. /// <param name="fireRotation">开火时枪口旋转角度, 弧度制</param>
  329. protected abstract void OnShoot(float fireRotation);
  330.  
  331. /// <summary>
  332. /// 上膛开始时调用
  333. /// </summary>
  334. protected virtual void OnBeginBeLoaded()
  335. {
  336. }
  337. /// <summary>
  338. /// 上膛结束时调用
  339. /// </summary>
  340. protected virtual void OnBeLoadedFinish()
  341. {
  342. }
  343. /// <summary>
  344. /// 当按下扳机时调用
  345. /// </summary>
  346. protected virtual void OnDownTrigger()
  347. {
  348. }
  349.  
  350. /// <summary>
  351. /// 当松开扳机时调用
  352. /// </summary>
  353. protected virtual void OnUpTrigger()
  354. {
  355. }
  356.  
  357. /// <summary>
  358. /// 开始蓄力时调用,
  359. /// 注意, 该函数仅在 Attribute.LooseShoot == false 时才能被调用
  360. /// </summary>
  361. protected virtual void OnBeginCharge()
  362. {
  363. }
  364.  
  365. /// <summary>
  366. /// 当换弹时调用, 如果设置单独装弹, 则每装一次弹调用一次该函数
  367. /// </summary>
  368. protected virtual void OnReload()
  369. {
  370. }
  371.  
  372. /// <summary>
  373. /// 当开始换弹时调用
  374. /// </summary>
  375. protected virtual void OnBeginReload()
  376. {
  377. }
  378. /// <summary>
  379. /// 当换弹完成时调用
  380. /// </summary>
  381. protected virtual void OnReloadFinish()
  382. {
  383. }
  384.  
  385. /// <summary>
  386. /// 单独装弹完成时调用
  387. /// </summary>
  388. protected virtual void OnAloneReloadStateFinish()
  389. {
  390. }
  391. /// <summary>
  392. /// 当武器被拾起时调用
  393. /// </summary>
  394. /// <param name="master">拾起该武器的角色</param>
  395. protected virtual void OnPickUp(Role master)
  396. {
  397. }
  398.  
  399. /// <summary>
  400. /// 当武器从武器背包中移除时调用
  401. /// </summary>
  402. /// <param name="master">移除该武器的角色</param>
  403. protected virtual void OnRemove(Role master)
  404. {
  405. }
  406.  
  407. /// <summary>
  408. /// 当武器被激活时调用, 也就是使用当武器时调用
  409. /// </summary>
  410. protected virtual void OnActive()
  411. {
  412. }
  413.  
  414. /// <summary>
  415. /// 当武器被收起时调用
  416. /// </summary>
  417. protected virtual void OnConceal()
  418. {
  419. }
  420.  
  421. /// <summary>
  422. /// 射击时调用, 返回消耗弹药数量, 默认为1, 如果返回为 0, 则不消耗弹药
  423. /// </summary>
  424. protected virtual int UseAmmoCount()
  425. {
  426. return 1;
  427. }
  428.  
  429. public override void EnterTree()
  430. {
  431. //收集落在地上的武器
  432. if (IsInGround())
  433. {
  434. World.Weapon_UnclaimedWeapons.Add(this);
  435. }
  436. }
  437.  
  438. public override void ExitTree()
  439. {
  440. World.Weapon_UnclaimedWeapons.Remove(this);
  441. }
  442.  
  443. protected override void Process(float delta)
  444. {
  445. //未开火时间
  446. _noAttackTime += delta;
  447. //这把武器被扔在地上, 或者当前武器没有被使用
  448. //if (Master == null || Master.WeaponPack.ActiveItem != this)
  449. if ((Master != null && Master.WeaponPack.ActiveItem != this) || !_triggerRoleFlag) //在背上, 或者被扔出去了
  450. {
  451. //_triggerTimer
  452. _triggerTimer = _triggerTimer > 0 ? _triggerTimer - delta : 0;
  453. //攻击冷却计时
  454. _attackTimer = _attackTimer > 0 ? _attackTimer - delta : 0;
  455. //武器的当前散射半径
  456. ScatteringRangeBackHandler(delta);
  457. //武器当前射速
  458. FiringSpeedBackHandler(delta);
  459. //松开扳机
  460. if (_triggerFlag || _downTimer > 0)
  461. {
  462. UpTrigger();
  463. _downTimer = 0;
  464. }
  465. _triggerFlag = false;
  466.  
  467. //重置数据
  468. if (_dirtyFlag)
  469. {
  470. _dirtyFlag = false;
  471. //_aloneReloadState = 0;
  472. StopReload();
  473. _attackFlag = false;
  474. _continuousCount = 0;
  475. _delayedTime = 0;
  476. _upTimer = 0;
  477. _looseShootFlag = false;
  478. _chargeTime = 0;
  479. _beLoadedStateTimer = -1;
  480. }
  481. }
  482. else //正在使用中
  483. {
  484. _dirtyFlag = true;
  485. //上膛
  486. if (_beLoadedState == 1)
  487. {
  488. _beLoadedStateTimer -= delta;
  489. //上膛完成
  490. if (_beLoadedStateTimer <= 0)
  491. {
  492. _beLoadedStateTimer = -1;
  493. _beLoadedState = 2;
  494. OnBeLoadedFinish();
  495. }
  496. }
  497. //换弹
  498. if (Reloading)
  499. {
  500. //换弹用时
  501. _reloadUseTime += delta;
  502. _reloadTimer -= delta;
  503.  
  504. if (Attribute.AloneReload) //单独装弹模式
  505. {
  506. switch (_aloneReloadState)
  507. {
  508. case 0:
  509. Debug.LogError("AloneReload状态错误!");
  510. break;
  511. case 1: //装第一颗子弹之前
  512. {
  513. if (_reloadTimer <= 0)
  514. {
  515. //开始装第一颗子弹
  516. _aloneReloadState = 2;
  517. ReloadHandler();
  518. }
  519. _aloneReloadStop = false;
  520. }
  521. break;
  522. case 2: //单独装弹中
  523. {
  524. if (_reloadTimer <= 0)
  525. {
  526. ReloadSuccess();
  527. if (_aloneReloadStop || ResidueAmmo == 0 || CurrAmmo == Attribute.AmmoCapacity) //单独装弹完成
  528. {
  529. AloneReloadStateFinish();
  530. if (Attribute.AloneReloadFinishIntervalTime <= 0)
  531. {
  532. //换弹完成
  533. StopReload();
  534. ReloadFinishHandler();
  535. }
  536. else
  537. {
  538. _reloadTimer = Attribute.AloneReloadFinishIntervalTime;
  539. _aloneReloadState = 3;
  540. }
  541. }
  542. }
  543. }
  544. break;
  545. case 3: //单独装弹完成
  546. {
  547. //播放换弹完成音效
  548. if (!_playReloadFinishSoundFlag && Attribute.ReloadFinishSound != null && _reloadTimer <= Attribute.ReloadFinishSoundAdvanceTime)
  549. {
  550. _playReloadFinishSoundFlag = true;
  551. // Debug.Log("播放换弹完成音效.");
  552. PlayReloadFinishSound();
  553. }
  554. if (_reloadTimer <= 0)
  555. {
  556. //换弹完成
  557. StopReload();
  558. ReloadFinishHandler();
  559. }
  560. _aloneReloadStop = false;
  561. }
  562. break;
  563. }
  564. }
  565. else //普通换弹模式
  566. {
  567. //播放换弹完成音效
  568. if (!_playReloadFinishSoundFlag && Attribute.ReloadFinishSound != null && _reloadTimer <= Attribute.ReloadFinishSoundAdvanceTime)
  569. {
  570. _playReloadFinishSoundFlag = true;
  571. // Debug.Log("播放换弹完成音效.");
  572. PlayReloadFinishSound();
  573. }
  574.  
  575. if (_reloadTimer <= 0)
  576. {
  577. ReloadSuccess();
  578. }
  579. }
  580. }
  581.  
  582. //攻击的计时器
  583. if (_attackTimer > 0)
  584. {
  585. _attackTimer -= delta;
  586. if (_attackTimer < 0)
  587. {
  588. _delayedTime += _attackTimer;
  589. _attackTimer = 0;
  590. //枪口默认角度
  591. if (Master != null)
  592. {
  593. RotationDegrees = -Attribute.DefaultAngle;
  594. }
  595.  
  596. //自动上膛
  597. if (_beLoadedState == -1)
  598. {
  599. BeLoaded();
  600. }
  601. }
  602. }
  603. else if (_delayedTime > 0) //攻击延时
  604. {
  605. _delayedTime -= delta;
  606. if (_attackTimer < 0)
  607. {
  608. _delayedTime = 0;
  609. }
  610. }
  611. //扳机判定
  612. if (_triggerFlag)
  613. {
  614. if (_looseShootFlag) //蓄力时长
  615. {
  616. _chargeTime += delta;
  617. }
  618.  
  619. _downTimer += delta;
  620. if (_upTimer > 0) //第一帧按下扳机
  621. {
  622. DownTrigger();
  623. _upTimer = 0;
  624. }
  625. }
  626. else
  627. {
  628. _upTimer += delta;
  629. if (_downTimer > 0) //第一帧松开扳机
  630. {
  631. UpTrigger();
  632. _downTimer = 0;
  633. }
  634. }
  635.  
  636. //连发判断
  637. if (!_looseShootFlag && _continuousCount > 0 && _delayedTime <= 0 && _attackTimer <= 0)
  638. {
  639. //连发开火
  640. TriggerFire();
  641. //连发最后一发打完了
  642. if (Attribute.ManualBeLoaded && _continuousCount <= 0)
  643. {
  644. //执行上膛逻辑
  645. RunBeLoaded();
  646. }
  647. }
  648.  
  649. //散射值消退
  650. if (_noAttackTime >= Attribute.ScatteringRangeBackDelayTime)
  651. {
  652. ScatteringRangeBackHandler(delta);
  653. }
  654.  
  655. if (_triggerFlag) //射速增加
  656. {
  657. FiringSpeedAddHandler(delta);
  658. }
  659. else if (_noAttackTime >= Attribute.FiringSpeedBackTime) //射速消退
  660. {
  661. FiringSpeedBackHandler(delta);
  662. }
  663.  
  664. _triggerTimer = _triggerTimer > 0 ? _triggerTimer - delta : 0;
  665. _triggerFlag = false;
  666. _attackFlag = false;
  667. //武器身回归
  668. //Position = Position.MoveToward(Vector2.Zero, Attribute.BacklashRegressionSpeed * delta).Rotated(Rotation);
  669. _currBacklashLength = Mathf.MoveToward(_currBacklashLength, 0, Attribute.BacklashRegressionSpeed * delta);
  670. if (Master != null)
  671. {
  672. Position = new Vector2(_currBacklashLength, 0).Rotated(Rotation);
  673. if (_attackTimer > 0)
  674. {
  675. RotationDegrees = Mathf.Lerp(
  676. _fireAngle, -Attribute.DefaultAngle,
  677. Mathf.Clamp((_fireInterval - _attackTimer) * Attribute.UpliftAngleRestore / _fireInterval, 0, 1)
  678. );
  679. }
  680. }
  681. }
  682. }
  683.  
  684. /// <summary>
  685. /// 返回武器是否在地上
  686. /// </summary>
  687. /// <returns></returns>
  688. public bool IsInGround()
  689. {
  690. return Master == null && GetParent() == GameApplication.Instance.World.NormalLayer;
  691. }
  692.  
  693. /// <summary>
  694. /// 扳机函数, 调用即视为按下扳机
  695. /// </summary>
  696. /// <param name="triggerRole">按下扳机的角色, 如果传 null, 则视为走火</param>
  697. public void Trigger(Role triggerRole)
  698. {
  699. //不能触发扳机
  700. if (!NoMasterCanTrigger && Master == null) return;
  701.  
  702. //这一帧已经按过了, 不需要再按下
  703. if (_triggerFlag) return;
  704.  
  705. //更新武器属性信息
  706. _triggerFlag = true;
  707. _triggerRoleFlag = true;
  708. if (triggerRole != null)
  709. {
  710. TriggerRole = triggerRole;
  711. TriggerRoleAttackLayer = triggerRole.AttackLayer;
  712. SetCurrentWeaponAttribute(triggerRole.IsAi ? _aiWeaponAttribute : _playerWeaponAttribute);
  713. }
  714. else if (Master != null)
  715. {
  716. TriggerRole = Master;
  717. TriggerRoleAttackLayer = Master.AttackLayer;
  718. SetCurrentWeaponAttribute(Master.IsAi ? _aiWeaponAttribute : _playerWeaponAttribute);
  719. }
  720.  
  721. //是否第一帧按下
  722. var justDown = _downTimer == 0;
  723. if (_beLoadedState == 0 || _beLoadedState == -1) //需要执行上膛操作
  724. {
  725. if (justDown && !Reloading && triggerRole != null)
  726. {
  727. if (CurrAmmo <= 0)
  728. {
  729. //子弹不够, 触发换弹
  730. Reload();
  731. }
  732. else if (_attackTimer <= 0)
  733. {
  734. //触发上膛操作
  735. BeLoaded();
  736. }
  737. }
  738. }
  739. else if (_beLoadedState == 1) //上膛中
  740. {
  741.  
  742. }
  743. else //上膛完成
  744. {
  745. //是否能发射
  746. var flag = false;
  747. if (_continuousCount <= 0) //不能处于连发状态下
  748. {
  749. if (Attribute.ContinuousShoot) //自动射击
  750. {
  751. if (_triggerTimer > 0)
  752. {
  753. if (_continuousShootFlag)
  754. {
  755. flag = true;
  756. }
  757. }
  758. else
  759. {
  760. flag = true;
  761. if (_delayedTime <= 0 && _attackTimer <= 0)
  762. {
  763. _continuousShootFlag = true;
  764. }
  765. }
  766. }
  767. else //半自动
  768. {
  769. if (justDown && _triggerTimer <= 0 && _attackTimer <= 0)
  770. {
  771. flag = true;
  772. }
  773. }
  774. }
  775.  
  776. if (flag)
  777. {
  778. var fireFlag = true; //是否能开火
  779. if (Reloading) //换弹中
  780. {
  781. fireFlag = false;
  782. if (CurrAmmo > 0 && Attribute.AloneReload && Attribute.AloneReloadCanShoot)
  783. {
  784. //检查是否允许停止换弹
  785. if (_aloneReloadState == 2 || _aloneReloadState == 1)
  786. {
  787. //强制结束
  788. _aloneReloadStop = true;
  789. }
  790. }
  791. }
  792. else if (CurrAmmo <= 0) //子弹不够
  793. {
  794. fireFlag = false;
  795. if (justDown && triggerRole != null)
  796. {
  797. //第一帧按下, 触发换弹
  798. Reload();
  799. }
  800. }
  801.  
  802. if (fireFlag)
  803. {
  804. if (justDown)
  805. {
  806. //开火前延时
  807. if (!Attribute.LooseShoot)
  808. {
  809. _delayedTime = Attribute.DelayedTime;
  810. }
  811.  
  812. //扳机按下间隔
  813. _triggerTimer = Attribute.TriggerInterval;
  814. //连发数量
  815. if (!Attribute.ContinuousShoot)
  816. {
  817. _continuousCount = Utils.Random.RandomConfigRange(Attribute.ContinuousCountRange);
  818. }
  819. }
  820.  
  821. if (_delayedTime <= 0 && _attackTimer <= 0)
  822. {
  823. if (Attribute.LooseShoot) //松发开火
  824. {
  825. _looseShootFlag = true;
  826. OnBeginCharge();
  827. }
  828. else
  829. {
  830. //开火
  831. TriggerFire();
  832.  
  833. //非连射模式
  834. if (!Attribute.ContinuousShoot && Attribute.ManualBeLoaded && _continuousCount <= 0)
  835. {
  836. //执行上膛逻辑
  837. RunBeLoaded();
  838. }
  839. }
  840. }
  841. }
  842. }
  843. else //不能开火
  844. {
  845. if (CurrAmmo <= 0 && justDown && triggerRole != null) //子弹不够
  846. {
  847. //第一帧按下, 触发换弹
  848. Reload();
  849. }
  850. }
  851. }
  852. }
  853.  
  854. /// <summary>
  855. /// 返回是否按下扳机
  856. /// </summary>
  857. public bool IsPressTrigger()
  858. {
  859. return _triggerFlag;
  860. }
  861. /// <summary>
  862. /// 获取本次扳机按下的时长, 单位: 秒
  863. /// </summary>
  864. public float GetTriggerDownTime()
  865. {
  866. return _downTimer;
  867. }
  868.  
  869. /// <summary>
  870. /// 获取扳机蓄力时长, 计算按下扳机后从可以开火到当前一共经过了多长时间, 可用于计算蓄力攻击
  871. /// 注意, 该函数仅在 Attribute.LooseShoot == false 时有正确的返回值, 否则返回 0
  872. /// </summary>
  873. public float GetTriggerChargeTime()
  874. {
  875. return _chargeTime;
  876. }
  877. /// <summary>
  878. /// 获取延时射击倒计时, 单位: 秒
  879. /// </summary>
  880. public float GetDelayedAttackTime()
  881. {
  882. return _delayedTime;
  883. }
  884.  
  885. /// <summary>
  886. /// 获取当前需要连发弹药的数量, 配置了 ContinuousCountRange 时生效
  887. /// </summary>
  888. public int GetContinuousCount()
  889. {
  890. return _continuousCount;
  891. }
  892.  
  893. /// <summary>
  894. /// 获取攻击冷却计时时间, 小于等于 0 表示冷却完成
  895. /// </summary>
  896. public float GetAttackTimer()
  897. {
  898. return _attackTimer;
  899. }
  900.  
  901. /// <summary>
  902. /// 返回是否是攻击间隙时间
  903. /// </summary>
  904. public bool IsAttackIntervalTime()
  905. {
  906. return _attackTimer > 0 || _triggerTimer > 0;
  907. }
  908.  
  909. /// <summary>
  910. /// 是否可以按下扳机并发射了
  911. /// </summary>
  912. public bool TriggerIsReady()
  913. {
  914. return GetBeLoadedStateState() >= 2 && !IsAttackIntervalTime();
  915. }
  916.  
  917. /// <summary>
  918. /// 获取上膛状态,-1: 等待执行自动上膛 , 0: 未上膛, 1: 上膛中, 2: 已经完成上膛
  919. /// </summary>
  920. public sbyte GetBeLoadedStateState()
  921. {
  922. return _beLoadedState;
  923. }
  924. /// <summary>
  925. /// 刚按下扳机
  926. /// </summary>
  927. private void DownTrigger()
  928. {
  929. OnDownTrigger();
  930. }
  931.  
  932. /// <summary>
  933. /// 刚松开扳机
  934. /// </summary>
  935. private void UpTrigger()
  936. {
  937. _continuousShootFlag = false;
  938. if (_delayedTime > 0)
  939. {
  940. _continuousCount = 0;
  941. }
  942.  
  943. //松发开火执行
  944. if (_looseShootFlag)
  945. {
  946. _looseShootFlag = false;
  947. if (_chargeTime >= Attribute.MinChargeTime) //判断蓄力是否够了
  948. {
  949. TriggerFire();
  950. //非连射模式
  951. if (!Attribute.ContinuousShoot && Attribute.ManualBeLoaded && _continuousCount <= 0)
  952. {
  953. //执行上膛逻辑
  954. RunBeLoaded();
  955. }
  956. }
  957. else //不能攻击
  958. {
  959. _continuousCount = 0;
  960. }
  961. _chargeTime = 0;
  962. }
  963.  
  964. OnUpTrigger();
  965. }
  966.  
  967. /// <summary>
  968. /// 触发开火
  969. /// </summary>
  970. private void TriggerFire()
  971. {
  972. _attackFlag = true;
  973. _noAttackTime = 0;
  974. _reloadShellFlag = false;
  975.  
  976. //减子弹数量
  977. if (_playerWeaponAttribute != _weaponAttribute) //Ai使用该武器, 有一定概率不消耗弹药
  978. {
  979. var count = UseAmmoCount();
  980. CurrAmmo -= count;
  981. if (Utils.Random.RandomRangeFloat(0, 1) >= _weaponAttribute.AiAttackAttr.AmmoConsumptionProbability) //不消耗弹药
  982. {
  983. ResidueAmmo += count;
  984. }
  985. }
  986. else
  987. {
  988. CurrAmmo -= UseAmmoCount();
  989. }
  990.  
  991. if (CurrAmmo == 0)
  992. {
  993. _continuousCount = 0;
  994. }
  995. else
  996. {
  997. _continuousCount = _continuousCount > 0 ? _continuousCount - 1 : 0;
  998. }
  999.  
  1000. //开火间隙, 这里的60指的是60秒
  1001. _fireInterval = 60 / CurrentFiringSpeed;
  1002. //攻击冷却
  1003. _attackTimer += _fireInterval;
  1004.  
  1005. //播放开火动画
  1006. if (IsAutoPlaySpriteFrames)
  1007. {
  1008. PlaySpriteAnimation(AnimatorNames.Fire);
  1009. PlayAnimationPlayer(AnimatorNames.Fire);
  1010. }
  1011.  
  1012. //播放射击音效
  1013. PlayShootSound();
  1014. //抛弹
  1015. if (!Attribute.ReloadThrowShell && (Attribute.ContinuousShoot || !Attribute.ManualBeLoaded))
  1016. {
  1017. ThrowShellHandler(1f);
  1018. }
  1019. //触发开火函数
  1020. OnFire();
  1021.  
  1022.  
  1023. //武器口角度
  1024. var angle = new Vector2(GameConfig.ScatteringDistance, CurrScatteringRange).Angle();
  1025.  
  1026. //先算武器口方向
  1027. var tempRotation = Utils.Random.RandomRangeFloat(-angle, angle);
  1028. var tempAngle = Mathf.RadToDeg(tempRotation);
  1029.  
  1030. //开火时枪口角度
  1031. var fireRotation = tempRotation;
  1032. //开火发射的子弹数量
  1033. var bulletCount = Utils.Random.RandomConfigRange(Attribute.FireBulletCountRange);
  1034. if (Master != null)
  1035. {
  1036. bulletCount = Master.RoleState.CalcBulletCount(bulletCount);
  1037. fireRotation += Master.MountPoint.RealRotation;
  1038. }
  1039. else
  1040. {
  1041. fireRotation += GlobalRotation;
  1042. }
  1043.  
  1044. //创建子弹
  1045. for (var i = 0; i < bulletCount; i++)
  1046. {
  1047. //发射子弹
  1048. OnShoot(fireRotation);
  1049. }
  1050.  
  1051. //开火添加散射值
  1052. ScatteringRangeAddHandler();
  1053. //武器的旋转角度
  1054. tempAngle -= Attribute.UpliftAngle;
  1055. _fireAngle = tempAngle;
  1056. if (Master != null) //被拾起
  1057. {
  1058. //武器身位置
  1059. var max = Mathf.Abs(Mathf.Max(Utils.GetConfigRangeStart(Attribute.BacklashRange), Utils.GetConfigRangeEnd(Attribute.BacklashRange)));
  1060. _currBacklashLength = Mathf.Clamp(
  1061. _currBacklashLength - Utils.Random.RandomConfigRange(Attribute.BacklashRange),
  1062. -max, max
  1063. );
  1064. Position = new Vector2(_currBacklashLength, 0).Rotated(Rotation);
  1065. RotationDegrees = tempAngle;
  1066. }
  1067. else //在地上
  1068. {
  1069. var v = Utils.Random.RandomConfigRange(Attribute.BacklashRange) * 5;
  1070. var externalForce = MoveController.AddForce(new Vector2(-v, 0).Rotated(Rotation));
  1071. externalForce.RotationSpeed = -Mathf.DegToRad(40);
  1072. externalForce.RotationResistance = Mathf.DegToRad(80);
  1073. }
  1074.  
  1075. if (IsInGround())
  1076. {
  1077. //在地上弹药打光
  1078. if (IsTotalAmmoEmpty())
  1079. {
  1080. //停止动画
  1081. AnimationPlayer.Stop();
  1082. //清除泛白效果
  1083. SetBlendSchedule(0);
  1084. }
  1085. }
  1086. }
  1087.  
  1088. // /// <summary>
  1089. // /// 触发武器的近战攻击
  1090. // /// </summary>
  1091. // public virtual void TriggerMeleeAttack(Role trigger)
  1092. // {
  1093. //
  1094. // }
  1095.  
  1096. /// <summary>
  1097. /// 根据触扳机的角色对象判断该角色使用的武器数据
  1098. /// </summary>
  1099. public ExcelConfig.WeaponBase GetUseAttribute(Role triggerRole)
  1100. {
  1101. if (triggerRole == null || !triggerRole.IsAi)
  1102. {
  1103. return PlayerUseAttribute;
  1104. }
  1105.  
  1106. return AiUseAttribute;
  1107. }
  1108. /// <summary>
  1109. /// 获取武器攻击的目标层级
  1110. /// </summary>
  1111. /// <returns></returns>
  1112. public uint GetAttackLayer()
  1113. {
  1114. if (TriggerRoleAttackLayer > 0)
  1115. {
  1116. return (uint)TriggerRoleAttackLayer;
  1117. }
  1118. return Master != null ? Master.AttackLayer : Role.DefaultAttackLayer;
  1119. }
  1120. /// <summary>
  1121. /// 返回弹药是否到达上限
  1122. /// </summary>
  1123. public bool IsAmmoFull()
  1124. {
  1125. return CurrAmmo + ResidueAmmo >= Attribute.MaxAmmoCapacity;
  1126. }
  1127.  
  1128. /// <summary>
  1129. /// 返回弹夹是否打空
  1130. /// </summary>
  1131. public bool IsAmmoEmpty()
  1132. {
  1133. return CurrAmmo == 0;
  1134. }
  1135. /// <summary>
  1136. /// 返回是否弹药耗尽
  1137. /// </summary>
  1138. public bool IsTotalAmmoEmpty()
  1139. {
  1140. return CurrAmmo + ResidueAmmo == 0;
  1141. }
  1142.  
  1143. /// <summary>
  1144. /// 强制修改当前弹夹弹药量
  1145. /// </summary>
  1146. public void SetCurrAmmo(int count)
  1147. {
  1148. CurrAmmo = Mathf.Clamp(count, 0, Attribute.AmmoCapacity);
  1149. }
  1150.  
  1151. /// <summary>
  1152. /// 强制修改备用弹药量
  1153. /// </summary>
  1154. public void SetResidueAmmo(int count)
  1155. {
  1156. ResidueAmmo = Mathf.Clamp(count, 0, Attribute.MaxAmmoCapacity - CurrAmmo);
  1157. }
  1158. /// <summary>
  1159. /// 强制修改弹药量, 优先改动备用弹药
  1160. /// </summary>
  1161. public void SetTotalAmmo(int total)
  1162. {
  1163. if (total < 0)
  1164. {
  1165. return;
  1166. }
  1167. var totalAmmo = CurrAmmo + ResidueAmmo;
  1168. if (totalAmmo == total)
  1169. {
  1170. return;
  1171. }
  1172. if (total > totalAmmo) //弹药增加
  1173. {
  1174. ResidueAmmo = Mathf.Min(total - CurrAmmo, Attribute.MaxAmmoCapacity - CurrAmmo);
  1175. }
  1176. else //弹药减少
  1177. {
  1178. if (CurrAmmo < total)
  1179. {
  1180. ResidueAmmo = total - CurrAmmo;
  1181. }
  1182. else
  1183. {
  1184. CurrAmmo = total;
  1185. ResidueAmmo = 0;
  1186. }
  1187. }
  1188. }
  1189.  
  1190. /// <summary>
  1191. /// 拾起的弹药数量, 如果到达容量上限, 则返回拾取完毕后剩余的弹药数量
  1192. /// </summary>
  1193. /// <param name="count">弹药数量</param>
  1194. private int PickUpAmmo(int count)
  1195. {
  1196. var num = ResidueAmmo;
  1197. ResidueAmmo = Mathf.Min(ResidueAmmo + count, Attribute.MaxAmmoCapacity - CurrAmmo);
  1198. return count - ResidueAmmo + num;
  1199. }
  1200.  
  1201. /// <summary>
  1202. /// 触发换弹
  1203. /// </summary>
  1204. public void Reload()
  1205. {
  1206. if (!Reloading && CurrAmmo < Attribute.AmmoCapacity && ResidueAmmo > 0 && _beLoadedState != 1)
  1207. {
  1208. Reloading = true;
  1209. _playReloadFinishSoundFlag = false;
  1210.  
  1211. //播放开始换弹音效
  1212. PlayBeginReloadSound();
  1213. // Debug.Log("开始换弹.");
  1214. //抛弹
  1215. if (!Attribute.ReloadThrowShell && !Attribute.ContinuousShoot &&
  1216. (_beLoadedState == 0 || _beLoadedState == -1) && Attribute.BeLoadedTime > 0)
  1217. {
  1218. ThrowShellHandler(0.6f);
  1219. }
  1220. //第一次换弹
  1221. OnBeginReload();
  1222.  
  1223. if (Attribute.AloneReload)
  1224. {
  1225. //单独换弹, 特殊处理
  1226. AloneReloadHandler();
  1227. }
  1228. else
  1229. {
  1230. //普通换弹处理
  1231. ReloadHandler();
  1232. }
  1233. //上膛标记完成
  1234. _beLoadedState = 2;
  1235. }
  1236. }
  1237.  
  1238. /// <summary>
  1239. /// 强制停止换弹, 或者结束换弹状态
  1240. /// </summary>
  1241. public void StopReload()
  1242. {
  1243. _aloneReloadState = 0;
  1244. if (_beLoadedState == -1)
  1245. {
  1246. _beLoadedState = 0;
  1247. }
  1248. else if (_beLoadedState == 1)
  1249. {
  1250. _beLoadedState = 2;
  1251. }
  1252.  
  1253. Reloading = false;
  1254. _reloadTimer = 0;
  1255. _reloadUseTime = 0;
  1256. }
  1257.  
  1258. /// <summary>
  1259. /// 触发上膛
  1260. /// </summary>
  1261. public void BeLoaded()
  1262. {
  1263. if (_beLoadedState > 0)
  1264. {
  1265. return;
  1266. }
  1267. //上膛抛弹
  1268. if (!Attribute.ReloadThrowShell && !Attribute.ContinuousShoot && Attribute.BeLoadedTime > 0)
  1269. {
  1270. ThrowShellHandler(0.6f);
  1271. }
  1272.  
  1273. //开始上膛
  1274. OnBeginBeLoaded();
  1275.  
  1276. //上膛时间为0, 直接结束上膛
  1277. if (Attribute.BeLoadedTime <= 0)
  1278. {
  1279. //直接上膛完成
  1280. _beLoadedState = 2;
  1281. OnBeLoadedFinish();
  1282. return;
  1283. }
  1284. //上膛中
  1285. _beLoadedState = 1;
  1286. _beLoadedStateTimer = Attribute.BeLoadedTime;
  1287. //播放上膛动画
  1288. if (IsAutoPlaySpriteFrames)
  1289. {
  1290. if (Attribute.BeLoadedSoundDelayTime <= 0)
  1291. {
  1292. PlaySpriteAnimation(AnimatorNames.BeLoaded);
  1293. PlayAnimationPlayer(AnimatorNames.BeLoaded);
  1294. }
  1295. else
  1296. {
  1297. this.CallDelay(Attribute.BeLoadedSoundDelayTime, () =>
  1298. {
  1299. PlaySpriteAnimation(AnimatorNames.BeLoaded);
  1300. PlayAnimationPlayer(AnimatorNames.BeLoaded);
  1301. });
  1302. }
  1303. }
  1304.  
  1305. //播放上膛音效
  1306. PlayBeLoadedSound();
  1307. }
  1308. //播放换弹开始音效
  1309. private void PlayBeginReloadSound()
  1310. {
  1311. if (Attribute.BeginReloadSound != null)
  1312. {
  1313. if (Attribute.BeginReloadSoundDelayTime <= 0)
  1314. {
  1315. SoundManager.PlaySoundByConfig(Attribute.BeginReloadSound, GlobalPosition, TriggerRole);
  1316. }
  1317. else
  1318. {
  1319. SoundManager.PlaySoundByConfigDelay(Attribute.BeginReloadSound, GlobalPosition, Attribute.BeginReloadSoundDelayTime, TriggerRole);
  1320. }
  1321. }
  1322. }
  1323. //播放换弹音效
  1324. private void PlayReloadSound()
  1325. {
  1326. if (Attribute.ReloadSound != null)
  1327. {
  1328. if (Attribute.ReloadSoundDelayTime <= 0)
  1329. {
  1330. SoundManager.PlaySoundByConfig(Attribute.ReloadSound, GlobalPosition, TriggerRole);
  1331. }
  1332. else
  1333. {
  1334. SoundManager.PlaySoundByConfigDelay(Attribute.ReloadSound, GlobalPosition, Attribute.ReloadSoundDelayTime, TriggerRole);
  1335. }
  1336. }
  1337. }
  1338. //播放换弹完成音效
  1339. private void PlayReloadFinishSound()
  1340. {
  1341. if (Attribute.ReloadFinishSound != null)
  1342. {
  1343. SoundManager.PlaySoundByConfig(Attribute.ReloadFinishSound, GlobalPosition, TriggerRole);
  1344. }
  1345. }
  1346.  
  1347. //播放射击音效
  1348. private void PlayShootSound()
  1349. {
  1350. if (Attribute.ShootSound != null)
  1351. {
  1352. SoundManager.PlaySoundByConfig(Attribute.ShootSound, GlobalPosition, TriggerRole);
  1353. }
  1354. }
  1355.  
  1356. //播放上膛音效
  1357. private void PlayBeLoadedSound()
  1358. {
  1359. if (Attribute.BeLoadedSound != null)
  1360. {
  1361. if (Attribute.BeLoadedSoundDelayTime <= 0)
  1362. {
  1363. SoundManager.PlaySoundByConfig(Attribute.BeLoadedSound, GlobalPosition, TriggerRole);
  1364. }
  1365. else
  1366. {
  1367. SoundManager.PlaySoundByConfigDelay(Attribute.BeLoadedSound, GlobalPosition, Attribute.BeLoadedSoundDelayTime, TriggerRole);
  1368. }
  1369. }
  1370. }
  1371.  
  1372. //执行上膛逻辑
  1373. private void RunBeLoaded()
  1374. {
  1375. if (Attribute.AutoManualBeLoaded)
  1376. {
  1377. if (_attackTimer <= 0)
  1378. {
  1379. //执行自动上膛
  1380. BeLoaded();
  1381. }
  1382. else if (CurrAmmo > 0)
  1383. {
  1384. //等待执行自动上膛
  1385. _beLoadedState = -1;
  1386. }
  1387. else
  1388. {
  1389. //没子弹了, 需要手动上膛
  1390. _beLoadedState = 0;
  1391. }
  1392. }
  1393. else
  1394. {
  1395. //手动上膛
  1396. _beLoadedState = 0;
  1397. }
  1398. }
  1399.  
  1400. //单独换弹处理
  1401. private void AloneReloadHandler()
  1402. {
  1403. if (Attribute.AloneReloadBeginIntervalTime <= 0)
  1404. {
  1405. //开始装第一颗子弹
  1406. _aloneReloadState = 2;
  1407. ReloadHandler();
  1408. }
  1409. else
  1410. {
  1411. _aloneReloadState = 1;
  1412. _reloadTimer = Attribute.AloneReloadBeginIntervalTime;
  1413. }
  1414. }
  1415.  
  1416. //换弹处理逻辑
  1417. private void ReloadHandler()
  1418. {
  1419. _reloadTimer = Attribute.ReloadTime;
  1420. //播放换弹动画
  1421. if (IsAutoPlaySpriteFrames)
  1422. {
  1423. PlaySpriteAnimation(AnimatorNames.Reloading);
  1424. PlayAnimationPlayer(AnimatorNames.Reloading);
  1425. }
  1426. //播放换弹音效
  1427. PlayReloadSound();
  1428. //抛出弹壳
  1429. if (Attribute.ReloadThrowShell && !_reloadShellFlag)
  1430. {
  1431. ThrowShellHandler(0.6f);
  1432. }
  1433. OnReload();
  1434. // Debug.Log("装弹.");
  1435. }
  1436. //换弹完成处理逻辑
  1437. private void ReloadFinishHandler()
  1438. {
  1439. // Debug.Log("装弹完成.");
  1440. OnReloadFinish();
  1441. }
  1442.  
  1443. //单独装弹完成
  1444. private void AloneReloadStateFinish()
  1445. {
  1446. // Debug.Log("单独装弹完成.");
  1447. OnAloneReloadStateFinish();
  1448. }
  1449.  
  1450. //抛弹逻辑
  1451. private void ThrowShellHandler(float speedScale)
  1452. {
  1453. if (Attribute.Shell == null)
  1454. {
  1455. return;
  1456. }
  1457. //创建一个弹壳
  1458. if (Attribute.ThrowShellDelayTime > 0)
  1459. {
  1460. this.CallDelay(Attribute.ThrowShellDelayTime, () =>
  1461. {
  1462. _reloadShellFlag = true;
  1463. FireManager.ThrowShell(this, Attribute.Shell, speedScale);
  1464. });
  1465. }
  1466. else if (Attribute.ThrowShellDelayTime == 0)
  1467. {
  1468. _reloadShellFlag = true;
  1469. FireManager.ThrowShell(this, Attribute.Shell, speedScale);
  1470. }
  1471. }
  1472.  
  1473. //散射值消退处理
  1474. private void ScatteringRangeBackHandler(float delta)
  1475. {
  1476. var startScatteringRange = Attribute.StartScatteringRange;
  1477. var finalScatteringRange = Attribute.FinalScatteringRange;
  1478. if (Master != null)
  1479. {
  1480. startScatteringRange = Master.RoleState.CalcStartScattering(startScatteringRange);
  1481. finalScatteringRange = Master.RoleState.CalcFinalScattering(finalScatteringRange);
  1482. }
  1483. if (startScatteringRange <= finalScatteringRange)
  1484. {
  1485. CurrScatteringRange = Mathf.Max(CurrScatteringRange - Attribute.ScatteringRangeBackSpeed * delta,
  1486. startScatteringRange);
  1487. }
  1488. else
  1489. {
  1490. CurrScatteringRange = Mathf.Min(CurrScatteringRange + Attribute.ScatteringRangeBackSpeed * delta,
  1491. startScatteringRange);
  1492. }
  1493. }
  1494.  
  1495. //散射值添加处理
  1496. private void ScatteringRangeAddHandler()
  1497. {
  1498. var startScatteringRange = Attribute.StartScatteringRange;
  1499. var finalScatteringRange = Attribute.FinalScatteringRange;
  1500. if (Master != null)
  1501. {
  1502. startScatteringRange = Master.RoleState.CalcStartScattering(startScatteringRange);
  1503. finalScatteringRange = Master.RoleState.CalcFinalScattering(finalScatteringRange);
  1504. }
  1505. if (startScatteringRange <= finalScatteringRange)
  1506. {
  1507. CurrScatteringRange = Mathf.Min(CurrScatteringRange + Attribute.ScatteringRangeAddValue,
  1508. finalScatteringRange);
  1509. }
  1510. else
  1511. {
  1512. CurrScatteringRange = Mathf.Min(CurrScatteringRange - Attribute.ScatteringRangeAddValue,
  1513. finalScatteringRange);
  1514. }
  1515. }
  1516.  
  1517. //射速增加处理
  1518. private void FiringSpeedAddHandler(float delta)
  1519. {
  1520. if (Attribute.ContinuousShoot)
  1521. {
  1522. CurrentFiringSpeed = Mathf.MoveToward(CurrentFiringSpeed, Attribute.FinalFiringSpeed,
  1523. Attribute.FiringSpeedAddSpeed * delta);
  1524. }
  1525. else
  1526. {
  1527. CurrentFiringSpeed = Attribute.StartFiringSpeed;
  1528. }
  1529. }
  1530. //射速衰减处理
  1531. private void FiringSpeedBackHandler(float delta)
  1532. {
  1533. if (Attribute.ContinuousShoot)
  1534. {
  1535. CurrentFiringSpeed = Mathf.MoveToward(CurrentFiringSpeed, Attribute.StartFiringSpeed,
  1536. Attribute.FiringSpeedBackSpeed * delta);
  1537. }
  1538. else
  1539. {
  1540. CurrentFiringSpeed = Attribute.StartFiringSpeed;
  1541. }
  1542. }
  1543.  
  1544. /// <summary>
  1545. /// 换弹计时器时间到, 执行换弹操作
  1546. /// </summary>
  1547. private void ReloadSuccess()
  1548. {
  1549. if (Attribute.AloneReload) //单独装填
  1550. {
  1551. if (ResidueAmmo >= Attribute.AloneReloadCount) //剩余子弹充足
  1552. {
  1553. if (CurrAmmo + Attribute.AloneReloadCount <= Attribute.AmmoCapacity)
  1554. {
  1555. ResidueAmmo -= Attribute.AloneReloadCount;
  1556. CurrAmmo += Attribute.AloneReloadCount;
  1557. }
  1558. else //子弹满了
  1559. {
  1560. var num = Attribute.AmmoCapacity - CurrAmmo;
  1561. CurrAmmo = Attribute.AmmoCapacity;
  1562. ResidueAmmo -= num;
  1563. }
  1564. }
  1565. else if (ResidueAmmo != 0) //剩余子弹不足
  1566. {
  1567. if (ResidueAmmo + CurrAmmo <= Attribute.AmmoCapacity)
  1568. {
  1569. CurrAmmo += ResidueAmmo;
  1570. ResidueAmmo = 0;
  1571. }
  1572. else //子弹满了
  1573. {
  1574. var num = Attribute.AmmoCapacity - CurrAmmo;
  1575. CurrAmmo = Attribute.AmmoCapacity;
  1576. ResidueAmmo -= num;
  1577. }
  1578. }
  1579.  
  1580. if (!_aloneReloadStop && ResidueAmmo != 0 && CurrAmmo != Attribute.AmmoCapacity) //继续装弹
  1581. {
  1582. ReloadHandler();
  1583. }
  1584. }
  1585. else //换弹结束
  1586. {
  1587. if (CurrAmmo + ResidueAmmo >= Attribute.AmmoCapacity)
  1588. {
  1589. ResidueAmmo -= Attribute.AmmoCapacity - CurrAmmo;
  1590. CurrAmmo = Attribute.AmmoCapacity;
  1591. }
  1592. else
  1593. {
  1594. CurrAmmo += ResidueAmmo;
  1595. ResidueAmmo = 0;
  1596. }
  1597.  
  1598. StopReload();
  1599. ReloadFinishHandler();
  1600. }
  1601. }
  1602.  
  1603. //播放动画
  1604. private void PlayAnimationPlayer(string name)
  1605. {
  1606. if (AnimationPlayer != null && AnimationPlayer.HasAnimation(name))
  1607. {
  1608. AnimationPlayer.Play(name);
  1609. }
  1610. }
  1611.  
  1612. //帧动画播放结束
  1613. private void OnAnimatedSpriteFinished()
  1614. {
  1615. // Debug.Log("帧动画播放结束...");
  1616. AnimatedSprite.Play(AnimatorNames.Default);
  1617. }
  1618.  
  1619. //动画播放器播放结束
  1620. private void OnAnimationPlayerFinished(StringName name)
  1621. {
  1622. if (Master != null && !IsActive)
  1623. {
  1624. Master.OnPutBackMount(this, PackageIndex);
  1625. }
  1626. }
  1627.  
  1628. public override CheckInteractiveResult CheckInteractive(ActivityObject master)
  1629. {
  1630. var result = new CheckInteractiveResult(this);
  1631.  
  1632. if (master is Role roleMaster) //碰到角色
  1633. {
  1634. if (Master == null)
  1635. {
  1636. if (roleMaster.WeaponPack.Capacity == 0)
  1637. {
  1638. //容量为0
  1639. return result;
  1640. }
  1641. var masterWeapon = roleMaster.WeaponPack.ActiveItem;
  1642. //查找是否有同类型武器
  1643. var index = roleMaster.WeaponPack.FindIndex(ActivityBase.Id);
  1644. if (index != -1) //如果有这个武器
  1645. {
  1646. if (CurrAmmo + ResidueAmmo != 0) //子弹不为空
  1647. {
  1648. var targetWeapon = roleMaster.WeaponPack.GetItem(index);
  1649. if (!targetWeapon.IsAmmoFull()) //背包里面的武器子弹未满
  1650. {
  1651. //可以互动拾起弹药
  1652. result.CanInteractive = true;
  1653. result.Type = CheckInteractiveResult.InteractiveType.Bullet;
  1654. return result;
  1655. }
  1656. }
  1657. }
  1658. else //没有武器
  1659. {
  1660. if (roleMaster.WeaponPack.HasVacancy()) //有空位, 能拾起武器
  1661. {
  1662. //可以互动, 拾起武器
  1663. result.CanInteractive = true;
  1664. result.Type = CheckInteractiveResult.InteractiveType.PickUp;
  1665. return result;
  1666. }
  1667. else if (masterWeapon != null) //替换武器 // && masterWeapon.Attribute.WeightType == Attribute.WeightType)
  1668. {
  1669. //可以互动, 切换武器
  1670. result.CanInteractive = true;
  1671. result.Type = CheckInteractiveResult.InteractiveType.Replace;
  1672. return result;
  1673. }
  1674. }
  1675. }
  1676. }
  1677.  
  1678. return result;
  1679. }
  1680.  
  1681. public override void Interactive(ActivityObject master)
  1682. {
  1683. if (master is Role roleMaster) //与role互动
  1684. {
  1685. if (roleMaster.WeaponPack.Capacity == 0)
  1686. {
  1687. //容量为0
  1688. return;
  1689. }
  1690. var holster = roleMaster.WeaponPack;
  1691. //查找是否有同类型武器
  1692. var index = holster.FindIndex(ActivityBase.Id);
  1693. if (index != -1) //如果有这个武器
  1694. {
  1695. if (CurrAmmo + ResidueAmmo == 0) //没有子弹了
  1696. {
  1697. return;
  1698. }
  1699.  
  1700. var weapon = holster.GetItem(index);
  1701. //子弹上限
  1702. var maxCount = Attribute.MaxAmmoCapacity;
  1703. //是否捡到子弹
  1704. var flag = false;
  1705. if (ResidueAmmo > 0 && weapon.CurrAmmo + weapon.ResidueAmmo < maxCount)
  1706. {
  1707. var count = weapon.PickUpAmmo(ResidueAmmo);
  1708. if (count != ResidueAmmo)
  1709. {
  1710. ResidueAmmo = count;
  1711. flag = true;
  1712. }
  1713. }
  1714.  
  1715. if (CurrAmmo > 0 && weapon.CurrAmmo + weapon.ResidueAmmo < maxCount)
  1716. {
  1717. var count = weapon.PickUpAmmo(CurrAmmo);
  1718. if (count != CurrAmmo)
  1719. {
  1720. CurrAmmo = count;
  1721. flag = true;
  1722. }
  1723. }
  1724.  
  1725. //播放互动效果
  1726. if (flag)
  1727. {
  1728. Throw(GlobalPosition, 0, Utils.Random.RandomRangeInt(20, 50), Vector2.Zero, Utils.Random.RandomRangeInt(-180, 180));
  1729. //没有子弹了, 停止播放泛白效果
  1730. if (IsTotalAmmoEmpty())
  1731. {
  1732. //停止动画
  1733. AnimationPlayer.Stop();
  1734. //清除泛白效果
  1735. SetBlendSchedule(0);
  1736. }
  1737. }
  1738. }
  1739. else //没有武器
  1740. {
  1741. if (!holster.HasVacancy()) //没有空位置, 扔掉当前武器
  1742. {
  1743. //替换武器
  1744. roleMaster.ThrowWeapon();
  1745. }
  1746. roleMaster.PickUpWeapon(this);
  1747. }
  1748. }
  1749. }
  1750.  
  1751. /// <summary>
  1752. /// 获取当前武器真实的旋转角度(弧度制), 由于武器旋转时加入了旋转吸附, 所以需要通过该函数来来知道当前武器的真实旋转角度
  1753. /// </summary>
  1754. public float GetRealGlobalRotation()
  1755. {
  1756. return Mathf.DegToRad(Master.MountPoint.RealRotationDegrees) + Rotation;
  1757. }
  1758.  
  1759. /// <summary>
  1760. /// 触发扔掉武器时抛出的效果, 并不会管武器是否在武器背包中
  1761. /// </summary>
  1762. /// <param name="master">触发扔掉该武器的的角色</param>
  1763. public void ThrowWeapon(Role master)
  1764. {
  1765. ThrowWeapon(master, master.GlobalPosition);
  1766. }
  1767.  
  1768. /// <summary>
  1769. /// 触发扔掉武器时抛出的效果, 并不会管武器是否在武器背包中
  1770. /// </summary>
  1771. /// <param name="master">触发扔掉该武器的的角色</param>
  1772. /// <param name="startPosition">投抛起始位置</param>
  1773. public void ThrowWeapon(Role master, Vector2 startPosition)
  1774. {
  1775. //阴影偏移
  1776. ShadowOffset = new Vector2(0, 2);
  1777.  
  1778. if (master.Face == FaceDirection.Left)
  1779. {
  1780. Scale *= new Vector2(1, -1);
  1781. }
  1782.  
  1783. var rotation = master.MountPoint.GlobalRotation;
  1784. GlobalRotation = rotation;
  1785.  
  1786. if (master.Face == FaceDirection.Right)
  1787. {
  1788. startPosition += _gripPoint.Rotated(rotation);
  1789. }
  1790. else
  1791. {
  1792. startPosition += new Vector2(_gripPoint.X, -_gripPoint.Y).Rotated(rotation);
  1793. }
  1794.  
  1795. var startHeight = -master.MountPoint.Position.Y;
  1796. var velocity = new Vector2(20, 0).Rotated(rotation);
  1797. var yf = Utils.Random.RandomRangeInt(50, 70);
  1798. Throw(startPosition, startHeight, yf, velocity, 0);
  1799. //继承role的移动速度
  1800. InheritVelocity(master);
  1801. }
  1802.  
  1803. protected override void OnThrowStart()
  1804. {
  1805. //禁用碰撞
  1806. //Collision.Disabled = true;
  1807. //AnimationPlayer.Play(AnimatorNames.Floodlight);
  1808. }
  1809.  
  1810. protected override void OnThrowOver()
  1811. {
  1812. //启用碰撞
  1813. //Collision.Disabled = false;
  1814. //还有弹药, 播放泛白效果
  1815. if (!IsTotalAmmoEmpty())
  1816. {
  1817. AnimationPlayer.Play(AnimatorNames.Floodlight);
  1818. }
  1819. }
  1820.  
  1821. /// <summary>
  1822. /// 触发启用武器, 这个函数由 Holster 对象调用
  1823. /// </summary>
  1824. private void Active()
  1825. {
  1826. //调整阴影
  1827. //ShadowOffset = new Vector2(0, Master.GlobalPosition.Y - GlobalPosition.Y);
  1828. //ShadowOffset = new Vector2(0, -Master.MountPoint.Position.Y + 2);
  1829. ShadowOffset = new Vector2(0, -Master.MountPoint.Position.Y);
  1830. //枪口默认抬起角度
  1831. RotationDegrees = -Attribute.DefaultAngle;
  1832. ShowShadowSprite();
  1833. OnActive();
  1834. }
  1835.  
  1836. /// <summary>
  1837. /// 触发收起武器, 这个函数由 Holster 对象调用
  1838. /// </summary>
  1839. private void Conceal()
  1840. {
  1841. //停止换弹动画
  1842. if (AnimationPlayer.CurrentAnimation == AnimatorNames.Reloading && AnimationPlayer.IsPlaying())
  1843. {
  1844. AnimationPlayer.Play(AnimatorNames.Reset);
  1845. }
  1846. HideShadowSprite();
  1847. OnConceal();
  1848. }
  1849. public void OnRemoveItem()
  1850. {
  1851. //要重置部分重要属性属性
  1852. if (Master.IsAi)
  1853. {
  1854. _triggerTimer = 0;
  1855. }
  1856. GetParent().RemoveChild(this);
  1857. _triggerRoleFlag = false;
  1858. SetCurrentWeaponAttribute(_playerWeaponAttribute);
  1859. CollisionLayer = _tempLayer;
  1860.  
  1861. //精灵位置, 旋转中心点
  1862. AnimatedSprite.Position = Vector2.Zero;
  1863. AnimatedSprite.Offset = Vector2.Zero;
  1864. Collision.Position = Vector2.Zero;
  1865. //清除 Ai 拾起标记
  1866. RemoveSign(SignNames.AiFindWeaponSign);
  1867. //停止换弹
  1868. if (Reloading)
  1869. {
  1870. StopReload();
  1871. }
  1872. //停止换弹动画
  1873. if (AnimationPlayer.CurrentAnimation == AnimatorNames.Reloading && AnimationPlayer.IsPlaying())
  1874. {
  1875. AnimationPlayer.Play(AnimatorNames.Reset);
  1876. }
  1877. OnRemove(Master);
  1878. }
  1879.  
  1880. public void OnPickUpItem()
  1881. {
  1882. Pickup();
  1883. _triggerRoleFlag = true;
  1884. SetCurrentWeaponAttribute(Master.IsAi ? _aiWeaponAttribute : _playerWeaponAttribute);
  1885. //停止动画
  1886. AnimationPlayer.Stop();
  1887. //清除泛白效果
  1888. SetBlendSchedule(0);
  1889. ZIndex = 0;
  1890. //禁用碰撞
  1891. //Collision.Disabled = true;
  1892. AnimatedSprite.Position = _gripPoint;
  1893. AnimatedSprite.Offset = _gripOffset;
  1894. Collision.Position = _collPoint;
  1895. //修改层级
  1896. _tempLayer = CollisionLayer;
  1897. CollisionLayer = PhysicsLayer.OnHand;
  1898. //清除 Ai 拾起标记
  1899. RemoveSign(SignNames.AiFindWeaponSign);
  1900. OnPickUp(Master);
  1901. }
  1902.  
  1903. public void OnActiveItem()
  1904. {
  1905. //更改父节点
  1906. var parent = GetParentOrNull<Node>();
  1907. if (parent == null)
  1908. {
  1909. Master.MountPoint.AddChild(this);
  1910. }
  1911. else if (parent != Master.MountPoint)
  1912. {
  1913. parent.RemoveChild(this);
  1914. Master.MountPoint.AddChild(this);
  1915. }
  1916.  
  1917. Position = Vector2.Zero;
  1918. Scale = Vector2.One;
  1919. RotationDegrees = 0;
  1920. Visible = true;
  1921. Active();
  1922. }
  1923.  
  1924. public void OnConcealItem()
  1925. {
  1926. var tempParent = GetParentOrNull<Node2D>();
  1927. if (tempParent != null)
  1928. {
  1929. tempParent.RemoveChild(this);
  1930. Master.BackMountPoint.AddChild(this);
  1931. Master.OnPutBackMount(this, PackageIndex);
  1932. Conceal();
  1933. }
  1934. }
  1935.  
  1936. public void OnOverflowItem()
  1937. {
  1938. Master.ThrowWeapon(PackageIndex);
  1939. }
  1940.  
  1941. /// <summary>
  1942. /// 获取相对于武器本地坐标的开火位置
  1943. /// </summary>
  1944. public Vector2 GetLocalFirePosition()
  1945. {
  1946. return AnimatedSprite.Position + FirePoint.Position;
  1947. }
  1948. /// <summary>
  1949. /// 获取相对于武器本地坐标的抛壳位置
  1950. /// </summary>
  1951. public Vector2 GetLocalShellPosition()
  1952. {
  1953. return AnimatedSprite.Position + ShellPoint.Position;
  1954. }
  1955.  
  1956. /// <summary>
  1957. /// 获取握把位置, 相当于武器的原点坐标
  1958. /// </summary>
  1959. public Vector2 GetGripPosition()
  1960. {
  1961. return _gripPoint + _gripOffset;
  1962. }
  1963.  
  1964. //设置当前使用的武器属性
  1965. private void SetCurrentWeaponAttribute(ExcelConfig.WeaponBase attr)
  1966. {
  1967. if (attr != _weaponAttribute)
  1968. {
  1969. _weaponAttribute = attr;
  1970. //重置开火速率
  1971. CurrentFiringSpeed = _weaponAttribute.StartFiringSpeed;
  1972. }
  1973. }
  1974.  
  1975. //-------------------------------- Ai相关 -----------------------------
  1976. /// <summary>
  1977. /// Ai调用, 触发扣动扳机, 并返回攻击状态
  1978. /// </summary>
  1979. public AiAttackEnum AiTriggerAttackState()
  1980. {
  1981. AiAttackEnum flag;
  1982. if (IsTotalAmmoEmpty()) //当前武器弹药打空
  1983. {
  1984. //切换到有子弹的武器
  1985. var index = Master.WeaponPack.FindIndex((we, i) => !we.IsTotalAmmoEmpty());
  1986. if (index != -1)
  1987. {
  1988. flag = AiAttackEnum.ExchangeWeapon;
  1989. Master.WeaponPack.ExchangeByIndex(index);
  1990. }
  1991. else //所有子弹打光
  1992. {
  1993. flag = AiAttackEnum.NoAmmo;
  1994. }
  1995. }
  1996. else if (Reloading) //换弹中
  1997. {
  1998. flag = AiAttackEnum.TriggerReload;
  1999. }
  2000. else if (IsAmmoEmpty()) //弹夹已经打空
  2001. {
  2002. flag = AiAttackEnum.Reloading;
  2003. Reload();
  2004. }
  2005. else if (_beLoadedState == 0 || _beLoadedState == -1) //需要上膛
  2006. {
  2007. flag = AiAttackEnum.AttackInterval;
  2008. if (_attackTimer <= 0)
  2009. {
  2010. BeLoaded();
  2011. }
  2012. }
  2013. else if (_beLoadedState == 1) //上膛中
  2014. {
  2015. flag = AiAttackEnum.AttackInterval;
  2016. }
  2017. else if (_continuousCount >= 1) //连发中
  2018. {
  2019. flag = AiAttackEnum.Attack;
  2020. }
  2021. else if (IsAttackIntervalTime()) //开火间隙
  2022. {
  2023. flag = AiAttackEnum.AttackInterval;
  2024. }
  2025. else
  2026. {
  2027. var enemy = (Enemy)Master;
  2028. if (enemy.LockTargetTime >= Attribute.AiAttackAttr.LockingTime) //正常射击
  2029. {
  2030. if (GetDelayedAttackTime() > 0)
  2031. {
  2032. flag = AiAttackEnum.Attack;
  2033. enemy.Attack();
  2034. if (_attackFlag)
  2035. {
  2036. enemy.LockingTime = 0;
  2037. }
  2038. }
  2039. else
  2040. {
  2041. if (Attribute.ContinuousShoot) //连发
  2042. {
  2043. flag = AiAttackEnum.Attack;
  2044. enemy.Attack();
  2045. if (_attackFlag)
  2046. {
  2047. enemy.LockingTime = 0;
  2048. }
  2049. }
  2050. else //单发
  2051. {
  2052. flag = AiAttackEnum.Attack;
  2053. enemy.Attack();
  2054. if (_attackFlag)
  2055. {
  2056. enemy.LockingTime = 0;
  2057. }
  2058. }
  2059. }
  2060. }
  2061. else //锁定时间没到
  2062. {
  2063. flag = AiAttackEnum.LockingTime;
  2064. }
  2065. }
  2066.  
  2067. return flag;
  2068. }
  2069.  
  2070. // /// <summary>
  2071. // /// 获取 Ai 对于该武器的评分, 评分越高, 代表 Ai 会越优先选择该武器, 如果为 -1, 则表示 Ai 不会使用该武器
  2072. // /// </summary>
  2073. // public float GetAiScore()
  2074. // {
  2075. // return 1;
  2076. // }
  2077. }