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