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