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