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