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