Client LuaCsForBarotrauma
Camera.cs
2 using Lidgren.Network;
3 using Microsoft.Xna.Framework;
4 using Microsoft.Xna.Framework.Input;
5 using System;
6 
7 namespace Barotrauma
8 {
9  class Camera
10  {
11  public static bool FollowSub = true;
12 
13  private float? defaultZoom;
14  public float DefaultZoom
15  {
16  get { return defaultZoom ?? (GameSettings.CurrentConfig.EnableMouseLook ? 1.3f : 1.0f); }
17  set
18  {
19  defaultZoom = MathHelper.Clamp(value, 0.5f, 2.0f);
20  }
21  }
22 
23  private float zoomSmoothness = 8.0f;
24  public float ZoomSmoothness
25  {
26  get { return zoomSmoothness; }
27  set { zoomSmoothness = Math.Max(value, 0.01f); }
28  }
29  private float moveSmoothness = 8.0f;
30  public float MoveSmoothness
31  {
32  get { return moveSmoothness; }
33  set { moveSmoothness = Math.Max(value, 0.01f); }
34  }
35 
36  private float minZoom = 0.1f;
37  public float MinZoom
38  {
39  get { return minZoom; }
40  set { minZoom = MathHelper.Clamp(value, 0.001f, 10.0f); }
41  }
42 
43  private float maxZoom = 2.0f;
44  public float MaxZoom
45  {
46  get { return maxZoom; }
47  set { maxZoom = MathHelper.Clamp(value, 1.0f, 10.0f); }
48  }
49 
50  public float FreeCamMoveSpeed = 1.0f;
51 
52  private float zoom;
53 
54  private Matrix transform, shaderTransform, viewMatrix;
55  private Vector2 position;
56  private float rotation;
57 
58  private float angularVelocity;
59  private float angularDamping;
60  private float angularSpring;
61 
62  private Vector2 prevPosition;
63  private float prevZoom;
64 
65  public float Shake;
66  public Vector2 ShakePosition { get; private set; }
67  private float shakeTimer;
68 
69  public float MovementLockTimer;
70 
71  private float globalZoomScale = 1.0f;
72 
73  //used to smooth out the movement when in freecam
74  private float targetZoom;
75  private Vector2 velocity;
76 
77  public float Zoom
78  {
79  get { return zoom; }
80  set
81  {
82  zoom = MathHelper.Clamp(value, GameMain.DebugDraw ? 0.01f : MinZoom, MaxZoom);
83 
84  Vector2 center = WorldViewCenter;
85  float newWidth = Resolution.X / zoom;
86  float newHeight = Resolution.Y / zoom;
87 
88  WorldView = new Rectangle(
89  (int)(center.X - newWidth / 2.0f),
90  (int)(center.Y + newHeight / 2.0f),
91  (int)newWidth,
92  (int)newHeight);
93 
94  //UpdateTransform();
95  }
96  }
97 
98  public float Rotation
99  {
100  get { return rotation; }
101  set
102  {
103  if (!MathUtils.IsValid(value)) return;
104  rotation = value;
105  }
106  }
107 
108  public float AngularVelocity
109  {
110  get { return angularVelocity; }
111  set
112  {
113  if (!MathUtils.IsValid(value)) return;
114  angularVelocity = value;
115  }
116  }
117 
118  public float OffsetAmount { get; set; }
119 
120  public Point Resolution { get; private set; }
121 
122  //the area of the world inside the camera view
123  public Rectangle WorldView { get; private set; }
124 
125  public Vector2 WorldViewCenter
126  {
127  get
128  {
129  return new Vector2(
130  WorldView.X + WorldView.Width / 2.0f,
131  WorldView.Y - WorldView.Height / 2.0f);
132  }
133  }
134 
135  public Matrix Transform
136  {
137  get { return transform; }
138  }
139 
140  public Matrix ShaderTransform
141  {
142  get { return shaderTransform; }
143  }
144 
145  public Camera()
146  {
147  zoom = prevZoom = targetZoom = 1.0f;
148  rotation = 0.0f;
149  position = Vector2.Zero;
150 
151  CreateMatrices();
152 
153  UpdateTransform(false);
154  }
155 
156  public Vector2 TargetPos { get; set; }
157 
158  public Vector2 GetPosition()
159  {
160  return position;
161  }
162 
163  // Auxiliary function to move the camera
164  public void Translate(Vector2 amount)
165  {
166  position += amount;
167  }
168 
169  public void ClientWrite(in SegmentTableWriter<ClientNetSegment> segmentTableWriter, IWriteMessage msg)
170  {
171  if (Character.Controlled != null && !Character.Controlled.IsDead) { return; }
172 
173  segmentTableWriter.StartNewSegment(ClientNetSegment.SpectatingPos);
174  msg.WriteSingle(position.X);
175  msg.WriteSingle(position.Y);
176  }
177 
178  private void CreateMatrices()
179  {
181  }
182 
183  public void SetResolution(Point res)
184  {
185  Resolution = res;
186 
187  WorldView = new Rectangle(0, 0, res.X, res.Y);
188  viewMatrix = Matrix.CreateTranslation(new Vector3(res.X / 2.0f, res.Y / 2.0f, 0));
189  float newGlobalZoomScale = (float)new Vector2(GUI.UIWidth, Resolution.Y).Length() / GUI.ReferenceResolution.Length();
190  if (globalZoomScale > 0.0f)
191  {
192  Zoom *= newGlobalZoomScale / globalZoomScale;
193  targetZoom *= newGlobalZoomScale / globalZoomScale;
194  prevZoom *= newGlobalZoomScale / globalZoomScale;
195  }
196  globalZoomScale = newGlobalZoomScale;
197  }
198 
199  public void UpdateTransform(bool interpolate = true, bool updateListener = true)
200  {
201  if (GameMain.GraphicsWidth != Resolution.X ||
203  {
204  CreateMatrices();
205  }
206 
207  Vector2 interpolatedPosition = interpolate ? Timing.Interpolate(prevPosition, position) : position;
208 
209  float interpolatedZoom = interpolate ? Timing.Interpolate(prevZoom, zoom) : zoom;
210 
211  WorldView = new Rectangle((int)(interpolatedPosition.X - WorldView.Width / 2.0),
212  (int)(interpolatedPosition.Y + WorldView.Height / 2.0),
213  WorldView.Width, WorldView.Height);
214 
215  transform = Matrix.CreateTranslation(
216  new Vector3(-interpolatedPosition.X, interpolatedPosition.Y, 0)) *
217  Matrix.CreateScale(new Vector3(interpolatedZoom, interpolatedZoom, 1)) *
218  Matrix.CreateRotationZ(rotation) * viewMatrix;
219 
220  shaderTransform = Matrix.CreateTranslation(
221  new Vector3(
222  -interpolatedPosition.X - Resolution.X / interpolatedZoom / 2.0f,
223  -interpolatedPosition.Y - Resolution.Y / interpolatedZoom / 2.0f, 0)) *
224  Matrix.CreateScale(new Vector3(interpolatedZoom, interpolatedZoom, 1)) *
225 
226  viewMatrix * Matrix.CreateRotationZ(-rotation);
227 
228  if (updateListener)
229  {
230  if (Character.Controlled == null)
231  {
232  GameMain.SoundManager.ListenerPosition = new Vector3(WorldViewCenter.X, WorldViewCenter.Y, -(100.0f / zoom));
233  }
234  else
235  {
236  GameMain.SoundManager.ListenerPosition = new Vector3(Character.Controlled.WorldPosition.X, Character.Controlled.WorldPosition.Y, -(100.0f / zoom));
237  }
238  }
239 
240 
241  if (!interpolate)
242  {
243  prevPosition = position;
244  prevZoom = zoom;
245  }
246  }
247 
248  private Vector2 previousOffset;
249 
253  public bool Freeze { get; set; }
254 
255  public void MoveCamera(float deltaTime, bool allowMove = true, bool allowZoom = true, bool allowInput = true, bool? followSub = null)
256  {
257  prevPosition = position;
258  prevZoom = zoom;
259 
260  float moveSpeed = 20.0f / zoom;
261 
262  MovementLockTimer -= deltaTime;
263 
264  Vector2 moveCam = Vector2.Zero;
265  if (TargetPos == Vector2.Zero)
266  {
267  Vector2 moveInput = Vector2.Zero;
268  if (allowMove && !Freeze)
269  {
270  if (GUI.KeyboardDispatcher.Subscriber == null && allowInput && MovementLockTimer <= 0.0f)
271  {
272  if (PlayerInput.KeyDown(Keys.LeftShift)) { moveSpeed *= 2.0f; }
273  if (PlayerInput.KeyDown(Keys.LeftControl)) { moveSpeed *= 0.5f; }
274 
275  if (GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Left].IsDown()) { moveInput.X -= 1.0f; }
276  if (GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Right].IsDown()) { moveInput.X += 1.0f; }
277  if (GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Down].IsDown()) { moveInput.Y -= 1.0f; }
278  if (GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Up].IsDown()) { moveInput.Y += 1.0f; }
279  }
280 
281  velocity = Vector2.Lerp(velocity, moveInput, deltaTime * 10.0f);
282  moveCam = velocity * moveSpeed * deltaTime * FreeCamMoveSpeed * 60.0f;
283 
284  if (Screen.Selected == GameMain.GameScreen && (followSub ?? FollowSub) && GameMain.Instance is not { Paused: true })
285  {
286  var closestSub = Submarine.FindClosest(WorldViewCenter);
287  if (closestSub != null)
288  {
289  moveCam += FarseerPhysics.ConvertUnits.ToDisplayUnits(closestSub.Velocity * deltaTime);
290  }
291  }
292  }
293 
294  if (allowZoom)
295  {
296  Vector2 mouseInWorld = ScreenToWorld(PlayerInput.MousePosition);
297  Vector2 diffViewCenter;
298  diffViewCenter = (mouseInWorld - Position) * Zoom;
299  targetZoom = MathHelper.Clamp(
300  targetZoom + PlayerInput.ScrollWheelSpeed / 1000.0f * zoom,
301  GameMain.DebugDraw ? MinZoom * 0.1f : MinZoom,
302  MaxZoom);
303 
304  if (PlayerInput.KeyDown(Keys.LeftControl))
305  {
306  Zoom += (targetZoom - zoom) / (ZoomSmoothness * 10.0f);
307  }
308  else
309  {
310  Zoom = MathHelper.Lerp(Zoom, targetZoom, deltaTime * 10.0f);
311  }
312  if (!PlayerInput.KeyDown(Keys.F)) { Position = mouseInWorld - (diffViewCenter / Zoom); }
313  }
314  }
315  else if (allowMove)
316  {
317  Vector2 mousePos = PlayerInput.MousePosition;
318  Vector2 offset = mousePos - Resolution.ToVector2() / 2;
319  offset.X = offset.X / (Resolution.X * 0.4f);
320  offset.Y = -offset.Y / (Resolution.Y * 0.3f);
321  if (offset.LengthSquared() > 1.0f) offset.Normalize();
322  float offsetUnscaledLen = offset.Length();
323  offset *= OffsetAmount;
324  // Freeze the camera movement by default, when the cursor is on top of an ui element.
325  // Setting a positive value to the OffsetAmount, will override this behaviour.
326  if (GUI.MouseOn != null && OffsetAmount > 0)
327  {
328  Freeze = true;
329  }
331  {
332  offset *= 0;
333  Freeze = false;
334  }
335  if (Freeze)
336  {
337  if (offset.LengthSquared() > 0.001f) { offset = previousOffset; }
338  }
339  else
340  {
341  previousOffset = offset;
342  }
343 
344  if (allowZoom)
345  {
346  //how much to zoom out (zoom completely out when offset is 1000)
347  float zoomOutAmount = GetZoomAmount(offset);
348  //scaled zoom amount
349  float scaledZoom = MathHelper.Lerp(DefaultZoom, MinZoom, zoomOutAmount) * globalZoomScale;
350  //zoom in further if zoomOutAmount is low and resolution is lower than reference
351  float newZoom = scaledZoom * (MathHelper.Lerp(0.3f * (1f - Math.Min(globalZoomScale, 1f)), 0f,
352  (GameSettings.CurrentConfig.EnableMouseLook) ? (float)Math.Sqrt(offsetUnscaledLen) : 0.3f) + 1f);
353 
354  Zoom += (newZoom - zoom) / ZoomSmoothness;
355  }
356 
357  //force targetzoom to the current zoom value, so the camera stays at the same zoom when switching to freecam
358  targetZoom = Zoom;
359 
360  Vector2 diff = (TargetPos + offset) - position;
361 
362  moveCam = diff / MoveSmoothness;
363  }
364  rotation += angularVelocity * deltaTime;
365  angularVelocity *= (1.0f - angularDamping);
366  angularVelocity += -rotation * angularSpring;
367 
368  angularDamping = 0.05f;
369  angularSpring = 0.2f;
370 
371  if (Shake < 0.01f)
372  {
373  ShakePosition = Vector2.Zero;
374  shakeTimer = 0.0f;
375  }
376  else
377  {
378  shakeTimer += deltaTime * 5.0f;
379  Vector2 noisePos = new Vector2((float)PerlinNoise.CalculatePerlin(shakeTimer, shakeTimer, 0) - 0.5f, (float)PerlinNoise.CalculatePerlin(shakeTimer, shakeTimer, 0.5f) - 0.5f);
380 
381  ShakePosition = noisePos * Shake * 2.0f;
382  Shake = MathHelper.Lerp(Shake, 0.0f, deltaTime * 2.0f);
383  }
384 
385  Translate(moveCam + ShakePosition);
386  Freeze = false;
387  }
388 
389  public void StopMovement()
390  {
391  targetZoom = zoom;
392  velocity = Vector2.Zero;
393  angularVelocity = 0.0f;
394  rotation = 0.0f;
395  }
396 
397  public Vector2 Position
398  {
399  get { return position; }
400  set
401  {
402  if (!MathUtils.IsValid(value))
403  {
404  return;
405  }
406  position = value;
407  }
408  }
409 
410  public Vector2 ScreenToWorld(Vector2 coords)
411  {
412  Vector2 worldCoords = Vector2.Transform(coords, Matrix.Invert(transform));
413  return new Vector2(worldCoords.X, -worldCoords.Y);
414  }
415 
416  public Vector2 WorldToScreen(Vector2 coords)
417  {
418  coords.Y = -coords.Y;
419  //Vector2 screenCoords = Vector2.Transform(coords, transform);
420  return Vector2.Transform(coords, transform);
421  }
422 
423  private float GetZoomAmount(Vector2 offset)
424  {
425  return Math.Min(offset.Length() / 1000.0f, 1.0f);
426  }
427 
429  {
430  return GetZoomAmount(previousOffset);
431  }
432  }
433 }
Vector2 WorldToScreen(Vector2 coords)
Definition: Camera.cs:416
float MaxZoom
Definition: Camera.cs:45
Vector2 GetPosition()
Definition: Camera.cs:158
float? Zoom
Definition: Camera.cs:78
float AngularVelocity
Definition: Camera.cs:109
Vector2 ShakePosition
Definition: Camera.cs:66
bool Freeze
Resets to false each time the MoveCamera method is called.
Definition: Camera.cs:253
void MoveCamera(float deltaTime, bool allowMove=true, bool allowZoom=true, bool allowInput=true, bool? followSub=null)
Definition: Camera.cs:255
Matrix Transform
Definition: Camera.cs:136
float Rotation
Definition: Camera.cs:99
float??? DefaultZoom
Definition: Camera.cs:15
Point Resolution
Definition: Camera.cs:120
float MinZoom
Definition: Camera.cs:38
Vector2 ScreenToWorld(Vector2 coords)
Definition: Camera.cs:410
float FreeCamMoveSpeed
Definition: Camera.cs:50
void UpdateTransform(bool interpolate=true, bool updateListener=true)
Definition: Camera.cs:199
float OffsetAmount
Definition: Camera.cs:118
Vector2 Position
Definition: Camera.cs:398
Rectangle WorldView
Definition: Camera.cs:123
float ZoomSmoothness
Definition: Camera.cs:25
static bool FollowSub
Definition: Camera.cs:11
void ClientWrite(in SegmentTableWriter< ClientNetSegment > segmentTableWriter, IWriteMessage msg)
Definition: Camera.cs:169
void Translate(Vector2 amount)
Definition: Camera.cs:164
float MoveSmoothness
Definition: Camera.cs:31
float MovementLockTimer
Definition: Camera.cs:69
float GetZoomAmountFromPrevious()
Definition: Camera.cs:428
Vector2 WorldViewCenter
Definition: Camera.cs:126
void SetResolution(Point res)
Definition: Camera.cs:183
void StopMovement()
Definition: Camera.cs:389
Vector2 TargetPos
Definition: Camera.cs:156
Matrix ShaderTransform
Definition: Camera.cs:141
Triggers a "conversation popup" with text and support for different branching options.
Responsible for keeping track of the characters in the player crew, saving and loading their orders,...
virtual Vector2 WorldPosition
Definition: Entity.cs:49
static int GraphicsWidth
Definition: GameMain.cs:162
static int GraphicsHeight
Definition: GameMain.cs:168
static GameScreen GameScreen
Definition: GameMain.cs:52
static bool DebugDraw
Definition: GameMain.cs:29
static GameMain Instance
Definition: GameMain.cs:144
static Sounds.SoundManager SoundManager
Definition: GameMain.cs:80
static bool KeyDown(InputType inputType)
static Submarine FindClosest(Vector2 worldPosition, bool ignoreOutposts=false, bool ignoreOutsideLevel=true, bool ignoreRespawnShuttle=false, CharacterTeamType? teamType=null)
If has value, the sub must match the team type.