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. if (_originWeaponAttribute != _weaponAttribute) //Ai使用该武器, 有一定概率不消耗弹药
  606. {
  607. if (Utils.RandomRangeFloat(0, 1) < _weaponAttribute.AiAmmoConsumptionProbability) //触发消耗弹药
  608. {
  609. CurrAmmo -= UseAmmoCount();
  610. }
  611. }
  612. else
  613. {
  614. CurrAmmo -= UseAmmoCount();
  615. }
  616.  
  617. //开火间隙
  618. _fireInterval = 60 / Attribute.StartFiringSpeed;
  619. //攻击冷却
  620. _attackTimer += _fireInterval;
  621.  
  622. //触发开火函数
  623. OnFire();
  624.  
  625. //开火发射的子弹数量
  626. var bulletCount = Utils.RandomRangeInt(Attribute.MaxFireBulletCount, Attribute.MinFireBulletCount);
  627. //武器口角度
  628. var angle = new Vector2(GameConfig.ScatteringDistance, CurrScatteringRange).Angle();
  629.  
  630. //先算武器口方向
  631. var tempRotation = Utils.RandomRangeFloat(-angle, angle);
  632. var tempAngle = Mathf.RadToDeg(tempRotation);
  633.  
  634. //开火时枪口角度
  635. var fireRotation = Mathf.DegToRad(Master.MountPoint.RealAngle) + tempRotation;
  636. //创建子弹
  637. for (int i = 0; i < bulletCount; i++)
  638. {
  639. //发射子弹
  640. OnShoot(fireRotation);
  641. }
  642.  
  643. //当前的散射半径
  644. CurrScatteringRange = Mathf.Min(CurrScatteringRange + Attribute.ScatteringRangeAddValue,
  645. Attribute.FinalScatteringRange);
  646. //武器的旋转角度
  647. tempAngle -= Attribute.UpliftAngle;
  648. RotationDegrees = tempAngle;
  649. _fireAngle = tempAngle;
  650. //武器身位置
  651. var max = Mathf.Abs(Mathf.Max(Attribute.MaxBacklash, Attribute.MinBacklash));
  652. _currBacklashLength = Mathf.Clamp(
  653. _currBacklashLength - Utils.RandomRangeFloat(Attribute.MinBacklash, Attribute.MaxBacklash),
  654. -max, max
  655. );
  656. Position = new Vector2(_currBacklashLength, 0).Rotated(Rotation);
  657.  
  658. if (FireEvent != null)
  659. {
  660. FireEvent(this);
  661. }
  662. }
  663.  
  664. /// <summary>
  665. /// 获取武器攻击的目标层级
  666. /// </summary>
  667. /// <returns></returns>
  668. public uint GetAttackLayer()
  669. {
  670. return Master != null ? Master.AttackLayer : Role.DefaultAttackLayer;
  671. }
  672. /// <summary>
  673. /// 返回弹药是否到达上限
  674. /// </summary>
  675. public bool IsAmmoFull()
  676. {
  677. return CurrAmmo + ResidueAmmo >= Attribute.MaxAmmoCapacity;
  678. }
  679.  
  680. /// <summary>
  681. /// 返回弹夹是否打空
  682. /// </summary>
  683. public bool IsAmmoEmpty()
  684. {
  685. return CurrAmmo == 0;
  686. }
  687. /// <summary>
  688. /// 返回是否弹药耗尽
  689. /// </summary>
  690. public bool IsTotalAmmoEmpty()
  691. {
  692. return CurrAmmo + ResidueAmmo == 0;
  693. }
  694.  
  695. /// <summary>
  696. /// 强制修改当前弹夹弹药量
  697. /// </summary>
  698. public void SetCurrAmmo(int count)
  699. {
  700. CurrAmmo = Mathf.Clamp(count, 0, Attribute.AmmoCapacity);
  701. }
  702.  
  703. /// <summary>
  704. /// 强制修改备用弹药量
  705. /// </summary>
  706. public void SetResidueAmmo(int count)
  707. {
  708. ResidueAmmo = Mathf.Clamp(count, 0, Attribute.MaxAmmoCapacity - CurrAmmo);
  709. }
  710. /// <summary>
  711. /// 强制修改弹药量, 优先改动备用弹药
  712. /// </summary>
  713. public void SetTotalAmmo(int total)
  714. {
  715. if (total < 0)
  716. {
  717. return;
  718. }
  719. var totalAmmo = CurrAmmo + ResidueAmmo;
  720. if (totalAmmo == total)
  721. {
  722. return;
  723. }
  724. if (total > totalAmmo) //弹药增加
  725. {
  726. ResidueAmmo = Mathf.Min(total - CurrAmmo, Attribute.MaxAmmoCapacity - CurrAmmo);
  727. }
  728. else //弹药减少
  729. {
  730. if (CurrAmmo < total)
  731. {
  732. ResidueAmmo = total - CurrAmmo;
  733. }
  734. else
  735. {
  736. CurrAmmo = total;
  737. ResidueAmmo = 0;
  738. }
  739. }
  740. }
  741.  
  742. /// <summary>
  743. /// 拾起的弹药数量, 如果到达容量上限, 则返回拾取完毕后剩余的弹药数量
  744. /// </summary>
  745. /// <param name="count">弹药数量</param>
  746. private int PickUpAmmo(int count)
  747. {
  748. var num = ResidueAmmo;
  749. ResidueAmmo = Mathf.Min(ResidueAmmo + count, Attribute.MaxAmmoCapacity - CurrAmmo);
  750. return count - ResidueAmmo + num;
  751. }
  752.  
  753. /// <summary>
  754. /// 触发换弹
  755. /// </summary>
  756. public void Reload()
  757. {
  758. if (CurrAmmo < Attribute.AmmoCapacity && ResidueAmmo > 0 && !Reloading)
  759. {
  760. Reloading = true;
  761. ReloadTimer = Attribute.ReloadTime;
  762. OnReload();
  763. }
  764. }
  765.  
  766. /// <summary>
  767. /// 换弹计时器时间到, 执行换弹操作
  768. /// </summary>
  769. private void ReloadSuccess()
  770. {
  771. if (Attribute.AloneReload) //单独装填
  772. {
  773. if (ResidueAmmo >= Attribute.AloneReloadCount) //剩余子弹充足
  774. {
  775. if (CurrAmmo + Attribute.AloneReloadCount <= Attribute.AmmoCapacity)
  776. {
  777. ResidueAmmo -= Attribute.AloneReloadCount;
  778. CurrAmmo += Attribute.AloneReloadCount;
  779. }
  780. else //子弹满了
  781. {
  782. var num = Attribute.AmmoCapacity - CurrAmmo;
  783. CurrAmmo = Attribute.AmmoCapacity;
  784. ResidueAmmo -= num;
  785. }
  786. }
  787. else if (ResidueAmmo != 0) //剩余子弹不足
  788. {
  789. if (ResidueAmmo + CurrAmmo <= Attribute.AmmoCapacity)
  790. {
  791. CurrAmmo += ResidueAmmo;
  792. ResidueAmmo = 0;
  793. }
  794. else //子弹满了
  795. {
  796. var num = Attribute.AmmoCapacity - CurrAmmo;
  797. CurrAmmo = Attribute.AmmoCapacity;
  798. ResidueAmmo -= num;
  799. }
  800. }
  801.  
  802. if (ResidueAmmo == 0 || CurrAmmo == Attribute.AmmoCapacity) //换弹结束
  803. {
  804. Reloading = false;
  805. ReloadTimer = 0;
  806. OnReloadFinish();
  807. }
  808. else
  809. {
  810. ReloadTimer = Attribute.ReloadTime;
  811. OnReload();
  812. }
  813. }
  814. else //换弹结束
  815. {
  816. if (CurrAmmo + ResidueAmmo >= Attribute.AmmoCapacity)
  817. {
  818. ResidueAmmo -= Attribute.AmmoCapacity - CurrAmmo;
  819. CurrAmmo = Attribute.AmmoCapacity;
  820. }
  821. else
  822. {
  823. CurrAmmo += ResidueAmmo;
  824. ResidueAmmo = 0;
  825. }
  826.  
  827. Reloading = false;
  828. ReloadTimer = 0;
  829. OnReloadFinish();
  830. }
  831. }
  832.  
  833. public override CheckInteractiveResult CheckInteractive(ActivityObject master)
  834. {
  835. var result = new CheckInteractiveResult(this);
  836.  
  837. if (master is Role roleMaster) //碰到角色
  838. {
  839. if (Master == null)
  840. {
  841. var masterWeapon = roleMaster.Holster.ActiveWeapon;
  842. //查找是否有同类型武器
  843. var index = roleMaster.Holster.FindWeapon(ItemId);
  844. if (index != -1) //如果有这个武器
  845. {
  846. if (CurrAmmo + ResidueAmmo != 0) //子弹不为空
  847. {
  848. var targetWeapon = roleMaster.Holster.GetWeapon(index);
  849. if (!targetWeapon.IsAmmoFull()) //背包里面的武器子弹未满
  850. {
  851. //可以互动拾起弹药
  852. result.CanInteractive = true;
  853. result.Message = Attribute.Name;
  854. result.ShowIcon = ResourcePath.resource_sprite_ui_icon_icon_bullet_png;
  855. return result;
  856. }
  857. }
  858. }
  859. else //没有武器
  860. {
  861. if (roleMaster.Holster.CanPickupWeapon(this)) //能拾起武器
  862. {
  863. //可以互动, 拾起武器
  864. result.CanInteractive = true;
  865. result.Message = Attribute.Name;
  866. result.ShowIcon = ResourcePath.resource_sprite_ui_icon_icon_pickup_png;
  867. return result;
  868. }
  869. else if (masterWeapon != null && masterWeapon.Attribute.WeightType == Attribute.WeightType) //替换武器
  870. {
  871. //可以互动, 切换武器
  872. result.CanInteractive = true;
  873. result.Message = Attribute.Name;
  874. result.ShowIcon = ResourcePath.resource_sprite_ui_icon_icon_replace_png;
  875. return result;
  876. }
  877. }
  878. }
  879. }
  880.  
  881. return result;
  882. }
  883.  
  884. public override void Interactive(ActivityObject master)
  885. {
  886. if (master is Role roleMaster) //与role互动
  887. {
  888. var holster = roleMaster.Holster;
  889. //查找是否有同类型武器
  890. var index = holster.FindWeapon(ItemId);
  891. if (index != -1) //如果有这个武器
  892. {
  893. if (CurrAmmo + ResidueAmmo == 0) //没有子弹了
  894. {
  895. return;
  896. }
  897.  
  898. var weapon = holster.GetWeapon(index);
  899. //子弹上限
  900. var maxCount = Attribute.MaxAmmoCapacity;
  901. //是否捡到子弹
  902. var flag = false;
  903. if (ResidueAmmo > 0 && weapon.CurrAmmo + weapon.ResidueAmmo < maxCount)
  904. {
  905. var count = weapon.PickUpAmmo(ResidueAmmo);
  906. if (count != ResidueAmmo)
  907. {
  908. ResidueAmmo = count;
  909. flag = true;
  910. }
  911. }
  912.  
  913. if (CurrAmmo > 0 && weapon.CurrAmmo + weapon.ResidueAmmo < maxCount)
  914. {
  915. var count = weapon.PickUpAmmo(CurrAmmo);
  916. if (count != CurrAmmo)
  917. {
  918. CurrAmmo = count;
  919. flag = true;
  920. }
  921. }
  922.  
  923. //播放互动效果
  924. if (flag)
  925. {
  926. Throw(GlobalPosition, 0, Utils.RandomRangeInt(20, 50), Vector2.Zero, Utils.RandomRangeInt(-180, 180));
  927. }
  928. }
  929. else //没有武器
  930. {
  931. if (holster.PickupWeapon(this) == -1)
  932. {
  933. //替换武器
  934. var slot = holster.SlotList[holster.ActiveIndex];
  935. if (slot.Type == Attribute.WeightType)
  936. {
  937. roleMaster.ThrowWeapon();
  938. roleMaster.PickUpWeapon(this);
  939. }
  940. }
  941. }
  942. }
  943. }
  944.  
  945. /// <summary>
  946. /// 获取当前武器真实的旋转角度(弧度制), 由于武器旋转时加入了旋转吸附, 所以需要通过该函数来来知道当前武器的真实旋转角度
  947. /// </summary>
  948. public float GetRealGlobalRotation()
  949. {
  950. return Mathf.DegToRad(Master.MountPoint.RealAngle) + Rotation;
  951. }
  952.  
  953. /// <summary>
  954. /// 触发扔掉武器抛出的效果, 并不会管武器是否在武器袋中
  955. /// </summary>
  956. /// <param name="master">触发扔掉该武器的的角色</param>
  957. public void ThrowWeapon(Role master)
  958. {
  959. ThrowWeapon(master, master.GlobalPosition);
  960. }
  961.  
  962. /// <summary>
  963. /// 触发扔掉武器抛出的效果, 并不会管武器是否在武器袋中
  964. /// </summary>
  965. /// <param name="master">触发扔掉该武器的的角色</param>
  966. /// <param name="startPosition">投抛起始位置</param>
  967. public void ThrowWeapon(Role master, Vector2 startPosition)
  968. {
  969. //阴影偏移
  970. ShadowOffset = new Vector2(0, 2);
  971.  
  972. if (master.Face == FaceDirection.Left)
  973. {
  974. Scale *= new Vector2(1, -1);
  975. }
  976.  
  977. var angle = master.MountPoint.GlobalRotationDegrees;
  978. GlobalRotationDegrees = angle;
  979.  
  980. var startHeight = 6;
  981. var direction = angle + Utils.RandomRangeInt(-20, 20);
  982. var velocity = new Vector2(20, 0).Rotated(direction * Mathf.Pi / 180);
  983. var yf = Utils.RandomRangeInt(50, 70);
  984. var rotate = Utils.RandomRangeInt(-90, 90);
  985. Throw(startPosition, startHeight, yf, velocity, rotate);
  986. }
  987.  
  988. protected override void OnThrowOver()
  989. {
  990. //启用碰撞
  991. Collision.Disabled = false;
  992. AnimationPlayer.Play("floodlight");
  993. }
  994.  
  995. public override void PutDown(RoomLayerEnum layer, bool showShadow = true)
  996. {
  997. base.PutDown(layer, showShadow);
  998. AnimationPlayer.Play("floodlight");
  999. }
  1000.  
  1001. /// <summary>
  1002. /// 触发拾起
  1003. /// </summary>
  1004. public void PickUpWeapon(Role master)
  1005. {
  1006. Master = master;
  1007. if (master.IsAi && _originWeaponAttribute.AiUseAttribute != null)
  1008. {
  1009. _weaponAttribute = _originWeaponAttribute.AiUseAttribute;
  1010. }
  1011. else
  1012. {
  1013. _weaponAttribute = _originWeaponAttribute;
  1014. }
  1015. //握把位置
  1016. AnimatedSprite.Position = Attribute.HoldPosition;
  1017. //停止动画
  1018. AnimationPlayer.Stop();
  1019. //清除泛白效果
  1020. ShaderMaterial sm = (ShaderMaterial)AnimatedSprite.Material;
  1021. sm.SetShaderParameter("schedule", 0);
  1022. ZIndex = 0;
  1023. //禁用碰撞
  1024. Collision.Disabled = true;
  1025. //清除 Ai 拾起标记
  1026. RemoveSign(SignNames.AiFindWeaponSign);
  1027. OnPickUp(master);
  1028. }
  1029.  
  1030. /// <summary>
  1031. /// 触发移除, 这个函数由 Holster 对象调用
  1032. /// </summary>
  1033. public void RemoveAt()
  1034. {
  1035. Master = null;
  1036. _weaponAttribute = _originWeaponAttribute;
  1037. AnimatedSprite.Position = Attribute.CenterPosition;
  1038. OnRemove();
  1039. }
  1040.  
  1041. /// <summary>
  1042. /// 触发启用武器
  1043. /// </summary>
  1044. public void Active()
  1045. {
  1046. //调整阴影
  1047. ShadowOffset = new Vector2(0, Master.GlobalPosition.Y - GlobalPosition.Y);
  1048. //枪口默认抬起角度
  1049. RotationDegrees = -Attribute.DefaultAngle;
  1050. ShowShadowSprite();
  1051. OnActive();
  1052. }
  1053.  
  1054. /// <summary>
  1055. /// 触发收起武器
  1056. /// </summary>
  1057. public void Conceal()
  1058. {
  1059. HideShadowSprite();
  1060. OnConceal();
  1061. }
  1062. //-------------------------------- Ai相关 -----------------------------
  1063.  
  1064. /// <summary>
  1065. /// 获取 Ai 对于该武器的评分, 评分越高, 代表 Ai 会越优先选择该武器, 如果为 -1, 则表示 Ai 不会使用该武器
  1066. /// </summary>
  1067. public float GetAiScore()
  1068. {
  1069. return 1;
  1070. }
  1071. }