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