Newer
Older
DungeonShooting / DungeonShooting_Godot / src / game / item / weapon / Weapon.cs
  1. using Godot;
  2. using System;
  3. using System.Collections.Generic;
  4.  
  5. /// <summary>
  6. /// 武器的基类
  7. /// </summary>
  8. public abstract partial class Weapon : ActivityObject
  9. {
  10. /// <summary>
  11. /// 所有被扔在地上的武器
  12. /// </summary>
  13. public static readonly HashSet<Weapon> UnclaimedWeapons = new HashSet<Weapon>();
  14.  
  15. /// <summary>
  16. /// 开火回调事件
  17. /// </summary>
  18. public event Action<Weapon> FireEvent;
  19.  
  20. /// <summary>
  21. /// 武器属性数据
  22. /// </summary>
  23. public WeaponAttribute Attribute => _weaponAttribute;
  24.  
  25. private WeaponAttribute _weaponAttribute;
  26. private WeaponAttribute _originWeaponAttribute;
  27.  
  28. /// <summary>
  29. /// 武器攻击的目标阵营
  30. /// </summary>
  31. public CampEnum TargetCamp { get; set; }
  32.  
  33. /// <summary>
  34. /// 该武器的拥有者
  35. /// </summary>
  36. public Role Master { get; private set; }
  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. public Marker2D FirePoint { get; private set; }
  52.  
  53. /// <summary>
  54. /// 武器管的原点
  55. /// </summary>
  56. public Marker2D OriginPoint { get; private set; }
  57.  
  58. /// <summary>
  59. /// 弹壳抛出的点
  60. /// </summary>
  61. public Marker2D ShellPoint { get; private set; }
  62.  
  63. /// <summary>
  64. /// 武器的当前散射半径
  65. /// </summary>
  66. public float CurrScatteringRange { get; private set; } = 0;
  67.  
  68. /// <summary>
  69. /// 是否在换弹中
  70. /// </summary>
  71. /// <value></value>
  72. public bool Reloading { get; private set; } = false;
  73.  
  74. /// <summary>
  75. /// 换弹计时器
  76. /// </summary>
  77. public float ReloadTimer { get; private set; } = 0;
  78.  
  79. /// <summary>
  80. /// 换弹进度 (0 - 1)
  81. /// </summary>
  82. public float ReloadProgress
  83. {
  84. get
  85. {
  86. if (Attribute.AloneReload)
  87. {
  88. var num = 1f / Attribute.AmmoCapacity;
  89. return num * (Attribute.AmmoCapacity - CurrAmmo - 1) + num * (ReloadTimer / Attribute.ReloadTime);
  90. }
  91.  
  92. return ReloadTimer / Attribute.ReloadTime;
  93. }
  94. }
  95.  
  96. /// <summary>
  97. /// 返回是否在蓄力中,
  98. /// 注意, 属性仅在 Attribute.LooseShoot == false 时有正确的返回值, 否则返回 false
  99. /// </summary>
  100. public bool IsCharging => _looseShootFlag;
  101.  
  102. /// <summary>
  103. /// 返回武器是否在武器袋中
  104. /// </summary>
  105. public bool IsInHolster => Master != null;
  106.  
  107. /// <summary>
  108. /// 返回是否真正使用该武器
  109. /// </summary>
  110. public bool IsActive => Master != null && Master.Holster.ActiveWeapon == this;
  111. /// <summary>
  112. /// 动画播放器
  113. /// </summary>
  114. public AnimationPlayer AnimationPlayer { get; private set; }
  115.  
  116.  
  117. //--------------------------------------------------------------------------------------------
  118. //是否按下
  119. private bool _triggerFlag = false;
  120.  
  121. //扳机计时器
  122. private float _triggerTimer = 0;
  123.  
  124. //开火前延时时间
  125. private float _delayedTime = 0;
  126.  
  127. //开火间隙时间
  128. private float _fireInterval = 0;
  129.  
  130. //开火武器口角度
  131. private float _fireAngle = 0;
  132.  
  133. //攻击冷却计时
  134. private float _attackTimer = 0;
  135.  
  136. //攻击状态
  137. private bool _attackFlag = false;
  138.  
  139. //按下的时间
  140. private float _downTimer = 0;
  141.  
  142. //松开的时间
  143. private float _upTimer = 0;
  144.  
  145. //连发次数
  146. private float _continuousCount = 0;
  147.  
  148. //连发状态记录
  149. private bool _continuousShootFlag = false;
  150.  
  151. //松开扳机是否开火
  152. private bool _looseShootFlag = false;
  153.  
  154. //蓄力攻击时长
  155. private float _chargeTime = 0;
  156.  
  157. //是否需要重置武器数据
  158. private bool _dirtyFlag = false;
  159.  
  160. //当前后坐力导致的偏移长度
  161. private float _currBacklashLength = 0;
  162.  
  163. /// <summary>
  164. /// 初始化武器属性
  165. /// </summary>
  166. public void InitWeapon(WeaponAttribute attribute)
  167. {
  168. _originWeaponAttribute = attribute;
  169. _weaponAttribute = attribute;
  170.  
  171. AnimationPlayer = GetNode<AnimationPlayer>("AnimationPlayer");
  172. FirePoint = GetNode<Marker2D>("FirePoint");
  173. OriginPoint = GetNode<Marker2D>("OriginPoint");
  174. ShellPoint = GetNode<Marker2D>("ShellPoint");
  175.  
  176. //图标
  177. SetDefaultTexture(ResourceLoader.Load<Texture2D>(Attribute.Sprite2D));
  178. AnimatedSprite.Position = Attribute.CenterPosition;
  179.  
  180. //开火位置
  181. FirePoint.Position = new Vector2(Attribute.FirePosition.X, -Attribute.FirePosition.Y);
  182. OriginPoint.Position = new Vector2(0, -Attribute.FirePosition.Y);
  183.  
  184. if (Attribute.AmmoCapacity > Attribute.MaxAmmoCapacity)
  185. {
  186. Attribute.AmmoCapacity = Attribute.MaxAmmoCapacity;
  187. GD.PrintErr("弹夹的容量不能超过弹药上限, 武器id: " + ItemId);
  188. }
  189. //弹药量
  190. CurrAmmo = Attribute.AmmoCapacity;
  191. //剩余弹药量
  192. ResidueAmmo = Mathf.Min(Attribute.StandbyAmmoCapacity + CurrAmmo, Attribute.MaxAmmoCapacity) - CurrAmmo;
  193. ThrowCollisionSize = new Vector2(20, 15);
  194. }
  195.  
  196. /// <summary>
  197. /// 单次开火时调用的函数
  198. /// </summary>
  199. protected abstract void OnFire();
  200.  
  201. /// <summary>
  202. /// 发射子弹时调用的函数, 每发射一枚子弹调用一次,
  203. /// 如果做霰弹武器效果, 一次开火发射5枚子弹, 则该函数调用5次
  204. /// </summary>
  205. /// <param name="fireRotation">开火时枪口旋转角度</param>
  206. protected abstract void OnShoot(float fireRotation);
  207. /// <summary>
  208. /// 当按下扳机时调用
  209. /// </summary>
  210. protected virtual void OnDownTrigger()
  211. {
  212. }
  213.  
  214. /// <summary>
  215. /// 当松开扳机时调用
  216. /// </summary>
  217. protected virtual void OnUpTrigger()
  218. {
  219. }
  220.  
  221. /// <summary>
  222. /// 开始蓄力时调用,
  223. /// 注意, 该函数仅在 Attribute.LooseShoot == false 时才能被调用
  224. /// </summary>
  225. protected virtual void OnStartCharge()
  226. {
  227. }
  228.  
  229. /// <summary>
  230. /// 当开始换弹时调用
  231. /// </summary>
  232. protected virtual void OnReload()
  233. {
  234. }
  235.  
  236. /// <summary>
  237. /// 当换弹完成时调用
  238. /// </summary>
  239. protected virtual void OnReloadFinish()
  240. {
  241. }
  242.  
  243. /// <summary>
  244. /// 当武器被拾起时调用
  245. /// </summary>
  246. /// <param name="master">拾起该武器的角色</param>
  247. protected virtual void OnPickUp(Role master)
  248. {
  249. }
  250.  
  251. /// <summary>
  252. /// 当武器从武器袋中移除时调用
  253. /// </summary>
  254. protected virtual void OnRemove()
  255. {
  256. }
  257.  
  258. /// <summary>
  259. /// 当武器被激活时调用, 也就是使用当武器时调用
  260. /// </summary>
  261. protected virtual void OnActive()
  262. {
  263. }
  264.  
  265. /// <summary>
  266. /// 当武器被收起时调用
  267. /// </summary>
  268. protected virtual void OnConceal()
  269. {
  270. }
  271.  
  272. /// <summary>
  273. /// 射击时调用, 返回消耗弹药数量, 默认为1, 如果返回为 0, 则不消耗弹药
  274. /// </summary>
  275. protected virtual int UseAmmoCount()
  276. {
  277. return 1;
  278. }
  279.  
  280. public override void _EnterTree()
  281. {
  282. base._EnterTree();
  283.  
  284. //收集落在地上的武器
  285. if (IsInGround())
  286. {
  287. UnclaimedWeapons.Add(this);
  288. }
  289. }
  290.  
  291. public override void _ExitTree()
  292. {
  293. base._ExitTree();
  294. UnclaimedWeapons.Remove(this);
  295. }
  296.  
  297. protected override void Process(float delta)
  298. {
  299. //这把武器被扔在地上, 或者当前武器没有被使用
  300. if (Master == null || Master.Holster.ActiveWeapon != this)
  301. {
  302. //_triggerTimer
  303. _triggerTimer = _triggerTimer > 0 ? _triggerTimer - delta : 0;
  304. //攻击冷却计时
  305. _attackTimer = _attackTimer > 0 ? _attackTimer - delta : 0;
  306. //武器的当前散射半径
  307. CurrScatteringRange = Mathf.Max(CurrScatteringRange - Attribute.ScatteringRangeBackSpeed * delta,
  308. Attribute.StartScatteringRange);
  309. //松开扳机
  310. if (_triggerFlag || _downTimer > 0)
  311. {
  312. UpTrigger();
  313. _downTimer = 0;
  314. }
  315. _triggerFlag = false;
  316.  
  317. //重置数据
  318. if (_dirtyFlag)
  319. {
  320. _dirtyFlag = false;
  321. Reloading = false;
  322. ReloadTimer = 0;
  323. _attackFlag = false;
  324. _continuousCount = 0;
  325. _delayedTime = 0;
  326. _upTimer = 0;
  327. _looseShootFlag = false;
  328. _chargeTime = 0;
  329. }
  330. }
  331. else //正在使用中
  332. {
  333. _dirtyFlag = true;
  334. //换弹
  335. if (Reloading)
  336. {
  337. ReloadTimer -= delta;
  338. if (ReloadTimer <= 0)
  339. {
  340. ReloadSuccess();
  341. }
  342. }
  343.  
  344. // 攻击的计时器
  345. if (_attackTimer > 0)
  346. {
  347. _attackTimer -= delta;
  348. if (_attackTimer < 0)
  349. {
  350. _delayedTime += _attackTimer;
  351. _attackTimer = 0;
  352. //枪口默认角度
  353. RotationDegrees = -Attribute.DefaultAngle;
  354. }
  355. }
  356. else if (_delayedTime > 0) //攻击延时
  357. {
  358. _delayedTime -= delta;
  359. if (_attackTimer < 0)
  360. {
  361. _delayedTime = 0;
  362. }
  363. }
  364. //扳机判定
  365. if (_triggerFlag)
  366. {
  367. if (_looseShootFlag) //蓄力时长
  368. {
  369. _chargeTime += delta;
  370. }
  371.  
  372. _downTimer += delta;
  373. if (_upTimer > 0) //第一帧按下扳机
  374. {
  375. DownTrigger();
  376. _upTimer = 0;
  377. }
  378. }
  379. else
  380. {
  381. _upTimer += delta;
  382. if (_downTimer > 0) //第一帧松开扳机
  383. {
  384. UpTrigger();
  385. _downTimer = 0;
  386. }
  387. }
  388.  
  389. //连发判断
  390. if (!_looseShootFlag && _continuousCount > 0 && _delayedTime <= 0 && _attackTimer <= 0)
  391. {
  392. //连发开火
  393. TriggerFire();
  394. }
  395.  
  396. if (!_attackFlag && _attackTimer <= 0)
  397. {
  398. CurrScatteringRange = Mathf.Max(CurrScatteringRange - Attribute.ScatteringRangeBackSpeed * delta,
  399. Attribute.StartScatteringRange);
  400. }
  401.  
  402. _triggerTimer = _triggerTimer > 0 ? _triggerTimer - delta : 0;
  403. _triggerFlag = false;
  404. _attackFlag = false;
  405. //武器身回归
  406. //Position = Position.MoveToward(Vector2.Zero, Attribute.BacklashRegressionSpeed * delta).Rotated(Rotation);
  407. _currBacklashLength = Mathf.MoveToward(_currBacklashLength, 0, Attribute.BacklashRegressionSpeed * delta);
  408. Position = new Vector2(_currBacklashLength, 0).Rotated(Rotation);
  409. if (_attackTimer > 0)
  410. {
  411. RotationDegrees = Mathf.Lerp(
  412. _fireAngle, -Attribute.DefaultAngle,
  413. Mathf.Clamp((_fireInterval - _attackTimer) * Attribute.UpliftAngleRestore / _fireInterval, 0, 1)
  414. );
  415. }
  416. }
  417. }
  418.  
  419. /// <summary>
  420. /// 返回武器是否在地上
  421. /// </summary>
  422. /// <returns></returns>
  423. public bool IsInGround()
  424. {
  425. return Master == null && GetParent() == GameApplication.Instance.RoomManager.NormalLayer;
  426. }
  427. /// <summary>
  428. /// 扳机函数, 调用即视为按下扳机
  429. /// </summary>
  430. public void Trigger()
  431. {
  432. //这一帧已经按过了, 不需要再按下
  433. if (_triggerFlag) return;
  434. //是否第一帧按下
  435. var justDown = _downTimer == 0;
  436. //是否能发射
  437. var flag = false;
  438. if (_continuousCount <= 0) //不能处于连发状态下
  439. {
  440. if (Attribute.ContinuousShoot) //自动射击
  441. {
  442. if (_triggerTimer > 0)
  443. {
  444. if (_continuousShootFlag)
  445. {
  446. flag = true;
  447. }
  448. }
  449. else
  450. {
  451. flag = true;
  452. if (_delayedTime <= 0 && _attackTimer <= 0)
  453. {
  454. _continuousShootFlag = true;
  455. }
  456. }
  457. }
  458. else //半自动
  459. {
  460. if (justDown && _triggerTimer <= 0 && _attackTimer <= 0)
  461. {
  462. flag = true;
  463. }
  464. }
  465. }
  466.  
  467. if (flag)
  468. {
  469. var fireFlag = true; //是否能开火
  470. if (Reloading) //换弹中
  471. {
  472. if (CurrAmmo > 0 && Attribute.AloneReload && Attribute.AloneReloadCanShoot) //立即停止换弹
  473. {
  474. Reloading = false;
  475. ReloadTimer = 0;
  476. }
  477. else
  478. {
  479. fireFlag = false;
  480. }
  481. }
  482. else if (CurrAmmo <= 0) //子弹不够
  483. {
  484. fireFlag = false;
  485. if (justDown)
  486. {
  487. //第一帧按下, 触发换弹
  488. Reload();
  489. }
  490. }
  491.  
  492. if (fireFlag)
  493. {
  494. if (justDown)
  495. {
  496. //开火前延时
  497. _delayedTime = Attribute.DelayedTime;
  498. //扳机按下间隔
  499. _triggerTimer = Attribute.TriggerInterval;
  500. //连发数量
  501. if (!Attribute.ContinuousShoot)
  502. {
  503. _continuousCount =
  504. Utils.RandomRangeInt(Attribute.MinContinuousCount, Attribute.MaxContinuousCount);
  505. }
  506. }
  507.  
  508. if (_delayedTime <= 0 && _attackTimer <= 0)
  509. {
  510. if (Attribute.LooseShoot) //松发开火
  511. {
  512. _looseShootFlag = true;
  513. OnStartCharge();
  514. }
  515. else
  516. {
  517. //开火
  518. TriggerFire();
  519. }
  520. }
  521.  
  522. _attackFlag = true;
  523. }
  524.  
  525. }
  526.  
  527. _triggerFlag = true;
  528. }
  529.  
  530. /// <summary>
  531. /// 返回是否按下扳机
  532. /// </summary>
  533. public bool IsPressTrigger()
  534. {
  535. return _triggerFlag;
  536. }
  537. /// <summary>
  538. /// 获取本次扳机按下的时长, 单位: 秒
  539. /// </summary>
  540. public float GetTriggerDownTime()
  541. {
  542. return _downTimer;
  543. }
  544.  
  545. /// <summary>
  546. /// 获取扳机蓄力时长, 计算按下扳机后从可以开火到当前一共经过了多长时间, 可用于计算蓄力攻击
  547. /// 注意, 该函数仅在 Attribute.LooseShoot == false 时有正确的返回值, 否则返回 0
  548. /// </summary>
  549. public float GetTriggerChargeTime()
  550. {
  551. return _chargeTime;
  552. }
  553. /// <summary>
  554. /// 获取延时射击倒计时, 单位: 秒
  555. /// </summary>
  556. public float GetDelayedAttackTime()
  557. {
  558. return _delayedTime;
  559. }
  560. /// <summary>
  561. /// 刚按下扳机
  562. /// </summary>
  563. private void DownTrigger()
  564. {
  565. OnDownTrigger();
  566. }
  567.  
  568. /// <summary>
  569. /// 刚松开扳机
  570. /// </summary>
  571. private void UpTrigger()
  572. {
  573. _continuousShootFlag = false;
  574. if (_delayedTime > 0)
  575. {
  576. _continuousCount = 0;
  577. }
  578.  
  579. //松发开火执行
  580. if (_looseShootFlag)
  581. {
  582. _looseShootFlag = false;
  583. if (_chargeTime >= Attribute.MinChargeTime) //判断蓄力是否够了
  584. {
  585. TriggerFire();
  586. }
  587. else //不能攻击
  588. {
  589. _continuousCount = 0;
  590. }
  591. _chargeTime = 0;
  592. }
  593.  
  594. OnUpTrigger();
  595. }
  596.  
  597. /// <summary>
  598. /// 触发开火
  599. /// </summary>
  600. private void TriggerFire()
  601. {
  602. _continuousCount = _continuousCount > 0 ? _continuousCount - 1 : 0;
  603.  
  604. //减子弹数量
  605. CurrAmmo -= UseAmmoCount();
  606. //开火间隙
  607. _fireInterval = 60 / Attribute.StartFiringSpeed;
  608. //攻击冷却
  609. _attackTimer += _fireInterval;
  610.  
  611. //触发开火函数
  612. OnFire();
  613.  
  614. //开火发射的子弹数量
  615. var bulletCount = Utils.RandomRangeInt(Attribute.MaxFireBulletCount, Attribute.MinFireBulletCount);
  616. //武器口角度
  617. var angle = new Vector2(GameConfig.ScatteringDistance, CurrScatteringRange).Angle();
  618.  
  619. //先算武器口方向
  620. var tempRotation = Utils.RandomRangeFloat(-angle, angle);
  621. var tempAngle = Mathf.RadToDeg(tempRotation);
  622.  
  623. //开火时枪口角度
  624. var fireRotation = Mathf.DegToRad(Master.MountPoint.RealAngle) + tempRotation;
  625. //创建子弹
  626. for (int i = 0; i < bulletCount; i++)
  627. {
  628. //发射子弹
  629. OnShoot(fireRotation);
  630. }
  631.  
  632. //当前的散射半径
  633. CurrScatteringRange = Mathf.Min(CurrScatteringRange + Attribute.ScatteringRangeAddValue,
  634. Attribute.FinalScatteringRange);
  635. //武器的旋转角度
  636. tempAngle -= Attribute.UpliftAngle;
  637. RotationDegrees = tempAngle;
  638. _fireAngle = tempAngle;
  639. //武器身位置
  640. var max = Mathf.Abs(Mathf.Max(Attribute.MaxBacklash, Attribute.MinBacklash));
  641. _currBacklashLength = Mathf.Clamp(
  642. _currBacklashLength - Utils.RandomRangeFloat(Attribute.MinBacklash, Attribute.MaxBacklash),
  643. -max, max
  644. );
  645. Position = new Vector2(_currBacklashLength, 0).Rotated(Rotation);
  646.  
  647. if (FireEvent != null)
  648. {
  649. FireEvent(this);
  650. }
  651. }
  652.  
  653. /// <summary>
  654. /// 获取武器攻击的目标层级
  655. /// </summary>
  656. /// <returns></returns>
  657. public uint GetAttackLayer()
  658. {
  659. return Master != null ? Master.AttackLayer : Role.DefaultAttackLayer;
  660. }
  661. /// <summary>
  662. /// 返回弹药是否到达上限
  663. /// </summary>
  664. public bool IsAmmoFull()
  665. {
  666. return CurrAmmo + ResidueAmmo >= Attribute.MaxAmmoCapacity;
  667. }
  668.  
  669. /// <summary>
  670. /// 返回弹夹是否打空
  671. /// </summary>
  672. public bool IsAmmoEmpty()
  673. {
  674. return CurrAmmo == 0;
  675. }
  676. /// <summary>
  677. /// 返回是否弹药耗尽
  678. /// </summary>
  679. public bool IsTotalAmmoEmpty()
  680. {
  681. return CurrAmmo + ResidueAmmo == 0;
  682. }
  683.  
  684. /// <summary>
  685. /// 强制修改当前弹夹弹药量
  686. /// </summary>
  687. public void SetCurrAmmo(int count)
  688. {
  689. CurrAmmo = Mathf.Clamp(count, 0, Attribute.AmmoCapacity);
  690. }
  691.  
  692. /// <summary>
  693. /// 强制修改备用弹药量
  694. /// </summary>
  695. public void SetResidueAmmo(int count)
  696. {
  697. ResidueAmmo = Mathf.Clamp(count, 0, Attribute.MaxAmmoCapacity - CurrAmmo);
  698. }
  699. /// <summary>
  700. /// 强制修改弹药量, 优先改动备用弹药
  701. /// </summary>
  702. public void SetTotalAmmo(int total)
  703. {
  704. if (total < 0)
  705. {
  706. return;
  707. }
  708. var totalAmmo = CurrAmmo + ResidueAmmo;
  709. if (totalAmmo == total)
  710. {
  711. return;
  712. }
  713. if (total > totalAmmo) //弹药增加
  714. {
  715. ResidueAmmo = Mathf.Min(total - CurrAmmo, Attribute.MaxAmmoCapacity - CurrAmmo);
  716. }
  717. else //弹药减少
  718. {
  719. if (CurrAmmo < total)
  720. {
  721. ResidueAmmo = total - CurrAmmo;
  722. }
  723. else
  724. {
  725. CurrAmmo = total;
  726. ResidueAmmo = 0;
  727. }
  728. }
  729. }
  730.  
  731. /// <summary>
  732. /// 拾起的弹药数量, 如果到达容量上限, 则返回拾取完毕后剩余的弹药数量
  733. /// </summary>
  734. /// <param name="count">弹药数量</param>
  735. private int PickUpAmmo(int count)
  736. {
  737. var num = ResidueAmmo;
  738. ResidueAmmo = Mathf.Min(ResidueAmmo + count, Attribute.MaxAmmoCapacity - CurrAmmo);
  739. return count - ResidueAmmo + num;
  740. }
  741.  
  742. /// <summary>
  743. /// 触发换弹
  744. /// </summary>
  745. public void Reload()
  746. {
  747. if (CurrAmmo < Attribute.AmmoCapacity && ResidueAmmo > 0 && !Reloading)
  748. {
  749. Reloading = true;
  750. ReloadTimer = Attribute.ReloadTime;
  751. OnReload();
  752. }
  753. }
  754.  
  755. /// <summary>
  756. /// 换弹计时器时间到, 执行换弹操作
  757. /// </summary>
  758. private void ReloadSuccess()
  759. {
  760. if (Attribute.AloneReload) //单独装填
  761. {
  762. if (ResidueAmmo >= Attribute.AloneReloadCount) //剩余子弹充足
  763. {
  764. if (CurrAmmo + Attribute.AloneReloadCount <= Attribute.AmmoCapacity)
  765. {
  766. ResidueAmmo -= Attribute.AloneReloadCount;
  767. CurrAmmo += Attribute.AloneReloadCount;
  768. }
  769. else //子弹满了
  770. {
  771. var num = Attribute.AmmoCapacity - CurrAmmo;
  772. CurrAmmo = Attribute.AmmoCapacity;
  773. ResidueAmmo -= num;
  774. }
  775. }
  776. else if (ResidueAmmo != 0) //剩余子弹不足
  777. {
  778. if (ResidueAmmo + CurrAmmo <= Attribute.AmmoCapacity)
  779. {
  780. CurrAmmo += ResidueAmmo;
  781. ResidueAmmo = 0;
  782. }
  783. else //子弹满了
  784. {
  785. var num = Attribute.AmmoCapacity - CurrAmmo;
  786. CurrAmmo = Attribute.AmmoCapacity;
  787. ResidueAmmo -= num;
  788. }
  789. }
  790.  
  791. if (ResidueAmmo == 0 || CurrAmmo == Attribute.AmmoCapacity) //换弹结束
  792. {
  793. Reloading = false;
  794. ReloadTimer = 0;
  795. OnReloadFinish();
  796. }
  797. else
  798. {
  799. ReloadTimer = Attribute.ReloadTime;
  800. OnReload();
  801. }
  802. }
  803. else //换弹结束
  804. {
  805. if (CurrAmmo + ResidueAmmo >= Attribute.AmmoCapacity)
  806. {
  807. ResidueAmmo -= Attribute.AmmoCapacity - CurrAmmo;
  808. CurrAmmo = Attribute.AmmoCapacity;
  809. }
  810. else
  811. {
  812. CurrAmmo += ResidueAmmo;
  813. ResidueAmmo = 0;
  814. }
  815.  
  816. Reloading = false;
  817. ReloadTimer = 0;
  818. OnReloadFinish();
  819. }
  820. }
  821.  
  822. public override CheckInteractiveResult CheckInteractive(ActivityObject master)
  823. {
  824. var result = new CheckInteractiveResult(this);
  825.  
  826. if (master is Role roleMaster) //碰到角色
  827. {
  828. if (Master == null)
  829. {
  830. var masterWeapon = roleMaster.Holster.ActiveWeapon;
  831. //查找是否有同类型武器
  832. var index = roleMaster.Holster.FindWeapon(ItemId);
  833. if (index != -1) //如果有这个武器
  834. {
  835. if (CurrAmmo + ResidueAmmo != 0) //子弹不为空
  836. {
  837. var targetWeapon = roleMaster.Holster.GetWeapon(index);
  838. if (!targetWeapon.IsAmmoFull()) //背包里面的武器子弹未满
  839. {
  840. //可以互动拾起弹药
  841. result.CanInteractive = true;
  842. result.Message = Attribute.Name;
  843. result.ShowIcon = ResourcePath.resource_sprite_ui_icon_icon_bullet_png;
  844. return result;
  845. }
  846. }
  847. }
  848. else //没有武器
  849. {
  850. if (roleMaster.Holster.CanPickupWeapon(this)) //能拾起武器
  851. {
  852. //可以互动, 拾起武器
  853. result.CanInteractive = true;
  854. result.Message = Attribute.Name;
  855. result.ShowIcon = ResourcePath.resource_sprite_ui_icon_icon_pickup_png;
  856. return result;
  857. }
  858. else if (masterWeapon != null && masterWeapon.Attribute.WeightType == Attribute.WeightType) //替换武器
  859. {
  860. //可以互动, 切换武器
  861. result.CanInteractive = true;
  862. result.Message = Attribute.Name;
  863. result.ShowIcon = ResourcePath.resource_sprite_ui_icon_icon_replace_png;
  864. return result;
  865. }
  866. }
  867. }
  868. }
  869.  
  870. return result;
  871. }
  872.  
  873. public override void Interactive(ActivityObject master)
  874. {
  875. if (master is Role roleMaster) //与role互动
  876. {
  877. var holster = roleMaster.Holster;
  878. //查找是否有同类型武器
  879. var index = holster.FindWeapon(ItemId);
  880. if (index != -1) //如果有这个武器
  881. {
  882. if (CurrAmmo + ResidueAmmo == 0) //没有子弹了
  883. {
  884. return;
  885. }
  886.  
  887. var weapon = holster.GetWeapon(index);
  888. //子弹上限
  889. var maxCount = Attribute.MaxAmmoCapacity;
  890. //是否捡到子弹
  891. var flag = false;
  892. if (ResidueAmmo > 0 && weapon.CurrAmmo + weapon.ResidueAmmo < maxCount)
  893. {
  894. var count = weapon.PickUpAmmo(ResidueAmmo);
  895. if (count != ResidueAmmo)
  896. {
  897. ResidueAmmo = count;
  898. flag = true;
  899. }
  900. }
  901.  
  902. if (CurrAmmo > 0 && weapon.CurrAmmo + weapon.ResidueAmmo < maxCount)
  903. {
  904. var count = weapon.PickUpAmmo(CurrAmmo);
  905. if (count != CurrAmmo)
  906. {
  907. CurrAmmo = count;
  908. flag = true;
  909. }
  910. }
  911.  
  912. //播放互动效果
  913. if (flag)
  914. {
  915. Throw(GlobalPosition, 0, Utils.RandomRangeInt(20, 50), Vector2.Zero, Utils.RandomRangeInt(-180, 180));
  916. }
  917. }
  918. else //没有武器
  919. {
  920. if (holster.PickupWeapon(this) == -1)
  921. {
  922. //替换武器
  923. var slot = holster.SlotList[holster.ActiveIndex];
  924. if (slot.Type == Attribute.WeightType)
  925. {
  926. roleMaster.ThrowWeapon();
  927. roleMaster.PickUpWeapon(this);
  928. }
  929. }
  930. }
  931. }
  932. }
  933.  
  934. /// <summary>
  935. /// 获取当前武器真实的旋转角度(弧度制), 由于武器旋转时加入了旋转吸附, 所以需要通过该函数来来知道当前武器的真实旋转角度
  936. /// </summary>
  937. public float GetRealGlobalRotation()
  938. {
  939. return Mathf.DegToRad(Master.MountPoint.RealAngle) + Rotation;
  940. }
  941.  
  942. /// <summary>
  943. /// 触发扔掉武器抛出的效果, 并不会管武器是否在武器袋中
  944. /// </summary>
  945. /// <param name="master">触发扔掉该武器的的角色</param>
  946. public void ThrowWeapon(Role master)
  947. {
  948. ThrowWeapon(master, master.GlobalPosition);
  949. }
  950.  
  951. /// <summary>
  952. /// 触发扔掉武器抛出的效果, 并不会管武器是否在武器袋中
  953. /// </summary>
  954. /// <param name="master">触发扔掉该武器的的角色</param>
  955. /// <param name="startPosition">投抛起始位置</param>
  956. public void ThrowWeapon(Role master, Vector2 startPosition)
  957. {
  958. //阴影偏移
  959. ShadowOffset = new Vector2(0, 2);
  960.  
  961. if (master.Face == FaceDirection.Left)
  962. {
  963. Scale *= new Vector2(1, -1);
  964. }
  965.  
  966. var angle = master.MountPoint.GlobalRotationDegrees;
  967. GlobalRotationDegrees = angle;
  968.  
  969. var startHeight = 6;
  970. var direction = angle + Utils.RandomRangeInt(-20, 20);
  971. var velocity = new Vector2(20, 0).Rotated(direction * Mathf.Pi / 180);
  972. var yf = Utils.RandomRangeInt(50, 70);
  973. var rotate = Utils.RandomRangeInt(-90, 90);
  974. Throw(startPosition, startHeight, yf, velocity, rotate);
  975. }
  976.  
  977. protected override void OnThrowOver()
  978. {
  979. //启用碰撞
  980. Collision.Disabled = false;
  981. AnimationPlayer.Play("floodlight");
  982. }
  983.  
  984. public override void PutDown(RoomLayerEnum layer, bool showShadow = true)
  985. {
  986. base.PutDown(layer, showShadow);
  987. AnimationPlayer.Play("floodlight");
  988. }
  989.  
  990. /// <summary>
  991. /// 触发拾起
  992. /// </summary>
  993. public void PickUpWeapon(Role master)
  994. {
  995. Master = master;
  996. if (master.IsAi && _originWeaponAttribute.AiUseAttribute != null)
  997. {
  998. _weaponAttribute = _originWeaponAttribute.AiUseAttribute;
  999. }
  1000. else
  1001. {
  1002. _weaponAttribute = _originWeaponAttribute;
  1003. }
  1004. //握把位置
  1005. AnimatedSprite.Position = Attribute.HoldPosition;
  1006. //停止动画
  1007. AnimationPlayer.Stop();
  1008. //清除泛白效果
  1009. ShaderMaterial sm = (ShaderMaterial)AnimatedSprite.Material;
  1010. sm.SetShaderParameter("schedule", 0);
  1011. ZIndex = 0;
  1012. //禁用碰撞
  1013. Collision.Disabled = true;
  1014. //清除 Ai 拾起标记
  1015. RemoveSign(SignNames.AiFindWeaponSign);
  1016. OnPickUp(master);
  1017. }
  1018.  
  1019. /// <summary>
  1020. /// 触发移除, 这个函数由 Holster 对象调用
  1021. /// </summary>
  1022. public void RemoveAt()
  1023. {
  1024. Master = null;
  1025. _weaponAttribute = _originWeaponAttribute;
  1026. AnimatedSprite.Position = Attribute.CenterPosition;
  1027. OnRemove();
  1028. }
  1029.  
  1030. /// <summary>
  1031. /// 触发启用武器
  1032. /// </summary>
  1033. public void Active()
  1034. {
  1035. //调整阴影
  1036. ShadowOffset = new Vector2(0, Master.GlobalPosition.Y - GlobalPosition.Y);
  1037. //枪口默认抬起角度
  1038. RotationDegrees = -Attribute.DefaultAngle;
  1039. ShowShadowSprite();
  1040. OnActive();
  1041. }
  1042.  
  1043. /// <summary>
  1044. /// 触发收起武器
  1045. /// </summary>
  1046. public void Conceal()
  1047. {
  1048. HideShadowSprite();
  1049. OnConceal();
  1050. }
  1051. //-------------------------------- Ai相关 -----------------------------
  1052.  
  1053. /// <summary>
  1054. /// 获取 Ai 对于该武器的评分, 评分越高, 代表 Ai 会越优先选择该武器, 如果为 -1, 则表示 Ai 不会使用该武器
  1055. /// </summary>
  1056. public float GetAiScore()
  1057. {
  1058. return 1;
  1059. }
  1060. }