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