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