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