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