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