v0.13.0.11
This commit is contained in:
@@ -37,7 +37,7 @@ namespace Barotrauma
|
||||
public float MinZoom
|
||||
{
|
||||
get { return minZoom;}
|
||||
set { minZoom = MathHelper.Clamp(value, 0.01f, 10.0f); }
|
||||
set { minZoom = MathHelper.Clamp(value, 0.001f, 10.0f); }
|
||||
}
|
||||
|
||||
private float maxZoom = 2.0f;
|
||||
@@ -51,8 +51,6 @@ namespace Barotrauma
|
||||
|
||||
private float zoom;
|
||||
|
||||
private float offsetAmount;
|
||||
|
||||
private Matrix transform, shaderTransform, viewMatrix;
|
||||
private Vector2 position;
|
||||
private float rotation;
|
||||
@@ -67,16 +65,9 @@ namespace Barotrauma
|
||||
public float Shake;
|
||||
private Vector2 shakePosition;
|
||||
private float shakeTimer;
|
||||
|
||||
//the area of the world inside the camera view
|
||||
private Rectangle worldView;
|
||||
|
||||
private float globalZoomScale = 1.0f;
|
||||
|
||||
private Point resolution;
|
||||
|
||||
private Vector2 targetPos;
|
||||
|
||||
//used to smooth out the movement when in freecam
|
||||
private float targetZoom;
|
||||
private Vector2 velocity;
|
||||
@@ -89,10 +80,10 @@ namespace Barotrauma
|
||||
zoom = MathHelper.Clamp(value, GameMain.DebugDraw ? 0.01f : MinZoom, MaxZoom);
|
||||
|
||||
Vector2 center = WorldViewCenter;
|
||||
float newWidth = resolution.X / zoom;
|
||||
float newHeight = resolution.Y / zoom;
|
||||
float newWidth = Resolution.X / zoom;
|
||||
float newHeight = Resolution.Y / zoom;
|
||||
|
||||
worldView = new Rectangle(
|
||||
WorldView = new Rectangle(
|
||||
(int)(center.X - newWidth / 2.0f),
|
||||
(int)(center.Y + newHeight / 2.0f),
|
||||
(int)newWidth,
|
||||
@@ -122,29 +113,20 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public float OffsetAmount
|
||||
{
|
||||
get { return offsetAmount; }
|
||||
set { offsetAmount = value; }
|
||||
}
|
||||
public float OffsetAmount { get; set; }
|
||||
|
||||
public Point Resolution
|
||||
{
|
||||
get { return resolution; }
|
||||
}
|
||||
public Point Resolution { get; private set; }
|
||||
|
||||
public Rectangle WorldView
|
||||
{
|
||||
get { return worldView; }
|
||||
}
|
||||
//the area of the world inside the camera view
|
||||
public Rectangle WorldView { get; private set; }
|
||||
|
||||
public Vector2 WorldViewCenter
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Vector2(
|
||||
worldView.X + worldView.Width / 2.0f,
|
||||
worldView.Y - worldView.Height / 2.0f);
|
||||
WorldView.X + WorldView.Width / 2.0f,
|
||||
WorldView.Y - WorldView.Height / 2.0f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,12 +153,13 @@ namespace Barotrauma
|
||||
UpdateTransform(false);
|
||||
}
|
||||
|
||||
public Vector2 TargetPos
|
||||
~Camera()
|
||||
{
|
||||
get { return targetPos; }
|
||||
set { targetPos = value; }
|
||||
GameMain.Instance.ResolutionChanged -= CreateMatrices;
|
||||
}
|
||||
|
||||
public Vector2 TargetPos { get; set; }
|
||||
|
||||
public Vector2 GetPosition()
|
||||
{
|
||||
return position;
|
||||
@@ -204,21 +187,29 @@ namespace Barotrauma
|
||||
|
||||
public void SetResolution(Point res)
|
||||
{
|
||||
resolution = res;
|
||||
Resolution = res;
|
||||
|
||||
worldView = new Rectangle(0, 0, res.X, res.Y);
|
||||
WorldView = new Rectangle(0, 0, res.X, res.Y);
|
||||
viewMatrix = Matrix.CreateTranslation(new Vector3(res.X / 2.0f, res.Y / 2.0f, 0));
|
||||
globalZoomScale = (float)Math.Pow(new Vector2(GUI.UIWidth, resolution.Y).Length() / GUI.ReferenceResolution.Length(), 2);
|
||||
float newGlobalZoomScale = (float)new Vector2(GUI.UIWidth, Resolution.Y).Length() / GUI.ReferenceResolution.Length();
|
||||
if (globalZoomScale > 0.0f)
|
||||
{
|
||||
Zoom *= newGlobalZoomScale / globalZoomScale;
|
||||
targetZoom *= newGlobalZoomScale / globalZoomScale;
|
||||
prevZoom *= newGlobalZoomScale / globalZoomScale;
|
||||
}
|
||||
globalZoomScale = newGlobalZoomScale;
|
||||
}
|
||||
|
||||
public void UpdateTransform(bool interpolate = true)
|
||||
public void UpdateTransform(bool interpolate = true, bool updateListener = true)
|
||||
{
|
||||
Vector2 interpolatedPosition = interpolate ? Timing.Interpolate(prevPosition, position) : position;
|
||||
|
||||
float interpolatedZoom = interpolate ? Timing.Interpolate(prevZoom, zoom) : zoom;
|
||||
|
||||
worldView.X = (int)(interpolatedPosition.X - worldView.Width / 2.0);
|
||||
worldView.Y = (int)(interpolatedPosition.Y + worldView.Height / 2.0);
|
||||
WorldView = new Rectangle((int)(interpolatedPosition.X - WorldView.Width / 2.0),
|
||||
(int)(interpolatedPosition.Y + WorldView.Height / 2.0),
|
||||
WorldView.Width, WorldView.Height);
|
||||
|
||||
transform = Matrix.CreateTranslation(
|
||||
new Vector3(-interpolatedPosition.X, interpolatedPosition.Y, 0)) *
|
||||
@@ -227,19 +218,22 @@ namespace Barotrauma
|
||||
|
||||
shaderTransform = Matrix.CreateTranslation(
|
||||
new Vector3(
|
||||
-interpolatedPosition.X - resolution.X / interpolatedZoom / 2.0f,
|
||||
-interpolatedPosition.Y - resolution.Y / interpolatedZoom / 2.0f, 0)) *
|
||||
-interpolatedPosition.X - Resolution.X / interpolatedZoom / 2.0f,
|
||||
-interpolatedPosition.Y - Resolution.Y / interpolatedZoom / 2.0f, 0)) *
|
||||
Matrix.CreateScale(new Vector3(interpolatedZoom, interpolatedZoom, 1)) *
|
||||
|
||||
viewMatrix * Matrix.CreateRotationZ(-rotation);
|
||||
|
||||
if (Character.Controlled == null)
|
||||
if (updateListener)
|
||||
{
|
||||
GameMain.SoundManager.ListenerPosition = new Vector3(WorldViewCenter.X, WorldViewCenter.Y, -(100.0f / zoom));
|
||||
}
|
||||
else
|
||||
{
|
||||
GameMain.SoundManager.ListenerPosition = new Vector3(Character.Controlled.WorldPosition.X, Character.Controlled.WorldPosition.Y, -(100.0f / zoom));
|
||||
if (Character.Controlled == null)
|
||||
{
|
||||
GameMain.SoundManager.ListenerPosition = new Vector3(WorldViewCenter.X, WorldViewCenter.Y, -(100.0f / zoom));
|
||||
}
|
||||
else
|
||||
{
|
||||
GameMain.SoundManager.ListenerPosition = new Vector3(Character.Controlled.WorldPosition.X, Character.Controlled.WorldPosition.Y, -(100.0f / zoom));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -257,7 +251,7 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public bool Freeze { get; set; }
|
||||
|
||||
public void MoveCamera(float deltaTime, bool allowMove = true, bool allowZoom = true)
|
||||
public void MoveCamera(float deltaTime, bool allowMove = true, bool allowZoom = true, bool? followSub = null)
|
||||
{
|
||||
prevPosition = position;
|
||||
prevZoom = zoom;
|
||||
@@ -265,7 +259,7 @@ namespace Barotrauma
|
||||
float moveSpeed = 20.0f / zoom;
|
||||
|
||||
Vector2 moveCam = Vector2.Zero;
|
||||
if (targetPos == Vector2.Zero)
|
||||
if (TargetPos == Vector2.Zero)
|
||||
{
|
||||
Vector2 moveInput = Vector2.Zero;
|
||||
if (allowMove && !Freeze)
|
||||
@@ -284,7 +278,7 @@ namespace Barotrauma
|
||||
velocity = Vector2.Lerp(velocity, moveInput, deltaTime * 10.0f);
|
||||
moveCam = velocity * moveSpeed * deltaTime * FreeCamMoveSpeed * 60.0f;
|
||||
|
||||
if (Screen.Selected == GameMain.GameScreen && FollowSub)
|
||||
if (Screen.Selected == GameMain.GameScreen && (followSub ?? FollowSub))
|
||||
{
|
||||
var closestSub = Submarine.FindClosest(WorldViewCenter);
|
||||
if (closestSub != null)
|
||||
@@ -294,7 +288,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (allowZoom && GUI.MouseOn == null)
|
||||
if (allowZoom)
|
||||
{
|
||||
Vector2 mouseInWorld = ScreenToWorld(PlayerInput.MousePosition);
|
||||
Vector2 diffViewCenter;
|
||||
@@ -318,14 +312,15 @@ namespace Barotrauma
|
||||
else if (allowMove)
|
||||
{
|
||||
Vector2 mousePos = PlayerInput.MousePosition;
|
||||
Vector2 offset = mousePos - resolution.ToVector2() / 2;
|
||||
offset.X = offset.X / (resolution.X * 0.4f);
|
||||
offset.Y = -offset.Y / (resolution.Y * 0.3f);
|
||||
Vector2 offset = mousePos - Resolution.ToVector2() / 2;
|
||||
offset.X = offset.X / (Resolution.X * 0.4f);
|
||||
offset.Y = -offset.Y / (Resolution.Y * 0.3f);
|
||||
if (offset.LengthSquared() > 1.0f) offset.Normalize();
|
||||
offset *= offsetAmount;
|
||||
float offsetUnscaledLen = offset.Length();
|
||||
offset *= OffsetAmount;
|
||||
// Freeze the camera movement by default, when the cursor is on top of an ui element.
|
||||
// Setting a positive value to the OffsetAmount, will override this behaviour.
|
||||
if (GUI.MouseOn != null && offsetAmount > 0)
|
||||
if (GUI.MouseOn != null && OffsetAmount > 0)
|
||||
{
|
||||
Freeze = true;
|
||||
}
|
||||
@@ -336,7 +331,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (Freeze)
|
||||
{
|
||||
offset = previousOffset;
|
||||
if (offset.LengthSquared() > 0.001f) { offset = previousOffset; }
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -344,24 +339,19 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
//how much to zoom out (zoom completely out when offset is 1000)
|
||||
float zoomOutAmount = GetZoomAmount(offset);
|
||||
//zoom amount when resolution is not taken into account
|
||||
float unscaledZoom = MathHelper.Lerp(DefaultZoom, MinZoom, zoomOutAmount);
|
||||
//zoom with resolution taken into account (zoom further out on smaller resolutions)
|
||||
float scaledZoom = unscaledZoom * globalZoomScale;
|
||||
|
||||
//an ad-hoc way of allowing the players to have roughly the same maximum view distance regardless of the resolution,
|
||||
//while still keeping the zoom around 1.0 when not looking further away (because otherwise we'd always be downsampling
|
||||
//on lower resolutions, which doesn't look that good)
|
||||
float newZoom = MathHelper.Lerp(unscaledZoom, scaledZoom,
|
||||
(GameMain.Config == null || GameMain.Config.EnableMouseLook) ? (float)Math.Sqrt(zoomOutAmount) : 0.3f);
|
||||
float zoomOutAmount = GetZoomAmount(offset);
|
||||
//scaled zoom amount
|
||||
float scaledZoom = MathHelper.Lerp(DefaultZoom, MinZoom, zoomOutAmount) * globalZoomScale;
|
||||
//zoom in further if zoomOutAmount is low and resolution is lower than reference
|
||||
float newZoom = scaledZoom * (MathHelper.Lerp(0.3f * (1f - Math.Min(globalZoomScale, 1f)), 0f,
|
||||
(GameMain.Config == null || GameMain.Config.EnableMouseLook) ? (float)Math.Sqrt(offsetUnscaledLen) : 0.3f) + 1f);
|
||||
|
||||
Zoom += (newZoom - zoom) / ZoomSmoothness;
|
||||
|
||||
//force targetzoom to the current zoom value, so the camera stays at the same zoom when switching to freecam
|
||||
targetZoom = Zoom;
|
||||
|
||||
Vector2 diff = (targetPos + offset) - position;
|
||||
Vector2 diff = (TargetPos + offset) - position;
|
||||
|
||||
moveCam = diff / MoveSmoothness;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@ namespace Barotrauma
|
||||
{
|
||||
if (!ShowAITargets) { return; }
|
||||
var pos = new Vector2(WorldPosition.X, -WorldPosition.Y);
|
||||
float thickness = 1 / Screen.Selected.Cam.Zoom;
|
||||
|
||||
float offset = MathUtils.VectorToAngle(new Vector2(sectorDir.X, -sectorDir.Y)) - (sectorRad / 2f);
|
||||
if (soundRange > 0.0f)
|
||||
{
|
||||
Color color;
|
||||
@@ -26,8 +29,16 @@ namespace Barotrauma
|
||||
{
|
||||
color = Color.OrangeRed;
|
||||
}
|
||||
ShapeExtensions.DrawCircle(spriteBatch, pos, SoundRange, 100, color, thickness: 1 / Screen.Selected.Cam.Zoom);
|
||||
ShapeExtensions.DrawCircle(spriteBatch, pos, 3, 8, color, thickness: 2 / Screen.Selected.Cam.Zoom);
|
||||
|
||||
if (sectorRad < MathHelper.TwoPi)
|
||||
{
|
||||
spriteBatch.DrawSector(pos, SoundRange, sectorRad, 100, color, offset: offset, thickness: thickness);
|
||||
}
|
||||
else
|
||||
{
|
||||
spriteBatch.DrawCircle(pos, SoundRange, 100, color, thickness: thickness);
|
||||
}
|
||||
spriteBatch.DrawCircle(pos, 3, 8, color, thickness: 2 / Screen.Selected.Cam.Zoom);
|
||||
GUI.DrawLine(spriteBatch, pos, pos + Vector2.UnitY * SoundRange, color, width: (int)(1 / Screen.Selected.Cam.Zoom) + 1);
|
||||
}
|
||||
if (sightRange > 0.0f)
|
||||
@@ -47,7 +58,14 @@ namespace Barotrauma
|
||||
// disable the indicators for structures and hulls, because they clutter the debug view
|
||||
return;
|
||||
}
|
||||
ShapeExtensions.DrawCircle(spriteBatch, pos, SightRange, 100, color, thickness: 1 / Screen.Selected.Cam.Zoom);
|
||||
if (sectorRad < MathHelper.TwoPi)
|
||||
{
|
||||
spriteBatch.DrawSector(pos, SightRange, sectorRad, 100, color, offset: offset, thickness: thickness);
|
||||
}
|
||||
else
|
||||
{
|
||||
spriteBatch.DrawCircle(pos, SightRange, 100, color, thickness: thickness);
|
||||
}
|
||||
ShapeExtensions.DrawCircle(spriteBatch, pos, 6, 8, color, thickness: 2 / Screen.Selected.Cam.Zoom);
|
||||
GUI.DrawLine(spriteBatch, pos, pos + Vector2.UnitY * SightRange, color, width: (int)(1 / Screen.Selected.Cam.Zoom) + 1);
|
||||
}
|
||||
|
||||
@@ -1,20 +1,12 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using FarseerPhysics;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class HumanAIController : AIController
|
||||
{
|
||||
partial void InitProjSpecific()
|
||||
{
|
||||
/*if (GameMain.GameSession != null && GameMain.GameSession.CrewManager != null)
|
||||
{
|
||||
CurrentOrder = Order.GetPrefab("dismissed");
|
||||
objectiveManager.SetOrder(CurrentOrder, "", null);
|
||||
GameMain.GameSession.CrewManager.SetCharacterOrder(Character, CurrentOrder, null, null);
|
||||
}*/
|
||||
}
|
||||
|
||||
public override void DebugDraw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch)
|
||||
{
|
||||
if (Character == Character.Controlled) { return; }
|
||||
@@ -22,6 +14,7 @@ namespace Barotrauma
|
||||
Vector2 pos = Character.WorldPosition;
|
||||
pos.Y = -pos.Y;
|
||||
Vector2 textOffset = new Vector2(-40, -160);
|
||||
textOffset.Y -= Math.Max(ObjectiveManager.CurrentOrders.Count - 1, 0) * 20;
|
||||
|
||||
if (SelectedAiTarget?.Entity != null)
|
||||
{
|
||||
@@ -29,56 +22,55 @@ namespace Barotrauma
|
||||
//GUI.DrawString(spriteBatch, pos + textOffset, $"AI TARGET: {SelectedAiTarget.Entity.ToString()}", Color.White, Color.Black);
|
||||
}
|
||||
|
||||
GUI.DrawString(spriteBatch, pos + textOffset, Character.Name, Color.White, Color.Black);
|
||||
Vector2 stringDrawPos = pos + textOffset;
|
||||
GUI.DrawString(spriteBatch, stringDrawPos, Character.Name, Color.White, Color.Black);
|
||||
|
||||
var currentOrder = ObjectiveManager.CurrentOrder;
|
||||
if (currentOrder != null)
|
||||
if (ObjectiveManager.CurrentOrders.Any())
|
||||
{
|
||||
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 20), $"ORDER: {currentOrder.DebugTag} ({currentOrder.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
|
||||
var currentOrders = ObjectiveManager.CurrentOrders;
|
||||
currentOrders.Sort((x, y) => y.ManualPriority.CompareTo(x.ManualPriority));
|
||||
for (int i = 0; i < currentOrders.Count; i++)
|
||||
{
|
||||
stringDrawPos += new Vector2(0, 20);
|
||||
var order = currentOrders[i];
|
||||
GUI.DrawString(spriteBatch, stringDrawPos, $"ORDER {i + 1}: {order.Objective.DebugTag} ({order.Objective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
|
||||
}
|
||||
}
|
||||
else if (ObjectiveManager.WaitTimer > 0)
|
||||
{
|
||||
GUI.DrawString(spriteBatch, pos + new Vector2(0, 20), $"Waiting... {ObjectiveManager.WaitTimer.FormatZeroDecimal()}", Color.White, Color.Black);
|
||||
stringDrawPos += new Vector2(0, 20);
|
||||
GUI.DrawString(spriteBatch, stringDrawPos - textOffset, $"Waiting... {ObjectiveManager.WaitTimer.FormatZeroDecimal()}", Color.White, Color.Black);
|
||||
}
|
||||
var currentObjective = ObjectiveManager.CurrentObjective;
|
||||
if (currentObjective != null)
|
||||
{
|
||||
int offset = currentOrder != null ? 20 : 0;
|
||||
int offset = currentOrder != null ? 20 + ((ObjectiveManager.CurrentOrders.Count - 1) * 20) : 0;
|
||||
if (currentOrder == null || currentOrder.Priority <= 0)
|
||||
{
|
||||
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 20 + offset), $"MAIN OBJECTIVE: {currentObjective.DebugTag} ({currentObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
|
||||
stringDrawPos += new Vector2(0, 20);
|
||||
GUI.DrawString(spriteBatch, stringDrawPos, $"MAIN OBJECTIVE: {currentObjective.DebugTag} ({currentObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
|
||||
}
|
||||
var subObjective = currentObjective.CurrentSubObjective;
|
||||
if (subObjective != null)
|
||||
{
|
||||
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 40 + offset), $"SUBOBJECTIVE: {subObjective.DebugTag} ({subObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
|
||||
stringDrawPos += new Vector2(0, 20);
|
||||
GUI.DrawString(spriteBatch, stringDrawPos, $"SUBOBJECTIVE: {subObjective.DebugTag} ({subObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
|
||||
}
|
||||
var activeObjective = ObjectiveManager.GetActiveObjective();
|
||||
if (activeObjective != null)
|
||||
{
|
||||
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 60 + offset), $"ACTIVE OBJECTIVE: {activeObjective.DebugTag} ({activeObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
|
||||
stringDrawPos += new Vector2(0, 20);
|
||||
GUI.DrawString(spriteBatch, stringDrawPos, $"ACTIVE OBJECTIVE: {activeObjective.DebugTag} ({activeObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 objectiveStringDrawPos = stringDrawPos + new Vector2(120, 40);
|
||||
for (int i = 0; i < ObjectiveManager.Objectives.Count; i++)
|
||||
{
|
||||
var objective = ObjectiveManager.Objectives[i];
|
||||
int offsetMultiplier;
|
||||
if (ObjectiveManager.CurrentOrder == null)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
offsetMultiplier = i - 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
offsetMultiplier = i + 1;
|
||||
}
|
||||
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(120, offsetMultiplier * 18 + 100), $"{objective.DebugTag} ({objective.Priority.FormatZeroDecimal()})", Color.White, Color.Black * 0.5f);
|
||||
GUI.DrawString(spriteBatch, objectiveStringDrawPos, $"{objective.DebugTag} ({objective.Priority.FormatZeroDecimal()})", Color.White, Color.Black * 0.5f);
|
||||
objectiveStringDrawPos += new Vector2(0, 18);
|
||||
}
|
||||
|
||||
if (steeringManager is IndoorsSteeringManager pathSteering)
|
||||
@@ -106,7 +98,7 @@ namespace Barotrauma
|
||||
new Vector2(path.CurrentNode.DrawPosition.X, -path.CurrentNode.DrawPosition.Y),
|
||||
Color.BlueViolet, 0, 3);
|
||||
|
||||
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 100), "Path cost: " + path.Cost.FormatZeroDecimal(), Color.White, Color.Black * 0.5f);
|
||||
GUI.DrawString(spriteBatch, stringDrawPos + new Vector2(0, 40), "Path cost: " + path.Cost.FormatZeroDecimal(), Color.White, Color.Black * 0.5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,11 @@ namespace Barotrauma
|
||||
set
|
||||
{
|
||||
if (controlled == value) return;
|
||||
if ((!(controlled is null)) && (!(Screen.Selected?.Cam is null)) && value is null)
|
||||
{
|
||||
Screen.Selected.Cam.TargetPos = Vector2.Zero;
|
||||
Lights.LightManager.ViewTarget = null;
|
||||
}
|
||||
controlled = value;
|
||||
if (controlled != null) controlled.Enabled = true;
|
||||
CharacterHealth.OpenHealthWindow = null;
|
||||
@@ -96,6 +101,13 @@ namespace Barotrauma
|
||||
get { return chromaticAberrationStrength; }
|
||||
set { chromaticAberrationStrength = MathHelper.Clamp(value, 0.0f, 100.0f); }
|
||||
}
|
||||
|
||||
private float grainStrength;
|
||||
public float GrainStrength
|
||||
{
|
||||
get => grainStrength;
|
||||
set => grainStrength = MathHelper.Clamp(value, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
private readonly List<ParticleEmitter> bloodEmitters = new List<ParticleEmitter>();
|
||||
public IEnumerable<ParticleEmitter> BloodEmitters
|
||||
@@ -115,6 +127,9 @@ namespace Barotrauma
|
||||
get { return gibEmitters; }
|
||||
}
|
||||
|
||||
public static bool IsMouseOnUI => GUI.MouseOn != null ||
|
||||
(CharacterInventory.IsMouseOnInventory() && !CharacterInventory.DraggingItemToWorld);
|
||||
|
||||
public class ObjectiveEntity
|
||||
{
|
||||
public Entity Entity;
|
||||
@@ -217,8 +232,7 @@ namespace Barotrauma
|
||||
float targetOffsetAmount = 0.0f;
|
||||
if (moveCam)
|
||||
{
|
||||
if (NeedsAir &&
|
||||
pressureProtection < 80.0f &&
|
||||
if (NeedsAir && !IsProtectedFromPressure() &&
|
||||
(AnimController.CurrentHull == null || AnimController.CurrentHull.LethalPressure > 0.0f))
|
||||
{
|
||||
float pressure = AnimController.CurrentHull == null ? 100.0f : AnimController.CurrentHull.LethalPressure;
|
||||
@@ -285,6 +299,10 @@ namespace Barotrauma
|
||||
cam.OffsetAmount = targetOffsetAmount = 0.0f;
|
||||
}
|
||||
}
|
||||
else if (IsMouseOnUI)
|
||||
{
|
||||
targetOffsetAmount = cam.OffsetAmount;
|
||||
}
|
||||
else if (Vector2.DistanceSquared(AnimController.Limbs[0].SimPosition, mouseSimPos) > 1.0f)
|
||||
{
|
||||
Body body = Submarine.CheckVisibility(AnimController.Limbs[0].SimPosition, mouseSimPos);
|
||||
@@ -375,25 +393,63 @@ namespace Barotrauma
|
||||
|
||||
partial void KillProjSpecific(CauseOfDeathType causeOfDeath, Affliction causeOfDeathAffliction, bool log)
|
||||
{
|
||||
HintManager.OnCharacterKilled(this);
|
||||
|
||||
if (GameMain.NetworkMember != null && controlled == this)
|
||||
{
|
||||
string chatMessage = CauseOfDeath.Type == CauseOfDeathType.Affliction ?
|
||||
CauseOfDeath.Affliction.SelfCauseOfDeathDescription :
|
||||
TextManager.Get("Self_CauseOfDeathDescription." + CauseOfDeath.Type.ToString(), fallBackTag: "Self_CauseOfDeathDescription.Damage");
|
||||
|
||||
if (GameMain.Client != null) chatMessage += " " + TextManager.Get("DeathChatNotification");
|
||||
if (GameMain.Client != null) { chatMessage += " " + TextManager.Get("DeathChatNotification"); }
|
||||
|
||||
if (GameMain.NetworkMember.RespawnManager?.UseRespawnPrompt ?? false)
|
||||
{
|
||||
CoroutineManager.InvokeAfter(() =>
|
||||
{
|
||||
if (controlled != null || (!(GameMain.GameSession?.IsRunning ?? false))) { return; }
|
||||
var respawnPrompt = new GUIMessageBox(
|
||||
TextManager.Get("tutorial.tryagainheader"), TextManager.Get("respawnquestionprompt"),
|
||||
new string[] { TextManager.Get("respawnquestionpromptrespawn"), TextManager.Get("respawnquestionpromptwait") });
|
||||
respawnPrompt.Buttons[0].OnClicked += (btn, userdata) =>
|
||||
{
|
||||
GameMain.Client?.SendRespawnPromptResponse(waitForNextRoundRespawn: false);
|
||||
respawnPrompt.Close();
|
||||
return true;
|
||||
};
|
||||
respawnPrompt.Buttons[1].OnClicked += (btn, userdata) =>
|
||||
{
|
||||
GameMain.Client?.SendRespawnPromptResponse(waitForNextRoundRespawn: true);
|
||||
respawnPrompt.Close();
|
||||
return true;
|
||||
};
|
||||
}, delay: 5.0f);
|
||||
}
|
||||
|
||||
GameMain.NetworkMember.AddChatMessage(chatMessage, ChatMessageType.Dead);
|
||||
GameMain.LightManager.LosEnabled = false;
|
||||
controlled = null;
|
||||
if (!(Screen.Selected?.Cam is null))
|
||||
{
|
||||
Screen.Selected.Cam.TargetPos = Vector2.Zero;
|
||||
Lights.LightManager.ViewTarget = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PlaySound(CharacterSound.SoundType.Die);
|
||||
}
|
||||
|
||||
partial void DisposeProjSpecific()
|
||||
{
|
||||
if (controlled == this) controlled = null;
|
||||
if (controlled == this)
|
||||
{
|
||||
controlled = null;
|
||||
if (!(Screen.Selected?.Cam is null))
|
||||
{
|
||||
Screen.Selected.Cam.TargetPos = Vector2.Zero;
|
||||
Lights.LightManager.ViewTarget = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (GameMain.GameSession?.CrewManager != null &&
|
||||
GameMain.GameSession.CrewManager.GetCharacters().Contains(this))
|
||||
@@ -634,9 +690,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
partial void SetOrderProjSpecific(Order order, string orderOption)
|
||||
partial void SetOrderProjSpecific(Order order, string orderOption, int priority)
|
||||
{
|
||||
GameMain.GameSession?.CrewManager?.AddCurrentOrderIcon(this, order, orderOption);
|
||||
GameMain.GameSession?.CrewManager?.AddCurrentOrderIcon(this, order, orderOption, priority);
|
||||
}
|
||||
|
||||
public static void AddAllToGUIUpdateList()
|
||||
@@ -815,7 +871,7 @@ namespace Barotrauma
|
||||
iconPos.Y = -iconPos.Y;
|
||||
nameColor = iconStyle.Color;
|
||||
var icon = iconStyle.Sprites[GUIComponent.ComponentState.None].First();
|
||||
float iconScale = 30.0f / icon.Sprite.size.X / cam.Zoom;
|
||||
float iconScale = (30.0f / icon.Sprite.size.X / cam.Zoom) * GUI.Scale;
|
||||
icon.Sprite.Draw(spriteBatch, iconPos + new Vector2(-35.0f, -25.0f), iconStyle.Color * hudInfoAlpha, scale: iconScale);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
@@ -10,7 +11,57 @@ using System.Linq;
|
||||
namespace Barotrauma
|
||||
{
|
||||
class CharacterHUD
|
||||
{
|
||||
{
|
||||
const float BossHealthBarDuration = 120.0f;
|
||||
|
||||
class BossHealthBar
|
||||
{
|
||||
public readonly Character Character;
|
||||
public float FadeTimer;
|
||||
|
||||
public readonly GUIComponent TopContainer;
|
||||
public readonly GUIComponent SideContainer;
|
||||
|
||||
public readonly GUIProgressBar TopHealthBar;
|
||||
public readonly GUIProgressBar SideHealthBar;
|
||||
|
||||
public BossHealthBar(Character character)
|
||||
{
|
||||
Character = character;
|
||||
FadeTimer = BossHealthBarDuration;
|
||||
|
||||
TopContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.18f, 0.03f), HUDFrame.RectTransform, Anchor.TopCenter)
|
||||
{
|
||||
MinSize = new Point(100, 50),
|
||||
RelativeOffset = new Vector2(0.0f, 0.01f)
|
||||
}, isHorizontal: false, childAnchor: Anchor.TopCenter);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.4f), TopContainer.RectTransform), character.DisplayName, textAlignment: Alignment.Center, textColor: GUI.Style.Red);
|
||||
TopHealthBar = new GUIProgressBar(new RectTransform(new Vector2(1.0f, 0.6f), TopContainer.RectTransform)
|
||||
{
|
||||
MinSize = new Point(100, HUDLayoutSettings.HealthBarArea.Size.Y)
|
||||
}, barSize: 0.0f, style: "CharacterHealthBarCentered")
|
||||
{
|
||||
Color = GUI.Style.Red
|
||||
};
|
||||
|
||||
SideContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), bossHealthContainer.RectTransform)
|
||||
{
|
||||
MinSize = new Point(80, 60)
|
||||
}, isHorizontal: false, childAnchor: Anchor.TopRight);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), SideContainer.RectTransform), character.DisplayName, textAlignment: Alignment.CenterRight, textColor: GUI.Style.Red);
|
||||
SideHealthBar = new GUIProgressBar(new RectTransform(new Vector2(1.0f, 0.7f), SideContainer.RectTransform), barSize: 0.0f, style: "CharacterHealthBar")
|
||||
{
|
||||
Color = GUI.Style.Red
|
||||
};
|
||||
|
||||
TopContainer.Visible = SideContainer.Visible = false;
|
||||
TopContainer.CanBeFocused = false;
|
||||
TopContainer.Children.ForEach(c => c.CanBeFocused = false);
|
||||
SideContainer.CanBeFocused = false;
|
||||
SideContainer.Children.ForEach(c => c.CanBeFocused = false);
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly Dictionary<ISpatialEntity, int> orderIndicatorCount = new Dictionary<ISpatialEntity, int>();
|
||||
const float ItemOverlayDelay = 1.0f;
|
||||
private static Item focusedItem;
|
||||
@@ -19,8 +70,12 @@ namespace Barotrauma
|
||||
private static readonly List<Item> brokenItems = new List<Item>();
|
||||
private static float brokenItemsCheckTimer;
|
||||
|
||||
private static readonly List<BossHealthBar> bossHealthBars = new List<BossHealthBar>();
|
||||
|
||||
private static readonly Dictionary<string, string> cachedHudTexts = new Dictionary<string, string>();
|
||||
|
||||
private static GUILayoutGroup bossHealthContainer;
|
||||
|
||||
private static GUIFrame hudFrame;
|
||||
public static GUIFrame HUDFrame
|
||||
{
|
||||
@@ -33,6 +88,13 @@ namespace Barotrauma
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
bossHealthContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.15f, 0.5f), hudFrame.RectTransform, Anchor.CenterRight)
|
||||
{
|
||||
RelativeOffset = new Vector2(0.005f, 0.0f)
|
||||
})
|
||||
{
|
||||
AbsoluteSpacing = GUI.IntScale(10)
|
||||
};
|
||||
}
|
||||
return hudFrame;
|
||||
}
|
||||
@@ -70,7 +132,7 @@ namespace Barotrauma
|
||||
|
||||
public static void AddToGUIUpdateList(Character character)
|
||||
{
|
||||
if (GUI.DisableHUD) return;
|
||||
if (GUI.DisableHUD) { return; }
|
||||
|
||||
if (!character.IsIncapacitated && character.Stun <= 0.0f && !IsCampaignInterfaceOpen)
|
||||
{
|
||||
@@ -83,7 +145,7 @@ namespace Barotrauma
|
||||
|
||||
foreach (ItemComponent ic in item.Components)
|
||||
{
|
||||
if (ic.DrawHudWhenEquipped) ic.AddToGUIUpdateList();
|
||||
if (ic.DrawHudWhenEquipped) { ic.AddToGUIUpdateList(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -99,13 +161,14 @@ namespace Barotrauma
|
||||
|
||||
public static void Update(float deltaTime, Character character, Camera cam)
|
||||
{
|
||||
UpdateBossHealthBars(deltaTime);
|
||||
|
||||
if (GUI.DisableHUD)
|
||||
{
|
||||
if (character.Inventory != null && !LockInventory(character))
|
||||
{
|
||||
character.Inventory.UpdateSlotInput();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -130,17 +193,6 @@ namespace Barotrauma
|
||||
{
|
||||
character.Inventory.ClearSubInventories();
|
||||
}
|
||||
|
||||
for (int i = 0; i < character.Inventory.Capacity; i++)
|
||||
{
|
||||
var item = character.Inventory.GetItemAt(i);
|
||||
if (item == null || character.Inventory.SlotTypes[i] == InvSlotType.Any) { continue; }
|
||||
|
||||
foreach (ItemComponent ic in item.Components)
|
||||
{
|
||||
if (ic.DrawHudWhenEquipped) ic.UpdateHUD(character, deltaTime, cam);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (character.IsHumanoid && character.SelectedCharacter != null && character.SelectedCharacter.Inventory != null)
|
||||
@@ -221,10 +273,10 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (DrawIcon(character.CurrentOrder))
|
||||
if (character.GetCurrentOrderWithTopPriority()?.Order is Order currentOrder && DrawIcon(currentOrder))
|
||||
{
|
||||
DrawOrderIndicator(spriteBatch, cam, character, character.CurrentOrder, 1.0f);
|
||||
}
|
||||
DrawOrderIndicator(spriteBatch, cam, character, currentOrder, 1.0f);
|
||||
}
|
||||
|
||||
static bool DrawIcon(Order o) =>
|
||||
o != null &&
|
||||
@@ -253,7 +305,7 @@ namespace Barotrauma
|
||||
return Math.Min((maxDistance - dist) / maxDistance * 2.0f, 1.0f);
|
||||
}
|
||||
|
||||
if (!character.IsIncapacitated && character.Stun <= 0.0f && !IsCampaignInterfaceOpen && (!character.IsKeyDown(InputType.Aim) || character.HeldItems.Any(it => it?.GetComponent<Sprayer>() == null)))
|
||||
if (!character.IsIncapacitated && character.Stun <= 0.0f && !IsCampaignInterfaceOpen && (!character.IsKeyDown(InputType.Aim) || character.HeldItems.None(it => it?.GetComponent<Sprayer>() != null)))
|
||||
{
|
||||
if (character.FocusedCharacter != null && character.FocusedCharacter.CanBeSelected)
|
||||
{
|
||||
@@ -499,6 +551,86 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public static void ShowBossHealthBar(Character character)
|
||||
{
|
||||
if (character == null || character.IsDead || character.Removed) { return; }
|
||||
|
||||
var existingBar = bossHealthBars.Find(b => b.Character == character);
|
||||
if (existingBar != null)
|
||||
{
|
||||
existingBar.FadeTimer = BossHealthBarDuration;
|
||||
return;
|
||||
}
|
||||
|
||||
if (bossHealthBars.Count > 5)
|
||||
{
|
||||
BossHealthBar oldestHealthBar = bossHealthBars.First();
|
||||
foreach (var bar in bossHealthBars)
|
||||
{
|
||||
if (bar.TopHealthBar.BarSize < oldestHealthBar.TopHealthBar.BarSize)
|
||||
{
|
||||
oldestHealthBar = bar;
|
||||
}
|
||||
}
|
||||
oldestHealthBar.FadeTimer = Math.Min(oldestHealthBar.FadeTimer, 1.0f);
|
||||
}
|
||||
|
||||
bossHealthBars.Add(new BossHealthBar(character));
|
||||
}
|
||||
|
||||
public static void UpdateBossHealthBars(float deltaTime)
|
||||
{
|
||||
for (int i = 0; i < bossHealthBars.Count; i++)
|
||||
{
|
||||
var bossHealthBar = bossHealthBars[i];
|
||||
|
||||
bool showTopBar = i == 0;
|
||||
if (showTopBar != bossHealthBar.TopContainer.Visible)
|
||||
{
|
||||
bossHealthContainer.Recalculate();
|
||||
}
|
||||
|
||||
bossHealthBar.TopContainer.Visible = showTopBar;
|
||||
bossHealthBar.SideContainer.Visible = !bossHealthBar.TopContainer.Visible;
|
||||
|
||||
float health = bossHealthBar.Character.Vitality / bossHealthBar.Character.MaxVitality;
|
||||
|
||||
float alpha = Math.Min(bossHealthBar.FadeTimer, 1.0f);
|
||||
foreach (var c in bossHealthBar.SideContainer.GetAllChildren().Concat(bossHealthBar.TopContainer.GetAllChildren()))
|
||||
{
|
||||
c.Color = new Color(c.Color, (byte)(alpha * 255));
|
||||
if (c is GUITextBlock textBlock)
|
||||
{
|
||||
textBlock.TextColor = new Color(bossHealthBar.Character.IsDead ? Color.Gray : textBlock.TextColor, (byte)(alpha * 255));
|
||||
}
|
||||
}
|
||||
|
||||
bossHealthBar.TopHealthBar.BarSize = bossHealthBar.SideHealthBar.BarSize = health;
|
||||
|
||||
if (bossHealthBar.Character.Removed || !bossHealthBar.Character.Enabled)
|
||||
{
|
||||
bossHealthBar.FadeTimer = Math.Min(bossHealthBar.FadeTimer, 1.0f);
|
||||
}
|
||||
else if (bossHealthBar.Character.IsDead)
|
||||
{
|
||||
bossHealthBar.FadeTimer = Math.Min(bossHealthBar.FadeTimer, 5.0f);
|
||||
}
|
||||
bossHealthBar.FadeTimer -= deltaTime;
|
||||
}
|
||||
|
||||
for (int i = bossHealthBars.Count - 1; i >= 0 ; i--)
|
||||
{
|
||||
var bossHealthBar = bossHealthBars[i];
|
||||
if (bossHealthBar.FadeTimer <= 0)
|
||||
{
|
||||
bossHealthBar.SideContainer.Parent?.RemoveChild(bossHealthBar.SideContainer);
|
||||
bossHealthBar.TopContainer.Parent?.RemoveChild(bossHealthBar.TopContainer);
|
||||
bossHealthBars.RemoveAt(i);
|
||||
bossHealthContainer.Recalculate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool LockInventory(Character character)
|
||||
{
|
||||
if (character?.Inventory == null || !character.AllowInput || character.LockHands || IsCampaignInterfaceOpen) { return true; }
|
||||
|
||||
@@ -100,7 +100,22 @@ namespace Barotrauma
|
||||
Color textColor = Color.White * (0.5f + skill.Level / 200.0f);
|
||||
|
||||
var skillName = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), skillsArea.RectTransform), TextManager.Get("SkillName." + skill.Identifier), textColor: textColor, font: font) { Padding = Vector4.Zero };
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), skillName.RectTransform), ((int)skill.Level).ToString(), textColor: textColor, font: font, textAlignment: Alignment.CenterRight);
|
||||
|
||||
float modifiedSkillLevel = skill.Level;
|
||||
if (Character != null)
|
||||
{
|
||||
modifiedSkillLevel = Character.GetSkillLevel(skill.Identifier);
|
||||
}
|
||||
if (!MathUtils.NearlyEqual(MathF.Round(modifiedSkillLevel), MathF.Round(skill.Level)))
|
||||
{
|
||||
int skillChange = (int)MathF.Round(modifiedSkillLevel - skill.Level);
|
||||
string changeText = $"{(skillChange > 0 ? "+" : "") + skillChange}";
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), skillName.RectTransform), $"{(int)skill.Level} ({changeText})", textColor: textColor, font: font, textAlignment: Alignment.CenterRight);
|
||||
}
|
||||
else
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), skillName.RectTransform), ((int)skill.Level).ToString(), textColor: textColor, font: font, textAlignment: Alignment.CenterRight);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Character != null && Character.IsDead)
|
||||
@@ -529,6 +544,7 @@ namespace Barotrauma
|
||||
{
|
||||
ushort infoID = inc.ReadUInt16();
|
||||
string newName = inc.ReadString();
|
||||
string originalName = inc.ReadString();
|
||||
int gender = inc.ReadByte();
|
||||
int race = inc.ReadByte();
|
||||
int headSpriteID = inc.ReadByte();
|
||||
@@ -556,7 +572,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
// TODO: animations
|
||||
CharacterInfo ch = new CharacterInfo(speciesName, newName, jobPrefab, ragdollFile, variant)
|
||||
CharacterInfo ch = new CharacterInfo(speciesName, newName, originalName, jobPrefab, ragdollFile, variant)
|
||||
{
|
||||
ID = infoID,
|
||||
};
|
||||
|
||||
@@ -292,7 +292,7 @@ namespace Barotrauma
|
||||
break;
|
||||
case ServerNetObject.ENTITY_EVENT:
|
||||
|
||||
int eventType = msg.ReadRangedInteger(0, 5);
|
||||
int eventType = msg.ReadRangedInteger(0, 6);
|
||||
switch (eventType)
|
||||
{
|
||||
case 0: //NetEntityEvent.Type.InventoryState
|
||||
@@ -330,6 +330,8 @@ namespace Barotrauma
|
||||
GameMain.Client.HasSpawned = true;
|
||||
GameMain.Client.Character = this;
|
||||
GameMain.LightManager.LosEnabled = true;
|
||||
GameMain.LightManager.LosAlpha = 1f;
|
||||
GameMain.Client.WaitForNextRoundRespawn = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -390,6 +392,19 @@ namespace Barotrauma
|
||||
byte campaignInteractionType = msg.ReadByte();
|
||||
(GameMain.GameSession?.GameMode as CampaignMode)?.AssignNPCMenuInteraction(this, (CampaignMode.InteractionType)campaignInteractionType);
|
||||
break;
|
||||
case 6: //NetEntityEvent.Type.ObjectiveManagerOrderState
|
||||
bool properData = msg.ReadBoolean();
|
||||
if (!properData) { break; }
|
||||
int orderIndex = msg.ReadRangedInteger(0, Order.PrefabList.Count);
|
||||
var orderPrefab = Order.PrefabList[orderIndex];
|
||||
string option = null;
|
||||
if (orderPrefab.HasOptions)
|
||||
{
|
||||
int optionIndex = msg.ReadRangedInteger(0, orderPrefab.Options.Length);
|
||||
option = orderPrefab.Options[optionIndex];
|
||||
}
|
||||
GameMain.GameSession.CrewManager.SetHighlightedOrderIcon(this, orderPrefab.Identifier, option);
|
||||
break;
|
||||
}
|
||||
msg.ReadPadBits();
|
||||
break;
|
||||
@@ -441,13 +456,15 @@ namespace Barotrauma
|
||||
(GameMain.GameSession.GameMode as CampaignMode)?.AssignNPCMenuInteraction(character, character.CampaignInteractionType);
|
||||
}
|
||||
|
||||
// Check if the character has a current order
|
||||
if (inc.ReadBoolean())
|
||||
// Check if the character has current orders
|
||||
int orderCount = inc.ReadByte();
|
||||
for (int i = 0; i < orderCount; i++)
|
||||
{
|
||||
int orderPrefabIndex = inc.ReadByte();
|
||||
Entity targetEntity = FindEntityByID(inc.ReadUInt16());
|
||||
Character orderGiver = inc.ReadBoolean() ? FindEntityByID(inc.ReadUInt16()) as Character : null;
|
||||
int orderOptionIndex = inc.ReadByte();
|
||||
int orderPriority = inc.ReadByte();
|
||||
OrderTarget targetPosition = null;
|
||||
if (inc.ReadBoolean())
|
||||
{
|
||||
@@ -468,7 +485,7 @@ namespace Barotrauma
|
||||
new Order(orderPrefab, targetPosition, orderGiver: orderGiver);
|
||||
character.SetOrder(order,
|
||||
orderOptionIndex >= 0 && orderOptionIndex < orderPrefab.Options.Length ? orderPrefab.Options[orderOptionIndex] : null,
|
||||
orderGiver, speak: false);
|
||||
orderPriority, orderGiver, speak: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -487,7 +504,7 @@ namespace Barotrauma
|
||||
character.ReadStatus(inc);
|
||||
}
|
||||
|
||||
if (character.IsHuman && character.TeamID != CharacterTeamType.FriendlyNPC && !character.IsDead)
|
||||
if (character.IsHuman && character.TeamID != CharacterTeamType.FriendlyNPC && character.TeamID != CharacterTeamType.None && !character.IsDead)
|
||||
{
|
||||
CharacterInfo duplicateCharacterInfo = GameMain.GameSession.CrewManager.GetCharacterInfos().FirstOrDefault(c => c.ID == info.ID);
|
||||
GameMain.GameSession.CrewManager.RemoveCharacterInfo(duplicateCharacterInfo);
|
||||
@@ -501,6 +518,7 @@ namespace Barotrauma
|
||||
if (!character.IsDead) { Controlled = character; }
|
||||
|
||||
GameMain.LightManager.LosEnabled = true;
|
||||
GameMain.LightManager.LosAlpha = 1f;
|
||||
|
||||
character.memInput.Clear();
|
||||
character.memState.Clear();
|
||||
|
||||
@@ -240,6 +240,8 @@ namespace Barotrauma
|
||||
Character.Controlled.SelectedConstruction = null;
|
||||
}
|
||||
}
|
||||
|
||||
HintManager.OnShowHealthInterface();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,6 +297,7 @@ namespace Barotrauma
|
||||
barSize: 1.0f, color: GUI.Style.HealthBarColorHigh, style: horizontal ? "CharacterHealthBar" : "GUIProgressBarVertical")
|
||||
{
|
||||
HoverCursor = CursorState.Hand,
|
||||
ToolTip = TextManager.GetWithVariable("hudbutton.healthinterface", "[key]", GameMain.Config.KeyBindText(InputType.Health)),
|
||||
Enabled = true,
|
||||
IsHorizontal = horizontal
|
||||
};
|
||||
@@ -668,12 +671,17 @@ namespace Barotrauma
|
||||
bloodParticleTimer -= deltaTime * (affliction.Strength / 10.0f);
|
||||
if (bloodParticleTimer <= 0.0f)
|
||||
{
|
||||
var emitter = Character.BloodEmitters.FirstOrDefault();
|
||||
float particleMinScale = emitter != null ? emitter.Prefab.ScaleMin : 0.5f;
|
||||
float particleMaxScale = emitter != null ? emitter.Prefab.ScaleMax : 1;
|
||||
float severity = Math.Min(affliction.Strength / affliction.Prefab.MaxStrength * Character.Params.BleedParticleMultiplier, 1);
|
||||
float bloodParticleSize = MathHelper.Lerp(particleMinScale, particleMaxScale, severity);
|
||||
bool inWater = Character.AnimController.InWater;
|
||||
float bloodParticleSize = MathHelper.Lerp(0.5f, 1.0f, affliction.Strength / 100.0f);
|
||||
if (!inWater)
|
||||
{
|
||||
bloodParticleSize *= 2.0f;
|
||||
}
|
||||
|
||||
var blood = GameMain.ParticleManager.CreateParticle(
|
||||
inWater ? Character.Params.BleedParticleWater : Character.Params.BleedParticleAir,
|
||||
targetLimb.WorldPosition, Rand.Vector(affliction.Strength), 0.0f, Character.AnimController.CurrentHull);
|
||||
@@ -682,7 +690,7 @@ namespace Barotrauma
|
||||
{
|
||||
blood.Size *= bloodParticleSize;
|
||||
}
|
||||
bloodParticleTimer = 1.0f;
|
||||
bloodParticleTimer = MathHelper.Lerp(2, 0.5f, severity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -713,6 +721,7 @@ namespace Barotrauma
|
||||
int dmgPerSecond = Math.Sign(a2.DamagePerSecond - a1.DamagePerSecond);
|
||||
return dmgPerSecond != 0 ? dmgPerSecond : Math.Sign(a1.Strength - a1.Strength);
|
||||
});
|
||||
HintManager.OnAfflictionDisplayed(Character, currentDisplayedAfflictions);
|
||||
updateDisplayedAfflictionsTimer = UpdateDisplayedAfflictionsInterval;
|
||||
}
|
||||
|
||||
@@ -732,6 +741,7 @@ namespace Barotrauma
|
||||
float distortSpeed = 0.0f;
|
||||
float radialDistortStrength = 0.0f;
|
||||
float chromaticAberrationStrength = 0.0f;
|
||||
float grainStrength = 0.0f;
|
||||
|
||||
if (Character.IsUnconscious)
|
||||
{
|
||||
@@ -752,6 +762,7 @@ namespace Barotrauma
|
||||
blurStrength = Math.Max(blurStrength, affliction.GetScreenBlurStrength());
|
||||
radialDistortStrength = Math.Max(radialDistortStrength, affliction.GetRadialDistortStrength());
|
||||
chromaticAberrationStrength = Math.Max(chromaticAberrationStrength, affliction.GetChromaticAberrationStrength());
|
||||
grainStrength = Math.Max(grainStrength, affliction.GetScreenGrainStrength());
|
||||
}
|
||||
foreach (LimbHealth limbHealth in limbHealths)
|
||||
{
|
||||
@@ -766,6 +777,7 @@ namespace Barotrauma
|
||||
|
||||
Character.RadialDistortStrength = radialDistortStrength;
|
||||
Character.ChromaticAberrationStrength = chromaticAberrationStrength;
|
||||
Character.GrainStrength = grainStrength;
|
||||
if (blurStrength > 0.0f)
|
||||
{
|
||||
distortTimer = (distortTimer + deltaTime * distortSpeed) % MathHelper.TwoPi;
|
||||
@@ -986,8 +998,8 @@ namespace Barotrauma
|
||||
|
||||
cprButton.Visible =
|
||||
Character == Character.Controlled?.SelectedCharacter
|
||||
&& (Character.IsUnconscious || Character.Stun > 0.0f)
|
||||
&& !Character.IsDead
|
||||
&& Character.IsKnockedDown
|
||||
&& openHealthWindow == this;
|
||||
cprButton.IgnoreLayoutGroups = !cprButton.Visible;
|
||||
cprButton.Selected =
|
||||
@@ -1183,7 +1195,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private Color GetAfflictionIconColor(AfflictionPrefab prefab, Affliction affliction)
|
||||
public static Color GetAfflictionIconColor(AfflictionPrefab prefab, Affliction affliction)
|
||||
{
|
||||
// No specific colors, use generic
|
||||
if (prefab.IconColors == null)
|
||||
@@ -1203,6 +1215,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public static Color GetAfflictionIconColor(Affliction affliction) => GetAfflictionIconColor(affliction.Prefab, affliction);
|
||||
|
||||
private void UpdateAfflictionContainer(LimbHealth selectedLimb)
|
||||
{
|
||||
selectedLimbText.Text = selectedLimb == null ? "" : selectedLimb.Name;
|
||||
@@ -1270,7 +1284,7 @@ namespace Barotrauma
|
||||
|
||||
var afflictionIcon = new GUIImage(new RectTransform(Vector2.One * 0.8f, button.RectTransform, Anchor.Center), affliction.Prefab.Icon, scaleToFit: true)
|
||||
{
|
||||
Color = GetAfflictionIconColor(affliction.Prefab, affliction),
|
||||
Color = GetAfflictionIconColor(affliction),
|
||||
CanBeFocused = false
|
||||
};
|
||||
afflictionIcon.PressedColor = afflictionIcon.Color;
|
||||
@@ -1906,7 +1920,7 @@ namespace Barotrauma
|
||||
float alpha = MathHelper.Lerp(0.3f, 1.0f,
|
||||
(affliction.Strength - showIconThreshold) / Math.Min(affliction.Prefab.MaxStrength - showIconThreshold, 10.0f));
|
||||
|
||||
affliction.Prefab.Icon.Draw(spriteBatch, iconPos - iconSize / 2.0f, GetAfflictionIconColor(affliction.Prefab, affliction) * alpha, 0, iconScale);
|
||||
affliction.Prefab.Icon.Draw(spriteBatch, iconPos - iconSize / 2.0f, GetAfflictionIconColor(affliction) * alpha, 0, iconScale);
|
||||
iconPos += new Vector2(10.0f, 20.0f) * iconScale;
|
||||
}
|
||||
|
||||
|
||||
@@ -299,7 +299,12 @@ namespace Barotrauma
|
||||
{
|
||||
var textContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.0f), listBox.Content.RectTransform), style: "InnerFrame", color: Color.White)
|
||||
{
|
||||
CanBeFocused = false
|
||||
CanBeFocused = true,
|
||||
OnSecondaryClicked = (component, data) =>
|
||||
{
|
||||
GUIContextMenu.CreateContextMenu(new ContextMenuOption("editor.copytoclipboard", true, () => { Clipboard.SetText(msg.Text); }));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
var textBlock = new GUITextBlock(new RectTransform(new Point(listBox.Content.Rect.Width - 5, 0), textContainer.RectTransform, Anchor.TopLeft) { AbsoluteOffset = new Point(2, 2) },
|
||||
msg.Text, textAlignment: Alignment.TopLeft, font: GUI.SmallFont, wrap: true)
|
||||
@@ -480,7 +485,7 @@ namespace Barotrauma
|
||||
var subInfo = new SubmarineInfo(string.Join(" ", args));
|
||||
Submarine.MainSub = Submarine.Load(subInfo, true);
|
||||
}
|
||||
GameMain.SubEditorScreen.Select();
|
||||
GameMain.SubEditorScreen.Select(enableAutoSave: Screen.Selected != GameMain.GameScreen);
|
||||
}, isCheat: true));
|
||||
|
||||
commands.Add(new Command("editparticles|particleeditor", "editparticles/particleeditor: Switch to the Particle Editor to edit particle effects.", (string[] args) =>
|
||||
@@ -620,6 +625,11 @@ namespace Barotrauma
|
||||
NewMessage(SubEditorScreen.ShouldDrawGrid ? "Enabled submarine grid." : "Disabled submarine grid.", GUI.Style.Green);
|
||||
}));
|
||||
|
||||
commands.Add(new Command("spreadsheetexport", "Export items in format recognized by the spreadsheet importer.", (string[] args) =>
|
||||
{
|
||||
SpreadsheetExport.Export();
|
||||
}));
|
||||
|
||||
commands.Add(new Command("wikiimage_character", "Save an image of the currently controlled character with a transparent background.", (string[] args) =>
|
||||
{
|
||||
if (Character.Controlled == null) { return; }
|
||||
@@ -649,6 +659,7 @@ namespace Barotrauma
|
||||
AssignRelayToServer("bindkey", false);
|
||||
AssignRelayToServer("unbindkey", false);
|
||||
AssignRelayToServer("savebinds", false);
|
||||
AssignRelayToServer("spreadsheetexport", false);
|
||||
#if DEBUG
|
||||
AssignRelayToServer("crash", false);
|
||||
AssignRelayToServer("showballastflorasprite", false);
|
||||
@@ -1236,6 +1247,7 @@ namespace Barotrauma
|
||||
GameMain.DebugDraw = false;
|
||||
GameMain.LightManager.LightingEnabled = true;
|
||||
GameMain.LightManager.LosEnabled = true;
|
||||
GameMain.LightManager.LosAlpha = 1f;
|
||||
}
|
||||
NewMessage(HumanAIController.debugai ? "AI debug info visible" : "AI debug info hidden", Color.White);
|
||||
});
|
||||
@@ -1523,7 +1535,9 @@ namespace Barotrauma
|
||||
List<string> lines = missingTags.Select(t => "\"" + t.Key + "\"\n missing from " + string.Join(", ", t.Value)).ToList();
|
||||
|
||||
string filePath = "missingloca.txt";
|
||||
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
|
||||
File.WriteAllLines(filePath, lines);
|
||||
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
|
||||
ToolBox.OpenFileWithShell(Path.GetFullPath(filePath));
|
||||
TextManager.Language = "English";
|
||||
}));
|
||||
@@ -1532,7 +1546,9 @@ namespace Barotrauma
|
||||
{
|
||||
var debugLines = EventSet.GetDebugStatistics();
|
||||
string filePath = "eventstats.txt";
|
||||
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
|
||||
File.WriteAllLines(filePath, debugLines);
|
||||
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
|
||||
ToolBox.OpenFileWithShell(Path.GetFullPath(filePath));
|
||||
}));
|
||||
|
||||
@@ -1705,6 +1721,15 @@ namespace Barotrauma
|
||||
{
|
||||
GameMain.Client?.ForceTimeOut();
|
||||
}, isCheat: false));
|
||||
commands.Add(new Command("bumpitem", "", (string[] args) =>
|
||||
{
|
||||
float vel = 10.0f;
|
||||
if (args.Length > 0)
|
||||
{
|
||||
float.TryParse(args[0], NumberStyles.Number, CultureInfo.InvariantCulture, out vel);
|
||||
}
|
||||
Character.Controlled?.FocusedItem?.body?.ApplyLinearImpulse(Rand.Vector(vel));
|
||||
}, isCheat: false));
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1813,7 +1838,9 @@ namespace Barotrauma
|
||||
lines.Add("<EntityName." + me.Identifier + ">" + me.Name + "</EntityName." + me.Identifier + ">");
|
||||
lines.Add("<EntityDescription." + me.Identifier + ">" + me.Description + "</EntityDescription." + me.Identifier + ">");
|
||||
}
|
||||
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
|
||||
File.WriteAllLines(filePath, lines);
|
||||
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
|
||||
}));
|
||||
|
||||
commands.Add(new Command("dumpeventtexts", "dumpeventtexts [filepath]: gets the texts from event files and and writes them into a file along with xml tags that can be used in translation files. If the filepath is omitted, the file is written to Content/Texts/EventTexts.txt", (string[] args) =>
|
||||
@@ -1832,16 +1859,15 @@ namespace Barotrauma
|
||||
docs.Add(eventPrefab.ConfigElement.Document);
|
||||
getTextsFromElement(eventPrefab.ConfigElement, lines, eventPrefab.Identifier);
|
||||
}
|
||||
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
|
||||
File.WriteAllLines(filePath, lines);
|
||||
|
||||
ToolBox.OpenFileWithShell(Path.GetFullPath(filePath));
|
||||
|
||||
System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings
|
||||
{
|
||||
Indent = true,
|
||||
NewLineOnAttributes = false
|
||||
};
|
||||
|
||||
};
|
||||
foreach (XDocument doc in docs)
|
||||
{
|
||||
using (var writer = XmlWriter.Create(new System.Uri(doc.BaseUri).LocalPath, settings))
|
||||
@@ -1850,6 +1876,7 @@ namespace Barotrauma
|
||||
writer.Flush();
|
||||
}
|
||||
}
|
||||
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
|
||||
|
||||
void getTextsFromElement(XElement element, List<string> list, string parentName)
|
||||
{
|
||||
@@ -1996,10 +2023,17 @@ namespace Barotrauma
|
||||
lines.Add("[/table]");
|
||||
lines.Add("");
|
||||
}
|
||||
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
|
||||
File.WriteAllLines(filePath, lines);
|
||||
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
|
||||
ToolBox.OpenFileWithShell(Path.GetFullPath(filePath));
|
||||
}));
|
||||
#if DEBUG
|
||||
commands.Add(new Command("playovervc", "Plays a sound over voice chat.", (args) =>
|
||||
{
|
||||
VoipCapture.Instance?.SetOverrideSound(args.Length > 0 ? args[0] : null);
|
||||
}));
|
||||
|
||||
commands.Add(new Command("querylobbies", "Queries all SteamP2P lobbies", (args) =>
|
||||
{
|
||||
TaskPool.Add("DebugQueryLobbies",
|
||||
@@ -2033,7 +2067,9 @@ namespace Barotrauma
|
||||
commands.Add(new Command("printproperties", "Goes through the currently collected property list for missing localizations and writes them to a file.", (string[] args) =>
|
||||
{
|
||||
string path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\propertylocalization.txt";
|
||||
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
|
||||
File.WriteAllLines(path, SerializableEntityEditor.MissingLocalizations);
|
||||
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
|
||||
}));
|
||||
|
||||
commands.Add(new Command("getproperties", "Goes through the MapEntity prefabs and checks their serializable properties for localization issues.", (string[] args) =>
|
||||
@@ -2248,12 +2284,15 @@ namespace Barotrauma
|
||||
"revokeperm",
|
||||
(string[] args) =>
|
||||
{
|
||||
if (args.Length < 1) return;
|
||||
if (args.Length < 1) { return; }
|
||||
|
||||
NewMessage("Valid permissions are:", Color.White);
|
||||
foreach (ClientPermissions permission in Enum.GetValues(typeof(ClientPermissions)))
|
||||
if (args.Length < 2)
|
||||
{
|
||||
NewMessage(" - " + permission.ToString(), Color.White);
|
||||
NewMessage("Valid permissions are:", Color.White);
|
||||
foreach (ClientPermissions permission in Enum.GetValues(typeof(ClientPermissions)))
|
||||
{
|
||||
NewMessage(" - " + permission.ToString(), Color.White);
|
||||
}
|
||||
}
|
||||
|
||||
ShowQuestionPrompt("Permission to revoke from client " + args[0] + "?", (perm) =>
|
||||
|
||||
@@ -560,6 +560,22 @@ namespace Barotrauma
|
||||
};
|
||||
}
|
||||
break;
|
||||
case NetworkEventType.UNLOCKPATH:
|
||||
UInt16 connectionIndex = msg.ReadUInt16();
|
||||
if (GameMain.GameSession?.Map?.Connections != null)
|
||||
{
|
||||
if (connectionIndex >= GameMain.GameSession.Map.Connections.Count)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to unlock a path on the campaign map. Connection index out of bounds (index: {connectionIndex}, number of connections: {GameMain.GameSession.Map.Connections.Count})");
|
||||
}
|
||||
else
|
||||
{
|
||||
GameMain.GameSession.Map.Connections[connectionIndex].Locked = false;
|
||||
new GUIMessageBox(string.Empty, TextManager.Get("pathunlockedgeneric"),
|
||||
new string[0], type: GUIMessageBox.Type.InGame, iconStyle: "UnlockPathIcon", relativeSize: new Vector2(0.3f, 0.15f), minSize: new Point(512, 128));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class AbandonedOutpostMission : Mission
|
||||
{
|
||||
public override int State
|
||||
{
|
||||
get { return base.State; }
|
||||
protected set
|
||||
{
|
||||
if (state != value)
|
||||
{
|
||||
base.State = value;
|
||||
if (state == HostagesKilledState && !string.IsNullOrEmpty(hostagesKilledMessage))
|
||||
{
|
||||
CreateMessageBox(string.Empty, hostagesKilledMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void ClientReadInitial(IReadMessage msg)
|
||||
{
|
||||
byte characterCount = msg.ReadByte();
|
||||
|
||||
for (int i = 0; i < characterCount; i++)
|
||||
{
|
||||
Character character = Character.ReadSpawnData(msg);
|
||||
characters.Add(character);
|
||||
if (msg.ReadBoolean()) { requireKill.Add(character); }
|
||||
if (msg.ReadBoolean())
|
||||
{
|
||||
requireRescue.Add(character);
|
||||
#if CLIENT
|
||||
GameMain.GameSession.CrewManager.AddCharacterToCrewList(character);
|
||||
#endif
|
||||
}
|
||||
ushort itemCount = msg.ReadUInt16();
|
||||
for (int j = 0; j < itemCount; j++)
|
||||
{
|
||||
Item.ReadSpawnData(msg);
|
||||
}
|
||||
if (character.Submarine != null && character.AIController is EnemyAIController enemyAi)
|
||||
{
|
||||
enemyAi.UnattackableSubmarines.Add(character.Submarine);
|
||||
enemyAi.UnattackableSubmarines.Add(Submarine.MainSub);
|
||||
foreach (Submarine sub in Submarine.MainSub.DockedTo)
|
||||
{
|
||||
enemyAi.UnattackableSubmarines.Add(sub);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (characters.Contains(null))
|
||||
{
|
||||
throw new System.Exception("Error in AbandonedOutpostMission.ClientReadInitial: character list contains null (mission: " + Prefab.Identifier + ")");
|
||||
}
|
||||
if (characters.Count != characterCount)
|
||||
{
|
||||
throw new System.Exception("Error in AbandonedOutpostMission.ClientReadInitial: character count does not match the server count (" + characters + " != " + characters.Count + "mission: " + Prefab.Identifier + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,66 @@
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
abstract partial class Mission
|
||||
{
|
||||
private readonly List<string> shownMessages = new List<string>();
|
||||
public IEnumerable<string> ShownMessages
|
||||
{
|
||||
get { return shownMessages; }
|
||||
}
|
||||
|
||||
public Color GetDifficultyColor()
|
||||
{
|
||||
int v = Difficulty ?? MissionPrefab.MinDifficulty;
|
||||
float t = MathUtils.InverseLerp(MissionPrefab.MinDifficulty, MissionPrefab.MaxDifficulty, v);
|
||||
return ToolBox.GradientLerp(t, GUI.Style.Green, GUI.Style.Orange, GUI.Style.Red);
|
||||
}
|
||||
|
||||
public string GetMissionRewardText()
|
||||
{
|
||||
string rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", Reward));
|
||||
return TextManager.GetWithVariable("missionreward", "[reward]", $"‖color:gui.orange‖{rewardText}‖end‖");
|
||||
}
|
||||
|
||||
public string GetReputationRewardText(Location currLocation)
|
||||
{
|
||||
List<string> reputationRewardTexts = new List<string>();
|
||||
foreach (var reputationReward in ReputationRewards)
|
||||
{
|
||||
string name = "";
|
||||
|
||||
if (reputationReward.Key.Equals("location", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
name = $"‖color:gui.orange‖{currLocation.Name}‖end‖";
|
||||
}
|
||||
else
|
||||
{
|
||||
var faction = FactionPrefab.Prefabs.Find(f => f.Identifier.Equals(reputationReward.Key, StringComparison.OrdinalIgnoreCase));
|
||||
if (faction != null)
|
||||
{
|
||||
name = $"‖color:{XMLExtensions.ColorToString(faction.IconColor)}‖{faction.Name}‖end‖";
|
||||
}
|
||||
else
|
||||
{
|
||||
name = TextManager.Get(reputationReward.Key);
|
||||
}
|
||||
}
|
||||
float normalizedValue = MathUtils.InverseLerp(-100.0f, 100.0f, reputationReward.Value);
|
||||
string formattedValue = ((int)reputationReward.Value).ToString("+#;-#;0"); //force plus sign for positive numbers
|
||||
string rewardText = TextManager.GetWithVariables(
|
||||
"reputationformat",
|
||||
new string[] { "[reputationname]", "[reputationvalue]" },
|
||||
new string[] { name, $"‖color:{XMLExtensions.ColorToString(Reputation.GetReputationColor(normalizedValue))}‖{formattedValue}‖end‖" });
|
||||
reputationRewardTexts.Add(rewardText);
|
||||
}
|
||||
return TextManager.AddPunctuation(':', TextManager.Get("reputation"), string.Join(", ", reputationRewardTexts));
|
||||
}
|
||||
|
||||
partial void ShowMessageProjSpecific(int missionState)
|
||||
{
|
||||
int messageIndex = missionState - 1;
|
||||
@@ -23,11 +79,17 @@ namespace Barotrauma
|
||||
{
|
||||
yield return new WaitForSeconds(1.0f);
|
||||
}
|
||||
new GUIMessageBox(header, message, buttons: new string[0], type: GUIMessageBox.Type.InGame, icon: Prefab.Icon)
|
||||
CreateMessageBox(header, message);
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
protected void CreateMessageBox(string header, string message)
|
||||
{
|
||||
shownMessages.Add(message);
|
||||
new GUIMessageBox(header, message, buttons: new string[0], type: GUIMessageBox.Type.InGame, icon: Prefab.Icon, parseRichText: true)
|
||||
{
|
||||
IconColor = Prefab.IconColor
|
||||
};
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
public void ClientRead(IReadMessage msg)
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
|
||||
namespace Barotrauma
|
||||
namespace Barotrauma
|
||||
{
|
||||
abstract partial class MissionMode : GameMode
|
||||
{
|
||||
public override void ShowStartMessage()
|
||||
{
|
||||
if (mission == null) return;
|
||||
|
||||
new GUIMessageBox(mission.Name, mission.Description, new string[0], type: GUIMessageBox.Type.InGame, icon: mission.Prefab.Icon)
|
||||
foreach (Mission mission in missions)
|
||||
{
|
||||
IconColor = mission.Prefab.IconColor,
|
||||
UserData = "missionstartmessage"
|
||||
};
|
||||
new GUIMessageBox(mission.Name, mission.Description, new string[0], type: GUIMessageBox.Type.InGame, icon: mission.Prefab.Icon, parseRichText: true)
|
||||
{
|
||||
IconColor = mission.Prefab.IconColor,
|
||||
UserData = "missionstartmessage"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class OutpostDestroyMission : AbandonedOutpostMission
|
||||
{
|
||||
public override void ClientReadInitial(IReadMessage msg)
|
||||
{
|
||||
base.ClientReadInitial(msg);
|
||||
ushort itemCount = msg.ReadUInt16();
|
||||
for (int i = 0; i < itemCount; i++)
|
||||
{
|
||||
var item = Item.ReadSpawnData(msg);
|
||||
items.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -422,12 +422,12 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawStringWithColors(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects se, float layerDepth, List<RichTextData> richTextData)
|
||||
public void DrawStringWithColors(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects se, float layerDepth, List<RichTextData> richTextData, int rtdOffset = 0)
|
||||
{
|
||||
DrawStringWithColors(sb, text, position, color, rotation, origin, new Vector2(scale), se, layerDepth, richTextData);
|
||||
DrawStringWithColors(sb, text, position, color, rotation, origin, new Vector2(scale), se, layerDepth, richTextData, rtdOffset);
|
||||
}
|
||||
|
||||
public void DrawStringWithColors(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects se, float layerDepth, List<RichTextData> richTextData)
|
||||
public void DrawStringWithColors(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects se, float layerDepth, List<RichTextData> richTextData, int rtdOffset = 0)
|
||||
{
|
||||
if (textures.Count == 0 && !DynamicLoading) { return; }
|
||||
|
||||
@@ -457,15 +457,15 @@ namespace Barotrauma
|
||||
|
||||
Color currentTextColor;
|
||||
|
||||
if (currentRichTextData != null && i > currentRichTextData.EndIndex + lineNum)
|
||||
while (currentRichTextData != null && i + rtdOffset > currentRichTextData.EndIndex + lineNum)
|
||||
{
|
||||
richTextDataIndex++;
|
||||
currentRichTextData = richTextDataIndex < richTextData.Count ? richTextData[richTextDataIndex] : null;
|
||||
}
|
||||
|
||||
if (currentRichTextData != null && currentRichTextData.StartIndex + lineNum <= i && i <= currentRichTextData.EndIndex + lineNum)
|
||||
if (currentRichTextData != null && currentRichTextData.StartIndex + lineNum <= i + rtdOffset && i + rtdOffset <= currentRichTextData.EndIndex + lineNum)
|
||||
{
|
||||
currentTextColor = currentRichTextData.Color ?? color;
|
||||
currentTextColor = currentRichTextData.Color * currentRichTextData.Alpha ?? color;
|
||||
if (!string.IsNullOrEmpty(currentRichTextData.Metadata))
|
||||
{
|
||||
currentTextColor = Color.Lerp(currentTextColor, Color.White, 0.5f);
|
||||
|
||||
@@ -321,6 +321,7 @@ namespace Barotrauma
|
||||
float prevSize = chatBox.BarSize;
|
||||
|
||||
string displayedText = message.TranslatedText;
|
||||
|
||||
string senderName = "";
|
||||
Color senderColor = Color.White;
|
||||
if (!string.IsNullOrWhiteSpace(message.SenderName))
|
||||
@@ -377,13 +378,29 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
var msgText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), msgHolder.RectTransform)
|
||||
{ AbsoluteOffset = new Point((int)(10 * GUI.Scale), senderNameTimestamp == null ? 0 : senderNameTimestamp.Rect.Height) },
|
||||
{ AbsoluteOffset = new Point((int)(10 * GUI.Scale), senderNameTimestamp == null ? 0 : senderNameTimestamp.Rect.Height) },
|
||||
displayedText, textColor: message.Color, font: GUI.SmallFont, textAlignment: Alignment.TopLeft, style: null, wrap: true,
|
||||
color: ((chatBox.Content.CountChildren % 2) == 0) ? Color.Transparent : Color.Black * 0.1f)
|
||||
color: ((chatBox.Content.CountChildren % 2) == 0) ? Color.Transparent : Color.Black * 0.1f, parseRichText: true)
|
||||
{
|
||||
UserData = message.SenderName,
|
||||
CanBeFocused = true
|
||||
CanBeFocused = false
|
||||
};
|
||||
msgText.CalculateHeightFromText();
|
||||
if (msgText.RichTextData != null)
|
||||
{
|
||||
foreach (var data in msgText.RichTextData)
|
||||
{
|
||||
var clickableArea = new GUITextBlock.ClickableArea()
|
||||
{
|
||||
Data = data
|
||||
};
|
||||
if (GameMain.NetLobbyScreen != null && GameMain.NetworkMember != null)
|
||||
{
|
||||
clickableArea.OnClick = GameMain.NetLobbyScreen.SelectPlayer;
|
||||
}
|
||||
msgText.ClickableAreas.Add(clickableArea);
|
||||
}
|
||||
}
|
||||
|
||||
if (message is OrderChatMessage orderChatMsg &&
|
||||
Character.Controlled != null &&
|
||||
@@ -444,7 +461,7 @@ namespace Barotrauma
|
||||
senderText.RectTransform.MinSize = new Point(0, senderText.Rect.Height);
|
||||
}
|
||||
var msgPopupText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform),
|
||||
displayedText, textColor: message.Color, font: GUI.SmallFont, textAlignment: Alignment.BottomLeft, style: null, wrap: true)
|
||||
displayedText, textColor: message.Color, font: GUI.SmallFont, textAlignment: Alignment.BottomLeft, style: null, wrap: true, parseRichText: true)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
@@ -523,6 +540,7 @@ namespace Barotrauma
|
||||
|
||||
if (ToggleButton != null)
|
||||
{
|
||||
ToggleButton.Selected = ToggleOpen;
|
||||
ToggleButton.RectTransform.AbsoluteOffset = new Point(GUIFrame.Rect.Right, GUIFrame.Rect.Y + HUDLayoutSettings.ChatBoxArea.Height - ToggleButton.Rect.Height);
|
||||
}
|
||||
|
||||
@@ -566,6 +584,7 @@ namespace Barotrauma
|
||||
|
||||
if (ToggleOpen)
|
||||
{
|
||||
GUIFrame.CanBeFocused = true;
|
||||
openState += deltaTime * 5.0f;
|
||||
//delete all popup messages when the chatbox is open
|
||||
foreach (var popupMsg in popupMessages)
|
||||
@@ -576,6 +595,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
GUIFrame.CanBeFocused = false;
|
||||
openState -= deltaTime * 5.0f;
|
||||
|
||||
int yOffset = 0;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -91,9 +91,10 @@ namespace Barotrauma
|
||||
UserData = "container"
|
||||
};
|
||||
|
||||
int panelMaxWidth = (int)(GUI.xScale * (GUI.HorizontalAspectRatio < 1.4f ? 650 : 560));
|
||||
var availableMainGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.4f, 1.0f), campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).RectTransform)
|
||||
{
|
||||
MaxSize = new Point(560, campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Rect.Height)
|
||||
MaxSize = new Point(panelMaxWidth, campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Rect.Height)
|
||||
})
|
||||
{
|
||||
Stretch = true,
|
||||
@@ -149,7 +150,7 @@ namespace Barotrauma
|
||||
|
||||
var pendingAndCrewMainGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.4f, 1.0f), campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).RectTransform, anchor: Anchor.TopRight)
|
||||
{
|
||||
MaxSize = new Point(560, campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Rect.Height)
|
||||
MaxSize = new Point(panelMaxWidth, campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Rect.Height)
|
||||
})
|
||||
{
|
||||
Stretch = true,
|
||||
@@ -177,7 +178,7 @@ namespace Barotrauma
|
||||
var pendingAndCrewGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.95f), anchor: Anchor.Center,
|
||||
parent: new GUIFrame(new RectTransform(new Vector2(1.0f, 13.25f / 14.0f), pendingAndCrewMainGroup.RectTransform)
|
||||
{
|
||||
MaxSize = new Point(560, campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Rect.Height)
|
||||
MaxSize = new Point(panelMaxWidth, campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Rect.Height)
|
||||
}).RectTransform));
|
||||
|
||||
float height = 0.05f;
|
||||
@@ -188,7 +189,7 @@ namespace Barotrauma
|
||||
};
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, height), pendingAndCrewGroup.RectTransform), TextManager.Get("campaignmenucrew"), font: GUI.SubHeadingFont);
|
||||
crewList = new GUIListBox(new RectTransform(new Vector2(1.0f, (8)* height), pendingAndCrewGroup.RectTransform))
|
||||
crewList = new GUIListBox(new RectTransform(new Vector2(1.0f, 8 * height), pendingAndCrewGroup.RectTransform))
|
||||
{
|
||||
Spacing = 1
|
||||
};
|
||||
@@ -207,7 +208,7 @@ namespace Barotrauma
|
||||
{
|
||||
ClickSound = GUISoundType.HireRepairClick,
|
||||
ForceUpperCase = true,
|
||||
OnClicked = (b, o) => ValidatePendingHires(true)
|
||||
OnClicked = (b, o) => ValidateHires(PendingHires, true)
|
||||
};
|
||||
clearAllButton = new GUIButton(new RectTransform(new Vector2(1.0f / 3.0f, 1.0f), group.RectTransform), text: TextManager.Get("campaignstore.clearall"))
|
||||
{
|
||||
@@ -335,9 +336,9 @@ namespace Barotrauma
|
||||
jobColor = characterInfo.Job.Prefab.UIColor;
|
||||
}
|
||||
|
||||
GUIFrame frame = new GUIFrame(new RectTransform(new Point(listBox.Content.Rect.Width, 55), parent: listBox.Content.RectTransform), "ListBoxElement")
|
||||
GUIFrame frame = new GUIFrame(new RectTransform(new Point(listBox.Content.Rect.Width, (int)(GUI.yScale * 55)), parent: listBox.Content.RectTransform), "ListBoxElement")
|
||||
{
|
||||
UserData = new Tuple<CharacterInfo, float>(characterInfo, skill != null ? skill.Level : 0.0f)
|
||||
UserData = new Tuple<CharacterInfo, float>(characterInfo, skill?.Level ?? 0.0f)
|
||||
};
|
||||
GUILayoutGroup mainGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), frame.RectTransform, anchor: Anchor.Center), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
@@ -345,19 +346,22 @@ namespace Barotrauma
|
||||
};
|
||||
|
||||
float portraitWidth = (0.8f * mainGroup.Rect.Height) / mainGroup.Rect.Width;
|
||||
new GUICustomComponent(new RectTransform(new Vector2(portraitWidth, 0.8f), mainGroup.RectTransform),
|
||||
var icon = new GUICustomComponent(new RectTransform(new Vector2(portraitWidth, 0.8f), mainGroup.RectTransform),
|
||||
onDraw: (sb, component) => characterInfo.DrawIcon(sb, component.Rect.Center.ToVector2(), targetAreaSize: component.Rect.Size.ToVector2()))
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
GUILayoutGroup nameAndJobGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.4f - portraitWidth, 0.8f), mainGroup.RectTransform));
|
||||
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), nameAndJobGroup.RectTransform),
|
||||
characterInfo.Name, textColor: jobColor, textAlignment: Alignment.BottomLeft)
|
||||
GUILayoutGroup nameAndJobGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.4f - portraitWidth, 0.8f), mainGroup.RectTransform)) { CanBeFocused = false };
|
||||
GUILayoutGroup nameGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), nameAndJobGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { CanBeFocused = false };
|
||||
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(Vector2.One, nameGroup.RectTransform),
|
||||
listBox == hireableList ? characterInfo.OriginalName : characterInfo.Name,
|
||||
textColor: jobColor, textAlignment: Alignment.BottomLeft)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
nameBlock.Text = ToolBox.LimitString(nameBlock.Text, nameBlock.Font, nameBlock.Rect.Width);
|
||||
|
||||
GUITextBlock jobBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), nameAndJobGroup.RectTransform),
|
||||
characterInfo.Job.Name, textColor: Color.White, font: GUI.SmallFont, textAlignment: Alignment.TopLeft)
|
||||
{
|
||||
@@ -366,7 +370,7 @@ namespace Barotrauma
|
||||
jobBlock.Text = ToolBox.LimitString(jobBlock.Text, jobBlock.Font, jobBlock.Rect.Width);
|
||||
|
||||
float width = 0.6f / 3;
|
||||
if (characterInfo.Job != null)
|
||||
if (characterInfo.Job != null && skill != null)
|
||||
{
|
||||
GUILayoutGroup skillGroup = new GUILayoutGroup(new RectTransform(new Vector2(width, 0.6f), mainGroup.RectTransform), isHorizontal: true);
|
||||
float iconWidth = (float)skillGroup.Rect.Height / skillGroup.Rect.Width;
|
||||
@@ -383,11 +387,18 @@ namespace Barotrauma
|
||||
|
||||
if (listBox != crewList)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(width, 1.0f), mainGroup.RectTransform), FormatCurrency(characterInfo.Salary), textAlignment: Alignment.Center)
|
||||
new GUITextBlock(new RectTransform(new Vector2(width, 1.0f), mainGroup.RectTransform),
|
||||
FormatCurrency(characterInfo.Salary),
|
||||
textAlignment: Alignment.Center)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just a bit of padding to make list layouts similar
|
||||
new GUIFrame(new RectTransform(new Vector2(width, 1.0f), mainGroup.RectTransform), style: null) { CanBeFocused = false };
|
||||
}
|
||||
|
||||
if (listBox == hireableList)
|
||||
{
|
||||
@@ -446,6 +457,23 @@ namespace Barotrauma
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (listBox == pendingList || listBox == crewList)
|
||||
{
|
||||
nameBlock.RectTransform.Resize(new Point(nameBlock.Rect.Width - nameBlock.Rect.Height, nameBlock.Rect.Height));
|
||||
nameBlock.Text = ToolBox.LimitString(nameBlock.Text, nameBlock.Font, nameBlock.Rect.Width);
|
||||
nameBlock.RectTransform.Resize(new Point((int)(nameBlock.Padding.X + nameBlock.TextSize.X + nameBlock.Padding.Z), nameBlock.Rect.Height));
|
||||
Point size = new Point((int)(0.7f * nameBlock.Rect.Height));
|
||||
new GUIImage(new RectTransform(size, nameGroup.RectTransform), "EditIcon") { CanBeFocused = false };
|
||||
size = new Point(3 * mainGroup.AbsoluteSpacing + icon.Rect.Width + nameAndJobGroup.Rect.Width, mainGroup.Rect.Height);
|
||||
new GUIButton(new RectTransform(size, frame.RectTransform) { RelativeOffset = new Vector2(0.025f) }, style: null)
|
||||
{
|
||||
Enabled = HasPermission,
|
||||
ToolTip = TextManager.GetWithVariable("campaigncrew.givenicknametooltip", "[mouseprimary]", TextManager.Get($"input.{(PlayerInput.MouseButtonsSwapped() ? "rightmouse" : "leftmouse")}")),
|
||||
UserData = characterInfo,
|
||||
OnClicked = CreateRenamingComponent
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateCharacterPreviewFrame(GUIListBox listBox, GUIFrame characterFrame, CharacterInfo characterInfo)
|
||||
@@ -478,7 +506,10 @@ namespace Barotrauma
|
||||
GUILayoutGroup infoValueGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.6f, 1.0f), infoGroup.RectTransform)) { Stretch = true };
|
||||
float blockHeight = 1.0f / 4;
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("name"));
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), characterInfo.Name);
|
||||
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), "");
|
||||
string name = listBox == hireableList ? characterInfo.OriginalName : characterInfo.Name;
|
||||
nameBlock.Text = ToolBox.LimitString(name, nameBlock.Font, nameBlock.Rect.Width);
|
||||
|
||||
if (characterInfo.HasGenders)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("gender"));
|
||||
@@ -553,9 +584,18 @@ namespace Barotrauma
|
||||
if (PendingHires.Contains(characterInfo)) { PendingHires.Remove(characterInfo); }
|
||||
pendingList.Content.RemoveChild(pendingList.Content.FindChild(c => (c.UserData as Tuple<CharacterInfo, float>).Item1 == characterInfo));
|
||||
pendingList.UpdateScrollBarSize();
|
||||
CreateCharacterFrame(characterInfo, hireableList);
|
||||
SortCharacters(hireableList, (SortingMethod)sortingDropDown.SelectedItemData);
|
||||
hireableList.UpdateScrollBarSize();
|
||||
|
||||
// Server will reset the names to originals in multiplayer
|
||||
if (!GameMain.IsMultiplayer) { characterInfo?.ResetName(); }
|
||||
|
||||
if (campaign.Map.CurrentLocation.HireManager.AvailableCharacters.Any(info => info.GetIdentifierUsingOriginalName() == characterInfo.GetIdentifierUsingOriginalName()) &&
|
||||
hireableList.Content.Children.None(c => c.UserData is Tuple<CharacterInfo, float> userData && userData.Item1.GetIdentifierUsingOriginalName() == characterInfo.GetIdentifierUsingOriginalName()))
|
||||
{
|
||||
CreateCharacterFrame(characterInfo, hireableList);
|
||||
SortCharacters(hireableList, (SortingMethod)sortingDropDown.SelectedItemData);
|
||||
hireableList.UpdateScrollBarSize();
|
||||
}
|
||||
|
||||
if (setTotalHireCost) { SetTotalHireCost(); }
|
||||
if (createNetworkMessage) { SendCrewState(true); }
|
||||
return true;
|
||||
@@ -572,36 +612,41 @@ namespace Barotrauma
|
||||
{
|
||||
if (pendingList == null || totalBlock == null || validateHiresButton == null) { return; }
|
||||
int total = 0;
|
||||
pendingList.Content.Children.ForEach(c => total += (c.UserData as Tuple<CharacterInfo, float>).Item1.Salary);
|
||||
pendingList.Content.Children.ForEach(c =>
|
||||
{
|
||||
total += (c.UserData as Tuple<CharacterInfo, float>).Item1.Salary;
|
||||
});
|
||||
totalBlock.Text = FormatCurrency(total);
|
||||
bool enoughMoney = campaign != null ? total <= campaign.Money : true;
|
||||
totalBlock.TextColor = enoughMoney ? Color.White : Color.Red;
|
||||
validateHiresButton.Enabled = enoughMoney && pendingList.Content.RectTransform.Children.Any();
|
||||
}
|
||||
|
||||
public bool ValidatePendingHires(bool createNetworkEvent = false)
|
||||
public bool ValidateHires(List<CharacterInfo> hires, bool createNetworkEvent = false)
|
||||
{
|
||||
List<CharacterInfo> hires = new List<CharacterInfo>();
|
||||
int total = 0;
|
||||
foreach (GUIComponent c in pendingList.Content.Children.ToList())
|
||||
{
|
||||
if (c.UserData is Tuple<CharacterInfo, float> info)
|
||||
{
|
||||
hires.Add(info.Item1);
|
||||
total += info.Item1.Salary;
|
||||
}
|
||||
}
|
||||
if (hires == null || hires.None()) { return false; }
|
||||
|
||||
if (hires.None() || total > campaign.Money) { return false; }
|
||||
List<CharacterInfo> nonDuplicateHires = new List<CharacterInfo>();
|
||||
hires.ForEach(hireInfo =>
|
||||
{
|
||||
if(campaign.CrewManager.GetCharacterInfos().None(crewInfo => crewInfo.IsNewHire && crewInfo.GetIdentifierUsingOriginalName() == hireInfo.GetIdentifierUsingOriginalName()))
|
||||
{
|
||||
nonDuplicateHires.Add(hireInfo);
|
||||
}
|
||||
});
|
||||
|
||||
if (nonDuplicateHires.None()) { return false; }
|
||||
|
||||
int total = nonDuplicateHires.Aggregate(0, (total, info) => total + info.Salary);
|
||||
|
||||
if (total > campaign.Money) { return false; }
|
||||
|
||||
bool atLeastOneHired = false;
|
||||
foreach (CharacterInfo ci in hires)
|
||||
foreach (CharacterInfo ci in nonDuplicateHires)
|
||||
{
|
||||
if (campaign.TryHireCharacter(campaign.Map.CurrentLocation, ci))
|
||||
{
|
||||
atLeastOneHired = true;
|
||||
PendingHires.Remove(ci);
|
||||
pendingList.Content.RemoveChild(pendingList.Content.FindChild(c => (c.UserData as Tuple<CharacterInfo, float>).Item1 == ci));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -628,6 +673,93 @@ namespace Barotrauma
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool CreateRenamingComponent(GUIButton button, object userData)
|
||||
{
|
||||
if (!HasPermission || !(userData is CharacterInfo characterInfo)) { return false; }
|
||||
var outerGlowFrame = new GUIFrame(new RectTransform(new Vector2(1.25f, 1.25f), parentComponent.RectTransform, Anchor.Center),
|
||||
style: "OuterGlow", color: Color.Black * 0.7f);
|
||||
var frame = new GUIFrame(new RectTransform(new Vector2(0.33f, 0.4f), outerGlowFrame.RectTransform, anchor: Anchor.Center)
|
||||
{
|
||||
MaxSize = new Point(400, 300).Multiply(GUI.Scale)
|
||||
});
|
||||
var layoutGroup = new GUILayoutGroup(new RectTransform((frame.Rect.Size - GUIStyle.ItemFrameMargin).Multiply(new Vector2(0.75f, 1.0f)), frame.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter)
|
||||
{
|
||||
RelativeSpacing = 0.02f,
|
||||
Stretch = true
|
||||
};
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), layoutGroup.RectTransform), TextManager.Get("campaigncrew.givenickname"), font: GUI.SubHeadingFont, textAlignment: Alignment.Center, wrap: true);
|
||||
var groupElementSize = new Vector2(1.0f, 0.25f);
|
||||
var nameBox = new GUITextBox(new RectTransform(groupElementSize, layoutGroup.RectTransform))
|
||||
{
|
||||
MaxTextLength = Client.MaxNameLength
|
||||
};
|
||||
new GUIButton(new RectTransform(groupElementSize, layoutGroup.RectTransform), text: TextManager.Get("confirm"))
|
||||
{
|
||||
OnClicked = (button, userData) =>
|
||||
{
|
||||
if (RenameCharacter(characterInfo, nameBox.Text?.Trim()))
|
||||
{
|
||||
parentComponent.RemoveChild(outerGlowFrame);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
nameBox.Flash(color: Color.Red);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
new GUIButton(new RectTransform(groupElementSize, layoutGroup.RectTransform), text: TextManager.Get("cancel"))
|
||||
{
|
||||
OnClicked = (button, userData) =>
|
||||
{
|
||||
parentComponent.RemoveChild(outerGlowFrame);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
layoutGroup.Recalculate();
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool RenameCharacter(CharacterInfo characterInfo, string newName)
|
||||
{
|
||||
if (characterInfo == null || string.IsNullOrEmpty(newName)) { return false; }
|
||||
if (newName == characterInfo.Name) { return false; }
|
||||
if (GameMain.IsMultiplayer)
|
||||
{
|
||||
SendCrewState(false, renameCharacter: (characterInfo, newName));
|
||||
}
|
||||
else
|
||||
{
|
||||
var crewComponent = crewList.Content.FindChild(c => (c.UserData as Tuple<CharacterInfo, float>).Item1 == characterInfo);
|
||||
if (crewComponent != null)
|
||||
{
|
||||
crewList.Content.RemoveChild(crewComponent);
|
||||
campaign.CrewManager.RenameCharacter(characterInfo, newName);
|
||||
CreateCharacterFrame(characterInfo, crewList);
|
||||
SortCharacters(crewList, SortingMethod.JobAsc);
|
||||
}
|
||||
else
|
||||
{
|
||||
var pendingComponent = pendingList.Content.FindChild(c => (c.UserData as Tuple<CharacterInfo, float>).Item1 == characterInfo);
|
||||
if (pendingComponent != null)
|
||||
{
|
||||
pendingList.Content.RemoveChild(pendingComponent);
|
||||
campaign.Map.CurrentLocation.HireManager.RenameCharacter(characterInfo, newName);
|
||||
CreateCharacterFrame(characterInfo, pendingList);
|
||||
SortCharacters(pendingList, SortingMethod.JobAsc);
|
||||
SetTotalHireCost();
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool FireCharacter(GUIButton button, object selection)
|
||||
{
|
||||
if (!(selection is CharacterInfo characterInfo)) { return false; }
|
||||
@@ -648,9 +780,10 @@ namespace Barotrauma
|
||||
UpdateLocationView(campaign.Map.CurrentLocation, false);
|
||||
}
|
||||
|
||||
if ((GUI.MouseOn?.UserData as Tuple<CharacterInfo, float>)?.Item1 is CharacterInfo characterInfo)
|
||||
(GUIComponent highlightedFrame, CharacterInfo highlightedInfo) = FindHighlightedCharacter(GUI.MouseOn);
|
||||
if (highlightedFrame != null && highlightedInfo != null)
|
||||
{
|
||||
if (characterPreviewFrame == null || characterInfo != characterPreviewFrame.UserData)
|
||||
if (characterPreviewFrame == null || highlightedInfo != characterPreviewFrame.UserData)
|
||||
{
|
||||
GUIComponent component = GUI.MouseOn;
|
||||
GUIListBox listBox = null;
|
||||
@@ -673,7 +806,7 @@ namespace Barotrauma
|
||||
|
||||
if (listBox != null)
|
||||
{
|
||||
SelectCharacter(listBox, GUI.MouseOn as GUIFrame, characterInfo);
|
||||
SelectCharacter(listBox, highlightedFrame as GUIFrame, highlightedInfo);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -687,6 +820,27 @@ namespace Barotrauma
|
||||
characterPreviewFrame.Parent?.RemoveChild(characterPreviewFrame);
|
||||
characterPreviewFrame = null;
|
||||
}
|
||||
|
||||
static (GUIComponent, CharacterInfo) FindHighlightedCharacter(GUIComponent c)
|
||||
{
|
||||
if (c == null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
if (c.UserData is Tuple<CharacterInfo, float> highlightedData)
|
||||
{
|
||||
return (c, highlightedData.Item1);
|
||||
}
|
||||
if (c.Parent != null)
|
||||
{
|
||||
if (c.Parent is GUIListBox)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
return FindHighlightedCharacter(c.Parent);
|
||||
}
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPendingHires(List<int> characterInfos, Location location)
|
||||
@@ -699,7 +853,7 @@ namespace Barotrauma
|
||||
PendingHires.Clear();
|
||||
foreach (int identifier in characterInfos)
|
||||
{
|
||||
CharacterInfo match = location.HireManager.AvailableCharacters.Find(info => info.GetIdentifier() == identifier);
|
||||
CharacterInfo match = location.HireManager.AvailableCharacters.Find(info => info.GetIdentifierUsingOriginalName() == identifier);
|
||||
if (match != null)
|
||||
{
|
||||
PendingHires.Add(match);
|
||||
@@ -716,9 +870,10 @@ namespace Barotrauma
|
||||
/// Notify the server of crew changes
|
||||
/// </summary>
|
||||
/// <param name="updatePending">When set to true will tell the server to update the pending hires</param>
|
||||
/// <param name="renameCharacter">When not null tell the server to rename this character. Item1 is the character to rename, Item2 is the new name, Item3 indicates whether the renamed character is already a part of the crew.</param>
|
||||
/// <param name="firedCharacter">When not null tell the server to fire this character</param>
|
||||
/// <param name="validateHires">When set to true will tell the server to validate pending hires</param>
|
||||
public void SendCrewState(bool updatePending, CharacterInfo firedCharacter = null, bool validateHires = false)
|
||||
public void SendCrewState(bool updatePending, (CharacterInfo info, string newName) renameCharacter = default, CharacterInfo firedCharacter = null, bool validateHires = false)
|
||||
{
|
||||
if (campaign is MultiPlayerCampaign)
|
||||
{
|
||||
@@ -731,12 +886,23 @@ namespace Barotrauma
|
||||
msg.Write((ushort)PendingHires.Count);
|
||||
foreach (CharacterInfo pendingHire in PendingHires)
|
||||
{
|
||||
msg.Write(pendingHire.GetIdentifier());
|
||||
msg.Write(pendingHire.GetIdentifierUsingOriginalName());
|
||||
}
|
||||
}
|
||||
|
||||
msg.Write(validateHires);
|
||||
|
||||
bool validRenaming = renameCharacter.info != null && !string.IsNullOrEmpty(renameCharacter.newName);
|
||||
msg.Write(validRenaming);
|
||||
if (validRenaming)
|
||||
{
|
||||
int identifier = renameCharacter.info.GetIdentifierUsingOriginalName();
|
||||
msg.Write(identifier);
|
||||
msg.Write(renameCharacter.newName);
|
||||
bool existingCrewMember = campaign.CrewManager?.GetCharacterInfos().Any(ci => ci.GetIdentifierUsingOriginalName() == identifier) ?? false;
|
||||
msg.Write(existingCrewMember);
|
||||
}
|
||||
|
||||
msg.Write(firedCharacter != null);
|
||||
if (firedCharacter != null)
|
||||
{
|
||||
|
||||
@@ -218,6 +218,21 @@ namespace Barotrauma
|
||||
|
||||
public static bool DisableHUD, DisableUpperHUD, DisableItemHighlights, DisableCharacterNames;
|
||||
|
||||
private static bool isSavingIndicatorEnabled;
|
||||
private static Color savingIndicatorColor = Color.Transparent;
|
||||
private static bool IsSavingIndicatorVisible => savingIndicatorColor.A > 0;
|
||||
private static float savingIndicatorSpriteIndex;
|
||||
private static float savingIndicatorColorLerpAmount;
|
||||
private static SavingIndicatorState savingIndicatorState = SavingIndicatorState.None;
|
||||
private static float? timeUntilSavingIndicatorDisabled;
|
||||
|
||||
private enum SavingIndicatorState
|
||||
{
|
||||
None,
|
||||
FadingIn,
|
||||
FadingOut
|
||||
}
|
||||
|
||||
public static void Init(GameWindow window, IEnumerable<ContentPackage> selectedContentPackages, GraphicsDevice graphicsDevice)
|
||||
{
|
||||
GraphicsDevice = graphicsDevice;
|
||||
@@ -345,7 +360,11 @@ namespace Barotrauma
|
||||
}
|
||||
#endif
|
||||
|
||||
if (DisableHUD) { return; }
|
||||
if (DisableHUD)
|
||||
{
|
||||
DrawSavingIndicator(spriteBatch);
|
||||
return;
|
||||
}
|
||||
|
||||
if (GameMain.ShowFPS || GameMain.DebugDraw)
|
||||
{
|
||||
@@ -539,60 +558,42 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<string> strings;
|
||||
if (MouseOn != null)
|
||||
{
|
||||
RectTransform mouseOnRect = MouseOn.RectTransform;
|
||||
bool isAbsoluteOffsetInUse = mouseOnRect.AbsoluteOffset != Point.Zero || mouseOnRect.RelativeOffset == Vector2.Zero;
|
||||
|
||||
string selectedString = $"Selected UI Element: {MouseOn.GetType().Name} ({ MouseOn.Style?.Element.Name.LocalName ?? "no style" }, {MouseOn.Rect}";
|
||||
string offsetString = $"Relative Offset: {mouseOnRect.RelativeOffset} | Absolute Offset: {(isAbsoluteOffsetInUse ? mouseOnRect.AbsoluteOffset : mouseOnRect.ParentRect.MultiplySize(mouseOnRect.RelativeOffset))}{(isAbsoluteOffsetInUse ? "" : " (Calculated from RelativeOffset)")}";
|
||||
string anchorPivotString = $"Anchor: {mouseOnRect.Anchor} | Pivot: {mouseOnRect.Pivot}";
|
||||
Vector2 selectedStringSize = SmallFont.MeasureString(selectedString);
|
||||
Vector2 offsetStringSize = SmallFont.MeasureString(offsetString);
|
||||
Vector2 anchorPivotStringSize = SmallFont.MeasureString(anchorPivotString);
|
||||
|
||||
int padding = IntScale(10);
|
||||
int yPos = padding;
|
||||
|
||||
DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - (int)selectedStringSize.X - padding, yPos), selectedString, Color.LightGreen, Color.Black, 0, SmallFont);
|
||||
yPos += (int)selectedStringSize.Y + padding / 2;
|
||||
|
||||
DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - (int)offsetStringSize.X - padding, yPos), offsetString, Color.LightGreen, Color.Black, 0, SmallFont);
|
||||
yPos += (int)offsetStringSize.Y + padding / 2;
|
||||
|
||||
DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - (int)anchorPivotStringSize.X - padding, yPos), anchorPivotString, Color.LightGreen, Color.Black, 0, SmallFont);
|
||||
yPos += (int)anchorPivotStringSize.Y + padding / 2;
|
||||
strings = new string[]
|
||||
{
|
||||
$"Selected UI Element: {MouseOn.GetType().Name} ({ MouseOn.Style?.Element.Name.LocalName ?? "no style" }, {MouseOn.Rect}",
|
||||
$"Relative Offset: {mouseOnRect.RelativeOffset} | Absolute Offset: {(isAbsoluteOffsetInUse ? mouseOnRect.AbsoluteOffset : mouseOnRect.ParentRect.MultiplySize(mouseOnRect.RelativeOffset))}{(isAbsoluteOffsetInUse ? "" : " (Calculated from RelativeOffset)")}",
|
||||
$"Anchor: {mouseOnRect.Anchor} | Pivot: {mouseOnRect.Pivot}"
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
string guiScaleString = $"GUI.Scale: {Scale}";
|
||||
string guixScaleString = $"GUI.xScale: {xScale}";
|
||||
string guiyScaleString = $"GUI.yScale: {yScale}";
|
||||
string relativeHorizontalAspectRatioString = $"RelativeHorizontalAspectRatio: {RelativeHorizontalAspectRatio}";
|
||||
string relativeVerticalAspectRatioString = $"RelativeVerticalAspectRatio: {RelativeVerticalAspectRatio}";
|
||||
Vector2 guiScaleStringSize = SmallFont.MeasureString(guiScaleString);
|
||||
Vector2 guixScaleStringSize = SmallFont.MeasureString(guixScaleString);
|
||||
Vector2 guiyScaleStringSize = SmallFont.MeasureString(guiyScaleString);
|
||||
Vector2 relativeHorizontalAspectRatioStringSize = SmallFont.MeasureString(relativeHorizontalAspectRatioString);
|
||||
Vector2 relativeVerticalAspectRatioStringSize = SmallFont.MeasureString(relativeVerticalAspectRatioString);
|
||||
strings = new string[]
|
||||
{
|
||||
$"GUI.Scale: {Scale}",
|
||||
$"GUI.xScale: {xScale}",
|
||||
$"GUI.yScale: {yScale}",
|
||||
$"RelativeHorizontalAspectRatio: {RelativeHorizontalAspectRatio}",
|
||||
$"RelativeVerticalAspectRatio: {RelativeVerticalAspectRatio}",
|
||||
};
|
||||
}
|
||||
|
||||
int padding = IntScale(10);
|
||||
int yPos = padding;
|
||||
strings = strings.Concat(new string[] { $"Cam.Zoom: {Screen.Selected.Cam?.Zoom ?? 0f}" });
|
||||
|
||||
DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - (int)guiScaleStringSize.X - padding, yPos), guiScaleString, Color.LightGreen, Color.Black, 0, SmallFont);
|
||||
yPos += (int)guiScaleStringSize.Y + padding / 2;
|
||||
int padding = IntScale(10);
|
||||
int yPos = padding;
|
||||
|
||||
DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - (int)guixScaleStringSize.X - padding, yPos), guixScaleString, Color.LightGreen, Color.Black, 0, SmallFont);
|
||||
yPos += (int)guixScaleStringSize.Y + padding / 2;
|
||||
foreach (string str in strings)
|
||||
{
|
||||
Vector2 stringSize = SmallFont.MeasureString(str);
|
||||
|
||||
DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - (int)guiyScaleStringSize.X - padding, yPos), guiyScaleString, Color.LightGreen, Color.Black, 0, SmallFont);
|
||||
yPos += (int)guiyScaleStringSize.Y + padding / 2;
|
||||
|
||||
DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - (int)relativeHorizontalAspectRatioStringSize.X - padding, yPos), relativeHorizontalAspectRatioString, Color.LightGreen, Color.Black, 0, SmallFont);
|
||||
yPos += (int)relativeHorizontalAspectRatioStringSize.Y + padding / 2;
|
||||
|
||||
DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - (int)relativeVerticalAspectRatioStringSize.X - padding, yPos), relativeVerticalAspectRatioString, Color.LightGreen, Color.Black, 0, SmallFont);
|
||||
yPos += (int)relativeVerticalAspectRatioStringSize.Y + padding / 2;
|
||||
DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - (int)stringSize.X - padding, yPos), str, Color.LightGreen, Color.Black, 0, SmallFont);
|
||||
yPos += (int)stringSize.Y + padding / 2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -645,6 +646,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
DrawSavingIndicator(spriteBatch);
|
||||
|
||||
if (GameMain.WindowActive && !HideCursor)
|
||||
{
|
||||
spriteBatch.End();
|
||||
@@ -855,6 +858,7 @@ namespace Barotrauma
|
||||
lock (mutex)
|
||||
{
|
||||
GUIMessageBox.AddActiveToGUIUpdateList();
|
||||
GUIContextMenu.AddActiveToGUIUpdateList();
|
||||
|
||||
if (pauseMenuOpen)
|
||||
{
|
||||
@@ -921,6 +925,7 @@ namespace Barotrauma
|
||||
if ((!PlayerInput.PrimaryMouseButtonHeld() && !PlayerInput.PrimaryMouseButtonClicked()) || c == prevMouseOn)
|
||||
{
|
||||
MouseOn = c;
|
||||
var sakdjfnsjkd = c.MouseRect;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1043,7 +1048,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (parent != null)
|
||||
if (parent != null && parent.CanBeFocused)
|
||||
{
|
||||
if (!parent.Rect.Equals(monitorRect)) { return parent.HoverCursor; }
|
||||
}
|
||||
@@ -1222,6 +1227,7 @@ namespace Barotrauma
|
||||
Debug.Assert(updateList.Count == updateListSet.Count);
|
||||
updateList.ForEach(c => c.UpdateAuto(deltaTime));
|
||||
UpdateMessages(deltaTime);
|
||||
UpdateSavingIndicator(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1266,6 +1272,58 @@ namespace Barotrauma
|
||||
|
||||
}
|
||||
|
||||
private static void UpdateSavingIndicator(float deltaTime)
|
||||
{
|
||||
lock (mutex)
|
||||
{
|
||||
if (timeUntilSavingIndicatorDisabled.HasValue)
|
||||
{
|
||||
timeUntilSavingIndicatorDisabled -= deltaTime;
|
||||
if (timeUntilSavingIndicatorDisabled <= 0.0f)
|
||||
{
|
||||
isSavingIndicatorEnabled = false;
|
||||
timeUntilSavingIndicatorDisabled = null;
|
||||
}
|
||||
}
|
||||
if (isSavingIndicatorEnabled)
|
||||
{
|
||||
if (savingIndicatorColor == Color.Transparent)
|
||||
{
|
||||
savingIndicatorState = SavingIndicatorState.FadingIn;
|
||||
savingIndicatorColorLerpAmount = 0.0f;
|
||||
}
|
||||
else if (savingIndicatorColor == Color.White)
|
||||
{
|
||||
savingIndicatorState = SavingIndicatorState.None;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (savingIndicatorColor == Color.White)
|
||||
{
|
||||
savingIndicatorState = SavingIndicatorState.FadingOut;
|
||||
savingIndicatorColorLerpAmount = 0.0f;
|
||||
}
|
||||
else if (savingIndicatorColor == Color.Transparent)
|
||||
{
|
||||
savingIndicatorState = SavingIndicatorState.None;
|
||||
}
|
||||
}
|
||||
if (savingIndicatorState != SavingIndicatorState.None)
|
||||
{
|
||||
bool isFadingIn = savingIndicatorState == SavingIndicatorState.FadingIn;
|
||||
Color lerpStartColor = isFadingIn ? Color.Transparent : Color.White;
|
||||
Color lerpTargetColor = isFadingIn ? Color.White : Color.Transparent;
|
||||
savingIndicatorColorLerpAmount += (isFadingIn ? 2.0f : 0.5f) * deltaTime;
|
||||
savingIndicatorColor = Color.Lerp(lerpStartColor, lerpTargetColor, savingIndicatorColorLerpAmount);
|
||||
}
|
||||
if (IsSavingIndicatorVisible)
|
||||
{
|
||||
savingIndicatorSpriteIndex = (savingIndicatorSpriteIndex + 15.0f * deltaTime) % (Style.SavingIndicator.FrameCount + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Element drawing
|
||||
|
||||
private static List<float> usedIndicatorAngles = new List<float>();
|
||||
@@ -1278,7 +1336,7 @@ namespace Barotrauma
|
||||
Vector2 diff = worldPosition - cam.WorldViewCenter;
|
||||
float dist = diff.Length();
|
||||
|
||||
float symbolScale = Math.Min(64.0f / sprite.size.X, 1.0f) * scaleMultiplier;
|
||||
float symbolScale = Math.Min(64.0f / sprite.size.X, 1.0f) * scaleMultiplier * Scale;
|
||||
|
||||
if (overrideAlpha.HasValue || dist > hideDist)
|
||||
{
|
||||
@@ -1336,9 +1394,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawLine(SpriteBatch sb, Vector2 start, Vector2 end, Color clr, float depth = 0.0f, int width = 1)
|
||||
public static void DrawLine(SpriteBatch sb, Vector2 start, Vector2 end, Color clr, float depth = 0.0f, float width = 1)
|
||||
{
|
||||
DrawLine(sb, t, start, end, clr, depth, width);
|
||||
DrawLine(sb, t, start, end, clr, depth, (int)width);
|
||||
}
|
||||
|
||||
public static void DrawLine(SpriteBatch sb, Sprite sprite, Vector2 start, Vector2 end, Color clr, float depth = 0.0f, int width = 1)
|
||||
@@ -1405,7 +1463,7 @@ namespace Barotrauma
|
||||
font.DrawStringWithColors(sb, text, pos, color, 0.0f, Vector2.Zero, 1f, SpriteEffects.None, depth, richTextData);
|
||||
}
|
||||
|
||||
public static void DrawRectangle(SpriteBatch sb, Vector2 start, Vector2 size, Color clr, bool isFilled = false, float depth = 0.0f, int thickness = 1)
|
||||
public static void DrawRectangle(SpriteBatch sb, Vector2 start, Vector2 size, Color clr, bool isFilled = false, float depth = 0.0f, float thickness = 1)
|
||||
{
|
||||
if (size.X < 0)
|
||||
{
|
||||
@@ -1420,7 +1478,7 @@ namespace Barotrauma
|
||||
DrawRectangle(sb, new Rectangle((int)start.X, (int)start.Y, (int)size.X, (int)size.Y), clr, isFilled, depth, thickness);
|
||||
}
|
||||
|
||||
public static void DrawRectangle(SpriteBatch sb, Rectangle rect, Color clr, bool isFilled = false, float depth = 0.0f, int thickness = 1)
|
||||
public static void DrawRectangle(SpriteBatch sb, Rectangle rect, Color clr, bool isFilled = false, float depth = 0.0f, float thickness = 1)
|
||||
{
|
||||
if (isFilled)
|
||||
{
|
||||
@@ -1428,15 +1486,15 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Draw(t, new Rectangle(rect.X + thickness, rect.Y, rect.Width - thickness * 2, thickness), null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth);
|
||||
sb.Draw(t, new Rectangle(rect.X + thickness, rect.Y + rect.Height - thickness, rect.Width - thickness * 2, thickness), null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth);
|
||||
|
||||
sb.Draw(t, new Rectangle(rect.X, rect.Y, thickness, rect.Height), null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth);
|
||||
sb.Draw(t, new Rectangle(rect.X + rect.Width - thickness, rect.Y, thickness, rect.Height), null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth);
|
||||
Rectangle srcRect = new Rectangle(0, 0, 1, 1);
|
||||
sb.Draw(t, new Vector2(rect.X, rect.Y), srcRect, clr, 0.0f, Vector2.Zero, new Vector2(thickness, rect.Height), SpriteEffects.None, depth);
|
||||
sb.Draw(t, new Vector2(rect.X + thickness, rect.Y), srcRect, clr, 0.0f, Vector2.Zero, new Vector2(rect.Width - thickness, thickness), SpriteEffects.None, depth);
|
||||
sb.Draw(t, new Vector2(rect.X + thickness, rect.Bottom - thickness), srcRect, clr, 0.0f, Vector2.Zero, new Vector2(rect.Width - thickness, thickness), SpriteEffects.None, depth);
|
||||
sb.Draw(t, new Vector2(rect.Right - thickness, rect.Y + thickness), srcRect, clr, 0.0f, Vector2.Zero, new Vector2(thickness, rect.Height - thickness * 2f), SpriteEffects.None, depth);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawRectangle(SpriteBatch sb, Vector2 center, float width, float height, float rotation, Color clr, float depth = 0.0f, int thickness = 1)
|
||||
public static void DrawRectangle(SpriteBatch sb, Vector2 center, float width, float height, float rotation, Color clr, float depth = 0.0f, float thickness = 1)
|
||||
{
|
||||
Matrix rotate = Matrix.CreateRotationZ(rotation);
|
||||
|
||||
@@ -1453,7 +1511,7 @@ namespace Barotrauma
|
||||
DrawLine(sb, bottomLeft, topLeft, clr, depth, thickness);
|
||||
}
|
||||
|
||||
public static void DrawRectangle(SpriteBatch sb, Vector2[] corners, Color clr, float depth = 0.0f, int thickness = 1)
|
||||
public static void DrawRectangle(SpriteBatch sb, Vector2[] corners, Color clr, float depth = 0.0f, float thickness = 1)
|
||||
{
|
||||
if (corners.Length != 4)
|
||||
{
|
||||
@@ -1590,6 +1648,14 @@ namespace Barotrauma
|
||||
ShapeExtensions.DrawPoint(spriteBatch, pos, color, dotSize);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DrawSavingIndicator(SpriteBatch spriteBatch)
|
||||
{
|
||||
if (!IsSavingIndicatorVisible) { return; }
|
||||
var sheet = Style.SavingIndicator;
|
||||
Vector2 pos = new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight) - new Vector2(HUDLayoutSettings.Padding) - 2 * Scale * sheet.FrameSize.ToVector2();
|
||||
sheet.Draw(spriteBatch, (int)Math.Floor(savingIndicatorSpriteIndex), pos, savingIndicatorColor, origin: Vector2.Zero, rotate: 0.0f, scale: new Vector2(Scale));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Element creation
|
||||
@@ -2246,7 +2312,7 @@ namespace Barotrauma
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
if (!GameMain.Client.HasPermission(ClientPermissions.ManageRound)) { return false; }
|
||||
if (GameMain.GameSession.GameMode is CampaignMode || (!Submarine.MainSub.AtStartPosition && !Submarine.MainSub.AtEndPosition))
|
||||
if (GameMain.GameSession.GameMode is CampaignMode || (!Submarine.MainSub.AtStartExit && !Submarine.MainSub.AtEndExit))
|
||||
{
|
||||
var msgBox = new GUIMessageBox("",
|
||||
TextManager.Get(GameMain.GameSession.GameMode is CampaignMode ? "PauseMenuReturnToServerLobbyVerification" : "EndRoundSubNotAtLevelEnd"),
|
||||
@@ -2343,6 +2409,21 @@ namespace Barotrauma
|
||||
float aspectRatio = HorizontalAspectRatio;
|
||||
return aspectRatio > 1.3f && aspectRatio < 1.4f;
|
||||
}
|
||||
|
||||
public static void SetSavingIndicatorState(bool enabled)
|
||||
{
|
||||
if (enabled)
|
||||
{
|
||||
timeUntilSavingIndicatorDisabled = null;
|
||||
}
|
||||
isSavingIndicatorEnabled = enabled;
|
||||
}
|
||||
|
||||
public static void DisableSavingIndicatorDelayed(float delay = 3.0f)
|
||||
{
|
||||
if (!isSavingIndicatorEnabled) { return; }
|
||||
timeUntilSavingIndicatorDisabled = delay;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,6 +77,12 @@ namespace Barotrauma
|
||||
return RectTransform.IsParentOf(component.RectTransform, recursive);
|
||||
}
|
||||
|
||||
public bool IsChildOf(GUIComponent component, bool recursive = true)
|
||||
{
|
||||
if (component == null) { return false; }
|
||||
return RectTransform.IsChildOf(component.RectTransform, recursive);
|
||||
}
|
||||
|
||||
public virtual void RemoveChild(GUIComponent child)
|
||||
{
|
||||
if (child == null) { return; }
|
||||
@@ -705,6 +711,29 @@ namespace Barotrauma
|
||||
if (!Visible) return;
|
||||
DrawToolTip(spriteBatch, ToolTip, GUI.MouseOn.Rect, TooltipRichTextData);
|
||||
}
|
||||
|
||||
public static void DrawToolTip(SpriteBatch spriteBatch, string toolTip, Vector2 pos, List<RichTextData> richTextData = null)
|
||||
{
|
||||
if (Tutorials.Tutorial.ContentRunning) { return; }
|
||||
|
||||
int width = (int)(400 * GUI.Scale);
|
||||
int height = (int)(18 * GUI.Scale);
|
||||
Point padding = new Point((int)(10 * GUI.Scale));
|
||||
|
||||
if (toolTipBlock == null || (string)toolTipBlock.userData != toolTip)
|
||||
{
|
||||
toolTipBlock = new GUITextBlock(new RectTransform(new Point(width, height), null), richTextData, toolTip, font: GUI.SmallFont, wrap: true, style: "GUIToolTip");
|
||||
toolTipBlock.RectTransform.NonScaledSize = new Point(
|
||||
(int)(GUI.SmallFont.MeasureString(toolTipBlock.WrappedText).X + padding.X + toolTipBlock.Padding.X + toolTipBlock.Padding.Z),
|
||||
(int)(GUI.SmallFont.MeasureString(toolTipBlock.WrappedText).Y + padding.Y + toolTipBlock.Padding.Y + toolTipBlock.Padding.W));
|
||||
toolTipBlock.userData = toolTip;
|
||||
}
|
||||
|
||||
toolTipBlock.RectTransform.AbsoluteOffset = pos.ToPoint();
|
||||
toolTipBlock.SetTextPos();
|
||||
|
||||
toolTipBlock.DrawManually(spriteBatch);
|
||||
}
|
||||
|
||||
public static void DrawToolTip(SpriteBatch spriteBatch, string toolTip, Rectangle targetElement, List<RichTextData> richTextData = null)
|
||||
{
|
||||
|
||||
292
Barotrauma/BarotraumaClient/ClientSource/GUI/GUIContextMenu.cs
Normal file
292
Barotrauma/BarotraumaClient/ClientSource/GUI/GUIContextMenu.cs
Normal file
@@ -0,0 +1,292 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
struct ContextMenuOption
|
||||
{
|
||||
public string Label;
|
||||
public Action OnSelected;
|
||||
public ContextMenuOption[]? SubOptions;
|
||||
public bool IsEnabled;
|
||||
public string Tooltip;
|
||||
|
||||
// Creates a regular context menu
|
||||
public ContextMenuOption(string label, bool isEnabled, Action onSelected)
|
||||
{
|
||||
Label = TextManager.Get(label, returnNull: true) ?? label;
|
||||
OnSelected = onSelected;
|
||||
IsEnabled = isEnabled;
|
||||
SubOptions = null;
|
||||
Tooltip = string.Empty;
|
||||
}
|
||||
|
||||
// Creates a option with a sub context menu
|
||||
public ContextMenuOption(string label, bool isEnabled, params ContextMenuOption[] options): this(label, isEnabled, () => { })
|
||||
{
|
||||
SubOptions = options;
|
||||
}
|
||||
}
|
||||
|
||||
internal class GUIContextMenu : GUIComponent
|
||||
{
|
||||
public static GUIContextMenu? CurrentContextMenu;
|
||||
|
||||
private readonly Dictionary<ContextMenuOption, GUITextBlock> Options = new Dictionary<ContextMenuOption, GUITextBlock>();
|
||||
private GUIContextMenu? SubMenu;
|
||||
public readonly GUITextBlock? HeaderLabel;
|
||||
public GUITextBlock? ParentOption;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a context menu. This constructor does not make the context menu active.
|
||||
/// Use <see cref="CreateContextMenu(Barotrauma.ContextMenuOption[])"/> to make right click context menus.
|
||||
/// </summary>
|
||||
/// <param name="position">Position at which to create the context menu</param>
|
||||
/// <param name="header">Header text</param>
|
||||
/// <param name="style">Background style</param>
|
||||
/// <param name="options">list of context menu options</param>
|
||||
public GUIContextMenu(Vector2? position, string header, string style, params ContextMenuOption[] options) : base(style, new RectTransform(Point.Zero, GUI.Canvas))
|
||||
{
|
||||
Vector2 pos = position ?? PlayerInput.MousePosition;
|
||||
ScalableFont headerFont = GUI.SubHeadingFont;
|
||||
ScalableFont font = GUI.SmallFont; // font the context menu options use
|
||||
Vector4 padding = new Vector4(4), headerPadding = new Vector4(8);
|
||||
int horizontalPadding = (int) (padding.X + padding.Z), verticalPadding = (int) (padding.Y + padding.W);
|
||||
bool hasHeader = !string.IsNullOrWhiteSpace(header);
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Estimate the size of the context menu
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
Dictionary<ContextMenuOption, Vector2> optionsAndSizes = new Dictionary<ContextMenuOption, Vector2>();
|
||||
|
||||
// estimate how big the context menu needs to be
|
||||
Point estimatedSize = new Point(horizontalPadding, verticalPadding);
|
||||
|
||||
if (hasHeader)
|
||||
{
|
||||
InflateSize(ref estimatedSize, header, headerFont);
|
||||
}
|
||||
|
||||
foreach (ContextMenuOption option in options)
|
||||
{
|
||||
Vector2 optionSize = InflateSize(ref estimatedSize, option.Label, font);
|
||||
optionsAndSizes.Add(option, optionSize);
|
||||
}
|
||||
|
||||
// it's better to overestimate the size since it's going to be cropped anyways
|
||||
estimatedSize = estimatedSize.Multiply(1.2f);
|
||||
|
||||
RectTransform.NonScaledSize = estimatedSize;
|
||||
RectTransform.AbsoluteOffset = pos.ToPoint();
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Construct the GUI elements
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
GUILayoutGroup background = new GUILayoutGroup(new RectTransform(Vector2.One, RectTransform, Anchor.Center));
|
||||
|
||||
if (hasHeader)
|
||||
{
|
||||
HeaderLabel = new GUITextBlock(new RectTransform(new Vector2(1f, 0.2f), background.RectTransform), header, font: headerFont) { Padding = headerPadding };
|
||||
}
|
||||
|
||||
GUIListBox optionList = new GUIListBox(new RectTransform(new Vector2(1f, hasHeader ? 0.8f : 1f), background.RectTransform), style: null)
|
||||
{
|
||||
AutoHideScrollBar = false,
|
||||
ScrollBarVisible = false,
|
||||
Padding = hasHeader ? new Vector4(4, 0, 4, 4) : padding
|
||||
};
|
||||
|
||||
foreach (var (option, size) in optionsAndSizes)
|
||||
{
|
||||
GUITextBlock optionElement = new GUITextBlock(new RectTransform(size.ToPoint(), optionList.Content.RectTransform), option.Label, font: font)
|
||||
{
|
||||
UserData = option,
|
||||
Enabled = option.IsEnabled
|
||||
};
|
||||
Options.Add(option, optionElement);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(option.Tooltip) && optionElement.Enabled)
|
||||
{
|
||||
optionElement.ToolTip = option.Tooltip;
|
||||
}
|
||||
|
||||
if (!option.IsEnabled)
|
||||
{
|
||||
optionElement.TextColor *= 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Positioning and cropping the context menu
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
List<GUIComponent> children = optionList.Content.Children.ToList();
|
||||
|
||||
// Resize all children to the size of their text
|
||||
foreach (GUITextBlock block in children.Where(c => c is GUITextBlock).Cast<GUITextBlock>())
|
||||
{
|
||||
block.RectTransform.NonScaledSize = new Point((int) (block.TextSize.X + (block.Padding.X + block.Padding.Z)), (int) (18 * GUI.Scale));
|
||||
}
|
||||
|
||||
int largestWidth = children.Max(c => c.Rect.Width + horizontalPadding);
|
||||
|
||||
// if the header is bigger than any of the options then overwrite
|
||||
if (HeaderLabel != null)
|
||||
{
|
||||
RectTransform headerTransform = HeaderLabel.RectTransform;
|
||||
headerTransform.MinSize = new Point((int) (HeaderLabel.TextSize.X + (headerPadding.X + headerPadding.Z)), headerTransform.NonScaledSize.Y);
|
||||
if (largestWidth < headerTransform.MinSize.X)
|
||||
{
|
||||
largestWidth = headerTransform.MinSize.X;
|
||||
}
|
||||
}
|
||||
|
||||
// resize all children to the size of the longest element
|
||||
foreach (GUIComponent c in children)
|
||||
{
|
||||
c.RectTransform.MinSize = new Point(largestWidth, c.Rect.Height);
|
||||
}
|
||||
|
||||
// the cropped size of the option list
|
||||
Point newSize = new Point(largestWidth, children.Sum(c => c.Rect.Height) + verticalPadding);
|
||||
// resize the menu itself taking into account the option menus relative Y size
|
||||
RectTransform.NonScaledSize = new Point(newSize.X, (int) (newSize.Y / optionList.RectTransform.RelativeSize.Y));
|
||||
optionList.RectTransform.NonScaledSize = newSize;
|
||||
|
||||
// move the context menu if it would go outside of screen
|
||||
if (RectTransform.Rect.Bottom > GameMain.GraphicsHeight)
|
||||
{
|
||||
Rectangle rect = RectTransform.Rect;
|
||||
RectTransform.AbsoluteOffset = new Point(rect.X, rect.Y - rect.Height);
|
||||
}
|
||||
|
||||
if (RectTransform.Rect.Right > GameMain.GraphicsWidth)
|
||||
{
|
||||
Rectangle rect = RectTransform.Rect;
|
||||
RectTransform.AbsoluteOffset = new Point(rect.X - rect.Width, rect.Y);
|
||||
}
|
||||
|
||||
background.Recalculate();
|
||||
|
||||
optionList.OnSelected = OnSelected;
|
||||
}
|
||||
|
||||
public static GUIContextMenu CreateContextMenu(params ContextMenuOption[] options) => CreateContextMenu(PlayerInput.MousePosition, string.Empty, null, options);
|
||||
|
||||
public static GUIContextMenu CreateContextMenu(Vector2? pos, string header, Color? headerColor, params ContextMenuOption[] options)
|
||||
{
|
||||
GUIContextMenu menu = new GUIContextMenu(pos,header, "GUIToolTip", options);
|
||||
if (headerColor != null)
|
||||
{
|
||||
menu.HeaderLabel?.OverrideTextColor(headerColor.Value);
|
||||
}
|
||||
CurrentContextMenu = menu;
|
||||
return menu;
|
||||
}
|
||||
|
||||
private bool OnSelected(GUIComponent _, object data)
|
||||
{
|
||||
if (data is ContextMenuOption option && option.IsEnabled)
|
||||
{
|
||||
CurrentContextMenu = null;
|
||||
option.OnSelected();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inflates a point by the size of the text
|
||||
/// </summary>
|
||||
/// <param name="size">Pint to resize</param>
|
||||
/// <param name="label">String whose size to inflate by</param>
|
||||
/// <param name="font">What font to use</param>
|
||||
/// <returns>The size of the text</returns>
|
||||
private Vector2 InflateSize(ref Point size, string label, ScalableFont font)
|
||||
{
|
||||
Vector2 textSize = font.MeasureString(label);
|
||||
size.X = Math.Max((int) Math.Ceiling(textSize.X), size.X);
|
||||
size.Y += (int) Math.Ceiling(textSize.Y);
|
||||
return textSize;
|
||||
}
|
||||
|
||||
protected override void Update(float deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
|
||||
// keep the parent highlighted
|
||||
if (ParentOption != null)
|
||||
{
|
||||
ParentOption.State = ComponentState.Hover;
|
||||
}
|
||||
|
||||
if (SubMenu != null && !SubMenu.IsMouseOver())
|
||||
{
|
||||
SubMenu = null;
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (option, textBlock) in Options)
|
||||
{
|
||||
// Create a new sub context menu if hovering over an option with sub options
|
||||
if (GUI.MouseOn != textBlock) { continue; }
|
||||
if (option.IsEnabled && option.SubOptions is { } subOptions && subOptions.Any())
|
||||
{
|
||||
Vector2 subMenuPos = new Vector2(textBlock.MouseRect.Right + 4, textBlock.MouseRect.Y);
|
||||
SubMenu = new GUIContextMenu(subMenuPos, "", "GUIToolTip", subOptions)
|
||||
{
|
||||
ParentOption = textBlock
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the mouse cursor is over this context menu or any of its sub menus
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private bool IsMouseOver()
|
||||
{
|
||||
Rectangle expandedRect = Rect;
|
||||
expandedRect.Inflate(20, 20);
|
||||
|
||||
bool isMouseOn = expandedRect.Contains(PlayerInput.MousePosition);
|
||||
|
||||
if (ParentOption != null)
|
||||
{
|
||||
isMouseOn |= GUI.MouseOn == ParentOption;
|
||||
}
|
||||
|
||||
// Recursively check sub context menus
|
||||
if (!isMouseOn && SubMenu != null)
|
||||
{
|
||||
isMouseOn = SubMenu.IsMouseOver();
|
||||
}
|
||||
|
||||
return isMouseOn;
|
||||
}
|
||||
|
||||
public override void AddToGUIUpdateList(bool ignoreChildren = false, int order = 0)
|
||||
{
|
||||
base.AddToGUIUpdateList(ignoreChildren, order);
|
||||
SubMenu?.AddToGUIUpdateList();
|
||||
}
|
||||
|
||||
public static void AddActiveToGUIUpdateList()
|
||||
{
|
||||
if (CurrentContextMenu != null && !CurrentContextMenu.IsMouseOver())
|
||||
{
|
||||
CurrentContextMenu = null;
|
||||
}
|
||||
|
||||
CurrentContextMenu?.AddToGUIUpdateList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -194,6 +194,8 @@ namespace Barotrauma
|
||||
public bool ScrollBarEnabled { get; set; } = true;
|
||||
public bool KeepSpaceForScrollBar { get; set; }
|
||||
|
||||
public bool CanTakeKeyBoardFocus { get; set; } = true;
|
||||
|
||||
public bool ScrollBarVisible
|
||||
{
|
||||
get
|
||||
@@ -214,7 +216,22 @@ namespace Barotrauma
|
||||
public bool AutoHideScrollBar { get; set; } = true;
|
||||
private bool IsScrollBarOnDefaultSide { get; set; }
|
||||
|
||||
public bool CanDragElements { get; set; } = false;
|
||||
public bool CanDragElements
|
||||
{
|
||||
get
|
||||
{
|
||||
return canDragElements;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == false && canDragElements && draggedElement != null)
|
||||
{
|
||||
draggedElement = null;
|
||||
}
|
||||
canDragElements = value;
|
||||
}
|
||||
}
|
||||
private bool canDragElements = false;
|
||||
private GUIComponent draggedElement;
|
||||
private Rectangle draggedReferenceRectangle;
|
||||
private Point draggedReferenceOffset;
|
||||
@@ -223,9 +240,12 @@ namespace Barotrauma
|
||||
|
||||
private bool scheduledScroll = false;
|
||||
|
||||
private readonly bool isHorizontal;
|
||||
|
||||
/// <param name="isScrollBarOnDefaultSide">For horizontal listbox, default side is on the bottom. For vertical, it's on the right.</param>
|
||||
public GUIListBox(RectTransform rectT, bool isHorizontal = false, Color? color = null, string style = "", bool isScrollBarOnDefaultSide = true, bool useMouseDownToSelect = false) : base(style, rectT)
|
||||
{
|
||||
this.isHorizontal = isHorizontal;
|
||||
HoverCursor = CursorState.Hand;
|
||||
CanBeFocused = true;
|
||||
selected = new List<GUIComponent>();
|
||||
@@ -454,22 +474,42 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
draggedElement.RectTransform.AbsoluteOffset = draggedReferenceOffset + new Point(0, (int)PlayerInput.MousePosition.Y - draggedReferenceRectangle.Center.Y);
|
||||
draggedElement.RectTransform.AbsoluteOffset = isHorizontal ?
|
||||
draggedReferenceOffset + new Point((int)PlayerInput.MousePosition.X - draggedReferenceRectangle.Center.X, 0) :
|
||||
draggedReferenceOffset + new Point(0, (int)PlayerInput.MousePosition.Y - draggedReferenceRectangle.Center.Y);
|
||||
|
||||
int index = Content.RectTransform.GetChildIndex(draggedElement.RectTransform);
|
||||
int currIndex = index;
|
||||
|
||||
while (currIndex > 0 && PlayerInput.MousePosition.Y < draggedReferenceRectangle.Top)
|
||||
if (isHorizontal)
|
||||
{
|
||||
currIndex--;
|
||||
draggedReferenceRectangle.Y -= draggedReferenceRectangle.Height;
|
||||
draggedReferenceOffset.Y -= draggedReferenceRectangle.Height;
|
||||
while (currIndex > 0 && PlayerInput.MousePosition.X < draggedReferenceRectangle.Left)
|
||||
{
|
||||
currIndex--;
|
||||
draggedReferenceRectangle.X -= draggedReferenceRectangle.Width;
|
||||
draggedReferenceOffset.X -= draggedReferenceRectangle.Width;
|
||||
}
|
||||
while (currIndex < Content.CountChildren - 1 && PlayerInput.MousePosition.X > draggedReferenceRectangle.Right)
|
||||
{
|
||||
currIndex++;
|
||||
draggedReferenceRectangle.X += draggedReferenceRectangle.Width;
|
||||
draggedReferenceOffset.X += draggedReferenceRectangle.Width;
|
||||
}
|
||||
}
|
||||
while (currIndex < Content.CountChildren - 1 && PlayerInput.MousePosition.Y > draggedReferenceRectangle.Bottom)
|
||||
else
|
||||
{
|
||||
currIndex++;
|
||||
draggedReferenceRectangle.Y += draggedReferenceRectangle.Height;
|
||||
draggedReferenceOffset.Y += draggedReferenceRectangle.Height;
|
||||
while (currIndex > 0 && PlayerInput.MousePosition.Y < draggedReferenceRectangle.Top)
|
||||
{
|
||||
currIndex--;
|
||||
draggedReferenceRectangle.Y -= draggedReferenceRectangle.Height;
|
||||
draggedReferenceOffset.Y -= draggedReferenceRectangle.Height;
|
||||
}
|
||||
while (currIndex < Content.CountChildren - 1 && PlayerInput.MousePosition.Y > draggedReferenceRectangle.Bottom)
|
||||
{
|
||||
currIndex++;
|
||||
draggedReferenceRectangle.Y += draggedReferenceRectangle.Height;
|
||||
draggedReferenceOffset.Y += draggedReferenceRectangle.Height;
|
||||
}
|
||||
}
|
||||
|
||||
if (currIndex != index)
|
||||
@@ -853,7 +893,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
// If one of the children is the subscriber, we don't want to register, because it will unregister the child.
|
||||
if (takeKeyBoardFocus && RectTransform.GetAllChildren().None(rt => rt.GUIComponent == GUI.KeyboardDispatcher.Subscriber))
|
||||
if (takeKeyBoardFocus && CanTakeKeyBoardFocus && RectTransform.GetAllChildren().None(rt => rt.GUIComponent == GUI.KeyboardDispatcher.Subscriber))
|
||||
{
|
||||
Selected = true;
|
||||
GUI.KeyboardDispatcher.Subscriber = this;
|
||||
@@ -943,9 +983,10 @@ namespace Barotrauma
|
||||
|
||||
public override void RemoveChild(GUIComponent child)
|
||||
{
|
||||
if (child == null) { return; }
|
||||
if (child == null) { return; }
|
||||
child.RectTransform.Parent = null;
|
||||
if (selected.Contains(child)) selected.Remove(child);
|
||||
if (selected.Contains(child)) { selected.Remove(child); }
|
||||
if (draggedElement == child) { draggedElement = null; }
|
||||
UpdateScrollBarSize();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -22,11 +23,11 @@ namespace Barotrauma
|
||||
{
|
||||
Default,
|
||||
InGame,
|
||||
Vote
|
||||
Vote,
|
||||
Hint
|
||||
}
|
||||
|
||||
public List<GUIButton> Buttons { get; private set; } = new List<GUIButton>();
|
||||
//public GUIFrame BackgroundFrame { get; private set; }
|
||||
public GUILayoutGroup Content { get; private set; }
|
||||
public GUIFrame InnerFrame { get; private set; }
|
||||
public GUITextBlock Header { get; private set; }
|
||||
@@ -58,8 +59,6 @@ namespace Barotrauma
|
||||
|
||||
public bool AutoClose;
|
||||
|
||||
private readonly bool alwaysVisible;
|
||||
|
||||
private float openState;
|
||||
private float iconState;
|
||||
private bool iconSwitching;
|
||||
@@ -77,10 +76,17 @@ namespace Barotrauma
|
||||
this.Buttons[0].OnClicked = Close;
|
||||
}
|
||||
|
||||
public GUIMessageBox(string headerText, string text, string[] buttons, Vector2? relativeSize = null, Point? minSize = null, Alignment textAlignment = Alignment.TopLeft, Type type = Type.Default, string tag = "", Sprite icon = null, string iconStyle = "", Sprite backgroundIcon = null)
|
||||
public GUIMessageBox(string headerText, string text, string[] buttons, Vector2? relativeSize = null, Point? minSize = null, Alignment textAlignment = Alignment.TopLeft, Type type = Type.Default, string tag = "", Sprite icon = null, string iconStyle = "", Sprite backgroundIcon = null, bool parseRichText = false)
|
||||
: base(new RectTransform(GUI.Canvas.RelativeSize, GUI.Canvas, Anchor.Center), style: GUI.Style.GetComponentStyle("GUIMessageBox." + type) != null ? "GUIMessageBox." + type : "GUIMessageBox")
|
||||
{
|
||||
int width = (int)(DefaultWidth * (type == Type.Default ? 1.0f : 1.5f)), height = 0;
|
||||
int width = (int)(DefaultWidth * type switch
|
||||
{
|
||||
Type.Default => 1.0f,
|
||||
Type.Hint => 1.25f,
|
||||
_ => 1.5f
|
||||
});
|
||||
int height = 0;
|
||||
|
||||
if (relativeSize.HasValue)
|
||||
{
|
||||
width = (int)(GameMain.GraphicsWidth * relativeSize.Value.X);
|
||||
@@ -107,6 +113,7 @@ namespace Barotrauma
|
||||
Anchor anchor = type switch
|
||||
{
|
||||
Type.InGame => Anchor.TopCenter,
|
||||
Type.Hint => Anchor.TopRight,
|
||||
Type.Vote => Anchor.TopRight,
|
||||
_ => Anchor.Center
|
||||
};
|
||||
@@ -127,13 +134,13 @@ namespace Barotrauma
|
||||
Content = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.85f), InnerFrame.RectTransform, Anchor.Center)) { AbsoluteSpacing = 5 };
|
||||
|
||||
Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform),
|
||||
headerText, font: GUI.SubHeadingFont, textAlignment: Alignment.Center, wrap: true);
|
||||
headerText, font: GUI.SubHeadingFont, textAlignment: Alignment.Center, wrap: true, parseRichText: parseRichText);
|
||||
GUI.Style.Apply(Header, "", this);
|
||||
Header.RectTransform.MinSize = new Point(0, Header.Rect.Height);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true);
|
||||
Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true, parseRichText: parseRichText);
|
||||
GUI.Style.Apply(Text, "", this);
|
||||
Text.RectTransform.NonScaledSize = Text.RectTransform.MinSize = Text.RectTransform.MaxSize =
|
||||
new Point(Text.Rect.Width, Text.Rect.Height);
|
||||
@@ -180,7 +187,6 @@ namespace Barotrauma
|
||||
else if (type == Type.InGame)
|
||||
{
|
||||
InnerFrame.RectTransform.AbsoluteOffset = new Point(0, GameMain.GraphicsHeight);
|
||||
alwaysVisible = true;
|
||||
CanBeFocused = false;
|
||||
AutoClose = true;
|
||||
GUI.Style.Apply(InnerFrame, "", this);
|
||||
@@ -235,13 +241,13 @@ namespace Barotrauma
|
||||
};
|
||||
}
|
||||
|
||||
Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), headerText, wrap: true);
|
||||
Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), headerText, wrap: true, parseRichText: parseRichText);
|
||||
GUI.Style.Apply(Header, "", this);
|
||||
Header.RectTransform.MinSize = new Point(0, Header.Rect.Height);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true);
|
||||
Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true, parseRichText: parseRichText);
|
||||
GUI.Style.Apply(Text, "", this);
|
||||
Content.Recalculate();
|
||||
Text.RectTransform.NonScaledSize = Text.RectTransform.MinSize = Text.RectTransform.MaxSize =
|
||||
@@ -266,30 +272,184 @@ namespace Barotrauma
|
||||
}
|
||||
Buttons[0].RectTransform.MaxSize = new Point((int)(0.4f * Buttons[0].Rect.Y), Buttons[0].Rect.Y);
|
||||
}
|
||||
|
||||
else if (type == Type.Hint)
|
||||
{
|
||||
CanBeFocused = false;
|
||||
GUI.Style.Apply(InnerFrame, "", this);
|
||||
|
||||
Point absoluteSpacing = GUIStyle.ItemFrameMargin.Multiply(1.0f / 5.0f);
|
||||
var verticalLayoutGroup = new GUILayoutGroup(new RectTransform(GetVerticalLayoutGroupSize(), parent: InnerFrame.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter)
|
||||
{
|
||||
AbsoluteSpacing = absoluteSpacing.Y,
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
var topHorizontalLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.7f), verticalLayoutGroup.RectTransform),
|
||||
isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.02f
|
||||
};
|
||||
|
||||
int iconMaxHeight = 0;
|
||||
if (icon != null)
|
||||
{
|
||||
Icon = new GUIImage(new RectTransform(new Vector2(0.15f, 0.95f), topHorizontalLayoutGroup.RectTransform), icon, scaleToFit: true);
|
||||
iconMaxHeight = (int)Icon.Sprite.size.Y;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool iconStyleDefined = !string.IsNullOrEmpty(iconStyle);
|
||||
Icon = new GUIImage(new RectTransform(new Vector2(0.15f, 0.95f), topHorizontalLayoutGroup.RectTransform),
|
||||
iconStyleDefined ? iconStyle : "GUIButtonInfo", scaleToFit: true);
|
||||
if (!iconStyleDefined)
|
||||
{
|
||||
Icon.Color = Color.Orange;
|
||||
}
|
||||
iconMaxHeight = (int)(Icon.Style.GetDefaultSprite()?.size.Y ?? GUI.yScale * 40);
|
||||
}
|
||||
|
||||
iconMaxHeight = Math.Min((int)(GUI.yScale * 40), iconMaxHeight);
|
||||
int iconMinHeight = Math.Min((int)(GUI.yScale * 40), iconMaxHeight);
|
||||
Icon.RectTransform.MinSize = new Point(Icon.Rect.Width, iconMinHeight);
|
||||
Icon.RectTransform.MaxSize = new Point(Icon.Rect.Width, iconMaxHeight);
|
||||
|
||||
Content = new GUILayoutGroup(new RectTransform(new Vector2(Icon != null ? 0.85f : 1.0f, 1.0f), topHorizontalLayoutGroup.RectTransform))
|
||||
{
|
||||
AbsoluteSpacing = absoluteSpacing.Y,
|
||||
};
|
||||
|
||||
var bottomContainer = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.3f), verticalLayoutGroup.RectTransform), style: null);
|
||||
|
||||
var tickBoxLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.67f, 1.0f), bottomContainer.RectTransform, anchor: Anchor.CenterLeft),
|
||||
isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.02f
|
||||
};
|
||||
|
||||
var dontShowAgainTickBox = new GUITickBox(new RectTransform(new Vector2(0.5f, 1.0f), tickBoxLayoutGroup.RectTransform),
|
||||
TextManager.Get("hintmessagebox.dontshowagain"))
|
||||
{
|
||||
ToolTip = TextManager.Get("hintmessagebox.dontshowagaintooltip"),
|
||||
UserData = "dontshowagain"
|
||||
};
|
||||
|
||||
//var disableHintsTickBox = new GUITickBox(new RectTransform(new Vector2(0.33f, 1.0f), tickBoxLayoutGroup.RectTransform),
|
||||
// TextManager.Get("hintmessagebox.disablehints"))
|
||||
//{
|
||||
// ToolTip = TextManager.Get("hintmessagebox.disablehintstooltip"),
|
||||
// UserData = "disablehints"
|
||||
//};
|
||||
|
||||
Buttons = new List<GUIButton>(1)
|
||||
{
|
||||
new GUIButton(new RectTransform(new Vector2(0.33f, 1.0f), bottomContainer.RectTransform, Anchor.CenterRight),
|
||||
text: TextManager.Get("hintmessagebox.dismiss"), style: "GUIButtonSmall")
|
||||
{
|
||||
OnClicked = Close
|
||||
}
|
||||
};
|
||||
|
||||
Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), headerText, wrap: true);
|
||||
GUI.Style.Apply(Header, "", this);
|
||||
Header.RectTransform.MinSize = new Point(0, Header.Rect.Height);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true);
|
||||
GUI.Style.Apply(Text, "", this);
|
||||
Content.Recalculate();
|
||||
Text.RectTransform.NonScaledSize = Text.RectTransform.MinSize = Text.RectTransform.MaxSize =
|
||||
new Point(Text.Rect.Width, Text.Rect.Height);
|
||||
Text.RectTransform.IsFixedSize = true;
|
||||
if (string.IsNullOrWhiteSpace(headerText))
|
||||
{
|
||||
Header.RectTransform.Parent = null;
|
||||
Content.ChildAnchor = Anchor.Center;
|
||||
}
|
||||
}
|
||||
|
||||
if (height == 0)
|
||||
{
|
||||
height = absoluteSpacing.Y;
|
||||
int upperContainerHeight = absoluteSpacing.Y;
|
||||
if (Header.Rect.Height > 0) { upperContainerHeight += Header.Rect.Height + Content.AbsoluteSpacing; }
|
||||
if (Text != null) { upperContainerHeight += Text.Rect.Height + Content.AbsoluteSpacing; }
|
||||
upperContainerHeight = Math.Max(upperContainerHeight, Icon.Rect.Height);
|
||||
height += upperContainerHeight;
|
||||
height += absoluteSpacing.Y;
|
||||
height += (int)((bottomContainer.RectTransform.RelativeSize.Y / topHorizontalLayoutGroup.RectTransform.RelativeSize.Y) * upperContainerHeight);
|
||||
height += absoluteSpacing.Y;
|
||||
if (minSize.HasValue) { height = Math.Max(height, minSize.Value.Y); }
|
||||
|
||||
InnerFrame.RectTransform.NonScaledSize = new Point(InnerFrame.Rect.Width, height);
|
||||
verticalLayoutGroup.RectTransform.NonScaledSize = GetVerticalLayoutGroupSize();
|
||||
verticalLayoutGroup.Recalculate();
|
||||
topHorizontalLayoutGroup.Recalculate();
|
||||
Content.Recalculate();
|
||||
tickBoxLayoutGroup.Recalculate();
|
||||
}
|
||||
|
||||
InnerFrame.RectTransform.AbsoluteOffset = new Point(GUI.IntScale(64), -InnerFrame.Rect.Height);
|
||||
|
||||
Point GetVerticalLayoutGroupSize()
|
||||
{
|
||||
return InnerFrame.Rect.Size - absoluteSpacing.Multiply(2);
|
||||
}
|
||||
}
|
||||
|
||||
MessageBoxes.Add(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use to create a message box of Hint type
|
||||
/// </summary>
|
||||
public GUIMessageBox(string hintIdentifier, string text, Sprite icon) : this("", text, new string[0], textAlignment: Alignment.CenterLeft, type: Type.Hint, icon: icon)
|
||||
{
|
||||
if (InnerFrame.FindChild("dontshowagain", recursive: true) is GUITickBox dontShowAgainTickBox)
|
||||
{
|
||||
dontShowAgainTickBox.OnSelected = HintManager.OnDontShowAgain;
|
||||
dontShowAgainTickBox.UserData = hintIdentifier;
|
||||
}
|
||||
if (InnerFrame.FindChild("disablehints", recursive: true) is GUITickBox disableHintsTickBox)
|
||||
{
|
||||
disableHintsTickBox.OnSelected = HintManager.OnDisableHints;
|
||||
disableHintsTickBox.UserData = hintIdentifier;
|
||||
}
|
||||
}
|
||||
|
||||
private static Type[] messageBoxTypes;
|
||||
|
||||
public static void AddActiveToGUIUpdateList()
|
||||
{
|
||||
for (int i = 0; i < MessageBoxes.Count; i++)
|
||||
messageBoxTypes ??= (Type[])Enum.GetValues(typeof(Type));
|
||||
|
||||
foreach (var type in messageBoxTypes)
|
||||
{
|
||||
if (MessageBoxes[i] is GUIMessageBox alwaysVisibleMsgBox && alwaysVisibleMsgBox.alwaysVisible)
|
||||
// Don't display hints when HUD is disabled
|
||||
if (type == Type.Hint && GUI.DisableHUD) { continue; }
|
||||
|
||||
for (int i = 0; i < MessageBoxes.Count; i++)
|
||||
{
|
||||
alwaysVisibleMsgBox.AddToGUIUpdateList();
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = MessageBoxes.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (MessageBoxes[i].UserData as string == "verificationprompt" ||
|
||||
MessageBoxes[i].UserData as string == "bugreporter")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!(MessageBoxes[i] is GUIMessageBox msgBox) || !msgBox.alwaysVisible)
|
||||
{
|
||||
MessageBoxes[i].AddToGUIUpdateList();
|
||||
if (MessageBoxes[i] == null) { continue; }
|
||||
if (!(MessageBoxes[i] is GUIMessageBox messageBox))
|
||||
{
|
||||
if (type == Type.Default)
|
||||
{
|
||||
// Message box not of type GUIMessageBox is likely the round summary
|
||||
MessageBoxes[i].AddToGUIUpdateList();
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (messageBox.type != type) { continue; }
|
||||
|
||||
// These are handled separately in GUI.HandlePersistingElements()
|
||||
if (MessageBoxes[i].UserData as string == "verificationprompt") { continue; }
|
||||
if (MessageBoxes[i].UserData as string == "bugreporter") { continue; }
|
||||
|
||||
messageBox.AddToGUIUpdateList();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -337,11 +497,21 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (type == Type.InGame)
|
||||
if (type == Type.InGame || type == Type.Hint)
|
||||
{
|
||||
Vector2 initialPos = new Vector2(0.0f, GameMain.GraphicsHeight);
|
||||
Vector2 defaultPos = new Vector2(0.0f, HUDLayoutSettings.InventoryAreaLower.Y - InnerFrame.Rect.Height - 20 * GUI.Scale);
|
||||
Vector2 endPos = new Vector2(GameMain.GraphicsWidth, defaultPos.Y);
|
||||
Vector2 initialPos, defaultPos, endPos;
|
||||
if (type == Type.InGame)
|
||||
{
|
||||
initialPos = new Vector2(0.0f, GameMain.GraphicsHeight);
|
||||
defaultPos = new Vector2(0.0f, HUDLayoutSettings.InventoryAreaLower.Y - InnerFrame.Rect.Height - 20 * GUI.Scale);
|
||||
endPos = new Vector2(GameMain.GraphicsWidth, defaultPos.Y);
|
||||
}
|
||||
else
|
||||
{
|
||||
initialPos = new Vector2(GUI.IntScale(64), -InnerFrame.Rect.Height);
|
||||
defaultPos = new Vector2(initialPos.X, HUDLayoutSettings.ButtonAreaTop.Height + GUI.IntScale(64));
|
||||
endPos = new Vector2(-InnerFrame.Rect.Width, defaultPos.Y);
|
||||
}
|
||||
|
||||
if (!closing)
|
||||
{
|
||||
@@ -428,7 +598,7 @@ namespace Barotrauma
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (type == Type.InGame)
|
||||
if (type == Type.InGame || type == Type.Hint)
|
||||
{
|
||||
closing = true;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,11 @@ namespace Barotrauma
|
||||
|
||||
public readonly Sprite[] CursorSprite = new Sprite[7];
|
||||
|
||||
public UISprite RadiationSprite { get; private set; }
|
||||
public SpriteSheet RadiationAnimSpriteSheet { get; private set; }
|
||||
|
||||
public SpriteSheet SavingIndicator { get; private set; }
|
||||
|
||||
public UISprite UIGlow { get; private set; }
|
||||
public UISprite UIGlowCircular { get; private set; }
|
||||
|
||||
@@ -77,6 +82,12 @@ namespace Barotrauma
|
||||
public Color TextColorDark { get; private set; } = Color.Black * 0.9f;
|
||||
public Color TextColorDim { get; private set; } = Color.White * 0.6f;
|
||||
|
||||
public Color ColorReputationVeryLow { get; private set; } = Color.Red;
|
||||
public Color ColorReputationLow { get; private set; } = Color.Orange;
|
||||
public Color ColorReputationNeutral { get; private set; } = Color.White * 0.8f;
|
||||
public Color ColorReputationHigh { get; private set; } = Color.LightBlue;
|
||||
public Color ColorReputationVeryHigh { get; private set; } = Color.Blue;
|
||||
|
||||
// Inventory
|
||||
public Color EquipmentSlotIconColor { get; private set; } = new Color(99, 70, 64);
|
||||
|
||||
@@ -167,6 +178,21 @@ namespace Barotrauma
|
||||
case "textcolor":
|
||||
TextColor = subElement.GetAttributeColor("color", TextColor);
|
||||
break;
|
||||
case "colorreputationverylow":
|
||||
ColorReputationVeryLow = subElement.GetAttributeColor("color", TextColor);
|
||||
break;
|
||||
case "colorreputationlow":
|
||||
ColorReputationLow = subElement.GetAttributeColor("color", TextColor);
|
||||
break;
|
||||
case "colorreputationneutral":
|
||||
ColorReputationNeutral = subElement.GetAttributeColor("color", TextColor);
|
||||
break;
|
||||
case "colorreputationhigh":
|
||||
ColorReputationHigh = subElement.GetAttributeColor("color", TextColor);
|
||||
break;
|
||||
case "colorreputationveryhigh":
|
||||
ColorReputationVeryHigh = subElement.GetAttributeColor("color", TextColor);
|
||||
break;
|
||||
case "equipmentsloticoncolor":
|
||||
EquipmentSlotIconColor = subElement.GetAttributeColor("color", EquipmentSlotIconColor);
|
||||
break;
|
||||
@@ -209,6 +235,12 @@ namespace Barotrauma
|
||||
case "uiglow":
|
||||
UIGlow = new UISprite(subElement);
|
||||
break;
|
||||
case "radiation":
|
||||
RadiationSprite = new UISprite(subElement);
|
||||
break;
|
||||
case "radiationanimspritesheet":
|
||||
RadiationAnimSpriteSheet = new SpriteSheet(subElement);
|
||||
break;
|
||||
case "uiglowcircular":
|
||||
UIGlowCircular = new UISprite(subElement);
|
||||
break;
|
||||
@@ -218,6 +250,9 @@ namespace Barotrauma
|
||||
case "focusindicator":
|
||||
FocusIndicator = new SpriteSheet(subElement);
|
||||
break;
|
||||
case "savingindicator":
|
||||
SavingIndicator = new SpriteSheet(subElement);
|
||||
break;
|
||||
case "font":
|
||||
Font = LoadFont(subElement, graphicsDevice);
|
||||
ForceFontUpperCase[Font] = subElement.GetAttributeBool("forceuppercase", false);
|
||||
|
||||
@@ -259,9 +259,11 @@ namespace Barotrauma
|
||||
|
||||
public StrikethroughSettings Strikethrough = null;
|
||||
|
||||
private readonly List<RichTextData> richTextData = null;
|
||||
public readonly List<RichTextData> RichTextData = null;
|
||||
|
||||
private readonly bool hasColorHighlight = false;
|
||||
public bool HasColorHighlight => RichTextData != null;
|
||||
|
||||
public bool OverrideRichTextDataAlpha = true;
|
||||
|
||||
public struct ClickableArea
|
||||
{
|
||||
@@ -279,7 +281,8 @@ namespace Barotrauma
|
||||
/// If the rectT height is set 0, the height is calculated from the text.
|
||||
/// </summary>
|
||||
public GUITextBlock(RectTransform rectT, string text, Color? textColor = null, ScalableFont font = null,
|
||||
Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null, bool playerInput = false)
|
||||
Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null,
|
||||
bool playerInput = false, bool parseRichText = false)
|
||||
: base(style, rectT)
|
||||
{
|
||||
if (color.HasValue)
|
||||
@@ -289,7 +292,12 @@ namespace Barotrauma
|
||||
if (textColor.HasValue)
|
||||
{
|
||||
OverrideTextColor(textColor.Value);
|
||||
}
|
||||
}
|
||||
|
||||
if (parseRichText)
|
||||
{
|
||||
RichTextData = Barotrauma.RichTextData.GetRichTextData(text, out text);
|
||||
}
|
||||
|
||||
//if the text is in chinese/korean/japanese and we're not using a CJK-compatible font,
|
||||
//use the default CJK font as a fallback
|
||||
@@ -318,8 +326,7 @@ namespace Barotrauma
|
||||
public GUITextBlock(RectTransform rectT, List<RichTextData> richTextData, string text, Color? textColor = null, ScalableFont font = null, Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null, bool playerInput = false)
|
||||
: this(rectT, text, textColor, font, textAlignment, wrap, style, color, playerInput)
|
||||
{
|
||||
this.richTextData = richTextData;
|
||||
hasColorHighlight = richTextData != null;
|
||||
this.RichTextData = richTextData;
|
||||
}
|
||||
|
||||
public void CalculateHeightFromText(int padding = 0, bool removeExtraSpacing = false)
|
||||
@@ -568,8 +575,9 @@ namespace Barotrauma
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
|
||||
if (ClickableAreas.Any() && (GUI.MouseOn?.IsParentOf(this) ?? true) && Rect.Contains(PlayerInput.MousePosition))
|
||||
if (ClickableAreas.Any() && (GUI.MouseOn?.IsParentOf(this) ?? true))
|
||||
{
|
||||
if (!Rect.Contains(PlayerInput.MousePosition)) { return; }
|
||||
int index = GetCaretIndexFromScreenPos(PlayerInput.MousePosition);
|
||||
foreach (ClickableArea clickableArea in ClickableAreas)
|
||||
{
|
||||
@@ -627,7 +635,7 @@ namespace Barotrauma
|
||||
currentTextColor = selectedTextColor;
|
||||
}
|
||||
|
||||
if (!hasColorHighlight)
|
||||
if (!HasColorHighlight)
|
||||
{
|
||||
string textToShow = Censor ? censoredText : (Wrap ? wrappedText : text);
|
||||
Color colorToShow = currentTextColor * (currentTextColor.A / 255.0f);
|
||||
@@ -639,12 +647,15 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
Font.DrawString(spriteBatch, textToShow, pos, colorToShow, 0.0f, origin, TextScale, SpriteEffects.None, textDepth);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (OverrideRichTextDataAlpha)
|
||||
{
|
||||
RichTextData.ForEach(rt => rt.Alpha = currentTextColor.A / 255.0f);
|
||||
}
|
||||
Font.DrawStringWithColors(spriteBatch, Censor ? censoredText : (Wrap ? wrappedText : text), pos,
|
||||
currentTextColor * (currentTextColor.A / 255.0f), 0.0f, origin, TextScale, SpriteEffects.None, textDepth, richTextData);
|
||||
currentTextColor * (currentTextColor.A / 255.0f), 0.0f, origin, TextScale, SpriteEffects.None, textDepth, RichTextData);
|
||||
}
|
||||
|
||||
if (Strikethrough != null)
|
||||
|
||||
@@ -626,6 +626,9 @@ namespace Barotrauma
|
||||
{
|
||||
if (Text == null) Text = "";
|
||||
|
||||
// Prevent alt gr from triggering any of these as that combination is often needed for special characters
|
||||
if (PlayerInput.IsAltDown()) return;
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case '\b' when !Readonly: //backspace
|
||||
@@ -667,7 +670,10 @@ namespace Barotrauma
|
||||
}
|
||||
break;
|
||||
case (char)0x1: // ctrl-a
|
||||
SelectAll();
|
||||
if (PlayerInput.IsCtrlDown())
|
||||
{
|
||||
SelectAll();
|
||||
}
|
||||
break;
|
||||
case (char)0x1A when !Readonly && !SubEditorScreen.IsSubEditor(): // ctrl-z
|
||||
text = memento.Undo();
|
||||
|
||||
@@ -128,7 +128,7 @@ namespace Barotrauma
|
||||
|
||||
|
||||
int messageAreaWidth = GameMain.GraphicsWidth / 3;
|
||||
MessageAreaTop = new Rectangle((GameMain.GraphicsWidth - messageAreaWidth) / 2, ButtonAreaTop.Bottom, messageAreaWidth, ButtonAreaTop.Height);
|
||||
MessageAreaTop = new Rectangle((GameMain.GraphicsWidth - messageAreaWidth) / 2, ButtonAreaTop.Bottom + ButtonAreaTop.Height, messageAreaWidth, ButtonAreaTop.Height);
|
||||
|
||||
bool isFourByThree = GUI.IsFourByThree();
|
||||
int chatBoxWidth = !isFourByThree ? (int)(475 * GUI.Scale) : (int)(375 * GUI.Scale);
|
||||
@@ -139,7 +139,9 @@ namespace Barotrauma
|
||||
int objectiveAnchorOffsetY = (int)(150 * GUI.Scale);
|
||||
ObjectiveAnchor = new Rectangle(Padding, ChatBoxArea.Y - objectiveAnchorOffsetY, objectiveAnchorWidth, 0);
|
||||
|
||||
CrewArea = new Rectangle(Padding, Padding, (int)Math.Max(400 * GUI.Scale, 220), ObjectiveAnchor.Top - Padding * 2);
|
||||
int crewAreaY = ButtonAreaTop.Bottom + Padding;
|
||||
int crewAreaHeight = ObjectiveAnchor.Top - Padding - crewAreaY;
|
||||
CrewArea = new Rectangle(Padding, crewAreaY, (int)Math.Max(400 * GUI.Scale, 220), crewAreaHeight);
|
||||
|
||||
InventoryAreaLower = new Rectangle(ChatBoxArea.Right + Padding * 7, inventoryTopY, GameMain.GraphicsWidth - Padding * 9 - ChatBoxArea.Width, GameMain.GraphicsHeight - inventoryTopY);
|
||||
|
||||
|
||||
@@ -70,6 +70,14 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
private string selectedTip;
|
||||
private List<RichTextData> selectedTipRichTextData;
|
||||
private bool selectedTipRichTextUnparsed;
|
||||
private void SetSelectedTip(string tip)
|
||||
{
|
||||
selectedTip = tip;
|
||||
selectedTipRichTextData = null;
|
||||
selectedTipRichTextUnparsed = true;
|
||||
}
|
||||
|
||||
private readonly object loadMutex = new object();
|
||||
private float? loadState;
|
||||
@@ -115,7 +123,7 @@ namespace Barotrauma
|
||||
overlay = TextureLoader.FromFile("Content/UI/LoadingScreenOverlay.png");
|
||||
noiseSprite = new Sprite("Content/UI/noise.png", Vector2.Zero);
|
||||
DrawLoadingText = true;
|
||||
selectedTip = TextManager.Get("LoadingScreenTip", true);
|
||||
SetSelectedTip(TextManager.Get("LoadingScreenTip", true));
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, GraphicsDevice graphics, float deltaTime)
|
||||
@@ -215,14 +223,34 @@ namespace Barotrauma
|
||||
|
||||
if (GUI.Font != null && selectedTip != null)
|
||||
{
|
||||
if (selectedTipRichTextUnparsed)
|
||||
{
|
||||
selectedTipRichTextData = RichTextData.GetRichTextData(selectedTip, out selectedTip);
|
||||
selectedTipRichTextUnparsed = false;
|
||||
}
|
||||
|
||||
string wrappedTip = ToolBox.WrapText(selectedTip, GameMain.GraphicsWidth * 0.5f, GUI.Font);
|
||||
string[] lines = wrappedTip.Split('\n');
|
||||
float lineHeight = GUI.Font.MeasureString(selectedTip).Y;
|
||||
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
if (selectedTipRichTextData != null)
|
||||
{
|
||||
GUI.Font.DrawString(spriteBatch, lines[i],
|
||||
new Vector2((int)(GameMain.GraphicsWidth / 2.0f - GUI.Font.MeasureString(lines[i]).X / 2.0f), (int)(GameMain.GraphicsHeight * 0.8f + i * lineHeight)), Color.White);
|
||||
int rtdOffset = 0;
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
GUI.Font.DrawStringWithColors(spriteBatch, lines[i],
|
||||
new Vector2((int)(GameMain.GraphicsWidth / 2.0f - GUI.Font.MeasureString(lines[i]).X / 2.0f), (int)(GameMain.GraphicsHeight * 0.8f + i * lineHeight)), Color.White,
|
||||
0f, Vector2.Zero, 1f, SpriteEffects.None, 0f, selectedTipRichTextData, rtdOffset);
|
||||
rtdOffset += lines[i].Length;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
GUI.Font.DrawString(spriteBatch, lines[i],
|
||||
new Vector2((int)(GameMain.GraphicsWidth / 2.0f - GUI.Font.MeasureString(lines[i]).X / 2.0f), (int)(GameMain.GraphicsHeight * 0.8f + i * lineHeight)), Color.White);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,7 +330,7 @@ namespace Barotrauma
|
||||
{
|
||||
GameMain.Config.Language = language;
|
||||
//reload tip in the selected language
|
||||
selectedTip = TextManager.Get("LoadingScreenTip", true);
|
||||
SetSelectedTip(TextManager.Get("LoadingScreenTip", true));
|
||||
GameMain.Config.SetDefaultBindings(legacy: false);
|
||||
GameMain.Config.CheckBindings(useDefaults: true);
|
||||
WaitForLanguageSelection = false;
|
||||
@@ -364,7 +392,7 @@ namespace Barotrauma
|
||||
{
|
||||
drawn = false;
|
||||
LoadState = null;
|
||||
selectedTip = TextManager.Get("LoadingScreenTip", true);
|
||||
SetSelectedTip(TextManager.Get("LoadingScreenTip", true));
|
||||
currentBackgroundTexture = LocationType.List.GetRandom()?.GetPortrait(Rand.Int(int.MaxValue))?.Texture;
|
||||
|
||||
while (!drawn)
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private static Point maxPoint = new Point(int.MaxValue, int.MaxValue);
|
||||
public readonly static Point MaxPoint = new Point(int.MaxValue, int.MaxValue);
|
||||
private Point? maxSize;
|
||||
|
||||
/// <summary>
|
||||
@@ -103,7 +103,7 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public Point MaxSize
|
||||
{
|
||||
get { return maxSize ?? maxPoint; }
|
||||
get { return maxSize ?? MaxPoint; }
|
||||
set
|
||||
{
|
||||
if (maxSize == value) { return; }
|
||||
@@ -640,6 +640,12 @@ namespace Barotrauma
|
||||
return children.Contains(rectT) || (recursive && children.Any(c => c.IsParentOf(rectT)));
|
||||
}
|
||||
|
||||
public bool IsChildOf(RectTransform rectT, bool recursive = true)
|
||||
{
|
||||
if (Parent == null) { return false; }
|
||||
return Parent == rectT || (recursive && Parent.IsChildOf(rectT));
|
||||
}
|
||||
|
||||
public void ClearChildren()
|
||||
{
|
||||
children.ForEachMod(c => c.Parent = null);
|
||||
|
||||
@@ -24,7 +24,6 @@ namespace Barotrauma
|
||||
private int buyTotal, sellTotal;
|
||||
|
||||
private GUITextBlock merchantBalanceBlock;
|
||||
private GUILayoutGroup valueChangeGroup;
|
||||
private GUITextBlock currentSellValueBlock, newSellValueBlock;
|
||||
private GUIImage sellValueChangeArrow;
|
||||
private GUIDropDown sortingDropDown;
|
||||
@@ -158,7 +157,7 @@ namespace Barotrauma
|
||||
};
|
||||
|
||||
// Store header ------------------------------------------------
|
||||
var headerGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.75f / 14.0f), storeContent.RectTransform), isHorizontal: true)
|
||||
var headerGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.95f / 14.0f), storeContent.RectTransform), isHorizontal: true)
|
||||
{
|
||||
RelativeSpacing = 0.005f
|
||||
};
|
||||
@@ -209,7 +208,7 @@ namespace Barotrauma
|
||||
// Item sell value ------------------------------------------------
|
||||
var sellValueContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), balanceAndValueGroup.RectTransform))
|
||||
{
|
||||
CanBeFocused = false,
|
||||
CanBeFocused = true,
|
||||
RelativeSpacing = 0.005f
|
||||
};
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), sellValueContainer.RectTransform),
|
||||
@@ -220,9 +219,9 @@ namespace Barotrauma
|
||||
ForceUpperCase = true
|
||||
};
|
||||
|
||||
valueChangeGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), sellValueContainer.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
var valueChangeGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), sellValueContainer.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
CanBeFocused = true,
|
||||
CanBeFocused = false,
|
||||
RelativeSpacing = 0.02f
|
||||
};
|
||||
float blockWidth = GUI.IsFourByThree() ? 0.32f : 0.28f;
|
||||
@@ -247,7 +246,7 @@ namespace Barotrauma
|
||||
{
|
||||
string tooltipTag = newStatus.SellPriceModifier > CurrentLocation.ActiveStoreBalanceStatus.SellPriceModifier ?
|
||||
"campaingstore.valueincreasetooltip" : "campaingstore.valuedecreasetooltip";
|
||||
valueChangeGroup.ToolTip = TextManager.Get(tooltipTag);
|
||||
sellValueContainer.ToolTip = TextManager.Get(tooltipTag);
|
||||
currentSellValueBlock.TextColor = newStatus.Color;
|
||||
sellValueChangeArrow.Color = newStatus.Color;
|
||||
sellValueChangeArrow.Visible = true;
|
||||
@@ -256,7 +255,7 @@ namespace Barotrauma
|
||||
return $"{(CurrentLocation.ActiveStoreBalanceStatus.SellPriceModifier * 100).FormatZeroDecimal()} %";
|
||||
}
|
||||
}
|
||||
valueChangeGroup.ToolTip = null;
|
||||
sellValueContainer.ToolTip = TextManager.Get("campaignstore.sellvaluetooltip");
|
||||
currentSellValueBlock.TextColor = CurrentLocation.BalanceColor;
|
||||
sellValueChangeArrow.Visible = false;
|
||||
newSellValueBlock.Text = null;
|
||||
@@ -264,7 +263,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
valueChangeGroup.ToolTip = null;
|
||||
sellValueContainer.ToolTip = null;
|
||||
sellValueChangeArrow.Visible = false;
|
||||
newSellValueBlock.Text = null;
|
||||
return null;
|
||||
@@ -293,7 +292,7 @@ namespace Barotrauma
|
||||
newSellValueBlock.Padding = newPadding;
|
||||
|
||||
// Store mode buttons ------------------------------------------------
|
||||
var modeButtonFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.6f / 14.0f), storeContent.RectTransform), style: null);
|
||||
var modeButtonFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.4f / 14.0f), storeContent.RectTransform), style: null);
|
||||
var modeButtonContainer = new GUILayoutGroup(new RectTransform(Vector2.One, modeButtonFrame.RectTransform), isHorizontal: true);
|
||||
|
||||
var tabs = Enum.GetValues(typeof(StoreTab));
|
||||
@@ -676,6 +675,7 @@ namespace Barotrauma
|
||||
(itemFrame.UserData as PurchasedItem).Quantity = quantity;
|
||||
SetQuantityLabelText(StoreTab.Buy, itemFrame);
|
||||
SetOwnedLabelText(itemFrame);
|
||||
SetPriceGetters(itemFrame, true);
|
||||
}
|
||||
SetItemFrameStatus(itemFrame, hasPermissions && quantity > 0);
|
||||
existingItemFrames.Add(itemFrame);
|
||||
@@ -750,6 +750,7 @@ namespace Barotrauma
|
||||
(itemFrame.UserData as PurchasedItem).Quantity = itemQuantity;
|
||||
SetQuantityLabelText(StoreTab.Sell, itemFrame);
|
||||
SetOwnedLabelText(itemFrame);
|
||||
SetPriceGetters(itemFrame, false);
|
||||
}
|
||||
SetItemFrameStatus(itemFrame, hasPermissions && itemQuantity > 0);
|
||||
if (itemQuantity < 1 && !isRequestedGood)
|
||||
@@ -772,6 +773,37 @@ namespace Barotrauma
|
||||
shoppingCrateSellList.BarScroll = prevShoppingCrateScroll;
|
||||
}
|
||||
|
||||
private void SetPriceGetters(GUIComponent itemFrame, bool buying)
|
||||
{
|
||||
if (itemFrame == null || !(itemFrame.UserData is PurchasedItem pi)) { return; }
|
||||
|
||||
if (itemFrame.FindChild("undiscountedprice", recursive: true) is GUITextBlock undiscountedPriceBlock)
|
||||
{
|
||||
if (buying)
|
||||
{
|
||||
undiscountedPriceBlock.TextGetter = () => GetCurrencyFormatted(
|
||||
CurrentLocation?.GetAdjustedItemBuyPrice(pi.ItemPrefab, considerDailySpecials: false) ?? 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
undiscountedPriceBlock.TextGetter = () => GetCurrencyFormatted(
|
||||
CurrentLocation?.GetAdjustedItemSellPrice(pi.ItemPrefab, considerRequestedGoods: false) ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (itemFrame.FindChild("price", recursive: true) is GUITextBlock priceBlock)
|
||||
{
|
||||
if (buying)
|
||||
{
|
||||
priceBlock.TextGetter = () => GetCurrencyFormatted(CurrentLocation?.GetAdjustedItemBuyPrice(pi.ItemPrefab) ?? 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
priceBlock.TextGetter = () => GetCurrencyFormatted(CurrentLocation?.GetAdjustedItemSellPrice(pi.ItemPrefab) ?? 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RefreshItemsToSell()
|
||||
{
|
||||
itemsToSell.Clear();
|
||||
@@ -1188,14 +1220,6 @@ namespace Barotrauma
|
||||
};
|
||||
priceBlock.Color *= (forceDisable ? 0.5f : 1.0f);
|
||||
priceBlock.CalculateHeightFromText();
|
||||
if (isSellingRelatedList)
|
||||
{
|
||||
priceBlock.TextGetter = () => GetCurrencyFormatted(CurrentLocation?.GetAdjustedItemSellPrice(pi.ItemPrefab, priceInfo: priceInfo) ?? 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
priceBlock.TextGetter = () => GetCurrencyFormatted(CurrentLocation?.GetAdjustedItemBuyPrice(pi.ItemPrefab, priceInfo: priceInfo) ?? 0);
|
||||
}
|
||||
if (locationHasDealOnItem)
|
||||
{
|
||||
var undiscounterPriceBlock = new GUITextBlock(
|
||||
@@ -1209,17 +1233,8 @@ namespace Barotrauma
|
||||
TextColor = priceBlock.TextColor,
|
||||
UserData = "undiscountedprice"
|
||||
};
|
||||
if (isSellingRelatedList)
|
||||
{
|
||||
undiscounterPriceBlock.TextGetter = () => GetCurrencyFormatted(
|
||||
CurrentLocation?.GetAdjustedItemSellPrice(pi.ItemPrefab, priceInfo: priceInfo, considerRequestedGoods: false) ?? 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
undiscounterPriceBlock.TextGetter = () => GetCurrencyFormatted(
|
||||
CurrentLocation?.GetAdjustedItemBuyPrice(pi.ItemPrefab, priceInfo: priceInfo, considerDailySpecials: false) ?? 0);
|
||||
}
|
||||
}
|
||||
SetPriceGetters(frame, !isSellingRelatedList);
|
||||
|
||||
if (isParentOnLeftSideOfInterface)
|
||||
{
|
||||
@@ -1268,7 +1283,8 @@ namespace Barotrauma
|
||||
// Add items on the sub(s)
|
||||
Submarine.MainSub?.GetItems(true)
|
||||
.Where(i => i.Components.All(c => !(c is Holdable h) || !h.Attachable || !h.Attached) &&
|
||||
i.Components.All(c => !(c is Wire w) || w.Connections.All(c => c == null)))
|
||||
i.Components.All(c => !(c is Wire w) || w.Connections.All(c => c == null)) &&
|
||||
ItemAndAllContainersInteractable(i))
|
||||
.ForEach(i => AddToOwnedItems(i.Prefab));
|
||||
|
||||
// Add items in character inventories
|
||||
@@ -1286,6 +1302,16 @@ namespace Barotrauma
|
||||
|
||||
ownedItemsUpdateTimer = 0.0f;
|
||||
|
||||
static bool ItemAndAllContainersInteractable(Item item)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (!item.IsPlayerTeamInteractable) { return false; }
|
||||
item = item.Container;
|
||||
} while (item != null);
|
||||
return true;
|
||||
}
|
||||
|
||||
void AddToOwnedItems(ItemPrefab itemPrefab, int amount = 1)
|
||||
{
|
||||
if (OwnedItems.ContainsKey(itemPrefab))
|
||||
|
||||
@@ -60,6 +60,7 @@ namespace Barotrauma
|
||||
public GUITextBlock submarineFee;
|
||||
public GUIButton selectSubmarineButton;
|
||||
public GUITextBlock middleTextBlock;
|
||||
public GUIButton previewButton;
|
||||
}
|
||||
|
||||
public SubmarineSelection(bool transfer, Action closeAction, RectTransform parent)
|
||||
@@ -191,6 +192,12 @@ namespace Barotrauma
|
||||
submarineDisplayElement.submarineClass = new GUITextBlock(new RectTransform(new Vector2(1f, 0.1f), submarineDisplayElement.background.RectTransform, Anchor.TopCenter, Pivot.TopCenter) { AbsoluteOffset = new Point(0, HUDLayoutSettings.Padding + (int)GUI.Font.MeasureString(submarineDisplayElement.submarineName.Text).Y) }, string.Empty, textAlignment: Alignment.Center);
|
||||
submarineDisplayElement.submarineFee = new GUITextBlock(new RectTransform(new Vector2(1f, 0.1f), submarineDisplayElement.background.RectTransform, Anchor.BottomCenter, Pivot.BottomCenter) { AbsoluteOffset = new Point(0, HUDLayoutSettings.Padding) }, string.Empty, textAlignment: Alignment.Center, font: GUI.SubHeadingFont);
|
||||
submarineDisplayElement.selectSubmarineButton = new GUIButton(new RectTransform(Vector2.One, submarineDisplayElement.background.RectTransform), style: null);
|
||||
submarineDisplayElement.previewButton = new GUIButton(new RectTransform(Vector2.One * 0.12f, submarineDisplayElement.background.RectTransform, anchor: Anchor.BottomRight, pivot: Pivot.BottomRight, scaleBasis: ScaleBasis.BothHeight) { AbsoluteOffset = new Point((int)(0.03f * background.Rect.Height)) }, style: "ExpandButton")
|
||||
{
|
||||
Color = Color.White,
|
||||
HoverColor = Color.White,
|
||||
PressedColor = Color.White
|
||||
};
|
||||
submarineDisplays[i] = submarineDisplayElement;
|
||||
}
|
||||
|
||||
@@ -299,6 +306,7 @@ namespace Barotrauma
|
||||
submarineDisplays[i].selectSubmarineButton.OnClicked = null;
|
||||
submarineDisplays[i].displayedSubmarine = null;
|
||||
submarineDisplays[i].middleTextBlock.AutoDraw = false;
|
||||
submarineDisplays[i].previewButton.Visible = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -369,6 +377,13 @@ namespace Barotrauma
|
||||
{
|
||||
SelectSubmarine(subToDisplay, submarineDisplays[i].background.Rect);
|
||||
}
|
||||
|
||||
submarineDisplays[i].previewButton.Visible = true;
|
||||
submarineDisplays[i].previewButton.OnClicked = (btn, obj) =>
|
||||
{
|
||||
SubmarinePreview.Create(subToDisplay);
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
submarineIndex++;
|
||||
|
||||
@@ -5,6 +5,7 @@ using Microsoft.Xna.Framework.Graphics;
|
||||
using System.Linq;
|
||||
using Barotrauma.Networking;
|
||||
using System.Globalization;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -17,7 +18,7 @@ namespace Barotrauma
|
||||
private static UISprite spectateIcon, disconnectedIcon;
|
||||
private static Sprite ownerIcon, moderatorIcon;
|
||||
|
||||
private enum InfoFrameTab { Crew, Mission, MyCharacter, Traitor };
|
||||
private enum InfoFrameTab { Crew, Mission, Reputation, MyCharacter, Traitor, Submarine };
|
||||
private static InfoFrameTab selectedTab;
|
||||
private GUIFrame infoFrame, contentFrame;
|
||||
|
||||
@@ -40,11 +41,11 @@ namespace Barotrauma
|
||||
private const ushort mediumPingThreshold = 200;
|
||||
|
||||
private ushort currentPing;
|
||||
private Client client;
|
||||
private Character character;
|
||||
private bool hasCharacter;
|
||||
private GUITextBlock textBlock;
|
||||
private GUIFrame frame;
|
||||
private readonly Client client;
|
||||
private readonly Character character;
|
||||
private readonly bool hasCharacter;
|
||||
private readonly GUITextBlock textBlock;
|
||||
private readonly GUIFrame frame;
|
||||
|
||||
public LinkedGUI(Client client, GUIFrame frame, bool hasCharacter, GUITextBlock textBlock)
|
||||
{
|
||||
@@ -180,60 +181,79 @@ namespace Barotrauma
|
||||
infoFrame = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), style: null);
|
||||
new GUIFrame(new RectTransform(GUI.Canvas.RelativeSize, infoFrame.RectTransform, Anchor.Center), style: "GUIBackgroundBlocker");
|
||||
|
||||
//this used to be a switch expression but i changed it because it killed enc :(
|
||||
Vector2 contentFrameSize;
|
||||
switch (selectedTab)
|
||||
{
|
||||
case InfoFrameTab.Crew:
|
||||
case InfoFrameTab.Mission:
|
||||
case InfoFrameTab.Traitor:
|
||||
default:
|
||||
contentFrame = new GUIFrame(new RectTransform(new Vector2(0.33f, 0.667f), infoFrame.RectTransform, Anchor.TopCenter, Pivot.TopCenter) { /*MinSize = new Point(width, height),*/ RelativeOffset = new Vector2(0.025f, 0.12f) });
|
||||
break;
|
||||
case InfoFrameTab.MyCharacter:
|
||||
contentFrame = new GUIFrame(new RectTransform(new Vector2(0.33f, 0.5f), infoFrame.RectTransform, Anchor.TopCenter, Pivot.TopCenter) { /*MinSize = new Point(width, height),*/ RelativeOffset = new Vector2(0.025f, 0.12f) });
|
||||
contentFrameSize = new Vector2(0.45f, 0.5f);
|
||||
break;
|
||||
default:
|
||||
contentFrameSize = new Vector2(0.45f, 0.667f);
|
||||
break;
|
||||
}
|
||||
contentFrame = new GUIFrame(new RectTransform(contentFrameSize, infoFrame.RectTransform, Anchor.TopCenter, Pivot.TopCenter) { RelativeOffset = new Vector2(0.0f, 0.12f) });
|
||||
|
||||
var innerFrame = new GUIFrame(new RectTransform(new Vector2(0.958f, 0.943f), contentFrame.RectTransform, Anchor.TopCenter, Pivot.TopCenter) { AbsoluteOffset = new Point(0, GUI.IntScale(17.5f)) }, style: null);
|
||||
var buttonArea = new GUILayoutGroup(new RectTransform(new Point(innerFrame.Rect.Width, GUI.IntScale(25f)), innerFrame.RectTransform) { AbsoluteOffset = new Point(2, 0) }, isHorizontal: true)
|
||||
var horizontalLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.958f, 0.943f), contentFrame.RectTransform, Anchor.TopCenter, Pivot.TopCenter) { AbsoluteOffset = new Point(0, GUI.IntScale(25f)) }, isHorizontal: true)
|
||||
{
|
||||
RelativeSpacing = 0.01f
|
||||
};
|
||||
|
||||
infoFrameHolder = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.926f), innerFrame.RectTransform, Anchor.BottomCenter, Pivot.BottomCenter), style: null);
|
||||
|
||||
var crewButton = new GUIButton(new RectTransform(new Vector2(0.245f, 1.0f), buttonArea.RectTransform), TextManager.Get("Crew"), style: "GUITabButton")
|
||||
var buttonArea = new GUILayoutGroup(new RectTransform(new Vector2(0.07f, 1f), parent: horizontalLayoutGroup.RectTransform), isHorizontal: false)
|
||||
{
|
||||
UserData = InfoFrameTab.Crew,
|
||||
OnClicked = SelectInfoFrameTab
|
||||
AbsoluteSpacing = GUI.IntScale(5f)
|
||||
};
|
||||
tabButtons.Add(crewButton);
|
||||
|
||||
var missionButton = new GUIButton(new RectTransform(new Vector2(0.245f, 1.0f), buttonArea.RectTransform), TextManager.Get("Mission"), style: "GUITabButton")
|
||||
var innerLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.92f, 1f), horizontalLayoutGroup.RectTransform))
|
||||
{
|
||||
UserData = InfoFrameTab.Mission,
|
||||
OnClicked = SelectInfoFrameTab
|
||||
RelativeSpacing = 0.01f,
|
||||
Stretch = true
|
||||
};
|
||||
tabButtons.Add(missionButton);
|
||||
|
||||
bool isTraitor = GameMain.Client?.Character?.IsTraitor ?? false;
|
||||
if (isTraitor && GameMain.Client.TraitorMission != null)
|
||||
float absoluteSpacing = innerLayoutGroup.RelativeSpacing * innerLayoutGroup.Rect.Height;
|
||||
int multiplier = GameMain.GameSession?.GameMode is CampaignMode ? 2 : 1;
|
||||
int infoFrameHolderHeight = Math.Min((int)(0.97f * innerLayoutGroup.Rect.Height), (int)(innerLayoutGroup.Rect.Height - multiplier * (GUI.IntScale(15f) + absoluteSpacing)));
|
||||
infoFrameHolder = new GUIFrame(new RectTransform(new Point(innerLayoutGroup.Rect.Width, infoFrameHolderHeight), parent: innerLayoutGroup.RectTransform), style: null);
|
||||
|
||||
GUIButton createTabButton(InfoFrameTab tab, string textTag)
|
||||
{
|
||||
var traitorButton = new GUIButton(new RectTransform(new Vector2(0.245f, 1.0f), buttonArea.RectTransform), TextManager.Get("tabmenu.traitor"), style: "GUITabButton")
|
||||
var newButton = new GUIButton(new RectTransform(Vector2.One, buttonArea.RectTransform, scaleBasis: ScaleBasis.BothWidth), style: $"InfoFrameTabButton.{tab}")
|
||||
{
|
||||
UserData = InfoFrameTab.Traitor,
|
||||
UserData = tab,
|
||||
ToolTip = TextManager.Get(textTag),
|
||||
OnClicked = SelectInfoFrameTab
|
||||
};
|
||||
tabButtons.Add(traitorButton);
|
||||
tabButtons.Add(newButton);
|
||||
return newButton;
|
||||
}
|
||||
|
||||
var crewButton = createTabButton(InfoFrameTab.Crew, "crew");
|
||||
|
||||
var missionButton = createTabButton(InfoFrameTab.Mission, "mission");
|
||||
|
||||
if (GameMain.GameSession?.GameMode is CampaignMode campaignMode)
|
||||
{
|
||||
var reputationButton = createTabButton(InfoFrameTab.Reputation, "reputation");
|
||||
|
||||
var balanceFrame = new GUIFrame(new RectTransform(new Point(innerLayoutGroup.Rect.Width, innerLayoutGroup.Rect.Height - infoFrameHolderHeight), parent: innerLayoutGroup.RectTransform), style: "InnerFrame");
|
||||
new GUITextBlock(new RectTransform(Vector2.One, balanceFrame.RectTransform), "", textAlignment: Alignment.Right, parseRichText: true)
|
||||
{
|
||||
TextGetter = () => TextManager.GetWithVariable("campaignmoney", "[money]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", campaignMode.Money))
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
bool isTraitor = GameMain.Client?.Character?.IsTraitor ?? false;
|
||||
if (isTraitor && GameMain.Client.TraitorMission != null)
|
||||
{
|
||||
var traitorButton = createTabButton(InfoFrameTab.Traitor, "tabmenu.traitor");
|
||||
}
|
||||
}
|
||||
|
||||
var submarineButton = createTabButton(InfoFrameTab.Submarine, "submarine");
|
||||
|
||||
if (GameMain.NetworkMember != null)
|
||||
{
|
||||
var myCharacterButton = new GUIButton(new RectTransform(new Vector2(0.245f, 1.0f), buttonArea.RectTransform), TextManager.Get("tabmenu.character"), style: "GUITabButton")
|
||||
{
|
||||
UserData = InfoFrameTab.MyCharacter,
|
||||
OnClicked = SelectInfoFrameTab
|
||||
};
|
||||
tabButtons.Add(myCharacterButton);
|
||||
var myCharacterButton = createTabButton(InfoFrameTab.MyCharacter, "tabmenu.character");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,6 +272,14 @@ namespace Barotrauma
|
||||
case InfoFrameTab.Mission:
|
||||
CreateMissionInfo(infoFrameHolder);
|
||||
break;
|
||||
case InfoFrameTab.Reputation:
|
||||
if (GameMain.GameSession.RoundSummary != null && GameMain.GameSession.GameMode is CampaignMode campaignMode)
|
||||
{
|
||||
infoFrameHolder.ClearChildren();
|
||||
GUIFrame reputationFrame = new GUIFrame(new RectTransform(Vector2.One, infoFrameHolder.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox");
|
||||
GameMain.GameSession.RoundSummary.CreateReputationInfoPanel(reputationFrame, campaignMode);
|
||||
}
|
||||
break;
|
||||
case InfoFrameTab.Traitor:
|
||||
TraitorMissionPrefab traitorMission = GameMain.Client.TraitorMission;
|
||||
Character traitor = GameMain.Client.Character;
|
||||
@@ -262,6 +290,9 @@ namespace Barotrauma
|
||||
if (GameMain.NetworkMember == null) { return false; }
|
||||
GameMain.NetLobbyScreen.CreatePlayerFrame(infoFrameHolder);
|
||||
break;
|
||||
case InfoFrameTab.Submarine:
|
||||
CreateSubmarineInfo(infoFrameHolder, Submarine.MainSub);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -356,6 +387,21 @@ namespace Barotrauma
|
||||
|
||||
crewFrame.RectTransform.AbsoluteOffset = new Point(0, (int)(headerFrames[0].Rect.Height * headerFrames.Length) - (teamIDs.Count > 1 ? GUI.IntScale(10f) : 0));
|
||||
|
||||
float totalRelativeHeight = 0.0f;
|
||||
if (teamIDs.Count > 1) { totalRelativeHeight += teamIDs.Count * nameHeight; }
|
||||
headerFrames.ForEach(f => totalRelativeHeight += f.RectTransform.RelativeSize.Y);
|
||||
crewListArray.ForEach(f => totalRelativeHeight += f.RectTransform.RelativeSize.Y);
|
||||
if (totalRelativeHeight > 1.0f)
|
||||
{
|
||||
float heightOverflow = totalRelativeHeight - 1.0f;
|
||||
float heightToReduce = heightOverflow / crewListArray.Length;
|
||||
crewListArray.ForEach(l =>
|
||||
{
|
||||
l.RectTransform.Resize(l.RectTransform.RelativeSize - new Vector2(0.0f, heightToReduce));
|
||||
l.UpdateDimensions();
|
||||
});
|
||||
}
|
||||
|
||||
if (GameMain.IsMultiplayer)
|
||||
{
|
||||
CreateMultiPlayerList(false);
|
||||
@@ -676,7 +722,7 @@ namespace Barotrauma
|
||||
GUIComponent existingPreview = infoFrameHolder.FindChild("SelectedCharacter");
|
||||
if (existingPreview != null) infoFrameHolder.RemoveChild(existingPreview);
|
||||
|
||||
GUIFrame background = new GUIFrame(new RectTransform(new Vector2(0.543f, 0.717f), infoFrameHolder.RectTransform, Anchor.TopLeft, Pivot.TopRight) { RelativeOffset = new Vector2(-0.061f, 0) })
|
||||
GUIFrame background = new GUIFrame(new RectTransform(new Vector2(0.543f, 0.717f), infoFrameHolder.RectTransform, Anchor.TopLeft, Pivot.TopRight) { RelativeOffset = new Vector2(-0.145f, 0) })
|
||||
{
|
||||
UserData = "SelectedCharacter"
|
||||
};
|
||||
@@ -831,12 +877,24 @@ namespace Barotrauma
|
||||
|
||||
if (logList != null)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), logList.Content.RectTransform), line, wrap: true, font: GUI.SmallFont)
|
||||
var textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), logList.Content.RectTransform), line, wrap: true, font: GUI.SmallFont, parseRichText: true)
|
||||
{
|
||||
TextColor = textColor,
|
||||
CanBeFocused = false,
|
||||
UserData = line
|
||||
}.CalculateHeightFromText();
|
||||
};
|
||||
textBlock.CalculateHeightFromText();
|
||||
if (textBlock.HasColorHighlight)
|
||||
{
|
||||
foreach (var data in textBlock.RichTextData)
|
||||
{
|
||||
textBlock.ClickableAreas.Add(new GUITextBlock.ClickableArea()
|
||||
{
|
||||
Data = data,
|
||||
OnClick = GameMain.NetLobbyScreen.SelectPlayer
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -857,54 +915,109 @@ namespace Barotrauma
|
||||
|
||||
int locationInfoYOffset = locationNameText.Rect.Height + locationTypeText.Rect.Height + padding * 2;
|
||||
|
||||
GUIFrame missionDescriptionHolder;
|
||||
GUIListBox missionList;
|
||||
|
||||
if (hasPortrait)
|
||||
{
|
||||
GUIFrame missionImageHolder = new GUIFrame(new RectTransform(new Point(contentWidth, (int)(missionFrame.Rect.Height * 0.588f)), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, locationInfoYOffset) });
|
||||
GUIFrame portraitHolder = new GUIFrame(new RectTransform(new Point(contentWidth, (int)(missionFrame.Rect.Height * 0.588f)), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, locationInfoYOffset) });
|
||||
float portraitAspectRatio = portrait.SourceRect.Width / portrait.SourceRect.Height;
|
||||
GUIImage portraitImage = new GUIImage(new RectTransform(new Vector2(1.0f, 1f), missionImageHolder.RectTransform), portrait, scaleToFit: true);
|
||||
missionImageHolder.RectTransform.NonScaledSize = new Point(portraitImage.Rect.Size.X, (int)(portraitImage.Rect.Size.X / portraitAspectRatio));
|
||||
missionDescriptionHolder = new GUIFrame(new RectTransform(new Point(contentWidth, 0), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, missionImageHolder.RectTransform.AbsoluteOffset.Y + missionImageHolder.Rect.Height + padding) }, style: null);
|
||||
GUIImage portraitImage = new GUIImage(new RectTransform(new Vector2(1.0f, 1f), portraitHolder.RectTransform), portrait, scaleToFit: true);
|
||||
portraitHolder.RectTransform.NonScaledSize = new Point(portraitImage.Rect.Size.X, (int)(portraitImage.Rect.Size.X / portraitAspectRatio));
|
||||
|
||||
missionList = new GUIListBox(new RectTransform(new Point(contentWidth, missionFrame.Rect.Bottom - portraitHolder.Rect.Bottom - padding), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, portraitHolder.RectTransform.AbsoluteOffset.Y + portraitHolder.Rect.Height + padding) });
|
||||
}
|
||||
else
|
||||
{
|
||||
missionDescriptionHolder = new GUIFrame(new RectTransform(new Point(contentWidth, 0), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, locationInfoYOffset) }, style: null);
|
||||
}
|
||||
missionList = new GUIListBox(new RectTransform(new Point(contentWidth, missionFrame.Rect.Height - locationInfoYOffset - padding), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, locationInfoYOffset) });
|
||||
}
|
||||
missionList.ContentBackground.Color = Color.Transparent;
|
||||
missionList.Spacing = GUI.IntScale(15);
|
||||
|
||||
Mission mission = GameMain.GameSession?.Mission;
|
||||
if (mission != null)
|
||||
if (GameMain.GameSession?.Missions != null)
|
||||
{
|
||||
GUILayoutGroup missionTextGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.744f, 0f), missionDescriptionHolder.RectTransform, Anchor.CenterLeft) { RelativeOffset = new Vector2(0.225f, 0f) }, false, childAnchor: Anchor.TopLeft);
|
||||
|
||||
string missionNameString = ToolBox.WrapText(mission.Name, missionTextGroup.Rect.Width, GUI.LargeFont);
|
||||
string missionDescriptionString = ToolBox.WrapText(mission.Description, missionTextGroup.Rect.Width, GUI.Font);
|
||||
string rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", mission.Reward));
|
||||
string missionRewardString = ToolBox.WrapText(TextManager.GetWithVariable("MissionReward", "[reward]", rewardText), missionTextGroup.Rect.Width, GUI.Font);
|
||||
|
||||
Vector2 missionNameSize = GUI.LargeFont.MeasureString(missionNameString);
|
||||
Vector2 missionDescriptionSize = GUI.Font.MeasureString(missionDescriptionString);
|
||||
Vector2 missionRewardSize = GUI.Font.MeasureString(missionRewardString);
|
||||
|
||||
missionDescriptionHolder.RectTransform.NonScaledSize = new Point(missionDescriptionHolder.RectTransform.NonScaledSize.X, (int)(missionNameSize.Y + missionDescriptionSize.Y + missionRewardSize.Y));
|
||||
missionTextGroup.RectTransform.NonScaledSize = new Point(missionTextGroup.RectTransform.NonScaledSize.X, missionDescriptionHolder.RectTransform.NonScaledSize.Y);
|
||||
|
||||
if (mission.Prefab.Icon != null)
|
||||
foreach (Mission mission in GameMain.GameSession.Missions)
|
||||
{
|
||||
float iconAspectRatio = mission.Prefab.Icon.SourceRect.Width / mission.Prefab.Icon.SourceRect.Height;
|
||||
int iconWidth = (int)(0.225f * missionDescriptionHolder.RectTransform.NonScaledSize.X);
|
||||
int iconHeight = Math.Max(missionTextGroup.RectTransform.NonScaledSize.Y, (int)(iconWidth * iconAspectRatio));
|
||||
Point iconSize = new Point(iconWidth, iconHeight);
|
||||
GUIFrame missionDescriptionHolder = new GUIFrame(new RectTransform(Vector2.One, missionList.Content.RectTransform), style: null);
|
||||
GUILayoutGroup missionTextGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.744f, 0f), missionDescriptionHolder.RectTransform, Anchor.CenterLeft) { RelativeOffset = new Vector2(0.225f, 0f) }, false, childAnchor: Anchor.TopLeft)
|
||||
{
|
||||
AbsoluteSpacing = GUI.IntScale(5)
|
||||
};
|
||||
string descriptionText = mission.Description;
|
||||
foreach (string missionMessage in mission.ShownMessages)
|
||||
{
|
||||
descriptionText += "\n\n" + missionMessage;
|
||||
}
|
||||
string rewardText = mission.GetMissionRewardText();
|
||||
string reputationText = mission.GetReputationRewardText(mission.Locations[0]);
|
||||
|
||||
new GUIImage(new RectTransform(iconSize, missionDescriptionHolder.RectTransform), mission.Prefab.Icon, null, true) { Color = mission.Prefab.IconColor };
|
||||
var missionNameRichTextData = RichTextData.GetRichTextData(mission.Name, out string missionNameString);
|
||||
var missionRewardRichTextData = RichTextData.GetRichTextData(rewardText, out string missionRewardString);
|
||||
var missionReputationRichTextData = RichTextData.GetRichTextData(reputationText, out string missionReputationString);
|
||||
var missionDescriptionRichTextData = RichTextData.GetRichTextData(descriptionText, out string missionDescriptionString);
|
||||
|
||||
missionNameString = ToolBox.WrapText(missionNameString, missionTextGroup.Rect.Width, GUI.LargeFont);
|
||||
missionRewardString = ToolBox.WrapText(missionRewardString, missionTextGroup.Rect.Width, GUI.Font);
|
||||
missionReputationString = ToolBox.WrapText(missionReputationString, missionTextGroup.Rect.Width, GUI.Font);
|
||||
missionDescriptionString = ToolBox.WrapText(missionDescriptionString, missionTextGroup.Rect.Width, GUI.Font);
|
||||
|
||||
Vector2 missionNameSize = GUI.LargeFont.MeasureString(missionNameString);
|
||||
Vector2 missionDescriptionSize = GUI.Font.MeasureString(missionDescriptionString);
|
||||
Vector2 missionRewardSize = GUI.Font.MeasureString(missionRewardString);
|
||||
Vector2 missionReputationSize = GUI.Font.MeasureString(missionReputationString);
|
||||
|
||||
float ySize = missionNameSize.Y + missionDescriptionSize.Y + missionRewardSize.Y + missionReputationSize.Y + missionTextGroup.AbsoluteSpacing * 4;
|
||||
bool displayDifficulty = mission.Difficulty.HasValue;
|
||||
if (displayDifficulty) { ySize += missionRewardSize.Y; }
|
||||
|
||||
missionDescriptionHolder.RectTransform.NonScaledSize = new Point(missionDescriptionHolder.RectTransform.NonScaledSize.X, (int)ySize);
|
||||
missionTextGroup.RectTransform.NonScaledSize = new Point(missionTextGroup.RectTransform.NonScaledSize.X, missionDescriptionHolder.RectTransform.NonScaledSize.Y);
|
||||
|
||||
if (mission.Prefab.Icon != null)
|
||||
{
|
||||
float iconAspectRatio = mission.Prefab.Icon.SourceRect.Width / mission.Prefab.Icon.SourceRect.Height;
|
||||
int iconWidth = (int)(0.225f * missionDescriptionHolder.RectTransform.NonScaledSize.X);
|
||||
int iconHeight = Math.Max(missionTextGroup.RectTransform.NonScaledSize.Y, (int)(iconWidth * iconAspectRatio));
|
||||
Point iconSize = new Point(iconWidth, iconHeight);
|
||||
|
||||
new GUIImage(new RectTransform(iconSize, missionDescriptionHolder.RectTransform), mission.Prefab.Icon, null, true)
|
||||
{
|
||||
Color = mission.Prefab.IconColor,
|
||||
HoverColor = mission.Prefab.IconColor,
|
||||
SelectedColor = mission.Prefab.IconColor,
|
||||
CanBeFocused = false
|
||||
};
|
||||
}
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionNameRichTextData, missionNameString, font: GUI.LargeFont);
|
||||
GUILayoutGroup difficultyIndicatorGroup = null;
|
||||
if (displayDifficulty)
|
||||
{
|
||||
difficultyIndicatorGroup = new GUILayoutGroup(new RectTransform(new Point(missionTextGroup.Rect.Width, (int)missionRewardSize.Y), parent: missionTextGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
AbsoluteSpacing = 1
|
||||
};
|
||||
var difficultyColor = mission.GetDifficultyColor();
|
||||
for (int i = 0; i < mission.Difficulty.Value; i++)
|
||||
{
|
||||
new GUIImage(new RectTransform(Vector2.One, difficultyIndicatorGroup.RectTransform, scaleBasis: ScaleBasis.Smallest), "DifficultyIndicator", scaleToFit: true)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
Color = difficultyColor
|
||||
};
|
||||
}
|
||||
}
|
||||
var rewardTextBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionRewardRichTextData, missionRewardString);
|
||||
if (difficultyIndicatorGroup != null)
|
||||
{
|
||||
difficultyIndicatorGroup.RectTransform.Resize(new Point((int)(difficultyIndicatorGroup.Rect.Width - rewardTextBlock.Padding.X - rewardTextBlock.Padding.Z), difficultyIndicatorGroup.Rect.Height));
|
||||
difficultyIndicatorGroup.RectTransform.AbsoluteOffset = new Point((int)rewardTextBlock.Padding.X, 0);
|
||||
}
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionReputationRichTextData, missionReputationString);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionDescriptionRichTextData, missionDescriptionString);
|
||||
}
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionNameString, font: GUI.LargeFont);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionRewardString);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionDescriptionString);
|
||||
}
|
||||
else
|
||||
{
|
||||
GUILayoutGroup missionTextGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0f), missionDescriptionHolder.RectTransform, Anchor.CenterLeft), false, childAnchor: Anchor.TopLeft);
|
||||
GUILayoutGroup missionTextGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0f), missionList.RectTransform, Anchor.CenterLeft), false, childAnchor: Anchor.TopLeft);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), TextManager.Get("NoMission"), font: GUI.LargeFont);
|
||||
}
|
||||
}
|
||||
@@ -938,5 +1051,93 @@ namespace Barotrauma
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionNameString, font: GUI.LargeFont);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionDescriptionString);
|
||||
}
|
||||
|
||||
private void CreateSubmarineInfo(GUIFrame infoFrame, Submarine sub)
|
||||
{
|
||||
GUIFrame subInfoFrame = new GUIFrame(new RectTransform(Vector2.One, infoFrame.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox");
|
||||
GUIFrame paddedFrame = new GUIFrame(new RectTransform(Vector2.One * 0.97f, subInfoFrame.RectTransform, Anchor.Center), style: null);
|
||||
|
||||
var previewButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.43f), paddedFrame.RectTransform), style: null)
|
||||
{
|
||||
OnClicked = (btn, obj) => { SubmarinePreview.Create(sub.Info); return false; },
|
||||
};
|
||||
|
||||
var previewImage = sub.Info.PreviewImage ?? SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name.Equals(sub.Info.Name, StringComparison.OrdinalIgnoreCase))?.PreviewImage;
|
||||
if (previewImage == null)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(Vector2.One, previewButton.RectTransform), TextManager.Get("SubPreviewImageNotFound"));
|
||||
}
|
||||
else
|
||||
{
|
||||
var submarinePreviewBackground = new GUIFrame(new RectTransform(Vector2.One, previewButton.RectTransform), style: null)
|
||||
{
|
||||
Color = Color.Black,
|
||||
HoverColor = Color.Black,
|
||||
SelectedColor = Color.Black,
|
||||
PressedColor = Color.Black,
|
||||
CanBeFocused = false,
|
||||
};
|
||||
new GUIImage(new RectTransform(new Vector2(0.98f), submarinePreviewBackground.RectTransform, Anchor.Center), previewImage, scaleToFit: true) { CanBeFocused = false };
|
||||
new GUIFrame(new RectTransform(Vector2.One, submarinePreviewBackground.RectTransform), "InnerGlow", color: Color.Black) { CanBeFocused = false };
|
||||
}
|
||||
|
||||
new GUIFrame(new RectTransform(Vector2.One * 0.12f, previewButton.RectTransform, anchor: Anchor.BottomRight, pivot: Pivot.BottomRight, scaleBasis: ScaleBasis.BothHeight)
|
||||
{
|
||||
AbsoluteOffset = new Point((int)(0.03f * previewButton.Rect.Height))
|
||||
},
|
||||
"ExpandButton", Color.White)
|
||||
{
|
||||
Color = Color.White,
|
||||
HoverColor = Color.White,
|
||||
PressedColor = Color.White
|
||||
};
|
||||
|
||||
var subInfoTextLayout = new GUILayoutGroup(new RectTransform(Vector2.One, paddedFrame.RectTransform));
|
||||
|
||||
string className = !sub.Info.HasTag(SubmarineTag.Shuttle) ? TextManager.Get($"submarineclass.{sub.Info.SubmarineClass}") : TextManager.Get("shuttle");
|
||||
|
||||
int nameHeight = (int)GUI.LargeFont.MeasureString(sub.Info.DisplayName, true).Y;
|
||||
int classHeight = (int)GUI.SubHeadingFont.MeasureString(className).Y;
|
||||
|
||||
var submarineNameText = new GUITextBlock(new RectTransform(new Point(subInfoTextLayout.Rect.Width, nameHeight + HUDLayoutSettings.Padding / 2), subInfoTextLayout.RectTransform), sub.Info.DisplayName, textAlignment: Alignment.CenterLeft, font: GUI.LargeFont) { CanBeFocused = false };
|
||||
submarineNameText.RectTransform.MinSize = new Point(0, (int)submarineNameText.TextSize.Y);
|
||||
var submarineClassText = new GUITextBlock(new RectTransform(new Point(subInfoTextLayout.Rect.Width, classHeight), subInfoTextLayout.RectTransform), className, textAlignment: Alignment.CenterLeft, font: GUI.SubHeadingFont) { CanBeFocused = false };
|
||||
submarineClassText.RectTransform.MinSize = new Point(0, (int)submarineClassText.TextSize.Y);
|
||||
|
||||
if (GameMain.GameSession?.GameMode is CampaignMode campaign)
|
||||
{
|
||||
GUILayoutGroup headerLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.09f), paddedFrame.RectTransform) { RelativeOffset = new Vector2(0f, 0.43f) }, isHorizontal: true) { Stretch = true };
|
||||
GUIImage headerIcon = new GUIImage(new RectTransform(Vector2.One, headerLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "SubmarineIcon");
|
||||
new GUITextBlock(new RectTransform(Vector2.One, headerLayout.RectTransform), TextManager.Get("uicategory.upgrades"), font: GUI.LargeFont);
|
||||
|
||||
var upgradeRootLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.48f), paddedFrame.RectTransform, Anchor.BottomLeft, Pivot.BottomLeft), isHorizontal: true);
|
||||
|
||||
var upgradeCategoryPanel = UpgradeStore.CreateUpgradeCategoryList(new RectTransform(new Vector2(0.4f, 1f), upgradeRootLayout.RectTransform));
|
||||
upgradeCategoryPanel.HideChildrenOutsideFrame = true;
|
||||
UpgradeStore.UpdateCategoryList(upgradeCategoryPanel, campaign, sub, UpgradeStore.GetApplicableCategories(sub).ToArray());
|
||||
GUIComponent[] toRemove = upgradeCategoryPanel.Content.FindChildren(c => !c.Enabled).ToArray();
|
||||
toRemove.ForEach(c => upgradeCategoryPanel.RemoveChild(c));
|
||||
|
||||
var upgradePanel = new GUIListBox(new RectTransform(new Vector2(0.6f, 1f), upgradeRootLayout.RectTransform));
|
||||
upgradeCategoryPanel.OnSelected = (component, userData) =>
|
||||
{
|
||||
upgradePanel.ClearChildren();
|
||||
if (userData is UpgradeStore.CategoryData categoryData && Submarine.MainSub != null)
|
||||
{
|
||||
foreach (UpgradePrefab prefab in categoryData.Prefabs)
|
||||
{
|
||||
var frame = UpgradeStore.CreateUpgradeFrame(prefab, categoryData.Category, campaign, new RectTransform(new Vector2(1f, 0.3f), upgradePanel.Content.RectTransform), addBuyButton: false);
|
||||
UpgradeStore.UpdateUpgradeEntry(frame, prefab, categoryData.Category, campaign);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
var specsListBox = new GUIListBox(new RectTransform(new Vector2(1f, 0.57f), paddedFrame.RectTransform, Anchor.BottomLeft, Pivot.BottomLeft));
|
||||
sub.Info.CreateSpecsWindow(specsListBox, GUI.Font, includeTitle: false, includeClass: false, includeDescription: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Barotrauma
|
||||
|
||||
internal class UpgradeStore
|
||||
{
|
||||
private readonly struct CategoryData
|
||||
public readonly struct CategoryData
|
||||
{
|
||||
public readonly UpgradeCategory Category;
|
||||
public readonly List<UpgradePrefab> Prefabs;
|
||||
@@ -125,7 +125,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (component.UserData is CategoryData data)
|
||||
{
|
||||
UpdateUpgradeEntry(component, data.SinglePrefab, data.Category);
|
||||
UpdateUpgradeEntry(component, data.SinglePrefab, data.Category, Campaign);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -133,29 +133,35 @@ namespace Barotrauma
|
||||
// update the small indicator icons on the list
|
||||
if (currentStoreLayout?.Parent != null)
|
||||
{
|
||||
foreach (GUIComponent component in currentStoreLayout.Content.Children)
|
||||
{
|
||||
if (!(component.UserData is CategoryData data)) { continue; }
|
||||
if (component.FindChild("indicators", true) is { } indicators)
|
||||
{
|
||||
UpdateCategoryIndicators(indicators, component, data.Prefabs, data.Category);
|
||||
}
|
||||
}
|
||||
UpdateCategoryList(currentStoreLayout, Campaign, drawnSubmarine, applicableCategories);
|
||||
}
|
||||
}
|
||||
|
||||
// reset the order first
|
||||
foreach (UpgradeCategory category in UpgradeCategory.Categories)
|
||||
//TODO: move this somewhere else
|
||||
public static void UpdateCategoryList(GUIListBox categoryList, CampaignMode campaign, Submarine drawnSubmarine, IEnumerable<UpgradeCategory> applicableCategories)
|
||||
{
|
||||
foreach (GUIComponent component in categoryList.Content.Children)
|
||||
{
|
||||
if (!(component.UserData is CategoryData data)) { continue; }
|
||||
if (component.FindChild("indicators", true) is { } indicators)
|
||||
{
|
||||
GUIComponent component = currentStoreLayout.Content.FindChild(c => c.UserData is CategoryData categoryData && categoryData.Category == category);
|
||||
component?.SetAsLastChild();
|
||||
UpdateCategoryIndicators(indicators, component, data.Prefabs, data.Category, campaign, drawnSubmarine, applicableCategories);
|
||||
}
|
||||
}
|
||||
|
||||
// send the disabled components to the bottom
|
||||
List<GUIComponent> lastChilds = currentStoreLayout.Content.Children.Where(component => !component.Enabled).ToList();
|
||||
// reset the order first
|
||||
foreach (UpgradeCategory category in UpgradeCategory.Categories)
|
||||
{
|
||||
GUIComponent component = categoryList.Content.FindChild(c => c.UserData is CategoryData categoryData && categoryData.Category == category);
|
||||
component?.SetAsLastChild();
|
||||
}
|
||||
|
||||
foreach (var lastChild in lastChilds)
|
||||
{
|
||||
lastChild.SetAsLastChild();
|
||||
}
|
||||
// send the disabled components to the bottom
|
||||
List<GUIComponent> lastChilds = categoryList.Content.Children.Where(component => !component.Enabled).ToList();
|
||||
|
||||
foreach (var lastChild in lastChilds)
|
||||
{
|
||||
lastChild.SetAsLastChild();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -511,9 +517,10 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateUpgradeTab()
|
||||
//TODO: put this somewhere else
|
||||
public static GUIListBox CreateUpgradeCategoryList(RectTransform rectTransform)
|
||||
{
|
||||
currentStoreLayout = new GUIListBox(rectT(1.0f, 1.5f, storeLayout), style: null)
|
||||
var upgradeCategoryList = new GUIListBox(rectTransform, style: null)
|
||||
{
|
||||
AutoHideScrollBar = false,
|
||||
ScrollBarVisible = false,
|
||||
@@ -548,7 +555,7 @@ namespace Barotrauma
|
||||
|
||||
foreach (var (category, prefabs) in upgrades)
|
||||
{
|
||||
var frameChild = new GUIFrame(rectT(1, 0.15f, currentStoreLayout.Content), style: "UpgradeUIFrame")
|
||||
var frameChild = new GUIFrame(rectT(1, 0.15f, upgradeCategoryList.Content), style: "UpgradeUIFrame")
|
||||
{
|
||||
UserData = new CategoryData(category, prefabs),
|
||||
GlowOnSelect = true
|
||||
@@ -565,9 +572,9 @@ namespace Barotrauma
|
||||
* |-----------------------------|--------------------------|
|
||||
*/
|
||||
GUILayoutGroup contentLayout = new GUILayoutGroup(rectT(0.9f, 0.85f, frameChild, Anchor.Center));
|
||||
var itemCategoryLabel = new GUITextBlock(rectT(1, 1, contentLayout), category.Name, font: GUI.SubHeadingFont) { CanBeFocused = false };
|
||||
GUILayoutGroup indicatorLayout = new GUILayoutGroup(rectT(0.5f, 0.25f, contentLayout, Anchor.BottomRight), isHorizontal: true, childAnchor: Anchor.TopRight) { UserData = "indicators", IgnoreLayoutGroups = true, RelativeSpacing = 0.01f };
|
||||
|
||||
var itemCategoryLabel = new GUITextBlock(rectT(1, 1, contentLayout), category.Name, font: GUI.SubHeadingFont) { CanBeFocused = false };
|
||||
GUILayoutGroup indicatorLayout = new GUILayoutGroup(rectT(0.5f, 0.25f, contentLayout, Anchor.BottomRight), isHorizontal: true, childAnchor: Anchor.TopRight) { UserData = "indicators", IgnoreLayoutGroups = true, RelativeSpacing = 0.01f };
|
||||
|
||||
foreach (var prefab in prefabs)
|
||||
{
|
||||
GUIImage upgradeIndicator = new GUIImage(rectT(0.1f, 1f, indicatorLayout), style: "UpgradeIndicator", scaleToFit: true) { UserData = prefab, CanBeFocused = false };
|
||||
@@ -582,6 +589,13 @@ namespace Barotrauma
|
||||
indicatorLayout.Recalculate();
|
||||
}
|
||||
|
||||
return upgradeCategoryList;
|
||||
}
|
||||
|
||||
private void CreateUpgradeTab()
|
||||
{
|
||||
currentStoreLayout = CreateUpgradeCategoryList(rectT(1.0f, 1.5f, storeLayout));
|
||||
|
||||
selectedUpgradeCategoryLayout = new GUIFrame(rectT(GUI.IsFourByThree() ? 0.3f : 0.25f, 1, mainStoreLayout), style: null) { CanBeFocused = false };
|
||||
|
||||
RefreshUpgradeList();
|
||||
@@ -637,7 +651,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateUpgradeEntry(UpgradePrefab prefab, UpgradeCategory category, GUIComponent parent, List<Item> itemsOnSubmarine)
|
||||
public static GUIFrame CreateUpgradeFrame(UpgradePrefab prefab, UpgradeCategory category, CampaignMode campaign, RectTransform rectTransform, bool addBuyButton = true)
|
||||
{
|
||||
/* UPGRADE PREFAB ENTRY
|
||||
* |------------------------------------------------------------------|
|
||||
@@ -648,8 +662,8 @@ namespace Barotrauma
|
||||
* | | progress bar | x / y | |
|
||||
* |------------------------------------------------------------------|
|
||||
*/
|
||||
GUIFrame prefabFrame = new GUIFrame(rectT(1f, 0.25f, parent), style: "ListBoxElement") { SelectedColor = Color.Transparent, UserData = new CategoryData(category, prefab) };
|
||||
GUILayoutGroup prefabLayout = new GUILayoutGroup(rectT(0.98f,0.95f, prefabFrame, Anchor.Center), isHorizontal: true);
|
||||
GUIFrame prefabFrame = new GUIFrame(rectTransform, style: "ListBoxElement") { SelectedColor = Color.Transparent, UserData = new CategoryData(category, prefab) };
|
||||
GUILayoutGroup prefabLayout = new GUILayoutGroup(rectT(0.98f, 0.95f, prefabFrame, Anchor.Center), isHorizontal: true) { Stretch = true };
|
||||
GUILayoutGroup imageLayout = new GUILayoutGroup(rectT(new Point(prefabLayout.Rect.Height, prefabLayout.Rect.Height), prefabLayout), childAnchor: Anchor.Center);
|
||||
var icon = new GUIImage(rectT(0.9f, 0.9f, imageLayout), prefab.Sprite, scaleToFit: true) { CanBeFocused = false };
|
||||
GUILayoutGroup textLayout = new GUILayoutGroup(rectT(0.8f - imageLayout.RectTransform.RelativeSize.X, 1, prefabLayout));
|
||||
@@ -659,9 +673,13 @@ namespace Barotrauma
|
||||
GUILayoutGroup progressLayout = new GUILayoutGroup(rectT(1, 0.25f, textLayout), isHorizontal: true, childAnchor: Anchor.CenterLeft) { UserData = "progressbar" };
|
||||
new GUIProgressBar(rectT(0.8f, 0.75f, progressLayout), 0.0f, GUI.Style.Orange);
|
||||
new GUITextBlock(rectT(0.2f, 1, progressLayout), string.Empty, font: GUI.SmallFont, textAlignment: Alignment.Center) { Padding = Vector4.Zero };
|
||||
GUILayoutGroup buyButtonLayout = new GUILayoutGroup(rectT(0.2f, 1, prefabLayout), childAnchor: Anchor.TopCenter) { UserData = "buybutton" };
|
||||
new GUITextBlock(rectT(1, 0.4f, buyButtonLayout), FormatCurrency(prefab.Price.GetBuyprice(Campaign.UpgradeManager.GetUpgradeLevel(prefab, category), Campaign.Map?.CurrentLocation)), textAlignment: Alignment.Center) { Padding = Vector4.Zero };
|
||||
GUILayoutGroup buyButtonLayout = null;
|
||||
if (addBuyButton)
|
||||
{
|
||||
buyButtonLayout = new GUILayoutGroup(rectT(0.2f, 1, prefabLayout), childAnchor: Anchor.TopCenter) { UserData = "buybutton" };
|
||||
new GUITextBlock(rectT(1, 0.4f, buyButtonLayout), FormatCurrency(prefab.Price.GetBuyprice(campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation)), textAlignment: Alignment.Center) { Padding = Vector4.Zero };
|
||||
var buyButton = new GUIButton(rectT(0.7f, 0.5f, buyButtonLayout), string.Empty, style: "UpgradeBuyButton") { Enabled = false };
|
||||
}
|
||||
|
||||
description.CalculateHeightFromText();
|
||||
// cut the description if it overflows and add a tooltip to it
|
||||
@@ -677,14 +695,33 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
// Recalculate everything to prevent jumping
|
||||
if (parent is GUILayoutGroup group) { group.Recalculate(); }
|
||||
if (rectTransform.Parent.GUIComponent is GUILayoutGroup group) { group.Recalculate(); }
|
||||
|
||||
descriptionLayout.Recalculate();
|
||||
prefabLayout.Recalculate();
|
||||
imageLayout.Recalculate();
|
||||
textLayout.Recalculate();
|
||||
progressLayout.Recalculate();
|
||||
buyButtonLayout.Recalculate();
|
||||
buyButtonLayout?.Recalculate();
|
||||
|
||||
return prefabFrame;
|
||||
}
|
||||
|
||||
private void CreateUpgradeEntry(UpgradePrefab prefab, UpgradeCategory category, GUIComponent parent, List<Item> itemsOnSubmarine)
|
||||
{
|
||||
GUIFrame prefabFrame = CreateUpgradeFrame(prefab, category, Campaign, rectT(1f, 0.25f, parent));
|
||||
var prefabLayout = prefabFrame.GetChild<GUILayoutGroup>();
|
||||
GUILayoutGroup[] childLayouts = prefabLayout.GetAllChildren<GUILayoutGroup>().ToArray();
|
||||
var imageLayout = childLayouts[0];
|
||||
var icon = imageLayout.GetChild<GUIImage>();
|
||||
var textLayout = childLayouts[1];
|
||||
var name = textLayout.GetChild<GUITextBlock>();
|
||||
GUILayoutGroup[] textChildLayouts = textLayout.GetAllChildren<GUILayoutGroup>().ToArray();
|
||||
var descriptionLayout = textChildLayouts[0];
|
||||
var description = descriptionLayout.GetChild<GUITextBlock>();
|
||||
var progressLayout = textChildLayouts[1];
|
||||
var buyButtonLayout = childLayouts[2];
|
||||
var buyButton = buyButtonLayout.GetChild<GUIButton>();
|
||||
|
||||
if (!HasPermission || itemsOnSubmarine != null && !itemsOnSubmarine.Any(it => category.CanBeApplied(it, prefab)))
|
||||
{
|
||||
@@ -713,7 +750,7 @@ namespace Barotrauma
|
||||
return true;
|
||||
};
|
||||
|
||||
UpdateUpgradeEntry(prefabFrame, prefab, category);
|
||||
UpdateUpgradeEntry(prefabFrame, prefab, category, Campaign);
|
||||
}
|
||||
|
||||
private void CreateItemTooltip(MapEntity entity)
|
||||
@@ -778,6 +815,18 @@ namespace Barotrauma
|
||||
static string CreateListEntry(string name, int level) => TextManager.GetWithVariables("upgradeuitooltip.upgradelistelement", new[] { "[upgradename]", "[level]" }, new[] { name, $"{level}" });
|
||||
}
|
||||
|
||||
public static IEnumerable<UpgradeCategory> GetApplicableCategories(Submarine drawnSubmarine)
|
||||
{
|
||||
Item[] entitiesOnSub = drawnSubmarine.GetItems(true).Where(i => drawnSubmarine.IsEntityFoundOnThisSub(i, true)).ToArray();
|
||||
foreach (UpgradeCategory category in UpgradeCategory.Categories)
|
||||
{
|
||||
if (entitiesOnSub.Any(item => category.CanBeApplied(item, null)))
|
||||
{
|
||||
yield return category;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSubmarinePreview(float deltaTime, GUICustomComponent parent)
|
||||
{
|
||||
if (!parent.Children.Any() || Submarine.MainSub != null && Submarine.MainSub != drawnSubmarine || GameMain.GraphicsWidth != screenResolution.X || GameMain.GraphicsHeight != screenResolution.Y)
|
||||
@@ -789,16 +838,8 @@ namespace Barotrauma
|
||||
CreateSubmarinePreview(drawnSubmarine, parent);
|
||||
CreateHullBorderVerticies(drawnSubmarine, parent);
|
||||
|
||||
List<Item> entitiesOnSub = drawnSubmarine.GetItems(true).Where(i => drawnSubmarine.IsEntityFoundOnThisSub(i, true)).ToList();
|
||||
applicableCategories.Clear();
|
||||
|
||||
foreach (UpgradeCategory category in UpgradeCategory.Categories)
|
||||
{
|
||||
if (entitiesOnSub.Any(item => category.CanBeApplied(item, null)))
|
||||
{
|
||||
applicableCategories.Add(category);
|
||||
}
|
||||
}
|
||||
applicableCategories.AddRange(GetApplicableCategories(drawnSubmarine));
|
||||
}
|
||||
|
||||
screenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
|
||||
@@ -1002,9 +1043,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateUpgradeEntry(GUIComponent prefabFrame, UpgradePrefab prefab, UpgradeCategory category)
|
||||
public static void UpdateUpgradeEntry(GUIComponent prefabFrame, UpgradePrefab prefab, UpgradeCategory category, CampaignMode campaign)
|
||||
{
|
||||
int currentLevel = Campaign.UpgradeManager.GetUpgradeLevel(prefab, category);
|
||||
int currentLevel = campaign.UpgradeManager.GetUpgradeLevel(prefab, category);
|
||||
|
||||
string progressText = TextManager.GetWithVariables("upgrades.progressformat", new[] { "[level]", "[maxlevel]" }, new[] { currentLevel.ToString(), prefab.MaxLevel.ToString() });
|
||||
if (prefabFrame.FindChild("progressbar", true) is { } progressParent)
|
||||
@@ -1023,7 +1064,7 @@ namespace Barotrauma
|
||||
if (prefabFrame.FindChild("buybutton", true) is { } buttonParent)
|
||||
{
|
||||
GUITextBlock priceLabel = buttonParent.GetChild<GUITextBlock>();
|
||||
int price = prefab.Price.GetBuyprice(Campaign.UpgradeManager.GetUpgradeLevel(prefab, category), Campaign.Map?.CurrentLocation);
|
||||
int price = prefab.Price.GetBuyprice(campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation);
|
||||
|
||||
if (priceLabel != null && !WaitForServerUpdate)
|
||||
{
|
||||
@@ -1038,7 +1079,7 @@ namespace Barotrauma
|
||||
if (button != null)
|
||||
{
|
||||
button.Enabled = currentLevel < prefab.MaxLevel;
|
||||
if (WaitForServerUpdate || !HasPermission || price > Campaign.Money)
|
||||
if (WaitForServerUpdate || !campaign.AllowedToManageCampaign() || price > campaign.Money)
|
||||
{
|
||||
button.Enabled = false;
|
||||
}
|
||||
@@ -1046,7 +1087,14 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateCategoryIndicators(GUIComponent indicators, GUIComponent parent, List<UpgradePrefab> prefabs, UpgradeCategory category)
|
||||
private static void UpdateCategoryIndicators(
|
||||
GUIComponent indicators,
|
||||
GUIComponent parent,
|
||||
List<UpgradePrefab> prefabs,
|
||||
UpgradeCategory category,
|
||||
CampaignMode campaign,
|
||||
Submarine drawnSubmarine,
|
||||
IEnumerable<UpgradeCategory> applicableCategories)
|
||||
{
|
||||
// Disables the parent and only re-enables if the submarine contains valid items
|
||||
if (!category.IsWallUpgrade && drawnSubmarine != null)
|
||||
@@ -1078,13 +1126,13 @@ namespace Barotrauma
|
||||
GUIComponentStyle dimStyle = styles["upgradeindicatordim"];
|
||||
GUIComponentStyle offStyle = styles["upgradeindicatoroff"];
|
||||
|
||||
if (Campaign.UpgradeManager.GetUpgradeLevel(prefab, category) >= prefab.MaxLevel)
|
||||
if (campaign.UpgradeManager.GetUpgradeLevel(prefab, category) >= prefab.MaxLevel)
|
||||
{
|
||||
// we check this to avoid flickering from re-applying the same style
|
||||
if (image.Style == onStyle) { continue; }
|
||||
image.ApplyStyle(onStyle);
|
||||
}
|
||||
else if (Campaign.UpgradeManager.GetUpgradeLevel(prefab, category) > 0)
|
||||
else if (campaign.UpgradeManager.GetUpgradeLevel(prefab, category) > 0)
|
||||
{
|
||||
if (image.Style == dimStyle) { continue; }
|
||||
image.ApplyStyle(dimStyle);
|
||||
|
||||
@@ -77,10 +77,6 @@ namespace Barotrauma
|
||||
set
|
||||
{
|
||||
if (gameSession == value) { return; }
|
||||
if (value == null && Screen.Selected == GameScreen && gameSession.GameMode is CampaignMode)
|
||||
{
|
||||
DebugConsole.AddWarning("GameSession set to null while in the game screen\n" + Environment.StackTrace.CleanupStackTrace());
|
||||
}
|
||||
if (gameSession?.GameMode != null && gameSession.GameMode != value?.GameMode)
|
||||
{
|
||||
gameSession.GameMode.Remove();
|
||||
@@ -465,7 +461,28 @@ namespace Barotrauma
|
||||
|
||||
while (Config.WaitingForAutoUpdate) { yield return CoroutineStatus.Running; }
|
||||
}
|
||||
|
||||
|
||||
#if DEBUG
|
||||
if (Config.ModBreakerMode)
|
||||
{
|
||||
Config.SelectCorePackage(ContentPackage.CorePackages.GetRandom());
|
||||
foreach (var regularPackage in ContentPackage.RegularPackages)
|
||||
{
|
||||
if (Rand.Range(0.0, 1.0) <= 0.5)
|
||||
{
|
||||
Config.EnableRegularPackage(regularPackage);
|
||||
}
|
||||
else
|
||||
{
|
||||
Config.DisableRegularPackage(regularPackage);
|
||||
}
|
||||
}
|
||||
ContentPackage.SortContentPackages(p =>
|
||||
{
|
||||
return Rand.Int(int.MaxValue);
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
if (Config.AllEnabledPackages.None())
|
||||
{
|
||||
@@ -535,6 +552,7 @@ namespace Barotrauma
|
||||
Order.Init();
|
||||
EventManagerSettings.Init();
|
||||
BallastFloraPrefab.LoadAll(GetFilesOfType(ContentType.MapCreature));
|
||||
HintManager.Init();
|
||||
TitleScreen.LoadState = 50.0f;
|
||||
yield return CoroutineStatus.Running;
|
||||
|
||||
@@ -904,7 +922,9 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
#if !DEBUG
|
||||
if (NetworkMember == null && !WindowActive && !Paused && true && Screen.Selected != MainMenuScreen && Config.PauseOnFocusLost)
|
||||
if (NetworkMember == null && !WindowActive && !Paused && true && Config.PauseOnFocusLost &&
|
||||
Screen.Selected != MainMenuScreen && Screen.Selected != ServerListScreen && Screen.Selected != NetLobbyScreen &&
|
||||
Screen.Selected != SubEditorScreen && Screen.Selected != LevelEditorScreen)
|
||||
{
|
||||
GUI.TogglePauseMenu();
|
||||
Paused = true;
|
||||
@@ -918,6 +938,8 @@ namespace Barotrauma
|
||||
Client.AddToGUIUpdateList();
|
||||
}
|
||||
|
||||
SubmarinePreview.AddToGUIUpdateList();
|
||||
|
||||
FileSelection.AddToGUIUpdateList();
|
||||
|
||||
DebugConsole.AddToGUIUpdateList();
|
||||
@@ -1044,6 +1066,8 @@ namespace Barotrauma
|
||||
{
|
||||
if (save)
|
||||
{
|
||||
GUI.SetSavingIndicatorState(true);
|
||||
|
||||
if (GameSession.Submarine != null && !GameSession.Submarine.Removed)
|
||||
{
|
||||
GameSession.SubmarineInfo = new SubmarineInfo(GameSession.Submarine);
|
||||
@@ -1074,13 +1098,6 @@ namespace Barotrauma
|
||||
{
|
||||
((TutorialMode)GameSession.GameMode).Tutorial?.Stop();
|
||||
}
|
||||
|
||||
if (GameSettings.SendUserStatistics)
|
||||
{
|
||||
Mission mission = GameSession.Mission;
|
||||
GameAnalyticsManager.AddDesignEvent("QuitRound:" + (save ? "Save" : "NoSave"));
|
||||
GameAnalyticsManager.AddDesignEvent("EndRound:" + (mission == null ? "NoMission" : (mission.Completed ? "MissionCompleted" : "MissionFailed")));
|
||||
}
|
||||
}
|
||||
GUIMessageBox.CloseAll();
|
||||
MainMenuScreen.Select();
|
||||
@@ -1214,13 +1231,19 @@ namespace Barotrauma
|
||||
base.OnExiting(sender, args);
|
||||
}
|
||||
|
||||
public void ShowOpenUrlInWebBrowserPrompt(string url)
|
||||
public void ShowOpenUrlInWebBrowserPrompt(string url, string promptExtensionTag = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(url)) { return; }
|
||||
if (GUIMessageBox.VisibleBox?.UserData as string == "verificationprompt") { return; }
|
||||
|
||||
var msgBox = new GUIMessageBox("", TextManager.GetWithVariable("openlinkinbrowserprompt", "[link]", url),
|
||||
new string[] { TextManager.Get("Yes"), TextManager.Get("No") })
|
||||
string text = TextManager.GetWithVariable("openlinkinbrowserprompt", "[link]", url);
|
||||
string extensionText = TextManager.Get(promptExtensionTag, returnNull: true, useEnglishAsFallBack: false);
|
||||
if (!string.IsNullOrEmpty(extensionText))
|
||||
{
|
||||
text += $"\n\n{extensionText}";
|
||||
}
|
||||
|
||||
var msgBox = new GUIMessageBox("", text, new string[] { TextManager.Get("Yes"), TextManager.Get("No") })
|
||||
{
|
||||
UserData = "verificationprompt"
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,8 @@ namespace Barotrauma
|
||||
protected Color overlayTextColor;
|
||||
protected Sprite overlaySprite;
|
||||
|
||||
private TransitionType prevCampaignUIAutoOpenType;
|
||||
|
||||
protected GUIButton endRoundButton;
|
||||
|
||||
public GUIButton ReadyCheckButton;
|
||||
@@ -53,19 +55,26 @@ namespace Barotrauma
|
||||
{
|
||||
chatBox.ToggleOpen = wasChatBoxOpen;
|
||||
}
|
||||
if (!value && CampaignUI?.SelectedTab == InteractionType.PurchaseSub)
|
||||
{
|
||||
SubmarinePreview.Close();
|
||||
}
|
||||
showCampaignUI = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override void ShowStartMessage()
|
||||
{
|
||||
if (Mission == null) return;
|
||||
|
||||
new GUIMessageBox(Mission.Name, Mission.Description, new string[0], type: GUIMessageBox.Type.InGame, icon: Mission.Prefab.Icon)
|
||||
foreach (Mission mission in Missions)
|
||||
{
|
||||
IconColor = Mission.Prefab.IconColor,
|
||||
UserData = "missionstartmessage"
|
||||
};
|
||||
new GUIMessageBox(
|
||||
mission.Prefab.IsSideObjective ? TextManager.AddPunctuation(':', TextManager.Get("sideobjective"), mission.Name) : mission.Name,
|
||||
mission.Description, new string[0], type: GUIMessageBox.Type.InGame, icon: mission.Prefab.Icon, parseRichText: true)
|
||||
{
|
||||
IconColor = mission.Prefab.IconColor,
|
||||
UserData = "missionstartmessage"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -119,22 +128,22 @@ namespace Barotrauma
|
||||
{
|
||||
var backgroundSprite = GUI.Style.GetComponentStyle("CommandBackground").GetDefaultSprite();
|
||||
Vector2 centerPos = new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight) / 2;
|
||||
string wrappedText = ToolBox.WrapText(overlayText, GameMain.GraphicsWidth / 3, GUI.Font);
|
||||
Vector2 textSize = GUI.Font.MeasureString(wrappedText);
|
||||
Vector2 textPos = centerPos - textSize / 2;
|
||||
backgroundSprite.Draw(spriteBatch,
|
||||
centerPos,
|
||||
Color.White * (overlayTextColor.A / 255.0f),
|
||||
origin: backgroundSprite.size / 2,
|
||||
rotate: 0.0f,
|
||||
scale: new Vector2(1.5f, 0.7f) * (GameMain.GraphicsWidth / 3 / backgroundSprite.size.X));
|
||||
scale: new Vector2(GameMain.GraphicsWidth / 2 / backgroundSprite.size.X, textSize.Y / backgroundSprite.size.Y * 1.5f));
|
||||
|
||||
string wrappedText = ToolBox.WrapText(overlayText, GameMain.GraphicsWidth / 3, GUI.Font);
|
||||
Vector2 textSize = GUI.Font.MeasureString(wrappedText);
|
||||
Vector2 textPos = centerPos - textSize / 2;
|
||||
GUI.DrawString(spriteBatch, textPos + Vector2.One, wrappedText, Color.Black * (overlayTextColor.A / 255.0f));
|
||||
GUI.DrawString(spriteBatch, textPos, wrappedText, overlayTextColor);
|
||||
|
||||
if (!string.IsNullOrEmpty(overlayTextBottom))
|
||||
{
|
||||
Vector2 bottomTextPos = centerPos + new Vector2(0.0f, textSize.Y + 30 * GUI.Scale) - GUI.Font.MeasureString(overlayTextBottom) / 2;
|
||||
Vector2 bottomTextPos = centerPos + new Vector2(0.0f, textSize.Y / 2 + 40 * GUI.Scale) - GUI.Font.MeasureString(overlayTextBottom) / 2;
|
||||
GUI.DrawString(spriteBatch, bottomTextPos + Vector2.One, overlayTextBottom, Color.Black * (overlayTextColor.A / 255.0f));
|
||||
GUI.DrawString(spriteBatch, bottomTextPos, overlayTextBottom, overlayTextColor);
|
||||
}
|
||||
@@ -147,7 +156,7 @@ namespace Barotrauma
|
||||
if (ReadyCheckButton != null) { ReadyCheckButton.Visible = false; }
|
||||
return;
|
||||
}
|
||||
if (Submarine.MainSub == null) { return; }
|
||||
if (Submarine.MainSub == null || Level.Loaded == null) { return; }
|
||||
|
||||
endRoundButton.Visible = false;
|
||||
var availableTransition = GetAvailableTransition(out _, out Submarine leavingSub);
|
||||
@@ -158,7 +167,8 @@ namespace Barotrauma
|
||||
case TransitionType.ProgressToNextEmptyLocation:
|
||||
if (Level.Loaded.EndOutpost == null || !Level.Loaded.EndOutpost.DockedTo.Contains(leavingSub))
|
||||
{
|
||||
buttonText = TextManager.GetWithVariable("EnterLocation", "[locationname]", Level.Loaded.EndLocation?.Name ?? "[ERROR]");
|
||||
string textTag = availableTransition == TransitionType.ProgressToNextLocation ? "EnterLocation" : "EnterEmptyLocation";
|
||||
buttonText = TextManager.GetWithVariable(textTag, "[locationname]", Level.Loaded.EndLocation?.Name ?? "[ERROR]");
|
||||
endRoundButton.Visible = !ForceMapUI && !ShowCampaignUI;
|
||||
}
|
||||
break;
|
||||
@@ -170,7 +180,8 @@ namespace Barotrauma
|
||||
case TransitionType.ReturnToPreviousEmptyLocation:
|
||||
if (Level.Loaded.StartOutpost == null || !Level.Loaded.StartOutpost.DockedTo.Contains(leavingSub))
|
||||
{
|
||||
buttonText = TextManager.GetWithVariable("EnterLocation", "[locationname]", Level.Loaded.StartLocation?.Name ?? "[ERROR]");
|
||||
string textTag = availableTransition == TransitionType.ReturnToPreviousLocation ? "EnterLocation" : "EnterEmptyLocation";
|
||||
buttonText = TextManager.GetWithVariable(textTag, "[locationname]", Level.Loaded.StartLocation?.Name ?? "[ERROR]");
|
||||
endRoundButton.Visible = !ForceMapUI && !ShowCampaignUI;
|
||||
}
|
||||
|
||||
@@ -194,7 +205,20 @@ namespace Barotrauma
|
||||
|
||||
if (endRoundButton.Visible)
|
||||
{
|
||||
if (!AllowedToEndRound()) { buttonText = TextManager.Get("map"); }
|
||||
if (!AllowedToEndRound())
|
||||
{
|
||||
buttonText = TextManager.Get("map");
|
||||
}
|
||||
else if (prevCampaignUIAutoOpenType != availableTransition &&
|
||||
(availableTransition == TransitionType.ProgressToNextEmptyLocation || availableTransition == TransitionType.ReturnToPreviousEmptyLocation))
|
||||
{
|
||||
HintManager.OnAvailableTransition(availableTransition);
|
||||
//opening the campaign map pauses the game and prevents HintManager from running -> update it manually to get the hint to show up immediately
|
||||
HintManager.Update();
|
||||
Map.SelectLocation(-1);
|
||||
endRoundButton.OnClicked(EndRoundButton, null);
|
||||
prevCampaignUIAutoOpenType = availableTransition;
|
||||
}
|
||||
endRoundButton.Text = ToolBox.LimitString(buttonText, endRoundButton.Font, endRoundButton.Rect.Width - 5);
|
||||
if (endRoundButton.Text != buttonText)
|
||||
{
|
||||
@@ -209,15 +233,20 @@ namespace Barotrauma
|
||||
{
|
||||
endRoundButton.RectTransform.ScreenSpaceOffset = new Point(0, Character.Controlled.CharacterHealth.SuicideButton.Rect.Height);
|
||||
}
|
||||
else if (GameMain.Client != null && GameMain.Client.IsFollowSubTickBoxVisible)
|
||||
{
|
||||
endRoundButton.RectTransform.ScreenSpaceOffset = new Point(0, HUDLayoutSettings.Padding + GameMain.Client.FollowSubTickBox.Rect.Height);
|
||||
}
|
||||
else
|
||||
{
|
||||
endRoundButton.RectTransform.ScreenSpaceOffset = Point.Zero;
|
||||
}
|
||||
}
|
||||
endRoundButton.DrawManually(spriteBatch);
|
||||
if (this is MultiPlayerCampaign)
|
||||
if (this is MultiPlayerCampaign && ReadyCheckButton != null)
|
||||
{
|
||||
ReadyCheckButton?.DrawManually(spriteBatch);
|
||||
ReadyCheckButton.RectTransform.ScreenSpaceOffset = endRoundButton.RectTransform.ScreenSpaceOffset;
|
||||
ReadyCheckButton.DrawManually(spriteBatch);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -227,6 +227,11 @@ namespace Barotrauma
|
||||
float timer = 0.0f;
|
||||
while (timer < textDuration)
|
||||
{
|
||||
if (GameMain.GameSession == null || Screen.Selected != GameMain.GameScreen)
|
||||
{
|
||||
GUI.DisableHUD = false;
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
// Try to grab the controlled here to prevent inputs, assigned late on multiplayer
|
||||
if (Character.Controlled != null)
|
||||
{
|
||||
@@ -239,11 +244,20 @@ namespace Barotrauma
|
||||
timer = Math.Min(timer + CoroutineManager.DeltaTime, textDuration);
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
var outpost = GameMain.GameSession.Level.StartOutpost;
|
||||
var borders = outpost.GetDockedBorders();
|
||||
borders.Location += outpost.WorldPosition.ToPoint();
|
||||
GameMain.GameScreen.Cam.Position = new Vector2(borders.X + borders.Width / 2, borders.Y - borders.Height / 2);
|
||||
float startZoom = 0.8f /
|
||||
((float)Math.Max(borders.Width, borders.Height) / (float)GameMain.GameScreen.Cam.Resolution.X);
|
||||
GameMain.GameScreen.Cam.MinZoom = Math.Min(startZoom, GameMain.GameScreen.Cam.MinZoom);
|
||||
var transition = new CameraTransition(prevControlled, GameMain.GameScreen.Cam,
|
||||
null, null,
|
||||
fadeOut: false,
|
||||
duration: 5,
|
||||
startZoom: 1.5f, endZoom: 1.0f)
|
||||
losFadeIn: true,
|
||||
waitDuration: 1,
|
||||
panDuration: 5,
|
||||
startZoom: startZoom, endZoom: 1.0f)
|
||||
{
|
||||
AllowInterrupt = true,
|
||||
RemoveControlFromCharacter = false
|
||||
@@ -274,7 +288,8 @@ namespace Barotrauma
|
||||
var transition = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam,
|
||||
null, null,
|
||||
fadeOut: false,
|
||||
duration: 5,
|
||||
losFadeIn: true,
|
||||
panDuration: 5,
|
||||
startZoom: 0.5f, endZoom: 1.0f)
|
||||
{
|
||||
AllowInterrupt = true,
|
||||
@@ -311,6 +326,7 @@ namespace Barotrauma
|
||||
Level prevLevel = Level.Loaded;
|
||||
|
||||
bool success = CrewManager.GetCharacters().Any(c => !c.IsDead);
|
||||
GUI.SetSavingIndicatorState(success);
|
||||
crewDead = false;
|
||||
|
||||
var continueButton = GameMain.GameSession.RoundSummary?.ContinueButton;
|
||||
@@ -327,7 +343,7 @@ namespace Barotrauma
|
||||
var endTransition = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam, null,
|
||||
Alignment.Center,
|
||||
fadeOut: false,
|
||||
duration: EndTransitionDuration);
|
||||
panDuration: EndTransitionDuration);
|
||||
GameMain.Client.EndCinematic = endTransition;
|
||||
|
||||
Location portraitLocation = Map?.SelectedLocation ?? Map?.CurrentLocation ?? Level.Loaded?.StartLocation;
|
||||
@@ -335,7 +351,7 @@ namespace Barotrauma
|
||||
{
|
||||
overlaySprite = portraitLocation.Type.GetPortrait(portraitLocation.PortraitId);
|
||||
}
|
||||
float fadeOutDuration = endTransition.Duration;
|
||||
float fadeOutDuration = endTransition.PanDuration;
|
||||
float t = 0.0f;
|
||||
while (t < fadeOutDuration || endTransition.Running)
|
||||
{
|
||||
@@ -368,6 +384,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
GUI.SetSavingIndicatorState(false);
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
@@ -420,7 +437,7 @@ namespace Barotrauma
|
||||
{
|
||||
//wasn't initially docked (sub doesn't have a docking port?)
|
||||
// -> choose a destination when the sub is far enough from the start outpost
|
||||
if (!Submarine.MainSub.AtStartPosition)
|
||||
if (!Submarine.MainSub.AtStartExit)
|
||||
{
|
||||
ForceMapUI = true;
|
||||
if (CampaignUI == null) { InitCampaignUI(); }
|
||||
@@ -437,8 +454,10 @@ namespace Barotrauma
|
||||
{
|
||||
ShowCampaignUI = false;
|
||||
}
|
||||
HintManager.OnAvailableTransition(transitionType);
|
||||
}
|
||||
}
|
||||
|
||||
public override void End(TransitionType transitionType = TransitionType.None)
|
||||
{
|
||||
base.End(transitionType);
|
||||
@@ -496,7 +515,7 @@ namespace Barotrauma
|
||||
var transition = new CameraTransition(endObject ?? Submarine.MainSub, GameMain.GameScreen.Cam,
|
||||
null, Alignment.Center,
|
||||
fadeOut: true,
|
||||
duration: 10,
|
||||
panDuration: 10,
|
||||
startZoom: null, endZoom: 0.2f);
|
||||
|
||||
while (transition.Running)
|
||||
@@ -649,7 +668,7 @@ namespace Barotrauma
|
||||
{
|
||||
string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer);
|
||||
|
||||
GameMain.GameSession = new GameSession(null, savePath, GameModePreset.MultiPlayerCampaign, mapSeed);
|
||||
GameMain.GameSession = new GameSession(null, savePath, GameModePreset.MultiPlayerCampaign, CampaignSettings.Unsure, mapSeed);
|
||||
campaign = (MultiPlayerCampaign)GameMain.GameSession.GameMode;
|
||||
campaign.CampaignID = campaignID;
|
||||
GameMain.NetLobbyScreen.ToggleCampaignMode(true);
|
||||
@@ -711,13 +730,20 @@ namespace Barotrauma
|
||||
DebugConsole.ThrowError($"Error when receiving campaign data from the server: mission prefab \"{availableMission.First}\" not found.");
|
||||
continue;
|
||||
}
|
||||
if (availableMission.Second < 0 || availableMission.Second >= campaign.Map.CurrentLocation.Connections.Count)
|
||||
if (availableMission.Second == 255)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error when receiving campaign data from the server: connection index for mission \"{availableMission.First}\" out of range (index: {availableMission.Second}, current location: {campaign.Map.CurrentLocation.Name}, connections: {campaign.Map.CurrentLocation.Connections.Count}).");
|
||||
continue;
|
||||
campaign.Map.CurrentLocation.UnlockMission(missionPrefab);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (availableMission.Second < 0 || availableMission.Second >= campaign.Map.CurrentLocation.Connections.Count)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error when receiving campaign data from the server: connection index for mission \"{availableMission.First}\" out of range (index: {availableMission.Second}, current location: {campaign.Map.CurrentLocation.Name}, connections: {campaign.Map.CurrentLocation.Connections.Count}).");
|
||||
continue;
|
||||
}
|
||||
LocationConnection connection = campaign.Map.CurrentLocation.Connections[availableMission.Second];
|
||||
campaign.Map.CurrentLocation.UnlockMission(missionPrefab, connection);
|
||||
}
|
||||
LocationConnection connection = campaign.Map.CurrentLocation.Connections[availableMission.Second];
|
||||
campaign.Map.CurrentLocation.UnlockMission(missionPrefab, connection);
|
||||
}
|
||||
|
||||
GameMain.NetLobbyScreen.ToggleCampaignMode(true);
|
||||
@@ -770,16 +796,29 @@ namespace Barotrauma
|
||||
{
|
||||
pendingHires.Add(msg.ReadInt32());
|
||||
}
|
||||
|
||||
bool validateHires = msg.ReadBoolean();
|
||||
|
||||
ushort hiredLength = msg.ReadUInt16();
|
||||
List<CharacterInfo> hiredCharacters = new List<CharacterInfo>();
|
||||
for (int i = 0; i < hiredLength; i++)
|
||||
{
|
||||
CharacterInfo hired = CharacterInfo.ClientRead("human", msg);
|
||||
hired.Salary = msg.ReadInt32();
|
||||
hiredCharacters.Add(hired);
|
||||
}
|
||||
|
||||
bool renameCrewMember = msg.ReadBoolean();
|
||||
if (renameCrewMember)
|
||||
{
|
||||
int renamedIdentifier = msg.ReadInt32();
|
||||
string newName = msg.ReadString();
|
||||
CharacterInfo renamedCharacter = CrewManager.CharacterInfos.FirstOrDefault(info => info.GetIdentifierUsingOriginalName() == renamedIdentifier);
|
||||
if (renamedCharacter != null) { CrewManager.RenameCharacter(renamedCharacter, newName); }
|
||||
}
|
||||
|
||||
bool fireCharacter = msg.ReadBoolean();
|
||||
|
||||
int firedIdentifier = -1;
|
||||
if (fireCharacter) { firedIdentifier = msg.ReadInt32(); }
|
||||
|
||||
if (fireCharacter)
|
||||
{
|
||||
int firedIdentifier = msg.ReadInt32();
|
||||
CharacterInfo firedCharacter = CrewManager.CharacterInfos.FirstOrDefault(info => info.GetIdentifier() == firedIdentifier);
|
||||
// this one might and is allowed to be null since the character is already fired on the original sender's game
|
||||
if (firedCharacter != null) { CrewManager.FireCharacter(firedCharacter); }
|
||||
@@ -787,10 +826,10 @@ namespace Barotrauma
|
||||
|
||||
if (map?.CurrentLocation?.HireManager != null && CampaignUI?.CrewManagement != null)
|
||||
{
|
||||
CampaignUI?.CrewManagement?.SetHireables(map.CurrentLocation, availableHires);
|
||||
if (validateHires) { CampaignUI?.CrewManagement.ValidatePendingHires(); }
|
||||
CampaignUI?.CrewManagement?.SetPendingHires(pendingHires, map?.CurrentLocation);
|
||||
if (fireCharacter) { CampaignUI?.CrewManagement.UpdateCrew(); }
|
||||
CampaignUI.CrewManagement.SetHireables(map.CurrentLocation, availableHires);
|
||||
if (hiredCharacters.Any()) { CampaignUI.CrewManagement.ValidateHires(hiredCharacters); }
|
||||
CampaignUI.CrewManagement.SetPendingHires(pendingHires, map.CurrentLocation);
|
||||
if (renameCrewMember || fireCharacter) { CampaignUI.CrewManagement.UpdateCrew(); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (CoroutineManager.IsCoroutineRunning("LevelTransition") || CoroutineManager.IsCoroutineRunning("SubmarineTransition") || gameOver) { return; }
|
||||
|
||||
if (PlayerInput.RightButtonClicked() ||
|
||||
if (PlayerInput.SecondaryMouseButtonClicked() ||
|
||||
PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape))
|
||||
{
|
||||
ShowCampaignUI = false;
|
||||
@@ -57,11 +57,12 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// Instantiates a new single player campaign
|
||||
/// </summary>
|
||||
private SinglePlayerCampaign(string mapSeed) : base(GameModePreset.SinglePlayerCampaign)
|
||||
private SinglePlayerCampaign(string mapSeed, CampaignSettings settings) : base(GameModePreset.SinglePlayerCampaign)
|
||||
{
|
||||
CampaignMetadata = new CampaignMetadata(this);
|
||||
UpgradeManager = new UpgradeManager(this);
|
||||
map = new Map(this, mapSeed);
|
||||
map = new Map(this, mapSeed, settings);
|
||||
Settings = settings;
|
||||
foreach (JobPrefab jobPrefab in JobPrefab.Prefabs)
|
||||
{
|
||||
for (int i = 0; i < jobPrefab.InitialCount; i++)
|
||||
@@ -85,11 +86,14 @@ namespace Barotrauma
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "campaignsettings":
|
||||
Settings = new CampaignSettings(subElement);
|
||||
break;
|
||||
case "crew":
|
||||
GameMain.GameSession.CrewManager = new CrewManager(subElement, true);
|
||||
break;
|
||||
case "map":
|
||||
map = Map.Load(this, subElement);
|
||||
map = Map.Load(this, subElement, Settings);
|
||||
break;
|
||||
case "metadata":
|
||||
CampaignMetadata = new CampaignMetadata(this, subElement);
|
||||
@@ -141,9 +145,9 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// Start a completely new single player campaign
|
||||
/// </summary>
|
||||
public static SinglePlayerCampaign StartNew(string mapSeed, SubmarineInfo selectedSub)
|
||||
public static SinglePlayerCampaign StartNew(string mapSeed, SubmarineInfo selectedSub, CampaignSettings settings)
|
||||
{
|
||||
var campaign = new SinglePlayerCampaign(mapSeed);
|
||||
var campaign = new SinglePlayerCampaign(mapSeed, settings);
|
||||
return campaign;
|
||||
}
|
||||
|
||||
@@ -213,6 +217,7 @@ namespace Barotrauma
|
||||
|
||||
if (!savedOnStart)
|
||||
{
|
||||
GUI.SetSavingIndicatorState(true);
|
||||
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
|
||||
savedOnStart = true;
|
||||
}
|
||||
@@ -224,6 +229,8 @@ namespace Barotrauma
|
||||
{
|
||||
PetBehavior.LoadPets(petsElement);
|
||||
}
|
||||
|
||||
GUI.DisableSavingIndicatorDelayed();
|
||||
}
|
||||
|
||||
protected override void LoadInitialLevel()
|
||||
@@ -290,15 +297,29 @@ namespace Barotrauma
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (GameMain.GameSession == null)
|
||||
{
|
||||
GUI.DisableHUD = false;
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
overlayTextColor = Color.Lerp(Color.Transparent, Color.White, (timer - 1.0f) / fadeInDuration);
|
||||
timer = Math.Min(timer + CoroutineManager.DeltaTime, textDuration);
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
var outpost = GameMain.GameSession.Level.StartOutpost;
|
||||
var borders = outpost.GetDockedBorders();
|
||||
borders.Location += outpost.WorldPosition.ToPoint();
|
||||
GameMain.GameScreen.Cam.Position = new Vector2(borders.X + borders.Width / 2, borders.Y - borders.Height / 2);
|
||||
float startZoom = 0.8f /
|
||||
((float)Math.Max(borders.Width, borders.Height) / (float)GameMain.GameScreen.Cam.Resolution.X);
|
||||
GameMain.GameScreen.Cam.MinZoom = Math.Min(startZoom, GameMain.GameScreen.Cam.MinZoom);
|
||||
var transition = new CameraTransition(prevControlled, GameMain.GameScreen.Cam,
|
||||
null, null,
|
||||
fadeOut: false,
|
||||
duration: 5,
|
||||
startZoom: 1.5f, endZoom: 1.0f)
|
||||
losFadeIn: true,
|
||||
waitDuration: 1,
|
||||
panDuration: 5,
|
||||
startZoom: startZoom, endZoom: 1.0f)
|
||||
{
|
||||
AllowInterrupt = true,
|
||||
RemoveControlFromCharacter = false
|
||||
@@ -323,19 +344,13 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
ISpatialEntity transitionTarget;
|
||||
if (prevControlled != null)
|
||||
{
|
||||
transitionTarget = prevControlled;
|
||||
}
|
||||
else
|
||||
{
|
||||
transitionTarget = Submarine.MainSub;
|
||||
}
|
||||
transitionTarget = (ISpatialEntity)prevControlled ?? Submarine.MainSub;
|
||||
|
||||
var transition = new CameraTransition(transitionTarget, GameMain.GameScreen.Cam,
|
||||
null, null,
|
||||
fadeOut: false,
|
||||
duration: 5,
|
||||
losFadeIn: prevControlled != null,
|
||||
panDuration: 5,
|
||||
startZoom: 0.5f, endZoom: 1.0f)
|
||||
{
|
||||
AllowInterrupt = true,
|
||||
@@ -370,11 +385,9 @@ namespace Barotrauma
|
||||
bool success = CrewManager.GetCharacters().Any(c => !c.IsDead);
|
||||
SoundPlayer.OverrideMusicType = success ? "endround" : "crewdead";
|
||||
SoundPlayer.OverrideMusicDuration = 18.0f;
|
||||
GUI.SetSavingIndicatorState(success);
|
||||
crewDead = false;
|
||||
|
||||
LevelData lvlData = GameMain.GameSession.LevelData;
|
||||
bool beaconActive = GameMain.GameSession.Level.CheckBeaconActive();
|
||||
|
||||
GameMain.GameSession.EndRound("", traitorResults, transitionType);
|
||||
var continueButton = GameMain.GameSession.RoundSummary?.ContinueButton;
|
||||
RoundSummary roundSummary = null;
|
||||
@@ -408,13 +421,13 @@ namespace Barotrauma
|
||||
var endTransition = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam, null,
|
||||
transitionType == TransitionType.LeaveLocation ? Alignment.BottomCenter : Alignment.Center,
|
||||
fadeOut: false,
|
||||
duration: EndTransitionDuration);
|
||||
panDuration: EndTransitionDuration);
|
||||
|
||||
GUI.ClearMessages();
|
||||
|
||||
Location portraitLocation = Map.SelectedLocation ?? Map.CurrentLocation;
|
||||
overlaySprite = portraitLocation.Type.GetPortrait(portraitLocation.PortraitId);
|
||||
float fadeOutDuration = endTransition.Duration;
|
||||
float fadeOutDuration = endTransition.PanDuration;
|
||||
float t = 0.0f;
|
||||
while (t < fadeOutDuration || endTransition.Running)
|
||||
{
|
||||
@@ -459,8 +472,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
lvlData.IsBeaconActive = beaconActive;
|
||||
|
||||
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
|
||||
}
|
||||
else
|
||||
@@ -484,6 +495,7 @@ namespace Barotrauma
|
||||
overlayColor = Color.Transparent;
|
||||
});
|
||||
|
||||
GUI.SetSavingIndicatorState(false);
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
@@ -510,7 +522,7 @@ namespace Barotrauma
|
||||
var transition = new CameraTransition(endObject ?? Submarine.MainSub, GameMain.GameScreen.Cam,
|
||||
null, Alignment.Center,
|
||||
fadeOut: true,
|
||||
duration: 10,
|
||||
panDuration: 10,
|
||||
startZoom: null, endZoom: 0.2f);
|
||||
|
||||
while (transition.Running)
|
||||
@@ -530,6 +542,8 @@ namespace Barotrauma
|
||||
if (CoroutineManager.IsCoroutineRunning("LevelTransition") || CoroutineManager.IsCoroutineRunning("SubmarineTransition") || gameOver) { return; }
|
||||
|
||||
base.Update(deltaTime);
|
||||
|
||||
Map?.Radiation?.UpdateRadiation(deltaTime);
|
||||
|
||||
if (PlayerInput.SecondaryMouseButtonClicked() ||
|
||||
PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape))
|
||||
@@ -583,7 +597,7 @@ namespace Barotrauma
|
||||
{
|
||||
//wasn't initially docked (sub doesn't have a docking port?)
|
||||
// -> choose a destination when the sub is far enough from the start outpost
|
||||
if (!Submarine.MainSub.AtStartPosition)
|
||||
if (!Submarine.MainSub.AtStartExit)
|
||||
{
|
||||
ForceMapUI = true;
|
||||
CampaignUI.SelectTab(InteractionType.Map);
|
||||
@@ -611,6 +625,7 @@ namespace Barotrauma
|
||||
{
|
||||
ShowCampaignUI = false;
|
||||
}
|
||||
HintManager.OnAvailableTransition(transitionType);
|
||||
}
|
||||
|
||||
if (!crewDead)
|
||||
@@ -632,9 +647,9 @@ namespace Barotrauma
|
||||
if (nextLevel == null)
|
||||
{
|
||||
//no level selected -> force the player to select one
|
||||
ForceMapUI = true;
|
||||
CampaignUI.SelectTab(InteractionType.Map);
|
||||
map.SelectLocation(-1);
|
||||
ForceMapUI = true;
|
||||
return false;
|
||||
}
|
||||
else if (transitionType == TransitionType.ProgressToNextEmptyLocation)
|
||||
@@ -707,6 +722,7 @@ namespace Barotrauma
|
||||
new XAttribute("purchasedhullrepairs", PurchasedHullRepairs),
|
||||
new XAttribute("purchaseditemrepairs", PurchasedItemRepairs),
|
||||
new XAttribute("cheatsenabled", CheatsEnabled));
|
||||
modeElement.Add(Settings.Save());
|
||||
|
||||
//save and remove all items that are in someone's inventory so they don't get included in the sub file as well
|
||||
foreach (Character c in Character.CharacterList)
|
||||
|
||||
@@ -614,7 +614,7 @@ namespace Barotrauma.Tutorials
|
||||
GameMain.GameScreen.Cam.TargetPos = Vector2.Zero;
|
||||
GameMain.LightManager.LosEnabled = false;
|
||||
|
||||
var cinematic = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam, Alignment.CenterLeft, Alignment.CenterRight, duration: 5.0f);
|
||||
var cinematic = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam, Alignment.CenterLeft, Alignment.CenterRight, panDuration: 5.0f);
|
||||
|
||||
while (cinematic.Running)
|
||||
{
|
||||
|
||||
@@ -99,7 +99,7 @@ namespace Barotrauma.Tutorials
|
||||
captain_medicSpawnPos = Item.ItemList.Find(i => i.HasTag("captain_medicspawnpos")).WorldPosition;
|
||||
tutorial_submarineDoor = Item.ItemList.Find(i => i.HasTag("tutorial_submarinedoor")).GetComponent<Door>();
|
||||
tutorial_submarineDoorLight = Item.ItemList.Find(i => i.HasTag("tutorial_submarinedoorlight")).GetComponent<LightComponent>();
|
||||
var medicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("medicaldoctor"));
|
||||
var medicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("medicaldoctor"));
|
||||
captain_medic = Character.Create(medicInfo, captain_medicSpawnPos, "medicaldoctor");
|
||||
captain_medic.TeamID = CharacterTeamType.Team1;
|
||||
captain_medic.GiveJobItems(null);
|
||||
@@ -122,17 +122,17 @@ namespace Barotrauma.Tutorials
|
||||
SetDoorAccess(tutorial_lockedDoor_1, null, false);
|
||||
SetDoorAccess(tutorial_lockedDoor_2, null, false);
|
||||
|
||||
var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("mechanic"));
|
||||
var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("mechanic"));
|
||||
captain_mechanic = Character.Create(mechanicInfo, WayPoint.GetRandom(SpawnType.Human, mechanicInfo.Job, Submarine.MainSub).WorldPosition, "mechanic");
|
||||
captain_mechanic.TeamID = CharacterTeamType.Team1;
|
||||
captain_mechanic.GiveJobItems();
|
||||
|
||||
var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("securityofficer"));
|
||||
var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("securityofficer"));
|
||||
captain_security = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job, Submarine.MainSub).WorldPosition, "securityofficer");
|
||||
captain_security.TeamID = CharacterTeamType.Team1;
|
||||
captain_security.GiveJobItems();
|
||||
|
||||
var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer"));
|
||||
var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("engineer"));
|
||||
captain_engineer = Character.Create(engineerInfo, WayPoint.GetRandom(SpawnType.Human, engineerInfo.Job, Submarine.MainSub).WorldPosition, "engineer");
|
||||
captain_engineer.TeamID = CharacterTeamType.Team1;
|
||||
captain_engineer.GiveJobItems();
|
||||
@@ -249,7 +249,7 @@ namespace Barotrauma.Tutorials
|
||||
{
|
||||
//captain_navConsoleCustomInterface.HighlightElement(0, uiHighlightColor, duration: 1.0f, pulsateAmount: 0.0f);
|
||||
yield return new WaitForSeconds(1.0f, false);
|
||||
} while (!Submarine.MainSub.AtEndPosition || !Submarine.MainSub.DockedTo.Any());
|
||||
} while (!Submarine.MainSub.AtEndExit || !Submarine.MainSub.DockedTo.Any());
|
||||
RemoveCompletedObjective(segments[6]);
|
||||
yield return new WaitForSeconds(3f, false);
|
||||
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.GetWithVariable("Captain.Radio.Complete", "[OUTPOSTNAME]", GameMain.GameSession.EndLocation.Name), ChatMessageType.Radio, null);
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace Barotrauma.Tutorials
|
||||
var patientHull2 = WayPoint.WayPointList.Find(wp => wp.IdCardDesc == "airlock").CurrentHull;
|
||||
medBay = WayPoint.WayPointList.Find(wp => wp.IdCardDesc == "medbay").CurrentHull;
|
||||
|
||||
var assistantInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("assistant"));
|
||||
var assistantInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("assistant"));
|
||||
patient1 = Character.Create(assistantInfo, patientHull1.WorldPosition, "1");
|
||||
patient1.TeamID = CharacterTeamType.Team1;
|
||||
patient1.GiveJobItems(null);
|
||||
@@ -86,26 +86,26 @@ namespace Barotrauma.Tutorials
|
||||
patient1.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 15.0f) }, stun: 0, playSound: false);
|
||||
patient1.AIController.Enabled = false;
|
||||
|
||||
assistantInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("assistant"));
|
||||
assistantInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("assistant"));
|
||||
patient2 = Character.Create(assistantInfo, patientHull2.WorldPosition, "2");
|
||||
patient2.TeamID = CharacterTeamType.Team1;
|
||||
patient2.GiveJobItems(null);
|
||||
patient2.CanSpeak = false;
|
||||
patient2.AIController.Enabled = false;
|
||||
|
||||
var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer"));
|
||||
var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("engineer"));
|
||||
var subPatient1 = Character.Create(mechanicInfo, WayPoint.GetRandom(SpawnType.Human, mechanicInfo.Job, Submarine.MainSub).WorldPosition, "3");
|
||||
subPatient1.TeamID = CharacterTeamType.Team1;
|
||||
subPatient1.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 40.0f) }, stun: 0, playSound: false);
|
||||
subPatients.Add(subPatient1);
|
||||
|
||||
var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("securityofficer"));
|
||||
var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("securityofficer"));
|
||||
var subPatient2 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job, Submarine.MainSub).WorldPosition, "3");
|
||||
subPatient2.TeamID = CharacterTeamType.Team1;
|
||||
subPatient2.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.InternalDamage, 40.0f) }, stun: 0, playSound: false);
|
||||
subPatients.Add(subPatient2);
|
||||
|
||||
var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer"));
|
||||
var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("engineer"));
|
||||
var subPatient3 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, engineerInfo.Job, Submarine.MainSub).WorldPosition, "3");
|
||||
subPatient3.TeamID = CharacterTeamType.Team1;
|
||||
subPatient3.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 20.0f) }, stun: 0, playSound: false);
|
||||
@@ -283,7 +283,7 @@ namespace Barotrauma.Tutorials
|
||||
doctor.RemoveActiveObjectiveEntity(patient1);
|
||||
TriggerTutorialSegment(3, GameMain.Config.KeyBindText(InputType.Command)); // Get the patient to medbay
|
||||
|
||||
while (patient1.CurrentOrder == null || patient1.CurrentOrder.Identifier != "follow")
|
||||
while (patient1.GetCurrentOrderWithTopPriority()?.Order?.Identifier != "follow")
|
||||
{
|
||||
// TODO: Rework order highlighting for new command UI
|
||||
// GameMain.GameSession.CrewManager.HighlightOrderButton(patient1, "follow", highlightColor, new Vector2(5, 5));
|
||||
|
||||
@@ -317,7 +317,8 @@ namespace Barotrauma.Tutorials
|
||||
yield return new WaitForSeconds(2f, false);
|
||||
TriggerTutorialSegment(4, GameMain.Config.KeyBindText(InputType.Select), GameMain.Config.KeyBindText(InputType.Shoot), GameMain.Config.KeyBindText(InputType.Deselect)); // Kill hammerhead
|
||||
officer_hammerhead = SpawnMonster("hammerhead", officer_hammerheadSpawnPos);
|
||||
((EnemyAIController)officer_hammerhead.AIController).StayInsideLevel = false;
|
||||
officer_hammerhead.Params.AI.AvoidAbyss = false;
|
||||
officer_hammerhead.Params.AI.StayInAbyss = false;
|
||||
officer_hammerhead.AIController.SelectTarget(officer.AiTarget);
|
||||
SetHighlight(officer_coilgunPeriscope, true);
|
||||
float originalDistance = Vector2.Distance(officer_coilgunPeriscope.WorldPosition, officer_hammerheadSpawnPos);
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace Barotrauma.Tutorials
|
||||
|
||||
yield return CoroutineStatus.Running;
|
||||
|
||||
GameMain.GameSession = new GameSession(subInfo, GameModePreset.Tutorial, missionPrefab: null);
|
||||
GameMain.GameSession = new GameSession(subInfo, GameModePreset.Tutorial, missionPrefabs: null);
|
||||
(GameMain.GameSession.GameMode as TutorialMode).Tutorial = this;
|
||||
|
||||
if (generationParams != null)
|
||||
@@ -110,7 +110,7 @@ namespace Barotrauma.Tutorials
|
||||
}
|
||||
|
||||
CharacterInfo charInfo = configElement.Element("Character") == null ?
|
||||
new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer")) :
|
||||
new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("engineer")) :
|
||||
new CharacterInfo(configElement.Element("Character"));
|
||||
|
||||
WayPoint wayPoint = GetSpawnPoint(charInfo);
|
||||
@@ -182,7 +182,8 @@ namespace Barotrauma.Tutorials
|
||||
|
||||
protected bool HasOrder(Character character, string identifier, string option = null)
|
||||
{
|
||||
if (character.CurrentOrder?.Identifier == identifier)
|
||||
var currentOrderInfo = character.GetCurrentOrderWithTopPriority();
|
||||
if (currentOrderInfo?.Order?.Identifier == identifier)
|
||||
{
|
||||
if (option == null)
|
||||
{
|
||||
@@ -190,8 +191,7 @@ namespace Barotrauma.Tutorials
|
||||
}
|
||||
else
|
||||
{
|
||||
HumanAIController humanAI = character.AIController as HumanAIController;
|
||||
return humanAI.CurrentOrderOption == option;
|
||||
return currentOrderInfo?.OrderOption == option;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,7 +288,7 @@ namespace Barotrauma.Tutorials
|
||||
|
||||
yield return new WaitForSeconds(waitBeforeFade);
|
||||
|
||||
var endCinematic = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam, null, Alignment.Center, duration: fadeOutTime);
|
||||
var endCinematic = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam, null, Alignment.Center, panDuration: fadeOutTime);
|
||||
currentTutorialCompleted = Completed = true;
|
||||
while (endCinematic.Running) yield return null;
|
||||
Stop();
|
||||
|
||||
@@ -535,15 +535,15 @@ namespace Barotrauma.Tutorials
|
||||
titleBlock.RectTransform.IsFixedSize = true;
|
||||
}
|
||||
|
||||
List<RichTextData> richTextData = RichTextData.GetRichTextData(text, out text);
|
||||
List<RichTextData> richTextData = RichTextData.GetRichTextData(" " + text, out text);
|
||||
GUITextBlock textBlock;
|
||||
if (richTextData == null)
|
||||
{
|
||||
textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContent.RectTransform), " " + text, wrap: true);
|
||||
textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContent.RectTransform), text, wrap: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContent.RectTransform), richTextData, " " + text, wrap: true);
|
||||
textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContent.RectTransform), richTextData, text, wrap: true);
|
||||
}
|
||||
|
||||
textBlock.RectTransform.IsFixedSize = true;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -26,6 +27,7 @@ namespace Barotrauma
|
||||
if (tabMenu == null && GameMode is TutorialMode == false)
|
||||
{
|
||||
tabMenu = new TabMenu();
|
||||
HintManager.OnShowTabMenu();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -36,12 +38,106 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
|
||||
private GUILayoutGroup topLeftButtonGroup;
|
||||
private GUIButton crewListButton, commandButton, tabMenuButton;
|
||||
|
||||
private GUIComponent respawnInfoFrame, respawnButtonContainer;
|
||||
private GUITextBlock respawnInfoText;
|
||||
private GUITickBox respawnTickBox;
|
||||
private GUILayoutGroup TopLeftButtonGroup;
|
||||
private void CreateTopLeftButtons()
|
||||
{
|
||||
if (topLeftButtonGroup != null)
|
||||
{
|
||||
topLeftButtonGroup.RectTransform.Parent = null;
|
||||
topLeftButtonGroup = null;
|
||||
crewListButton = commandButton = tabMenuButton = null;
|
||||
}
|
||||
topLeftButtonGroup = new GUILayoutGroup(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.ButtonAreaTop, GUI.Canvas), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
AbsoluteSpacing = HUDLayoutSettings.Padding,
|
||||
CanBeFocused = false
|
||||
};
|
||||
topLeftButtonGroup.RectTransform.ParentChanged += (_) =>
|
||||
{
|
||||
GameMain.Instance.ResolutionChanged -= CreateTopLeftButtons;
|
||||
};
|
||||
int buttonHeight = GUI.IntScale(40);
|
||||
Vector2 buttonSpriteSize = GUI.Style.GetComponentStyle("CrewListToggleButton").GetDefaultSprite().size;
|
||||
int buttonWidth = (int)((buttonHeight / buttonSpriteSize.Y) * buttonSpriteSize.X);
|
||||
Point buttonSize = new Point(buttonWidth, buttonHeight);
|
||||
crewListButton = new GUIButton(new RectTransform(buttonSize, parent: topLeftButtonGroup.RectTransform), style: "CrewListToggleButton")
|
||||
{
|
||||
ToolTip = TextManager.GetWithVariable("hudbutton.crewlist", "[key]", GameMain.Config.KeyBindText(InputType.CrewOrders)),
|
||||
OnClicked = (GUIButton btn, object userdata) =>
|
||||
{
|
||||
if (CrewManager == null) { return false; }
|
||||
CrewManager.IsCrewMenuOpen = !CrewManager.IsCrewMenuOpen;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
commandButton = new GUIButton(new RectTransform(buttonSize, parent: topLeftButtonGroup.RectTransform), style: "CommandButton")
|
||||
{
|
||||
ToolTip = TextManager.GetWithVariable("hudbutton.commandinterface", "[key]", GameMain.Config.KeyBindText(InputType.Command)),
|
||||
OnClicked = (button, userData) =>
|
||||
{
|
||||
if (CrewManager == null) { return false; }
|
||||
CrewManager.ToggleCommandUI();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
tabMenuButton = new GUIButton(new RectTransform(buttonSize, parent: topLeftButtonGroup.RectTransform), style: "TabMenuButton")
|
||||
{
|
||||
ToolTip = TextManager.GetWithVariable("hudbutton.tabmenu", "[key]", GameMain.Config.KeyBindText(InputType.InfoTab)),
|
||||
OnClicked = (button, userData) =>
|
||||
{
|
||||
return ToggleTabMenu();
|
||||
}
|
||||
};
|
||||
GameMain.Instance.ResolutionChanged += CreateTopLeftButtons;
|
||||
|
||||
respawnInfoFrame = new GUIFrame(new RectTransform(new Vector2(0.5f, 1.0f), parent: topLeftButtonGroup.RectTransform)
|
||||
{ MaxSize = new Point(HUDLayoutSettings.ButtonAreaTop.Width / 3, int.MaxValue) }, style: null)
|
||||
{
|
||||
Visible = false
|
||||
};
|
||||
respawnInfoText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), respawnInfoFrame.RectTransform), "", wrap: true);
|
||||
respawnButtonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), respawnInfoFrame.RectTransform, Anchor.CenterRight), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
AbsoluteSpacing = HUDLayoutSettings.Padding,
|
||||
Stretch = true
|
||||
};
|
||||
respawnTickBox = new GUITickBox(new RectTransform(Vector2.One * 0.9f, respawnButtonContainer.RectTransform, Anchor.Center), TextManager.Get("respawnquestionpromptrespawn"))
|
||||
{
|
||||
ToolTip = TextManager.Get("respawnquestionprompt"),
|
||||
OnSelected = (tickbox) =>
|
||||
{
|
||||
GameMain.Client?.SendRespawnPromptResponse(waitForNextRoundRespawn: !tickbox.Selected);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void AddToGUIUpdateList()
|
||||
{
|
||||
if (GUI.DisableHUD) return;
|
||||
if (GUI.DisableHUD) { return; }
|
||||
GameMode?.AddToGUIUpdateList();
|
||||
tabMenu?.AddToGUIUpdateList();
|
||||
|
||||
if ((!(GameMode is CampaignMode campaign) || (!campaign.ForceMapUI && !campaign.ShowCampaignUI)) &&
|
||||
!CoroutineManager.IsCoroutineRunning("LevelTransition") && !CoroutineManager.IsCoroutineRunning("SubmarineTransition"))
|
||||
{
|
||||
if (topLeftButtonGroup == null)
|
||||
{
|
||||
CreateTopLeftButtons();
|
||||
}
|
||||
crewListButton.Selected = CrewManager != null && CrewManager.IsCrewMenuOpen;
|
||||
commandButton.Selected = CrewManager.IsCommandInterfaceOpen;
|
||||
commandButton.Enabled = CrewManager.CanIssueOrders;
|
||||
tabMenuButton.Selected = IsTabMenuOpen;
|
||||
topLeftButtonGroup.AddToGUIUpdateList();
|
||||
}
|
||||
|
||||
if (GameMain.NetworkMember != null)
|
||||
{
|
||||
GameMain.NetLobbyScreen?.HeadSelectionList?.AddToGUIUpdateList();
|
||||
@@ -55,7 +151,7 @@ namespace Barotrauma
|
||||
|
||||
if (tabMenu == null)
|
||||
{
|
||||
if (PlayerInput.KeyHit(InputType.InfoTab) && GUI.KeyboardDispatcher.Subscriber is GUITextBox == false)
|
||||
if (PlayerInput.KeyHit(InputType.InfoTab) && !(GUI.KeyboardDispatcher.Subscriber is GUITextBox))
|
||||
{
|
||||
ToggleTabMenu();
|
||||
}
|
||||
@@ -63,8 +159,8 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
tabMenu.Update();
|
||||
|
||||
if (PlayerInput.KeyHit(InputType.InfoTab) && GUI.KeyboardDispatcher.Subscriber is GUITextBox == false)
|
||||
if ((PlayerInput.KeyHit(InputType.InfoTab) || PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape)) &&
|
||||
!(GUI.KeyboardDispatcher.Subscriber is GUITextBox))
|
||||
{
|
||||
ToggleTabMenu();
|
||||
}
|
||||
@@ -88,6 +184,19 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HintManager.Update();
|
||||
}
|
||||
|
||||
public void SetRespawnInfo(bool visible, string text, Color textColor, bool buttonsVisible, bool waitForNextRoundRespawn)
|
||||
{
|
||||
if (topLeftButtonGroup == null) { return; }
|
||||
respawnInfoFrame.Visible = visible;
|
||||
if (!visible) { return; }
|
||||
respawnInfoText.Text = text;
|
||||
respawnInfoText.TextColor = textColor;
|
||||
respawnButtonContainer.Visible = buttonsVisible;
|
||||
respawnTickBox.Selected = !waitForNextRoundRespawn;
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch)
|
||||
|
||||
@@ -0,0 +1,731 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.IO;
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
static class HintManager
|
||||
{
|
||||
private const string HintManagerFile = "hintmanager.xml";
|
||||
private static HashSet<string> HintIdentifiers { get; set; }
|
||||
private static Dictionary<string, HashSet<string>> HintTags { get; } = new Dictionary<string, HashSet<string>>();
|
||||
private static Dictionary<string, (string identifier, string option)> HintOrders { get; } = new Dictionary<string, (string orderIdentifier, string orderOption)>();
|
||||
/// <summary>
|
||||
/// Hints that have already been shown this round and shouldn't be shown shown again until the next round
|
||||
/// </summary>
|
||||
private static HashSet<string> HintsIgnoredThisRound { get; } = new HashSet<string>();
|
||||
private static GUIMessageBox ActiveHintMessageBox { get; set; }
|
||||
private static Action OnUpdate { get; set; }
|
||||
private static double TimeStoppedInteracting { get; set; }
|
||||
private static double TimeRoundStarted { get; set; }
|
||||
/// <summary>
|
||||
/// Seconds before any reminders can be shown
|
||||
/// </summary>
|
||||
private static int TimeBeforeReminders { get; set; }
|
||||
/// <summary>
|
||||
/// Seconds before another reminder can be shown
|
||||
/// </summary>
|
||||
private static int ReminderCooldown { get; set; }
|
||||
private static double TimeReminderLastDisplayed { get; set; }
|
||||
private static HashSet<Hull> BallastHulls { get; } = new HashSet<Hull>();
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
if (File.Exists(HintManagerFile))
|
||||
{
|
||||
var doc = XMLExtensions.TryLoadXml(HintManagerFile);
|
||||
if (doc?.Root != null)
|
||||
{
|
||||
HintIdentifiers = new HashSet<string>();
|
||||
foreach (var element in doc.Root.Elements())
|
||||
{
|
||||
GetHintsRecursive(element, element.Name.ToString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"File \"{HintManagerFile}\" is empty - cannot initialize the HintManager!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"File \"{HintManagerFile}\" is missing - cannot initialize the HintManager!");
|
||||
}
|
||||
|
||||
static void GetHintsRecursive(XElement element, string identifier)
|
||||
{
|
||||
if (!element.HasElements)
|
||||
{
|
||||
HintIdentifiers.Add(identifier);
|
||||
if (element.GetAttributeStringArray("tags", null, convertToLowerInvariant: true) is string[] tags)
|
||||
{
|
||||
HintTags.TryAdd(identifier, tags.ToHashSet());
|
||||
}
|
||||
if (element.GetAttributeString("order", null) is string orderIdentifier && !string.IsNullOrEmpty(orderIdentifier))
|
||||
{
|
||||
string orderOption = element.GetAttributeString("orderoption", "");
|
||||
HintOrders.Add(identifier, (orderIdentifier, orderOption));
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (element.Name.ToString().Equals("reminder"))
|
||||
{
|
||||
TimeBeforeReminders = element.GetAttributeInt("timebeforereminders", TimeBeforeReminders);
|
||||
ReminderCooldown = element.GetAttributeInt("remindercooldown", ReminderCooldown);
|
||||
}
|
||||
foreach (var childElement in element.Elements())
|
||||
{
|
||||
GetHintsRecursive(childElement, $"{identifier}.{childElement.Name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Update()
|
||||
{
|
||||
if (HintIdentifiers == null || GameMain.Config.DisableInGameHints) { return; }
|
||||
if (GameMain.GameSession == null || !GameMain.GameSession.IsRunning) { return; }
|
||||
|
||||
if (ActiveHintMessageBox != null)
|
||||
{
|
||||
if (ActiveHintMessageBox.Closed)
|
||||
{
|
||||
ActiveHintMessageBox = null;
|
||||
OnUpdate = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
OnUpdate?.Invoke();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
CheckIsInteracting();
|
||||
CheckIfDivingGearOutOfOxygen();
|
||||
CheckHulls();
|
||||
CheckReminders();
|
||||
}
|
||||
|
||||
public static void OnSetSelectedConstruction(Character character, Item oldConstruction, Item newConstruction)
|
||||
{
|
||||
if (oldConstruction == newConstruction) { return; }
|
||||
|
||||
if (Character.Controlled != null && Character.Controlled == character && oldConstruction != null && oldConstruction.GetComponent<Ladder>() == null)
|
||||
{
|
||||
TimeStoppedInteracting = Timing.TotalTime;
|
||||
}
|
||||
|
||||
if (newConstruction == null) { return; }
|
||||
if (newConstruction.GetComponent<Ladder>() != null) { return; }
|
||||
if (newConstruction.GetComponent<ConnectionPanel>() is ConnectionPanel cp && cp.User == character) { return; }
|
||||
OnStartedInteracting(character, newConstruction);
|
||||
}
|
||||
|
||||
private static void OnStartedInteracting(Character character, Item item)
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
if (character != Character.Controlled || item == null) { return; }
|
||||
|
||||
string hintIdentifierBase = "onstartedinteracting";
|
||||
|
||||
// onstartedinteracting.brokenitem
|
||||
if (item.Repairables.Any(r => item.ConditionPercentage < r.RepairThreshold))
|
||||
{
|
||||
if (DisplayHint($"{hintIdentifierBase}.brokenitem")) { return; }
|
||||
}
|
||||
|
||||
// Don't display other item-related hints if the repair interface is displayed
|
||||
if (item.Repairables.Any(r => r.ShouldDrawHUD(character))) { return; }
|
||||
|
||||
// onstartedinteracting.lootingisstealing
|
||||
if (item.Submarine?.Info?.Type == SubmarineType.Outpost &&
|
||||
item.ContainedItems.Any(i => !i.AllowStealing))
|
||||
{
|
||||
if (DisplayHint($"{hintIdentifierBase}.lootingisstealing")) { return; }
|
||||
}
|
||||
|
||||
// onstartedinteracting.turretperiscope
|
||||
if (item.HasTag("periscope") &&
|
||||
item.GetConnectedComponents<Turret>().FirstOrDefault(t => t.Item.HasTag("turret")) is Turret)
|
||||
{
|
||||
if (DisplayHint($"{hintIdentifierBase}.turretperiscope",
|
||||
variableTags: new string[] { "[shootkey]", "[deselectkey]", },
|
||||
variableValues: new string[] { GameMain.Config.KeyBindText(InputType.Shoot), GameMain.Config.KeyBindText(InputType.Deselect) }))
|
||||
{ return; }
|
||||
}
|
||||
|
||||
// onstartedinteracting.item...
|
||||
hintIdentifierBase += ".item";
|
||||
foreach (string hintIdentifier in HintIdentifiers)
|
||||
{
|
||||
if (!hintIdentifier.StartsWith(hintIdentifierBase)) { continue; }
|
||||
if (!HintTags.TryGetValue(hintIdentifier, out var hintTags)) { continue; }
|
||||
if (!item.HasTag(hintTags)) { continue; }
|
||||
if (DisplayHint(hintIdentifier)) { return; }
|
||||
}
|
||||
}
|
||||
|
||||
private static void CheckIsInteracting()
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
if (Character.Controlled?.SelectedConstruction == null) { return; }
|
||||
|
||||
if (Character.Controlled.SelectedConstruction.GetComponent<Reactor>() is Reactor reactor && reactor.PowerOn &&
|
||||
Character.Controlled.SelectedConstruction.OwnInventory?.AllItems is IEnumerable<Item> containedItems &&
|
||||
containedItems.Count(i => i.HasTag("reactorfuel")) > 1)
|
||||
{
|
||||
if (DisplayHint("onisinteracting.reactorwithextrarods")) { return; }
|
||||
}
|
||||
}
|
||||
|
||||
public static void OnRoundStarted()
|
||||
{
|
||||
// Make sure everything's been reset properly, OnRoundEnded() isn't always called when exiting a game
|
||||
Reset();
|
||||
TimeRoundStarted = GameMain.GameScreen.GameTime;
|
||||
|
||||
var initRoundHandle = CoroutineManager.StartCoroutine(InitRound(), "HintManager.InitRound");
|
||||
if (!CanDisplayHints(requireGameScreen: false, requireControllingCharacter: false)) { return; }
|
||||
CoroutineManager.StartCoroutine(DisplayRoundStartedHints(initRoundHandle), "HintManager.DisplayRoundStartedHints");
|
||||
|
||||
static IEnumerable<object> InitRound()
|
||||
{
|
||||
while (Character.Controlled == null) { yield return CoroutineStatus.Running; }
|
||||
// Get the ballast hulls on round start not to find them again and again later
|
||||
BallastHulls.Clear();
|
||||
var sub = Submarine.MainSubs.FirstOrDefault(s => s != null && s.TeamID == Character.Controlled.TeamID);
|
||||
if (sub != null)
|
||||
{
|
||||
foreach (var item in sub.GetItems(true))
|
||||
{
|
||||
if (item.CurrentHull == null) { continue; }
|
||||
if (item.GetComponent<Pump>() == null) { continue; }
|
||||
if (!item.HasTag("ballast")) { continue; }
|
||||
BallastHulls.Add(item.CurrentHull);
|
||||
}
|
||||
}
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
static IEnumerable<object> DisplayRoundStartedHints(CoroutineHandle initRoundHandle)
|
||||
{
|
||||
while (GameMain.Instance.LoadingScreenOpen || Screen.Selected != GameMain.GameScreen ||
|
||||
CoroutineManager.IsCoroutineRunning(initRoundHandle) ||
|
||||
CoroutineManager.IsCoroutineRunning("LevelTransition") ||
|
||||
CoroutineManager.IsCoroutineRunning("SinglePlayerCampaign.DoInitialCameraTransition") ||
|
||||
CoroutineManager.IsCoroutineRunning("MultiPlayerCampaign.DoInitialCameraTransition") ||
|
||||
GUIMessageBox.VisibleBox != null || Character.Controlled == null)
|
||||
{
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
|
||||
OnStartedControlling();
|
||||
|
||||
while (ActiveHintMessageBox != null)
|
||||
{
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
|
||||
if (!GameMain.GameSession.GameMode.IsSinglePlayer &&
|
||||
GameMain.Config.VoiceSetting == GameSettings.VoiceMode.Disabled)
|
||||
{
|
||||
DisplayHint("onroundstarted.voipdisabled", onUpdate: () =>
|
||||
{
|
||||
if (GameMain.Config.VoiceSetting == GameSettings.VoiceMode.Disabled) { return; }
|
||||
ActiveHintMessageBox.Close();
|
||||
});
|
||||
}
|
||||
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
}
|
||||
|
||||
public static void OnRoundEnded()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
private static void Reset()
|
||||
{
|
||||
CoroutineManager.StopCoroutines("HintManager.InitRound");
|
||||
CoroutineManager.StopCoroutines("HintManager.DisplayRoundStartedHints");
|
||||
if (ActiveHintMessageBox != null)
|
||||
{
|
||||
GUIMessageBox.MessageBoxes.Remove(ActiveHintMessageBox);
|
||||
ActiveHintMessageBox = null;
|
||||
}
|
||||
OnUpdate = null;
|
||||
HintsIgnoredThisRound.Clear();
|
||||
}
|
||||
|
||||
public static void OnSonarSpottedCharacter(Item sonar, Character spottedCharacter)
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
if (sonar == null || sonar.Removed) { return; }
|
||||
if (spottedCharacter == null || spottedCharacter.Removed || spottedCharacter.IsDead) { return; }
|
||||
if (Character.Controlled.SelectedConstruction != sonar) { return; }
|
||||
if (HumanAIController.IsFriendly(Character.Controlled, spottedCharacter)) { return; }
|
||||
DisplayHint("onsonarspottedenemy");
|
||||
}
|
||||
|
||||
public static void OnAfflictionDisplayed(Character character, List<Affliction> displayedAfflictions)
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
if (character != Character.Controlled || displayedAfflictions == null) { return; }
|
||||
foreach (var affliction in displayedAfflictions)
|
||||
{
|
||||
if (affliction?.Prefab == null) { continue; }
|
||||
if (affliction.Prefab.IsBuff) { continue; }
|
||||
if (affliction.Prefab == AfflictionPrefab.OxygenLow) { continue; }
|
||||
if (affliction.Prefab == AfflictionPrefab.RadiationSickness && (GameMain.GameSession.Map?.Radiation?.IsEntityRadiated(character) ?? false)) { continue; }
|
||||
if (affliction.Strength < affliction.Prefab.ShowIconThreshold) { continue; }
|
||||
DisplayHint("onafflictiondisplayed",
|
||||
variableTags: new string[1] { "[key]" },
|
||||
variableValues: new string[1] { GameMain.Config.KeyBindText(InputType.Health) },
|
||||
icon: affliction.Prefab.Icon,
|
||||
iconColor: CharacterHealth.GetAfflictionIconColor(affliction),
|
||||
onUpdate: () =>
|
||||
{
|
||||
if (CharacterHealth.OpenHealthWindow == null) { return; }
|
||||
ActiveHintMessageBox.Close();
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public static void OnShootWithoutAiming(Character character, Item item)
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
if (character != Character.Controlled) { return; }
|
||||
if (character.SelectedConstruction != null || character.FocusedItem != null) { return; }
|
||||
if (item == null || !item.IsShootable || !item.RequireAimToUse) { return; }
|
||||
if (TimeStoppedInteracting + 1 > Timing.TotalTime) { return; }
|
||||
if (GUI.MouseOn != null) { return; }
|
||||
if (Character.Controlled.Inventory?.visualSlots != null && Character.Controlled.Inventory.visualSlots.Any(s => s.InteractRect.Contains(PlayerInput.MousePosition))) { return; }
|
||||
string hintIdentifier = "onshootwithoutaiming";
|
||||
if (!HintTags.TryGetValue(hintIdentifier, out var tags)) { return; }
|
||||
if (!item.HasTag(tags)) { return; }
|
||||
DisplayHint(hintIdentifier,
|
||||
variableTags: new string[1] { "[key]" },
|
||||
variableValues: new string[1] { GameMain.Config.KeyBindText(InputType.Aim) },
|
||||
onUpdate: () =>
|
||||
{
|
||||
if (character.SelectedConstruction == null && GUI.MouseOn == null && PlayerInput.KeyDown(InputType.Aim))
|
||||
{
|
||||
ActiveHintMessageBox.Close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void OnWeldingDoor(Character character, Door door)
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
if (character != Character.Controlled) { return; }
|
||||
if (door == null || door.Stuck < 20.0f) { return; }
|
||||
DisplayHint("onweldingdoor");
|
||||
}
|
||||
|
||||
public static void OnTryOpenStuckDoor(Character character)
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
if (character != Character.Controlled) { return; }
|
||||
DisplayHint("ontryopenstuckdoor");
|
||||
}
|
||||
|
||||
public static void OnShowCampaignInterface(CampaignMode.InteractionType interactionType)
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
if (interactionType == CampaignMode.InteractionType.None) { return; }
|
||||
string hintIdentifier = $"onshowcampaigninterface.{interactionType.ToString().ToLowerInvariant()}";
|
||||
DisplayHint(hintIdentifier, onUpdate: () =>
|
||||
{
|
||||
|
||||
if (!(GameMain.GameSession?.Campaign is CampaignMode campaign) ||
|
||||
(!campaign.ShowCampaignUI && !campaign.ForceMapUI) ||
|
||||
campaign.CampaignUI?.SelectedTab != CampaignMode.InteractionType.Map)
|
||||
{
|
||||
ActiveHintMessageBox.Close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void OnShowCommandInterface()
|
||||
{
|
||||
IgnoreReminder("commandinterface");
|
||||
if (!CanDisplayHints()) { return; }
|
||||
DisplayHint("onshowcommandinterface", onUpdate: () =>
|
||||
{
|
||||
if (CrewManager.IsCommandInterfaceOpen) { return; }
|
||||
ActiveHintMessageBox.Close();
|
||||
});
|
||||
}
|
||||
|
||||
public static void OnShowHealthInterface()
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
if (CharacterHealth.OpenHealthWindow == null) { return; }
|
||||
DisplayHint("onshowhealthinterface", onUpdate: () =>
|
||||
{
|
||||
if (CharacterHealth.OpenHealthWindow != null) { return; }
|
||||
ActiveHintMessageBox.Close();
|
||||
});
|
||||
}
|
||||
|
||||
public static void OnShowTabMenu()
|
||||
{
|
||||
IgnoreReminder("tabmenu");
|
||||
}
|
||||
|
||||
public static void OnStoleItem(Character character, Item item)
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
if (character != Character.Controlled) { return; }
|
||||
if (item == null || item.AllowStealing || !item.StolenDuringRound) { return; }
|
||||
DisplayHint("onstoleitem", onUpdate: () =>
|
||||
{
|
||||
if (item == null || item.Removed || item.GetRootInventoryOwner() != character)
|
||||
{
|
||||
ActiveHintMessageBox.Close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void OnHandcuffed(Character character)
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
if (character != Character.Controlled || !character.LockHands) { return; }
|
||||
DisplayHint("onhandcuffed", onUpdate: () =>
|
||||
{
|
||||
if (character != null && !character.Removed && character.LockHands) { return; }
|
||||
ActiveHintMessageBox.Close();
|
||||
});
|
||||
}
|
||||
|
||||
public static void OnReactorOutOfFuel(Reactor reactor)
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
if (reactor == null) { return; }
|
||||
if (reactor.Item.Submarine?.Info?.Type != SubmarineType.Player || reactor.Item.Submarine.TeamID != Character.Controlled.TeamID) { return; }
|
||||
if (!HasValidJob("engineer")) { return; }
|
||||
DisplayHint("onreactoroutoffuel", onUpdate: () =>
|
||||
{
|
||||
if (reactor?.Item != null && !reactor.Item.Removed && reactor.AvailableFuel < 1) { return; }
|
||||
ActiveHintMessageBox.Close();
|
||||
});
|
||||
}
|
||||
|
||||
public static void OnAvailableTransition(CampaignMode.TransitionType transitionType)
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
if (transitionType == CampaignMode.TransitionType.None) { return; }
|
||||
DisplayHint($"onavailabletransition.{transitionType.ToString().ToLowerInvariant()}");
|
||||
}
|
||||
|
||||
public static void OnShowSubInventory(Item item)
|
||||
{
|
||||
if (item?.Prefab == null) { return; }
|
||||
if (item.Prefab.Identifier.Equals("toolbelt", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
IgnoreReminder("toolbelt");
|
||||
}
|
||||
}
|
||||
|
||||
public static void OnChangeCharacter()
|
||||
{
|
||||
IgnoreReminder("characterchange");
|
||||
}
|
||||
|
||||
public static void OnCharacterUnconscious(Character character)
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
if (character != Character.Controlled) { return; }
|
||||
if (character.IsDead) { return; }
|
||||
if (character.CharacterHealth != null && character.Vitality < character.CharacterHealth.MinVitality) { return; }
|
||||
DisplayHint("oncharacterunconscious");
|
||||
}
|
||||
|
||||
public static void OnCharacterKilled(Character character)
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
if (character != Character.Controlled) { return; }
|
||||
if (GameMain.IsMultiplayer) { return; }
|
||||
if (GameMain.GameSession?.CrewManager == null) { return; }
|
||||
if (GameMain.GameSession.CrewManager.GetCharacters().None(c => !c.IsDead)) { return; }
|
||||
DisplayHint("oncharacterkilled");
|
||||
}
|
||||
|
||||
private static void OnStartedControlling()
|
||||
{
|
||||
if (Level.IsLoadedOutpost) { return; }
|
||||
if (Character.Controlled?.Info?.Job?.Prefab == null) { return; }
|
||||
string hintIdentifier = $"onstartedcontrolling.job.{Character.Controlled.Info.Job.Prefab.Identifier}";
|
||||
DisplayHint(hintIdentifier,
|
||||
icon: Character.Controlled.Info.Job.Prefab.Icon,
|
||||
iconColor: Character.Controlled.Info.Job.Prefab.UIColor,
|
||||
onDisplay: () =>
|
||||
{
|
||||
if (!HintOrders.TryGetValue(hintIdentifier, out var orderInfo)) { return; }
|
||||
var orderPrefab = Order.GetPrefab(orderInfo.identifier);
|
||||
if (orderPrefab == null) { return; }
|
||||
Item targetEntity = null;
|
||||
ItemComponent targetItem = null;
|
||||
if (orderPrefab.MustSetTarget)
|
||||
{
|
||||
targetEntity = orderPrefab.GetMatchingItems(true, interactableFor: Character.Controlled).FirstOrDefault();
|
||||
if (targetEntity == null) { return; }
|
||||
targetItem = orderPrefab.GetTargetItemComponent(targetEntity);
|
||||
}
|
||||
var order = new Order(orderPrefab, targetEntity as Entity, targetItem, orderGiver: Character.Controlled);
|
||||
GameMain.GameSession.CrewManager.SetCharacterOrder(Character.Controlled, order, orderInfo.option, CharacterInfo.HighestManualOrderPriority, Character.Controlled);
|
||||
});
|
||||
}
|
||||
|
||||
public static void OnAutoPilotPathUpdated(Steering steering)
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
if (!HasValidJob("captain")) { return; }
|
||||
if (steering?.Item?.Submarine?.Info == null) { return; }
|
||||
if (steering.Item.Submarine.Info.Type != SubmarineType.Player) { return; }
|
||||
if (steering.Item.Submarine.TeamID != Character.Controlled.TeamID) { return; }
|
||||
if (!steering.AutoPilot || steering.MaintainPos) { return; }
|
||||
if (steering.SteeringPath?.CurrentNode?.Tunnel?.Type != Level.TunnelType.MainPath) { return; }
|
||||
if (!steering.SteeringPath.Finished && steering.SteeringPath.NextNode != null) { return; }
|
||||
if (steering.LevelStartSelected && (Level.Loaded.StartOutpost == null || !steering.Item.Submarine.AtStartExit)) { return; }
|
||||
if (steering.LevelEndSelected && (Level.Loaded.EndOutpost == null || !steering.Item.Submarine.AtEndExit)) { return; }
|
||||
DisplayHint("onautopilotreachedoutpost");
|
||||
}
|
||||
|
||||
public static void OnStatusEffectApplied(ItemComponent component, ActionType actionType, Character character)
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
if (character != Character.Controlled) { return; }
|
||||
// Could make this more generic if there will ever be any other status effect related hints
|
||||
if (!(component is Repairable) || actionType != ActionType.OnFailure) { return; }
|
||||
DisplayHint("onrepairfailed");
|
||||
}
|
||||
|
||||
public static void OnActiveOrderAdded(Order order)
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
if (order == null) { return; }
|
||||
|
||||
if (order.Identifier == "reportballastflora" &&
|
||||
order.TargetEntity is Hull h &&
|
||||
h.Submarine?.TeamID == Character.Controlled.TeamID)
|
||||
{
|
||||
DisplayHint("onballastflorainfected");
|
||||
}
|
||||
}
|
||||
|
||||
private static void CheckIfDivingGearOutOfOxygen()
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
var divingGear = Character.Controlled.GetEquippedItem("diving");
|
||||
if (divingGear?.OwnInventory == null) { return; }
|
||||
if (divingGear.GetContainedItemConditionPercentage() > 0.0f) { return; }
|
||||
DisplayHint("ondivinggearoutofoxygen", onUpdate: () =>
|
||||
{
|
||||
if (divingGear == null || divingGear.Removed ||
|
||||
Character.Controlled == null || !Character.Controlled.HasEquippedItem(divingGear) ||
|
||||
divingGear.GetContainedItemConditionPercentage() > 0.0f)
|
||||
{
|
||||
ActiveHintMessageBox.Close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void CheckHulls()
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
if (Character.Controlled.CurrentHull == null) { return; }
|
||||
if (HumanAIController.IsBallastFloraNoticeable(Character.Controlled, Character.Controlled.CurrentHull))
|
||||
{
|
||||
if (DisplayHint("onballastflorainfected")) { return; }
|
||||
}
|
||||
foreach (var gap in Character.Controlled.CurrentHull.ConnectedGaps)
|
||||
{
|
||||
if (gap.ConnectedDoor == null || gap.ConnectedDoor.Impassable) { continue; }
|
||||
if (Vector2.DistanceSquared(Character.Controlled.WorldPosition, gap.ConnectedDoor.Item.WorldPosition) > 400 * 400) { continue; }
|
||||
if (!gap.IsRoomToRoom)
|
||||
{
|
||||
if (!(Character.Controlled.GetEquippedItem("deepdiving") is Item)) { continue; }
|
||||
if (Character.Controlled.IsProtectedFromPressure()) { continue; }
|
||||
if (DisplayHint("divingsuitwarning", extendTextTag: false)) { return; }
|
||||
continue;
|
||||
}
|
||||
foreach (var me in gap.linkedTo)
|
||||
{
|
||||
if (me == Character.Controlled.CurrentHull) { continue; }
|
||||
if (!(me is Hull adjacentHull)) { continue; }
|
||||
if (adjacentHull.LethalPressure > 5.0f && DisplayHint("onadjacenthull.highpressure")) { return; }
|
||||
if (adjacentHull.WaterPercentage > 75 && !BallastHulls.Contains(adjacentHull) && DisplayHint("onadjacenthull.highwaterpercentage")) { return; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void CheckReminders()
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
if (Level.Loaded == null) { return; }
|
||||
if (GameMain.GameScreen.GameTime < TimeRoundStarted + TimeBeforeReminders) { return; }
|
||||
if (GameMain.GameScreen.GameTime < TimeReminderLastDisplayed + ReminderCooldown) { return; }
|
||||
|
||||
string hintIdentifierBase = "reminder";
|
||||
|
||||
if (GameMain.GameSession.GameMode.IsSinglePlayer)
|
||||
{
|
||||
if (DisplayHint($"{hintIdentifierBase}.characterchange"))
|
||||
{
|
||||
TimeReminderLastDisplayed = GameMain.GameScreen.GameTime;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Level.Loaded.Type != LevelData.LevelType.Outpost)
|
||||
{
|
||||
if (DisplayHint($"{hintIdentifierBase}.commandinterface",
|
||||
variableTags: new string[] { "[commandkey]" },
|
||||
variableValues: new string[] { GameMain.Config.KeyBindText(InputType.Command) },
|
||||
onUpdate: () =>
|
||||
{
|
||||
if (!CrewManager.IsCommandInterfaceOpen) { return; }
|
||||
ActiveHintMessageBox.Close();
|
||||
}))
|
||||
{
|
||||
TimeReminderLastDisplayed = GameMain.GameScreen.GameTime;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (DisplayHint($"{hintIdentifierBase}.tabmenu",
|
||||
variableTags: new string[] { "[infotabkey]" },
|
||||
variableValues: new string[] { GameMain.Config.KeyBindText(InputType.InfoTab) },
|
||||
onUpdate: () =>
|
||||
{
|
||||
if (!GameSession.IsTabMenuOpen) { return; }
|
||||
ActiveHintMessageBox.Close();
|
||||
}))
|
||||
{
|
||||
TimeReminderLastDisplayed = GameMain.GameScreen.GameTime;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Character.Controlled.Inventory?.GetItemInLimbSlot(InvSlotType.Bag)?.Prefab?.Identifier == "toolbelt")
|
||||
{
|
||||
if (DisplayHint($"{hintIdentifierBase}.toolbelt"))
|
||||
{
|
||||
TimeReminderLastDisplayed = GameMain.GameScreen.GameTime;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool DisplayHint(string hintIdentifier, bool extendTextTag = true, string[] variableTags = null, string[] variableValues = null, Sprite icon = null, Color? iconColor = null, Action onDisplay = null, Action onUpdate = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(hintIdentifier)) { return false; }
|
||||
if (!HintIdentifiers.Contains(hintIdentifier)) { return false; }
|
||||
if (GameMain.Config.IgnoredHints.Contains(hintIdentifier)) { return false; }
|
||||
if (HintsIgnoredThisRound.Contains(hintIdentifier)) { return false; }
|
||||
|
||||
string text;
|
||||
string textTag = extendTextTag ? $"hint.{hintIdentifier}" : hintIdentifier;
|
||||
if (variableTags != null && variableTags != null && variableTags.Length > 0 && variableTags.Length == variableValues.Length)
|
||||
{
|
||||
text = TextManager.GetWithVariables(textTag, variableTags, variableValues, returnNull: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
text = TextManager.Get(textTag, returnNull: true);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError($"No hint text found for text tag \"{textTag}\"");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
HintsIgnoredThisRound.Add(hintIdentifier);
|
||||
|
||||
ActiveHintMessageBox = new GUIMessageBox(hintIdentifier, text, icon);
|
||||
if (iconColor.HasValue) { ActiveHintMessageBox.IconColor = iconColor.Value; }
|
||||
OnUpdate = onUpdate;
|
||||
|
||||
SoundPlayer.PlayUISound(GUISoundType.UIMessage);
|
||||
ActiveHintMessageBox.InnerFrame.Flash(color: iconColor ?? Color.Orange, flashDuration: 0.75f);
|
||||
onDisplay?.Invoke();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool OnDontShowAgain(GUITickBox tickBox)
|
||||
{
|
||||
IgnoreHint((string)tickBox.UserData, ignore: tickBox.Selected);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void IgnoreHint(string hintIdentifier, bool ignore = true)
|
||||
{
|
||||
if (string.IsNullOrEmpty(hintIdentifier)) { return; }
|
||||
if (!HintIdentifiers.Contains(hintIdentifier))
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError($"Tried to ignore a hint not defined in {HintManagerFile}: {hintIdentifier}");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
if (ignore)
|
||||
{
|
||||
GameMain.Config.IgnoredHints.Add(hintIdentifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
GameMain.Config.IgnoredHints.Remove(hintIdentifier);
|
||||
}
|
||||
}
|
||||
|
||||
private static void IgnoreReminder(string reminderIdentifier)
|
||||
{
|
||||
HintsIgnoredThisRound.Add($"reminder.{reminderIdentifier}");
|
||||
}
|
||||
|
||||
public static bool OnDisableHints(GUITickBox tickBox)
|
||||
{
|
||||
GameMain.Config.DisableInGameHints = tickBox.Selected;
|
||||
return GameMain.Config.SaveNewPlayerConfig();
|
||||
}
|
||||
|
||||
private static bool CanDisplayHints(bool requireGameScreen = true, bool requireControllingCharacter = true)
|
||||
{
|
||||
if (HintIdentifiers == null) { return false; }
|
||||
if (GameMain.Config.DisableInGameHints) { return false; }
|
||||
if (ActiveHintMessageBox != null) { return false; }
|
||||
if (requireControllingCharacter && Character.Controlled == null) { return false; }
|
||||
var gameMode = GameMain.GameSession?.GameMode;
|
||||
if (!(gameMode is CampaignMode || gameMode is MissionMode)) { return false; }
|
||||
if (requireGameScreen && Screen.Selected != GameMain.GameScreen) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool HasValidJob(string jobIdentifier)
|
||||
{
|
||||
// In singleplayer, we can control all character so we don't care about job restrictions
|
||||
if (GameMain.GameSession.GameMode.IsSinglePlayer) { return true; }
|
||||
if (Character.Controlled.HasJob(jobIdentifier)) { return true; }
|
||||
// In multiplayer, if there are players with the job, display the hint to all players
|
||||
foreach (var c in GameMain.GameSession.CrewManager.GetCharacters())
|
||||
{
|
||||
if (c == null || !c.IsRemotePlayer) { continue; }
|
||||
if (c.IsUnconscious || c.IsDead || c.Removed) { continue; }
|
||||
if (!c.HasJob(jobIdentifier)) { continue; }
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,7 @@ namespace Barotrauma
|
||||
msgBox.Buttons[0].OnClicked = delegate
|
||||
{
|
||||
msgBox.Close();
|
||||
if (GameMain.Client == null) { return true; }
|
||||
SendState(ReadyStatus.Yes);
|
||||
CreateResultsMessage();
|
||||
return true;
|
||||
@@ -57,6 +58,7 @@ namespace Barotrauma
|
||||
msgBox.Buttons[1].OnClicked = delegate
|
||||
{
|
||||
msgBox.Close();
|
||||
if (GameMain.Client == null) { return true; }
|
||||
SendState(ReadyStatus.No);
|
||||
CreateResultsMessage();
|
||||
return true;
|
||||
@@ -65,6 +67,8 @@ namespace Barotrauma
|
||||
|
||||
private void CreateResultsMessage()
|
||||
{
|
||||
if (GameMain.Client == null) { return; }
|
||||
|
||||
Vector2 relativeSize = new Vector2(0.2f, 0.3f);
|
||||
Point minSize = new Point(300, 400);
|
||||
resultsBox = new GUIMessageBox(readyCheckHeader, string.Empty, new[] { closeButton }, relativeSize, minSize, type: GUIMessageBox.Type.Vote) { UserData = ResultData, Draggable = true };
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Barotrauma
|
||||
private int jobColumnWidth, characterColumnWidth, statusColumnWidth;
|
||||
|
||||
private readonly SubmarineInfo sub;
|
||||
private readonly Mission selectedMission;
|
||||
private readonly List<Mission> selectedMissions;
|
||||
private readonly Location startLocation, endLocation;
|
||||
|
||||
private readonly GameMode gameMode;
|
||||
@@ -32,11 +32,11 @@ namespace Barotrauma
|
||||
|
||||
|
||||
|
||||
public RoundSummary(SubmarineInfo sub, GameMode gameMode, Mission selectedMission, Location startLocation, Location endLocation)
|
||||
public RoundSummary(SubmarineInfo sub, GameMode gameMode, IEnumerable<Mission> selectedMissions, Location startLocation, Location endLocation)
|
||||
{
|
||||
this.sub = sub;
|
||||
this.gameMode = gameMode;
|
||||
this.selectedMission = selectedMission;
|
||||
this.selectedMissions = selectedMissions.ToList();
|
||||
this.startLocation = startLocation;
|
||||
this.endLocation = endLocation;
|
||||
initialLocationReputation = startLocation?.Reputation?.Value ?? 0.0f;
|
||||
@@ -75,7 +75,7 @@ namespace Barotrauma
|
||||
|
||||
//crew panel -------------------------------------------------------------------------------
|
||||
|
||||
GUIFrame crewFrame = new GUIFrame(new RectTransform(new Vector2(0.35f, 0.55f), background.RectTransform, Anchor.TopCenter, minSize: new Point(minWidth, minHeight)));
|
||||
GUIFrame crewFrame = new GUIFrame(new RectTransform(new Vector2(0.35f, 0.45f), background.RectTransform, Anchor.TopCenter, minSize: new Point(minWidth, minHeight)));
|
||||
GUIFrame crewFrameInner = new GUIFrame(new RectTransform(new Point(crewFrame.Rect.Width - padding * 2, crewFrame.Rect.Height - padding * 2), crewFrame.RectTransform, Anchor.Center), style: "InnerFrame");
|
||||
|
||||
var crewContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), crewFrameInner.RectTransform, Anchor.Center))
|
||||
@@ -90,10 +90,10 @@ namespace Barotrauma
|
||||
CreateCrewList(crewContent, gameSession.CrewManager.GetCharacterInfos().Where(c => c.TeamID != CharacterTeamType.Team2));
|
||||
|
||||
//another crew frame for the 2nd team in combat missions
|
||||
if (gameSession.Mission is CombatMission)
|
||||
if (gameSession.Missions.Any(m => m is CombatMission))
|
||||
{
|
||||
crewHeader.Text = CombatMission.GetTeamName(CharacterTeamType.Team1);
|
||||
GUIFrame crewFrame2 = new GUIFrame(new RectTransform(new Vector2(0.35f, 0.55f), background.RectTransform, Anchor.TopCenter, minSize: new Point(minWidth, minHeight)));
|
||||
GUIFrame crewFrame2 = new GUIFrame(new RectTransform(new Vector2(0.35f, 0.45f), background.RectTransform, Anchor.TopCenter, minSize: new Point(minWidth, minHeight)));
|
||||
rightPanels.Add(crewFrame2);
|
||||
GUIFrame crewFrameInner2 = new GUIFrame(new RectTransform(new Point(crewFrame2.Rect.Width - padding * 2, crewFrame2.Rect.Height - padding * 2), crewFrame2.RectTransform, Anchor.Center), style: "InnerFrame");
|
||||
var crewContent2 = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), crewFrameInner2.RectTransform, Anchor.Center))
|
||||
@@ -183,7 +183,8 @@ namespace Barotrauma
|
||||
|
||||
//reputation panel -------------------------------------------------------------------------------
|
||||
|
||||
if (gameMode is CampaignMode campaignMode)
|
||||
var campaignMode = gameMode as CampaignMode;
|
||||
if (campaignMode != null)
|
||||
{
|
||||
GUIFrame reputationframe = new GUIFrame(new RectTransform(crewFrame.RectTransform.RelativeSize, background.RectTransform, Anchor.TopCenter, minSize: crewFrame.RectTransform.MinSize));
|
||||
rightPanels.Add(reputationframe);
|
||||
@@ -198,158 +199,154 @@ namespace Barotrauma
|
||||
TextManager.Get("reputation"), textAlignment: Alignment.TopLeft, font: GUI.SubHeadingFont);
|
||||
reputationHeader.RectTransform.MinSize = new Point(0, GUI.IntScale(reputationHeader.Rect.Height * 2.0f));
|
||||
|
||||
GUIListBox reputationList = new GUIListBox(new RectTransform(Vector2.One, reputationContent.RectTransform))
|
||||
{
|
||||
Padding = new Vector4(2, 5, 0, 0)
|
||||
};
|
||||
reputationList.ContentBackground.Color = Color.Transparent;
|
||||
|
||||
if (startLocation.Type.HasOutpost && startLocation.Reputation != null)
|
||||
{
|
||||
var iconStyle = GUI.Style.GetComponentStyle("LocationReputationIcon");
|
||||
CreateReputationElement(
|
||||
reputationList.Content,
|
||||
startLocation.Name,
|
||||
startLocation.Reputation.Value, startLocation.Reputation.NormalizedValue, initialLocationReputation,
|
||||
startLocation.Type.Name, "",
|
||||
iconStyle?.GetDefaultSprite(), startLocation.Type.GetPortrait(0), iconStyle?.Color ?? Color.White);
|
||||
}
|
||||
|
||||
foreach (Faction faction in campaignMode.Factions)
|
||||
{
|
||||
float initialReputation = faction.Reputation.Value;
|
||||
if (initialFactionReputations.ContainsKey(faction))
|
||||
{
|
||||
initialReputation = initialFactionReputations[faction];
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.AddWarning($"Could not determine reputation change for faction \"{faction.Prefab.Name}\" (faction was not present at the start of the round).");
|
||||
}
|
||||
CreateReputationElement(
|
||||
reputationList.Content,
|
||||
faction.Prefab.Name,
|
||||
faction.Reputation.Value, faction.Reputation.NormalizedValue, initialReputation,
|
||||
faction.Prefab.ShortDescription, faction.Prefab.Description,
|
||||
faction.Prefab.Icon, faction.Prefab.BackgroundPortrait, faction.Prefab.IconColor);
|
||||
}
|
||||
|
||||
float otherElementHeight = 0.0f;
|
||||
float maxDescriptionHeight = 0.0f;
|
||||
foreach (GUIComponent child in reputationList.Content.Children)
|
||||
{
|
||||
var descriptionElement = child.FindChild("description", recursive: true) as GUITextBlock;
|
||||
maxDescriptionHeight = Math.Max(maxDescriptionHeight, descriptionElement.TextSize.Y * 1.1f);
|
||||
otherElementHeight = Math.Max(otherElementHeight, descriptionElement.Parent.Rect.Height - descriptionElement.TextSize.Y);
|
||||
}
|
||||
foreach (GUIComponent child in reputationList.Content.Children)
|
||||
{
|
||||
var descriptionElement = child.FindChild("description", recursive: true) as GUITextBlock;
|
||||
descriptionElement.RectTransform.MaxSize = new Point(int.MaxValue, (int)(maxDescriptionHeight));
|
||||
child.RectTransform.MaxSize = new Point(int.MaxValue, (int)((maxDescriptionHeight + otherElementHeight) * 1.2f));
|
||||
(descriptionElement?.Parent as GUILayoutGroup).Recalculate();
|
||||
}
|
||||
CreateReputationInfoPanel(reputationContent, campaignMode);
|
||||
}
|
||||
|
||||
//mission panel -------------------------------------------------------------------------------
|
||||
|
||||
GUIFrame missionframe = new GUIFrame(new RectTransform(new Vector2(0.39f, 0.22f), background.RectTransform, Anchor.TopCenter, minSize: new Point(minWidth, minHeight / 4)));
|
||||
GUIFrame missionframeInner = new GUIFrame(new RectTransform(new Point(missionframe.Rect.Width - padding * 2, missionframe.Rect.Height - padding * 2), missionframe.RectTransform, Anchor.Center), style: "InnerFrame");
|
||||
|
||||
var missionContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), missionframeInner.RectTransform, Anchor.Center))
|
||||
GUIFrame missionframe = new GUIFrame(new RectTransform(new Vector2(0.39f, 0.3f), background.RectTransform, Anchor.TopCenter, minSize: new Point(minWidth, minHeight / 4)));
|
||||
GUILayoutGroup missionFrameContent = new GUILayoutGroup(new RectTransform(new Point(missionframe.Rect.Width - padding * 2, missionframe.Rect.Height - padding * 2), missionframe.RectTransform, Anchor.Center))
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.05f
|
||||
};
|
||||
GUIFrame missionframeInner = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.9f), missionFrameContent.RectTransform, Anchor.Center), style: "InnerFrame");
|
||||
|
||||
var missionContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.93f), missionframeInner.RectTransform, Anchor.Center))
|
||||
{
|
||||
RelativeSpacing = 0.05f,
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
List<Mission> missionsToDisplay = new List<Mission>(selectedMissions);
|
||||
if (!selectedMissions.Any() && startLocation?.SelectedMission != null)
|
||||
{
|
||||
if (startLocation.SelectedMission.Locations[0] == startLocation.SelectedMission.Locations[1] ||
|
||||
startLocation.SelectedMission.Locations.Contains(campaignMode?.Map.SelectedLocation))
|
||||
{
|
||||
missionsToDisplay.Add(startLocation.SelectedMission);
|
||||
}
|
||||
}
|
||||
|
||||
if (missionsToDisplay.Any())
|
||||
{
|
||||
var missionHeader = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionContent.RectTransform),
|
||||
TextManager.Get(missionsToDisplay.Count > 1 ? "Missions" : "Mission"), textAlignment: Alignment.TopLeft, font: GUI.SubHeadingFont);
|
||||
missionHeader.RectTransform.MinSize = new Point(0, (int)(missionHeader.Rect.Height * 1.2f));
|
||||
}
|
||||
|
||||
GUIListBox missionList = new GUIListBox(new RectTransform(Vector2.One, missionContent.RectTransform, Anchor.Center))
|
||||
{
|
||||
Padding = new Vector4(4, 10, 0, 0) * GUI.Scale
|
||||
};
|
||||
missionList.ContentBackground.Color = Color.Transparent;
|
||||
|
||||
ButtonArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), missionFrameContent.RectTransform, Anchor.BottomCenter), isHorizontal: true, childAnchor: Anchor.BottomRight)
|
||||
{
|
||||
RelativeSpacing = 0.025f
|
||||
};
|
||||
|
||||
missionFrameContent.Recalculate();
|
||||
missionContent.Recalculate();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(endMessage))
|
||||
{
|
||||
var endText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionContent.RectTransform),
|
||||
TextManager.GetServerMessage(endMessage), wrap: true);
|
||||
var endText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionList.Content.RectTransform),
|
||||
TextManager.GetServerMessage(endMessage), wrap: true)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
endText.RectTransform.MinSize = new Point(0, endText.Rect.Height);
|
||||
var line = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.1f), missionContent.RectTransform), style: "HorizontalLine");
|
||||
var line = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.1f), missionList.Content.RectTransform), style: "HorizontalLine");
|
||||
line.RectTransform.NonScaledSize = new Point(line.Rect.Width, GUI.IntScale(5.0f));
|
||||
}
|
||||
|
||||
var missionContentHorizontal = new GUILayoutGroup(new RectTransform(Vector2.One, missionContent.RectTransform), childAnchor: Anchor.TopLeft, isHorizontal: true)
|
||||
foreach (Mission displayedMission in missionsToDisplay)
|
||||
{
|
||||
RelativeSpacing = 0.025f,
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
Mission displayedMission = selectedMission ?? startLocation.SelectedMission;
|
||||
string missionMessage = "";
|
||||
GUIImage missionIcon;
|
||||
if (displayedMission != null)
|
||||
{
|
||||
missionMessage =
|
||||
displayedMission == selectedMission ?
|
||||
displayedMission.Completed ? displayedMission.SuccessMessage : displayedMission.FailureMessage :
|
||||
displayedMission.Description;
|
||||
missionIcon = new GUIImage(new RectTransform(new Point(missionContentHorizontal.Rect.Height), missionContentHorizontal.RectTransform), displayedMission.Prefab.Icon, scaleToFit: true)
|
||||
var missionContentHorizontal = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.8f), missionList.Content.RectTransform), childAnchor: Anchor.CenterLeft, isHorizontal: true)
|
||||
{
|
||||
Color = displayedMission.Prefab.IconColor
|
||||
RelativeSpacing = 0.025f,
|
||||
Stretch = true
|
||||
};
|
||||
if (displayedMission == selectedMission)
|
||||
{
|
||||
new GUIImage(new RectTransform(Vector2.One, missionIcon.RectTransform), displayedMission.Completed ? "MissionCompletedIcon" : "MissionFailedIcon", scaleToFit: true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
missionIcon = new GUIImage(new RectTransform(new Point(missionContentHorizontal.Rect.Height), missionContentHorizontal.RectTransform), style: "NoMissionIcon", scaleToFit: true);
|
||||
}
|
||||
var missionTextContent = new GUILayoutGroup(new RectTransform(Vector2.One, missionContentHorizontal.RectTransform))
|
||||
{
|
||||
RelativeSpacing = 0.05f
|
||||
};
|
||||
missionContentHorizontal.Recalculate();
|
||||
missionContent.Recalculate();
|
||||
missionIcon.RectTransform.MinSize = new Point(0, missionContentHorizontal.Rect.Height);
|
||||
missionTextContent.RectTransform.MaxSize = new Point(int.MaxValue, missionIcon.Rect.Width);
|
||||
|
||||
GUITextBlock missionDescription = null;
|
||||
if (displayedMission == null)
|
||||
{
|
||||
string missionMessage =
|
||||
selectedMissions.Contains(displayedMission) ?
|
||||
displayedMission.Completed ? displayedMission.SuccessMessage : displayedMission.FailureMessage :
|
||||
displayedMission.Description;
|
||||
GUIImage missionIcon = new GUIImage(new RectTransform(new Point((int)(missionContentHorizontal.Rect.Height)), missionContentHorizontal.RectTransform), displayedMission.Prefab.Icon, scaleToFit: true)
|
||||
{
|
||||
Color = displayedMission.Prefab.IconColor,
|
||||
HoverColor = displayedMission.Prefab.IconColor,
|
||||
SelectedColor = displayedMission.Prefab.IconColor
|
||||
};
|
||||
missionIcon.RectTransform.MinSize = new Point((int)(missionContentHorizontal.Rect.Height * 0.9f));
|
||||
if (selectedMissions.Contains(displayedMission))
|
||||
{
|
||||
new GUIImage(new RectTransform(Vector2.One, missionIcon.RectTransform), displayedMission.Completed ? "MissionCompletedIcon" : "MissionFailedIcon", scaleToFit: true);
|
||||
}
|
||||
|
||||
var missionTextContent = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 0.8f), missionContentHorizontal.RectTransform))
|
||||
{
|
||||
RelativeSpacing = 0.05f
|
||||
};
|
||||
var missionNameTextBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
|
||||
displayedMission.Name, font: GUI.SubHeadingFont);
|
||||
if (displayedMission.Difficulty.HasValue)
|
||||
{
|
||||
var groupSize = missionNameTextBlock.Rect.Size;
|
||||
groupSize.X -= (int)(missionNameTextBlock.Padding.X + missionNameTextBlock.Padding.Z);
|
||||
var indicatorGroup = new GUILayoutGroup(new RectTransform(groupSize, missionTextContent.RectTransform) { AbsoluteOffset = new Point((int)missionNameTextBlock.Padding.X, 0) },
|
||||
isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
AbsoluteSpacing = 1
|
||||
};
|
||||
var difficultyColor = displayedMission.GetDifficultyColor();
|
||||
for (int i = 0; i < displayedMission.Difficulty; i++)
|
||||
{
|
||||
new GUIImage(new RectTransform(Vector2.One, indicatorGroup.RectTransform, scaleBasis: ScaleBasis.Smallest) { IsFixedSize = true }, "DifficultyIndicator", scaleToFit: true)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
Color = difficultyColor
|
||||
};
|
||||
}
|
||||
}
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
|
||||
TextManager.Get("nomission"), font: GUI.LargeFont);
|
||||
}
|
||||
else
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
|
||||
TextManager.AddPunctuation(':', TextManager.Get("Mission"), displayedMission.Name), font: GUI.SubHeadingFont);
|
||||
missionDescription = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
|
||||
missionMessage, wrap: true);
|
||||
if (displayedMission == selectedMission && displayedMission.Completed)
|
||||
missionMessage, wrap: true, parseRichText: true);
|
||||
if (selectedMissions.Contains(displayedMission) && displayedMission.Completed && displayedMission.Reward > 0)
|
||||
{
|
||||
string rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", displayedMission.Reward));
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
|
||||
TextManager.GetWithVariable("MissionReward", "[reward]", rewardText));
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), displayedMission.GetMissionRewardText(), parseRichText: true);
|
||||
}
|
||||
|
||||
if (displayedMission != missionsToDisplay.Last())
|
||||
{
|
||||
var spacing = new GUIFrame(new RectTransform(new Vector2(1.0f, 1.0f), missionList.Content.RectTransform) { MaxSize = new Point(int.MaxValue, GUI.IntScale(15)) }, style: null);
|
||||
new GUIFrame(new RectTransform(new Vector2(0.8f, 1.0f), spacing.RectTransform, Anchor.Center) { RelativeOffset = new Vector2(0.1f, 0.0f) }, "HorizontalLine");
|
||||
}
|
||||
}
|
||||
|
||||
ButtonArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), missionContent.RectTransform, Anchor.BottomCenter), isHorizontal: true, childAnchor: Anchor.BottomRight)
|
||||
if (!missionsToDisplay.Any())
|
||||
{
|
||||
IgnoreLayoutGroups = true,
|
||||
RelativeSpacing = 0.025f
|
||||
};
|
||||
var missionContentHorizontal = new GUILayoutGroup(new RectTransform(Vector2.One, missionList.Content.RectTransform), childAnchor: Anchor.TopLeft, isHorizontal: true)
|
||||
{
|
||||
RelativeSpacing = 0.025f,
|
||||
Stretch = true
|
||||
};
|
||||
GUIImage missionIcon = new GUIImage(new RectTransform(new Point((int)(missionContentHorizontal.Rect.Height * 0.7f)), missionContentHorizontal.RectTransform), style: "NoMissionIcon", scaleToFit: true);
|
||||
missionIcon.RectTransform.MinSize = new Point((int)(missionContentHorizontal.Rect.Height * 0.7f));
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionContentHorizontal.RectTransform),
|
||||
TextManager.Get("nomission"), font: GUI.LargeFont);
|
||||
}
|
||||
|
||||
/*missionContentHorizontal.Recalculate();
|
||||
missionContent.Recalculate();
|
||||
missionIcon.RectTransform.MinSize = new Point(0, missionContentHorizontal.Rect.Height);
|
||||
missionTextContent.RectTransform.MaxSize = new Point(int.MaxValue, missionIcon.Rect.Width);*/
|
||||
|
||||
ContinueButton = new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), ButtonArea.RectTransform), TextManager.Get("Close"));
|
||||
ButtonArea.RectTransform.NonScaledSize = new Point(ButtonArea.Rect.Width, ContinueButton.Rect.Height);
|
||||
ButtonArea.RectTransform.IsFixedSize = true;
|
||||
|
||||
missionContent.Recalculate();
|
||||
//description overlapping with the buttons -> switch to small font
|
||||
if (missionDescription != null && missionDescription.Rect.Y + missionDescription.TextSize.Y > ButtonArea.Rect.Y)
|
||||
{
|
||||
missionDescription.Font = GUI.Style.SmallFont;
|
||||
//still overlapping -> shorten the text
|
||||
if (missionDescription.Rect.Y + missionDescription.TextSize.Y > ButtonArea.Rect.Y && missionDescription.WrappedText.Contains('\n'))
|
||||
{
|
||||
missionDescription.ToolTip = missionDescription.Text;
|
||||
missionDescription.Text = missionDescription.WrappedText.Split('\n').First() + "...";
|
||||
}
|
||||
}
|
||||
missionFrameContent.Recalculate();
|
||||
|
||||
// set layout -------------------------------------------------------------------
|
||||
|
||||
@@ -378,9 +375,114 @@ namespace Barotrauma
|
||||
return background;
|
||||
}
|
||||
|
||||
public void CreateReputationInfoPanel(GUIComponent parent, CampaignMode campaignMode)
|
||||
{
|
||||
GUIListBox reputationList = new GUIListBox(new RectTransform(Vector2.One, parent.RectTransform))
|
||||
{
|
||||
Padding = new Vector4(4, 10, 0, 0) * GUI.Scale
|
||||
};
|
||||
reputationList.ContentBackground.Color = Color.Transparent;
|
||||
|
||||
if (startLocation.Type.HasOutpost && startLocation.Reputation != null)
|
||||
{
|
||||
var iconStyle = GUI.Style.GetComponentStyle("LocationReputationIcon");
|
||||
var locationFrame = CreateReputationElement(
|
||||
reputationList.Content,
|
||||
startLocation.Name,
|
||||
startLocation.Reputation.Value, startLocation.Reputation.NormalizedValue, initialLocationReputation,
|
||||
startLocation.Type.Name, "",
|
||||
iconStyle?.GetDefaultSprite(), startLocation.Type.GetPortrait(0), iconStyle?.Color ?? Color.White);
|
||||
CreatePathUnlockElement(locationFrame, null, startLocation);
|
||||
}
|
||||
|
||||
foreach (Faction faction in campaignMode.Factions)
|
||||
{
|
||||
float initialReputation = faction.Reputation.Value;
|
||||
if (initialFactionReputations.ContainsKey(faction))
|
||||
{
|
||||
initialReputation = initialFactionReputations[faction];
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.AddWarning($"Could not determine reputation change for faction \"{faction.Prefab.Name}\" (faction was not present at the start of the round).");
|
||||
}
|
||||
var factionFrame = CreateReputationElement(
|
||||
reputationList.Content,
|
||||
faction.Prefab.Name,
|
||||
faction.Reputation.Value, faction.Reputation.NormalizedValue, initialReputation,
|
||||
faction.Prefab.ShortDescription, faction.Prefab.Description,
|
||||
faction.Prefab.Icon, faction.Prefab.BackgroundPortrait, faction.Prefab.IconColor);
|
||||
CreatePathUnlockElement(factionFrame, faction, null);
|
||||
}
|
||||
|
||||
float maxDescriptionHeight = 0.0f;
|
||||
foreach (GUIComponent child in reputationList.Content.Children)
|
||||
{
|
||||
var descriptionElement = child.FindChild("description", recursive: true) as GUITextBlock;
|
||||
maxDescriptionHeight = Math.Max(maxDescriptionHeight, descriptionElement.TextSize.Y * 1.1f);
|
||||
}
|
||||
foreach (GUIComponent child in reputationList.Content.Children)
|
||||
{
|
||||
var headerElement = child.FindChild("header", recursive: true) as GUITextBlock;
|
||||
var descriptionElement = child.FindChild("description", recursive: true) as GUITextBlock;
|
||||
descriptionElement.RectTransform.NonScaledSize = new Point(descriptionElement.Rect.Width, (int)maxDescriptionHeight);
|
||||
descriptionElement.RectTransform.IsFixedSize = true;
|
||||
child.RectTransform.NonScaledSize = new Point(child.Rect.Width, headerElement.Rect.Height + descriptionElement.RectTransform.Parent.Children.Sum(c => c.Rect.Height + ((GUILayoutGroup)descriptionElement.Parent).AbsoluteSpacing));
|
||||
}
|
||||
|
||||
void CreatePathUnlockElement(GUIComponent reputationFrame, Faction faction, Location location)
|
||||
{
|
||||
if (GameMain.GameSession?.Campaign?.Map != null)
|
||||
{
|
||||
foreach (LocationConnection connection in GameMain.GameSession.Campaign.Map.Connections)
|
||||
{
|
||||
if (!connection.Locked || (!connection.Locations[0].Discovered && !connection.Locations[1].Discovered)) { continue; }
|
||||
|
||||
var gateLocation = connection.Locations[0].IsGateBetweenBiomes ? connection.Locations[0] : connection.Locations[1];
|
||||
var unlockEvent =
|
||||
EventSet.PrefabList.Find(ep => ep.UnlockPathEvent && ep.BiomeIdentifier == gateLocation.LevelData.Biome.Identifier) ??
|
||||
EventSet.PrefabList.Find(ep => ep.UnlockPathEvent && string.IsNullOrEmpty(ep.BiomeIdentifier));
|
||||
|
||||
if (unlockEvent == null) { continue; }
|
||||
if (string.IsNullOrEmpty(unlockEvent.UnlockPathFaction) || unlockEvent.UnlockPathFaction.Equals("location", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (location == null || gateLocation != location) { continue; }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (faction == null || !faction.Prefab.Identifier.Equals(unlockEvent.UnlockPathFaction, StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
}
|
||||
|
||||
if (unlockEvent != null)
|
||||
{
|
||||
Reputation unlockReputation = gateLocation.Reputation;
|
||||
Faction unlockFaction = null;
|
||||
if (!string.IsNullOrEmpty(unlockEvent.UnlockPathFaction))
|
||||
{
|
||||
unlockFaction = GameMain.GameSession.Campaign.Factions.Find(f => f.Prefab.Identifier.Equals(unlockEvent.UnlockPathFaction, StringComparison.OrdinalIgnoreCase));
|
||||
unlockReputation = unlockFaction?.Reputation;
|
||||
}
|
||||
float normalizedUnlockReputation = MathUtils.InverseLerp(unlockReputation.MinReputation, unlockReputation.MaxReputation, unlockEvent.UnlockPathReputation);
|
||||
string unlockText = TextManager.GetWithVariables(
|
||||
"lockedpathreputationrequirement",
|
||||
new string[] { "[reputation]", "[biomename]" },
|
||||
new string[] { Reputation.GetFormattedReputationText(normalizedUnlockReputation, unlockEvent.UnlockPathReputation, addColorTags: true), $"‖color:gui.orange‖{connection.LevelData.Biome.DisplayName}‖end‖" });
|
||||
var unlockInfoPanel = new GUITextBlock(new RectTransform(new Vector2(0.8f, 0.0f), reputationFrame.RectTransform, Anchor.BottomCenter) { MinSize = new Point(0, GUI.IntScale(30)), AbsoluteOffset = new Point(0, GUI.IntScale(3)) },
|
||||
unlockText, style: "GUIButtonRound", textAlignment: Alignment.Center, textColor: GUI.Style.TextColor, parseRichText: true);
|
||||
unlockInfoPanel.Color = Color.Lerp(unlockInfoPanel.Color, Color.Black, 0.8f);
|
||||
if (unlockInfoPanel.TextSize.X > unlockInfoPanel.Rect.Width * 0.7f)
|
||||
{
|
||||
unlockInfoPanel.Font = GUI.SmallFont;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetHeaderText(bool gameOver, CampaignMode.TransitionType transitionType)
|
||||
{
|
||||
string locationName = Submarine.MainSub.AtEndPosition ? endLocation?.Name : startLocation?.Name;
|
||||
string locationName = Submarine.MainSub.AtEndExit ? endLocation?.Name : startLocation?.Name;
|
||||
|
||||
string textTag;
|
||||
if (gameOver)
|
||||
@@ -396,17 +498,23 @@ namespace Barotrauma
|
||||
textTag = "RoundSummaryLeaving";
|
||||
break;
|
||||
case CampaignMode.TransitionType.ProgressToNextLocation:
|
||||
case CampaignMode.TransitionType.ProgressToNextEmptyLocation:
|
||||
locationName = endLocation?.Name;
|
||||
textTag = "RoundSummaryProgress";
|
||||
break;
|
||||
case CampaignMode.TransitionType.ProgressToNextEmptyLocation:
|
||||
locationName = endLocation?.Name;
|
||||
textTag = "RoundSummaryProgressToEmptyLocation";
|
||||
break;
|
||||
case CampaignMode.TransitionType.ReturnToPreviousLocation:
|
||||
case CampaignMode.TransitionType.ReturnToPreviousEmptyLocation:
|
||||
locationName = startLocation?.Name;
|
||||
textTag = "RoundSummaryReturn";
|
||||
break;
|
||||
case CampaignMode.TransitionType.ReturnToPreviousEmptyLocation:
|
||||
locationName = startLocation?.Name;
|
||||
textTag = "RoundSummaryReturnToEmptyLocation";
|
||||
break;
|
||||
default:
|
||||
textTag = Submarine.MainSub.AtEndPosition ? "RoundSummaryProgress" : "RoundSummaryReturn";
|
||||
textTag = Submarine.MainSub.AtEndExit ? "RoundSummaryProgress" : "RoundSummaryReturn";
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -456,7 +564,7 @@ namespace Barotrauma
|
||||
|
||||
GUIListBox crewList = new GUIListBox(new RectTransform(Vector2.One, parent.RectTransform))
|
||||
{
|
||||
Padding = new Vector4(2, 5, 0, 0),
|
||||
Padding = new Vector4(4, 10, 0, 0) * GUI.Scale,
|
||||
AutoHideScrollBar = false
|
||||
};
|
||||
crewList.ContentBackground.Color = Color.Transparent;
|
||||
@@ -547,11 +655,11 @@ namespace Barotrauma
|
||||
ToolBox.LimitString(statusText, GUI.Font, characterColumnWidth), textAlignment: Alignment.Center, textColor: statusColor);
|
||||
}
|
||||
|
||||
private void CreateReputationElement(GUIComponent parent,
|
||||
private GUIFrame CreateReputationElement(GUIComponent parent,
|
||||
string name, float reputation, float normalizedReputation, float initialReputation,
|
||||
string shortDescription, string fullDescription, Sprite icon, Sprite backgroundPortrait, Color iconColor)
|
||||
{
|
||||
var factionFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.3f), parent.RectTransform), style: null);
|
||||
var factionFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.1f), parent.RectTransform), style: null);
|
||||
|
||||
if (backgroundPortrait != null)
|
||||
{
|
||||
@@ -568,13 +676,13 @@ namespace Barotrauma
|
||||
|
||||
var factionInfoHorizontal = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), factionFrame.RectTransform, Anchor.Center), childAnchor: Anchor.CenterLeft, isHorizontal: true)
|
||||
{
|
||||
RelativeSpacing = 0.02f,
|
||||
AbsoluteSpacing = GUI.IntScale(5),
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
var factionTextContent = new GUILayoutGroup(new RectTransform(Vector2.One, factionInfoHorizontal.RectTransform))
|
||||
{
|
||||
RelativeSpacing = 0.05f,
|
||||
AbsoluteSpacing = GUI.IntScale(10),
|
||||
Stretch = true
|
||||
};
|
||||
var factionIcon = new GUIImage(new RectTransform(new Point((int)(factionInfoHorizontal.Rect.Height * 0.7f)), factionInfoHorizontal.RectTransform, scaleBasis: ScaleBasis.Smallest), icon, scaleToFit: true)
|
||||
@@ -583,12 +691,48 @@ namespace Barotrauma
|
||||
};
|
||||
factionInfoHorizontal.Recalculate();
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), factionTextContent.RectTransform),
|
||||
var header = new GUITextBlock(new RectTransform(new Point(factionTextContent.Rect.Width, GUI.IntScale(40)), factionTextContent.RectTransform),
|
||||
name, font: GUI.SubHeadingFont)
|
||||
{
|
||||
Padding = Vector4.Zero
|
||||
Padding = Vector4.Zero,
|
||||
UserData = "header"
|
||||
};
|
||||
var factionDescription = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.6f), factionTextContent.RectTransform),
|
||||
header.RectTransform.IsFixedSize = true;
|
||||
|
||||
var sliderHolder = new GUILayoutGroup(new RectTransform(new Point((int)(factionTextContent.Rect.Width * 0.8f), GUI.IntScale(20.0f)), factionTextContent.RectTransform),
|
||||
childAnchor: Anchor.CenterLeft, isHorizontal: true)
|
||||
{
|
||||
RelativeSpacing = 0.05f,
|
||||
Stretch = true
|
||||
};
|
||||
sliderHolder.RectTransform.IsFixedSize = true;
|
||||
factionTextContent.Recalculate();
|
||||
|
||||
new GUICustomComponent(new RectTransform(new Vector2(0.8f, 1.0f), sliderHolder.RectTransform),
|
||||
onDraw: (sb, customComponent) => DrawReputationBar(sb, customComponent.Rect, normalizedReputation));
|
||||
|
||||
string reputationText = Reputation.GetFormattedReputationText(normalizedReputation, reputation, addColorTags: true);
|
||||
int reputationChange = (int)Math.Round(reputation - initialReputation);
|
||||
if (Math.Abs(reputationChange) > 0)
|
||||
{
|
||||
string changeText = $"{(reputationChange > 0 ? "+" : "") + reputationChange}";
|
||||
string colorStr = XMLExtensions.ColorToString(reputationChange > 0 ? GUI.Style.Green : GUI.Style.Red);
|
||||
var rtData = RichTextData.GetRichTextData($"{reputationText} (‖color:{colorStr}‖{changeText}‖color:end‖)", out string sanitizedText);
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), sliderHolder.RectTransform),
|
||||
rtData, sanitizedText,
|
||||
textAlignment: Alignment.CenterLeft, font: GUI.SubHeadingFont);
|
||||
}
|
||||
else
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), sliderHolder.RectTransform),
|
||||
reputationText,
|
||||
textAlignment: Alignment.CenterLeft, font: GUI.SubHeadingFont, parseRichText: true);
|
||||
}
|
||||
|
||||
//spacing
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.0f), factionTextContent.RectTransform) { MinSize = new Point(0, GUI.IntScale(5)) }, style: null);
|
||||
|
||||
var factionDescription = new GUITextBlock(new RectTransform(new Vector2(0.8f, 0.6f), factionTextContent.RectTransform),
|
||||
shortDescription, font: GUI.SmallFont, wrap: true)
|
||||
{
|
||||
UserData = "description",
|
||||
@@ -599,51 +743,32 @@ namespace Barotrauma
|
||||
factionDescription.ToolTip = fullDescription;
|
||||
}
|
||||
|
||||
var sliderHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), factionTextContent.RectTransform),
|
||||
childAnchor: Anchor.CenterLeft, isHorizontal: true)
|
||||
{
|
||||
RelativeSpacing = 0.05f,
|
||||
Stretch = true
|
||||
};
|
||||
sliderHolder.RectTransform.MaxSize = new Point(int.MaxValue, GUI.IntScale(25.0f));
|
||||
//spacing
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.0f), factionTextContent.RectTransform) { MinSize = new Point(0, GUI.IntScale(5)) }, style: null);
|
||||
|
||||
factionInfoHorizontal.Recalculate();
|
||||
factionTextContent.Recalculate();
|
||||
|
||||
new GUICustomComponent(new RectTransform(new Vector2(0.8f, 1.0f), sliderHolder.RectTransform),
|
||||
onDraw: (sb, customComponent) => DrawReputationBar(sb, customComponent.Rect, normalizedReputation));
|
||||
|
||||
string reputationText = ((int)Math.Round(reputation)).ToString();
|
||||
int reputationChange = (int)Math.Round( reputation - initialReputation);
|
||||
if (Math.Abs(reputationChange) > 0)
|
||||
{
|
||||
string changeText = $"{(reputationChange > 0 ? "+" : "") + reputationChange}";
|
||||
string colorStr = XMLExtensions.ColorToString(reputationChange > 0 ? GUI.Style.Green : GUI.Style.Red);
|
||||
var rtData = RichTextData.GetRichTextData($"{reputationText} (‖color:{colorStr}‖{changeText}‖color:end‖)", out string sanitizedText);
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), sliderHolder.RectTransform),
|
||||
rtData, sanitizedText,
|
||||
textAlignment: Alignment.CenterLeft, font: GUI.SubHeadingFont);
|
||||
}
|
||||
else
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), sliderHolder.RectTransform),
|
||||
reputationText,
|
||||
textAlignment: Alignment.CenterLeft, font: GUI.SubHeadingFont);
|
||||
}
|
||||
return factionFrame;
|
||||
}
|
||||
|
||||
public static void DrawReputationBar(SpriteBatch sb, Rectangle rect, float normalizedReputation)
|
||||
{
|
||||
GUI.DrawRectangle(sb, rect, GUI.Style.ColorInventoryBackground, isFilled: true);
|
||||
if (normalizedReputation < 0.5f)
|
||||
int segmentWidth = rect.Width / 5;
|
||||
rect.Width = segmentWidth * 5;
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
int barWidth = (int)((0.5f - normalizedReputation) * rect.Width);
|
||||
GUI.DrawRectangle(sb, new Rectangle(rect.Center.X - barWidth, rect.Y, barWidth, rect.Height), GUI.Style.Red, isFilled: true);
|
||||
GUI.DrawRectangle(sb, new Rectangle(rect.X + (segmentWidth * i), rect.Y, segmentWidth, rect.Height), Reputation.GetReputationColor(i / 5.0f), isFilled: true);
|
||||
GUI.DrawRectangle(sb, new Rectangle(rect.X + (segmentWidth * i), rect.Y, segmentWidth, rect.Height), GUI.Style.ColorInventoryBackground, isFilled: false);
|
||||
}
|
||||
else if (normalizedReputation > 0.5f)
|
||||
{
|
||||
int barWidth = (int)((normalizedReputation - 0.5f) * rect.Width);
|
||||
GUI.DrawRectangle(sb, new Rectangle(rect.Center.X, rect.Y, barWidth, rect.Height), GUI.Style.Green, isFilled: true);
|
||||
}
|
||||
GUI.DrawLine(sb, new Vector2(rect.Center.X, rect.Y - 2), new Vector2(rect.Center.X, rect.Bottom + 2), GUI.Style.TextColor);
|
||||
GUI.DrawRectangle(sb, rect, GUI.Style.ColorInventoryBackground, isFilled: false);
|
||||
|
||||
GUI.Arrow.Draw(sb, new Vector2(rect.X + rect.Width * normalizedReputation, rect.Y), GUI.Style.ColorInventoryBackground, scale: GUI.Scale, spriteEffect: SpriteEffects.FlipVertically);
|
||||
GUI.Arrow.Draw(sb, new Vector2(rect.X + rect.Width * normalizedReputation, rect.Y), GUI.Style.TextColor, scale: GUI.Scale * 0.8f, spriteEffect: SpriteEffects.FlipVertically);
|
||||
|
||||
GUI.DrawString(sb, new Vector2(rect.X, rect.Bottom), "-100", GUI.Style.TextColor, font: GUI.SmallFont);
|
||||
Vector2 textSize = GUI.SmallFont.MeasureString("100");
|
||||
GUI.DrawString(sb, new Vector2(rect.Right - textSize.X, rect.Bottom), "100", GUI.Style.TextColor, font: GUI.SmallFont);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ namespace Barotrauma
|
||||
Audio,
|
||||
VoiceChat,
|
||||
Controls,
|
||||
Gameplay,
|
||||
#if DEBUG
|
||||
Debug
|
||||
#endif
|
||||
@@ -532,29 +533,19 @@ namespace Barotrauma
|
||||
UserData = tab
|
||||
};
|
||||
|
||||
float tabWidth = 0.25f;
|
||||
float tabWidth = 1.0f / tabs.Length;
|
||||
#if DEBUG
|
||||
tabWidth = 0.2f;
|
||||
if (tab != Tab.Debug)
|
||||
{
|
||||
string buttonText = tab != Tab.Debug ? TextManager.Get("SettingsTab." + tab.ToString()) : "Debug";
|
||||
#else
|
||||
string buttonText = TextManager.Get("SettingsTab." + tab.ToString());
|
||||
#endif
|
||||
tabButtons[(int)tab] = new GUIButton(new RectTransform(new Vector2(tabWidth, 1.0f), tabButtonHolder.RectTransform),
|
||||
TextManager.Get("SettingsTab." + tab.ToString()), style: "GUITabButton")
|
||||
{
|
||||
UserData = tab,
|
||||
OnClicked = (bt, userdata) => { SelectTab((Tab)userdata); return true; }
|
||||
};
|
||||
#if DEBUG
|
||||
}
|
||||
else
|
||||
|
||||
tabButtons[(int)tab] = new GUIButton(new RectTransform(new Vector2(tabWidth, 1.0f), tabButtonHolder.RectTransform), style: "GUITabButton")
|
||||
{
|
||||
tabButtons[(int)tab] = new GUIButton(new RectTransform(new Vector2(tabWidth, 1.0f), tabButtonHolder.RectTransform), "Debug", style: "GUITabButton")
|
||||
{
|
||||
UserData = tab,
|
||||
OnClicked = (bt, userdata) => { SelectTab((Tab)userdata); return true; }
|
||||
};
|
||||
}
|
||||
#endif
|
||||
UserData = tab,
|
||||
OnClicked = (bt, userdata) => { SelectTab((Tab)userdata); return true; }
|
||||
};
|
||||
tabButtons[(int)tab].Text = ToolBox.LimitString(buttonText, tabButtons[(int)tab].Font, (int)(0.75f * tabWidth * tabButtonHolder.Rect.Width));
|
||||
}
|
||||
|
||||
new GUIButton(new RectTransform(new Vector2(0.05f, 0.75f), tabButtonHolder.RectTransform, Anchor.BottomRight) { RelativeOffset = new Vector2(0.0f, 0.2f) }, style: "GUIBugButton")
|
||||
@@ -669,19 +660,6 @@ namespace Barotrauma
|
||||
Selected = TextureCompressionEnabled
|
||||
};
|
||||
|
||||
GUITickBox pauseOnFocusLostBox = new GUITickBox(new RectTransform(tickBoxScale, leftColumn.RectTransform),
|
||||
TextManager.Get("PauseOnFocusLost"))
|
||||
{
|
||||
Selected = PauseOnFocusLost,
|
||||
ToolTip = TextManager.Get("PauseOnFocusLostToolTip"),
|
||||
OnSelected = (tickBox) =>
|
||||
{
|
||||
PauseOnFocusLost = tickBox.Selected;
|
||||
UnsavedSettings = true;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
GUITextBlock particleLimitText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("ParticleLimit"), font: GUI.SubHeadingFont, wrap: true);
|
||||
GUIScrollBar particleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), style: "GUISlider",
|
||||
barSize: 0.1f)
|
||||
@@ -773,56 +751,6 @@ namespace Barotrauma
|
||||
}
|
||||
};
|
||||
|
||||
GUITextBlock HUDScaleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("HUDScale"), font: GUI.SubHeadingFont, wrap: true);
|
||||
GUIScrollBar HUDScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform),
|
||||
style: "GUISlider", barSize: 0.1f)
|
||||
{
|
||||
UserData = HUDScaleText,
|
||||
BarScroll = (HUDScale - MinHUDScale) / (MaxHUDScale - MinHUDScale),
|
||||
OnMoved = (scrollBar, scroll) =>
|
||||
{
|
||||
HUDScale = MathHelper.Lerp(MinHUDScale, MaxHUDScale, scroll);
|
||||
ChangeSliderText(scrollBar, HUDScale);
|
||||
OnHUDScaleChanged?.Invoke();
|
||||
return true;
|
||||
},
|
||||
Step = 0.02f
|
||||
};
|
||||
HUDScaleScrollBar.OnMoved(HUDScaleScrollBar, HUDScaleScrollBar.BarScroll);
|
||||
|
||||
GUITextBlock inventoryScaleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("InventoryScale"), font: GUI.SubHeadingFont);
|
||||
GUIScrollBar inventoryScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform),
|
||||
style: "GUISlider", barSize: 0.1f)
|
||||
{
|
||||
UserData = inventoryScaleText,
|
||||
BarScroll = (InventoryScale - MinInventoryScale) / (MaxInventoryScale - MinInventoryScale),
|
||||
OnMoved = (scrollBar, scroll) =>
|
||||
{
|
||||
InventoryScale = MathHelper.Lerp(MinInventoryScale, MaxInventoryScale, scroll);
|
||||
ChangeSliderText(scrollBar, InventoryScale);
|
||||
return true;
|
||||
},
|
||||
Step = 0.02f
|
||||
};
|
||||
inventoryScaleScrollBar.OnMoved(inventoryScaleScrollBar, inventoryScaleScrollBar.BarScroll);
|
||||
|
||||
GUITextBlock textScaleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("TextScale"), font: GUI.SubHeadingFont);
|
||||
GUIScrollBar textScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform),
|
||||
style: "GUISlider", barSize: 0.1f)
|
||||
{
|
||||
UserData = textScaleText,
|
||||
BarScroll = (TextScale - MinTextScale) / (MaxTextScale - MinTextScale),
|
||||
OnMoved = (scrollBar, scroll) =>
|
||||
{
|
||||
TextScale = MathHelper.Lerp(MinTextScale, MaxTextScale, scroll);
|
||||
textScaleDirty = true;
|
||||
ChangeSliderText(scrollBar, TextScale);
|
||||
return true;
|
||||
},
|
||||
Step = 0.01f
|
||||
};
|
||||
textScaleScrollBar.OnMoved(textScaleScrollBar, textScaleScrollBar.BarScroll);
|
||||
|
||||
/// Audio tab ----------------------------------------------------------------
|
||||
|
||||
var audioContent = new GUILayoutGroup(new RectTransform(new Vector2(0.97f, 0.97f), tabs[(int)Tab.Audio].RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter)
|
||||
@@ -1177,13 +1105,14 @@ namespace Barotrauma
|
||||
style: "GUISlider", barSize: 0.05f)
|
||||
{
|
||||
UserData = micVolumeText,
|
||||
Range = new Vector2(0,540),
|
||||
Step = 1.0f / 9.0f
|
||||
Range = new Vector2(0, ((float)VoipConfig.BUFFER_SIZE / (float)VoipConfig.FREQUENCY) * 1000.0f * 25.0f),
|
||||
Step = 1.0f / 25.0f
|
||||
};
|
||||
cutoffPreventionSlider.BarScrollValue = VoiceChatCutoffPrevention;
|
||||
cutoffPreventionSlider.OnMoved = (scrollBar, scroll) =>
|
||||
{
|
||||
VoiceChatCutoffPrevention = (int)scrollBar.BarScrollValue;
|
||||
int bufferMsLength = (int)(((float)VoipConfig.BUFFER_SIZE / (float)VoipConfig.FREQUENCY) * 1000.0f);
|
||||
VoiceChatCutoffPrevention = (int)Math.Round(scrollBar.BarScrollValue / bufferMsLength) * bufferMsLength;
|
||||
cutoffPreventionText.Text = TextManager.Get("CutoffPrevention") +
|
||||
" " + TextManager.GetWithVariable("timeformatmilliseconds", "[milliseconds]", VoiceChatCutoffPrevention.ToString());
|
||||
return true;
|
||||
@@ -1379,6 +1308,93 @@ namespace Barotrauma
|
||||
GUITextBlock.AutoScaleAndNormalize(defaultBindingsButton.TextBlock, legacyBindingsButton.TextBlock);
|
||||
};
|
||||
|
||||
/// Gameplay tab -------------------------------------------------------------
|
||||
var gameplaySettingsGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.46f, 0.95f), tabs[(int)Tab.Gameplay].RectTransform, Anchor.TopLeft)
|
||||
{ RelativeOffset = new Vector2(0.025f, 0.02f) })
|
||||
{ RelativeSpacing = 0.01f };
|
||||
|
||||
GUITickBox pauseOnFocusLostBox = new GUITickBox(new RectTransform(tickBoxScale, gameplaySettingsGroup.RectTransform),
|
||||
TextManager.Get("PauseOnFocusLost"))
|
||||
{
|
||||
Selected = PauseOnFocusLost,
|
||||
ToolTip = TextManager.Get("PauseOnFocusLostToolTip"),
|
||||
OnSelected = (tickBox) =>
|
||||
{
|
||||
PauseOnFocusLost = tickBox.Selected;
|
||||
UnsavedSettings = true;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
GUITickBox disableInGameHintsBox = new GUITickBox(new RectTransform(tickBoxScale, gameplaySettingsGroup.RectTransform),
|
||||
TextManager.Get("DisableInGameHints"))
|
||||
{
|
||||
Selected = DisableInGameHints,
|
||||
ToolTip = TextManager.Get("DisableInGameHintsToolTip"),
|
||||
OnSelected = (tickBox) =>
|
||||
{
|
||||
DisableInGameHints = tickBox.Selected;
|
||||
if (!DisableInGameHints && GameMain.Config?.IgnoredHints != null)
|
||||
{
|
||||
// Reset the ignored hints when the hints are re-enabled (to-be-replaced by a separate button)
|
||||
GameMain.Config.IgnoredHints.Clear();
|
||||
}
|
||||
UnsavedSettings = true;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
GUITextBlock HUDScaleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), gameplaySettingsGroup.RectTransform), TextManager.Get("HUDScale"), font: GUI.SubHeadingFont, wrap: true);
|
||||
GUIScrollBar HUDScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), gameplaySettingsGroup.RectTransform),
|
||||
style: "GUISlider", barSize: 0.1f)
|
||||
{
|
||||
UserData = HUDScaleText,
|
||||
BarScroll = (HUDScale - MinHUDScale) / (MaxHUDScale - MinHUDScale),
|
||||
OnMoved = (scrollBar, scroll) =>
|
||||
{
|
||||
HUDScale = MathHelper.Lerp(MinHUDScale, MaxHUDScale, scroll);
|
||||
ChangeSliderText(scrollBar, HUDScale);
|
||||
OnHUDScaleChanged?.Invoke();
|
||||
return true;
|
||||
},
|
||||
Step = 0.02f
|
||||
};
|
||||
HUDScaleScrollBar.OnMoved(HUDScaleScrollBar, HUDScaleScrollBar.BarScroll);
|
||||
|
||||
GUITextBlock inventoryScaleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), gameplaySettingsGroup.RectTransform), TextManager.Get("InventoryScale"), font: GUI.SubHeadingFont);
|
||||
GUIScrollBar inventoryScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), gameplaySettingsGroup.RectTransform),
|
||||
style: "GUISlider", barSize: 0.1f)
|
||||
{
|
||||
UserData = inventoryScaleText,
|
||||
BarScroll = (InventoryScale - MinInventoryScale) / (MaxInventoryScale - MinInventoryScale),
|
||||
OnMoved = (scrollBar, scroll) =>
|
||||
{
|
||||
InventoryScale = MathHelper.Lerp(MinInventoryScale, MaxInventoryScale, scroll);
|
||||
ChangeSliderText(scrollBar, InventoryScale);
|
||||
return true;
|
||||
},
|
||||
Step = 0.02f
|
||||
};
|
||||
inventoryScaleScrollBar.OnMoved(inventoryScaleScrollBar, inventoryScaleScrollBar.BarScroll);
|
||||
|
||||
GUITextBlock textScaleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), gameplaySettingsGroup.RectTransform), TextManager.Get("TextScale"), font: GUI.SubHeadingFont);
|
||||
GUIScrollBar textScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), gameplaySettingsGroup.RectTransform),
|
||||
style: "GUISlider", barSize: 0.1f)
|
||||
{
|
||||
UserData = textScaleText,
|
||||
BarScroll = (TextScale - MinTextScale) / (MaxTextScale - MinTextScale),
|
||||
OnMoved = (scrollBar, scroll) =>
|
||||
{
|
||||
TextScale = MathHelper.Lerp(MinTextScale, MaxTextScale, scroll);
|
||||
textScaleDirty = true;
|
||||
ChangeSliderText(scrollBar, TextScale);
|
||||
return true;
|
||||
},
|
||||
Step = 0.01f
|
||||
};
|
||||
textScaleScrollBar.OnMoved(textScaleScrollBar, textScaleScrollBar.BarScroll);
|
||||
|
||||
/// Bottom buttons -------------------------------------------------------------
|
||||
new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonArea.RectTransform, Anchor.BottomLeft),
|
||||
TextManager.Get("Cancel"))
|
||||
{
|
||||
@@ -1464,55 +1480,54 @@ namespace Barotrauma
|
||||
{ RelativeOffset = new Vector2(0.02f, 0.02f) })
|
||||
{ RelativeSpacing = 0.01f };
|
||||
|
||||
var automaticQuickStartTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), "Automatic quickstart enabled", style: "GUITickBox");
|
||||
automaticQuickStartTickBox.Selected = AutomaticQuickStartEnabled;
|
||||
automaticQuickStartTickBox.ToolTip = "Will the game automatically move on to Quickstart when the game is launched";
|
||||
automaticQuickStartTickBox.OnSelected = (tickBox) =>
|
||||
void addDebugTickBox(bool initialValue, Action<bool> set, string label, string tooltip)
|
||||
{
|
||||
AutomaticQuickStartEnabled = tickBox.Selected;
|
||||
UnsavedSettings = true;
|
||||
return true;
|
||||
};
|
||||
var tickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), label, style: "GUITickBox");
|
||||
tickBox.Selected = initialValue;
|
||||
tickBox.ToolTip = tooltip;
|
||||
tickBox.OnSelected = (tickBox) =>
|
||||
{
|
||||
set(tickBox.Selected);
|
||||
UnsavedSettings = true;
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
var automaticCampaignLoadTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), "Automatic campaign load enabled", style: "GUITickBox");
|
||||
automaticCampaignLoadTickBox.Selected = AutomaticCampaignLoadEnabled;
|
||||
automaticCampaignLoadTickBox.ToolTip = "Will the game automatically load the latest campaign save when the game is launched";
|
||||
automaticCampaignLoadTickBox.OnSelected = (tickBox) =>
|
||||
{
|
||||
AutomaticCampaignLoadEnabled = tickBox.Selected;
|
||||
UnsavedSettings = true;
|
||||
return true;
|
||||
};
|
||||
addDebugTickBox(
|
||||
AutomaticQuickStartEnabled,
|
||||
(b) => AutomaticQuickStartEnabled = b,
|
||||
"Automatic quickstart enabled",
|
||||
"Will the game automatically move on to Quickstart when the game is launched");
|
||||
|
||||
var showSplashScreenTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), "Splash screen enabled", style: "GUITickBox");
|
||||
showSplashScreenTickBox.Selected = EnableSplashScreen;
|
||||
showSplashScreenTickBox.ToolTip = "Are the splash screens shown when the game is launched";
|
||||
showSplashScreenTickBox.OnSelected = (tickBox) =>
|
||||
{
|
||||
EnableSplashScreen = tickBox.Selected;
|
||||
UnsavedSettings = true;
|
||||
return true;
|
||||
};
|
||||
addDebugTickBox(
|
||||
AutomaticCampaignLoadEnabled,
|
||||
(b) => AutomaticCampaignLoadEnabled = b,
|
||||
"Automatic campaign load enabled",
|
||||
"Will the game automatically load the latest campaign save when the game is launched");
|
||||
|
||||
var verboseLoggingTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), "Verbose logging enabled", style: "GUITickBox");
|
||||
verboseLoggingTickBox.Selected = VerboseLogging;
|
||||
verboseLoggingTickBox.ToolTip = "Should verbose logging be used";
|
||||
verboseLoggingTickBox.OnSelected = (tickBox) =>
|
||||
{
|
||||
VerboseLogging = tickBox.Selected;
|
||||
UnsavedSettings = true;
|
||||
return true;
|
||||
};
|
||||
addDebugTickBox(
|
||||
EnableSplashScreen,
|
||||
(b) => EnableSplashScreen = b,
|
||||
"Splash screen enabled",
|
||||
"Are the splash screens shown when the game is launched");
|
||||
|
||||
var textManagerDebugModeTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), "TextManager debug mode enabled", style: "GUITickBox");
|
||||
textManagerDebugModeTickBox.Selected = TextManagerDebugModeEnabled;
|
||||
textManagerDebugModeTickBox.ToolTip = "Does the TextManager return the text tags for debug purposes?";
|
||||
textManagerDebugModeTickBox.OnSelected = (tickBox) =>
|
||||
{
|
||||
TextManagerDebugModeEnabled = tickBox.Selected;
|
||||
UnsavedSettings = true;
|
||||
return true;
|
||||
};
|
||||
addDebugTickBox(
|
||||
VerboseLogging,
|
||||
(b) => VerboseLogging = b,
|
||||
"Verbose logging enabled",
|
||||
"Should verbose logging be used");
|
||||
|
||||
addDebugTickBox(
|
||||
TextManagerDebugModeEnabled,
|
||||
(b) => TextManagerDebugModeEnabled = b,
|
||||
"TextManager debug mode enabled",
|
||||
"Does the TextManager return the text tags for debug purposes?");
|
||||
|
||||
addDebugTickBox(
|
||||
ModBreakerMode,
|
||||
(b) => ModBreakerMode = b,
|
||||
"Mod breaker mode enabled",
|
||||
"Do horrible things when loading mods to see if it breaks?");
|
||||
#endif
|
||||
|
||||
UnsavedSettings = false; // Reset unsaved settings to false once the UI has been created
|
||||
|
||||
@@ -160,13 +160,6 @@ namespace Barotrauma
|
||||
CreateSlots();
|
||||
}
|
||||
|
||||
public override void RemoveItem(Item item)
|
||||
{
|
||||
if (!Contains(item)) { return; }
|
||||
base.RemoveItem(item);
|
||||
CreateSlots();
|
||||
}
|
||||
|
||||
public override void CreateSlots()
|
||||
{
|
||||
if (visualSlots == null) { visualSlots = new VisualSlot[capacity]; }
|
||||
@@ -639,10 +632,16 @@ namespace Barotrauma
|
||||
foreach (Item doubleClickedItem in doubleClickedItems)
|
||||
{
|
||||
QuickUseItem(doubleClickedItem, true, true, true, quickUseAction, playSound: doubleClickedItem == doubleClickedItems.First());
|
||||
//only use one item if we're equipping or using it as a treatment
|
||||
if (quickUseAction == QuickUseAction.Equip || quickUseAction == QuickUseAction.UseTreatment)
|
||||
{
|
||||
break;
|
||||
}
|
||||
//if the item was put in a limb slot, only put one item from the stack
|
||||
if (doubleClickedItem.ParentInventory == this && !IsInLimbSlot(doubleClickedItem, InvSlotType.Any))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -696,6 +695,7 @@ namespace Barotrauma
|
||||
if (firstItem != null && !DraggingItems.Contains(firstItem) && Character.Controlled?.Inventory == this &&
|
||||
GUI.KeyboardDispatcher.Subscriber == null && !CrewManager.IsCommandInterfaceOpen && PlayerInput.InventoryKeyHit(visualSlots[i].InventoryKeyIndex))
|
||||
{
|
||||
if (SubEditorScreen.IsSubEditor() && SubEditorScreen.SkipInventorySlotUpdate) { continue; }
|
||||
#if LINUX
|
||||
// some window managers on Linux use windows key + number to change workspaces or perform other actions
|
||||
if (PlayerInput.KeyDown(Keys.RightWindows) || PlayerInput.KeyDown(Keys.LeftWindows)) { continue; }
|
||||
@@ -810,6 +810,8 @@ namespace Barotrauma
|
||||
highlightedSubInventorySlot.Inventory.HideTimer = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
HintManager.OnShowSubInventory(slotRef?.Item);
|
||||
}
|
||||
|
||||
public void AssignQuickUseNumKeys()
|
||||
@@ -835,6 +837,13 @@ namespace Barotrauma
|
||||
|
||||
if (item.ParentInventory != this)
|
||||
{
|
||||
if (Screen.Selected == GameMain.GameScreen)
|
||||
{
|
||||
if (item.NonInteractable || item.NonPlayerTeamInteractable)
|
||||
{
|
||||
return QuickUseAction.None;
|
||||
}
|
||||
}
|
||||
if (item.ParentInventory == null || item.ParentInventory.Locked)
|
||||
{
|
||||
return QuickUseAction.None;
|
||||
|
||||
@@ -70,17 +70,15 @@ namespace Barotrauma.Items.Components
|
||||
public override void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
{
|
||||
base.ClientRead(type, msg, sendingTime);
|
||||
|
||||
bool readAttachData = msg.ReadBoolean();
|
||||
if (!readAttachData) { return; }
|
||||
|
||||
bool shouldBeAttached = msg.ReadBoolean();
|
||||
Vector2 simPosition = new Vector2(msg.ReadSingle(), msg.ReadSingle());
|
||||
UInt16 submarineID = msg.ReadUInt16();
|
||||
Submarine sub = Entity.FindEntityByID(submarineID) as Submarine;
|
||||
|
||||
if (!attachable)
|
||||
{
|
||||
DebugConsole.ThrowError("Received an attachment event for an item that's not attachable.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (shouldBeAttached)
|
||||
{
|
||||
if (!attached)
|
||||
@@ -96,7 +94,6 @@ namespace Barotrauma.Items.Components
|
||||
if (attached)
|
||||
{
|
||||
DropConnectedWires(null);
|
||||
|
||||
if (body != null)
|
||||
{
|
||||
item.body = body;
|
||||
@@ -106,6 +103,11 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
DeattachFromWall();
|
||||
}
|
||||
else
|
||||
{
|
||||
item.SetTransform(simPosition, 0.0f);
|
||||
item.Submarine = sub;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,6 +179,23 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
// Expand the frame vertically if it's too small to fit the text
|
||||
if (label != null && label.RectTransform.RelativeSize.Y > 0.5f)
|
||||
{
|
||||
int newHeight = (int)(GuiFrame.Rect.Height + (2 * (label.RectTransform.RelativeSize.Y - 0.5f) * content.Rect.Height));
|
||||
if (newHeight > GuiFrame.RectTransform.MaxSize.Y)
|
||||
{
|
||||
Point newMaxSize = GuiFrame.RectTransform.MaxSize;
|
||||
newMaxSize.Y = newHeight;
|
||||
GuiFrame.RectTransform.MaxSize = newMaxSize;
|
||||
}
|
||||
GuiFrame.RectTransform.Resize(new Point(GuiFrame.Rect.Width, newHeight));
|
||||
content.RectTransform.Resize(GuiFrame.Rect.Size - GUIStyle.ItemFrameMargin);
|
||||
label.CalculateHeightFromText();
|
||||
guiCustomComponent.RectTransform.Resize(new Vector2(1.0f, Math.Max(1.0f - label.RectTransform.RelativeSize.Y, minInventoryAreaSize)));
|
||||
}
|
||||
|
||||
Inventory.RectTransform = guiCustomComponent.RectTransform;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private Pair<Rectangle, string> tooltip;
|
||||
|
||||
private GUITextBlock requiredTimeBlock;
|
||||
|
||||
partial void InitProjSpecific()
|
||||
{
|
||||
CreateGUI();
|
||||
@@ -384,8 +386,6 @@ namespace Barotrauma.Items.Components
|
||||
FabricationRecipe targetItem = fabricatedItem ?? selectedItem;
|
||||
if (targetItem != null)
|
||||
{
|
||||
var itemIcon = targetItem.TargetItem.InventoryIcon ?? targetItem.TargetItem.sprite;
|
||||
|
||||
Rectangle slotRect = outputContainer.Inventory.visualSlots[0].Rect;
|
||||
|
||||
if (fabricatedItem != null)
|
||||
@@ -398,11 +398,15 @@ namespace Barotrauma.Items.Components
|
||||
GUI.Style.Green * 0.5f, isFilled: true);
|
||||
}
|
||||
|
||||
itemIcon.Draw(
|
||||
spriteBatch,
|
||||
slotRect.Center.ToVector2(),
|
||||
color: targetItem.TargetItem.InventoryIconColor * 0.4f,
|
||||
scale: Math.Min(slotRect.Width / itemIcon.size.X, slotRect.Height / itemIcon.size.Y) * 0.9f);
|
||||
if (outputContainer.Inventory.IsEmpty())
|
||||
{
|
||||
var itemIcon = targetItem.TargetItem.InventoryIcon ?? targetItem.TargetItem.sprite;
|
||||
itemIcon.Draw(
|
||||
spriteBatch,
|
||||
slotRect.Center.ToVector2(),
|
||||
color: targetItem.TargetItem.InventoryIconColor * 0.4f,
|
||||
scale: Math.Min(slotRect.Width / itemIcon.size.X, slotRect.Height / itemIcon.size.Y) * 0.9f);
|
||||
}
|
||||
}
|
||||
|
||||
if (tooltip != null)
|
||||
@@ -522,7 +526,7 @@ namespace Barotrauma.Items.Components
|
||||
AutoScaleHorizontal = true,
|
||||
};
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedReqFrame.RectTransform), ToolBox.SecondsToReadableTime(requiredTime),
|
||||
requiredTimeBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedReqFrame.RectTransform), ToolBox.SecondsToReadableTime(requiredTime),
|
||||
font: GUI.SmallFont);
|
||||
return true;
|
||||
}
|
||||
@@ -606,6 +610,12 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
partial void UpdateRequiredTimeProjSpecific()
|
||||
{
|
||||
if (requiredTimeBlock == null) { return; }
|
||||
requiredTimeBlock.Text = ToolBox.SecondsToReadableTime(timeUntilReady > 0.0f ? timeUntilReady : requiredTime);
|
||||
}
|
||||
|
||||
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
|
||||
{
|
||||
int itemIndex = pendingFabricatedItem == null ? -1 : fabricationRecipes.IndexOf(pendingFabricatedItem);
|
||||
|
||||
@@ -21,18 +21,14 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private readonly List<Submarine> displayedSubs = new List<Submarine>();
|
||||
|
||||
private Point prevResolution;
|
||||
|
||||
partial void InitProjSpecific(XElement element)
|
||||
{
|
||||
noPowerTip = TextManager.Get("SteeringNoPowerTip");
|
||||
CreateGUI();
|
||||
}
|
||||
|
||||
protected override void OnResolutionChanged()
|
||||
{
|
||||
base.OnResolutionChanged();
|
||||
CreateHUD();
|
||||
}
|
||||
|
||||
protected override void CreateGUI()
|
||||
{
|
||||
GuiFrame.RectTransform.RelativeOffset = new Vector2(0.05f, 0.0f);
|
||||
@@ -76,15 +72,10 @@ namespace Barotrauma.Items.Components
|
||||
hullInfoFrame.AddToGUIUpdateList(order: 1);
|
||||
}
|
||||
|
||||
public override void OnMapLoaded()
|
||||
{
|
||||
base.OnMapLoaded();
|
||||
CreateHUD();
|
||||
}
|
||||
|
||||
private void CreateHUD()
|
||||
{
|
||||
submarineContainer.ClearChildren();
|
||||
prevResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
|
||||
submarineContainer?.ClearChildren();
|
||||
|
||||
if (item.Submarine == null) { return; }
|
||||
|
||||
@@ -94,19 +85,15 @@ namespace Barotrauma.Items.Components
|
||||
displayedSubs.AddRange(item.Submarine.DockedTo);
|
||||
}
|
||||
|
||||
public override void FlipX(bool relativeToSub)
|
||||
{
|
||||
CreateHUD();
|
||||
}
|
||||
|
||||
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
|
||||
{
|
||||
//recreate HUD if the subs we should display have changed
|
||||
if ((item.Submarine == null && displayedSubs.Count > 0) || //item not inside a sub anymore, but display is still showing subs
|
||||
!displayedSubs.Contains(item.Submarine) || //current sub not displayer
|
||||
item.Submarine.DockedTo.Any(s => !displayedSubs.Contains(s)) || //some of the docked subs not diplayed
|
||||
!submarineContainer.Children.Any() || // We lack a GUI
|
||||
displayedSubs.Any(s => s != item.Submarine && !item.Submarine.DockedTo.Contains(s))) //displaying a sub that shouldn't be displayed
|
||||
if ((item.Submarine == null && displayedSubs.Count > 0) || //item not inside a sub anymore, but display is still showing subs
|
||||
!displayedSubs.Contains(item.Submarine) || //current sub not displayer
|
||||
prevResolution.X != GameMain.GraphicsWidth || prevResolution.Y != GameMain.GraphicsHeight || //resolution changed
|
||||
item.Submarine.DockedTo.Any(s => !displayedSubs.Contains(s)) || //some of the docked subs not diplayed
|
||||
!submarineContainer.Children.Any() || // We lack a GUI
|
||||
displayedSubs.Any(s => s != item.Submarine && !item.Submarine.DockedTo.Contains(s))) //displaying a sub that shouldn't be displayed
|
||||
{
|
||||
CreateHUD();
|
||||
}
|
||||
|
||||
@@ -16,7 +16,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
Default,
|
||||
Disruption,
|
||||
Destructible
|
||||
Destructible,
|
||||
LongRange
|
||||
}
|
||||
|
||||
private PathFinder pathFinder;
|
||||
@@ -69,6 +70,9 @@ namespace Barotrauma.Items.Components
|
||||
private const float DisruptionUpdateInterval = 0.2f;
|
||||
private float disruptionUpdateTimer;
|
||||
|
||||
private const float LongRangeUpdateInterval = 10.0f;
|
||||
private float longRangeUpdateTimer;
|
||||
|
||||
private float showDirectionalIndicatorTimer;
|
||||
|
||||
private readonly List<LevelObject> nearbyObjects = new List<LevelObject>();
|
||||
@@ -122,6 +126,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
BlipType.Destructible,
|
||||
new Color[] { Color.TransparentBlack, new Color(74, 113, 75) * 0.8f, new Color(151, 236, 172) * 0.8f, new Color(153, 217, 234) * 0.8f }
|
||||
},
|
||||
{
|
||||
BlipType.LongRange,
|
||||
new Color[] { Color.TransparentBlack, Color.TransparentBlack, new Color(254, 68, 19) * 0.8f, Color.TransparentBlack }
|
||||
}
|
||||
};
|
||||
|
||||
@@ -133,7 +141,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public static Vector2 GUISizeCalculation => Vector2.One * Math.Min(GUI.RelativeHorizontalAspectRatio, 1f) * sonarAreaSize;
|
||||
|
||||
private List<Tuple<Vector2, List<Item>>> MineralClusters { get; set; }
|
||||
private List<(Vector2 center, List<Item> resources)> MineralClusters { get; set; }
|
||||
|
||||
private readonly List<GUITextBlock> textBlocksToScaleAndNormalize = new List<GUITextBlock>();
|
||||
|
||||
@@ -471,25 +479,26 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (MineralClusters == null)
|
||||
{
|
||||
MineralClusters = new List<Tuple<Vector2, List<Item>>>();
|
||||
foreach (var p in Level.Loaded.PathPoints)
|
||||
MineralClusters = new List<(Vector2, List<Item>)>();
|
||||
Level.Loaded.PathPoints.ForEach(p => p.ClusterLocations.ForEach(c => AddIfValid(c)));
|
||||
Level.Loaded.AbyssResources.ForEach(c => AddIfValid(c));
|
||||
|
||||
void AddIfValid(Level.ClusterLocation c)
|
||||
{
|
||||
foreach (var c in p.ClusterLocations)
|
||||
if (c.Resources == null) { return; }
|
||||
if (c.Resources.None(i => i != null && !i.Removed && i.Tags.Contains("ore"))) { return; }
|
||||
var pos = Vector2.Zero;
|
||||
foreach (var r in c.Resources)
|
||||
{
|
||||
if (c.Resources.None(i => i != null && !i.Removed && i.Tags.Contains("ore"))) { continue; }
|
||||
var pos = Vector2.Zero;
|
||||
foreach (var r in c.Resources)
|
||||
{
|
||||
pos += r.WorldPosition;
|
||||
}
|
||||
pos /= c.Resources.Count;
|
||||
MineralClusters.Add(new Tuple<Vector2, List<Item>>(pos, c.Resources));
|
||||
pos += r.WorldPosition;
|
||||
}
|
||||
pos /= c.Resources.Count;
|
||||
MineralClusters.Add((center: pos, resources: c.Resources));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MineralClusters.RemoveAll(t => t.Item2 == null || t.Item2.None() || t.Item2.All(i => i == null || i.Removed));
|
||||
MineralClusters.RemoveAll(c => c.resources == null || c.resources.None() || c.resources.All(i => i == null || i.Removed));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -673,7 +682,6 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
disruptionUpdateTimer -= deltaTime;
|
||||
|
||||
for (var pingIndex = 0; pingIndex < activePingsCount; ++pingIndex)
|
||||
{
|
||||
var activePing = activePings[pingIndex];
|
||||
@@ -683,12 +691,46 @@ namespace Barotrauma.Items.Components
|
||||
pingRadius, activePing.PrevPingRadius, displayScale, range / zoom, passive: false, pingStrength: 2.0f);
|
||||
activePing.PrevPingRadius = pingRadius;
|
||||
}
|
||||
|
||||
if (disruptionUpdateTimer <= 0.0f)
|
||||
{
|
||||
disruptionUpdateTimer = DisruptionUpdateInterval;
|
||||
}
|
||||
|
||||
longRangeUpdateTimer -= deltaTime;
|
||||
if (longRangeUpdateTimer <= 0.0f)
|
||||
{
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
if (c.AnimController.CurrentHull != null || !c.Enabled) { continue; }
|
||||
if (c.Params.HideInSonar) { continue; }
|
||||
|
||||
if (!c.IsUnconscious && c.Params.DistantSonarRange > 0.0f &&
|
||||
((c.WorldPosition - transducerCenter) * displayScale).LengthSquared() > DisplayRadius * DisplayRadius)
|
||||
{
|
||||
Vector2 targetVector = c.WorldPosition - transducerCenter;
|
||||
if (targetVector.LengthSquared() > MathUtils.Pow2(c.Params.DistantSonarRange)) { continue; }
|
||||
float dist = targetVector.Length();
|
||||
Vector2 targetDir = targetVector / dist;
|
||||
int blipCount = (int)MathHelper.Clamp(c.Mass, 50, 200);
|
||||
for (int i = 0; i < blipCount; i++)
|
||||
{
|
||||
float angle = Rand.Range(-0.5f, 0.5f);
|
||||
Vector2 blipDir = MathUtils.RotatePoint(targetDir, angle);
|
||||
Vector2 invBlipDir = MathUtils.RotatePoint(targetDir, -angle);
|
||||
var longRangeBlip = new SonarBlip(transducerCenter + blipDir * Range * 0.9f, Rand.Range(1.9f, 2.1f), Rand.Range(1.0f, 1.5f), BlipType.LongRange)
|
||||
{
|
||||
Velocity = -invBlipDir * (MathUtils.Round(Rand.Range(8000.0f, 15000.0f), 2000.0f) - Math.Abs(angle * angle * 10000.0f)),
|
||||
Rotation = (float)Math.Atan2(-invBlipDir.Y, invBlipDir.X),
|
||||
Alpha = MathUtils.Pow2((c.Params.DistantSonarRange - dist) / c.Params.DistantSonarRange)
|
||||
};
|
||||
longRangeBlip.Size.Y *= 5.0f;
|
||||
sonarBlips.Add(longRangeBlip);
|
||||
}
|
||||
}
|
||||
}
|
||||
longRangeUpdateTimer = LongRangeUpdateInterval;
|
||||
}
|
||||
|
||||
if (currentMode == Mode.Active && currentPingIndex != -1)
|
||||
{
|
||||
return;
|
||||
@@ -828,8 +870,8 @@ namespace Barotrauma.Items.Components
|
||||
float directionalPingVisibility = useDirectionalPing && currentMode == Mode.Active ? 1.0f : showDirectionalIndicatorTimer;
|
||||
if (directionalPingVisibility > 0.0f)
|
||||
{
|
||||
Vector2 sector1 = MathUtils.RotatePointAroundTarget(pingDirection * DisplayRadius, Vector2.Zero, DirectionalPingSector * 0.5f);
|
||||
Vector2 sector2 = MathUtils.RotatePointAroundTarget(pingDirection * DisplayRadius, Vector2.Zero, -DirectionalPingSector * 0.5f);
|
||||
Vector2 sector1 = MathUtils.RotatePointAroundTarget(pingDirection * DisplayRadius, Vector2.Zero, MathHelper.ToRadians(DirectionalPingSector * 0.5f));
|
||||
Vector2 sector2 = MathUtils.RotatePointAroundTarget(pingDirection * DisplayRadius, Vector2.Zero, MathHelper.ToRadians(-DirectionalPingSector * 0.5f));
|
||||
DrawLine(spriteBatch, Vector2.Zero, sector1, Color.LightCyan * 0.2f * directionalPingVisibility, width: 3);
|
||||
DrawLine(spriteBatch, Vector2.Zero, sector2, Color.LightCyan * 0.2f * directionalPingVisibility, width: 3);
|
||||
}
|
||||
@@ -862,9 +904,9 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
DrawMarker(spriteBatch,
|
||||
Level.Loaded.StartLocation.Name,
|
||||
"outpost",
|
||||
Level.Loaded.StartOutpost != null ? "outpost" : "location",
|
||||
Level.Loaded.StartLocation.Name,
|
||||
Level.Loaded.StartPosition, transducerCenter,
|
||||
Level.Loaded.StartExitPosition, transducerCenter,
|
||||
displayScale, center, DisplayRadius);
|
||||
}
|
||||
|
||||
@@ -872,9 +914,9 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
DrawMarker(spriteBatch,
|
||||
Level.Loaded.EndLocation.Name,
|
||||
"outpost",
|
||||
Level.Loaded.EndOutpost != null ? "outpost" : "location",
|
||||
Level.Loaded.EndLocation.Name,
|
||||
Level.Loaded.EndPosition, transducerCenter,
|
||||
Level.Loaded.EndExitPosition, transducerCenter,
|
||||
displayScale, center, DisplayRadius);
|
||||
}
|
||||
|
||||
@@ -906,10 +948,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
if (GameMain.GameSession.Mission != null)
|
||||
foreach (Mission mission in GameMain.GameSession.Missions)
|
||||
{
|
||||
var mission = GameMain.GameSession.Mission;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(mission.SonarLabel))
|
||||
{
|
||||
foreach (Vector2 sonarPosition in mission.SonarPositions)
|
||||
@@ -926,16 +966,16 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (AllowUsingMineralScanner && useMineralScanner && CurrentMode == Mode.Active && MineralClusters != null)
|
||||
{
|
||||
foreach (var t in MineralClusters)
|
||||
foreach (var c in MineralClusters)
|
||||
{
|
||||
var unobtainedMinerals = t.Item2.Where(i => i != null && i.GetRootInventoryOwner() == i);
|
||||
var unobtainedMinerals = c.resources.Where(i => i != null && i.GetRootInventoryOwner() == i);
|
||||
if (unobtainedMinerals.None()) { continue; }
|
||||
if (!CheckResourceMarkerVisibility(t.Item1, transducerCenter)) { continue; }
|
||||
if (!CheckResourceMarkerVisibility(c.center, transducerCenter)) { continue; }
|
||||
var i = unobtainedMinerals.FirstOrDefault();
|
||||
if (i == null) { continue; }
|
||||
DrawMarker(spriteBatch,
|
||||
i.Name, "mineral", i,
|
||||
t.Item1, transducerCenter,
|
||||
c.center, transducerCenter,
|
||||
displayScale, center, DisplayRadius * 0.95f,
|
||||
onlyShowTextOnMouseOver: true);
|
||||
}
|
||||
@@ -947,6 +987,19 @@ namespace Barotrauma.Items.Components
|
||||
if (connectedSubs.Contains(sub)) { continue; }
|
||||
if (sub.WorldPosition.Y > Level.Loaded.Size.Y) { continue; }
|
||||
|
||||
if (item.Submarine != null)
|
||||
{
|
||||
//hide enemy team
|
||||
if (sub.TeamID == CharacterTeamType.Team1 && (item.Submarine.TeamID == CharacterTeamType.Team2 || Character.Controlled?.TeamID == CharacterTeamType.Team2))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (sub.TeamID == CharacterTeamType.Team2 && (item.Submarine.TeamID == CharacterTeamType.Team1 || Character.Controlled?.TeamID == CharacterTeamType.Team1))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
DrawMarker(spriteBatch,
|
||||
sub.Info.DisplayName,
|
||||
sub.Info.HasTag(SubmarineTag.Shuttle) ? "shuttle" : "submarine",
|
||||
@@ -1046,15 +1099,18 @@ namespace Barotrauma.Items.Components
|
||||
foreach (DockingPort dockingPort in DockingPort.List)
|
||||
{
|
||||
if (Level.Loaded != null && dockingPort.Item.Submarine.WorldPosition.Y > Level.Loaded.Size.Y) { continue; }
|
||||
|
||||
if (dockingPort.Item.Submarine == null) { continue; }
|
||||
if (dockingPort.Item.Submarine.Info.IsWreck) { continue; }
|
||||
if (!dockingPort.Item.Submarine.ShowSonarMarker && !dockingPort.Item.Submarine.Info.IsOutpost) { continue; }
|
||||
|
||||
//don't show the docking ports of the opposing team on the sonar
|
||||
if (item.Submarine != null)
|
||||
{
|
||||
if ((dockingPort.Item.Submarine.TeamID == CharacterTeamType.Team1 && item.Submarine.TeamID == CharacterTeamType.Team2) ||
|
||||
(dockingPort.Item.Submarine.TeamID == CharacterTeamType.Team2 && item.Submarine.TeamID == CharacterTeamType.Team1))
|
||||
if (dockingPort.Item.Submarine.TeamID == CharacterTeamType.Team1 && (item.Submarine.TeamID == CharacterTeamType.Team2 || Character.Controlled?.TeamID == CharacterTeamType.Team2))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (dockingPort.Item.Submarine.TeamID == CharacterTeamType.Team2 && (item.Submarine.TeamID == CharacterTeamType.Team1 || Character.Controlled?.TeamID == CharacterTeamType.Team1))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -1382,6 +1438,7 @@ namespace Barotrauma.Items.Components
|
||||
MathHelper.Clamp(c.Mass * 0.03f, 0.1f, 2.0f));
|
||||
if (!passive && !CheckBlipVisibility(blip, transducerPos)) { continue; }
|
||||
sonarBlips.Add(blip);
|
||||
HintManager.OnSonarSpottedCharacter(Item, c);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -1401,6 +1458,7 @@ namespace Barotrauma.Items.Components
|
||||
MathHelper.Clamp(limb.Mass * 0.1f, 0.1f, 2.0f));
|
||||
if (!passive && !CheckBlipVisibility(blip, transducerPos)) { continue; }
|
||||
sonarBlips.Add(blip);
|
||||
HintManager.OnSonarSpottedCharacter(Item, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1554,12 +1612,12 @@ namespace Barotrauma.Items.Components
|
||||
float scale = (strength + 3.0f) * blip.Scale * blipScale;
|
||||
Color color = ToolBox.GradientLerp(strength, blipColorGradient[blip.BlipType]);
|
||||
|
||||
sonarBlip.Draw(spriteBatch, center + pos, color, sonarBlip.Origin, blip.Rotation ?? MathUtils.VectorToAngle(pos),
|
||||
sonarBlip.Draw(spriteBatch, center + pos, color * blip.Alpha, sonarBlip.Origin, blip.Rotation ?? MathUtils.VectorToAngle(pos),
|
||||
blip.Size * scale * 0.5f, SpriteEffects.None, 0);
|
||||
|
||||
pos += Rand.Range(0.0f, 1.0f) * dir + Rand.Range(-scale, scale) * normal;
|
||||
|
||||
sonarBlip.Draw(spriteBatch, center + pos, color * 0.5f, sonarBlip.Origin, 0, scale, SpriteEffects.None, 0);
|
||||
sonarBlip.Draw(spriteBatch, center + pos, color * 0.5f * blip.Alpha, sonarBlip.Origin, 0, scale, SpriteEffects.None, 0);
|
||||
}
|
||||
|
||||
private void DrawMarker(SpriteBatch spriteBatch, string label, string iconIdentifier, object targetIdentifier, Vector2 worldPosition, Vector2 transducerPosition, float scale, Vector2 center, float radius,
|
||||
@@ -1644,7 +1702,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(iconIdentifier) || !targetIcons.ContainsKey(iconIdentifier))
|
||||
if (iconIdentifier == null || !targetIcons.ContainsKey(iconIdentifier))
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle((int)markerPos.X - 3, (int)markerPos.Y - 3, 6, 6), markerColor, thickness: 2);
|
||||
}
|
||||
@@ -1777,6 +1835,7 @@ namespace Barotrauma.Items.Components
|
||||
public float? Rotation;
|
||||
public Vector2 Size;
|
||||
public Sonar.BlipType BlipType;
|
||||
public float Alpha = 1.0f;
|
||||
|
||||
public SonarBlip(Vector2 pos, float fadeTimer, float scale, Sonar.BlipType blipType = Sonar.BlipType.Default)
|
||||
{
|
||||
|
||||
@@ -351,14 +351,19 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
if (GameMain.GameSession?.Campaign is CampaignMode campaign)
|
||||
if (GameMain.GameSession?.Missions.Any(m => !m.AllowUndocking) ?? false)
|
||||
{
|
||||
new GUIMessageBox("", TextManager.Get("undockingdisabledbymission"));
|
||||
return false;
|
||||
}
|
||||
else if (GameMain.GameSession?.Campaign is CampaignMode campaign)
|
||||
{
|
||||
if (Level.IsLoadedOutpost &&
|
||||
DockingSources.Any(d => d.Docked && (d.DockingTarget?.Item.Submarine?.Info?.IsOutpost ?? false)))
|
||||
{
|
||||
// Undocking from an outpost
|
||||
campaign.CampaignUI.SelectTab(CampaignMode.InteractionType.Map);
|
||||
campaign.ShowCampaignUI = true;
|
||||
campaign.CampaignUI.SelectTab(CampaignMode.InteractionType.Map);
|
||||
return false;
|
||||
}
|
||||
else if (!Level.IsLoadedOutpost && DockingModeEnabled && ActiveDockingSource != null &&
|
||||
@@ -398,7 +403,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (GameMain.Client == null)
|
||||
{
|
||||
item.SendSignal(0, "1", "toggle_docking", sender: null);
|
||||
item.SendSignal("1", "toggle_docking");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -722,7 +727,19 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
pressureWarningText.Visible = item.Submarine != null && item.Submarine.AtDamageDepth && Timing.TotalTime % 1.0f < 0.8f;
|
||||
pressureWarningText.Visible = item.Submarine != null && Timing.TotalTime % 1.0f < 0.8f;
|
||||
float depthEffectThreshold = 500.0f;
|
||||
if (Level.Loaded != null && pressureWarningText.Visible &&
|
||||
item.Submarine.RealWorldDepth > Level.Loaded.RealWorldCrushDepth - depthEffectThreshold && item.Submarine.RealWorldDepth > item.Submarine.RealWorldCrushDepth - depthEffectThreshold)
|
||||
{
|
||||
pressureWarningText.Visible = true;
|
||||
pressureWarningText.Text = item.Submarine.AtDamageDepth ? TextManager.Get("SteeringDepthWarning") : TextManager.Get("SteeringDepthWarningLow").Replace("[crushdepth]", ((int)item.Submarine.RealWorldCrushDepth).ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
pressureWarningText.Visible = false;
|
||||
}
|
||||
|
||||
iceSpireWarningText.Visible = item.Submarine != null && !pressureWarningText.Visible && showIceSpireWarning && Timing.TotalTime % 1.0f < 0.8f;
|
||||
|
||||
if (Vector2.DistanceSquared(PlayerInput.MousePosition, steerArea.Rect.Center.ToVector2()) < steerRadius * steerRadius)
|
||||
@@ -748,7 +765,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
if (!AutoPilot && Character.DisableControls && GUI.KeyboardDispatcher.Subscriber == null)
|
||||
{
|
||||
steeringAdjustSpeed = character == null ? 0.2f : MathHelper.Lerp(0.2f, 1.0f, character.GetSkillLevel("helm") / 100.0f);
|
||||
steeringAdjustSpeed = character == null ? DefaultSteeringAdjustSpeed : MathHelper.Lerp(0.2f, 1.0f, character.GetSkillLevel("helm") / 100.0f);
|
||||
Vector2 input = Vector2.Zero;
|
||||
if (PlayerInput.KeyDown(InputType.Left)) { input -= Vector2.UnitX; }
|
||||
if (PlayerInput.KeyDown(InputType.Right)) { input += Vector2.UnitX; }
|
||||
@@ -914,7 +931,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (dockingButtonClicked)
|
||||
{
|
||||
item.SendSignal(0, "1", "toggle_docking", sender: null);
|
||||
item.SendSignal("1", "toggle_docking");
|
||||
}
|
||||
|
||||
if (autoPilot)
|
||||
|
||||
@@ -98,8 +98,11 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
|
||||
{
|
||||
float chargeRatio = charge / capacity;
|
||||
chargeIndicator.Color = ToolBox.GradientLerp(chargeRatio, Color.Red, Color.Orange, Color.Green);
|
||||
if (chargeIndicator != null)
|
||||
{
|
||||
float chargeRatio = charge / capacity;
|
||||
chargeIndicator.Color = ToolBox.GradientLerp(chargeRatio, Color.Red, Color.Orange, Color.Green);
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -28,15 +27,7 @@ namespace Barotrauma.Items.Components
|
||||
int x = panelRect.X, y = panelRect.Y;
|
||||
int width = panelRect.Width, height = panelRect.Height;
|
||||
|
||||
Vector2 scale = new Vector2(GUI.Scale);
|
||||
if (panel.GuiFrame.RectTransform.MaxSize.X < int.MaxValue)
|
||||
{
|
||||
scale.X = panel.GuiFrame.RectTransform.MaxSize.X / panel.GuiFrame.Rect.Width;
|
||||
}
|
||||
if (panel.GuiFrame.RectTransform.MaxSize.Y < int.MaxValue)
|
||||
{
|
||||
scale.Y = panel.GuiFrame.RectTransform.MaxSize.Y / panel.GuiFrame.Rect.Height;
|
||||
}
|
||||
Vector2 scale = GetScale(panel.GuiFrame.RectTransform.MaxSize, panel.GuiFrame.Rect.Size);
|
||||
|
||||
bool mouseInRect = panelRect.Contains(PlayerInput.MousePosition);
|
||||
|
||||
@@ -66,15 +57,15 @@ namespace Barotrauma.Items.Components
|
||||
//two passes: first the connector, then the wires to get the wires to render in front
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
Vector2 rightPos = new Vector2(x + width - 80 * scale.X, y + 60 * scale.Y);
|
||||
Vector2 leftPos = new Vector2(x + 80 * scale.X, y + 60 * scale.Y);
|
||||
Vector2 rightPos = GetRightPos(x, y, width, scale);
|
||||
Vector2 leftPos = GetLeftPos(x, y, scale);
|
||||
|
||||
Vector2 rightWirePos = new Vector2(x + width - 5 * scale.X, y + 30 * scale.Y);
|
||||
Vector2 leftWirePos = new Vector2(x + 5 * scale.X, y + 30 * scale.Y);
|
||||
|
||||
int wireInterval = (height - (int)(20 * scale.Y)) / Math.Max(totalWireCount, 1);
|
||||
int connectorIntervalLeft = (height - (int)(100 * scale.Y)) / Math.Max(panel.Connections.Count(c => c.IsOutput), 1);
|
||||
int connectorIntervalRight = (height - (int)(100 * scale.Y)) / Math.Max(panel.Connections.Count(c => !c.IsOutput), 1);
|
||||
int connectorIntervalLeft = GetConnectorIntervalLeft(height, scale, panel);
|
||||
int connectorIntervalRight = GetConnectorIntervalRight(height, scale, panel);
|
||||
|
||||
foreach (Connection c in panel.Connections)
|
||||
{
|
||||
@@ -101,15 +92,12 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
c.DrawConnection(spriteBatch, panel, rightPos,
|
||||
new Vector2(rightPos.X - GUI.SmallFont.MeasureString(c.DisplayName.ToUpper()).X - 25 * panel.Scale, rightPos.Y + 5 * panel.Scale),
|
||||
scale);
|
||||
c.DrawConnection(spriteBatch, panel, rightPos, GetOutputLabelPosition(rightPos, panel, c), scale);
|
||||
}
|
||||
else
|
||||
{
|
||||
c.DrawWires(spriteBatch, panel, rightPos, rightWirePos, mouseInRect, equippedWire, wireInterval);
|
||||
}
|
||||
|
||||
rightPos.Y += connectorIntervalLeft;
|
||||
rightWirePos.Y += c.Wires.Count(w => w != null) * wireInterval;
|
||||
}
|
||||
@@ -117,15 +105,12 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
c.DrawConnection(spriteBatch, panel, leftPos,
|
||||
new Vector2(leftPos.X + 25 * panel.Scale, leftPos.Y - 5 * panel.Scale - GUI.SmallFont.MeasureString(c.DisplayName.ToUpper()).Y),
|
||||
scale);
|
||||
c.DrawConnection(spriteBatch, panel, leftPos, GetInputLabelPosition(leftPos, panel, c), scale);
|
||||
}
|
||||
else
|
||||
{
|
||||
c.DrawWires(spriteBatch, panel, leftPos, leftWirePos, mouseInRect, equippedWire, wireInterval);
|
||||
}
|
||||
|
||||
leftPos.Y += connectorIntervalRight;
|
||||
leftWirePos.Y += c.Wires.Count(w => w != null) * wireInterval;
|
||||
}
|
||||
@@ -215,14 +200,11 @@ namespace Barotrauma.Items.Components
|
||||
private void DrawConnection(SpriteBatch spriteBatch, ConnectionPanel panel, Vector2 position, Vector2 labelPos, Vector2 scale)
|
||||
{
|
||||
string text = DisplayName.ToUpper();
|
||||
Vector2 textSize = GUI.SmallFont.MeasureString(text);
|
||||
|
||||
//nasty
|
||||
var labelSprite = GUI.Style.GetComponentStyle("ConnectionPanelLabel")?.Sprites.Values.First().First();
|
||||
if (labelSprite != null)
|
||||
if (GUI.Style.GetComponentStyle("ConnectionPanelLabel")?.Sprites.Values.First().First() is UISprite labelSprite)
|
||||
{
|
||||
Rectangle labelArea = new Rectangle(labelPos.ToPoint(), textSize.ToPoint());
|
||||
labelArea.Inflate(10 * scale.X, 3 * scale.Y);
|
||||
Rectangle labelArea = GetLabelArea(labelPos, text, scale);
|
||||
labelSprite.Draw(spriteBatch, labelArea, IsPower ? GUI.Style.Red : Color.SteelBlue);
|
||||
}
|
||||
|
||||
@@ -256,7 +238,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (!PlayerInput.PrimaryMouseButtonHeld())
|
||||
{
|
||||
if (GameMain.NetworkMember != null || panel.CheckCharacterSuccess(Character.Controlled))
|
||||
if ((GameMain.NetworkMember != null || panel.CheckCharacterSuccess(Character.Controlled)) &&
|
||||
Wires.Count(w => w != null) < MaxPlayerConnectableWires)
|
||||
{
|
||||
//find an empty cell for the new connection
|
||||
int index = FindEmptyIndex();
|
||||
@@ -390,5 +373,115 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool CheckConnectionLabelOverlap(ConnectionPanel panel, out Point newRectSize)
|
||||
{
|
||||
Rectangle panelRect = panel.GuiFrame.Rect;
|
||||
int x = panelRect.X, y = panelRect.Y;
|
||||
Vector2 scale = GetScale(panel.GuiFrame.RectTransform.MaxSize, panel.GuiFrame.Rect.Size);
|
||||
Vector2 rightPos = GetRightPos(x, y, panelRect.Width, scale);
|
||||
Vector2 leftPos = GetLeftPos(x, y, scale);
|
||||
int connectorIntervalLeft = GetConnectorIntervalLeft(panelRect.Height, scale, panel);
|
||||
int connectorIntervalRight = GetConnectorIntervalRight(panelRect.Height, scale, panel);
|
||||
newRectSize = panelRect.Size;
|
||||
var labelAreas = new List<Rectangle>();
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
labelAreas.Clear();
|
||||
foreach (var c in panel.Connections)
|
||||
{
|
||||
if (c.IsOutput)
|
||||
{
|
||||
var labelArea = GetLabelArea(GetOutputLabelPosition(rightPos, panel, c), c.DisplayName.ToUpper(), scale);
|
||||
labelAreas.Add(labelArea);
|
||||
rightPos.Y += connectorIntervalLeft;
|
||||
}
|
||||
else
|
||||
{
|
||||
var labelArea = GetLabelArea(GetInputLabelPosition(leftPos, panel, c), c.DisplayName.ToUpper(), scale);
|
||||
labelAreas.Add(labelArea);
|
||||
leftPos.Y += connectorIntervalRight;
|
||||
}
|
||||
}
|
||||
bool foundOverlap = false;
|
||||
for (int j = 0; j < labelAreas.Count; j++)
|
||||
{
|
||||
for (int k = 0; k < labelAreas.Count; k++)
|
||||
{
|
||||
if (k == j) { continue; }
|
||||
if (!labelAreas[j].Intersects(labelAreas[k])) { continue; }
|
||||
newRectSize += new Point(10);
|
||||
Point maxSize = new Point(
|
||||
Math.Max(panel.GuiFrame.RectTransform.MaxSize.X, newRectSize.X),
|
||||
Math.Max(panel.GuiFrame.RectTransform.MaxSize.Y, newRectSize.Y));
|
||||
scale = GetScale(maxSize, newRectSize);
|
||||
rightPos = GetRightPos(x, y, newRectSize.X, scale);
|
||||
leftPos = GetLeftPos(x, y, scale);
|
||||
connectorIntervalLeft = GetConnectorIntervalLeft(newRectSize.Y, scale, panel);
|
||||
connectorIntervalRight = GetConnectorIntervalRight(newRectSize.Y, scale, panel);
|
||||
foundOverlap = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundOverlap) { break; }
|
||||
}
|
||||
return newRectSize.X != panel.GuiFrame.Rect.Width || newRectSize.Y > panel.GuiFrame.Rect.Height;
|
||||
}
|
||||
|
||||
private static Vector2 GetScale(Point maxSize, Point size)
|
||||
{
|
||||
Vector2 scale = new Vector2(GUI.Scale);
|
||||
if (maxSize.X < int.MaxValue)
|
||||
{
|
||||
scale.X = maxSize.X / size.X;
|
||||
}
|
||||
if (maxSize.Y < int.MaxValue)
|
||||
{
|
||||
scale.Y = maxSize.Y / size.Y;
|
||||
}
|
||||
return scale;
|
||||
}
|
||||
|
||||
private static Vector2 GetInputLabelPosition(Vector2 connectorPosition, ConnectionPanel panel, Connection connection)
|
||||
{
|
||||
return new Vector2(
|
||||
connectorPosition.X + 25 * panel.Scale,
|
||||
connectorPosition.Y - 5 * panel.Scale - GUI.SmallFont.MeasureString(connection.DisplayName.ToUpper()).Y);
|
||||
}
|
||||
|
||||
private static Vector2 GetOutputLabelPosition(Vector2 connectorPosition, ConnectionPanel panel, Connection connection)
|
||||
{
|
||||
return new Vector2(
|
||||
connectorPosition.X - 25 * panel.Scale - GUI.SmallFont.MeasureString(connection.DisplayName.ToUpper()).X,
|
||||
connectorPosition.Y + 5 * panel.Scale);
|
||||
}
|
||||
|
||||
private static Rectangle GetLabelArea(Vector2 labelPos, string text, Vector2 scale)
|
||||
{
|
||||
Vector2 textSize = GUI.SmallFont.MeasureString(text);
|
||||
Rectangle labelArea = new Rectangle(labelPos.ToPoint(), textSize.ToPoint());
|
||||
labelArea.Inflate(10 * scale.X, 3 * scale.Y);
|
||||
return labelArea;
|
||||
}
|
||||
|
||||
private static Vector2 GetLeftPos(int x, int y, Vector2 scale)
|
||||
{
|
||||
return new Vector2(x + 80 * scale.X, y + 60 * scale.Y);
|
||||
}
|
||||
|
||||
private static Vector2 GetRightPos(int x, int y, int width, Vector2 scale)
|
||||
{
|
||||
return new Vector2(x + width - 80 * scale.X, y + 60 * scale.Y);
|
||||
}
|
||||
|
||||
private static int GetConnectorIntervalLeft(int height, Vector2 scale, ConnectionPanel panel)
|
||||
{
|
||||
return (height - (int)(100 * scale.Y)) / Math.Max(panel.Connections.Count(c => c.IsOutput), 1);
|
||||
}
|
||||
|
||||
private static int GetConnectorIntervalRight(int height, Vector2 scale, ConnectionPanel panel)
|
||||
{
|
||||
return (height - (int)(100 * scale.Y)) / Math.Max(panel.Connections.Count(c => !c.IsOutput), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,9 +24,15 @@ namespace Barotrauma.Items.Components
|
||||
get { return GuiFrame.Rect.Width / 400.0f; }
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(XElement element)
|
||||
private Point originalMaxSize;
|
||||
private Vector2 originalRelativeSize;
|
||||
|
||||
partial void InitProjSpecific()
|
||||
{
|
||||
if (GuiFrame == null) { return; }
|
||||
originalMaxSize = GuiFrame.RectTransform.MaxSize;
|
||||
originalRelativeSize = GuiFrame.RectTransform.RelativeSize;
|
||||
CheckForLabelOverlap();
|
||||
new GUICustomComponent(new RectTransform(Vector2.One, GuiFrame.RectTransform), DrawConnections, null)
|
||||
{
|
||||
UserData = this
|
||||
@@ -40,7 +46,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime)
|
||||
{
|
||||
foreach (Wire wire in DisconnectedWires)
|
||||
foreach (var _ in DisconnectedWires)
|
||||
{
|
||||
if (Rand.Range(0.0f, 500.0f) < 1.0f)
|
||||
{
|
||||
@@ -112,6 +118,32 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnResolutionChanged()
|
||||
{
|
||||
base.OnResolutionChanged();
|
||||
if (GuiFrame == null) { return; }
|
||||
CheckForLabelOverlap();
|
||||
}
|
||||
|
||||
private void CheckForLabelOverlap()
|
||||
{
|
||||
GuiFrame.RectTransform.MaxSize = originalMaxSize;
|
||||
GuiFrame.RectTransform.Resize(originalRelativeSize);
|
||||
if (Connection.CheckConnectionLabelOverlap(this, out Point newRectSize))
|
||||
{
|
||||
int xCenter = (int)(GameMain.GraphicsWidth / 2.0f);
|
||||
int maxNewWidth = 2 * Math.Min(xCenter - HUDLayoutSettings.CrewArea.Right, xCenter - HUDLayoutSettings.ChatBoxArea.Right);
|
||||
int yCenter = (int)(GameMain.GraphicsHeight / 2.0f);
|
||||
int maxNewHeight = 2 * Math.Min(yCenter - HUDLayoutSettings.MessageAreaTop.Bottom, HUDLayoutSettings.InventoryTopY - yCenter);
|
||||
// Make sure we don't expand the panel interface too much
|
||||
newRectSize = new Point(Math.Min(newRectSize.X, maxNewWidth), Math.Min(newRectSize.Y, maxNewHeight));
|
||||
GuiFrame.RectTransform.MaxSize = new Point(
|
||||
Math.Max(GuiFrame.RectTransform.MaxSize.X, newRectSize.X),
|
||||
Math.Max(GuiFrame.RectTransform.MaxSize.Y, newRectSize.Y));
|
||||
GuiFrame.RectTransform.Resize(newRectSize);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
{
|
||||
if (GameMain.Client.MidRoundSyncing)
|
||||
|
||||
@@ -50,7 +50,8 @@ namespace Barotrauma.Items.Components
|
||||
var textBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), layoutGroup.RectTransform), ciElement.Signal, style: "GUITextBoxNoIcon")
|
||||
{
|
||||
OverflowClip = true,
|
||||
UserData = ciElement
|
||||
UserData = ciElement,
|
||||
MaxTextLength = ciElement.MaxTextLength
|
||||
};
|
||||
//reset size restrictions set by the Style to make sure the elements can fit the interface
|
||||
textBox.RectTransform.MinSize = textBox.Frame.RectTransform.MinSize = new Point(0, 0);
|
||||
|
||||
@@ -63,21 +63,23 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
OutputValue = input;
|
||||
item.SendSignal(0, input, "signal_out", null);
|
||||
ShowOnDisplay(input);
|
||||
item.SendSignal(input, "signal_out");
|
||||
}
|
||||
|
||||
partial void ShowOnDisplay(string input)
|
||||
partial void ShowOnDisplay(string input, bool addToHistory = true)
|
||||
{
|
||||
messageHistory.Add(input);
|
||||
while (messageHistory.Count > MaxMessages)
|
||||
if (addToHistory)
|
||||
{
|
||||
messageHistory.RemoveAt(0);
|
||||
}
|
||||
|
||||
while (historyBox.Content.CountChildren > MaxMessages)
|
||||
{
|
||||
historyBox.RemoveChild(historyBox.Content.Children.First());
|
||||
messageHistory.Add(input);
|
||||
while (messageHistory.Count > MaxMessages)
|
||||
{
|
||||
messageHistory.RemoveAt(0);
|
||||
}
|
||||
while (historyBox.Content.CountChildren > MaxMessages)
|
||||
{
|
||||
historyBox.RemoveChild(historyBox.Content.Children.First());
|
||||
}
|
||||
}
|
||||
|
||||
GUITextBlock newBlock = new GUITextBlock(
|
||||
|
||||
@@ -87,7 +87,7 @@ namespace Barotrauma.Items.Components
|
||||
SpriteEffects.None,
|
||||
depth);
|
||||
}
|
||||
}
|
||||
}
|
||||
private static Sprite defaultWireSprite;
|
||||
private Sprite overrideSprite;
|
||||
private Sprite wireSprite;
|
||||
@@ -156,7 +156,8 @@ namespace Barotrauma.Items.Components
|
||||
drawOffset = sub.DrawPosition + sub.HiddenSubPosition;
|
||||
}
|
||||
|
||||
float depth = item.IsSelected ? 0.0f : SubEditorScreen.IsWiringMode() ? 0.02f : wireSprite.Depth + (item.ID % 100) * 0.000001f;// item.GetDrawDepth(wireSprite.Depth, wireSprite);
|
||||
float baseDepth = UseSpriteDepth ? item.SpriteDepth : wireSprite.Depth;
|
||||
float depth = item.IsSelected ? 0.0f : SubEditorScreen.IsWiringMode() ? 0.02f : baseDepth + (item.ID % 100) * 0.000001f;// item.GetDrawDepth(wireSprite.Depth, wireSprite);
|
||||
|
||||
if (item.IsHighlighted)
|
||||
{
|
||||
|
||||
@@ -225,7 +225,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
foreach (AfflictionPrefab affliction in combinedAfflictionStrengths.Keys)
|
||||
{
|
||||
texts.Add(TextManager.AddPunctuation(':', affliction.Name, ((int)combinedAfflictionStrengths[affliction]).ToString() + " %"));
|
||||
texts.Add(TextManager.AddPunctuation(':', affliction.Name, Math.Max(((int)combinedAfflictionStrengths[affliction]), 1).ToString() + " %"));
|
||||
textColors.Add(Color.Lerp(GUI.Style.Orange, GUI.Style.Red, combinedAfflictionStrengths[affliction] / affliction.MaxStrength));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,12 +130,15 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
powerIndicator = new GUIProgressBar(new RectTransform(new Vector2(0.18f, 0.03f), GUI.Canvas, Anchor.TopCenter)
|
||||
powerIndicator = new GUIProgressBar(new RectTransform(new Vector2(0.18f, 0.03f), GUI.Canvas, Anchor.BottomCenter)
|
||||
{
|
||||
MinSize = new Point(100,20),
|
||||
MinSize = new Point(100, 20),
|
||||
RelativeOffset = new Vector2(0.0f, 0.01f)
|
||||
},
|
||||
barSize: 0.0f, style: "DeviceProgressBar");
|
||||
},
|
||||
barSize: 0.0f, style: "DeviceProgressBar")
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
}
|
||||
|
||||
public override void Move(Vector2 amount)
|
||||
@@ -497,9 +500,15 @@ namespace Barotrauma.Items.Components
|
||||
foreach (MapEntity e in item.linkedTo)
|
||||
{
|
||||
if (!(e is Item linkedItem)) { continue; }
|
||||
availableAmmo.AddRange(linkedItem.ContainedItems);
|
||||
}
|
||||
|
||||
var itemContainer = linkedItem.GetComponent<ItemContainer>();
|
||||
if (itemContainer == null) { continue; }
|
||||
availableAmmo.AddRange(itemContainer.Inventory.AllItems);
|
||||
for (int i = 0; i < itemContainer.Inventory.Capacity - itemContainer.Inventory.AllItems.Count(); i++)
|
||||
{
|
||||
availableAmmo.Add(null);
|
||||
}
|
||||
}
|
||||
|
||||
float chargeRate =
|
||||
powerConsumption <= 0.0f ?
|
||||
1.0f :
|
||||
@@ -531,15 +540,16 @@ namespace Barotrauma.Items.Components
|
||||
if (ShowProjectileIndicator)
|
||||
{
|
||||
Point slotSize = (Inventory.SlotSpriteSmall.size * Inventory.UIScale).ToPoint();
|
||||
int spacing = 5;
|
||||
Point spacing = new Point(GUI.IntScale(5), GUI.IntScale(20));
|
||||
int slotsPerRow = Math.Min(availableAmmo.Count, 6);
|
||||
int totalWidth = slotSize.X * slotsPerRow + spacing * (slotsPerRow - 1);
|
||||
Point invSlotPos = new Point(GameMain.GraphicsWidth / 2 - totalWidth / 2, (int)(60 * GUI.Scale));
|
||||
int totalWidth = slotSize.X * slotsPerRow + spacing.X * (slotsPerRow - 1);
|
||||
int rows = (int)Math.Ceiling(availableAmmo.Count / (float)slotsPerRow);
|
||||
Point invSlotPos = new Point(GameMain.GraphicsWidth / 2 - totalWidth / 2, powerIndicator.Rect.Y - (slotSize.Y + spacing.Y) * rows);
|
||||
for (int i = 0; i < availableAmmo.Count; i++)
|
||||
{
|
||||
// TODO: Optimize? Creates multiple new objects per frame?
|
||||
Inventory.DrawSlot(spriteBatch, null,
|
||||
new VisualSlot(new Rectangle(invSlotPos + new Point((i % slotsPerRow) * (slotSize.X + spacing), (int)Math.Floor(i / (float)slotsPerRow) * (slotSize.Y + spacing)), slotSize)),
|
||||
new VisualSlot(new Rectangle(invSlotPos + new Point((i % slotsPerRow) * (slotSize.X + spacing.X), (int)Math.Floor(i / (float)slotsPerRow) * (slotSize.Y + spacing.Y)), slotSize)),
|
||||
availableAmmo[i], -1, true);
|
||||
}
|
||||
if (flashNoAmmo)
|
||||
@@ -578,7 +588,7 @@ namespace Barotrauma.Items.Components
|
||||
//ID ushort.MaxValue = launched without a projectile
|
||||
if (projectileID == ushort.MaxValue)
|
||||
{
|
||||
Launch(null);
|
||||
Launch(null, user);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -587,7 +597,7 @@ namespace Barotrauma.Items.Components
|
||||
DebugConsole.ThrowError("Failed to launch a projectile - item with the ID \"" + projectileID + " not found");
|
||||
return;
|
||||
}
|
||||
Launch(projectile, launchRotation: newTargetRotation);
|
||||
Launch(projectile, user, launchRotation: newTargetRotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +165,14 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (isLocked)
|
||||
{
|
||||
Lock(isNetworkMessage: true, forcePosition: true);
|
||||
if (DockingTarget.joint != null)
|
||||
{
|
||||
DockingTarget.Lock(isNetworkMessage: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Lock(isNetworkMessage: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -215,8 +215,10 @@ namespace Barotrauma
|
||||
public Inventory Inventory;
|
||||
public readonly Item Item;
|
||||
public readonly bool IsSubSlot;
|
||||
public string Tooltip;
|
||||
public List<RichTextData> TooltipRichTextData;
|
||||
public string Tooltip { get; private set; }
|
||||
public List<RichTextData> TooltipRichTextData { get; private set;}
|
||||
|
||||
public int tooltipDisplayedCondition;
|
||||
|
||||
public SlotReference(Inventory parentInventory, VisualSlot slot, int slotIndex, bool isSubSlot, Inventory subInventory = null)
|
||||
{
|
||||
@@ -227,16 +229,29 @@ namespace Barotrauma
|
||||
IsSubSlot = isSubSlot;
|
||||
Item = ParentInventory.GetItemAt(slotIndex);
|
||||
|
||||
int stackCount = 1;
|
||||
if (parentInventory != null && Item != null)
|
||||
{
|
||||
stackCount = parentInventory.GetItemsAt(slotIndex).Count();
|
||||
}
|
||||
|
||||
TooltipRichTextData = RichTextData.GetRichTextData(GetTooltip(Item, stackCount), out Tooltip);
|
||||
RefreshTooltip();
|
||||
}
|
||||
|
||||
private string GetTooltip(Item item, int stackCount)
|
||||
public bool TooltipNeedsRefresh()
|
||||
{
|
||||
if (Item == null) { return false; }
|
||||
return (int)Item.ConditionPercentage != tooltipDisplayedCondition;
|
||||
}
|
||||
|
||||
public void RefreshTooltip()
|
||||
{
|
||||
if (Item == null) { return; }
|
||||
IEnumerable<Item> itemsInSlot = null;
|
||||
if (ParentInventory != null && Item != null)
|
||||
{
|
||||
itemsInSlot = ParentInventory.GetItemsAt(SlotIndex);
|
||||
}
|
||||
TooltipRichTextData = RichTextData.GetRichTextData(GetTooltip(Item, itemsInSlot), out string newTooltip);
|
||||
Tooltip = newTooltip;
|
||||
tooltipDisplayedCondition = (int)Item.ConditionPercentage;
|
||||
}
|
||||
|
||||
private string GetTooltip(Item item, IEnumerable<Item> itemsInSlot)
|
||||
{
|
||||
if (item == null) { return null; }
|
||||
|
||||
@@ -288,9 +303,13 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
string colorStr = XMLExtensions.ColorToString(item.SpawnedInOutpost ? GUI.Style.Red : Color.White);
|
||||
string colorStr = XMLExtensions.ColorToString(!item.AllowStealing ? GUI.Style.Red : Color.White);
|
||||
|
||||
toolTip = $"‖color:{colorStr}‖{item.Name}‖color:end‖";
|
||||
if (itemsInSlot.All(it => it.NonInteractable || it.NonPlayerTeamInteractable))
|
||||
{
|
||||
toolTip += " " + TextManager.Get("connectionlocked");
|
||||
}
|
||||
if (!item.IsFullCondition && !item.Prefab.HideConditionBar)
|
||||
{
|
||||
string conditionColorStr = XMLExtensions.ColorToString(ToolBox.GradientLerp(item.Condition / item.MaxCondition, GUI.Style.ColorInventoryEmpty, GUI.Style.ColorInventoryHalf, GUI.Style.ColorInventoryFull));
|
||||
@@ -298,7 +317,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (!string.IsNullOrEmpty(description)) { toolTip += '\n' + description; }
|
||||
}
|
||||
if (stackCount > 2)
|
||||
if (itemsInSlot.Count() > 1)
|
||||
{
|
||||
string colorStr = XMLExtensions.ColorToString(GUI.Style.Blue);
|
||||
toolTip += $"\n‖color:{colorStr}‖[{GameMain.Config.KeyBindText(InputType.TakeOneFromInventorySlot)}] {TextManager.Get("inputtype.takeonefrominventoryslot")}‖color:end‖";
|
||||
@@ -315,10 +334,10 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
return DraggingItems.Any() &&
|
||||
Character.Controlled != null &&
|
||||
return Character.Controlled != null &&
|
||||
Character.Controlled.SelectedConstruction == null &&
|
||||
CharacterHealth.OpenHealthWindow == null;
|
||||
CharacterHealth.OpenHealthWindow == null &&
|
||||
DraggingItems.Any();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -565,39 +584,41 @@ namespace Barotrauma
|
||||
|
||||
if (!DraggingItems.Any())
|
||||
{
|
||||
if (PlayerInput.PrimaryMouseButtonDown() && slots[slotIndex].Any())
|
||||
var interactableItems = Screen.Selected == GameMain.GameScreen ? slots[slotIndex].Items.Where(it => !it.NonInteractable && !it.NonPlayerTeamInteractable) : slots[slotIndex].Items;
|
||||
if (PlayerInput.PrimaryMouseButtonDown() && interactableItems.Any())
|
||||
{
|
||||
if (PlayerInput.KeyDown(InputType.TakeHalfFromInventorySlot))
|
||||
{
|
||||
DraggingItems.AddRange(slots[slotIndex].Items.Skip(slots[slotIndex].ItemCount / 2));
|
||||
DraggingItems.AddRange(interactableItems.Skip(interactableItems.Count() / 2));
|
||||
}
|
||||
else if (PlayerInput.KeyDown(InputType.TakeOneFromInventorySlot))
|
||||
{
|
||||
DraggingItems.Add(slots[slotIndex].First());
|
||||
DraggingItems.Add(interactableItems.First());
|
||||
}
|
||||
else
|
||||
{
|
||||
DraggingItems.AddRange(slots[slotIndex].Items);
|
||||
DraggingItems.AddRange(interactableItems);
|
||||
}
|
||||
DraggingSlot = slot;
|
||||
}
|
||||
}
|
||||
else if (PlayerInput.PrimaryMouseButtonReleased())
|
||||
{
|
||||
if (PlayerInput.DoubleClicked() && slots[slotIndex].Any())
|
||||
var interactableItems = Screen.Selected == GameMain.GameScreen ? slots[slotIndex].Items.Where(it => !it.NonInteractable && !it.NonPlayerTeamInteractable) : slots[slotIndex].Items;
|
||||
if (PlayerInput.DoubleClicked() && interactableItems.Any())
|
||||
{
|
||||
doubleClickedItems.Clear();
|
||||
if (PlayerInput.KeyDown(InputType.TakeHalfFromInventorySlot))
|
||||
{
|
||||
doubleClickedItems.AddRange(slots[slotIndex].Items.Skip(slots[slotIndex].ItemCount / 2));
|
||||
doubleClickedItems.AddRange(interactableItems.Skip(interactableItems.Count() / 2));
|
||||
}
|
||||
else if (PlayerInput.KeyDown(InputType.TakeOneFromInventorySlot))
|
||||
{
|
||||
doubleClickedItems.Add(slots[slotIndex].First());
|
||||
doubleClickedItems.Add(interactableItems.First());
|
||||
}
|
||||
else
|
||||
{
|
||||
doubleClickedItems.AddRange(slots[slotIndex].Items);
|
||||
doubleClickedItems.AddRange(interactableItems);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1241,9 +1262,11 @@ namespace Barotrauma
|
||||
if (selectedSlot.ParentInventory?.Owner != Character.Controlled &&
|
||||
selectedSlot.ParentInventory?.Owner != Character.Controlled.SelectedCharacter &&
|
||||
selectedSlot.ParentInventory?.Owner != Character.Controlled.SelectedConstruction &&
|
||||
!(Character.Controlled.SelectedConstruction?.linkedTo.Contains(selectedSlot.ParentInventory?.Owner) ?? false) &&
|
||||
rootOwner != Character.Controlled &&
|
||||
rootOwner != Character.Controlled.SelectedCharacter &&
|
||||
rootOwner != Character.Controlled.SelectedConstruction)
|
||||
rootOwner != Character.Controlled.SelectedConstruction &&
|
||||
!(Character.Controlled.SelectedConstruction?.linkedTo.Contains(rootOwner) ?? false))
|
||||
{
|
||||
selectedSlot = null;
|
||||
}
|
||||
@@ -1362,6 +1385,10 @@ namespace Barotrauma
|
||||
{
|
||||
Rectangle slotRect = selectedSlot.Slot.Rect;
|
||||
slotRect.Location += selectedSlot.Slot.DrawOffset.ToPoint();
|
||||
if (selectedSlot.TooltipNeedsRefresh())
|
||||
{
|
||||
selectedSlot.RefreshTooltip();
|
||||
}
|
||||
DrawToolTip(spriteBatch, selectedSlot.Tooltip, slotRect, selectedSlot.TooltipRichTextData);
|
||||
}
|
||||
}
|
||||
@@ -1533,7 +1560,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
Color spriteColor = sprite == item.Sprite ? item.GetSpriteColor() : item.GetInventoryIconColor();
|
||||
if (inventory != null && inventory.Locked) { spriteColor *= 0.5f; }
|
||||
if (inventory != null && (inventory.Locked || inventory.slots[slotIndex].Items.All(it => it.NonInteractable || it.NonPlayerTeamInteractable))) { spriteColor *= 0.5f; }
|
||||
if (CharacterHealth.OpenHealthWindow != null && !item.UseInHealthInterface)
|
||||
{
|
||||
spriteColor = Color.Lerp(spriteColor, Color.TransparentBlack, 0.5f);
|
||||
@@ -1544,7 +1571,7 @@ namespace Barotrauma
|
||||
}
|
||||
sprite.Draw(spriteBatch, itemPos, spriteColor, rotation, scale);
|
||||
|
||||
if (item.SpawnedInOutpost && CharacterInventory.LimbSlotIcons.ContainsKey(InvSlotType.LeftHand))
|
||||
if (!item.AllowStealing && CharacterInventory.LimbSlotIcons.ContainsKey(InvSlotType.LeftHand))
|
||||
{
|
||||
var stealIcon = CharacterInventory.LimbSlotIcons[InvSlotType.LeftHand];
|
||||
Vector2 iconSize = new Vector2(25 * GUI.Scale);
|
||||
@@ -1559,7 +1586,7 @@ namespace Barotrauma
|
||||
{
|
||||
maxStackSize = Math.Min(maxStackSize, item.Container.GetComponent<ItemContainer>()?.MaxStackSize ?? maxStackSize);
|
||||
}
|
||||
if (maxStackSize > 1)
|
||||
if (maxStackSize > 1 && inventory != null)
|
||||
{
|
||||
int itemCount = slot.MouseOn() ? inventory.slots[slotIndex].ItemCount : inventory.slots[slotIndex].Items.Where(it => !DraggingItems.Contains(it)).Count();
|
||||
if (item.IsFullCondition || MathUtils.NearlyEqual(item.Condition, 0.0f) || itemCount > 1)
|
||||
|
||||
@@ -87,7 +87,7 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!GameMain.SubEditorScreen.ShowThalamus && prefab.Category.HasFlag(MapEntityCategory.Thalamus))
|
||||
if (GameMain.SubEditorScreen.IsSubcategoryHidden(prefab.Subcategory))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -618,6 +618,7 @@ namespace Barotrauma
|
||||
editingHUD = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.25f), GUI.Canvas, Anchor.CenterRight) { MinSize = new Point(400, 0) }) { UserData = this };
|
||||
GUIListBox listBox = new GUIListBox(new RectTransform(new Vector2(0.95f, 0.8f), editingHUD.RectTransform, Anchor.Center), style: null)
|
||||
{
|
||||
CanTakeKeyBoardFocus = false,
|
||||
Spacing = (int)(25 * GUI.Scale)
|
||||
};
|
||||
|
||||
@@ -1463,6 +1464,7 @@ namespace Barotrauma
|
||||
|
||||
byte bodyType = msg.ReadByte();
|
||||
bool spawnedInOutpost = msg.ReadBoolean();
|
||||
bool allowStealing = msg.ReadBoolean();
|
||||
byte teamID = msg.ReadByte();
|
||||
bool tagsChanged = msg.ReadBoolean();
|
||||
string tags = "";
|
||||
@@ -1534,7 +1536,8 @@ namespace Barotrauma
|
||||
|
||||
var item = new Item(itemPrefab, pos, sub, id: itemId)
|
||||
{
|
||||
SpawnedInOutpost = spawnedInOutpost
|
||||
SpawnedInOutpost = spawnedInOutpost,
|
||||
AllowStealing = allowStealing
|
||||
};
|
||||
|
||||
if (item.body != null)
|
||||
|
||||
@@ -22,47 +22,47 @@ namespace Barotrauma
|
||||
var underwaterExplosion = GameMain.ParticleManager.CreateParticle("underwaterexplosion", worldPosition, Vector2.Zero, 0.0f, hull);
|
||||
if (underwaterExplosion != null)
|
||||
{
|
||||
underwaterExplosion.Size *= MathHelper.Clamp(attack.Range / 150.0f, 0.5f, 10.0f);
|
||||
underwaterExplosion.Size *= MathHelper.Clamp(Attack.Range / 150.0f, 0.5f, 10.0f);
|
||||
underwaterExplosion.StartDelay = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < attack.Range * 0.1f; i++)
|
||||
for (int i = 0; i < Attack.Range * 0.1f; i++)
|
||||
{
|
||||
if (!underwater)
|
||||
{
|
||||
float particleSpeed = Rand.Range(0.0f, 1.0f);
|
||||
particleSpeed = particleSpeed * particleSpeed * attack.Range;
|
||||
particleSpeed = particleSpeed * particleSpeed * Attack.Range;
|
||||
|
||||
if (flames)
|
||||
{
|
||||
float particleScale = MathHelper.Clamp(attack.Range * 0.0025f, 0.5f, 2.0f);
|
||||
float particleScale = MathHelper.Clamp(Attack.Range * 0.0025f, 0.5f, 2.0f);
|
||||
var flameParticle = GameMain.ParticleManager.CreateParticle("explosionfire",
|
||||
ClampParticlePos(worldPosition + Rand.Vector((float)System.Math.Sqrt(Rand.Range(0.0f, attack.Range))), hull),
|
||||
ClampParticlePos(worldPosition + Rand.Vector((float)System.Math.Sqrt(Rand.Range(0.0f, Attack.Range))), hull),
|
||||
Rand.Vector(Rand.Range(0.0f, particleSpeed)), 0.0f, hull);
|
||||
if (flameParticle != null) flameParticle.Size *= particleScale;
|
||||
}
|
||||
if (smoke)
|
||||
{
|
||||
GameMain.ParticleManager.CreateParticle(Rand.Range(0.0f, 1.0f) < 0.5f ? "explosionsmoke" : "smoke",
|
||||
ClampParticlePos(worldPosition + Rand.Vector((float)System.Math.Sqrt(Rand.Range(0.0f, attack.Range))), hull),
|
||||
ClampParticlePos(worldPosition + Rand.Vector((float)System.Math.Sqrt(Rand.Range(0.0f, Attack.Range))), hull),
|
||||
Rand.Vector(Rand.Range(0.0f, particleSpeed)), 0.0f, hull);
|
||||
}
|
||||
}
|
||||
else if (underwaterBubble)
|
||||
{
|
||||
Vector2 bubblePos = Rand.Vector(Rand.Range(0.0f, attack.Range * 0.5f));
|
||||
Vector2 bubblePos = Rand.Vector(Rand.Range(0.0f, Attack.Range * 0.5f));
|
||||
|
||||
GameMain.ParticleManager.CreateParticle("risingbubbles", worldPosition + bubblePos,
|
||||
Vector2.Zero, 0.0f, hull);
|
||||
|
||||
if (i < attack.Range * 0.02f)
|
||||
if (i < Attack.Range * 0.02f)
|
||||
{
|
||||
var underwaterExplosion = GameMain.ParticleManager.CreateParticle("underwaterexplosion", worldPosition + bubblePos,
|
||||
Vector2.Zero, 0.0f, hull);
|
||||
if (underwaterExplosion != null)
|
||||
{
|
||||
underwaterExplosion.Size *= MathHelper.Clamp(attack.Range / 300.0f, 0.5f, 2.0f) * Rand.Range(0.8f, 1.2f);
|
||||
underwaterExplosion.Size *= MathHelper.Clamp(Attack.Range / 300.0f, 0.5f, 2.0f) * Rand.Range(0.8f, 1.2f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace Barotrauma
|
||||
|
||||
if (flash)
|
||||
{
|
||||
float displayRange = flashRange.HasValue ? flashRange.Value : attack.Range;
|
||||
float displayRange = flashRange.HasValue ? flashRange.Value : Attack.Range;
|
||||
if (displayRange < 0.1f) { return; }
|
||||
var light = new LightSource(worldPosition, displayRange, Color.LightYellow, null);
|
||||
CoroutineManager.StartCoroutine(DimLight(light));
|
||||
|
||||
@@ -90,7 +90,10 @@ namespace Barotrauma
|
||||
private GUIComponent CreateEditingHUD(bool inGame = false)
|
||||
{
|
||||
editingHUD = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.25f), GUI.Canvas, Anchor.CenterRight) { MinSize = new Point(400, 0) }) { UserData = this };
|
||||
GUIListBox listBox = new GUIListBox(new RectTransform(new Vector2(0.95f, 0.8f), editingHUD.RectTransform, Anchor.Center), style: null);
|
||||
GUIListBox listBox = new GUIListBox(new RectTransform(new Vector2(0.95f, 0.8f), editingHUD.RectTransform, Anchor.Center), style: null)
|
||||
{
|
||||
CanTakeKeyBoardFocus = false
|
||||
};
|
||||
new SerializableEntityEditor(listBox.Content.RectTransform, this, inGame, showName: true, titleFont: GUI.LargeFont);
|
||||
|
||||
PositionEditingHUD();
|
||||
|
||||
@@ -302,7 +302,7 @@ namespace Barotrauma
|
||||
{
|
||||
GUI.DrawLine(spriteBatch, new Vector2(edge.Point1.X + cell.Translation.X, -(edge.Point1.Y + cell.Translation.Y)),
|
||||
new Vector2(edge.Point2.X + cell.Translation.X, -(edge.Point2.Y + cell.Translation.Y)), edge.NextToCave ? Color.Red : (cell.Body == null ? Color.Cyan * 0.5f : (edge.IsSolid ? Color.White : Color.Gray)),
|
||||
width: edge.NextToCave ? 8 :1);
|
||||
width: edge.NextToCave ? 8 : 1);
|
||||
}
|
||||
|
||||
foreach (Vector2 point in cell.BodyVertices)
|
||||
@@ -322,6 +322,11 @@ namespace Barotrauma
|
||||
}
|
||||
}*/
|
||||
|
||||
foreach (var abyssIsland in level.AbyssIslands)
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch, new Vector2(abyssIsland.Area.X, -abyssIsland.Area.Y - abyssIsland.Area.Height), abyssIsland.Area.Size.ToVector2(), Color.Cyan, thickness: 5);
|
||||
}
|
||||
|
||||
foreach (var ruin in level.Ruins)
|
||||
{
|
||||
ruin.DebugDraw(spriteBatch);
|
||||
|
||||
@@ -48,6 +48,7 @@ namespace Barotrauma.Lights
|
||||
private readonly List<LightSource> lights;
|
||||
|
||||
public bool LosEnabled = true;
|
||||
public float LosAlpha = 1f;
|
||||
public LosMode LosMode = LosMode.Transparent;
|
||||
|
||||
public bool LightingEnabled = true;
|
||||
@@ -497,7 +498,6 @@ namespace Barotrauma.Lights
|
||||
|
||||
graphics.SetRenderTarget(LosTexture);
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, transformMatrix: cam.Transform * Matrix.CreateScale(new Vector3(GameMain.Config.LightMapScale, GameMain.Config.LightMapScale, 1.0f)));
|
||||
if (ObstructVision)
|
||||
{
|
||||
graphics.Clear(Color.Black);
|
||||
@@ -507,16 +507,17 @@ namespace Barotrauma.Lights
|
||||
float rotation = MathUtils.VectorToAngle(losOffset);
|
||||
|
||||
Vector2 scale = new Vector2(
|
||||
MathHelper.Clamp(losOffset.Length() / 256.0f, 2.0f, 5.0f), 2.0f);
|
||||
MathHelper.Clamp(losOffset.Length() / 256.0f, 4.0f, 5.0f), 3.0f);
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, transformMatrix: cam.Transform * Matrix.CreateScale(new Vector3(GameMain.Config.LightMapScale, GameMain.Config.LightMapScale, 1.0f)));
|
||||
spriteBatch.Draw(visionCircle, new Vector2(ViewTarget.WorldPosition.X, -ViewTarget.WorldPosition.Y), null, Color.White, rotation,
|
||||
new Vector2(visionCircle.Width * 0.2f, visionCircle.Height / 2), scale, SpriteEffects.None, 0.0f);
|
||||
spriteBatch.End();
|
||||
}
|
||||
else
|
||||
{
|
||||
graphics.Clear(Color.White);
|
||||
}
|
||||
spriteBatch.End();
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -63,7 +63,19 @@ namespace Barotrauma
|
||||
private Sprite[,] mapTiles;
|
||||
private bool[,] tileDiscovered;
|
||||
|
||||
private Pair<Rectangle, string> connectionTooltip;
|
||||
private float connectionHighlightState;
|
||||
|
||||
private (Rectangle targetArea, string tip)? tooltip;
|
||||
private string sanitizedTooltip;
|
||||
private List<RichTextData> tooltipRichTextData;
|
||||
private string prevTooltip;
|
||||
|
||||
private (SubmarineInfo pendingSub, float realWorldCrushDepth) pendingSubInfo;
|
||||
|
||||
/*private (Rectangle targetArea, string tip)? connectionTooltip;
|
||||
private string sanitizedConnectionTooltip;
|
||||
private List<RichTextData> connectionTooltipRichTextData;
|
||||
private string prevConnectionTooltip;*/
|
||||
|
||||
#if DEBUG
|
||||
private GUIComponent editor;
|
||||
@@ -93,13 +105,6 @@ namespace Barotrauma
|
||||
};
|
||||
}
|
||||
#endif
|
||||
public Location CurrentDisplayLocation
|
||||
{
|
||||
get
|
||||
{
|
||||
return GameMain.GameSession.Campaign.CurrentDisplayLocation;
|
||||
}
|
||||
}
|
||||
|
||||
partial void InitProjectSpecific()
|
||||
{
|
||||
@@ -120,7 +125,6 @@ namespace Barotrauma
|
||||
DrawOffset = -CurrentLocation.MapPosition;
|
||||
}
|
||||
|
||||
|
||||
Vector2 tileSize = generationParams.MapTiles.Values.First().First().size * generationParams.MapTileScale;
|
||||
int tilesX = (int)Math.Ceiling(Width / tileSize.X);
|
||||
int tilesY = (int)Math.Ceiling(Height / tileSize.Y);
|
||||
@@ -227,8 +231,8 @@ namespace Barotrauma
|
||||
if (change.Messages.Any())
|
||||
{
|
||||
string msg = change.Messages[Rand.Range(0, change.Messages.Count)]
|
||||
.Replace("[previousname]", prevName)
|
||||
.Replace("[name]", location.Name);
|
||||
.Replace("[previousname]", $"‖color:gui.orange‖{prevName}‖end‖")
|
||||
.Replace("[name]", $"‖color:gui.orange‖{location.Name}‖end‖");
|
||||
location.LastTypeChangeMessage = msg;
|
||||
if (GameMain.Client != null)
|
||||
{
|
||||
@@ -254,28 +258,52 @@ namespace Barotrauma
|
||||
{
|
||||
Rectangle rect = mapContainer.Rect;
|
||||
|
||||
if (CurrentDisplayLocation != null)
|
||||
var currentDisplayLocation = GameMain.GameSession?.Campaign?.GetCurrentDisplayLocation();
|
||||
|
||||
if (currentDisplayLocation != null)
|
||||
{
|
||||
if (!CurrentDisplayLocation.Discovered)
|
||||
if (!currentDisplayLocation.Discovered)
|
||||
{
|
||||
RemoveFogOfWar(CurrentDisplayLocation);
|
||||
CurrentDisplayLocation.Discovered = true;
|
||||
if (CurrentDisplayLocation.MapPosition.X > furthestDiscoveredLocation.MapPosition.X)
|
||||
RemoveFogOfWar(currentDisplayLocation);
|
||||
currentDisplayLocation.Discovered = true;
|
||||
if (currentDisplayLocation.MapPosition.X > furthestDiscoveredLocation.MapPosition.X)
|
||||
{
|
||||
furthestDiscoveredLocation = CurrentDisplayLocation;
|
||||
furthestDiscoveredLocation = currentDisplayLocation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currLocationIndicatorPos = Vector2.Lerp(currLocationIndicatorPos, CurrentDisplayLocation.MapPosition, deltaTime);
|
||||
Vector2 currentPosition = currentDisplayLocation.MapPosition;
|
||||
if (Level.Loaded?.Type == LevelData.LevelType.LocationConnection && Level.Loaded.StartLocation != null && Level.Loaded.EndLocation != null)
|
||||
{
|
||||
Vector2 startPos = currentDisplayLocation == Level.Loaded.StartLocation ? Level.Loaded.StartLocation.MapPosition : Level.Loaded.EndLocation.MapPosition;
|
||||
int moveDir = currentDisplayLocation == Level.Loaded.StartLocation ? 1 : -1;
|
||||
|
||||
Vector2 diff = Level.Loaded.EndLocation.MapPosition - Level.Loaded.StartLocation.MapPosition;
|
||||
currentPosition = startPos +
|
||||
Vector2.Normalize(diff) * Math.Min(100, diff.Length() * 0.2f) * moveDir;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentPosition += Vector2.UnitY * 35;
|
||||
}
|
||||
|
||||
currLocationIndicatorPos = Vector2.Lerp(currLocationIndicatorPos, currentPosition, deltaTime);
|
||||
#if DEBUG
|
||||
if (GameMain.DebugDraw)
|
||||
{
|
||||
if (editor == null) CreateEditor();
|
||||
editor.AddToGUIUpdateList(order: 1);
|
||||
}
|
||||
|
||||
if (PlayerInput.KeyHit(Keys.Space))
|
||||
{
|
||||
Radiation?.OnStep();
|
||||
}
|
||||
#endif
|
||||
|
||||
Radiation?.MapUpdate(deltaTime);
|
||||
|
||||
if (mapAnimQueue.Count > 0)
|
||||
{
|
||||
hudVisibility = Math.Max(hudVisibility - deltaTime, 0.0f);
|
||||
@@ -299,15 +327,16 @@ namespace Barotrauma
|
||||
for (int i = 0; i < Locations.Count; i++)
|
||||
{
|
||||
Location location = Locations[i];
|
||||
if (IsInFogOfWar(location) && !(CurrentDisplayLocation?.Connections.Any(c => c.Locations.Contains(location)) ?? false) && !GameMain.DebugDraw) { continue; }
|
||||
if (IsInFogOfWar(location) && !(currentDisplayLocation?.Connections.Any(c => c.Locations.Contains(location)) ?? false) && !GameMain.DebugDraw) { continue; }
|
||||
|
||||
Vector2 pos = rectCenter + (location.MapPosition + viewOffset) * zoom;
|
||||
if (!rect.Contains(pos)) { continue; }
|
||||
|
||||
float iconScale = generationParams.LocationIconSize / location.Type.Sprite.size.X;
|
||||
if (location == CurrentDisplayLocation) { iconScale *= 1.2f; }
|
||||
Sprite locationSprite = location.IsCriticallyRadiated() ? location.Type.RadiationSprite ?? location.Type.Sprite : location.Type.Sprite;
|
||||
float iconScale = generationParams.LocationIconSize / locationSprite.size.X;
|
||||
if (location == currentDisplayLocation) { iconScale *= 1.2f; }
|
||||
|
||||
Rectangle drawRect = location.Type.Sprite.SourceRect;
|
||||
Rectangle drawRect = locationSprite.SourceRect;
|
||||
drawRect.Width = (int)(drawRect.Width * iconScale * zoom * 1.4f);
|
||||
drawRect.Height = (int)(drawRect.Height * iconScale * zoom * 1.4f);
|
||||
drawRect.X = (int)pos.X - drawRect.Width / 2;
|
||||
@@ -324,6 +353,15 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (SelectedConnection != null)
|
||||
{
|
||||
connectionHighlightState = Math.Min(connectionHighlightState + deltaTime, 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
connectionHighlightState = 0.0f;
|
||||
}
|
||||
|
||||
if (GUI.KeyboardDispatcher.Subscriber == null)
|
||||
{
|
||||
float moveSpeed = 1000.0f;
|
||||
@@ -336,21 +374,27 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
targetZoom = MathHelper.Clamp(targetZoom, generationParams.MinZoom, generationParams.MaxZoom);
|
||||
zoom = MathHelper.Lerp(zoom, targetZoom, 0.1f);
|
||||
zoom = MathHelper.Lerp(zoom, targetZoom * GUI.Scale, 0.1f);
|
||||
|
||||
if (GUI.MouseOn == mapContainer)
|
||||
{
|
||||
foreach (LocationConnection connection in Connections)
|
||||
{
|
||||
if (HighlightedLocation != CurrentDisplayLocation &&
|
||||
connection.Locations.Contains(HighlightedLocation) && connection.Locations.Contains(CurrentDisplayLocation))
|
||||
if (HighlightedLocation != currentDisplayLocation &&
|
||||
connection.Locations.Contains(HighlightedLocation) &&
|
||||
connection.Locations.Contains(currentDisplayLocation))
|
||||
{
|
||||
if (PlayerInput.PrimaryMouseButtonClicked() &&
|
||||
SelectedLocation != HighlightedLocation && HighlightedLocation != null)
|
||||
{
|
||||
//clients aren't allowed to select the location without a permission
|
||||
if ((GameMain.GameSession?.GameMode as CampaignMode)?.AllowedToManageCampaign() ?? false)
|
||||
if (connection.Locked)
|
||||
{
|
||||
new GUIMessageBox(string.Empty, TextManager.Get("LockedPathTooltip"));
|
||||
}
|
||||
//clients aren't allowed to select the location without a permission
|
||||
else if ((GameMain.GameSession?.GameMode as CampaignMode)?.AllowedToManageCampaign() ?? false)
|
||||
{
|
||||
connectionHighlightState = 0.0f;
|
||||
SelectedConnection = connection;
|
||||
SelectedLocation = HighlightedLocation;
|
||||
|
||||
@@ -371,13 +415,13 @@ namespace Barotrauma
|
||||
{
|
||||
if (PlayerInput.DoubleClicked() && HighlightedLocation != null)
|
||||
{
|
||||
var passedConnection = CurrentDisplayLocation.Connections.Find(c => c.OtherLocation(CurrentDisplayLocation) == HighlightedLocation);
|
||||
var passedConnection = currentDisplayLocation.Connections.Find(c => c.OtherLocation(currentDisplayLocation) == HighlightedLocation);
|
||||
if (passedConnection != null)
|
||||
{
|
||||
passedConnection.Passed = true;
|
||||
}
|
||||
|
||||
Location prevLocation = CurrentDisplayLocation;
|
||||
Location prevLocation = currentDisplayLocation;
|
||||
CurrentLocation = HighlightedLocation;
|
||||
Level.Loaded.DebugSetStartLocation(CurrentLocation);
|
||||
Level.Loaded.DebugSetEndLocation(null);
|
||||
@@ -389,6 +433,7 @@ namespace Barotrauma
|
||||
{
|
||||
CurrentLocation.CreateStore();
|
||||
ProgressWorld();
|
||||
Radiation?.OnStep(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -412,12 +457,13 @@ namespace Barotrauma
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, GUICustomComponent mapContainer)
|
||||
{
|
||||
connectionTooltip = null;
|
||||
tooltip = null;
|
||||
var currentDisplayLocation = GameMain.GameSession?.Campaign?.GetCurrentDisplayLocation();
|
||||
|
||||
Rectangle rect = mapContainer.Rect;
|
||||
|
||||
Vector2 viewSize = new Vector2(rect.Width / zoom, rect.Height / zoom);
|
||||
Vector2 edgeBuffer = rect.Size.ToVector2() / 2;
|
||||
Vector2 edgeBuffer = new Vector2(rect.Width * 0.05f);
|
||||
DrawOffset.X = MathHelper.Clamp(DrawOffset.X, -Width - edgeBuffer.X + viewSize.X / 2.0f, edgeBuffer.X - viewSize.X / 2.0f);
|
||||
DrawOffset.Y = MathHelper.Clamp(DrawOffset.Y, -Height - edgeBuffer.Y + viewSize.Y / 2.0f, edgeBuffer.Y - viewSize.Y / 2.0f);
|
||||
|
||||
@@ -481,30 +527,16 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
float rawNoiseScale = 1.0f + PerlinNoise.GetPerlin((int)(Timing.TotalTime * 1 - 1), (int)(Timing.TotalTime * 1 - 1));
|
||||
cameraNoiseStrength = PerlinNoise.GetPerlin((int)(Timing.TotalTime * 1 - 1), (int)(Timing.TotalTime * 1 - 1));
|
||||
DrawNoise(spriteBatch, rect, rawNoiseScale);
|
||||
|
||||
noiseOverlay.DrawTiled(spriteBatch, rect.Location.ToVector2(), rect.Size.ToVector2(),
|
||||
startOffset: new Vector2(Rand.Range(0.0f, noiseOverlay.SourceRect.Width), Rand.Range(0.0f, noiseOverlay.SourceRect.Height)),
|
||||
color : Color.White * cameraNoiseStrength * 0.1f,
|
||||
textureScale: Vector2.One * rawNoiseScale);
|
||||
Radiation?.Draw(spriteBatch, rect, zoom);
|
||||
|
||||
noiseOverlay.DrawTiled(spriteBatch, rect.Location.ToVector2(), rect.Size.ToVector2(),
|
||||
startOffset: new Vector2(Rand.Range(0.0f, noiseOverlay.SourceRect.Width), Rand.Range(0.0f, noiseOverlay.SourceRect.Height)),
|
||||
color: new Color(20,20,20,50),
|
||||
textureScale: Vector2.One * rawNoiseScale * 2);
|
||||
|
||||
noiseOverlay.DrawTiled(spriteBatch, Vector2.Zero, new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight),
|
||||
startOffset: new Vector2(Rand.Range(0.0f, noiseOverlay.SourceRect.Width), Rand.Range(0.0f, noiseOverlay.SourceRect.Height)),
|
||||
color: Color.White * cameraNoiseStrength * 0.1f,
|
||||
textureScale: Vector2.One * noiseScale);
|
||||
|
||||
Pair<Rectangle, string> tooltip = null;
|
||||
if (generationParams.ShowLocations)
|
||||
{
|
||||
foreach (LocationConnection connection in Connections)
|
||||
{
|
||||
if (IsInFogOfWar(connection.Locations[0]) && IsInFogOfWar(connection.Locations[1])) { continue; }
|
||||
DrawConnection(spriteBatch, connection, rect, viewOffset);
|
||||
DrawConnection(spriteBatch, connection, rect, viewOffset, currentDisplayLocation);
|
||||
}
|
||||
|
||||
for (int i = 0; i < Locations.Count; i++)
|
||||
@@ -512,19 +544,48 @@ namespace Barotrauma
|
||||
Location location = Locations[i];
|
||||
if (IsInFogOfWar(location)) { continue; }
|
||||
Vector2 pos = rectCenter + (location.MapPosition + viewOffset) * zoom;
|
||||
|
||||
Rectangle drawRect = location.Type.Sprite.SourceRect;
|
||||
|
||||
Sprite locationSprite = location.IsCriticallyRadiated() ? location.Type.RadiationSprite ?? location.Type.Sprite : location.Type.Sprite;
|
||||
|
||||
Rectangle drawRect = locationSprite.SourceRect;
|
||||
drawRect.X = (int)pos.X - drawRect.Width / 2;
|
||||
drawRect.Y = (int)pos.Y - drawRect.Width / 2;
|
||||
|
||||
if (!rect.Intersects(drawRect)) { continue; }
|
||||
|
||||
if (location == CurrentDisplayLocation )
|
||||
Color color = location.Type.SpriteColor;
|
||||
if (!location.Discovered) { color = Color.White; }
|
||||
if (location.Connections.Find(c => c.Locations.Contains(currentDisplayLocation)) == null)
|
||||
{
|
||||
color *= 0.5f;
|
||||
}
|
||||
|
||||
float iconScale = location == currentDisplayLocation ? 1.2f : 1.0f;
|
||||
if (location == HighlightedLocation)
|
||||
{
|
||||
iconScale *= 1.2f;
|
||||
}
|
||||
|
||||
locationSprite.Draw(spriteBatch, pos, color,
|
||||
scale: generationParams.LocationIconSize / locationSprite.size.X * iconScale * zoom);
|
||||
|
||||
if (location == currentDisplayLocation)
|
||||
{
|
||||
if (SelectedLocation != null)
|
||||
{
|
||||
Vector2 dir = Vector2.Normalize(SelectedLocation.MapPosition - currLocationIndicatorPos);
|
||||
GUI.Arrow.Draw(spriteBatch,
|
||||
rectCenter + (currLocationIndicatorPos + viewOffset) * zoom + dir * generationParams.LocationIconSize * 0.6f * zoom,
|
||||
generationParams.IndicatorColor,
|
||||
GUI.Arrow.Origin,
|
||||
rotate: MathUtils.VectorToAngle(dir) + MathHelper.PiOver2,
|
||||
new Vector2(0.5f, 1.0f) * zoom);
|
||||
}
|
||||
generationParams.CurrentLocationIndicator.Draw(spriteBatch,
|
||||
rectCenter + (currLocationIndicatorPos + viewOffset) * zoom,
|
||||
generationParams.IndicatorColor,
|
||||
generationParams.CurrentLocationIndicator.Origin, 0, Vector2.One * (generationParams.LocationIconSize / generationParams.CurrentLocationIndicator.size.X) * 1.7f * zoom);
|
||||
generationParams.CurrentLocationIndicator.Origin, 0, Vector2.One * (generationParams.LocationIconSize / generationParams.CurrentLocationIndicator.size.X) * 0.8f * zoom);
|
||||
|
||||
}
|
||||
|
||||
if (location == SelectedLocation)
|
||||
@@ -535,31 +596,15 @@ namespace Barotrauma
|
||||
generationParams.SelectedLocationIndicator.Origin, 0, Vector2.One * (generationParams.LocationIconSize / generationParams.SelectedLocationIndicator.size.X) * 1.7f * zoom);
|
||||
}
|
||||
|
||||
Color color = location.Type.SpriteColor;
|
||||
if (!location.Discovered) { color = Color.White; }
|
||||
if (location.Connections.Find(c => c.Locations.Contains(CurrentDisplayLocation)) == null)
|
||||
{
|
||||
color *= 0.5f;
|
||||
}
|
||||
|
||||
float iconScale = location == CurrentDisplayLocation ? 1.2f : 1.0f;
|
||||
if (location == HighlightedLocation)
|
||||
{
|
||||
iconScale *= 1.2f;
|
||||
}
|
||||
|
||||
location.Type.Sprite.Draw(spriteBatch, pos, color,
|
||||
scale: generationParams.LocationIconSize / location.Type.Sprite.size.X * iconScale * zoom);
|
||||
if (location.TimeSinceLastTypeChange < 1 && !string.IsNullOrEmpty(location.LastTypeChangeMessage) && generationParams.TypeChangeIcon != null)
|
||||
{
|
||||
Vector2 typeChangeIconPos = pos + new Vector2(1.35f, -0.35f) * generationParams.LocationIconSize * 0.5f * zoom;
|
||||
float typeChangeIconScale = 18.0f / generationParams.TypeChangeIcon.SourceRect.Width;
|
||||
generationParams.TypeChangeIcon.Draw(spriteBatch, typeChangeIconPos, GUI.Style.Red, scale: typeChangeIconScale * zoom);
|
||||
if (Vector2.Distance(PlayerInput.MousePosition, typeChangeIconPos) < generationParams.TypeChangeIcon.SourceRect.Width * zoom)
|
||||
if (Vector2.Distance(PlayerInput.MousePosition, typeChangeIconPos) < generationParams.TypeChangeIcon.SourceRect.Width * zoom &&
|
||||
(tooltip == null || IsPreferredTooltip(typeChangeIconPos)))
|
||||
{
|
||||
tooltip = new Pair<Rectangle, string>(
|
||||
new Rectangle(typeChangeIconPos.ToPoint(), new Point(30)),
|
||||
location.LastTypeChangeMessage);
|
||||
tooltip = (new Rectangle(typeChangeIconPos.ToPoint(), new Point(30)), location.LastTypeChangeMessage);
|
||||
}
|
||||
}
|
||||
if (location != CurrentLocation && CurrentLocation.AvailableMissions.Any(m => m.Locations.Contains(location)) && generationParams.MissionIcon != null)
|
||||
@@ -567,16 +612,14 @@ namespace Barotrauma
|
||||
Vector2 missionIconPos = pos + new Vector2(1.35f, 0.35f) * generationParams.LocationIconSize * 0.5f * zoom;
|
||||
float missionIconScale = 18.0f / generationParams.MissionIcon.SourceRect.Width;
|
||||
generationParams.MissionIcon.Draw(spriteBatch, missionIconPos, generationParams.IndicatorColor, scale: missionIconScale * zoom);
|
||||
if (Vector2.Distance(PlayerInput.MousePosition, missionIconPos) < generationParams.MissionIcon.SourceRect.Width * zoom)
|
||||
if (Vector2.Distance(PlayerInput.MousePosition, missionIconPos) < generationParams.MissionIcon.SourceRect.Width * zoom && IsPreferredTooltip(missionIconPos))
|
||||
{
|
||||
var availableMissions = CurrentLocation.AvailableMissions.Where(m => m.Locations.Contains(location));
|
||||
tooltip = new Pair<Rectangle, string>(
|
||||
new Rectangle(missionIconPos.ToPoint(), new Point(30)),
|
||||
TextManager.Get("mission") + '\n'+ string.Join('\n', availableMissions.Select(m => "- " + m.Name)));
|
||||
tooltip = (new Rectangle(missionIconPos.ToPoint(), new Point(30)), TextManager.Get("mission") + '\n'+ string.Join('\n', availableMissions.Select(m => "- " + m.Name)));
|
||||
}
|
||||
}
|
||||
|
||||
if (GameMain.DebugDraw && location == HighlightedLocation && (!location.Discovered || !location.Type.HasOutpost))
|
||||
if (GameMain.DebugDraw && location == HighlightedLocation && (!location.Discovered || !location.HasOutpost()))
|
||||
{
|
||||
if (location.Reputation != null)
|
||||
{
|
||||
@@ -603,12 +646,27 @@ namespace Barotrauma
|
||||
|
||||
DrawDecorativeHUD(spriteBatch, rect);
|
||||
|
||||
if (HighlightedLocation != null)
|
||||
bool drawRadiationTooltip = true;
|
||||
|
||||
if (tooltip != null)
|
||||
{
|
||||
if (tooltip.Value.tip != prevTooltip)
|
||||
{
|
||||
prevTooltip = tooltip.Value.tip;
|
||||
tooltipRichTextData = RichTextData.GetRichTextData(tooltip.Value.tip, out sanitizedTooltip);
|
||||
}
|
||||
GUIComponent.DrawToolTip(spriteBatch, sanitizedTooltip, tooltip.Value.targetArea, tooltipRichTextData);
|
||||
drawRadiationTooltip = false;
|
||||
}
|
||||
else if (HighlightedLocation != null)
|
||||
{
|
||||
drawRadiationTooltip = false;
|
||||
Vector2 pos = rectCenter + (HighlightedLocation.MapPosition + viewOffset) * zoom;
|
||||
pos.X += 50 * zoom;
|
||||
pos.X = (int)pos.X;
|
||||
pos.Y = (int)pos.Y;
|
||||
Vector2 nameSize = GUI.LargeFont.MeasureString(HighlightedLocation.Name);
|
||||
Vector2 typeSize = GUI.Font.MeasureString(HighlightedLocation.Type.Name);
|
||||
Vector2 typeSize = string.IsNullOrEmpty(HighlightedLocation.Type.Name) ? Vector2.Zero : GUI.Font.MeasureString(HighlightedLocation.Type.Name);
|
||||
Vector2 size = new Vector2(Math.Max(nameSize.X, typeSize.X), nameSize.Y + typeSize.Y);
|
||||
bool showReputation = hudVisibility > 0.0f && HighlightedLocation.Discovered && HighlightedLocation.Type.HasOutpost && HighlightedLocation.Reputation != null;
|
||||
string repLabelText = null, repValueText = null;
|
||||
@@ -616,15 +674,14 @@ namespace Barotrauma
|
||||
if (showReputation)
|
||||
{
|
||||
repLabelText = TextManager.Get("reputation");
|
||||
repLabelSize = GUI.Font.MeasureString(repLabelText);
|
||||
size.X = Math.Max(size.X, repLabelSize.X);
|
||||
repBarSize = new Vector2(Math.Max(0.75f * size.X, 100), repLabelSize.Y);
|
||||
size.X = Math.Max(size.X, (4.0f / 3.0f) * repBarSize.X);
|
||||
size.Y += 2 * repLabelSize.Y + 4 + repBarSize.Y;
|
||||
repValueText = ((int)HighlightedLocation.Reputation.Value).ToString();
|
||||
repLabelSize = GUI.Font.MeasureString(repLabelText);
|
||||
repBarSize = new Vector2(GUI.IntScale(200), repLabelSize.Y);
|
||||
size.Y += 2 * repLabelSize.Y + GUI.IntScale(5) + repBarSize.Y;
|
||||
repValueText = HighlightedLocation.Reputation.GetFormattedReputationText(addColorTags: false);
|
||||
size.X = Math.Max(size.X, repBarSize.X + GUI.Font.MeasureString(repValueText).X + GUI.IntScale(10));
|
||||
}
|
||||
GUI.Style.GetComponentStyle("OuterGlow").Sprites[GUIComponent.ComponentState.None][0].Draw(
|
||||
spriteBatch, new Rectangle((int)(pos.X - 60 * GUI.Scale), (int)(pos.Y - size.Y), (int)(size.X + 120 * GUI.Scale), (int)(size.Y * 2.2f)), Color.Black * hudVisibility);
|
||||
spriteBatch, new Rectangle((int)(pos.X - 60 * GUI.Scale), (int)(pos.Y - size.Y), (int)(size.X + 120 * GUI.Scale), (int)(size.Y * 2.2f)), Color.Black * hudVisibility);
|
||||
var topLeftPos = pos - new Vector2(0.0f, size.Y / 2);
|
||||
GUI.DrawString(spriteBatch, topLeftPos, HighlightedLocation.Name, GUI.Style.TextColor * hudVisibility * 1.5f, font: GUI.LargeFont);
|
||||
topLeftPos += new Vector2(0.0f, nameSize.Y);
|
||||
@@ -633,26 +690,51 @@ namespace Barotrauma
|
||||
{
|
||||
topLeftPos += new Vector2(0.0f, typeSize.Y + repLabelSize.Y);
|
||||
GUI.DrawString(spriteBatch, topLeftPos, repLabelText, GUI.Style.TextColor * hudVisibility * 1.5f);
|
||||
topLeftPos += new Vector2(0.0f, repLabelSize.Y + 4);
|
||||
topLeftPos += new Vector2(0.0f, repLabelSize.Y + GUI.IntScale(10));
|
||||
Rectangle repBarRect = new Rectangle(new Point((int)topLeftPos.X, (int)topLeftPos.Y), new Point((int)repBarSize.X, (int)repBarSize.Y));
|
||||
RoundSummary.DrawReputationBar(spriteBatch, repBarRect, HighlightedLocation.Reputation.NormalizedValue);
|
||||
GUI.DrawString(spriteBatch, new Vector2(repBarRect.Right + 4, repBarRect.Top), repValueText, GUI.Style.TextColor);
|
||||
GUI.DrawString(spriteBatch, new Vector2(repBarRect.Right + GUI.IntScale(5), repBarRect.Top), repValueText, Reputation.GetReputationColor(HighlightedLocation.Reputation.NormalizedValue));
|
||||
}
|
||||
}
|
||||
if (tooltip != null)
|
||||
|
||||
if (drawRadiationTooltip)
|
||||
{
|
||||
GUIComponent.DrawToolTip(spriteBatch, tooltip.Second, tooltip.First);
|
||||
}
|
||||
if (connectionTooltip != null)
|
||||
{
|
||||
GUIComponent.DrawToolTip(spriteBatch, connectionTooltip.Second, connectionTooltip.First);
|
||||
Radiation?.DrawFront(spriteBatch);
|
||||
}
|
||||
|
||||
spriteBatch.End();
|
||||
GameMain.Instance.GraphicsDevice.ScissorRectangle = prevScissorRect;
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
|
||||
}
|
||||
|
||||
private void DrawConnection(SpriteBatch spriteBatch, LocationConnection connection, Rectangle viewArea, Vector2 viewOffset, Color? overrideColor = null)
|
||||
public static void DrawNoise(SpriteBatch spriteBatch, Rectangle rect, float strength)
|
||||
{
|
||||
noiseOverlay ??= new Sprite("Content/UI/noise.png", Vector2.Zero);
|
||||
|
||||
float noiseT = (float)(Timing.TotalTime * 0.01f);
|
||||
float noiseScale = (float)PerlinNoise.CalculatePerlin(noiseT * 5.0f, noiseT * 2.0f, 0) * 5.0f;
|
||||
|
||||
float rawNoiseScale = 1.0f + GetPerlinNoise();
|
||||
|
||||
noiseOverlay.DrawTiled(spriteBatch, rect.Location.ToVector2(), rect.Size.ToVector2(),
|
||||
startOffset: new Vector2(Rand.Range(0.0f, noiseOverlay.SourceRect.Width), Rand.Range(0.0f, noiseOverlay.SourceRect.Height)),
|
||||
color : Color.White * strength * 0.1f,
|
||||
textureScale: Vector2.One * rawNoiseScale);
|
||||
|
||||
noiseOverlay.DrawTiled(spriteBatch, rect.Location.ToVector2(), rect.Size.ToVector2(),
|
||||
startOffset: new Vector2(Rand.Range(0.0f, noiseOverlay.SourceRect.Width), Rand.Range(0.0f, noiseOverlay.SourceRect.Height)),
|
||||
color: new Color(20,20,20,50),
|
||||
textureScale: Vector2.One * rawNoiseScale * 2);
|
||||
|
||||
noiseOverlay.DrawTiled(spriteBatch, Vector2.Zero, new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight),
|
||||
startOffset: new Vector2(Rand.Range(0.0f, noiseOverlay.SourceRect.Width), Rand.Range(0.0f, noiseOverlay.SourceRect.Height)),
|
||||
color: Color.White * strength * 0.1f,
|
||||
textureScale: Vector2.One * noiseScale);
|
||||
}
|
||||
|
||||
private static float GetPerlinNoise() => PerlinNoise.GetPerlin((int)(Timing.TotalTime * 1 - 1), (int)(Timing.TotalTime * 1 - 1));
|
||||
|
||||
private void DrawConnection(SpriteBatch spriteBatch, LocationConnection connection, Rectangle viewArea, Vector2 viewOffset, Location currentDisplayLocation, Color? overrideColor = null)
|
||||
{
|
||||
Color connectionColor;
|
||||
if (GameMain.DebugDraw)
|
||||
@@ -674,19 +756,22 @@ namespace Barotrauma
|
||||
|
||||
int width = (int)(generationParams.LocationConnectionWidth * zoom);
|
||||
|
||||
//current level
|
||||
if (Level.Loaded?.LevelData == connection.LevelData)
|
||||
{
|
||||
connectionColor = generationParams.HighlightedConnectionColor;
|
||||
width = (int)(width * 1.5f);
|
||||
}
|
||||
if (SelectedLocation != CurrentDisplayLocation &&
|
||||
(connection.Locations.Contains(SelectedLocation) && connection.Locations.Contains(CurrentDisplayLocation)))
|
||||
//selected connection
|
||||
if (SelectedLocation != currentDisplayLocation &&
|
||||
connection.Locations.Contains(SelectedLocation) && connection.Locations.Contains(currentDisplayLocation))
|
||||
{
|
||||
connectionColor = generationParams.HighlightedConnectionColor;
|
||||
width *= 2;
|
||||
}
|
||||
else if (HighlightedLocation != CurrentDisplayLocation &&
|
||||
(connection.Locations.Contains(HighlightedLocation) && connection.Locations.Contains(CurrentDisplayLocation)))
|
||||
//highlighted connection
|
||||
else if (HighlightedLocation != currentDisplayLocation &&
|
||||
connection.Locations.Contains(HighlightedLocation) && connection.Locations.Contains(currentDisplayLocation))
|
||||
{
|
||||
connectionColor = generationParams.HighlightedConnectionColor;
|
||||
width *= 2;
|
||||
@@ -741,16 +826,52 @@ namespace Barotrauma
|
||||
}
|
||||
float dist = Vector2.Distance(start, end);
|
||||
var connectionSprite = connection.Passed ? generationParams.PassedConnectionSprite : generationParams.ConnectionSprite;
|
||||
|
||||
Color segmentColor = connectionColor;
|
||||
int segmentWidth = width;
|
||||
if (connection == SelectedConnection)
|
||||
{
|
||||
float t = (i - startIndex) / (float)(endIndex - startIndex - 1);
|
||||
if (currentDisplayLocation == connection.Locations[1]) { t = 1.0f - t; }
|
||||
if (t > connectionHighlightState)
|
||||
{
|
||||
segmentWidth /= 2;
|
||||
segmentColor = connection.Passed ? generationParams.ConnectionColor : generationParams.UnvisitedConnectionColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
spriteBatch.Draw(connectionSprite.Texture,
|
||||
new Rectangle((int)start.X, (int)start.Y, (int)(dist - 1 * zoom), width),
|
||||
connectionSprite.SourceRect, connectionColor * a, MathUtils.VectorToAngle(end - start),
|
||||
new Rectangle((int)start.X, (int)start.Y, (int)(dist - 1 * zoom), segmentWidth),
|
||||
connectionSprite.SourceRect, segmentColor * a,
|
||||
MathUtils.VectorToAngle(end - start),
|
||||
new Vector2(0, connectionSprite.size.Y / 2), SpriteEffects.None, 0.01f);
|
||||
}
|
||||
|
||||
int iconCount = 0, iconIndex = 0;
|
||||
if (connectionStart.HasValue && connectionEnd.HasValue)
|
||||
{
|
||||
GUIComponentStyle crushDepthWarningIconStyle = null;
|
||||
{
|
||||
if (connection.LevelData.HasBeaconStation) { iconCount++; }
|
||||
if (connection.LevelData.HasHuntingGrounds) { iconCount++; }
|
||||
if (connection.Locked) { iconCount++; }
|
||||
string tooltip = null;
|
||||
var subCrushDepth = Submarine.MainSub?.RealWorldCrushDepth ?? Level.DefaultRealWorldCrushDepth;
|
||||
float subCrushDepth = Level.DefaultRealWorldCrushDepth;
|
||||
var currentOrPendingSub = SubmarineSelection.CurrentOrPendingSubmarine();
|
||||
if (Submarine.MainSub != null && Submarine.MainSub.Info == currentOrPendingSub)
|
||||
{
|
||||
subCrushDepth = Submarine.MainSub.RealWorldCrushDepth;
|
||||
}
|
||||
else if (currentOrPendingSub != null)
|
||||
{
|
||||
if (pendingSubInfo.pendingSub != currentOrPendingSub)
|
||||
{
|
||||
// Store the real world crush depth for the pending sub so that we don't have to calculate it again every time
|
||||
pendingSubInfo = (currentOrPendingSub, currentOrPendingSub.GetRealWorldCrushDepth());
|
||||
}
|
||||
subCrushDepth = pendingSubInfo.realWorldCrushDepth;
|
||||
}
|
||||
if (GameMain.GameSession?.Campaign?.UpgradeManager != null)
|
||||
{
|
||||
var hullUpgradePrefab = UpgradePrefab.Find("increasewallhealth");
|
||||
@@ -769,38 +890,71 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
string crushDepthWarningIconStyle = null;
|
||||
if (connection.LevelData.InitialDepth * Physics.DisplayToRealWorldRatio > subCrushDepth)
|
||||
{
|
||||
crushDepthWarningIconStyle = GUI.Style.GetComponentStyle("CrushDepthWarningHighIcon");
|
||||
iconCount++;
|
||||
crushDepthWarningIconStyle = "CrushDepthWarningHighIcon";
|
||||
tooltip = "crushdepthwarninghigh";
|
||||
}
|
||||
else if ((connection.LevelData.InitialDepth + connection.LevelData.Size.Y) * Physics.DisplayToRealWorldRatio > subCrushDepth)
|
||||
{
|
||||
crushDepthWarningIconStyle = GUI.Style.GetComponentStyle("CrushDepthWarningLowIcon");
|
||||
iconCount++;
|
||||
crushDepthWarningIconStyle = "CrushDepthWarningLowIcon";
|
||||
tooltip = "crushdepthwarninglow";
|
||||
}
|
||||
|
||||
if (connection.LevelData.HasBeaconStation)
|
||||
{
|
||||
var beaconStationIconStyle = connection.LevelData.IsBeaconActive ? "BeaconStationActive" : "BeaconStationInactive";
|
||||
DrawIcon(beaconStationIconStyle, (int)(28 * zoom), TextManager.Get(connection.LevelData.IsBeaconActive ? "BeaconStationActiveTooltip" : "BeaconStationInactiveTooltip"));
|
||||
}
|
||||
|
||||
if (connection.Locked)
|
||||
{
|
||||
var gateLocation = connection.Locations[0].IsGateBetweenBiomes ? connection.Locations[0] : connection.Locations[1];
|
||||
var unlockEvent =
|
||||
EventSet.PrefabList.Find(ep => ep.UnlockPathEvent && ep.BiomeIdentifier == gateLocation.LevelData.Biome.Identifier) ??
|
||||
EventSet.PrefabList.Find(ep => ep.UnlockPathEvent && string.IsNullOrEmpty(ep.BiomeIdentifier));
|
||||
|
||||
if (unlockEvent != null)
|
||||
{
|
||||
Reputation unlockReputation = CurrentLocation.Reputation;
|
||||
Faction unlockFaction = null;
|
||||
if (!string.IsNullOrEmpty(unlockEvent.UnlockPathFaction))
|
||||
{
|
||||
unlockFaction = GameMain.GameSession.Campaign.Factions.Find(f => f.Prefab.Identifier.Equals(unlockEvent.UnlockPathFaction, StringComparison.OrdinalIgnoreCase));
|
||||
unlockReputation = unlockFaction?.Reputation;
|
||||
}
|
||||
|
||||
DrawIcon(
|
||||
"LockedLocationConnection", (int)(28 * zoom),
|
||||
TextManager.GetWithVariables(unlockEvent.UnlockPathTooltip ?? "LockedPathTooltip",
|
||||
new string[] { "[requiredreputation]", "[currentreputation]" },
|
||||
new string[] { Reputation.GetFormattedReputationText(MathUtils.InverseLerp(unlockReputation.MinReputation, unlockReputation.MaxReputation, unlockEvent.UnlockPathReputation), unlockEvent.UnlockPathReputation, addColorTags: true), unlockReputation.GetFormattedReputationText(addColorTags: true) }));
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawIcon("LockedLocationConnection", (int)(28 * zoom), TextManager.Get("LockedPathTooltip"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (connection.LevelData.HasHuntingGrounds)
|
||||
{
|
||||
DrawIcon("HuntingGrounds", (int)(28 * zoom), TextManager.Get("HuntingGroundsTooltip"));
|
||||
}
|
||||
|
||||
if (crushDepthWarningIconStyle != null)
|
||||
{
|
||||
Vector2 iconPos = (connectionStart.Value + connectionEnd.Value) / 2;
|
||||
float iconSize = 32.0f * GUI.Scale;
|
||||
bool mouseOn = HighlightedLocation == null && Vector2.DistanceSquared(iconPos, PlayerInput.MousePosition) < iconSize * iconSize;
|
||||
Sprite crushDepthWarningIcon = crushDepthWarningIconStyle.GetDefaultSprite();
|
||||
crushDepthWarningIcon.Draw(spriteBatch, iconPos,
|
||||
mouseOn ? crushDepthWarningIconStyle.HoverColor : crushDepthWarningIconStyle.Color,
|
||||
scale: iconSize / crushDepthWarningIcon.size.X);
|
||||
if (mouseOn)
|
||||
{
|
||||
connectionTooltip = new Pair<Rectangle, string>(
|
||||
new Rectangle(iconPos.ToPoint(), new Point((int)iconSize)),
|
||||
TextManager.Get(tooltip)
|
||||
.Replace("[initialdepth]", ((int)(connection.LevelData.InitialDepth * Physics.DisplayToRealWorldRatio)).ToString())
|
||||
.Replace("[submarinecrushdepth]", ((int)subCrushDepth).ToString()));
|
||||
}
|
||||
DrawIcon(crushDepthWarningIconStyle, (int)(32 * zoom),
|
||||
TextManager.Get(tooltip)
|
||||
.Replace("[initialdepth]", $"‖color:gui.orange‖{(int)(connection.LevelData.InitialDepth * Physics.DisplayToRealWorldRatio)}‖end‖")
|
||||
.Replace("[submarinecrushdepth]", $"‖color:gui.orange‖{(int)subCrushDepth}‖end‖"));
|
||||
}
|
||||
}
|
||||
|
||||
if (GameMain.DebugDraw && zoom > 1.0f && generationParams.ShowLevelTypeNames)
|
||||
if (GameMain.DebugDraw && zoom > (1.0f * GUI.Scale) && generationParams.ShowLevelTypeNames)
|
||||
{
|
||||
Vector2 center = rectCenter + (connection.CenterPos + viewOffset) * zoom;
|
||||
if (viewArea.Contains(center) && connection.Biome != null)
|
||||
@@ -808,6 +962,30 @@ namespace Barotrauma
|
||||
GUI.DrawString(spriteBatch, center, connection.Biome.Identifier + " (" + connection.Difficulty + ")", Color.White);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawIcon(string iconStyle, int iconSize, string tooltipText)
|
||||
{
|
||||
Vector2 iconPos = (connectionStart.Value + connectionEnd.Value) / 2;
|
||||
Vector2 iconDiff = Vector2.Normalize(connectionEnd.Value - connectionStart.Value) * iconSize;
|
||||
|
||||
iconPos += (iconDiff * -(iconCount - 1) / 2.0f) + iconDiff * iconIndex;
|
||||
|
||||
var style = GUI.Style.GetComponentStyle(iconStyle);
|
||||
bool mouseOn = Vector2.DistanceSquared(iconPos, PlayerInput.MousePosition) < iconSize * iconSize && IsPreferredTooltip(iconPos);
|
||||
Sprite iconSprite = style.GetDefaultSprite();
|
||||
iconSprite.Draw(spriteBatch, iconPos, (mouseOn ? style.HoverColor : style.Color) * 0.7f,
|
||||
scale: iconSize / iconSprite.size.X);
|
||||
if (mouseOn)
|
||||
{
|
||||
tooltip = (new Rectangle((iconPos - Vector2.One * iconSize / 2).ToPoint(), new Point(iconSize)), tooltipText);
|
||||
}
|
||||
iconIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsPreferredTooltip(Vector2 tooltipPos)
|
||||
{
|
||||
return tooltip == null || Vector2.DistanceSquared(tooltipPos, PlayerInput.MousePosition) < Vector2.DistanceSquared(tooltip.Value.targetArea.Center.ToVector2(), PlayerInput.MousePosition);
|
||||
}
|
||||
|
||||
private float hudVisibility;
|
||||
@@ -828,8 +1006,8 @@ namespace Barotrauma
|
||||
|
||||
private void UpdateMapAnim(MapAnim anim, float deltaTime)
|
||||
{
|
||||
//pause animation while there are messageboxes on screen
|
||||
if (GUIMessageBox.MessageBoxes.Count > 0) return;
|
||||
//pause animation while there are messageboxes (other than hints) on screen
|
||||
if (GUIMessageBox.MessageBoxes.Count(c => !(c is GUIMessageBox mb) || mb.MessageBoxType != GUIMessageBox.Type.Hint) > 0) { return; }
|
||||
|
||||
if (!string.IsNullOrEmpty(anim.StartMessage))
|
||||
{
|
||||
@@ -838,8 +1016,9 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
if (anim.StartZoom == null) { anim.StartZoom = MathUtils.InverseLerp(generationParams.MinZoom, generationParams.MaxZoom, zoom); }
|
||||
if (anim.EndZoom == null) { anim.EndZoom = MathUtils.InverseLerp(generationParams.MinZoom, generationParams.MaxZoom, zoom); }
|
||||
float unscaledZoom = zoom / GUI.Scale;
|
||||
if (anim.StartZoom == null) { anim.StartZoom = MathUtils.InverseLerp(generationParams.MinZoom, generationParams.MaxZoom, unscaledZoom); }
|
||||
if (anim.EndZoom == null) { anim.EndZoom = MathUtils.InverseLerp(generationParams.MinZoom, generationParams.MaxZoom, unscaledZoom); }
|
||||
|
||||
anim.StartPos = (anim.StartLocation == null) ? -DrawOffset : anim.StartLocation.MapPosition;
|
||||
|
||||
@@ -852,7 +1031,8 @@ namespace Barotrauma
|
||||
|
||||
zoom =
|
||||
MathHelper.Lerp(generationParams.MinZoom, generationParams.MaxZoom,
|
||||
MathHelper.SmoothStep(anim.StartZoom.Value, anim.EndZoom.Value, t));
|
||||
MathHelper.SmoothStep(anim.StartZoom.Value, anim.EndZoom.Value, t))
|
||||
* GUI.Scale;
|
||||
|
||||
if (anim.Timer >= anim.Duration)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal partial class Radiation
|
||||
{
|
||||
private static readonly string radiationTooltip = TextManager.Get("RadiationTooltip");
|
||||
private static float spriteIndex;
|
||||
private readonly SpriteSheet sheet = GUI.Style.RadiationAnimSpriteSheet;
|
||||
private int maxFrames => sheet.FrameCount + 1;
|
||||
|
||||
private bool isHovingOver;
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, Rectangle container, float zoom)
|
||||
{
|
||||
if (!Enabled) { return; }
|
||||
|
||||
UISprite uiSprite = GUI.Style.RadiationSprite;
|
||||
var (offsetX, offsetY) = Map.DrawOffset * zoom;
|
||||
var (centerX, centerY) = container.Center.ToVector2();
|
||||
var (halfSizeX, halfSizeY) = new Vector2(container.Width / 2f, container.Height / 2f) * zoom;
|
||||
float viewBottom = centerY + Map.Height * zoom;
|
||||
Vector2 topLeft = new Vector2(centerX + offsetX - halfSizeX, centerY + offsetY - halfSizeY);
|
||||
Vector2 size = new Vector2((Amount - increasedAmount) * zoom + halfSizeX, viewBottom - topLeft.Y);
|
||||
if (size.X < 0) { return; }
|
||||
|
||||
Vector2 spriteScale = new Vector2(zoom);
|
||||
|
||||
uiSprite.Sprite.DrawTiled(spriteBatch, topLeft, size, Params.RadiationAreaColor, Vector2.Zero, textureScale: spriteScale);
|
||||
|
||||
Vector2 topRight = topLeft + Vector2.UnitX * size.X;
|
||||
|
||||
int index = 0;
|
||||
for (float i = 0; i <= size.Y; i += sheet.FrameSize.Y / 2f * zoom)
|
||||
{
|
||||
bool isEven = ++index % 2 == 0;
|
||||
Vector2 origin = new Vector2(0.5f, 0) * sheet.FrameSize.X;
|
||||
// every other sprite's animation is reversed to make it seem more chaotic
|
||||
int sprite = (int) MathF.Floor(isEven ? spriteIndex : maxFrames - spriteIndex);
|
||||
sheet.Draw(spriteBatch, sprite, topRight + new Vector2(0, i), Params.RadiationBorderTint, origin, 0f, spriteScale);
|
||||
}
|
||||
|
||||
isHovingOver = container.Contains(PlayerInput.MousePosition) && PlayerInput.MousePosition.X < topLeft.X + size.X;
|
||||
}
|
||||
|
||||
public void DrawFront(SpriteBatch spriteBatch)
|
||||
{
|
||||
if (isHovingOver)
|
||||
{
|
||||
GUIComponent.DrawToolTip(spriteBatch, radiationTooltip, PlayerInput.MousePosition + new Vector2(18 * GUI.Scale));
|
||||
}
|
||||
}
|
||||
|
||||
public void MapUpdate(float deltaTime)
|
||||
{
|
||||
float spriteStep = Params.BorderAnimationSpeed * deltaTime;
|
||||
spriteIndex = (spriteIndex + spriteStep) % maxFrames;
|
||||
|
||||
if (increasedAmount > 0)
|
||||
{
|
||||
increasedAmount -= (lastIncrease / Params.AnimationSpeed) * deltaTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,9 +20,6 @@ namespace Barotrauma
|
||||
|
||||
public static Vector2 StartMovingPos => startMovingPos;
|
||||
|
||||
// Quick undo/redo for size and movement only. TODO: Remove if we do a more general implementation.
|
||||
private Memento<Rectangle> rectMemento;
|
||||
|
||||
public event Action<Rectangle> Resized;
|
||||
|
||||
private static bool resizing;
|
||||
@@ -65,8 +62,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
//protected bool isSelected;
|
||||
|
||||
private static bool disableSelect;
|
||||
public static bool DisableSelect
|
||||
{
|
||||
@@ -805,7 +800,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (selectionPos != null && selectionPos != Vector2.Zero)
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch, new Vector2(selectionPos.X, -selectionPos.Y), selectionSize, Color.DarkRed, false, 0, (int)Math.Max(1.5f / GameScreen.Selected.Cam.Zoom, 1.0f));
|
||||
GUI.DrawRectangle(spriteBatch, new Vector2(selectionPos.X, -selectionPos.Y), selectionSize, Color.DarkRed, false, 0, 2f / GameScreen.Selected.Cam.Zoom);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,20 +24,23 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!GameMain.SubEditorScreen.ShowThalamus && prefab.Category.HasFlag(MapEntityCategory.Thalamus))
|
||||
if (GameMain.SubEditorScreen.IsSubcategoryHidden(prefab.Subcategory))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return HasBody ? ShowWalls : ShowStructures;
|
||||
}
|
||||
}
|
||||
|
||||
private string specialTag;
|
||||
|
||||
#if DEBUG
|
||||
[Editable, Serialize("", true)]
|
||||
#else
|
||||
[Serialize("", true)]
|
||||
#endif
|
||||
public string SpecialTag
|
||||
{
|
||||
get { return specialTag; }
|
||||
set { specialTag = value; }
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
partial void InitProjSpecific()
|
||||
@@ -92,7 +95,10 @@ namespace Barotrauma
|
||||
{
|
||||
int heightScaled = (int)(20 * GUI.Scale);
|
||||
editingHUD = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.25f), GUI.Canvas, Anchor.CenterRight) { MinSize = new Point(400, 0) }) { UserData = this };
|
||||
GUIListBox listBox = new GUIListBox(new RectTransform(new Vector2(0.95f, 0.8f), editingHUD.RectTransform, Anchor.Center), style: null);
|
||||
GUIListBox listBox = new GUIListBox(new RectTransform(new Vector2(0.95f, 0.8f), editingHUD.RectTransform, Anchor.Center), style: null)
|
||||
{
|
||||
CanTakeKeyBoardFocus = false
|
||||
};
|
||||
var editor = new SerializableEntityEditor(listBox.Content.RectTransform, this, inGame, showName: true, titleFont: GUI.LargeFont) { UserData = this };
|
||||
|
||||
if (Submarine.MainSub?.Info?.Type == SubmarineType.OutpostModule)
|
||||
|
||||
@@ -79,14 +79,22 @@ namespace Barotrauma
|
||||
|
||||
if (maxDamageStructure != null)
|
||||
{
|
||||
SoundPlayer.PlayDamageSound(
|
||||
soundTag,
|
||||
impact * 10.0f,
|
||||
ConvertUnits.ToDisplayUnits(impactSimPos),
|
||||
MathHelper.Lerp(2000.0f, 10000.0f, (impact - MinCollisionImpact) / 2.0f),
|
||||
maxDamageStructure.Tags);
|
||||
PlayDamageSound(impactSimPos, impact, soundTag, maxDamageStructure);
|
||||
}
|
||||
}
|
||||
|
||||
private void PlayDamageSound(Vector2 impactSimPos, float impact, string soundTag, Structure hitStructure = null)
|
||||
{
|
||||
if (impact < MinCollisionImpact) { return; }
|
||||
|
||||
SoundPlayer.PlayDamageSound(
|
||||
soundTag,
|
||||
impact * 10.0f,
|
||||
ConvertUnits.ToDisplayUnits(impactSimPos),
|
||||
MathHelper.Lerp(2000.0f, 10000.0f, (impact - MinCollisionImpact) / 2.0f),
|
||||
hitStructure?.Tags);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -19,7 +20,7 @@ namespace Barotrauma
|
||||
{
|
||||
var texture = TextureLoader.FromStream(mem, path: FilePath, compress: false);
|
||||
if (texture == null) { throw new Exception("PreviewImage texture returned null"); }
|
||||
PreviewImage = new Sprite(texture, null, null);
|
||||
PreviewImage = new Sprite(texture, sourceRectangle: null, newOffset: null, path: FilePath);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -32,21 +33,49 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void CreatePreviewWindow(GUIComponent parent)
|
||||
{
|
||||
var content = new GUIFrame(new RectTransform(Vector2.One, parent.RectTransform), style: null);
|
||||
|
||||
if (PreviewImage == null)
|
||||
var previewButton = new GUIButton(new RectTransform(new Vector2(1f, 0.5f), content.RectTransform), style: null)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), content.RectTransform), TextManager.Get(SavedSubmarines.Contains(this) ? "SubPreviewImageNotFound" : "SubNotDownloaded"));
|
||||
CanBeFocused = SubmarineElement != null,
|
||||
OnClicked = (btn, obj) => { SubmarinePreview.Create(this); return false; },
|
||||
};
|
||||
|
||||
var previewImage = PreviewImage ?? savedSubmarines.Find(s => s.Name.Equals(Name, StringComparison.OrdinalIgnoreCase))?.PreviewImage;
|
||||
if (previewImage == null)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(Vector2.One, previewButton.RectTransform), TextManager.Get(SavedSubmarines.Contains(this) ? "SubPreviewImageNotFound" : "SubNotDownloaded"));
|
||||
}
|
||||
else
|
||||
{
|
||||
var submarinePreviewBackground = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.5f), content.RectTransform), style: null) { Color = Color.Black };
|
||||
new GUIImage(new RectTransform(new Vector2(0.98f), submarinePreviewBackground.RectTransform, Anchor.Center), PreviewImage, scaleToFit: true);
|
||||
new GUIFrame(new RectTransform(Vector2.One, submarinePreviewBackground.RectTransform), "InnerGlow", color: Color.Black);
|
||||
var submarinePreviewBackground = new GUIFrame(new RectTransform(Vector2.One, previewButton.RectTransform), style: null)
|
||||
{
|
||||
Color = Color.Black,
|
||||
HoverColor = Color.Black,
|
||||
SelectedColor = Color.Black,
|
||||
PressedColor = Color.Black,
|
||||
CanBeFocused = false,
|
||||
};
|
||||
new GUIImage(new RectTransform(new Vector2(0.98f), submarinePreviewBackground.RectTransform, Anchor.Center), previewImage, scaleToFit: true) { CanBeFocused = false };
|
||||
new GUIFrame(new RectTransform(Vector2.One, submarinePreviewBackground.RectTransform), "InnerGlow", color: Color.Black) { CanBeFocused = false };
|
||||
}
|
||||
|
||||
if (SubmarineElement != null)
|
||||
{
|
||||
new GUIFrame(new RectTransform(Vector2.One * 0.12f, previewButton.RectTransform, anchor: Anchor.BottomRight, pivot: Pivot.BottomRight, scaleBasis: ScaleBasis.BothHeight)
|
||||
{
|
||||
AbsoluteOffset = new Point((int)(0.03f * previewButton.Rect.Height))
|
||||
},
|
||||
"ExpandButton", Color.White)
|
||||
{
|
||||
Color = Color.White,
|
||||
HoverColor = Color.White,
|
||||
PressedColor = Color.White
|
||||
};
|
||||
}
|
||||
|
||||
var descriptionBox = new GUIListBox(new RectTransform(new Vector2(1, 0.5f), content.RectTransform, Anchor.BottomCenter))
|
||||
{
|
||||
UserData = "descriptionbox",
|
||||
@@ -56,39 +85,31 @@ namespace Barotrauma
|
||||
|
||||
ScalableFont font = parent.Rect.Width < 350 ? GUI.SmallFont : GUI.Font;
|
||||
|
||||
CreateSpecsWindow(descriptionBox, font);
|
||||
|
||||
//space
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), descriptionBox.Content.RectTransform), style: null);
|
||||
|
||||
if (!string.IsNullOrEmpty(Description))
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1, 0), descriptionBox.Content.RectTransform),
|
||||
TextManager.Get("SaveSubDialogDescription", fallBackTag: "WorkshopItemDescription"), font: GUI.Font, wrap: true)
|
||||
{ CanBeFocused = false, ForceUpperCase = true };
|
||||
}
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1, 0), descriptionBox.Content.RectTransform), Description, font: font, wrap: true)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
CreateSpecsWindow(descriptionBox, font, includeDescription: true);
|
||||
}
|
||||
|
||||
public void CreateSpecsWindow(GUIListBox parent, ScalableFont font)
|
||||
public void CreateSpecsWindow(GUIListBox parent, ScalableFont font, bool includeTitle = true, bool includeClass = true, bool includeDescription = false)
|
||||
{
|
||||
float leftPanelWidth = 0.6f;
|
||||
float rightPanelWidth = 0.4f / leftPanelWidth;
|
||||
float rightPanelWidth = 0.4f;
|
||||
string className = !HasTag(SubmarineTag.Shuttle) ? TextManager.Get($"submarineclass.{SubmarineClass}") : TextManager.Get("shuttle");
|
||||
|
||||
int nameHeight = (int)GUI.LargeFont.MeasureString(DisplayName, true).Y;
|
||||
int classHeight = (int)GUI.SubHeadingFont.MeasureString(className).Y;
|
||||
int leftPanelWidthInt = (int)(parent.Rect.Width * leftPanelWidth);
|
||||
|
||||
var submarineNameText = new GUITextBlock(new RectTransform(new Point(leftPanelWidthInt, nameHeight + HUDLayoutSettings.Padding / 2), parent.Content.RectTransform), DisplayName, textAlignment: Alignment.CenterLeft, font: GUI.LargeFont) { CanBeFocused = false };
|
||||
submarineNameText.RectTransform.MinSize = new Point(0, (int)submarineNameText.TextSize.Y);
|
||||
var submarineClassText = new GUITextBlock(new RectTransform(new Point(leftPanelWidthInt, classHeight), parent.Content.RectTransform), className, textAlignment: Alignment.CenterLeft, font: GUI.SubHeadingFont) { CanBeFocused = false };
|
||||
submarineClassText.RectTransform.MinSize = new Point(0, (int)submarineClassText.TextSize.Y);
|
||||
|
||||
GUITextBlock submarineNameText = null;
|
||||
GUITextBlock submarineClassText = null;
|
||||
if (includeTitle)
|
||||
{
|
||||
int nameHeight = (int)GUI.LargeFont.MeasureString(DisplayName, true).Y;
|
||||
submarineNameText = new GUITextBlock(new RectTransform(new Point(leftPanelWidthInt, nameHeight + HUDLayoutSettings.Padding / 2), parent.Content.RectTransform), DisplayName, textAlignment: Alignment.CenterLeft, font: GUI.LargeFont) { CanBeFocused = false };
|
||||
submarineNameText.RectTransform.MinSize = new Point(0, (int)submarineNameText.TextSize.Y);
|
||||
}
|
||||
if (includeClass)
|
||||
{
|
||||
submarineClassText = new GUITextBlock(new RectTransform(new Point(leftPanelWidthInt, classHeight), parent.Content.RectTransform), className, textAlignment: Alignment.CenterLeft, font: GUI.SubHeadingFont) { CanBeFocused = false };
|
||||
submarineClassText.RectTransform.MinSize = new Point(0, (int)submarineClassText.TextSize.Y);
|
||||
}
|
||||
Vector2 realWorldDimensions = Dimensions * Physics.DisplayToRealWorldRatio;
|
||||
if (realWorldDimensions != Vector2.Zero)
|
||||
{
|
||||
@@ -149,8 +170,30 @@ namespace Barotrauma
|
||||
versionText.RectTransform.MinSize = new Point(0, versionText.Children.First().Rect.Height);
|
||||
}
|
||||
|
||||
submarineNameText.AutoScaleHorizontal = true;
|
||||
GUITextBlock.AutoScaleAndNormalize(parent.Content.Children.Where(c => c is GUITextBlock && c != submarineNameText).Cast<GUITextBlock>());
|
||||
if (submarineNameText != null)
|
||||
{
|
||||
submarineNameText.AutoScaleHorizontal = true;
|
||||
}
|
||||
|
||||
GUITextBlock descBlock = null;
|
||||
if (includeDescription)
|
||||
{
|
||||
//space
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), parent.Content.RectTransform), style: null);
|
||||
|
||||
if (!string.IsNullOrEmpty(Description))
|
||||
{
|
||||
var wsItemDesc = new GUITextBlock(new RectTransform(new Vector2(1, 0), parent.Content.RectTransform),
|
||||
TextManager.Get("SaveSubDialogDescription", fallBackTag: "WorkshopItemDescription"), font: GUI.Font, wrap: true)
|
||||
{ CanBeFocused = false, ForceUpperCase = true };
|
||||
|
||||
descBlock = new GUITextBlock(new RectTransform(new Vector2(1, 0), parent.Content.RectTransform), Description, font: font, wrap: true)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
}
|
||||
}
|
||||
GUITextBlock.AutoScaleAndNormalize(parent.Content.GetAllChildren<GUITextBlock>().Where(c => c != submarineNameText && c != descBlock));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
648
Barotrauma/BarotraumaClient/ClientSource/Map/SubmarinePreview.cs
Normal file
648
Barotrauma/BarotraumaClient/ClientSource/Map/SubmarinePreview.cs
Normal file
@@ -0,0 +1,648 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.IO;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class SubmarinePreview : IDisposable
|
||||
{
|
||||
private SpriteRecorder spriteRecorder;
|
||||
private SubmarineInfo submarineInfo;
|
||||
private Camera camera;
|
||||
private Task loadTask;
|
||||
private volatile bool isDisposed;
|
||||
|
||||
private GUIFrame previewFrame;
|
||||
|
||||
private class HullCollection
|
||||
{
|
||||
public readonly List<Rectangle> Rects;
|
||||
public readonly string Name;
|
||||
|
||||
public HullCollection(string identifier)
|
||||
{
|
||||
Rects = new List<Rectangle>();
|
||||
Name = TextManager.Get(identifier, returnNull: true) ?? identifier;
|
||||
}
|
||||
|
||||
public void AddRect(XElement element)
|
||||
{
|
||||
Rectangle rect = element.GetAttributeRect("rect", Rectangle.Empty);
|
||||
rect.Y = -rect.Y;
|
||||
Rects.Add(rect);
|
||||
}
|
||||
}
|
||||
|
||||
private struct Door
|
||||
{
|
||||
public readonly Rectangle Rect;
|
||||
|
||||
public Door(Rectangle rect)
|
||||
{
|
||||
rect.Y = -rect.Y;
|
||||
Rect = rect;
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<string,HullCollection> hullCollections;
|
||||
private List<Door> doors;
|
||||
|
||||
|
||||
private static SubmarinePreview instance = null;
|
||||
|
||||
public static void Create(SubmarineInfo submarineInfo)
|
||||
{
|
||||
Close();
|
||||
instance = new SubmarinePreview(submarineInfo);
|
||||
}
|
||||
|
||||
public static void Close()
|
||||
{
|
||||
instance?.Dispose();
|
||||
}
|
||||
|
||||
private SubmarinePreview(SubmarineInfo subInfo)
|
||||
{
|
||||
camera = new Camera();
|
||||
submarineInfo = subInfo;
|
||||
spriteRecorder = new SpriteRecorder();
|
||||
isDisposed = false;
|
||||
loadTask = null;
|
||||
|
||||
hullCollections = new Dictionary<string, HullCollection>();
|
||||
doors = new List<Door>();
|
||||
|
||||
previewFrame = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), style: null);
|
||||
new GUIFrame(new RectTransform(GUI.Canvas.RelativeSize, previewFrame.RectTransform, Anchor.Center), style: "GUIBackgroundBlocker");
|
||||
|
||||
new GUIButton(new RectTransform(Vector2.One, previewFrame.RectTransform), "", style: null)
|
||||
{
|
||||
OnClicked = (btn, obj) => { Dispose(); return false; }
|
||||
};
|
||||
|
||||
var innerFrame = new GUIFrame(new RectTransform(Vector2.One * 0.9f, previewFrame.RectTransform, Anchor.Center));
|
||||
int innerPadding = GUI.IntScale(100f);
|
||||
var innerPadded = new GUIFrame(new RectTransform(new Point(innerFrame.Rect.Width - innerPadding, innerFrame.Rect.Height - innerPadding), previewFrame.RectTransform, Anchor.Center), style: null)
|
||||
{
|
||||
OutlineColor = Color.Black,
|
||||
OutlineThickness = 2
|
||||
};
|
||||
|
||||
GUITextBlock titleText = null;
|
||||
GUIListBox specsContainer = null;
|
||||
|
||||
new GUICustomComponent(new RectTransform(Vector2.One, innerPadded.RectTransform, Anchor.Center),
|
||||
(spriteBatch, component) => {
|
||||
camera.UpdateTransform(interpolate: true, updateListener: false);
|
||||
Rectangle drawRect = new Rectangle(component.Rect.X + 1, component.Rect.Y + 1, component.Rect.Width - 2, component.Rect.Height - 2);
|
||||
RenderSubmarine(spriteBatch, drawRect);
|
||||
},
|
||||
(deltaTime, component) => {
|
||||
bool isMouseOnComponent = GUI.MouseOn == component;
|
||||
camera.MoveCamera(deltaTime, allowZoom: isMouseOnComponent, followSub: false);
|
||||
if (isMouseOnComponent &&
|
||||
(PlayerInput.MidButtonHeld() || PlayerInput.LeftButtonHeld()))
|
||||
{
|
||||
Vector2 moveSpeed = PlayerInput.MouseSpeed * (float)deltaTime * 60.0f / camera.Zoom;
|
||||
moveSpeed.X = -moveSpeed.X;
|
||||
camera.Position += moveSpeed;
|
||||
}
|
||||
|
||||
if (titleText != null && specsContainer != null)
|
||||
{
|
||||
specsContainer.Visible = GUI.IsMouseOn(titleText);
|
||||
}
|
||||
});
|
||||
|
||||
var topContainer = new GUIFrame(new RectTransform(new Vector2(1f, 0.07f), innerPadded.RectTransform, Anchor.TopLeft), style: null)
|
||||
{
|
||||
Color = Color.Black * 0.65f
|
||||
};
|
||||
var topLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.97f, 5f / 7f), topContainer.RectTransform, Anchor.Center), isHorizontal: true, childAnchor: Anchor.CenterLeft);
|
||||
|
||||
titleText = new GUITextBlock(new RectTransform(new Vector2(0.95f, 1f), topLayout.RectTransform), subInfo.DisplayName, font: GUI.LargeFont);
|
||||
new GUIButton(new RectTransform(new Vector2(0.05f, 1f), topLayout.RectTransform), TextManager.Get("Close"))
|
||||
{
|
||||
OnClicked = (btn, obj) => { Dispose(); return false; }
|
||||
};
|
||||
|
||||
specsContainer = new GUIListBox(new RectTransform(new Vector2(0.4f, 1f), innerPadded.RectTransform, Anchor.TopLeft) { RelativeOffset = new Vector2(0.015f, 0.07f) })
|
||||
{
|
||||
Color = Color.Black * 0.65f,
|
||||
ScrollBarEnabled = false,
|
||||
ScrollBarVisible = false,
|
||||
Spacing = 5
|
||||
};
|
||||
subInfo.CreateSpecsWindow(specsContainer, GUI.Font, includeTitle: false, includeDescription: true);
|
||||
int width = specsContainer.Rect.Width;
|
||||
void recalculateSpecsContainerHeight()
|
||||
{
|
||||
int totalSize = 0;
|
||||
var children = specsContainer.Content.Children.Where(c => c.Visible);
|
||||
foreach (GUIComponent child in children)
|
||||
{
|
||||
totalSize += child.Rect.Height;
|
||||
}
|
||||
totalSize += specsContainer.Content.CountChildren * specsContainer.Spacing;
|
||||
if (specsContainer.PadBottom)
|
||||
{
|
||||
GUIComponent last = specsContainer.Content.Children.LastOrDefault();
|
||||
if (last != null)
|
||||
{
|
||||
totalSize += specsContainer.Rect.Height - last.Rect.Height;
|
||||
}
|
||||
}
|
||||
specsContainer.RectTransform.Resize(new Point(width, totalSize), true);
|
||||
specsContainer.RecalculateChildren();
|
||||
}
|
||||
//hell
|
||||
recalculateSpecsContainerHeight();
|
||||
specsContainer.Content.GetAllChildren<GUITextBlock>().ForEach(c =>
|
||||
{
|
||||
var firstChild = c.Children.FirstOrDefault() as GUITextBlock;
|
||||
if (firstChild != null)
|
||||
{
|
||||
firstChild.CalculateHeightFromText(); firstChild.SetTextPos();
|
||||
c.RectTransform.MinSize = new Point(0, firstChild.Rect.Height);
|
||||
}
|
||||
c.CalculateHeightFromText(); c.SetTextPos();
|
||||
});
|
||||
recalculateSpecsContainerHeight();
|
||||
|
||||
GeneratePreviewMeshes();
|
||||
}
|
||||
|
||||
public static void AddToGUIUpdateList()
|
||||
{
|
||||
instance?.previewFrame?.AddToGUIUpdateList();
|
||||
}
|
||||
|
||||
public Task GeneratePreviewMeshes()
|
||||
{
|
||||
if (loadTask != null) { throw new InvalidOperationException("Tried to start SubmarinePreview loadTask more than once!"); }
|
||||
loadTask = Task.Run(GeneratePreviewMeshesInternal);
|
||||
return loadTask;
|
||||
}
|
||||
|
||||
private async Task GeneratePreviewMeshesInternal()
|
||||
{
|
||||
await Task.Yield();
|
||||
spriteRecorder.Begin(SpriteSortMode.BackToFront);
|
||||
|
||||
HashSet<int> toIgnore = new HashSet<int>();
|
||||
|
||||
foreach (var subElement in submarineInfo.SubmarineElement.Elements())
|
||||
{
|
||||
switch (subElement.Name.LocalName.ToLowerInvariant())
|
||||
{
|
||||
case "item":
|
||||
foreach (var component in subElement.Elements())
|
||||
{
|
||||
switch (component.Name.LocalName.ToLowerInvariant())
|
||||
{
|
||||
case "itemcontainer":
|
||||
ExtractItemContainerIds(component, toIgnore);
|
||||
break;
|
||||
case "connectionpanel":
|
||||
ExtractConnectionPanelLinks(component, toIgnore);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (isDisposed) { return; }
|
||||
await Task.Yield();
|
||||
}
|
||||
|
||||
foreach (var subElement in submarineInfo.SubmarineElement.Elements())
|
||||
{
|
||||
switch (subElement.Name.LocalName.ToLowerInvariant())
|
||||
{
|
||||
case "item":
|
||||
if (!toIgnore.Contains(subElement.GetAttributeInt("ID", 0)))
|
||||
{
|
||||
BakeMapEntity(subElement);
|
||||
}
|
||||
break;
|
||||
case "structure":
|
||||
BakeMapEntity(subElement);
|
||||
break;
|
||||
case "hull":
|
||||
string identifier = subElement.GetAttributeString("roomname", "").ToLowerInvariant();
|
||||
if (!string.IsNullOrEmpty(identifier))
|
||||
{
|
||||
HullCollection hullCollection = null;
|
||||
if (!hullCollections.TryGetValue(identifier, out hullCollection))
|
||||
{
|
||||
hullCollection = new HullCollection(identifier);
|
||||
hullCollections.Add(identifier, hullCollection);
|
||||
}
|
||||
hullCollection.AddRect(subElement);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (isDisposed) { return; }
|
||||
await Task.Yield();
|
||||
}
|
||||
spriteRecorder.End();
|
||||
|
||||
camera.Position = (spriteRecorder.Min + spriteRecorder.Max) * 0.5f;
|
||||
float scaledSpan = (spriteRecorder.Max - spriteRecorder.Min).X / camera.Resolution.X;
|
||||
camera.Zoom = 0.8f / scaledSpan;
|
||||
camera.StopMovement();
|
||||
}
|
||||
|
||||
private void ExtractItemContainerIds(XElement component, HashSet<int> ids)
|
||||
{
|
||||
string containedString = component.GetAttributeString("contained", "");
|
||||
string[] itemIdStrings = containedString.Split(',');
|
||||
for (int i = 0; i < itemIdStrings.Length; i++)
|
||||
{
|
||||
foreach (string idStr in itemIdStrings[i].Split(';'))
|
||||
{
|
||||
if (!int.TryParse(idStr, NumberStyles.Any, CultureInfo.InvariantCulture, out int id)) { continue; }
|
||||
if (id != 0 && !ids.Contains(id)) { ids.Add(id); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ExtractConnectionPanelLinks(XElement component, HashSet<int> ids)
|
||||
{
|
||||
var pins = component.Elements("input").Concat(component.Elements("output"));
|
||||
foreach (var pin in pins)
|
||||
{
|
||||
var links = pin.Elements("link");
|
||||
foreach (var link in links)
|
||||
{
|
||||
int id = link.GetAttributeInt("w", 0);
|
||||
if (id != 0 && !ids.Contains(id)) { ids.Add(id); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void BakeMapEntity(XElement element)
|
||||
{
|
||||
string identifier = element.GetAttributeString("identifier", "");
|
||||
if (string.IsNullOrEmpty(identifier)) { return; }
|
||||
Rectangle rect = element.GetAttributeRect("rect", Rectangle.Empty);
|
||||
if (rect.Equals(Rectangle.Empty)) { return; }
|
||||
|
||||
float depth = element.GetAttributeFloat("spritedepth", 1f);
|
||||
bool flippedX = element.GetAttributeBool("flippedx", false);
|
||||
bool flippedY = element.GetAttributeBool("flippedy", false);
|
||||
|
||||
float scale = element.GetAttributeFloat("scale", 1f);
|
||||
Color color = element.GetAttributeColor("spritecolor", Color.White);
|
||||
|
||||
float rotation = element.GetAttributeFloat("rotation", 0f);
|
||||
|
||||
MapEntityPrefab prefab = MapEntityPrefab.List.FirstOrDefault(p => p.Identifier.Equals(identifier, StringComparison.OrdinalIgnoreCase));
|
||||
if (prefab == null) { return; }
|
||||
|
||||
var texture = prefab.sprite.Texture;
|
||||
var srcRect = prefab.sprite.SourceRect;
|
||||
|
||||
SpriteEffects spriteEffects = SpriteEffects.None;
|
||||
if (flippedX)
|
||||
{
|
||||
spriteEffects |= SpriteEffects.FlipHorizontally;
|
||||
}
|
||||
if (flippedY)
|
||||
{
|
||||
spriteEffects |= SpriteEffects.FlipVertically;
|
||||
}
|
||||
|
||||
var prevEffects = prefab.sprite.effects;
|
||||
prefab.sprite.effects ^= spriteEffects;
|
||||
|
||||
bool overrideSprite = false;
|
||||
ItemPrefab itemPrefab = prefab as ItemPrefab;
|
||||
StructurePrefab structurePrefab = prefab as StructurePrefab;
|
||||
if (itemPrefab != null)
|
||||
{
|
||||
BakeItemComponents(itemPrefab, rect, color, scale, rotation, depth, out overrideSprite);
|
||||
}
|
||||
|
||||
if (!overrideSprite)
|
||||
{
|
||||
if (structurePrefab != null)
|
||||
{
|
||||
ParseUpgrades(structurePrefab.ConfigElement, ref scale);
|
||||
|
||||
if (!prefab.ResizeVertical)
|
||||
{
|
||||
rect.Height = (int)(rect.Height * scale / prefab.Scale);
|
||||
}
|
||||
if (!prefab.ResizeHorizontal)
|
||||
{
|
||||
rect.Width = (int)(rect.Width * scale / prefab.Scale);
|
||||
}
|
||||
var textureScale = element.GetAttributeVector2("texturescale", Vector2.One);
|
||||
|
||||
Vector2 backGroundOffset = Vector2.Zero;
|
||||
|
||||
Vector2 textureOffset = element.GetAttributeVector2("textureoffset", Vector2.Zero);
|
||||
if (flippedX) { textureOffset.X = -textureOffset.X; }
|
||||
if (flippedY) { textureOffset.Y = -textureOffset.Y; }
|
||||
|
||||
backGroundOffset = new Vector2(
|
||||
MathUtils.PositiveModulo((int)-textureOffset.X, prefab.sprite.SourceRect.Width),
|
||||
MathUtils.PositiveModulo((int)-textureOffset.Y, prefab.sprite.SourceRect.Height));
|
||||
|
||||
prefab.sprite.DrawTiled(
|
||||
spriteRecorder,
|
||||
rect.Location.ToVector2() * new Vector2(1f, -1f),
|
||||
rect.Size.ToVector2(),
|
||||
color: color,
|
||||
startOffset: backGroundOffset,
|
||||
textureScale: textureScale * scale,
|
||||
depth: depth);
|
||||
}
|
||||
else if (itemPrefab != null)
|
||||
{
|
||||
bool usePrefabValues = element.GetAttributeBool("isoverride", false) != itemPrefab.IsOverride;
|
||||
if (usePrefabValues)
|
||||
{
|
||||
scale = itemPrefab.ConfigElement.GetAttributeFloat(scale, "scale", "Scale");
|
||||
}
|
||||
|
||||
ParseUpgrades(itemPrefab.ConfigElement, ref scale);
|
||||
|
||||
if (prefab.ResizeVertical || prefab.ResizeHorizontal)
|
||||
{
|
||||
if (!prefab.ResizeHorizontal)
|
||||
{
|
||||
rect.Width = (int)(prefab.sprite.size.X * scale);
|
||||
}
|
||||
if (!prefab.ResizeVertical)
|
||||
{
|
||||
rect.Height = (int)(prefab.sprite.size.Y * scale);
|
||||
}
|
||||
|
||||
var spritePos = rect.Center.ToVector2();
|
||||
//spritePos.Y = rect.Height - spritePos.Y;
|
||||
|
||||
prefab.sprite.DrawTiled(
|
||||
spriteRecorder,
|
||||
rect.Location.ToVector2() * new Vector2(1f, -1f),
|
||||
rect.Size.ToVector2(),
|
||||
color: color,
|
||||
textureScale: Vector2.One * scale,
|
||||
depth: depth);
|
||||
|
||||
foreach (var decorativeSprite in itemPrefab.DecorativeSprites)
|
||||
{
|
||||
float offsetState = 0f;
|
||||
Vector2 offset = decorativeSprite.GetOffset(ref offsetState, Vector2.Zero) * scale;
|
||||
if (flippedX && itemPrefab.CanSpriteFlipX) { offset.X = -offset.X; }
|
||||
if (flippedY && itemPrefab.CanSpriteFlipY) { offset.Y = -offset.Y; }
|
||||
decorativeSprite.Sprite.DrawTiled(spriteRecorder,
|
||||
new Vector2(spritePos.X + offset.X - rect.Width / 2, -(spritePos.Y + offset.Y + rect.Height / 2)),
|
||||
rect.Size.ToVector2(), color: color,
|
||||
textureScale: Vector2.One * scale,
|
||||
depth: Math.Min(depth + (decorativeSprite.Sprite.Depth - prefab.sprite.Depth), 0.999f));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rect.Width = (int)(rect.Width * scale / prefab.Scale);
|
||||
rect.Height = (int)(rect.Height * scale / prefab.Scale);
|
||||
|
||||
var spritePos = rect.Center.ToVector2();
|
||||
spritePos.Y -= rect.Height;
|
||||
//spritePos.Y = rect.Height - spritePos.Y;
|
||||
|
||||
prefab.sprite.Draw(
|
||||
spriteRecorder,
|
||||
spritePos * new Vector2(1f, -1f),
|
||||
color,
|
||||
prefab.sprite.Origin,
|
||||
rotation,
|
||||
scale,
|
||||
prefab.sprite.effects, depth);
|
||||
|
||||
foreach (var decorativeSprite in itemPrefab.DecorativeSprites)
|
||||
{
|
||||
float rotationState = 0f; float offsetState = 0f;
|
||||
float rot = decorativeSprite.GetRotation(ref rotationState, 0f);
|
||||
Vector2 offset = decorativeSprite.GetOffset(ref offsetState, Vector2.Zero) * scale;
|
||||
if (flippedX && itemPrefab.CanSpriteFlipX) { offset.X = -offset.X; }
|
||||
if (flippedY && itemPrefab.CanSpriteFlipY) { offset.Y = -offset.Y; }
|
||||
decorativeSprite.Sprite.Draw(spriteRecorder, new Vector2(spritePos.X + offset.X, -(spritePos.Y + offset.Y)), color,
|
||||
MathHelper.ToRadians(rotation) + rot, decorativeSprite.GetScale(0f) * scale, prefab.sprite.effects,
|
||||
depth: Math.Min(depth + (decorativeSprite.Sprite.Depth - prefab.sprite.Depth), 0.999f));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prefab.sprite.effects = prevEffects;
|
||||
}
|
||||
|
||||
private void BakeItemComponents(
|
||||
ItemPrefab prefab,
|
||||
Rectangle rect, Color color,
|
||||
float scale, float rotation, float depth,
|
||||
out bool overrideSprite)
|
||||
{
|
||||
overrideSprite = false;
|
||||
|
||||
foreach (var subElement in prefab.ConfigElement.Elements())
|
||||
{
|
||||
switch (subElement.Name.LocalName.ToLowerInvariant())
|
||||
{
|
||||
case "turret":
|
||||
Sprite barrelSprite = null;
|
||||
Sprite railSprite = null;
|
||||
foreach (XElement turretSubElem in subElement.Elements())
|
||||
{
|
||||
switch (turretSubElem.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "barrelsprite":
|
||||
barrelSprite = new Sprite(turretSubElem);
|
||||
break;
|
||||
case "railsprite":
|
||||
railSprite = new Sprite(turretSubElem);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var transformedBarrelPos = MathUtils.RotatePointAroundTarget(
|
||||
subElement.GetAttributeVector2("barrelpos", Vector2.Zero) * scale,
|
||||
new Vector2(rect.Width / 2, rect.Height / 2),
|
||||
MathHelper.ToRadians(rotation));
|
||||
|
||||
Vector2 drawPos = new Vector2(rect.X + transformedBarrelPos.X, rect.Y - transformedBarrelPos.Y);
|
||||
drawPos.Y = -drawPos.Y;
|
||||
|
||||
railSprite?.Draw(spriteRecorder,
|
||||
drawPos,
|
||||
color,
|
||||
rotation + MathHelper.PiOver2, scale,
|
||||
SpriteEffects.None, depth + (railSprite.Depth - prefab.sprite.Depth));
|
||||
|
||||
barrelSprite?.Draw(spriteRecorder,
|
||||
drawPos - new Vector2((float)Math.Cos(MathHelper.ToRadians(rotation)), (float)Math.Sin(MathHelper.ToRadians(rotation))) * scale,
|
||||
color,
|
||||
rotation + MathHelper.PiOver2, scale,
|
||||
SpriteEffects.None, depth + (barrelSprite.Depth - prefab.sprite.Depth));
|
||||
|
||||
break;
|
||||
case "door":
|
||||
doors.Add(new Door(rect));
|
||||
|
||||
var doorSpriteElem = subElement.Elements().FirstOrDefault(e => e.Name.LocalName.Equals("sprite", StringComparison.OrdinalIgnoreCase));
|
||||
if (doorSpriteElem != null)
|
||||
{
|
||||
string texturePath = doorSpriteElem.GetAttributeString("texture", "");
|
||||
Vector2 pos = rect.Location.ToVector2() * new Vector2(1f, -1f);
|
||||
if (subElement.GetAttributeBool("horizontal", false))
|
||||
{
|
||||
pos.Y += (float)rect.Height * 0.5f;
|
||||
}
|
||||
else
|
||||
{
|
||||
pos.X += (float)rect.Width * 0.5f;
|
||||
}
|
||||
Sprite doorSprite = new Sprite(doorSpriteElem, texturePath.Contains("/") ? "" : Path.GetDirectoryName(prefab.FilePath));
|
||||
spriteRecorder.Draw(doorSprite.Texture, pos,
|
||||
new Rectangle((int)doorSprite.SourceRect.X,
|
||||
(int)doorSprite.SourceRect.Y,
|
||||
(int)doorSprite.size.X, (int)doorSprite.size.Y),
|
||||
color, 0.0f, doorSprite.Origin, new Vector2(scale), SpriteEffects.None, doorSprite.Depth);
|
||||
}
|
||||
break;
|
||||
case "ladder":
|
||||
var backgroundSprElem = subElement.Elements().FirstOrDefault(e => e.Name.LocalName.Equals("backgroundsprite", StringComparison.OrdinalIgnoreCase));
|
||||
if (backgroundSprElem != null)
|
||||
{
|
||||
Sprite backgroundSprite = new Sprite(backgroundSprElem);
|
||||
backgroundSprite.DrawTiled(spriteRecorder,
|
||||
new Vector2(rect.Left, -rect.Top) - backgroundSprite.Origin * scale,
|
||||
new Vector2(backgroundSprite.size.X * scale, rect.Height), color: color,
|
||||
textureScale: Vector2.One * scale,
|
||||
depth: depth + 0.1f);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ParseUpgrades(XElement prefabConfigElement, ref float scale)
|
||||
{
|
||||
foreach (var upgrade in prefabConfigElement.Elements("Upgrade"))
|
||||
{
|
||||
var upgradeVersion = new Version(upgrade.GetAttributeString("gameversion", "0.0.0.0"));
|
||||
if (upgradeVersion >= submarineInfo.GameVersion)
|
||||
{
|
||||
string scaleModifier = upgrade.GetAttributeString("scale", "*1");
|
||||
|
||||
if (scaleModifier.StartsWith("*"))
|
||||
{
|
||||
if (float.TryParse(scaleModifier.Substring(1), NumberStyles.Any, CultureInfo.InvariantCulture, out float parsedScale))
|
||||
{
|
||||
scale *= parsedScale;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (float.TryParse(scaleModifier, NumberStyles.Any, CultureInfo.InvariantCulture, out float parsedScale))
|
||||
{
|
||||
scale = parsedScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderSubmarine(SpriteBatch spriteBatch, Rectangle scissorRectangle)
|
||||
{
|
||||
if (spriteRecorder == null) { return; }
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, scissorRectangle, new Color(0.051f, 0.149f, 0.271f, 1.0f), isFilled: true);
|
||||
|
||||
if (!spriteRecorder.ReadyToRender)
|
||||
{
|
||||
string waitText = !loadTask.IsCompleted ?
|
||||
"Generating preview..." :
|
||||
(loadTask.Exception?.ToString() ?? "Task completed without marking as ready to render");
|
||||
Vector2 origin = (GUI.Font.MeasureString(waitText) * 0.5f);
|
||||
origin.X = MathF.Round(origin.X);
|
||||
origin.Y = MathF.Round(origin.Y);
|
||||
GUI.Font.DrawString(
|
||||
spriteBatch,
|
||||
waitText,
|
||||
scissorRectangle.Center.ToVector2(),
|
||||
Color.White,
|
||||
0f,
|
||||
origin,
|
||||
1f,
|
||||
SpriteEffects.None,
|
||||
0f);
|
||||
return;
|
||||
}
|
||||
spriteBatch.End();
|
||||
|
||||
var prevScissorRect = GameMain.Instance.GraphicsDevice.ScissorRectangle;
|
||||
GameMain.Instance.GraphicsDevice.ScissorRectangle = scissorRectangle;
|
||||
|
||||
spriteRecorder.Render(camera);
|
||||
|
||||
var mousePos = camera.ScreenToWorld(PlayerInput.MousePosition);
|
||||
mousePos.Y = -mousePos.Y;
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.BackToFront, rasterizerState: GameMain.ScissorTestEnable, transformMatrix: camera.Transform);
|
||||
GameMain.Instance.GraphicsDevice.ScissorRectangle = scissorRectangle;
|
||||
foreach (var hullCollection in hullCollections.Values)
|
||||
{
|
||||
bool mouseOver = false;
|
||||
|
||||
foreach (var rect in hullCollection.Rects)
|
||||
{
|
||||
mouseOver = rect.Contains(mousePos);
|
||||
if (mouseOver) { break; }
|
||||
}
|
||||
|
||||
foreach (var rect in hullCollection.Rects)
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch, rect, mouseOver ? Color.Red : Color.Blue, depth: mouseOver ? 0.45f : 0.5f, thickness: (mouseOver ? 4f : 2f) / camera.Zoom);
|
||||
}
|
||||
|
||||
if (mouseOver)
|
||||
{
|
||||
string str = hullCollection.Name;
|
||||
Vector2 strSize = GUI.Font.MeasureString(str) / camera.Zoom;
|
||||
Vector2 padding = new Vector2(30, 30) / camera.Zoom;
|
||||
Vector2 shift = new Vector2(10, 0) / camera.Zoom;
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, mousePos + shift, strSize + padding, Color.Black, isFilled: true, depth: 0.25f);
|
||||
GUI.Font.DrawString(spriteBatch, str, mousePos + shift + (strSize + padding) * 0.5f, Color.White, 0f, strSize * camera.Zoom * 0.5f, 1f / camera.Zoom, SpriteEffects.None, 0f);
|
||||
}
|
||||
}
|
||||
foreach (var door in doors)
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch, door.Rect, GUI.Style.Green * 0.5f, isFilled: true, depth: 0.4f);
|
||||
}
|
||||
spriteBatch.End();
|
||||
|
||||
GameMain.Instance.GraphicsDevice.ScissorRectangle = prevScissorRect;
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
previewFrame = null;
|
||||
spriteRecorder?.Dispose();
|
||||
isDisposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -222,18 +222,20 @@ namespace Barotrauma
|
||||
private bool ChangeSpawnType(GUIButton button, object obj)
|
||||
{
|
||||
GUITextBlock spawnTypeText = button.Parent.GetChildByUserData("spawntypetext") as GUITextBlock;
|
||||
spawnType += (int)button.UserData;
|
||||
var values = Enum.GetValues(typeof(SpawnType));
|
||||
var values = (SpawnType[])Enum.GetValues(typeof(SpawnType));
|
||||
int currIndex = values.IndexOf(spawnType);
|
||||
currIndex += (int)button.UserData;
|
||||
int firstIndex = 1;
|
||||
int lastIndex = values.Length - 1;
|
||||
if ((int)spawnType > lastIndex)
|
||||
if (currIndex > lastIndex)
|
||||
{
|
||||
spawnType = (SpawnType)firstIndex;
|
||||
currIndex = firstIndex;
|
||||
}
|
||||
if ((int)spawnType < firstIndex)
|
||||
if (currIndex < firstIndex)
|
||||
{
|
||||
spawnType = (SpawnType)values.GetValue(lastIndex);
|
||||
currIndex = lastIndex;
|
||||
}
|
||||
spawnType = values[currIndex];
|
||||
spawnTypeText.Text = spawnType.ToString();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3,12 +3,13 @@ using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
partial class BannedPlayer
|
||||
{
|
||||
public BannedPlayer(string name, UInt16 uniqueIdentifier, bool isRangeBan, string endPoint, ulong steamID)
|
||||
public BannedPlayer(string name, UInt16 uniqueIdentifier, bool isRangeBan, string endPoint, ulong steamID, string reason, DateTime? expiration)
|
||||
{
|
||||
this.Name = name;
|
||||
this.EndPoint = endPoint;
|
||||
@@ -16,6 +17,8 @@ namespace Barotrauma.Networking
|
||||
ParseEndPointAsSteamId();
|
||||
this.IsRangeBan = isRangeBan;
|
||||
this.UniqueIdentifier = uniqueIdentifier;
|
||||
this.Reason = reason;
|
||||
this.ExpirationTime = expiration;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,12 +155,24 @@ namespace Barotrauma.Networking
|
||||
|
||||
bannedPlayers.Clear();
|
||||
UInt32 bannedPlayerCount = incMsg.ReadVariableUInt32();
|
||||
|
||||
for (int i = 0; i < (int)bannedPlayerCount; i++)
|
||||
{
|
||||
string name = incMsg.ReadString();
|
||||
UInt16 uniqueIdentifier = incMsg.ReadUInt16();
|
||||
bool isRangeBan = incMsg.ReadBoolean(); incMsg.ReadPadBits();
|
||||
|
||||
bool isRangeBan = incMsg.ReadBoolean();
|
||||
bool includesExpiration = incMsg.ReadBoolean();
|
||||
incMsg.ReadPadBits();
|
||||
|
||||
DateTime? expiration = null;
|
||||
if (includesExpiration)
|
||||
{
|
||||
double hoursFromNow = incMsg.ReadDouble();
|
||||
expiration = DateTime.Now + TimeSpan.FromHours(hoursFromNow);
|
||||
}
|
||||
|
||||
string reason = incMsg.ReadString();
|
||||
|
||||
string endPoint = "";
|
||||
UInt64 steamID = 0;
|
||||
if (isOwner)
|
||||
@@ -170,7 +185,7 @@ namespace Barotrauma.Networking
|
||||
endPoint = "Endpoint concealed by host";
|
||||
steamID = 0;
|
||||
}
|
||||
bannedPlayers.Add(new BannedPlayer(name, uniqueIdentifier, isRangeBan, endPoint, steamID));
|
||||
bannedPlayers.Add(new BannedPlayer(name, uniqueIdentifier, isRangeBan, endPoint, steamID, reason, expiration));
|
||||
}
|
||||
|
||||
if (banFrame != null)
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
public static void ClientRead(IReadMessage msg)
|
||||
{
|
||||
UInt16 ID = msg.ReadUInt16();
|
||||
UInt16 id = msg.ReadUInt16();
|
||||
ChatMessageType type = (ChatMessageType)msg.ReadByte();
|
||||
PlayerConnectionChangeType changeType = PlayerConnectionChangeType.None;
|
||||
string txt = "";
|
||||
@@ -29,6 +29,14 @@ namespace Barotrauma.Networking
|
||||
|
||||
string senderName = msg.ReadString();
|
||||
Character senderCharacter = null;
|
||||
Client senderClient = null;
|
||||
bool hasSenderClient = msg.ReadBoolean();
|
||||
if (hasSenderClient)
|
||||
{
|
||||
UInt64 clientId = msg.ReadUInt64();
|
||||
senderClient = GameMain.Client.ConnectedClients.Find(c => c.SteamID == clientId || c.ID == clientId);
|
||||
if (senderClient != null) { senderName = senderClient.Name; }
|
||||
}
|
||||
bool hasSenderCharacter = msg.ReadBoolean();
|
||||
if (hasSenderCharacter)
|
||||
{
|
||||
@@ -38,6 +46,7 @@ namespace Barotrauma.Networking
|
||||
senderName = senderCharacter.Name;
|
||||
}
|
||||
}
|
||||
msg.ReadPadBits();
|
||||
|
||||
switch (type)
|
||||
{
|
||||
@@ -48,7 +57,54 @@ namespace Barotrauma.Networking
|
||||
UInt16 targetCharacterID = msg.ReadUInt16();
|
||||
Character targetCharacter = Entity.FindEntityByID(targetCharacterID) as Character;
|
||||
Entity targetEntity = Entity.FindEntityByID(msg.ReadUInt16());
|
||||
int optionIndex = msg.ReadByte();
|
||||
|
||||
Order orderPrefab = null;
|
||||
int? optionIndex = null;
|
||||
string orderOption = null;
|
||||
|
||||
// The option of a Dismiss order is written differently so we know what order we target
|
||||
// now that the game supports multiple current orders simultaneously
|
||||
if (orderIndex >= 0 && orderIndex < Order.PrefabList.Count)
|
||||
{
|
||||
orderPrefab = Order.PrefabList[orderIndex];
|
||||
if (orderPrefab.Identifier != "dismissed")
|
||||
{
|
||||
optionIndex = msg.ReadByte();
|
||||
}
|
||||
// Does the dismiss order have a specified target?
|
||||
else if (msg.ReadBoolean())
|
||||
{
|
||||
int identifierCount = msg.ReadByte();
|
||||
if (identifierCount > 0)
|
||||
{
|
||||
int dismissedOrderIndex = msg.ReadByte();
|
||||
Order dismissedOrderPrefab = null;
|
||||
if (dismissedOrderIndex >= 0 && dismissedOrderIndex < Order.PrefabList.Count)
|
||||
{
|
||||
dismissedOrderPrefab = Order.PrefabList[dismissedOrderIndex];
|
||||
orderOption = dismissedOrderPrefab.Identifier;
|
||||
}
|
||||
if (identifierCount > 1)
|
||||
{
|
||||
int dismissedOrderOptionIndex = msg.ReadByte();
|
||||
if (dismissedOrderPrefab != null)
|
||||
{
|
||||
var options = dismissedOrderPrefab.Options;
|
||||
if (options != null && dismissedOrderOptionIndex >= 0 && dismissedOrderOptionIndex < options.Length)
|
||||
{
|
||||
orderOption += $".{options[dismissedOrderOptionIndex]}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
optionIndex = msg.ReadByte();
|
||||
}
|
||||
|
||||
int orderPriority = msg.ReadByte();
|
||||
OrderTarget orderTargetPosition = null;
|
||||
Order.OrderTargetType orderTargetType = (Order.OrderTargetType)msg.ReadByte();
|
||||
int wallSectionIndex = 0;
|
||||
@@ -64,22 +120,18 @@ namespace Barotrauma.Networking
|
||||
wallSectionIndex = msg.ReadByte();
|
||||
}
|
||||
|
||||
Order orderPrefab;
|
||||
if (orderIndex < 0 || orderIndex >= Order.PrefabList.Count)
|
||||
{
|
||||
DebugConsole.ThrowError("Invalid order message - order index out of bounds.");
|
||||
if (NetIdUtils.IdMoreRecent(ID, LastID)) { LastID = ID; }
|
||||
if (NetIdUtils.IdMoreRecent(id, LastID)) { LastID = id; }
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
orderPrefab = Order.PrefabList[orderIndex];
|
||||
}
|
||||
string orderOption = "";
|
||||
if (optionIndex >= 0 && optionIndex < orderPrefab.Options.Length)
|
||||
{
|
||||
orderOption = orderPrefab.Options[optionIndex];
|
||||
orderPrefab ??= Order.PrefabList[orderIndex];
|
||||
}
|
||||
|
||||
orderOption ??= optionIndex.HasValue && optionIndex >= 0 && optionIndex < orderPrefab.Options.Length ? orderPrefab.Options[optionIndex.Value] : "";
|
||||
txt = orderPrefab.GetChatMessage(targetCharacter?.Name, senderCharacter?.CurrentHull?.DisplayName, givingOrderToSelf: targetCharacter == senderCharacter, orderOption: orderOption);
|
||||
|
||||
if (GameMain.Client.GameStarted && Screen.Selected == GameMain.GameScreen)
|
||||
@@ -107,16 +159,16 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
else if (targetCharacter != null)
|
||||
{
|
||||
targetCharacter.SetOrder(order, orderOption, senderCharacter);
|
||||
targetCharacter.SetOrder(order, orderOption, orderPriority, senderCharacter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (NetIdUtils.IdMoreRecent(ID, LastID))
|
||||
if (NetIdUtils.IdMoreRecent(id, LastID))
|
||||
{
|
||||
GameMain.Client.AddChatMessage(
|
||||
new OrderChatMessage(orderPrefab, orderOption, txt, orderTargetPosition ?? targetEntity as ISpatialEntity, targetCharacter, senderCharacter));
|
||||
LastID = ID;
|
||||
new OrderChatMessage(orderPrefab, orderOption, orderPriority, txt, orderTargetPosition ?? targetEntity as ISpatialEntity, targetCharacter, senderCharacter));
|
||||
LastID = id;
|
||||
}
|
||||
return;
|
||||
case ChatMessageType.ServerMessageBox:
|
||||
@@ -128,7 +180,7 @@ namespace Barotrauma.Networking
|
||||
break;
|
||||
}
|
||||
|
||||
if (NetIdUtils.IdMoreRecent(ID, LastID))
|
||||
if (NetIdUtils.IdMoreRecent(id, LastID))
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
@@ -154,10 +206,10 @@ namespace Barotrauma.Networking
|
||||
GameMain.Client.ServerSettings.ServerLog?.WriteLine(txt, messageType);
|
||||
break;
|
||||
default:
|
||||
GameMain.Client.AddChatMessage(txt, type, senderName, senderCharacter, changeType);
|
||||
GameMain.Client.AddChatMessage(txt, type, senderName, senderClient, senderCharacter, changeType);
|
||||
break;
|
||||
}
|
||||
LastID = ID;
|
||||
LastID = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ namespace Barotrauma.Networking
|
||||
return;
|
||||
}
|
||||
Permissions = permissions;
|
||||
PermittedConsoleCommands = new List<DebugConsole.Command>(permittedConsoleCommands);
|
||||
PermittedConsoleCommands.Clear(); PermittedConsoleCommands.AddRange(permittedConsoleCommands);
|
||||
}
|
||||
|
||||
public void GivePermission(ClientPermissions permission)
|
||||
|
||||
@@ -56,6 +56,11 @@ namespace Barotrauma.Networking
|
||||
public readonly NetStats NetStats;
|
||||
|
||||
protected GUITickBox cameraFollowsSub;
|
||||
public GUITickBox FollowSubTickBox => cameraFollowsSub;
|
||||
|
||||
public bool IsFollowSubTickBoxVisible =>
|
||||
gameStarted && Screen.Selected == GameMain.GameScreen &&
|
||||
cameraFollowsSub != null && cameraFollowsSub.Visible;
|
||||
|
||||
public CameraTransition EndCinematic;
|
||||
|
||||
@@ -159,6 +164,12 @@ namespace Barotrauma.Networking
|
||||
get { return entityEventManager; }
|
||||
}
|
||||
|
||||
public bool? WaitForNextRoundRespawn
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
private readonly object serverEndpoint;
|
||||
private readonly int ownerKey;
|
||||
private readonly bool steamP2POwner;
|
||||
@@ -185,10 +196,10 @@ namespace Barotrauma.Networking
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
cameraFollowsSub = new GUITickBox(new RectTransform(new Vector2(0.05f, 0.05f), inGameHUD.RectTransform, anchor: Anchor.TopCenter)
|
||||
cameraFollowsSub = new GUITickBox(new RectTransform(new Vector2(0.05f, 0.05f), inGameHUD.RectTransform, anchor: Anchor.TopCenter, pivot: Pivot.CenterLeft)
|
||||
{
|
||||
AbsoluteOffset = new Point(0, 5),
|
||||
MaxSize = new Point(25, 25)
|
||||
AbsoluteOffset = new Point(0, HUDLayoutSettings.ButtonAreaTop.Y + HUDLayoutSettings.ButtonAreaTop.Height / 2),
|
||||
MaxSize = new Point(GUI.IntScale(25))
|
||||
}, TextManager.Get("CamFollowSubmarine"))
|
||||
{
|
||||
Selected = Camera.FollowSub,
|
||||
@@ -857,12 +868,25 @@ namespace Barotrauma.Networking
|
||||
string endMessage = string.Empty;
|
||||
|
||||
endMessage = inc.ReadString();
|
||||
bool missionSuccessful = inc.ReadBoolean();
|
||||
byte missionCount = inc.ReadByte();
|
||||
for (int i = 0; i < missionCount; i++)
|
||||
{
|
||||
bool missionSuccessful = inc.ReadBoolean();
|
||||
var mission = GameMain.GameSession?.GetMission(i);
|
||||
if (mission != null)
|
||||
{
|
||||
mission.Completed = missionSuccessful;
|
||||
}
|
||||
}
|
||||
CharacterTeamType winningTeam = (CharacterTeamType)inc.ReadByte();
|
||||
if (missionSuccessful && GameMain.GameSession?.Mission != null)
|
||||
if (winningTeam != CharacterTeamType.None)
|
||||
{
|
||||
GameMain.GameSession.WinningTeam = winningTeam;
|
||||
GameMain.GameSession.Mission.Completed = true;
|
||||
var combatMission = GameMain.GameSession.Missions.FirstOrDefault(m => m is CombatMission);
|
||||
if (combatMission != null)
|
||||
{
|
||||
combatMission.Completed = true;
|
||||
}
|
||||
}
|
||||
|
||||
byte traitorCount = inc.ReadByte();
|
||||
@@ -928,7 +952,11 @@ namespace Barotrauma.Networking
|
||||
ReadTraitorMessage(inc);
|
||||
break;
|
||||
case ServerPacketHeader.MISSION:
|
||||
GameMain.GameSession?.Mission?.ClientRead(inc);
|
||||
{
|
||||
int missionIndex = inc.ReadByte();
|
||||
Mission mission = GameMain.GameSession?.GetMission(missionIndex);
|
||||
mission?.ClientRead(inc);
|
||||
}
|
||||
break;
|
||||
case ServerPacketHeader.EVENTACTION:
|
||||
GameMain.GameSession?.EventManager.ClientRead(inc);
|
||||
@@ -959,17 +987,26 @@ namespace Barotrauma.Networking
|
||||
throw new Exception(errorMsg);
|
||||
}
|
||||
|
||||
string missionIdentifier = inc.ReadString() ?? "";
|
||||
if (missionIdentifier != (GameMain.GameSession.Mission?.Prefab.Identifier ?? ""))
|
||||
byte missionCount = inc.ReadByte();
|
||||
if (missionCount != GameMain.GameSession.Missions.Count())
|
||||
{
|
||||
string errorMsg = $"Mission equality check failed. The mission selected at your end doesn't match the one loaded by the server (server: {missionIdentifier ?? "null"}, client: {GameMain.GameSession.Mission?.Prefab.Identifier ?? ""})";
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:MissionsDontMatch" + Level.Loaded.Seed, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
string errorMsg = $"Mission equality check failed. Mission count doesn't match the server (server: {missionCount}, client: {GameMain.GameSession.Missions.Count()})";
|
||||
throw new Exception(errorMsg);
|
||||
}
|
||||
foreach (Mission mission in GameMain.GameSession.Missions)
|
||||
{
|
||||
string missionIdentifier = inc.ReadString() ?? "";
|
||||
if (missionIdentifier != mission.Prefab.Identifier)
|
||||
{
|
||||
string errorMsg = $"Mission equality check failed. The mission selected at your end doesn't match the one loaded by the server (server: {missionIdentifier ?? "null"}, client: {mission.Prefab.Identifier})";
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:MissionsDontMatch" + Level.Loaded.Seed, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
throw new Exception(errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
byte equalityCheckValueCount = inc.ReadByte();
|
||||
List<int> levelEqualityCheckValues = new List<int>();
|
||||
for (int i = 0; i<equalityCheckValueCount; i++)
|
||||
for (int i = 0; i < equalityCheckValueCount; i++)
|
||||
{
|
||||
levelEqualityCheckValues.Add(inc.ReadInt32());
|
||||
}
|
||||
@@ -1004,7 +1041,10 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
GameMain.GameSession.Mission?.ClientReadInitial(inc);
|
||||
foreach (Mission mission in GameMain.GameSession.Missions)
|
||||
{
|
||||
mission.ClientReadInitial(inc);
|
||||
}
|
||||
|
||||
roundInitStatus = RoundInitStatus.Started;
|
||||
}
|
||||
@@ -1395,6 +1435,8 @@ namespace Barotrauma.Networking
|
||||
|
||||
EndVoteTickBox.Selected = false;
|
||||
|
||||
WaitForNextRoundRespawn = null;
|
||||
|
||||
roundInitStatus = RoundInitStatus.Starting;
|
||||
|
||||
int seed = inc.ReadInt32();
|
||||
@@ -1411,6 +1453,7 @@ namespace Barotrauma.Networking
|
||||
bool respawnAllowed = inc.ReadBoolean();
|
||||
serverSettings.AllowDisguises = inc.ReadBoolean();
|
||||
serverSettings.AllowRewiring = inc.ReadBoolean();
|
||||
serverSettings.LockAllDefaultWires = inc.ReadBoolean();
|
||||
serverSettings.AllowRagdollButton = inc.ReadBoolean();
|
||||
GameMain.NetLobbyScreen.UsingShuttle = inc.ReadBoolean();
|
||||
GameMain.LightManager.LosMode = (LosMode)inc.ReadByte();
|
||||
@@ -1432,7 +1475,12 @@ namespace Barotrauma.Networking
|
||||
string subHash = inc.ReadString();
|
||||
string shuttleName = inc.ReadString();
|
||||
string shuttleHash = inc.ReadString();
|
||||
int missionIndex = inc.ReadInt16();
|
||||
List<int> missionIndices = new List<int>();
|
||||
int missionCount = inc.ReadByte();
|
||||
for (int i = 0; i < missionCount; i++)
|
||||
{
|
||||
missionIndices.Add(inc.ReadInt16());
|
||||
}
|
||||
if (!GameMain.NetLobbyScreen.TrySelectSub(subName, subHash, GameMain.NetLobbyScreen.SubList))
|
||||
{
|
||||
roundInitStatus = RoundInitStatus.Interrupted;
|
||||
@@ -1486,7 +1534,9 @@ namespace Barotrauma.Networking
|
||||
yield return CoroutineStatus.Failure;
|
||||
}
|
||||
|
||||
GameMain.GameSession = new GameSession(GameMain.NetLobbyScreen.SelectedSub, gameMode, missionPrefab: missionIndex < 0 ? null : MissionPrefab.List[missionIndex]);
|
||||
var selectedMissions = missionIndices.Select(i => MissionPrefab.List[i]);
|
||||
|
||||
GameMain.GameSession = new GameSession(GameMain.NetLobbyScreen.SelectedSub, gameMode, missionPrefabs: selectedMissions);
|
||||
GameMain.GameSession.StartRound(levelSeed, levelDifficulty);
|
||||
}
|
||||
else
|
||||
@@ -1653,13 +1703,15 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
foreach (Submarine sub in Submarine.MainSubs[i].DockedTo)
|
||||
{
|
||||
if (sub.Info.Type == SubmarineType.Outpost) { continue; }
|
||||
sub.TeamID = teamID;
|
||||
}
|
||||
}
|
||||
|
||||
if (respawnAllowed)
|
||||
{
|
||||
respawnManager = new RespawnManager(this, GameMain.NetLobbyScreen.UsingShuttle && gameMode != GameModePreset.MultiPlayerCampaign ? GameMain.NetLobbyScreen.SelectedShuttle : null);
|
||||
if (respawnAllowed)
|
||||
{
|
||||
bool isOutpost = GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign && Level.Loaded?.Type == LevelData.LevelType.Outpost;
|
||||
respawnManager = new RespawnManager(this, GameMain.NetLobbyScreen.UsingShuttle && !isOutpost ? GameMain.NetLobbyScreen.SelectedShuttle : null);
|
||||
}
|
||||
|
||||
gameStarted = true;
|
||||
@@ -1702,6 +1754,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
gameStarted = false;
|
||||
Character.Controlled = null;
|
||||
WaitForNextRoundRespawn = null;
|
||||
SpawnAsTraitor = false;
|
||||
GameMain.GameScreen.Cam.TargetPos = Vector2.Zero;
|
||||
GameMain.LightManager.LosEnabled = false;
|
||||
@@ -1712,7 +1765,7 @@ namespace Barotrauma.Networking
|
||||
// Enable characters near the main sub for the endCinematic
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
if (Vector2.DistanceSquared(Submarine.MainSub.WorldPosition, c.WorldPosition) < NetConfig.EnableCharacterDistSqr)
|
||||
if (Vector2.DistanceSquared(Submarine.MainSub.WorldPosition, c.WorldPosition) < MathUtils.Pow2(c.Params.DisableDistance))
|
||||
{
|
||||
c.Enabled = true;
|
||||
}
|
||||
@@ -1757,8 +1810,10 @@ namespace Barotrauma.Networking
|
||||
var matchingSub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == subName && s.MD5Hash.Hash == subHash);
|
||||
if (matchingSub == null)
|
||||
{
|
||||
matchingSub = new SubmarineInfo(Path.Combine(SubmarineInfo.SavePath, subName) + ".sub", subHash, tryLoad: false);
|
||||
matchingSub.SubmarineClass = (SubmarineClass)subClass;
|
||||
matchingSub = new SubmarineInfo(Path.Combine(SubmarineInfo.SavePath, subName) + ".sub", subHash, tryLoad: false)
|
||||
{
|
||||
SubmarineClass = (SubmarineClass)subClass
|
||||
};
|
||||
}
|
||||
matchingSub.RequiredContentPackagesInstalled = requiredContentPackagesInstalled;
|
||||
ServerSubmarines.Add(matchingSub);
|
||||
@@ -1928,14 +1983,19 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
foreach (Client client in ConnectedClients)
|
||||
{
|
||||
if (!previouslyConnectedClients.Any(c => c.ID == client.ID))
|
||||
int index = previouslyConnectedClients.FindIndex(c => c.ID == client.ID);
|
||||
if (index < 0)
|
||||
{
|
||||
while (previouslyConnectedClients.Count > 100)
|
||||
if (previouslyConnectedClients.Count > 100)
|
||||
{
|
||||
previouslyConnectedClients.RemoveAt(0);
|
||||
previouslyConnectedClients.RemoveRange(0, previouslyConnectedClients.Count - 100);
|
||||
}
|
||||
previouslyConnectedClients.Add(client);
|
||||
}
|
||||
else
|
||||
{
|
||||
previouslyConnectedClients.RemoveAt(index);
|
||||
}
|
||||
previouslyConnectedClients.Add(client);
|
||||
}
|
||||
if (updateClientListId) { LastClientListUpdateID = listId; }
|
||||
|
||||
@@ -2028,6 +2088,8 @@ namespace Barotrauma.Networking
|
||||
bool autoRestartEnabled = inc.ReadBoolean();
|
||||
float autoRestartTimer = autoRestartEnabled ? inc.ReadSingle() : 0.0f;
|
||||
|
||||
bool radiationEnabled = inc.ReadBoolean();
|
||||
|
||||
//ignore the message if we already a more up-to-date one
|
||||
//or if we're still waiting for the initial update
|
||||
if (NetIdUtils.IdMoreRecent(updateID, GameMain.NetLobbyScreen.LastUpdateID) &&
|
||||
@@ -2038,8 +2100,14 @@ namespace Barotrauma.Networking
|
||||
serverSettings.ClientRead(settingsBuf);
|
||||
if (!IsServerOwner)
|
||||
{
|
||||
ServerInfo info = GameMain.ServerListScreen.UpdateServerInfoWithServerSettings(serverEndpoint, serverSettings);
|
||||
ServerInfo info = serverSettings.GetServerListInfo();
|
||||
GameMain.ServerListScreen.AddToRecentServers(info);
|
||||
GameMain.NetLobbyScreen.Favorite.Visible = true;
|
||||
GameMain.NetLobbyScreen.Favorite.Selected = GameMain.ServerListScreen.IsFavorite(info);
|
||||
}
|
||||
else
|
||||
{
|
||||
GameMain.NetLobbyScreen.Favorite.Visible = false;
|
||||
}
|
||||
|
||||
GameMain.NetLobbyScreen.LastUpdateID = updateID;
|
||||
@@ -2083,8 +2151,9 @@ namespace Barotrauma.Networking
|
||||
GameMain.NetLobbyScreen.SetAllowSpectating(allowSpectating);
|
||||
GameMain.NetLobbyScreen.LevelSeed = levelSeed;
|
||||
GameMain.NetLobbyScreen.SetLevelDifficulty(levelDifficulty);
|
||||
GameMain.NetLobbyScreen.SetBotCount(botCount);
|
||||
GameMain.NetLobbyScreen.SetRadiationEnabled(radiationEnabled);
|
||||
GameMain.NetLobbyScreen.SetBotSpawnMode(botSpawnMode);
|
||||
GameMain.NetLobbyScreen.SetBotCount(botCount);
|
||||
GameMain.NetLobbyScreen.SetAutoRestart(autoRestartEnabled, autoRestartTimer);
|
||||
|
||||
serverSettings.VoiceChatEnabled = voiceChatEnabled;
|
||||
@@ -2325,7 +2394,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (outmsg.LengthBytes > MsgConstants.MTU)
|
||||
{
|
||||
DebugConsole.ThrowError("Maximum packet size exceeded (" + outmsg.LengthBytes + " > " + MsgConstants.MTU);
|
||||
DebugConsole.ThrowError($"Maximum packet size exceeded ({outmsg.LengthBytes} > {MsgConstants.MTU})");
|
||||
}
|
||||
|
||||
clientPeer.Send(outmsg, DeliveryMethod.Unreliable);
|
||||
@@ -2376,7 +2445,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (outmsg.LengthBytes > MsgConstants.MTU)
|
||||
{
|
||||
DebugConsole.ThrowError("Maximum packet size exceeded (" + outmsg.LengthBytes + " > " + MsgConstants.MTU);
|
||||
DebugConsole.ThrowError($"Maximum packet size exceeded ({outmsg.LengthBytes} > {MsgConstants.MTU})");
|
||||
}
|
||||
|
||||
clientPeer.Send(outmsg, DeliveryMethod.Unreliable);
|
||||
@@ -2406,6 +2475,15 @@ namespace Barotrauma.Networking
|
||||
chatMsgQueue.Add(chatMessage);
|
||||
}
|
||||
|
||||
public void SendRespawnPromptResponse(bool waitForNextRoundRespawn)
|
||||
{
|
||||
WaitForNextRoundRespawn = waitForNextRoundRespawn;
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.Write((byte)ClientPacketHeader.READY_TO_SPAWN);
|
||||
msg.Write((bool)waitForNextRoundRespawn);
|
||||
clientPeer?.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
|
||||
public void RequestFile(FileTransferType fileType, string file, string fileHash)
|
||||
{
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
@@ -2528,7 +2606,7 @@ namespace Barotrauma.Networking
|
||||
if (!(GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign) || campaign.CampaignID != campaignID)
|
||||
{
|
||||
string savePath = transfer.FilePath;
|
||||
GameMain.GameSession = new GameSession(null, savePath, GameModePreset.MultiPlayerCampaign);
|
||||
GameMain.GameSession = new GameSession(null, savePath, GameModePreset.MultiPlayerCampaign, CampaignSettings.Unsure);
|
||||
campaign = (MultiPlayerCampaign)GameMain.GameSession.GameMode;
|
||||
campaign.CampaignID = campaignID;
|
||||
GameMain.NetLobbyScreen.ToggleCampaignMode(true);
|
||||
@@ -2876,7 +2954,7 @@ namespace Barotrauma.Networking
|
||||
clientPeer.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
|
||||
public void SetupNewCampaign(SubmarineInfo sub, string saveName, string mapSeed)
|
||||
public void SetupNewCampaign(SubmarineInfo sub, string saveName, string mapSeed, CampaignSettings settings)
|
||||
{
|
||||
GameMain.NetLobbyScreen.CampaignSetupFrame.Visible = false;
|
||||
GameMain.NetLobbyScreen.CampaignFrame.Visible = false;
|
||||
@@ -2891,6 +2969,7 @@ namespace Barotrauma.Networking
|
||||
msg.Write(mapSeed);
|
||||
msg.Write(sub.Name);
|
||||
msg.Write(sub.MD5Hash.Hash);
|
||||
settings.Serialize(msg);
|
||||
|
||||
clientPeer.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
@@ -3096,11 +3175,11 @@ namespace Barotrauma.Networking
|
||||
|
||||
cameraFollowsSub.Visible = Character.Controlled == null;
|
||||
}
|
||||
if (Character.Controlled == null || Character.Controlled.IsDead)
|
||||
/*if (Character.Controlled == null || Character.Controlled.IsDead)
|
||||
{
|
||||
GameMain.GameScreen.Cam.TargetPos = Vector2.Zero;
|
||||
GameMain.LightManager.LosEnabled = false;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
//tab doesn't autoselect the chatbox when debug console is open,
|
||||
@@ -3208,9 +3287,13 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (respawnManager != null)
|
||||
{
|
||||
string respawnText = "";
|
||||
float textScale = 1.0f;
|
||||
string respawnText = string.Empty;
|
||||
Color textColor = Color.White;
|
||||
bool canChooseRespawn =
|
||||
GameMain.GameSession.GameMode is CampaignMode &&
|
||||
Character.Controlled == null &&
|
||||
Level.Loaded?.Type != LevelData.LevelType.Outpost &&
|
||||
(characterInfo == null || HasSpawned);
|
||||
if (respawnManager.CurrentState == RespawnManager.State.Waiting &&
|
||||
respawnManager.RespawnCountdownStarted)
|
||||
{
|
||||
@@ -3228,18 +3311,18 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
//oscillate between 0-1
|
||||
float phase = (float)(Math.Sin(timeLeft * MathHelper.Pi) + 1.0f) * 0.5f;
|
||||
textScale = 1.0f + phase * 0.5f;
|
||||
//textScale = 1.0f + phase * 0.5f;
|
||||
textColor = Color.Lerp(GUI.Style.Red, Color.White, 1.0f - phase);
|
||||
}
|
||||
canChooseRespawn = false;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(respawnText))
|
||||
{
|
||||
GUI.SmallFont.DrawString(spriteBatch, respawnText, new Vector2(120.0f, 10), textColor, 0.0f, Vector2.Zero, textScale, Microsoft.Xna.Framework.Graphics.SpriteEffects.None, 0.0f);
|
||||
}
|
||||
|
||||
GameMain.GameSession?.SetRespawnInfo(
|
||||
visible: !string.IsNullOrEmpty(respawnText) || canChooseRespawn, text: respawnText, textColor: textColor,
|
||||
buttonsVisible: canChooseRespawn, waitForNextRoundRespawn: (WaitForNextRoundRespawn ?? true));
|
||||
}
|
||||
|
||||
if (!ShowNetStats) return;
|
||||
if (!ShowNetStats) { return; }
|
||||
|
||||
NetStats.Draw(spriteBatch, new Rectangle(300, 10, 300, 150));
|
||||
|
||||
@@ -3367,9 +3450,12 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
var banReasonPrompt = new GUIMessageBox(
|
||||
TextManager.Get(ban ? "BanReasonPrompt" : "KickReasonPrompt"),
|
||||
"", new string[] { TextManager.Get("OK"), TextManager.Get("Cancel") }, new Vector2(0.25f, 0.22f), new Point(400, 220));
|
||||
"", new string[] { TextManager.Get("OK"), TextManager.Get("Cancel") }, new Vector2(0.25f, 0.25f), new Point(400, 260));
|
||||
|
||||
var content = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.6f), banReasonPrompt.InnerFrame.RectTransform, Anchor.Center));
|
||||
var content = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.6f), banReasonPrompt.InnerFrame.RectTransform, Anchor.Center))
|
||||
{
|
||||
AbsoluteSpacing = GUI.IntScale(5)
|
||||
};
|
||||
var banReasonBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.3f), content.RectTransform))
|
||||
{
|
||||
Wrap = true,
|
||||
@@ -3380,10 +3466,9 @@ namespace Barotrauma.Networking
|
||||
GUITickBox permaBanTickBox = null;
|
||||
|
||||
if (ban)
|
||||
{
|
||||
|
||||
{
|
||||
var labelContainer = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.25f), content.RectTransform), isHorizontal: false);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1f, 0.5f), labelContainer.RectTransform), TextManager.Get("BanDuration")) { Padding = Vector4.Zero };
|
||||
new GUITextBlock(new RectTransform(new Vector2(1f, 0.0f), labelContainer.RectTransform), TextManager.Get("BanDuration"), font: GUI.SubHeadingFont) { Padding = Vector4.Zero };
|
||||
var buttonContent = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.5f), labelContainer.RectTransform), isHorizontal: true);
|
||||
permaBanTickBox = new GUITickBox(new RectTransform(new Vector2(0.4f, 0.15f), buttonContent.RectTransform), TextManager.Get("BanPermanent"))
|
||||
{
|
||||
@@ -3494,12 +3579,19 @@ namespace Barotrauma.Networking
|
||||
errorLines.Add("Campaign ID: " + campaign.CampaignID);
|
||||
errorLines.Add("Campaign save ID: " + campaign.LastSaveID + "(pending: " + campaign.PendingSaveID + ")");
|
||||
}
|
||||
errorLines.Add("Mission: " + (GameMain.GameSession?.Mission?.Prefab.Identifier ?? "none"));
|
||||
foreach (Mission mission in GameMain.GameSession.Missions)
|
||||
{
|
||||
errorLines.Add("Mission: " + mission.Prefab.Identifier);
|
||||
}
|
||||
}
|
||||
if (GameMain.GameSession?.Submarine != null)
|
||||
{
|
||||
errorLines.Add("Submarine: " + GameMain.GameSession.Submarine.Info.Name);
|
||||
}
|
||||
if (GameMain.NetworkMember?.RespawnManager?.RespawnShuttle != null)
|
||||
{
|
||||
errorLines.Add("Respawn shuttle: " + GameMain.NetworkMember.RespawnManager.RespawnShuttle.Info.Name);
|
||||
}
|
||||
if (Level.Loaded != null)
|
||||
{
|
||||
errorLines.Add("Level: " + Level.Loaded.Seed + ", " + string.Join(", ", Level.Loaded.EqualityCheckValues.Select(cv => cv.ToString("X"))));
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
partial class OrderChatMessage : ChatMessage
|
||||
{
|
||||
@@ -9,26 +7,7 @@ namespace Barotrauma.Networking
|
||||
msg.Write((byte)ClientNetObject.CHAT_MESSAGE);
|
||||
msg.Write(NetStateID);
|
||||
msg.Write((byte)ChatMessageType.Order);
|
||||
msg.Write((byte)Order.PrefabList.IndexOf(Order.Prefab));
|
||||
msg.Write(TargetCharacter == null ? (UInt16)0 : TargetCharacter.ID);
|
||||
msg.Write(TargetEntity is Entity ? (TargetEntity as Entity).ID : (UInt16)0);
|
||||
msg.Write((byte)Array.IndexOf(Order.Prefab.Options, OrderOption));
|
||||
msg.Write((byte)Order.TargetType);
|
||||
if (Order.TargetType == Order.OrderTargetType.Position && TargetEntity is OrderTarget orderTarget)
|
||||
{
|
||||
msg.Write(true);
|
||||
msg.Write(orderTarget.Position.X);
|
||||
msg.Write(orderTarget.Position.Y);
|
||||
msg.Write(orderTarget.Hull == null ? (UInt16)0 : orderTarget.Hull.ID);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.Write(false);
|
||||
if (Order.TargetType == Order.OrderTargetType.WallSection)
|
||||
{
|
||||
msg.Write((byte)(WallSectionIndex ?? Order.WallSectionIndex ?? 0));
|
||||
}
|
||||
}
|
||||
WriteOrder(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,13 +147,36 @@ namespace Barotrauma.Networking
|
||||
if (missingPackages.Count > 0)
|
||||
{
|
||||
var nonDownloadable = missingPackages.Where(p => p.WorkshopId == 0);
|
||||
var mismatchedButDownloaded = missingPackages.Where(p =>
|
||||
{
|
||||
var localMatching = ContentPackage.RegularPackages.Find(l => l.SteamWorkshopId != 0 && p.WorkshopId == l.SteamWorkshopId);
|
||||
localMatching ??= ContentPackage.CorePackages.Find(l => l.SteamWorkshopId != 0 && p.WorkshopId == l.SteamWorkshopId);
|
||||
|
||||
if (nonDownloadable.Any())
|
||||
return localMatching != null;
|
||||
});
|
||||
|
||||
if (mismatchedButDownloaded.Any())
|
||||
{
|
||||
string disconnectMsg;
|
||||
if (mismatchedButDownloaded.Count() == 1)
|
||||
{
|
||||
disconnectMsg = $"DisconnectMessage.MismatchedWorkshopMod~[incompatiblecontentpackage]={GetPackageStr(mismatchedButDownloaded.First())}";
|
||||
}
|
||||
else
|
||||
{
|
||||
List<string> packageStrs = new List<string>();
|
||||
mismatchedButDownloaded.ForEach(cp => packageStrs.Add(GetPackageStr(cp)));
|
||||
disconnectMsg = $"DisconnectMessage.MismatchedWorkshopMods~[incompatiblecontentpackages]={string.Join(", ", packageStrs)}";
|
||||
}
|
||||
Close(disconnectMsg, disableReconnect: true);
|
||||
OnDisconnectMessageReceived?.Invoke(DisconnectReason.MissingContentPackage + "/" + disconnectMsg);
|
||||
}
|
||||
else if (nonDownloadable.Any())
|
||||
{
|
||||
string disconnectMsg;
|
||||
if (nonDownloadable.Count() == 1)
|
||||
{
|
||||
disconnectMsg = $"DisconnectMessage.MissingContentPackage~[missingcontentpackage]={GetPackageStr(missingPackages[0])}";
|
||||
disconnectMsg = $"DisconnectMessage.MissingContentPackage~[missingcontentpackage]={GetPackageStr(nonDownloadable.First())}";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -56,6 +56,7 @@ namespace Barotrauma.Networking
|
||||
Steamworks.SteamNetworking.AllowP2PPacketRelay(true);
|
||||
|
||||
ServerConnection = new SteamP2PConnection("Server", hostSteamId);
|
||||
ServerConnection.SetOwnerSteamIDIfUnknown(hostSteamId);
|
||||
|
||||
incomingInitializationMessages = new List<IReadMessage>();
|
||||
incomingDataMessages = new List<IReadMessage>();
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace Barotrauma.Networking
|
||||
class RemotePeer
|
||||
{
|
||||
public UInt64 SteamID;
|
||||
public UInt64 OwnerSteamID;
|
||||
public double? DisconnectTime;
|
||||
public bool Authenticating;
|
||||
public bool Authenticated;
|
||||
@@ -31,6 +32,7 @@ namespace Barotrauma.Networking
|
||||
public RemotePeer(UInt64 steamId)
|
||||
{
|
||||
SteamID = steamId;
|
||||
OwnerSteamID = 0;
|
||||
DisconnectTime = null;
|
||||
Authenticating = false;
|
||||
Authenticated = false;
|
||||
@@ -90,10 +92,19 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (status == Steamworks.AuthResponse.OK)
|
||||
{
|
||||
remotePeer.OwnerSteamID = ownerID;
|
||||
remotePeer.Authenticated = true;
|
||||
remotePeer.Authenticating = false;
|
||||
foreach (var msg in remotePeer.UnauthedMessages)
|
||||
{
|
||||
//rewrite the owner id before
|
||||
//forwarding the messages to
|
||||
//the server, since it's only
|
||||
//known now
|
||||
int prevBitPosition = msg.Message.BitPosition;
|
||||
msg.Message.BitPosition = sizeof(ulong) * 8;
|
||||
msg.Message.Write(ownerID);
|
||||
msg.Message.BitPosition = prevBitPosition;
|
||||
byte[] msgToSend = (byte[])msg.Message.Buffer.Clone();
|
||||
Array.Resize(ref msgToSend, msg.Message.LengthBytes);
|
||||
ChildServerRelay.Write(msgToSend);
|
||||
@@ -131,6 +142,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
IWriteMessage outMsg = new WriteOnlyMessage();
|
||||
outMsg.Write(steamId);
|
||||
outMsg.Write(remotePeer.OwnerSteamID);
|
||||
outMsg.Write(data, 1, dataLength - 1);
|
||||
|
||||
DeliveryMethod deliveryMethod = (DeliveryMethod)data[0];
|
||||
@@ -142,34 +154,27 @@ namespace Barotrauma.Networking
|
||||
bool isServerMessage = (incByte & (byte)PacketHeader.IsServerMessage) != 0;
|
||||
bool isHeartbeatMessage = (incByte & (byte)PacketHeader.IsHeartbeatMessage) != 0;
|
||||
|
||||
if (!remotePeer.Authenticated)
|
||||
if (!remotePeer.Authenticated & !remotePeer.Authenticating && isConnectionInitializationStep)
|
||||
{
|
||||
if (!remotePeer.Authenticating)
|
||||
remotePeer.DisconnectTime = null;
|
||||
|
||||
IReadMessage authMsg = new ReadOnlyMessage(data, isCompressed, 2, dataLength - 2, null);
|
||||
ConnectionInitialization initializationStep = (ConnectionInitialization)authMsg.ReadByte();
|
||||
if (initializationStep == ConnectionInitialization.SteamTicketAndVersion)
|
||||
{
|
||||
if (isConnectionInitializationStep)
|
||||
remotePeer.Authenticating = true;
|
||||
|
||||
authMsg.ReadString(); //skip name
|
||||
authMsg.ReadInt32(); //skip owner key
|
||||
authMsg.ReadUInt64(); //skip steamid
|
||||
UInt16 ticketLength = authMsg.ReadUInt16();
|
||||
byte[] ticket = authMsg.ReadBytes(ticketLength);
|
||||
|
||||
Steamworks.BeginAuthResult authSessionStartState = Steam.SteamManager.StartAuthSession(ticket, steamId);
|
||||
if (authSessionStartState != Steamworks.BeginAuthResult.OK)
|
||||
{
|
||||
remotePeer.DisconnectTime = null;
|
||||
|
||||
IReadMessage authMsg = new ReadOnlyMessage(data, isCompressed, 2, dataLength - 2, null);
|
||||
ConnectionInitialization initializationStep = (ConnectionInitialization)authMsg.ReadByte();
|
||||
//Console.WriteLine("received init step from "+steamId.ToString()+" ("+initializationStep.ToString()+")");
|
||||
if (initializationStep == ConnectionInitialization.SteamTicketAndVersion)
|
||||
{
|
||||
remotePeer.Authenticating = true;
|
||||
|
||||
authMsg.ReadString(); //skip name
|
||||
authMsg.ReadInt32(); //skip owner key
|
||||
authMsg.ReadUInt64(); //skip steamid
|
||||
UInt16 ticketLength = authMsg.ReadUInt16();
|
||||
byte[] ticket = authMsg.ReadBytes(ticketLength);
|
||||
|
||||
Steamworks.BeginAuthResult authSessionStartState = Steam.SteamManager.StartAuthSession(ticket, steamId);
|
||||
if (authSessionStartState != Steamworks.BeginAuthResult.OK)
|
||||
{
|
||||
DisconnectPeer(remotePeer, DisconnectReason.SteamAuthenticationFailed.ToString() + "/ Steam auth session failed to start: " + authSessionStartState.ToString());
|
||||
return;
|
||||
}
|
||||
}
|
||||
DisconnectPeer(remotePeer, DisconnectReason.SteamAuthenticationFailed.ToString() + "/ Steam auth session failed to start: " + authSessionStartState.ToString());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -336,6 +341,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
IWriteMessage outMsg = new WriteOnlyMessage();
|
||||
outMsg.Write(selfSteamID);
|
||||
outMsg.Write(selfSteamID);
|
||||
outMsg.Write((byte)(PacketHeader.IsConnectionInitializationStep));
|
||||
outMsg.Write(Name);
|
||||
|
||||
@@ -428,6 +434,7 @@ namespace Barotrauma.Networking
|
||||
byte[] msgData = new byte[msg.LengthBytes];
|
||||
msg.PrepareForSending(ref msgData, out bool isCompressed, out int length);
|
||||
msgToSend.Write(selfSteamID);
|
||||
msgToSend.Write(selfSteamID);
|
||||
msgToSend.Write((byte)(isCompressed ? PacketHeader.IsCompressed : PacketHeader.None));
|
||||
msgToSend.Write((UInt16)length);
|
||||
msgToSend.Write(msgData, 0, length);
|
||||
|
||||
@@ -159,6 +159,20 @@ namespace Barotrauma.Networking
|
||||
color: ServerListScreen.PlayStyleColors[(int)playStyle], style: "GUISlopedHeader");
|
||||
playStyleName.RectTransform.NonScaledSize = (playStyleName.Font.MeasureString(playStyleName.Text) + new Vector2(20, 5) * GUI.Scale).ToPoint();
|
||||
playStyleName.RectTransform.IsFixedSize = true;
|
||||
|
||||
var serverTypeContainer = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.2f), playStyleBanner.RectTransform, Anchor.BottomLeft, Pivot.BottomLeft),
|
||||
"MainMenuNotifBackground", Color.Black)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
};
|
||||
|
||||
var serverType = new GUITextBlock(new RectTransform(Vector2.One, serverTypeContainer.RectTransform, Anchor.CenterLeft),
|
||||
TextManager.Get((OwnerID != 0 || LobbyID != 0) ? "SteamP2PServer" : "DedicatedServer"), textAlignment: Alignment.CenterLeft);
|
||||
}
|
||||
else
|
||||
{
|
||||
var serverType = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), previewContainer.RectTransform, Anchor.CenterLeft),
|
||||
TextManager.Get((OwnerID != 0 || LobbyID != 0) ? "SteamP2PServer" : "DedicatedServer"), textAlignment: Alignment.CenterLeft);
|
||||
}
|
||||
|
||||
var content = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.6f), previewContainer.RectTransform))
|
||||
@@ -553,5 +567,10 @@ namespace Barotrauma.Networking
|
||||
(other.LobbyID == LobbyID || other.LobbyID == 0 || LobbyID == 0) &&
|
||||
((OwnerID == 0) ? (other.IP == IP && other.Port == Port) : true);
|
||||
}
|
||||
|
||||
public bool MatchesByEndpoint(ServerInfo other)
|
||||
{
|
||||
return OwnerID == other.OwnerID && (OwnerID != 0 ? true : (IP == other.IP && Port == other.Port));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,16 +242,7 @@ namespace Barotrauma.Networking
|
||||
textBlock.ClickableAreas.Add(new GUITextBlock.ClickableArea()
|
||||
{
|
||||
Data = data,
|
||||
OnClick = (component, area) =>
|
||||
{
|
||||
if (!UInt64.TryParse(area.Data.Metadata, out UInt64 id)) { return; }
|
||||
Client client = GameMain.Client.ConnectedClients.Find(c => c.SteamID == id)
|
||||
?? GameMain.Client.ConnectedClients.Find(c => c.ID == id)
|
||||
?? GameMain.Client.PreviouslyConnectedClients.FirstOrDefault(c => c.SteamID == id)
|
||||
?? GameMain.Client.PreviouslyConnectedClients.FirstOrDefault(c => c.ID == id);
|
||||
if (client == null) { return; }
|
||||
GameMain.NetLobbyScreen.SelectPlayer(client);
|
||||
}
|
||||
OnClick = GameMain.NetLobbyScreen.SelectPlayer
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,11 +126,15 @@ namespace Barotrauma.Networking
|
||||
|
||||
public void ClientRead(IReadMessage incMsg)
|
||||
{
|
||||
cachedServerListInfo = null;
|
||||
|
||||
ServerName = incMsg.ReadString();
|
||||
ServerMessageText = incMsg.ReadString();
|
||||
MaxPlayers = incMsg.ReadByte();
|
||||
HasPassword = incMsg.ReadBoolean();
|
||||
IsPublic = incMsg.ReadBoolean();
|
||||
GameMain.NetLobbyScreen.SetPublic(IsPublic);
|
||||
AllowFileTransfers = incMsg.ReadBoolean();
|
||||
incMsg.ReadPadBits();
|
||||
TickRate = incMsg.ReadRangedInteger(1, 60);
|
||||
GameMain.NetworkMember.TickRate = TickRate;
|
||||
@@ -147,7 +151,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientAdminWrite(NetFlags dataToSend, int? missionTypeOr = null, int? missionTypeAnd = null, float? levelDifficulty = null, bool? autoRestart = null, int traitorSetting = 0, int botCount = 0, int botSpawnMode = 0, bool? useRespawnShuttle = null)
|
||||
public void ClientAdminWrite(NetFlags dataToSend, int? missionTypeOr = null, int? missionTypeAnd = null, float? levelDifficulty = null, bool? autoRestart = null, int traitorSetting = 0, int botCount = 0, int botSpawnMode = 0, bool? radiationEnabled = null, bool? useRespawnShuttle = null)
|
||||
{
|
||||
if (!GameMain.Client.HasPermission(Networking.ClientPermissions.ManageSettings)) return;
|
||||
|
||||
@@ -212,6 +216,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
outMsg.Write(autoRestart != null);
|
||||
outMsg.Write(autoRestart ?? false);
|
||||
outMsg.Write(radiationEnabled ?? RadiationEnabled);
|
||||
outMsg.WritePadBits();
|
||||
}
|
||||
|
||||
@@ -274,7 +279,7 @@ namespace Barotrauma.Networking
|
||||
if (GUI.MouseOn == btn || GUI.MouseOn == btn.TextBlock) { ToggleSettingsFrame(btn, userData); }
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
new GUIButton(new RectTransform(Vector2.One, settingsFrame.RectTransform), "", style: null)
|
||||
{
|
||||
OnClicked = ToggleSettingsFrame
|
||||
@@ -500,8 +505,8 @@ namespace Barotrauma.Networking
|
||||
|
||||
CreateLabeledSlider(roundsTab, "ServerSettingsRespawnInterval", out slider, out sliderLabel);
|
||||
string intervalLabel = sliderLabel.Text;
|
||||
slider.Step = 0.05f;
|
||||
slider.Range = new Vector2(10.0f, 600.0f);
|
||||
slider.StepValue = 10.0f;
|
||||
GetPropertyData("RespawnInterval").AssignGUIComponent(slider);
|
||||
slider.OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
|
||||
{
|
||||
@@ -646,7 +651,14 @@ namespace Barotrauma.Networking
|
||||
|
||||
foreach (ItemPrefab ip in ItemPrefab.Prefabs)
|
||||
{
|
||||
if (!ip.CanBeBought && !ip.Tags.Contains("smallitem")) continue;
|
||||
if (ip.AllowAsExtraCargo.HasValue)
|
||||
{
|
||||
if (!ip.AllowAsExtraCargo.Value) { continue; }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ip.CanBeBought) { continue; }
|
||||
}
|
||||
|
||||
var itemFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), cargoFrame.Content.RectTransform) { MinSize = new Point(0, 30) }, isHorizontal: true)
|
||||
{
|
||||
@@ -725,6 +737,10 @@ namespace Barotrauma.Networking
|
||||
TextManager.Get("ServerSettingsDestructibleOutposts"));
|
||||
GetPropertyData("DestructibleOutposts").AssignGUIComponent(destructibleOutposts);
|
||||
|
||||
var lockAllDefaultWires = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform),
|
||||
TextManager.Get("ServerSettingsLockAllDefaultWires"));
|
||||
GetPropertyData("LockAllDefaultWires").AssignGUIComponent(lockAllDefaultWires);
|
||||
|
||||
var allowRewiring = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform),
|
||||
TextManager.Get("ServerSettingsAllowRewiring"));
|
||||
GetPropertyData("AllowRewiring").AssignGUIComponent(allowRewiring);
|
||||
@@ -915,6 +931,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
public bool ToggleSettingsFrame(GUIButton button, object obj)
|
||||
{
|
||||
if (GameMain.NetworkMember == null) { return false; }
|
||||
if (settingsFrame == null)
|
||||
{
|
||||
CreateSettingsFrame();
|
||||
@@ -936,5 +953,12 @@ namespace Barotrauma.Networking
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private ServerInfo cachedServerListInfo = null;
|
||||
public ServerInfo GetServerListInfo()
|
||||
{
|
||||
cachedServerListInfo ??= GameMain.ServerListScreen.UpdateServerInfoWithServerSettings(GameMain.Client.ClientPeer.ServerConnection, this);
|
||||
return cachedServerListInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Barotrauma.Sounds;
|
||||
using Microsoft.Xna.Framework;
|
||||
using OpenAL;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
@@ -56,7 +58,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
public bool Disconnected { get; private set; }
|
||||
|
||||
public static void Create(string deviceName, UInt16? storedBufferID=null)
|
||||
public static void Create(string deviceName, UInt16? storedBufferID = null)
|
||||
{
|
||||
if (Instance != null)
|
||||
{
|
||||
@@ -84,7 +86,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (captureDevice == IntPtr.Zero)
|
||||
{
|
||||
DebugConsole.NewMessage("Alc.CaptureOpenDevice attempt 1 failed: error code " + Alc.GetError(IntPtr.Zero).ToString(),Color.Orange);
|
||||
DebugConsole.NewMessage("Alc.CaptureOpenDevice attempt 1 failed: error code " + Alc.GetError(IntPtr.Zero).ToString(), Color.Orange);
|
||||
//attempt using a smaller buffer size
|
||||
captureDevice = Alc.CaptureOpenDevice(deviceName, VoipConfig.FREQUENCY, Al.FormatMono16, VoipConfig.BUFFER_SIZE * 2);
|
||||
}
|
||||
@@ -162,6 +164,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
IntPtr nativeBuffer;
|
||||
short[] uncompressedBuffer = new short[VoipConfig.BUFFER_SIZE];
|
||||
short[] prevUncompressedBuffer = new short[VoipConfig.BUFFER_SIZE];
|
||||
bool prevCaptured = true;
|
||||
@@ -171,143 +174,198 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
Array.Copy(uncompressedBuffer, 0, prevUncompressedBuffer, 0, VoipConfig.BUFFER_SIZE);
|
||||
Array.Clear(uncompressedBuffer, 0, VoipConfig.BUFFER_SIZE);
|
||||
while (capturing && !Disconnected)
|
||||
nativeBuffer = Marshal.AllocHGlobal(VoipConfig.BUFFER_SIZE * 2);
|
||||
try
|
||||
{
|
||||
int alcError;
|
||||
|
||||
if (CanDetectDisconnect)
|
||||
while (capturing)
|
||||
{
|
||||
Alc.GetInteger(captureDevice, Alc.EnumConnected, out int isConnected);
|
||||
int alcError;
|
||||
|
||||
if (CanDetectDisconnect)
|
||||
{
|
||||
Alc.GetInteger(captureDevice, Alc.EnumConnected, out int isConnected);
|
||||
alcError = Alc.GetError(captureDevice);
|
||||
if (alcError != Alc.NoError)
|
||||
{
|
||||
throw new Exception("Failed to determine if capture device is connected: " + alcError.ToString());
|
||||
}
|
||||
|
||||
if (isConnected == 0)
|
||||
{
|
||||
DebugConsole.ThrowError("Capture device has been disconnected. You can select another available device in the settings.");
|
||||
Disconnected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
FillBuffer();
|
||||
|
||||
alcError = Alc.GetError(captureDevice);
|
||||
if (alcError != Alc.NoError)
|
||||
{
|
||||
throw new Exception("Failed to determine if capture device is connected: " + alcError.ToString());
|
||||
throw new Exception("Failed to capture samples: " + alcError.ToString());
|
||||
}
|
||||
|
||||
if (isConnected == 0)
|
||||
double maxAmplitude = 0.0f;
|
||||
for (int i = 0; i < VoipConfig.BUFFER_SIZE; i++)
|
||||
{
|
||||
DebugConsole.ThrowError("Capture device has been disconnected. You can select another available device in the settings.");
|
||||
Disconnected = true;
|
||||
break;
|
||||
uncompressedBuffer[i] = (short)MathHelper.Clamp((uncompressedBuffer[i] * Gain), -short.MaxValue, short.MaxValue);
|
||||
double sampleVal = uncompressedBuffer[i] / (double)short.MaxValue;
|
||||
maxAmplitude = Math.Max(maxAmplitude, Math.Abs(sampleVal));
|
||||
}
|
||||
}
|
||||
double dB = Math.Min(20 * Math.Log10(maxAmplitude), 0.0);
|
||||
|
||||
Alc.GetInteger(captureDevice, Alc.EnumCaptureSamples, out int sampleCount);
|
||||
LastdB = dB;
|
||||
LastAmplitude = maxAmplitude;
|
||||
|
||||
alcError = Alc.GetError(captureDevice);
|
||||
if (alcError != Alc.NoError)
|
||||
{
|
||||
throw new Exception("Failed to determine sample count: " + alcError.ToString());
|
||||
}
|
||||
|
||||
if (sampleCount < VoipConfig.BUFFER_SIZE)
|
||||
{
|
||||
int sleepMs = (VoipConfig.BUFFER_SIZE - sampleCount) * 800 / VoipConfig.FREQUENCY;
|
||||
if (sleepMs < 5) sleepMs = 5;
|
||||
Thread.Sleep(sleepMs);
|
||||
continue;
|
||||
}
|
||||
|
||||
GCHandle handle = GCHandle.Alloc(uncompressedBuffer, GCHandleType.Pinned);
|
||||
try
|
||||
{
|
||||
Alc.CaptureSamples(captureDevice, handle.AddrOfPinnedObject(), VoipConfig.BUFFER_SIZE);
|
||||
}
|
||||
finally
|
||||
{
|
||||
handle.Free();
|
||||
}
|
||||
|
||||
alcError = Alc.GetError(captureDevice);
|
||||
if (alcError != Alc.NoError)
|
||||
{
|
||||
throw new Exception("Failed to capture samples: " + alcError.ToString());
|
||||
}
|
||||
|
||||
double maxAmplitude = 0.0f;
|
||||
for (int i = 0; i < VoipConfig.BUFFER_SIZE; i++)
|
||||
{
|
||||
uncompressedBuffer[i] = (short)MathHelper.Clamp((uncompressedBuffer[i] * Gain), -short.MaxValue, short.MaxValue);
|
||||
double sampleVal = uncompressedBuffer[i] / (double)short.MaxValue;
|
||||
maxAmplitude = Math.Max(maxAmplitude, Math.Abs(sampleVal));
|
||||
}
|
||||
double dB = Math.Min(20 * Math.Log10(maxAmplitude), 0.0);
|
||||
|
||||
LastdB = dB;
|
||||
LastAmplitude = maxAmplitude;
|
||||
|
||||
bool allowEnqueue = false;
|
||||
if (GameMain.WindowActive)
|
||||
{
|
||||
ForceLocal = captureTimer > 0 ? ForceLocal : GameMain.Config.UseLocalVoiceByDefault;
|
||||
bool pttDown = false;
|
||||
if ((PlayerInput.KeyDown(InputType.Voice) || PlayerInput.KeyDown(InputType.LocalVoice)) &&
|
||||
GUI.KeyboardDispatcher.Subscriber == null)
|
||||
bool allowEnqueue = overrideSound != null;
|
||||
if (GameMain.WindowActive)
|
||||
{
|
||||
pttDown = true;
|
||||
if (PlayerInput.KeyDown(InputType.LocalVoice))
|
||||
ForceLocal = captureTimer > 0 ? ForceLocal : GameMain.Config.UseLocalVoiceByDefault;
|
||||
bool pttDown = false;
|
||||
if ((PlayerInput.KeyDown(InputType.Voice) || PlayerInput.KeyDown(InputType.LocalVoice)) &&
|
||||
GUI.KeyboardDispatcher.Subscriber == null)
|
||||
{
|
||||
ForceLocal = true;
|
||||
pttDown = true;
|
||||
if (PlayerInput.KeyDown(InputType.LocalVoice))
|
||||
{
|
||||
ForceLocal = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ForceLocal = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (GameMain.Config.VoiceSetting == GameSettings.VoiceMode.Activity)
|
||||
{
|
||||
ForceLocal = false;
|
||||
if (dB > GameMain.Config.NoiseGateThreshold)
|
||||
{
|
||||
allowEnqueue = true;
|
||||
}
|
||||
}
|
||||
else if (GameMain.Config.VoiceSetting == GameSettings.VoiceMode.PushToTalk)
|
||||
{
|
||||
if (pttDown)
|
||||
{
|
||||
allowEnqueue = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (GameMain.Config.VoiceSetting == GameSettings.VoiceMode.Activity)
|
||||
|
||||
if (allowEnqueue || captureTimer > 0)
|
||||
{
|
||||
if (dB > GameMain.Config.NoiseGateThreshold)
|
||||
LastEnqueueAudio = DateTime.Now;
|
||||
if (GameMain.Client?.Character != null)
|
||||
{
|
||||
allowEnqueue = true;
|
||||
var messageType = !ForceLocal && ChatMessage.CanUseRadio(GameMain.Client.Character, out _) ? ChatMessageType.Radio : ChatMessageType.Default;
|
||||
GameMain.Client.Character.ShowSpeechBubble(1.25f, ChatMessage.MessageColor[(int)messageType]);
|
||||
}
|
||||
}
|
||||
else if (GameMain.Config.VoiceSetting == GameSettings.VoiceMode.PushToTalk)
|
||||
{
|
||||
if (pttDown)
|
||||
//encode audio and enqueue it
|
||||
lock (buffers)
|
||||
{
|
||||
allowEnqueue = true;
|
||||
if (!prevCaptured) //enqueue the previous buffer if not sent to avoid cutoff
|
||||
{
|
||||
int compressedCountPrev = VoipConfig.Encoder.Encode(prevUncompressedBuffer, 0, VoipConfig.BUFFER_SIZE, BufferToQueue, 0, VoipConfig.MAX_COMPRESSED_SIZE);
|
||||
EnqueueBuffer(compressedCountPrev);
|
||||
}
|
||||
int compressedCount = VoipConfig.Encoder.Encode(uncompressedBuffer, 0, VoipConfig.BUFFER_SIZE, BufferToQueue, 0, VoipConfig.MAX_COMPRESSED_SIZE);
|
||||
EnqueueBuffer(compressedCount);
|
||||
}
|
||||
captureTimer -= (VoipConfig.BUFFER_SIZE * 1000) / VoipConfig.FREQUENCY;
|
||||
if (allowEnqueue)
|
||||
{
|
||||
captureTimer = GameMain.Config.VoiceChatCutoffPrevention;
|
||||
}
|
||||
prevCaptured = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
captureTimer = 0;
|
||||
prevCaptured = false;
|
||||
//enqueue silence
|
||||
lock (buffers)
|
||||
{
|
||||
EnqueueBuffer(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError($"VoipCapture threw an exception. Disabling capture...", e);
|
||||
capturing = false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeHGlobal(nativeBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
if (allowEnqueue || captureTimer > 0)
|
||||
private Sound overrideSound;
|
||||
private int overridePos;
|
||||
private short[] overrideBuf = new short[VoipConfig.BUFFER_SIZE];
|
||||
|
||||
private void FillBuffer()
|
||||
{
|
||||
if (overrideSound != null)
|
||||
{
|
||||
int totalSampleCount = 0;
|
||||
while (totalSampleCount < VoipConfig.BUFFER_SIZE)
|
||||
{
|
||||
LastEnqueueAudio = DateTime.Now;
|
||||
if (GameMain.Client?.Character != null)
|
||||
int sampleCount = overrideSound.FillStreamBuffer(overridePos, overrideBuf);
|
||||
overridePos += sampleCount * 2;
|
||||
Array.Copy(overrideBuf, 0, uncompressedBuffer, totalSampleCount, sampleCount);
|
||||
totalSampleCount += sampleCount;
|
||||
|
||||
if (sampleCount == 0)
|
||||
{
|
||||
var messageType = !ForceLocal && ChatMessage.CanUseRadio(GameMain.Client.Character, out _) ? ChatMessageType.Radio : ChatMessageType.Default;
|
||||
GameMain.Client.Character.ShowSpeechBubble(1.25f, ChatMessage.MessageColor[(int)messageType]);
|
||||
overridePos = 0;
|
||||
}
|
||||
//encode audio and enqueue it
|
||||
lock (buffers)
|
||||
}
|
||||
int sleepMs = VoipConfig.BUFFER_SIZE * 800 / VoipConfig.FREQUENCY;
|
||||
Thread.Sleep(sleepMs - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
int sampleCount = 0;
|
||||
|
||||
while (sampleCount < VoipConfig.BUFFER_SIZE)
|
||||
{
|
||||
Alc.GetInteger(captureDevice, Alc.EnumCaptureSamples, out sampleCount);
|
||||
|
||||
int alcError = Alc.GetError(captureDevice);
|
||||
if (alcError != Alc.NoError)
|
||||
{
|
||||
if (!prevCaptured) //enqueue the previous buffer if not sent to avoid cutoff
|
||||
throw new Exception("Failed to determine sample count: " + alcError.ToString());
|
||||
}
|
||||
|
||||
if (sampleCount < VoipConfig.BUFFER_SIZE)
|
||||
{
|
||||
int sleepMs = (VoipConfig.BUFFER_SIZE - sampleCount) * 800 / VoipConfig.FREQUENCY;
|
||||
if (sleepMs >= 1)
|
||||
{
|
||||
int compressedCountPrev = VoipConfig.Encoder.Encode(prevUncompressedBuffer, 0, VoipConfig.BUFFER_SIZE, BufferToQueue, 0, VoipConfig.MAX_COMPRESSED_SIZE);
|
||||
EnqueueBuffer(compressedCountPrev);
|
||||
Thread.Sleep(sleepMs);
|
||||
}
|
||||
int compressedCount = VoipConfig.Encoder.Encode(uncompressedBuffer, 0, VoipConfig.BUFFER_SIZE, BufferToQueue, 0, VoipConfig.MAX_COMPRESSED_SIZE);
|
||||
EnqueueBuffer(compressedCount);
|
||||
}
|
||||
captureTimer -= (VoipConfig.BUFFER_SIZE * 1000) / VoipConfig.FREQUENCY;
|
||||
if (allowEnqueue)
|
||||
{
|
||||
captureTimer = GameMain.Config.VoiceChatCutoffPrevention;
|
||||
}
|
||||
prevCaptured = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
captureTimer = 0;
|
||||
prevCaptured = false;
|
||||
//enqueue silence
|
||||
lock (buffers)
|
||||
{
|
||||
EnqueueBuffer(0);
|
||||
}
|
||||
|
||||
if (!capturing) { return; }
|
||||
}
|
||||
|
||||
Thread.Sleep(10);
|
||||
Alc.CaptureSamples(captureDevice, nativeBuffer, VoipConfig.BUFFER_SIZE);
|
||||
Marshal.Copy(nativeBuffer, uncompressedBuffer, 0, uncompressedBuffer.Length);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetOverrideSound(string fileName)
|
||||
{
|
||||
overrideSound?.Dispose();
|
||||
if (string.IsNullOrEmpty(fileName))
|
||||
{
|
||||
overrideSound = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
overrideSound = GameMain.SoundManager.LoadSound(fileName, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,6 +94,7 @@ namespace Barotrauma.Networking
|
||||
DebugConsole.Log("Recreating voipsound " + queueId);
|
||||
client.VoipSound = new VoipSound(client.Name, GameMain.SoundManager, client.VoipQueue);
|
||||
}
|
||||
GameMain.SoundManager.ForceStreamUpdate();
|
||||
|
||||
if (client.Character != null && !client.Character.IsDead && !client.Character.Removed && client.Character.SpeechImpediment <= 100.0f)
|
||||
{
|
||||
@@ -102,7 +103,7 @@ namespace Barotrauma.Networking
|
||||
client.Character.ShowSpeechBubble(1.25f, ChatMessage.MessageColor[(int)messageType]);
|
||||
|
||||
client.VoipSound.UseRadioFilter = messageType == ChatMessageType.Radio && !GameMain.Config.DisableVoiceChatFilters;
|
||||
if (client.VoipSound.UseRadioFilter)
|
||||
if (messageType == ChatMessageType.Radio)
|
||||
{
|
||||
client.VoipSound.SetRange(radio.Range * 0.8f, radio.Range);
|
||||
}
|
||||
@@ -110,7 +111,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
client.VoipSound.SetRange(ChatMessage.SpeakRange * 0.4f, ChatMessage.SpeakRange);
|
||||
}
|
||||
if (!client.VoipSound.UseRadioFilter && Character.Controlled != null && !GameMain.Config.DisableVoiceChatFilters)
|
||||
if (messageType != ChatMessageType.Radio && Character.Controlled != null && !GameMain.Config.DisableVoiceChatFilters)
|
||||
{
|
||||
client.VoipSound.UseMuffleFilter = SoundPlayer.ShouldMuffleSound(Character.Controlled, client.Character.WorldPosition, ChatMessage.SpeakRange, client.Character.CurrentHull);
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
public static bool Ready = false;
|
||||
|
||||
public const int FREQUENCY = 48000; //not amazing, but not bad audio quality
|
||||
public const int BUFFER_SIZE = 2880; //60ms window, the max Opus seems to support
|
||||
public const int FREQUENCY = 48000;
|
||||
public const int BUFFER_SIZE = 960; //20ms window
|
||||
|
||||
public static OpusEncoder Encoder
|
||||
{
|
||||
|
||||
@@ -112,14 +112,17 @@ namespace Barotrauma
|
||||
|
||||
switch (voteType)
|
||||
{
|
||||
case VoteType.Sub:
|
||||
SubmarineInfo sub = data as SubmarineInfo;
|
||||
if (sub == null) { return; }
|
||||
case VoteType.Sub:
|
||||
if (!(data is SubmarineInfo sub)) { return; }
|
||||
msg.Write(sub.EqualityCheckVal);
|
||||
if (sub.EqualityCheckVal == 0)
|
||||
{
|
||||
//sub doesn't exist client-side, use hash to let the server know which one we voted for
|
||||
msg.Write(sub.MD5Hash.Hash);
|
||||
}
|
||||
break;
|
||||
case VoteType.Mode:
|
||||
GameModePreset gameMode = data as GameModePreset;
|
||||
if (gameMode == null) { return; }
|
||||
if (!(data is GameModePreset gameMode)) { return; }
|
||||
msg.Write(gameMode.Identifier);
|
||||
break;
|
||||
case VoteType.EndRound:
|
||||
@@ -127,8 +130,7 @@ namespace Barotrauma
|
||||
msg.Write((bool)data);
|
||||
break;
|
||||
case VoteType.Kick:
|
||||
Client votedClient = data as Client;
|
||||
if (votedClient == null) return;
|
||||
if (!(data is Client votedClient)) { return; }
|
||||
|
||||
msg.Write(votedClient.ID);
|
||||
break;
|
||||
|
||||
@@ -27,7 +27,8 @@ namespace Barotrauma.Particles
|
||||
private float angularVelocity;
|
||||
|
||||
private Vector2 dragVec = Vector2.Zero;
|
||||
private int dragWait = 0;
|
||||
private float dragWait = 0;
|
||||
private float collisionIgnoreTimer = 0;
|
||||
|
||||
private Vector2 size;
|
||||
private Vector2 sizeChange;
|
||||
@@ -103,7 +104,7 @@ namespace Barotrauma.Particles
|
||||
return debugName;
|
||||
}
|
||||
|
||||
public void Init(ParticlePrefab prefab, Vector2 position, Vector2 speed, float rotation, Hull hullGuess = null, bool drawOnTop = false)
|
||||
public void Init(ParticlePrefab prefab, Vector2 position, Vector2 speed, float rotation, Hull hullGuess = null, bool drawOnTop = false, float collisionIgnoreTimer = 0f)
|
||||
{
|
||||
this.prefab = prefab;
|
||||
debugName = $"Particle ({prefab.Name})";
|
||||
@@ -174,14 +175,22 @@ namespace Barotrauma.Particles
|
||||
}
|
||||
|
||||
DrawOnTop = drawOnTop;
|
||||
|
||||
this.collisionIgnoreTimer = collisionIgnoreTimer;
|
||||
}
|
||||
|
||||
public bool Update(float deltaTime)
|
||||
public enum UpdateResult
|
||||
{
|
||||
Normal,
|
||||
Delete
|
||||
}
|
||||
|
||||
public UpdateResult Update(float deltaTime)
|
||||
{
|
||||
if (startDelay > 0.0f)
|
||||
{
|
||||
startDelay -= deltaTime;
|
||||
return true;
|
||||
return UpdateResult.Normal;
|
||||
}
|
||||
|
||||
prevPosition = position;
|
||||
@@ -251,7 +260,7 @@ namespace Barotrauma.Particles
|
||||
}
|
||||
|
||||
lifeTime -= deltaTime;
|
||||
if (lifeTime <= 0.0f || color.A <= 0 || size.X <= 0.0f || size.Y <= 0.0f) { return false; }
|
||||
if (lifeTime <= 0.0f || color.A <= 0 || size.X <= 0.0f || size.Y <= 0.0f) { return UpdateResult.Delete; }
|
||||
|
||||
if (hasSubEmitters)
|
||||
{
|
||||
@@ -261,7 +270,13 @@ namespace Barotrauma.Particles
|
||||
}
|
||||
}
|
||||
|
||||
if (!prefab.UseCollision) { return true; }
|
||||
if (collisionIgnoreTimer > 0f)
|
||||
{
|
||||
collisionIgnoreTimer -= deltaTime;
|
||||
if (collisionIgnoreTimer <= 0f) { currentHull ??= Hull.FindHull(position); }
|
||||
return UpdateResult.Normal;
|
||||
}
|
||||
if (!prefab.UseCollision) { return UpdateResult.Normal; }
|
||||
|
||||
if (HighQualityCollisionDetection)
|
||||
{
|
||||
@@ -278,17 +293,17 @@ namespace Barotrauma.Particles
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return UpdateResult.Normal;
|
||||
}
|
||||
|
||||
private bool CollisionUpdate()
|
||||
private UpdateResult CollisionUpdate()
|
||||
{
|
||||
if (currentHull == null)
|
||||
{
|
||||
Hull collidedHull = Hull.FindHull(position);
|
||||
if (collidedHull != null)
|
||||
{
|
||||
if (prefab.DeleteOnCollision) return false;
|
||||
if (prefab.DeleteOnCollision) return UpdateResult.Delete;
|
||||
OnWallCollisionOutside(collidedHull);
|
||||
}
|
||||
}
|
||||
@@ -298,12 +313,12 @@ namespace Barotrauma.Particles
|
||||
Vector2 collisionNormal = Vector2.Zero;
|
||||
if (velocity.Y < 0.0f && position.Y - prefab.CollisionRadius * size.Y < hullRect.Y - hullRect.Height)
|
||||
{
|
||||
if (prefab.DeleteOnCollision) { return false; }
|
||||
if (prefab.DeleteOnCollision) { return UpdateResult.Delete; }
|
||||
collisionNormal = new Vector2(0.0f, 1.0f);
|
||||
}
|
||||
else if (velocity.Y > 0.0f && position.Y + prefab.CollisionRadius * size.Y > hullRect.Y)
|
||||
{
|
||||
if (prefab.DeleteOnCollision) { return false; }
|
||||
if (prefab.DeleteOnCollision) { return UpdateResult.Delete; }
|
||||
collisionNormal = new Vector2(0.0f, -1.0f);
|
||||
}
|
||||
|
||||
@@ -328,12 +343,12 @@ namespace Barotrauma.Particles
|
||||
|
||||
if (velocity.X < 0.0f && position.X - prefab.CollisionRadius * size.X < hullRect.X)
|
||||
{
|
||||
if (prefab.DeleteOnCollision) { return false; }
|
||||
if (prefab.DeleteOnCollision) { return UpdateResult.Delete; }
|
||||
collisionNormal = new Vector2(1.0f, 0.0f);
|
||||
}
|
||||
else if (velocity.X > 0.0f && position.X + prefab.CollisionRadius * size.X > hullRect.Right)
|
||||
{
|
||||
if (prefab.DeleteOnCollision) { return false; }
|
||||
if (prefab.DeleteOnCollision) { return UpdateResult.Delete; }
|
||||
collisionNormal = new Vector2(-1.0f, 0.0f);
|
||||
}
|
||||
|
||||
@@ -374,7 +389,7 @@ namespace Barotrauma.Particles
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return UpdateResult.Normal;
|
||||
}
|
||||
|
||||
private void ApplyDrag(float dragCoefficient, float deltaTime)
|
||||
@@ -389,10 +404,10 @@ namespace Barotrauma.Particles
|
||||
//TODO: some better way to handle particle drag
|
||||
//this doesn't work that well because the drag vector is only updated every 0.5 seconds, allowing the particle to accelerate way more than it should
|
||||
//(e.g. a falling particle can freely accelerate for 0.5 seconds before the drag takes effect)
|
||||
dragWait--;
|
||||
if (dragWait <= 0)
|
||||
dragWait-=deltaTime;
|
||||
if (dragWait <= 0f)
|
||||
{
|
||||
dragWait = 30;
|
||||
dragWait = 0.5f;
|
||||
|
||||
float speed = velocity.Length();
|
||||
|
||||
|
||||
@@ -115,12 +115,12 @@ namespace Barotrauma.Particles
|
||||
Prefabs.RemoveByFile(configFile);
|
||||
}
|
||||
|
||||
public Particle CreateParticle(string prefabName, Vector2 position, float angle, float speed, Hull hullGuess = null)
|
||||
public Particle CreateParticle(string prefabName, Vector2 position, float angle, float speed, Hull hullGuess = null, float collisionIgnoreTimer = 0f)
|
||||
{
|
||||
return CreateParticle(prefabName, position, new Vector2((float)Math.Cos(angle), (float)-Math.Sin(angle)) * speed, angle, hullGuess);
|
||||
return CreateParticle(prefabName, position, new Vector2((float)Math.Cos(angle), (float)-Math.Sin(angle)) * speed, angle, hullGuess, collisionIgnoreTimer);
|
||||
}
|
||||
|
||||
public Particle CreateParticle(string prefabName, Vector2 position, Vector2 velocity, float rotation = 0.0f, Hull hullGuess = null)
|
||||
public Particle CreateParticle(string prefabName, Vector2 position, Vector2 velocity, float rotation = 0.0f, Hull hullGuess = null, float collisionIgnoreTimer = 0f)
|
||||
{
|
||||
ParticlePrefab prefab = FindPrefab(prefabName);
|
||||
|
||||
@@ -130,10 +130,10 @@ namespace Barotrauma.Particles
|
||||
return null;
|
||||
}
|
||||
|
||||
return CreateParticle(prefab, position, velocity, rotation, hullGuess);
|
||||
return CreateParticle(prefab, position, velocity, rotation, hullGuess, collisionIgnoreTimer: collisionIgnoreTimer);
|
||||
}
|
||||
|
||||
public Particle CreateParticle(ParticlePrefab prefab, Vector2 position, Vector2 velocity, float rotation = 0.0f, Hull hullGuess = null, bool drawOnTop = false)
|
||||
public Particle CreateParticle(ParticlePrefab prefab, Vector2 position, Vector2 velocity, float rotation = 0.0f, Hull hullGuess = null, bool drawOnTop = false, float collisionIgnoreTimer = 0f)
|
||||
{
|
||||
if (particleCount >= MaxParticles || prefab == null || prefab.Sprites.Count == 0) { return null; }
|
||||
|
||||
@@ -149,7 +149,7 @@ namespace Barotrauma.Particles
|
||||
|
||||
if (particles[particleCount] == null) particles[particleCount] = new Particle();
|
||||
|
||||
particles[particleCount].Init(prefab, position, velocity, rotation, hullGuess, drawOnTop);
|
||||
particles[particleCount].Init(prefab, position, velocity, rotation, hullGuess, drawOnTop, collisionIgnoreTimer);
|
||||
|
||||
particleCount++;
|
||||
|
||||
@@ -181,10 +181,10 @@ namespace Barotrauma.Particles
|
||||
|
||||
for (int i = 0; i < particleCount; i++)
|
||||
{
|
||||
bool remove = false;
|
||||
bool remove;
|
||||
try
|
||||
{
|
||||
remove = !particles[i].Update(deltaTime);
|
||||
remove = particles[i].Update(deltaTime) == Particle.UpdateResult.Delete;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -476,6 +476,11 @@ namespace Barotrauma
|
||||
#endif
|
||||
}
|
||||
|
||||
public static bool IsAltDown()
|
||||
{
|
||||
return KeyDown(Keys.LeftAlt) || KeyDown(Keys.RightAlt);
|
||||
}
|
||||
|
||||
public static void Update(double deltaTime)
|
||||
{
|
||||
timeSinceClick += deltaTime;
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Barotrauma
|
||||
|
||||
private GUIButton loadGameButton, deleteMpSaveButton;
|
||||
|
||||
public Action<SubmarineInfo, string, string> StartNewGame;
|
||||
public Action<SubmarineInfo, string, string, CampaignSettings> StartNewGame;
|
||||
public Action<string> LoadGame;
|
||||
|
||||
private enum CategoryFilter { All = 0, Vanilla = 1, Custom = 2 };
|
||||
@@ -40,6 +40,8 @@ namespace Barotrauma
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public GUITickBox EnableRadiationToggle { get; set; }
|
||||
|
||||
private readonly bool isMultiplayer;
|
||||
|
||||
@@ -171,6 +173,16 @@ namespace Barotrauma
|
||||
string savePath = SaveUtil.CreateSavePath(isMultiplayer ? SaveUtil.SaveType.Multiplayer : SaveUtil.SaveType.Singleplayer, saveNameBox.Text);
|
||||
bool hasRequiredContentPackages = selectedSub.RequiredContentPackagesInstalled;
|
||||
|
||||
CampaignSettings settings = new CampaignSettings();
|
||||
if (isMultiplayer)
|
||||
{
|
||||
settings.RadiationEnabled = GameMain.NetLobbyScreen.IsRadiationEnabled();
|
||||
}
|
||||
else
|
||||
{
|
||||
settings.RadiationEnabled = EnableRadiationToggle?.Selected ?? false;
|
||||
}
|
||||
|
||||
if (selectedSub.HasTag(SubmarineTag.Shuttle) || !hasRequiredContentPackages)
|
||||
{
|
||||
if (!hasRequiredContentPackages)
|
||||
@@ -184,7 +196,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (GUIMessageBox.MessageBoxes.Count == 0)
|
||||
{
|
||||
StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text);
|
||||
StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text, settings);
|
||||
if (isMultiplayer)
|
||||
{
|
||||
CoroutineManager.StartCoroutine(WaitForCampaignSetup(), "WaitForCampaignSetup");
|
||||
@@ -204,7 +216,7 @@ namespace Barotrauma
|
||||
|
||||
msgBox.Buttons[0].OnClicked = (button, obj) =>
|
||||
{
|
||||
StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text);
|
||||
StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text, settings);
|
||||
if (isMultiplayer)
|
||||
{
|
||||
CoroutineManager.StartCoroutine(WaitForCampaignSetup(), "WaitForCampaignSetup");
|
||||
@@ -219,7 +231,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text);
|
||||
StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text, settings);
|
||||
if (isMultiplayer)
|
||||
{
|
||||
CoroutineManager.StartCoroutine(WaitForCampaignSetup(), "WaitForCampaignSetup");
|
||||
@@ -230,8 +242,7 @@ namespace Barotrauma
|
||||
}
|
||||
};
|
||||
|
||||
InitialMoneyText = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1f), buttonContainer.RectTransform), "",
|
||||
font: isMultiplayer ? GUI.Style.SmallFont : GUI.Style.Font, textColor: GUI.Style.Green)
|
||||
InitialMoneyText = new GUITextBlock(new RectTransform(new Vector2(isMultiplayer ? 0.6f : 0.3f, 1f), buttonContainer.RectTransform), "", font: isMultiplayer ? GUI.Style.SmallFont : GUI.Style.Font, textColor: GUI.Style.Green)
|
||||
{
|
||||
TextGetter = () =>
|
||||
{
|
||||
@@ -254,6 +265,15 @@ namespace Barotrauma
|
||||
|
||||
if (!isMultiplayer)
|
||||
{
|
||||
if (MapGenerationParams.Instance.RadiationParams != null)
|
||||
{
|
||||
EnableRadiationToggle = new GUITickBox(new RectTransform(new Vector2(0.3f, 1f), buttonContainer.RectTransform), TextManager.Get("CampaignOption.EnableRadiation"), font: GUI.Style.Font)
|
||||
{
|
||||
Selected = true,
|
||||
ToolTip = TextManager.Get("campaignoption.enableradiation.tooltip")
|
||||
};
|
||||
}
|
||||
|
||||
var disclaimerBtn = new GUIButton(new RectTransform(new Vector2(1.0f, 0.8f), rightColumn.RectTransform, Anchor.TopRight) { AbsoluteOffset = new Point(5) }, style: "GUINotificationButton")
|
||||
{
|
||||
IgnoreLayoutGroups = true,
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace Barotrauma
|
||||
campaign.Map.OnLocationSelected += SelectLocation;
|
||||
campaign.Map.OnMissionSelected += (connection, mission) =>
|
||||
{
|
||||
missionList.Select(mission);
|
||||
missionList?.Select(mission);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -306,7 +306,7 @@ namespace Barotrauma
|
||||
{
|
||||
var map = GameMain.GameSession?.Map;
|
||||
if (map == null) { return; }
|
||||
if (selectedLocation != null && selectedLocation == map.CurrentDisplayLocation)
|
||||
if (selectedLocation != null && selectedLocation == GameMain.GameSession.Campaign.GetCurrentDisplayLocation())
|
||||
{
|
||||
map.SelectLocation(-1);
|
||||
}
|
||||
@@ -394,6 +394,42 @@ namespace Barotrauma
|
||||
var difficultyLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), textContent.RectTransform),
|
||||
TextManager.Get("LevelDifficulty"), font: GUI.SubHeadingFont, textAlignment: Alignment.CenterLeft);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), difficultyLabel.RectTransform), ((int)connection.LevelData.Difficulty) + " %", textAlignment: Alignment.CenterRight);
|
||||
|
||||
if (connection.LevelData.HasBeaconStation)
|
||||
{
|
||||
var beaconStationContent = new GUILayoutGroup(new RectTransform(biomeLabel.RectTransform.NonScaledSize, textContent.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
|
||||
string style = connection.LevelData.IsBeaconActive ? "BeaconStationActive" : "BeaconStationInactive";
|
||||
var icon = new GUIImage(new RectTransform(new Point((int)(beaconStationContent.Rect.Height * 1.2f)), beaconStationContent.RectTransform),
|
||||
style, scaleToFit: true)
|
||||
{
|
||||
Color = MapGenerationParams.Instance.IndicatorColor,
|
||||
HoverColor = Color.Lerp(MapGenerationParams.Instance.IndicatorColor, Color.White, 0.5f),
|
||||
ToolTip = TextManager.Get(connection.LevelData.IsBeaconActive ? "BeaconStationActiveTooltip" : "BeaconStationInactiveTooltip")
|
||||
};
|
||||
new GUITextBlock(new RectTransform(Vector2.One, beaconStationContent.RectTransform),
|
||||
TextManager.Get("submarinetype.beaconstation"), font: GUI.SubHeadingFont, textAlignment: Alignment.CenterLeft)
|
||||
{
|
||||
Padding = Vector4.Zero,
|
||||
ToolTip = icon.ToolTip
|
||||
};
|
||||
}
|
||||
if (connection.LevelData.HasHuntingGrounds)
|
||||
{
|
||||
var huntingGroundsContent = new GUILayoutGroup(new RectTransform(biomeLabel.RectTransform.NonScaledSize, textContent.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
|
||||
var icon = new GUIImage(new RectTransform(new Point((int)(huntingGroundsContent.Rect.Height * 1.5f)), huntingGroundsContent.RectTransform),
|
||||
"HuntingGrounds", scaleToFit: true)
|
||||
{
|
||||
Color = MapGenerationParams.Instance.IndicatorColor,
|
||||
HoverColor = Color.Lerp(MapGenerationParams.Instance.IndicatorColor, Color.White, 0.5f),
|
||||
ToolTip = TextManager.Get("HuntingGroundsTooltip")
|
||||
};
|
||||
new GUITextBlock(new RectTransform(Vector2.One, huntingGroundsContent.RectTransform),
|
||||
TextManager.Get("missionname.huntinggrounds"), font: GUI.SubHeadingFont, textAlignment: Alignment.CenterLeft)
|
||||
{
|
||||
Padding = Vector4.Zero,
|
||||
ToolTip = icon.ToolTip
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
missionList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.4f), content.RectTransform))
|
||||
@@ -402,7 +438,7 @@ namespace Barotrauma
|
||||
};
|
||||
|
||||
SelectedLevel = connection?.LevelData;
|
||||
Location currentDisplayLocation = Campaign.CurrentDisplayLocation;
|
||||
Location currentDisplayLocation = Campaign.GetCurrentDisplayLocation();
|
||||
if (connection != null && connection.Locations.Contains(currentDisplayLocation))
|
||||
{
|
||||
List<Mission> availableMissions = currentDisplayLocation.GetMissionsInConnection(connection).ToList();
|
||||
@@ -437,11 +473,44 @@ namespace Barotrauma
|
||||
SelectedColor = MapGenerationParams.Instance.IndicatorColor,
|
||||
HoverColor = Color.Lerp(MapGenerationParams.Instance.IndicatorColor, Color.White, 0.5f)
|
||||
};
|
||||
missionName.Padding = new Vector4(missionName.Padding.X + icon.Rect.Width * 1.5f, missionName.Padding.Y, missionName.Padding.Z, missionName.Padding.W);
|
||||
icon.RectTransform.IsFixedSize = true;
|
||||
|
||||
GUILayoutGroup difficultyIndicatorGroup = null;
|
||||
if (mission.Difficulty.HasValue)
|
||||
{
|
||||
difficultyIndicatorGroup = new GUILayoutGroup(new RectTransform(Vector2.One * 0.9f, missionName.RectTransform, anchor: Anchor.CenterRight, scaleBasis: ScaleBasis.Smallest) { AbsoluteOffset = new Point((int)missionName.Padding.Z, 0) },
|
||||
isHorizontal: true, childAnchor: Anchor.CenterRight)
|
||||
{
|
||||
AbsoluteSpacing = 1,
|
||||
UserData = "difficulty"
|
||||
};
|
||||
var difficultyColor = mission.GetDifficultyColor();
|
||||
for (int i = 0; i < mission.Difficulty; i++)
|
||||
{
|
||||
new GUIImage(new RectTransform(Vector2.One, difficultyIndicatorGroup.RectTransform, scaleBasis: ScaleBasis.Smallest) { IsFixedSize = true }, "DifficultyIndicator", scaleToFit: true)
|
||||
{
|
||||
Color = difficultyColor * 0.5f,
|
||||
SelectedColor = difficultyColor,
|
||||
HoverColor = Color.Lerp(difficultyColor, Color.White, 0.5f)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
float extraPadding = 0.5f * icon.Rect.Width;
|
||||
float extraZPadding = difficultyIndicatorGroup != null ? mission.Difficulty.Value * (difficultyIndicatorGroup.Children.First().Rect.Width + difficultyIndicatorGroup.AbsoluteSpacing) : 0;
|
||||
missionName.Padding = new Vector4(missionName.Padding.X + icon.Rect.Width + extraPadding,
|
||||
missionName.Padding.Y,
|
||||
missionName.Padding.Z + extraZPadding + extraPadding,
|
||||
missionName.Padding.W);
|
||||
missionName.CalculateHeightFromText();
|
||||
}
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
|
||||
TextManager.GetWithVariable("missionreward", "[reward]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", mission.Reward)), wrap: true);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), mission.Description, wrap: true);
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), mission.GetMissionRewardText(), wrap: true, parseRichText: true);
|
||||
|
||||
string reputationText = mission.GetReputationRewardText(mission.Locations[0]);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), reputationText, wrap: true, parseRichText: true);
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), mission.Description, wrap: true, parseRichText: true);
|
||||
}
|
||||
missionPanel.RectTransform.MinSize = new Point(0, (int)(missionTextContent.Children.Sum(c => c.Rect.Height) / missionTextContent.RectTransform.RelativeSize.Y) + GUI.IntScale(20));
|
||||
foreach (GUIComponent child in missionTextContent.Children)
|
||||
@@ -454,6 +523,10 @@ namespace Barotrauma
|
||||
missionPanel.OnAddedToGUIUpdateList = (c) =>
|
||||
{
|
||||
missionTextContent.Children.ForEach(child => child.State = c.State);
|
||||
if (missionTextContent.FindChild("difficulty", recursive: true) is GUILayoutGroup group)
|
||||
{
|
||||
group.State = c.State;
|
||||
}
|
||||
};
|
||||
|
||||
if (mission != availableMissions.Last())
|
||||
@@ -491,7 +564,25 @@ namespace Barotrauma
|
||||
StartButton = new GUIButton(new RectTransform(new Vector2(0.5f, 0.1f), content.RectTransform),
|
||||
TextManager.Get("StartCampaignButton"), style: "GUIButtonLarge")
|
||||
{
|
||||
OnClicked = (GUIButton btn, object obj) => { StartRound?.Invoke(); return true; },
|
||||
OnClicked = (GUIButton btn, object obj) =>
|
||||
{
|
||||
if (missionList.Content.Children.Any(c => c.UserData is Mission) && !(missionList.SelectedData is Mission))
|
||||
{
|
||||
var noMissionVerification = new GUIMessageBox(string.Empty, TextManager.Get("nomissionprompt"), new string[] { TextManager.Get("yes"), TextManager.Get("no") });
|
||||
noMissionVerification.Buttons[0].OnClicked = (btn, userdata) =>
|
||||
{
|
||||
StartRound?.Invoke();
|
||||
noMissionVerification.Close();
|
||||
return true;
|
||||
};
|
||||
noMissionVerification.Buttons[1].OnClicked = noMissionVerification.Close;
|
||||
}
|
||||
else
|
||||
{
|
||||
StartRound?.Invoke();
|
||||
}
|
||||
return true;
|
||||
},
|
||||
Enabled = true,
|
||||
Visible = Campaign.AllowedToEndRound()
|
||||
};
|
||||
@@ -507,6 +598,11 @@ namespace Barotrauma
|
||||
|
||||
public void SelectTab(CampaignMode.InteractionType tab)
|
||||
{
|
||||
if (Campaign.ShowCampaignUI || (Campaign.ForceMapUI && tab == CampaignMode.InteractionType.Map))
|
||||
{
|
||||
HintManager.OnShowCampaignInterface(tab);
|
||||
}
|
||||
|
||||
selectedTab = tab;
|
||||
for (int i = 0; i < tabs.Length; i++)
|
||||
{
|
||||
|
||||
@@ -711,7 +711,7 @@ namespace Barotrauma.CharacterEditor
|
||||
}
|
||||
}
|
||||
// Camera
|
||||
Cam.MoveCamera((float)deltaTime, allowMove: false);
|
||||
Cam.MoveCamera((float)deltaTime, allowMove: false, allowZoom: GUI.MouseOn == null);
|
||||
Vector2 targetPos = character.WorldPosition;
|
||||
if (PlayerInput.MidButtonHeld())
|
||||
{
|
||||
@@ -766,7 +766,7 @@ namespace Barotrauma.CharacterEditor
|
||||
if (editLimbs && !spriteSheetRect.Contains(PlayerInput.MousePosition) &&
|
||||
MathUtils.RectangleContainsPoint(GetLimbPhysicRect(limb), PlayerInput.MousePosition)) { return CursorState.Hand; }
|
||||
// spritesheet
|
||||
if (GetLimbSpritesheetRect(limb).Contains(PlayerInput.MousePosition)) { return CursorState.Hand; }
|
||||
if (showSpritesheet && GetLimbSpritesheetRect(limb).Contains(PlayerInput.MousePosition)) { return CursorState.Hand; }
|
||||
}
|
||||
return CursorState.Default;
|
||||
}
|
||||
@@ -1761,9 +1761,9 @@ namespace Barotrauma.CharacterEditor
|
||||
#endif
|
||||
// Add to the selected content package
|
||||
contentPackage.AddFile(configFilePath, ContentType.Character);
|
||||
Barotrauma.IO.Validation.DevException = true;
|
||||
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
|
||||
contentPackage.Save(contentPackage.Path);
|
||||
Barotrauma.IO.Validation.DevException = false;
|
||||
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
|
||||
DebugConsole.NewMessage(GetCharacterEditorTranslation("ContentPackageSaved").Replace("[path]", contentPackage.Path));
|
||||
|
||||
// Ragdoll
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user