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