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