Client LuaCsForBarotrauma
BarotraumaClient/ClientSource/Items/Components/Holdable/RangedWeapon.cs
2 using Barotrauma.Sounds;
3 using FarseerPhysics;
4 using Microsoft.Xna.Framework;
5 using Microsoft.Xna.Framework.Graphics;
6 using System;
7 using System.Collections.Generic;
8 using System.Linq;
9 
11 {
12  partial class RangedWeapon : ItemComponent
13  {
14  protected Sprite crosshairSprite, crosshairPointerSprite;
15 
16  protected Vector2 crosshairPos, crosshairPointerPos;
17 
18  protected float currentCrossHairScale, currentCrossHairPointerScale;
19 
20  private RoundSound chargeSound;
21 
22  private SoundChannel chargeSoundChannel;
23 
24  [Serialize(defaultValue: "0.5, 1.5", IsPropertySaveable.No, description: "Pitch slides from X to Y over the charge time")]
26  {
27  get => _chargeSoundWindupPitchSlide;
28  set
29  {
30  _chargeSoundWindupPitchSlide = new Vector2(
31  Math.Max(value.X, SoundChannel.MinFrequencyMultiplier),
32  Math.Min(value.Y, SoundChannel.MaxFrequencyMultiplier));
33  }
34  }
35  private Vector2 _chargeSoundWindupPitchSlide;
36 
37  private readonly List<ParticleEmitter> particleEmitters = new List<ParticleEmitter>();
38  private readonly List<ParticleEmitter> particleEmitterCharges = new List<ParticleEmitter>();
39 
44  private float crossHairPosDirtyTimer;
45 
46  [Serialize(1.0f, IsPropertySaveable.No, description: "The scale of the crosshair sprite (if there is one).")]
47  public float CrossHairScale
48  {
49  get;
50  private set;
51  }
52 
53  partial void InitProjSpecific(ContentXElement rangedWeaponElement)
54  {
55  foreach (var subElement in rangedWeaponElement.Elements())
56  {
57  string textureDir = GetTextureDirectory(subElement);
58  switch (subElement.Name.ToString().ToLowerInvariant())
59  {
60  case "crosshair":
61  {
62  crosshairSprite = new Sprite(subElement, path: textureDir);
63  }
64  break;
65  case "crosshairpointer":
66  {
67  crosshairPointerSprite = new Sprite(subElement, path: textureDir);
68  }
69  break;
70  case "particleemitter":
71  particleEmitters.Add(new ParticleEmitter(subElement));
72  break;
73  case "particleemittercharge":
74  particleEmitterCharges.Add(new ParticleEmitter(subElement));
75  break;
76  case "chargesound":
77  chargeSound = RoundSound.Load(subElement, false);
78  break;
79  }
80  }
81  }
82 
83  public override void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam)
84  {
85  crossHairPosDirtyTimer -= deltaTime;
86  currentCrossHairScale = currentCrossHairPointerScale = cam == null ? 1.0f : cam.Zoom;
87  if (crosshairSprite != null)
88  {
89  Vector2 aimRefWorldPos = character.AimRefPosition;
90  if (character.Submarine != null) { aimRefWorldPos += character.Submarine.Position; }
91  Vector2 itemPos = cam.WorldToScreen(aimRefWorldPos);
92  float rotation = (item.body.Dir == 1.0f) ? item.body.Rotation : item.body.Rotation - MathHelper.Pi;
93  Vector2 barrelDir = new Vector2((float)Math.Cos(rotation), -(float)Math.Sin(rotation));
94 
95  Vector2 mouseDiff = itemPos - PlayerInput.MousePosition;
96  crosshairPos = new Vector2(
97  MathHelper.Clamp(itemPos.X + barrelDir.X * mouseDiff.Length(), 0, GameMain.GraphicsWidth),
98  MathHelper.Clamp(itemPos.Y + barrelDir.Y * mouseDiff.Length(), 0, GameMain.GraphicsHeight));
99 
100  float spread = GetSpread(character);
101  Projectile projectile = FindProjectile();
102  if (projectile != null)
103  {
104  spread += MathHelper.ToRadians(projectile.Spread);
105  }
106 
107  float crossHairDist = Vector2.Distance(item.WorldPosition, cam.ScreenToWorld(crosshairPos));
108  float spreadDist = (float)Math.Sin(spread) * crossHairDist;
109 
110  currentCrossHairPointerScale = MathHelper.Clamp(spreadDist / Math.Min(crosshairSprite.size.X, crosshairSprite.size.Y), 0.1f, 10.0f);
111  }
113  crosshairPointerPos = PlayerInput.MousePosition;
114  }
115 
116  public override void FlipX(bool relativeToSub)
117  {
118  crossHairPosDirtyTimer = 0.02f;
119  }
120  public override void FlipY(bool relativeToSub)
121  {
122  crossHairPosDirtyTimer = 0.02f;
123  }
124 
125  partial void UpdateProjSpecific(float deltaTime)
126  {
127  float chargeRatio = currentChargeTime / MaxChargeTime;
128 
129  switch (currentChargingState)
130  {
131  case ChargingState.WindingUp:
132  case ChargingState.WindingDown:
133  Vector2 particlePos = item.WorldPosition + ConvertUnits.ToDisplayUnits(TransformedBarrelPos);
134  float sizeMultiplier = Math.Clamp(chargeRatio, 0.1f, 1f);
135  foreach (ParticleEmitter emitter in particleEmitterCharges)
136  {
137  emitter.Emit(deltaTime, particlePos, hullGuess: item.CurrentHull, sizeMultiplier: sizeMultiplier, colorMultiplier: emitter.Prefab.Properties.ColorMultiplier);
138  }
139 
140  if (chargeSoundChannel == null || !chargeSoundChannel.IsPlaying)
141  {
142  if (chargeSound != null)
143  {
144  chargeSoundChannel = SoundPlayer.PlaySound(chargeSound.Sound, item.WorldPosition, chargeSound.Volume, chargeSound.Range, ignoreMuffling: chargeSound.IgnoreMuffling, freqMult: chargeSound.GetRandomFrequencyMultiplier());
145  if (chargeSoundChannel != null) { chargeSoundChannel.Looping = true; }
146  }
147  }
148  else if (chargeSoundChannel != null)
149  {
150  chargeSoundChannel.FrequencyMultiplier = MathHelper.Lerp(ChargeSoundWindupPitchSlide.X, ChargeSoundWindupPitchSlide.Y, chargeRatio);
151  chargeSoundChannel.Position = new Vector3(item.WorldPosition, 0.0f);
152  }
153  break;
154  default:
155  if (chargeSoundChannel != null)
156  {
157  if (chargeSoundChannel.IsPlaying)
158  {
159  chargeSoundChannel.FadeOutAndDispose();
160  chargeSoundChannel.Looping = false;
161  }
162  else
163  {
164  chargeSoundChannel = null;
165  }
166  }
167  break;
168  }
169  }
170 
171  public override void DrawHUD(SpriteBatch spriteBatch, Character character)
172  {
173  if (character == null || !character.IsKeyDown(InputType.Aim) || !character.CanAim) { return; }
174 
175  //camera focused on some other item/device, don't draw the crosshair
176  if (character.ViewTarget is Item viewTargetItem && viewTargetItem.Prefab.FocusOnSelected) { return; }
177  //don't draw the crosshair if the item is in some other type of equip slot than hands (e.g. assault rifle in the bag slot)
178  if (!character.HeldItems.Contains(item)) { return; }
179 
180  GUI.HideCursor = (crosshairSprite != null || crosshairPointerSprite != null) &&
181  GUI.MouseOn == null && !Inventory.IsMouseOnInventory && !GameMain.Instance.Paused;
182 
183  if (GUI.HideCursor && !character.AnimController.IsHoldingToRope)
184  {
185  if (crossHairPosDirtyTimer <= 0.0f)
186  {
187  crosshairSprite?.Draw(spriteBatch, crosshairPos, ReloadTimer <= 0.0f ? Color.White : Color.White * 0.2f, 0, currentCrossHairScale);
188  }
189  crosshairPointerSprite?.Draw(spriteBatch, crosshairPointerPos, 0, currentCrossHairPointerScale);
190  }
191 
192  if (GameMain.DebugDraw)
193  {
194  Vector2 barrelPos = item.DrawPosition + ConvertUnits.ToDisplayUnits(TransformedBarrelPos);
195  barrelPos = Screen.Selected.Cam.WorldToScreen(barrelPos);
196  GUI.DrawLine(spriteBatch, barrelPos - Vector2.UnitY * 3, barrelPos + Vector2.UnitY * 3, Color.Red);
197  GUI.DrawLine(spriteBatch, barrelPos - Vector2.UnitX * 3, barrelPos + Vector2.UnitX * 3, Color.Red);
198  }
199  }
200 
201  partial void LaunchProjSpecific()
202  {
203  Vector2 particlePos = item.WorldPosition + ConvertUnits.ToDisplayUnits(TransformedBarrelPos);
204  float rotation = item.body.Rotation;
205  if (item.body.Dir < 0.0f) { rotation += MathHelper.Pi; }
206  foreach (ParticleEmitter emitter in particleEmitters)
207  {
208  emitter.Emit(1.0f, particlePos, hullGuess: item.CurrentHull, angle: rotation, particleRotation: -rotation);
209  }
210  }
211 
212  protected override void RemoveComponentSpecific()
213  {
214  base.RemoveComponentSpecific();
216  crosshairSprite = null;
217  crosshairPointerSprite?.Remove();
218  crosshairSprite = null;
219  }
220  }
221 }
Vector2 WorldToScreen(Vector2 coords)
Definition: Camera.cs:416
float? Zoom
Definition: Camera.cs:78
Vector2 ScreenToWorld(Vector2 coords)
Definition: Camera.cs:410
IEnumerable< Item >?? HeldItems
Items the character has in their hand slots. Doesn't return nulls and only returns items held in both...
IEnumerable< ContentXElement > Elements()
virtual Vector2 DrawPosition
Definition: Entity.cs:51
Submarine Submarine
Definition: Entity.cs:53
static int GraphicsWidth
Definition: GameMain.cs:162
static int GraphicsHeight
Definition: GameMain.cs:168
static bool DebugDraw
Definition: GameMain.cs:29
static GameMain Instance
Definition: GameMain.cs:144
string GetTextureDirectory(ContentXElement subElement)
override void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam)
readonly ParticleEmitterPrefab Prefab
void Emit(float deltaTime, Vector2 position, Hull? hullGuess=null, float angle=0.0f, float particleRotation=0.0f, float velocityMultiplier=1.0f, float sizeMultiplier=1.0f, float amountMultiplier=1.0f, Color? colorMultiplier=null, ParticlePrefab? overrideParticle=null, bool mirrorAngle=false, Tuple< Vector2, Vector2 >? tracerPoints=null)
readonly ParticleEmitterProperties Properties
bool IsHoldingToRope
Is attached to something with a rope.
static ? RoundSound Load(ContentXElement element, bool stream=false)
Definition: RoundSound.cs:61
readonly float Range
Definition: RoundSound.cs:14
float GetRandomFrequencyMultiplier()
Definition: RoundSound.cs:54
readonly float Volume
Definition: RoundSound.cs:13
readonly bool IgnoreMuffling
Definition: RoundSound.cs:17
void Draw(ISpriteBatch spriteBatch, Vector2 pos, float rotate=0.0f, float scale=1.0f, SpriteEffects spriteEffect=SpriteEffects.None)