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