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