Unstable 0.1300.0.3

This commit is contained in:
Markus Isberg
2021-03-25 15:40:24 +02:00
parent 874616027b
commit 58c50a235d
136 changed files with 2486 additions and 1008 deletions

View File

@@ -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, Rectangle? overrideMouseOn = null)
public void MoveCamera(float deltaTime, bool allowMove = true, bool allowZoom = true)
{
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)
@@ -294,7 +288,7 @@ namespace Barotrauma
}
}
if (allowZoom && (GUI.MouseOn == null || (overrideMouseOn?.Contains(PlayerInput.MousePosition) ?? false)))
if (allowZoom)
{
Vector2 mouseInWorld = ScreenToWorld(PlayerInput.MousePosition);
Vector2 diffViewCenter;
@@ -318,14 +312,14 @@ 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.6f);
offset.Y = -offset.Y / (Resolution.Y * 0.6f);
if (offset.LengthSquared() > 1.0f) offset.Normalize();
offset *= offsetAmount;
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;
}
@@ -343,25 +337,21 @@ namespace Barotrauma
previousOffset = offset;
}
//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;
//TODO: remove magic numbers
float minMultiplier = OffsetAmount > 0f ? ((DefaultZoom * 8f * 250f) / OffsetAmount) : 15f;
//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);
//how much to zoom out (0.0 = Default zoom, 1.0 = zoom completely out)
float zoomOutAmount = GetZoomAmount(offset);
float newZoom = MathHelper.Lerp(DefaultZoom, MinZoom * minMultiplier, zoomOutAmount) * globalZoomScale;
//zoom in further if zoomOutAmount is low and resolution is lower than reference
newZoom *= MathHelper.Lerp(0.5f * (1f - Math.Min(globalZoomScale, 1f)), 0f, zoomOutAmount) + 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;
}
@@ -426,7 +416,7 @@ namespace Barotrauma
private float GetZoomAmount(Vector2 offset)
{
return Math.Min(offset.Length() / 1000.0f, 1.0f);
return Math.Min(offset.Length() / Math.Max(1f, OffsetAmount), 1.0f);
}
public float GetZoomAmountFromPrevious()

View File

@@ -224,8 +224,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;
@@ -382,19 +381,44 @@ 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.GameSession?.GameMode is CampaignMode && GameMain.NetworkMember.RespawnManager != null && Level.Loaded?.Type != LevelData.LevelType.Outpost)
{
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;
}
PlaySound(CharacterSound.SoundType.Die);
}

View File

@@ -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)

View File

@@ -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
{
@@ -516,6 +518,7 @@ namespace Barotrauma
if (!character.IsDead) { Controlled = character; }
GameMain.LightManager.LosEnabled = true;
GameMain.LightManager.LosAlpha = 1f;
character.memInput.Clear();
character.memState.Clear();

View File

@@ -997,8 +997,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 =

View File

@@ -1247,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);
});

View File

@@ -14,10 +14,7 @@ namespace Barotrauma
base.State = value;
if (state == HostagesKilledState && !string.IsNullOrEmpty(hostagesKilledMessage))
{
new GUIMessageBox(string.Empty, hostagesKilledMessage, buttons: new string[0], type: GUIMessageBox.Type.InGame, icon: Prefab.Icon, parseRichText: true)
{
IconColor = Prefab.IconColor
};
CreateMessageBox(string.Empty, hostagesKilledMessage);
}
}
}
@@ -44,6 +41,15 @@ namespace Barotrauma
{
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))
{

View File

@@ -1,4 +1,5 @@
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -7,6 +8,19 @@ 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));
@@ -71,6 +85,7 @@ namespace Barotrauma
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

View File

@@ -1,7 +1,5 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Linq;
namespace Barotrauma

View File

@@ -565,34 +565,26 @@ namespace Barotrauma
}
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);
string[] strings = new string[]
{
$"GUI.Scale: {Scale}",
$"GUI.xScale: {xScale}",
$"GUI.yScale: {yScale}",
$"RelativeHorizontalAspectRatio: {RelativeHorizontalAspectRatio}",
$"RelativeVerticalAspectRatio: {RelativeVerticalAspectRatio}",
$"Cam.Zoom: {Screen.Selected.Cam?.Zoom ?? 0f}",
};
int padding = IntScale(10);
int yPos = padding;
DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - (int)guiScaleStringSize.X - padding, yPos), guiScaleString, Color.LightGreen, Color.Black, 0, SmallFont);
yPos += (int)guiScaleStringSize.Y + padding / 2;
foreach (string str in strings)
{
Vector2 stringSize = SmallFont.MeasureString(str);
DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - (int)guixScaleStringSize.X - padding, yPos), guixScaleString, Color.LightGreen, Color.Black, 0, SmallFont);
yPos += (int)guixScaleStringSize.Y + padding / 2;
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;
}
}
}

View File

@@ -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
@@ -891,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;

View File

@@ -261,7 +261,7 @@ namespace Barotrauma
public readonly List<RichTextData> RichTextData = null;
private readonly bool hasColorHighlight = false;
public bool HasColorHighlight => RichTextData != null;
public struct ClickableArea
{
@@ -295,7 +295,6 @@ namespace Barotrauma
if (parseRichText)
{
RichTextData = Barotrauma.RichTextData.GetRichTextData(text, out text);
hasColorHighlight = RichTextData != null;
}
//if the text is in chinese/korean/japanese and we're not using a CJK-compatible font,
@@ -326,7 +325,6 @@ namespace Barotrauma
: this(rectT, text, textColor, font, textAlignment, wrap, style, color, playerInput)
{
this.RichTextData = richTextData;
hasColorHighlight = richTextData != null;
}
public void CalculateHeightFromText(int padding = 0, bool removeExtraSpacing = false)
@@ -634,7 +632,7 @@ namespace Barotrauma
currentTextColor = selectedTextColor;
}
if (!hasColorHighlight)
if (!HasColorHighlight)
{
string textToShow = Censor ? censoredText : (Wrap ? wrappedText : text);
Color colorToShow = currentTextColor * (currentTextColor.A / 255.0f);

View File

@@ -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; }

View File

@@ -18,7 +18,7 @@ namespace Barotrauma
private static UISprite spectateIcon, disconnectedIcon;
private static Sprite ownerIcon, moderatorIcon;
private enum InfoFrameTab { Crew, Mission, Reputation, MyCharacter, Traitor };
private enum InfoFrameTab { Crew, Mission, Reputation, MyCharacter, Traitor, Submarine };
private static InfoFrameTab selectedTab;
private GUIFrame infoFrame, contentFrame;
@@ -181,77 +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");
Vector2 contentFrameSize = selectedTab switch
//this used to be a switch expression but i changed it because it killed enc :(
Vector2 contentFrameSize;
switch (selectedTab)
{
InfoFrameTab.MyCharacter => new Vector2(0.33f, 0.5f),
_ => new Vector2(0.33f, 0.667f)
};
case InfoFrameTab.MyCharacter:
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.025f, 0.12f) });
var innerLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.958f, 0.943f), contentFrame.RectTransform, Anchor.TopCenter, Pivot.TopCenter) { AbsoluteOffset = new Point(0, GUI.IntScale(17.5f)) })
{
RelativeSpacing = 0.01f,
Stretch = true
};
var buttonArea = new GUILayoutGroup(new RectTransform(new Point(innerLayoutGroup.Rect.Width, GUI.IntScale(25f)), parent: innerLayoutGroup.RectTransform), 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
};
var buttonArea = new GUILayoutGroup(new RectTransform(new Vector2(0.07f, 1f), parent: horizontalLayoutGroup.RectTransform), isHorizontal: false)
{
AbsoluteSpacing = GUI.IntScale(5f)
};
var innerLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.92f, 1f), horizontalLayoutGroup.RectTransform))
{
RelativeSpacing = 0.01f,
Stretch = true
};
float absoluteSpacing = innerLayoutGroup.RelativeSpacing * innerLayoutGroup.Rect.Height;
int multiplier = GameMain.GameSession?.GameMode is CampaignMode ? 2 : 1;
int infoFrameHolderHeight = Math.Min((int)(0.926f * innerLayoutGroup.Rect.Height), (int)(innerLayoutGroup.Rect.Height - multiplier * (GUI.IntScale(25f) + absoluteSpacing)));
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);
var crewButton = new GUIButton(new RectTransform(new Vector2(0.245f, 1.0f), buttonArea.RectTransform), TextManager.Get("Crew"), style: "GUITabButton")
GUIButton createTabButton(InfoFrameTab tab, string textTag)
{
UserData = InfoFrameTab.Crew,
OnClicked = SelectInfoFrameTab
};
tabButtons.Add(crewButton);
var newButton = new GUIButton(new RectTransform(Vector2.One, buttonArea.RectTransform, scaleBasis: ScaleBasis.BothWidth), style: $"InfoFrameTabButton.{tab}")
{
UserData = tab,
ToolTip = TextManager.Get(textTag),
OnClicked = SelectInfoFrameTab
};
tabButtons.Add(newButton);
return newButton;
}
var missionButton = new GUIButton(new RectTransform(new Vector2(0.245f, 1.0f), buttonArea.RectTransform), TextManager.Get("Mission"), style: "GUITabButton")
{
UserData = InfoFrameTab.Mission,
OnClicked = SelectInfoFrameTab
};
tabButtons.Add(missionButton);
var crewButton = createTabButton(InfoFrameTab.Crew, "crew");
var missionButton = createTabButton(InfoFrameTab.Mission, "mission");
if (GameMain.GameSession?.GameMode is CampaignMode campaignMode)
{
var reputationButton = new GUIButton(new RectTransform(new Vector2(0.245f, 1.0f), buttonArea.RectTransform), TextManager.Get("reputation"), style: "GUITabButton")
{
UserData = InfoFrameTab.Reputation,
OnClicked = SelectInfoFrameTab
};
tabButtons.Add(reputationButton);
var reputationButton = createTabButton(InfoFrameTab.Reputation, "reputation");
var balanceFrame = new GUIFrame(new RectTransform(new Point(buttonArea.RectTransform.NonScaledSize.X, (int)(buttonArea.RectTransform.NonScaledSize.Y * 1.5f)), parent: innerLayoutGroup.RectTransform), style: "InnerFrame");
var submarineButton = createTabButton(InfoFrameTab.Submarine, "submarine");
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))
};
}
bool isTraitor = GameMain.Client?.Character?.IsTraitor ?? false;
if (isTraitor && GameMain.Client.TraitorMission != null)
else
{
var traitorButton = new GUIButton(new RectTransform(new Vector2(0.245f, 1.0f), buttonArea.RectTransform), TextManager.Get("tabmenu.traitor"), style: "GUITabButton")
bool isTraitor = GameMain.Client?.Character?.IsTraitor ?? false;
if (isTraitor && GameMain.Client.TraitorMission != null)
{
UserData = InfoFrameTab.Traitor,
OnClicked = SelectInfoFrameTab
};
tabButtons.Add(traitorButton);
var traitorButton = createTabButton(InfoFrameTab.Traitor, "tabmenu.traitor");
}
}
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");
}
}
@@ -288,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;
@@ -879,13 +884,16 @@ namespace Barotrauma
UserData = line
};
textBlock.CalculateHeightFromText();
foreach (var data in textBlock.RichTextData)
if (textBlock.HasColorHighlight)
{
textBlock.ClickableAreas.Add(new GUITextBlock.ClickableArea()
foreach (var data in textBlock.RichTextData)
{
Data = data,
OnClick = GameMain.NetLobbyScreen.SelectPlayer
});
textBlock.ClickableAreas.Add(new GUITextBlock.ClickableArea()
{
Data = data,
OnClick = GameMain.NetLobbyScreen.SelectPlayer
});
}
}
}
}
@@ -930,27 +938,38 @@ namespace Barotrauma
foreach (Mission mission in GameMain.GameSession.Missions)
{
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);
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]);
var missionNameRichTextData = RichTextData.GetRichTextData(mission.Name, out string missionNameString);
var missionDescriptionRichTextData = RichTextData.GetRichTextData(mission.Description, out string missionDescriptionString);
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);
missionDescriptionString = ToolBox.WrapText(missionDescriptionString, missionTextGroup.Rect.Width, GUI.Font);
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);
missionDescriptionHolder.RectTransform.NonScaledSize = new Point(missionDescriptionHolder.RectTransform.NonScaledSize.X, (int)(missionNameSize.Y + missionDescriptionSize.Y + missionRewardSize.Y + missionReputationSize.Y));
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)
@@ -960,10 +979,38 @@ namespace Barotrauma
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, CanBeFocused = false };
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);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionRewardRichTextData, missionRewardString);
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);
}
@@ -1004,5 +1051,84 @@ 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)
{
var upgradeRootLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.57f), paddedFrame.RectTransform, Anchor.BottomLeft, Pivot.BottomLeft), 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;
};
}
}
}
}

View File

@@ -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);

View File

@@ -1229,13 +1229,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"
};

View File

@@ -168,6 +168,7 @@ namespace Barotrauma
{
chatBox.ToggleButton = new GUIButton(new RectTransform(new Point((int)(182f * GUI.Scale * 0.4f), (int)(99f * GUI.Scale * 0.4f)), chatBox.GUIFrame.Parent.RectTransform), style: "ChatToggleButton")
{
ToolTip = TextManager.Get("chat"),
ClampMouseRectToParent = false
};
chatBox.ToggleButton.RectTransform.AbsoluteOffset = new Point(0, HUDLayoutSettings.ChatBoxArea.Height - chatBox.ToggleButton.Rect.Height);
@@ -289,19 +290,7 @@ namespace Barotrauma
new RectTransform(crewListEntrySize, parent: crewList.Content.RectTransform, anchor: Anchor.TopRight),
style: "CrewListBackground")
{
UserData = character,
OnSecondaryClicked = (comp, data) =>
{
if (data == null) { return false; }
var client = GameMain.NetworkMember?.ConnectedClients?.Find(c => c.Character == data);
if (client != null)
{
CreateModerationContextMenu(PlayerInput.MousePosition.ToPoint(), client);
return true;
}
return false;
}
UserData = character
};
var iconRelativeWidth = (float)crewListEntrySize.Y / background.Rect.Width;
@@ -379,7 +368,17 @@ namespace Barotrauma
background.RectTransform),
style: null)
{
UserData = character
UserData = character,
OnSecondaryClicked = (comp, data) =>
{
if (data == null) { return false; }
if (GameMain.NetworkMember?.ConnectedClients?.Find(c => c.Character == data) is Client client)
{
CreateModerationContextMenu(PlayerInput.MousePosition.ToPoint(), client);
return true;
}
return false;
}
};
SetCharacterButtonTooltip(characterButton);
@@ -389,7 +388,6 @@ namespace Barotrauma
}
else
{
characterButton.CanBeFocused = false;
characterButton.CanBeSelected = false;
}
@@ -1089,7 +1087,8 @@ namespace Barotrauma
public void CreateModerationContextMenu(Point mousePos, Client client)
{
if (GUIContextMenu.CurrentContextMenu != null) { return; }
if (IsSinglePlayer || client == null || (!GameMain.Client?.PreviouslyConnectedClients?.Contains(client) ?? true)) { return; }
if (IsSinglePlayer || client == null || ((!GameMain.Client?.PreviouslyConnectedClients?.Contains(client)) ?? true)) { return; }
bool hasSteam = client.SteamID > 0 && SteamManager.IsInitialized,
canKick = GameMain.Client.HasPermission(ClientPermissions.Kick),
@@ -1638,7 +1637,8 @@ namespace Barotrauma
#if DEBUG
if (Character.Controlled == null) { return true; }
#endif
return Character.Controlled != null && characters.Any(c => c != Character.Controlled && c.CanHearCharacter(Character.Controlled));
return Character.Controlled != null &&
(characters.Any(c => c != Character.Controlled && c.CanHearCharacter(Character.Controlled)) || GetOrderableFriendlyNPCs().Any(c => c != Character.Controlled && c.CanHearCharacter(Character.Controlled)));
}
private Entity FindEntityContext()

View File

@@ -124,22 +124,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);
}
@@ -152,7 +152,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);
@@ -211,6 +211,7 @@ namespace Barotrauma
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;
}

View File

@@ -239,11 +239,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 +283,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,
@@ -327,7 +337,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 +345,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)
{
@@ -498,7 +508,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)

View File

@@ -298,11 +298,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
@@ -327,19 +336,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,
@@ -409,13 +412,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)
{
@@ -509,7 +512,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)

View File

@@ -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)
{

View File

@@ -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();

View File

@@ -40,56 +40,82 @@ namespace Barotrauma
private GUILayoutGroup topLeftButtonGroup;
private GUIButton crewListButton, commandButton, tabMenuButton;
private GUILayoutGroup TopLeftButtonGroup
{
get
{
if (topLeftButtonGroup == null)
{
topLeftButtonGroup = new GUILayoutGroup(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.ButtonAreaTop, GUI.Canvas), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
AbsoluteSpacing = HUDLayoutSettings.Padding,
CanBeFocused = false
};
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);
// Crew list button
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;
}
};
// Command interface button
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;
}
};
// Tab menu button
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();
}
};
}
return topLeftButtonGroup;
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()
@@ -101,20 +127,15 @@ namespace Barotrauma
if ((!(GameMode is CampaignMode campaign) || (!campaign.ForceMapUI && !campaign.ShowCampaignUI)) &&
!CoroutineManager.IsCoroutineRunning("LevelTransition") && !CoroutineManager.IsCoroutineRunning("SubmarineTransition"))
{
if (crewListButton != null)
if (topLeftButtonGroup == null)
{
crewListButton.Selected = CrewManager != null && CrewManager.IsCrewMenuOpen;
CreateTopLeftButtons();
}
if (commandButton != null)
{
commandButton.Selected = CrewManager.IsCommandInterfaceOpen;
commandButton.Enabled = CrewManager.CanIssueOrders;
}
if (tabMenuButton != null)
{
tabMenuButton.Selected = IsTabMenuOpen;
}
TopLeftButtonGroup.AddToGUIUpdateList();
crewListButton.Selected = CrewManager != null && CrewManager.IsCrewMenuOpen;
commandButton.Selected = CrewManager.IsCommandInterfaceOpen;
commandButton.Enabled = CrewManager.CanIssueOrders;
tabMenuButton.Selected = IsTabMenuOpen;
topLeftButtonGroup.AddToGUIUpdateList();
}
if (GameMain.NetworkMember != null)
@@ -130,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();
}
@@ -138,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();
}
@@ -167,6 +188,17 @@ 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)
{
GameMode?.Draw(spriteBatch);

View File

@@ -1,4 +1,5 @@
using Barotrauma.IO;
using Barotrauma.Extensions;
using Barotrauma.IO;
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using System;
@@ -21,41 +22,16 @@ namespace Barotrauma
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 Queue<HintInfo> HintQueue { get; } = new Queue<HintInfo>();
public struct HintInfo
{
public string Identifier { get; }
public string Text { get; }
public Sprite Icon { get; }
public Color? IconColor { get; }
public Action OnDisplay { get; }
public Action OnUpdate { get; }
public HintInfo(string hintIdentifier, string text, Sprite icon, Color? iconColor, Action onDisplay, Action onUpdate)
{
Identifier = hintIdentifier;
Text = text;
Icon = icon;
IconColor = iconColor;
OnDisplay = onDisplay;
OnUpdate = onUpdate;
}
}
private static HashSet<Hull> BallastHulls { get; } = new HashSet<Hull>();
public static void Init()
@@ -127,15 +103,6 @@ namespace Barotrauma
return;
}
}
else if (HintQueue.TryDequeue(out var hint))
{
ActiveHintMessageBox = new GUIMessageBox(hint.Identifier, hint.Text, hint.Icon);
if (hint.IconColor.HasValue) { ActiveHintMessageBox.IconColor = hint.IconColor.Value; }
OnUpdate = hint.OnUpdate;
ActiveHintMessageBox.InnerFrame.Flash(color: hint.IconColor ?? Color.Orange, flashDuration: 0.75f);
SoundPlayer.PlayUISound(GUISoundType.UIMessage);
hint.OnDisplay?.Invoke();
}
CheckIsInteracting();
CheckIfDivingGearOutOfOxygen();
@@ -166,21 +133,21 @@ namespace Barotrauma
// onstartedinteracting.brokenitem
if (item.Repairables.Any(r => item.ConditionPercentage < r.RepairThreshold))
{
if (EnqueueHint($"{hintIdentifierBase}.brokenitem")) { return; }
if (DisplayHint($"{hintIdentifierBase}.brokenitem")) { return; }
}
// onstartedinteracting.lootingisstealing
if (item.Submarine?.Info?.Type == SubmarineType.Outpost &&
item.ContainedItems.Any(i => i.SpawnedInOutpost))
{
if (EnqueueHint($"{hintIdentifierBase}.lootingisstealing")) { return; }
if (DisplayHint($"{hintIdentifierBase}.lootingisstealing")) { return; }
}
// onstartedinteracting.turretperiscope
if (item.HasTag("periscope") &&
item.GetConnectedComponents<Turret>().FirstOrDefault(t => t.Item.HasTag("turret")) is Turret)
{
if (EnqueueHint($"{hintIdentifierBase}.turretperiscope",
if (DisplayHint($"{hintIdentifierBase}.turretperiscope",
variableTags: new string[] { "[shootkey]", "[deselectkey]", },
variableValues: new string[] { GameMain.Config.KeyBindText(InputType.Shoot), GameMain.Config.KeyBindText(InputType.Deselect) }))
{ return; }
@@ -193,7 +160,7 @@ namespace Barotrauma
if (!hintIdentifier.StartsWith(hintIdentifierBase)) { continue; }
if (!HintTags.TryGetValue(hintIdentifier, out var hintTags)) { continue; }
if (!item.HasTag(hintTags)) { continue; }
if (EnqueueHint(hintIdentifier)) { return; }
if (DisplayHint(hintIdentifier)) { return; }
}
}
@@ -206,7 +173,7 @@ namespace Barotrauma
Character.Controlled.SelectedConstruction.OwnInventory?.AllItems is IEnumerable<Item> containedItems &&
containedItems.Count(i => i.HasTag("reactorfuel")) > 1)
{
if (EnqueueHint("onisinteracting.reactorwithextrarods")) { return; }
if (DisplayHint("onisinteracting.reactorwithextrarods")) { return; }
}
}
@@ -214,6 +181,7 @@ namespace Barotrauma
{
// 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; }
@@ -252,10 +220,15 @@ namespace Barotrauma
OnStartedControlling();
while (ActiveHintMessageBox != null)
{
yield return CoroutineStatus.Running;
}
if (!GameMain.GameSession.GameMode.IsSinglePlayer &&
GameMain.Config.VoiceSetting == GameSettings.VoiceMode.Disabled)
{
EnqueueHint("onroundstarted.voipdisabled", onUpdate: () =>
DisplayHint("onroundstarted.voipdisabled", onUpdate: () =>
{
if (GameMain.Config.VoiceSetting == GameSettings.VoiceMode.Disabled) { return; }
ActiveHintMessageBox.Close();
@@ -281,7 +254,6 @@ namespace Barotrauma
ActiveHintMessageBox = null;
}
OnUpdate = null;
HintQueue.Clear();
HintsIgnoredThisRound.Clear();
}
@@ -292,7 +264,7 @@ namespace Barotrauma
if (spottedCharacter == null || spottedCharacter.Removed || spottedCharacter.IsDead) { return; }
if (Character.Controlled.SelectedConstruction != sonar) { return; }
if (HumanAIController.IsFriendly(Character.Controlled, spottedCharacter)) { return; }
EnqueueHint("onsonarspottedenemy");
DisplayHint("onsonarspottedenemy");
}
public static void OnAfflictionDisplayed(Character character, List<Affliction> displayedAfflictions)
@@ -305,7 +277,8 @@ namespace Barotrauma
if (affliction.Prefab.IsBuff) { continue; }
if (affliction.Prefab == AfflictionPrefab.OxygenLow) { continue; }
if (affliction.Prefab == AfflictionPrefab.RadiationSickness && (GameMain.GameSession.Map?.Radiation?.IsEntityRadiated(character) ?? false)) { continue; }
EnqueueHint("onafflictiondisplayed",
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,
@@ -325,12 +298,13 @@ namespace Barotrauma
if (character != Character.Controlled) { return; }
if (character.SelectedConstruction != null || character.FocusedItem != null) { return; }
if (item == null || !item.IsShootable || !item.RequireAimToUse) { return; }
if (GUI.MouseOn != null) { 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; }
EnqueueHint(hintIdentifier,
DisplayHint(hintIdentifier,
variableTags: new string[1] { "[key]" },
variableValues: new string[1] { GameMain.Config.KeyBindText(InputType.Aim) },
onUpdate: () =>
@@ -347,14 +321,14 @@ namespace Barotrauma
if (!CanDisplayHints()) { return; }
if (character != Character.Controlled) { return; }
if (door == null || door.Stuck < 20.0f) { return; }
EnqueueHint("onweldingdoor");
DisplayHint("onweldingdoor");
}
public static void OnTryOpenStuckDoor(Character character)
{
if (!CanDisplayHints()) { return; }
if (character != Character.Controlled) { return; }
EnqueueHint("ontryopenstuckdoor");
DisplayHint("ontryopenstuckdoor");
}
public static void OnShowCampaignInterface(CampaignMode.InteractionType interactionType)
@@ -362,36 +336,34 @@ namespace Barotrauma
if (!CanDisplayHints()) { return; }
if (interactionType == CampaignMode.InteractionType.None) { return; }
string hintIdentifier = $"onshowcampaigninterface.{interactionType.ToString().ToLowerInvariant()}";
EnqueueHint(hintIdentifier,
onUpdate: () =>
{
DisplayHint(hintIdentifier, onUpdate: () =>
{
if (!(GameMain.GameSession?.Campaign is CampaignMode campaign) ||
(!campaign.ShowCampaignUI && !campaign.ForceMapUI) ||
campaign.CampaignUI?.SelectedTab != CampaignMode.InteractionType.Map)
{
ActiveHintMessageBox.Close();
}
});
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; }
EnqueueHint("onshowcommandinterface",
onUpdate: () =>
{
if (CrewManager.IsCommandInterfaceOpen) { return; }
ActiveHintMessageBox.Close();
});
DisplayHint("onshowcommandinterface", onUpdate: () =>
{
if (CrewManager.IsCommandInterfaceOpen) { return; }
ActiveHintMessageBox.Close();
});
}
public static void OnShowHealthInterface()
{
if (!CanDisplayHints()) { return; }
if (CharacterHealth.OpenHealthWindow == null) { return; }
EnqueueHint("onshowhealthinterface", onUpdate: () =>
DisplayHint("onshowhealthinterface", onUpdate: () =>
{
if (CharacterHealth.OpenHealthWindow != null) { return; }
ActiveHintMessageBox.Close();
@@ -408,7 +380,7 @@ namespace Barotrauma
if (!CanDisplayHints()) { return; }
if (character != Character.Controlled) { return; }
if (item == null || !item.SpawnedInOutpost || !item.StolenDuringRound) { return; }
EnqueueHint("onstoleitem", onUpdate: () =>
DisplayHint("onstoleitem", onUpdate: () =>
{
if (item == null || item.Removed || item.GetRootInventoryOwner() != character)
{
@@ -421,7 +393,7 @@ namespace Barotrauma
{
if (!CanDisplayHints()) { return; }
if (character != Character.Controlled || !character.LockHands) { return; }
EnqueueHint("onhandcuffed", onUpdate: () =>
DisplayHint("onhandcuffed", onUpdate: () =>
{
if (character != null && !character.Removed && character.LockHands) { return; }
ActiveHintMessageBox.Close();
@@ -434,7 +406,7 @@ namespace Barotrauma
if (reactor == null) { return; }
if (reactor.Item.Submarine?.Info?.Type != SubmarineType.Player || reactor.Item.Submarine.TeamID != Character.Controlled.TeamID) { return; }
if (!HasValidJob("engineer")) { return; }
EnqueueHint("onreactoroutoffuel", onUpdate: () =>
DisplayHint("onreactoroutoffuel", onUpdate: () =>
{
if (reactor?.Item != null && !reactor.Item.Removed && reactor.AvailableFuel < 1) { return; }
ActiveHintMessageBox.Close();
@@ -445,7 +417,7 @@ namespace Barotrauma
{
if (!CanDisplayHints()) { return; }
if (transitionType == CampaignMode.TransitionType.None) { return; }
EnqueueHint($"onavailabletransition.{transitionType.ToString().ToLowerInvariant()}");
DisplayHint($"onavailabletransition.{transitionType.ToString().ToLowerInvariant()}");
}
public static void OnShowSubInventory(Item item)
@@ -462,12 +434,31 @@ namespace Barotrauma
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}";
EnqueueHint(hintIdentifier,
DisplayHint(hintIdentifier,
icon: Character.Controlled.Info.Job.Prefab.Icon,
iconColor: Character.Controlled.Info.Job.Prefab.UIColor,
onDisplay: () =>
@@ -500,7 +491,7 @@ namespace Barotrauma
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; }
EnqueueHint("onautopilotreachedoutpost");
DisplayHint("onautopilotreachedoutpost");
}
public static void OnStatusEffectApplied(ItemComponent component, ActionType actionType, Character character)
@@ -509,7 +500,7 @@ namespace Barotrauma
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; }
EnqueueHint("onrepairfailed");
DisplayHint("onrepairfailed");
}
private static void CheckIfDivingGearOutOfOxygen()
@@ -517,12 +508,12 @@ namespace Barotrauma
if (!CanDisplayHints()) { return; }
var divingGear = Character.Controlled.GetEquippedItem("diving");
if (divingGear?.OwnInventory == null) { return; }
if (divingGear.GetContainedItemConditionPercentage() > 0.05f) { return; }
EnqueueHint("ondivinggearoutofoxygen", onUpdate: () =>
if (divingGear.GetContainedItemConditionPercentage() > 0.0f) { return; }
DisplayHint("ondivinggearoutofoxygen", onUpdate: () =>
{
if (divingGear == null || divingGear.Removed ||
Character.Controlled == null || !Character.Controlled.HasEquippedItem(divingGear) ||
divingGear.GetContainedItemConditionPercentage() > 0.05f)
divingGear.GetContainedItemConditionPercentage() > 0.0f)
{
ActiveHintMessageBox.Close();
}
@@ -535,15 +526,21 @@ namespace Barotrauma
if (Character.Controlled.CurrentHull == null) { return; }
foreach (var gap in Character.Controlled.CurrentHull.ConnectedGaps)
{
if (!gap.IsRoomToRoom) { continue; }
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 && EnqueueHint("onadjacenthull.highpressure")) { return; }
if (adjacentHull.WaterPercentage > 75 && !BallastHulls.Contains(adjacentHull) && EnqueueHint("onadjacenthull.highwaterpercentage")) { return; }
if (adjacentHull.LethalPressure > 5.0f && DisplayHint("onadjacenthull.highpressure")) { return; }
if (adjacentHull.WaterPercentage > 75 && !BallastHulls.Contains(adjacentHull) && DisplayHint("onadjacenthull.highwaterpercentage")) { return; }
}
}
}
@@ -552,23 +549,23 @@ namespace Barotrauma
{
if (!CanDisplayHints()) { return; }
if (Level.Loaded == null) { return; }
if (Timing.TotalTime < GameMain.GameSession.RoundStartTime + TimeBeforeReminders) { return; }
if (Timing.TotalTime < TimeReminderLastDisplayed + ReminderCooldown) { return; }
if (GameMain.GameScreen.GameTime < TimeRoundStarted + TimeBeforeReminders) { return; }
if (GameMain.GameScreen.GameTime < TimeReminderLastDisplayed + ReminderCooldown) { return; }
string hintIdentifierBase = "reminder";
if (GameMain.GameSession.GameMode.IsSinglePlayer)
{
if (EnqueueHint($"{hintIdentifierBase}.characterchange"))
if (DisplayHint($"{hintIdentifierBase}.characterchange"))
{
TimeReminderLastDisplayed = Timing.TotalTime;
TimeReminderLastDisplayed = GameMain.GameScreen.GameTime;
return;
}
}
if (Level.Loaded.Type != LevelData.LevelType.Outpost)
{
if (EnqueueHint($"{hintIdentifierBase}.commandinterface",
if (DisplayHint($"{hintIdentifierBase}.commandinterface",
variableTags: new string[] { "[commandkey]" },
variableValues: new string[] { GameMain.Config.KeyBindText(InputType.Command) },
onUpdate: () =>
@@ -577,12 +574,12 @@ namespace Barotrauma
ActiveHintMessageBox.Close();
}))
{
TimeReminderLastDisplayed = Timing.TotalTime;
TimeReminderLastDisplayed = GameMain.GameScreen.GameTime;
return;
}
}
if (EnqueueHint($"{hintIdentifierBase}.tabmenu",
if (DisplayHint($"{hintIdentifierBase}.tabmenu",
variableTags: new string[] { "[infotabkey]" },
variableValues: new string[] { GameMain.Config.KeyBindText(InputType.InfoTab) },
onUpdate: () =>
@@ -591,21 +588,21 @@ namespace Barotrauma
ActiveHintMessageBox.Close();
}))
{
TimeReminderLastDisplayed = Timing.TotalTime;
TimeReminderLastDisplayed = GameMain.GameScreen.GameTime;
return;
}
if (Character.Controlled.Inventory?.GetItemInLimbSlot(InvSlotType.Bag)?.Prefab?.Identifier == "toolbelt")
{
if (EnqueueHint($"{hintIdentifierBase}.toolbelt"))
if (DisplayHint($"{hintIdentifierBase}.toolbelt"))
{
TimeReminderLastDisplayed = Timing.TotalTime;
TimeReminderLastDisplayed = GameMain.GameScreen.GameTime;
return;
}
}
}
private static bool EnqueueHint(string hintIdentifier, string[] variableTags = null, string[] variableValues = null, Sprite icon = null, Color? iconColor = null, Action onDisplay = null, Action onUpdate = null)
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; }
@@ -613,7 +610,7 @@ namespace Barotrauma
if (HintsIgnoredThisRound.Contains(hintIdentifier)) { return false; }
string text;
string textTag = $"hint.{hintIdentifier}";
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);
@@ -631,9 +628,16 @@ namespace Barotrauma
return false;
}
var hint = new HintInfo(hintIdentifier, text, icon, iconColor, onDisplay, onUpdate);
HintQueue.Enqueue(hint);
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;
}
@@ -678,6 +682,7 @@ namespace Barotrauma
{
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; }

View File

@@ -266,7 +266,9 @@ namespace Barotrauma
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
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))
@@ -278,8 +280,27 @@ namespace Barotrauma
{
RelativeSpacing = 0.05f
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
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),
missionMessage, wrap: true, parseRichText: true);
if (selectedMissions.Contains(displayedMission) && displayedMission.Completed && displayedMission.Reward > 0)
@@ -430,7 +451,7 @@ namespace Barotrauma
Faction unlockFaction = null;
if (!string.IsNullOrEmpty(unlockEvent.UnlockPathFaction))
{
unlockFaction = GameMain.GameSession.Campaign.Factions.Find(f => f.Prefab.Identifier == 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);

View File

@@ -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]; }

View File

@@ -381,15 +381,11 @@ namespace Barotrauma.Items.Components
private void DrawOutputOverLay(SpriteBatch spriteBatch, GUICustomComponent overlayComponent)
{
if (!outputContainer.Inventory.IsEmpty()) { return; }
overlayComponent.RectTransform.SetAsLastChild();
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)
@@ -402,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)

View File

@@ -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 }
}
};
@@ -674,7 +682,6 @@ namespace Barotrauma.Items.Components
}
disruptionUpdateTimer -= deltaTime;
for (var pingIndex = 0; pingIndex < activePingsCount; ++pingIndex)
{
var activePing = activePings[pingIndex];
@@ -684,12 +691,44 @@ 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)
{
float dist = Vector2.Distance(c.WorldPosition, transducerCenter);
Vector2 targetDir = (c.WorldPosition - transducerCenter) / 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;
@@ -946,6 +985,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",
@@ -1045,15 +1097,17 @@ 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; }
//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;
}
@@ -1555,12 +1609,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,
@@ -1778,6 +1832,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)
{

View File

@@ -727,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)

View File

@@ -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);
}
@@ -390,5 +372,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);
}
}
}

View File

@@ -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)

View File

@@ -67,17 +67,19 @@ namespace Barotrauma.Items.Components
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(

View File

@@ -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)
{

View File

@@ -302,7 +302,7 @@ namespace Barotrauma
}
if (!string.IsNullOrEmpty(description)) { toolTip += '\n' + description; }
}
if (itemsInSlot.Count() > 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‖";
@@ -569,47 +569,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);
}
if (Screen.Selected == GameMain.GameScreen)
{
DraggingItems.RemoveAll(it => it.NonInteractable || it.NonPlayerTeamInteractable);
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);
}
if (Screen.Selected == GameMain.GameScreen)
{
doubleClickedItems.RemoveAll(it => it.NonInteractable || it.NonPlayerTeamInteractable);
doubleClickedItems.AddRange(interactableItems);
}
}
}

View File

@@ -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)
};

View File

@@ -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();

View File

@@ -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;
@@ -495,9 +496,10 @@ namespace Barotrauma.Lights
if ((!LosEnabled || LosMode == LosMode.None) && !ObstructVision) return;
if (ViewTarget == null) return;
if (Character.Controlled == null) { DebugConsole.NewMessage("aaa", Color.Orange); }
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 +509,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();
//--------------------------------------

View File

@@ -465,7 +465,7 @@ namespace Barotrauma
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);
@@ -677,11 +677,10 @@ namespace Barotrauma
{
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);
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);
@@ -696,7 +695,7 @@ namespace Barotrauma
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, Reputation.GetReputationColor(HighlightedLocation.Reputation.NormalizedValue));
GUI.DrawString(spriteBatch, new Vector2(repBarRect.Right + GUI.IntScale(5), repBarRect.Top), repValueText, Reputation.GetReputationColor(HighlightedLocation.Reputation.NormalizedValue));
}
}
@@ -912,7 +911,7 @@ namespace Barotrauma
Faction unlockFaction = null;
if (!string.IsNullOrEmpty(unlockEvent.UnlockPathFaction))
{
unlockFaction = GameMain.GameSession.Campaign.Factions.Find(f => f.Prefab.Identifier == unlockEvent.UnlockPathFaction);
unlockFaction = GameMain.GameSession.Campaign.Factions.Find(f => f.Prefab.Identifier.Equals(unlockEvent.UnlockPathFaction, StringComparison.OrdinalIgnoreCase));
unlockReputation = unlockFaction?.Reputation;
}

View File

@@ -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
{

View File

@@ -95,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)

View File

@@ -1,5 +1,6 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma
@@ -32,24 +33,45 @@ namespace Barotrauma
}
}
public void CreatePreviewWindow(GUIComponent parent)
{
var content = new GUIButton(new RectTransform(Vector2.One, parent.RectTransform), style: null)
var content = new GUIFrame(new RectTransform(Vector2.One, parent.RectTransform), style: null);
var previewButton = new GUIButton(new RectTransform(new Vector2(1f, 0.5f), content.RectTransform), style: null)
{
OnClicked = (btn, obj) => { SubmarinePreview.Create(this); return false; }
OnClicked = (btn, obj) => { SubmarinePreview.Create(this); return false; },
};
if (PreviewImage == null)
var previewImage = PreviewImage ?? savedSubmarines.Find(s => s.Name.Equals(Name, StringComparison.OrdinalIgnoreCase))?.PreviewImage;
if (previewImage == null)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), content.RectTransform), TextManager.Get(SavedSubmarines.Contains(this) ? "SubPreviewImageNotFound" : "SubNotDownloaded"));
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 };
}
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",
@@ -59,36 +81,25 @@ 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, includesDescription: true);
}
public void CreateSpecsWindow(GUIListBox parent, ScalableFont font)
public void CreateSpecsWindow(GUIListBox parent, ScalableFont font, bool includeTitle = true, bool includesDescription = 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);
GUITextBlock submarineNameText = 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);
}
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);
@@ -152,8 +163,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 (includesDescription)
{
//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));
}
}
}

View File

@@ -1,4 +1,5 @@
using Barotrauma.IO;
using Barotrauma.Extensions;
using Barotrauma.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
@@ -83,28 +84,94 @@ namespace Barotrauma
};
var innerFrame = new GUIFrame(new RectTransform(Vector2.One * 0.9f, previewFrame.RectTransform, Anchor.Center));
var verticalLayout = new GUILayoutGroup(new RectTransform(Vector2.One * 0.95f, innerFrame.RectTransform, Anchor.Center), isHorizontal: false);
var topLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), verticalLayout.RectTransform, Anchor.Center), isHorizontal: true, childAnchor: Anchor.CenterLeft);
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"))
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)
{
OnClicked = (btn, obj) => { Dispose(); return false; }
OutlineColor = Color.Black,
OutlineThickness = 2
};
new GUICustomComponent(new RectTransform(new Vector2(1f, 0.9f), verticalLayout.RectTransform, Anchor.Center),
(spriteBatch, component) => { camera.UpdateTransform(true); RenderSubmarine(spriteBatch, component.Rect); },
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) => {
camera.MoveCamera(deltaTime, overrideMouseOn: component.Rect);
if (component.Rect.Contains(PlayerInput.MousePosition) &&
bool isMouseOnComponent = GUI.MouseOn == component;
camera.MoveCamera(deltaTime, allowZoom: isMouseOnComponent);
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, includesDescription: 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();
}

View File

@@ -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 = "";
@@ -114,7 +114,7 @@ namespace Barotrauma.Networking
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
@@ -155,11 +155,11 @@ namespace Barotrauma.Networking
}
}
if (NetIdUtils.IdMoreRecent(ID, LastID))
if (NetIdUtils.IdMoreRecent(id, LastID))
{
GameMain.Client.AddChatMessage(
new OrderChatMessage(orderPrefab, orderOption, orderPriority, txt, orderTargetPosition ?? targetEntity as ISpatialEntity, targetCharacter, senderCharacter));
LastID = ID;
LastID = id;
}
return;
case ChatMessageType.ServerMessageBox:
@@ -171,7 +171,7 @@ namespace Barotrauma.Networking
break;
}
if (NetIdUtils.IdMoreRecent(ID, LastID))
if (NetIdUtils.IdMoreRecent(id, LastID))
{
switch (type)
{
@@ -200,7 +200,7 @@ namespace Barotrauma.Networking
GameMain.Client.AddChatMessage(txt, type, senderName, senderCharacter, changeType);
break;
}
LastID = ID;
LastID = id;
}
}
}

View File

@@ -159,6 +159,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 +191,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,
@@ -1424,6 +1430,8 @@ namespace Barotrauma.Networking
EndVoteTickBox.Selected = false;
WaitForNextRoundRespawn = null;
roundInitStatus = RoundInitStatus.Starting;
int seed = inc.ReadInt32();
@@ -1690,13 +1698,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;
@@ -1739,6 +1749,7 @@ namespace Barotrauma.Networking
gameStarted = false;
Character.Controlled = null;
WaitForNextRoundRespawn = null;
SpawnAsTraitor = false;
GameMain.GameScreen.Cam.TargetPos = Vector2.Zero;
GameMain.LightManager.LosEnabled = false;
@@ -1965,14 +1976,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; }
@@ -2452,6 +2468,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();
@@ -3143,11 +3168,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,
@@ -3255,9 +3280,10 @@ namespace Barotrauma.Networking
if (respawnManager != null)
{
string respawnText = "";
string respawnText = string.Empty;
float textScale = 1.0f;
Color textColor = Color.White;
bool canChooseRespawn = GameMain.GameSession.GameMode is CampaignMode && Character.Controlled == null && Level.Loaded?.Type != LevelData.LevelType.Outpost;
if (respawnManager.CurrentState == RespawnManager.State.Waiting &&
respawnManager.RespawnCountdownStarted)
{
@@ -3275,18 +3301,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));

View File

@@ -473,7 +473,36 @@ 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), mission.GetMissionRewardText(), wrap: true, parseRichText: true);
@@ -494,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())

View File

@@ -171,10 +171,7 @@ namespace Barotrauma
Character.Controlled.ObstructVision &&
(Character.Controlled.ViewTarget == Character.Controlled || Character.Controlled.ViewTarget == null);
if (Character.Controlled != null)
{
GameMain.LightManager.UpdateObstructVision(graphics, spriteBatch, cam, Character.Controlled.CursorWorldPosition);
}
GameMain.LightManager.UpdateObstructVision(graphics, spriteBatch, cam, Character.Controlled?.CursorWorldPosition ?? Vector2.Zero);
//------------------------------------------------------------------------
graphics.SetRenderTarget(renderTarget);
@@ -334,12 +331,13 @@ namespace Barotrauma
}
spriteBatch.End();
if (GameMain.LightManager.LosEnabled && GameMain.LightManager.LosMode != LosMode.None && Character.Controlled != null)
if (GameMain.LightManager.LosEnabled && GameMain.LightManager.LosMode != LosMode.None && Lights.LightManager.ViewTarget != null)
{
GameMain.LightManager.LosEffect.CurrentTechnique = GameMain.LightManager.LosEffect.Techniques["LosShader"];
GameMain.LightManager.LosEffect.Parameters["xTexture"].SetValue(renderTargetBackground);
GameMain.LightManager.LosEffect.Parameters["xLosTexture"].SetValue(GameMain.LightManager.LosTexture);
GameMain.LightManager.LosEffect.Parameters["xLosAlpha"].SetValue(GameMain.LightManager.LosAlpha);
Color losColor;
if (GameMain.LightManager.LosMode == LosMode.Transparent)

View File

@@ -348,6 +348,16 @@ namespace Barotrauma
CanBeFocused = false
};
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), optionList.RectTransform), TextManager.Get("EditorDisclaimerWikiLink"), textAlignment: Alignment.Left, style: "MainMenuGUIButton")
{
ForceUpperCase = true,
OnClicked = (button, userData) =>
{
string url = TextManager.Get("EditorDisclaimerWikiUrl", returnNull: true) ?? "https://barotraumagame.com/wiki";
GameMain.Instance.ShowOpenUrlInWebBrowserPrompt(url, promptExtensionTag: "wikinotice");
return true;
}
};
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), optionList.RectTransform), TextManager.Get("CreditsButton"), textAlignment: Alignment.Left, style: "MainMenuGUIButton")
{
ForceUpperCase = true,

View File

@@ -1305,9 +1305,9 @@ namespace Barotrauma
StartButton.Visible = GameMain.Client.HasPermission(ClientPermissions.ManageRound) && !GameMain.Client.GameStarted && !CampaignSetupFrame.Visible && !CampaignFrame.Visible;
ServerName.Readonly = !GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
ServerMessage.Readonly = !GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
shuttleTickBox.Enabled = !CampaignFrame.Visible && !CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
shuttleTickBox.Enabled = GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
SubList.Enabled = !CampaignFrame.Visible && (GameMain.Client.ServerSettings.Voting.AllowSubVoting || GameMain.Client.HasPermission(ClientPermissions.SelectSub));
shuttleList.Enabled = shuttleList.ButtonEnabled = shuttleTickBox.Enabled = !CampaignFrame.Visible && !CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.SelectSub);
shuttleList.Enabled = shuttleList.ButtonEnabled = GameMain.Client.HasPermission(ClientPermissions.SelectSub);
ModeList.Enabled = GameMain.Client.ServerSettings.Voting.AllowModeVoting || GameMain.Client.HasPermission(ClientPermissions.SelectMode);
LogButtons.Visible = GameMain.Client.HasPermission(ClientPermissions.ServerLog);
GameMain.Client.ShowLogButton.Visible = GameMain.Client.HasPermission(ClientPermissions.ServerLog);

View File

@@ -533,20 +533,6 @@ namespace Barotrauma
OnSelected = (GUITickBox obj) =>
{
lightingEnabled = obj.Selected;
if (lightingEnabled)
{
//turn off lights that are inside containers
foreach (Item item in Item.ItemList)
{
foreach (LightComponent lightComponent in item.GetComponents<LightComponent>())
{
lightComponent.Light.Color = item.Container != null || (item.body != null && !item.body.Enabled) ?
Color.Transparent :
lightComponent.LightColor;
lightComponent.Light.LightSpriteEffect = lightComponent.Item.SpriteEffects;
}
}
}
return true;
}
};
@@ -2704,16 +2690,6 @@ namespace Barotrauma
cam.Position = Submarine.MainSub.Position + Submarine.MainSub.HiddenSubPosition;
loadFrame = null;
//turn off lights that are inside an inventory (cabinet for example)
foreach (Item item in Item.ItemList)
{
var lightComponent = item.GetComponent<LightComponent>();
if (lightComponent != null)
{
lightComponent.Light.Enabled = item.ParentInventory == null;
}
}
}
private bool LoadSub(GUIButton button, object obj)
@@ -2748,16 +2724,6 @@ namespace Barotrauma
loadFrame = null;
//turn off lights that are inside an inventory (cabinet for example)
foreach (Item item in Item.ItemList)
{
var lightComponent = item.GetComponent<LightComponent>();
if (lightComponent != null)
{
lightComponent.Light.Enabled = item.ParentInventory == null;
}
}
if (selectedSub.Info.GameVersion < new Version("0.8.9.0"))
{
var adjustLightsPrompt = new GUIMessageBox(TextManager.Get("Warning"), TextManager.Get("AdjustLightsPrompt"),
@@ -2857,8 +2823,8 @@ namespace Barotrauma
categorizedEntityList.UpdateScrollBarSize();
categorizedEntityList.BarScroll = 0.0f;
categorizedEntityList.Visible = true;
allEntityList.Visible = false;
// categorizedEntityList.Visible = true;
// allEntityList.Visible = false;
}
private void FilterEntities(string filter)
@@ -3047,6 +3013,37 @@ namespace Barotrauma
public static GUIMessageBox CreatePropertyColorPicker(Color originalColor, SerializableProperty property, ISerializableEntity entity)
{
var entities = new List<(ISerializableEntity Entity, Color OriginalColor, SerializableProperty Property)> { (entity, originalColor, property) };
foreach (ISerializableEntity selectedEntity in MapEntity.SelectedList.Where(selectedEntity => selectedEntity is ISerializableEntity && entity != selectedEntity).Cast<ISerializableEntity>())
{
switch (entity)
{
case ItemComponent _ when selectedEntity is Item item:
foreach (var component in item.Components)
{
if (component.GetType() == entity.GetType() && component != entity)
{
entities.Add((component, (Color) property.GetValue(component), property));
}
}
break;
default:
if (selectedEntity.GetType() == entity.GetType())
{
entities.Add((selectedEntity, (Color) property.GetValue(selectedEntity), property));
}
else if (selectedEntity is { SerializableProperties: { } props} )
{
if (props.TryGetValue(property.NameToLowerInvariant, out SerializableProperty foundProp))
{
entities.Add((selectedEntity, (Color) foundProp.GetValue(selectedEntity), foundProp));
}
}
break;
}
}
bool setValues = true;
object sliderMutex = new object(),
sliderTextMutex = new object(),
@@ -3142,9 +3139,22 @@ namespace Barotrauma
colorPicker.DisposeTextures();
msgBox.Close();
if (entity is MapEntity { Removed: true } me) { return true; }
Color newColor = SetColor(null);
StoreCommand(new PropertyCommand(entity, property.Name, newColor, originalColor));
Dictionary<object, List<ISerializableEntity>> oldProperties = new Dictionary<object, List<ISerializableEntity>>();
foreach (var (sEntity, color, _) in entities)
{
if (sEntity is MapEntity { Removed: true }) { continue; }
if (!oldProperties.ContainsKey(color))
{
oldProperties.Add(color, new List<ISerializableEntity>());
}
oldProperties[color].Add(sEntity);
}
List<ISerializableEntity> affected = entities.Select(t => t.Entity).Where(se => se is MapEntity { Removed: false }).ToList();
StoreCommand(new PropertyCommand(affected, property.Name, newColor, oldProperties));
if (MapEntity.EditingHUD != null && (MapEntity.EditingHUD.UserData == entity || (!(entity is ItemComponent ic) || MapEntity.EditingHUD.UserData == ic.Item)))
{
@@ -3170,8 +3180,12 @@ namespace Barotrauma
{
colorPicker.DisposeTextures();
msgBox.Close();
if (entity is MapEntity { Removed: true } me) { return true; }
property.SetValue(entity, originalColor);
foreach (var (e, color, prop) in entities)
{
if (e is MapEntity { Removed: true }) { continue; }
prop.TrySetValue(e, color);
}
return true;
};
@@ -3218,8 +3232,12 @@ namespace Barotrauma
}
Color color = ToolBox.HSVToRGB(colorPicker.SelectedHue, colorPicker.SelectedSaturation, colorPicker.SelectedValue);
color.A = originalColor.A;
property.TrySetValue(entity, color);
foreach (var (e, origColor, prop) in entities)
{
if (e is MapEntity { Removed: true }) { continue; }
color.A = origColor.A;
prop.TrySetValue(e, color);
}
return color;
void SetSliders(Vector3 hsv)
@@ -3238,9 +3256,11 @@ namespace Barotrauma
void SetColorPicker(Vector3 hsv)
{
bool hueChanged = !MathUtils.NearlyEqual(colorPicker.SelectedHue, hsv.X);
colorPicker.SelectedHue = hsv.X;
colorPicker.SelectedSaturation = hsv.Y;
colorPicker.SelectedValue = hsv.Z;
if (hueChanged) { colorPicker.RefreshHue(); }
}
void SetHex(Vector3 hsv)
@@ -3496,6 +3516,8 @@ namespace Barotrauma
private bool SelectPrefab(GUIComponent component, object obj)
{
allEntityList.Deselect();
categorizedEntityList.Deselect();
if (GUI.MouseOn is GUIButton || GUI.MouseOn?.Parent is GUIButton) { return false; }
AddPreviouslyUsed(obj as MapEntityPrefab);
@@ -3586,7 +3608,7 @@ namespace Barotrauma
SoundPlayer.PlayUISound(GUISoundType.PickItem);
MapEntityPrefab.SelectPrefab(obj);
}
return false;
}
@@ -4332,6 +4354,17 @@ namespace Barotrauma
if (lightingEnabled)
{
//turn off lights that are inside containers
foreach (Item item in Item.ItemList)
{
foreach (LightComponent lightComponent in item.GetComponents<LightComponent>())
{
lightComponent.Light.Color = item.Container != null || (item.body != null && !item.body.Enabled) ?
Color.Transparent :
lightComponent.LightColor;
lightComponent.Light.LightSpriteEffect = lightComponent.Item.SpriteEffects;
}
}
GameMain.LightManager?.Update((float)deltaTime);
}

View File

@@ -1208,7 +1208,7 @@ namespace Barotrauma
SafeAdd((ISerializableEntity) entity, property);
property.PropertyInfo.SetValue(entity, value);
}
else if (entity is ISerializableEntity sEntity && sEntity.SerializableProperties != null)
else if (entity is ISerializableEntity { SerializableProperties: { } } sEntity)
{
var props = sEntity.SerializableProperties;

View File

@@ -30,6 +30,7 @@ namespace Barotrauma.Sounds
public override int FillStreamBuffer(int samplePos, short[] buffer)
{
if (!Stream) throw new Exception("Called FillStreamBuffer on a non-streamed sound!");
if (reader == null) throw new Exception("Called FillStreamBuffer when the reader is null!");
if (samplePos >= reader.TotalSamples * reader.Channels * 2) return 0;
@@ -73,6 +74,8 @@ namespace Barotrauma.Sounds
{
if (!Stream)
{
reader ??= new VorbisReader(Filename);
reader.DecodedPosition = 0;
int bufferSize = (int)reader.TotalSamples * reader.Channels;
@@ -126,7 +129,7 @@ namespace Barotrauma.Sounds
{
if (Stream)
{
reader.Dispose();
reader?.Dispose();
}
base.Dispose();

View File

@@ -14,35 +14,15 @@ namespace Barotrauma.Sounds
get { return disposed; }
}
public SoundManager Owner
{
get;
protected set;
}
public readonly SoundManager Owner;
public string Filename
{
get;
protected set;
}
public readonly string Filename;
public XElement XElement
{
get;
protected set;
}
public readonly XElement XElement;
public bool Stream
{
get;
protected set;
}
public readonly bool Stream;
public bool StreamsReliably
{
get;
protected set;
}
public readonly bool StreamsReliably;
public virtual SoundManager.SourcePoolIndex SourcePoolIndex
{
@@ -79,10 +59,10 @@ namespace Barotrauma.Sounds
public float BaseNear;
public float BaseFar;
public Sound(SoundManager owner, string filename, bool stream, bool streamsReliably, XElement xElement=null)
public Sound(SoundManager owner, string filename, bool stream, bool streamsReliably, XElement xElement=null, bool getFullPath=true)
{
Owner = owner;
Filename = Path.GetFullPath(filename.CleanUpPath()).CleanUpPath();
Filename = getFullPath ? Path.GetFullPath(filename.CleanUpPath()).CleanUpPath() : filename;
Stream = stream;
StreamsReliably = streamsReliably;
XElement = xElement;

View File

@@ -21,12 +21,14 @@ namespace Barotrauma
public readonly string requiredTag;
public DamageSound(Sound sound, Vector2 damageRange, string damageType, string requiredTag = "")
public bool ignoreMuffling;
public DamageSound(Sound sound, Vector2 damageRange, string damageType, bool ignoreMuffling, string requiredTag = "")
{
this.sound = sound;
this.damageRange = damageRange;
this.damageType = damageType;
this.ignoreMuffling = ignoreMuffling;
this.requiredTag = requiredTag;
}
}
@@ -269,6 +271,7 @@ namespace Barotrauma
damageSound,
soundElement.GetAttributeVector2("damagerange", Vector2.Zero),
damageSoundType,
soundElement.GetAttributeBool("ignoremuffling", false),
soundElement.GetAttributeString("requiredtag", "")));
break;
@@ -531,7 +534,7 @@ namespace Barotrauma
Vector2 diff = gap.WorldPosition - listenerPos;
if (Math.Abs(diff.X) < FlowSoundRange && Math.Abs(diff.Y) < FlowSoundRange)
{
if (gap.Open < 0.01f) { continue; }
if (gap.Open < 0.01f || gap.LerpedFlowForce.LengthSquared() < 100.0f) { continue; }
float gapFlow = Math.Abs(gap.LerpedFlowForce.X) + Math.Abs(gap.LerpedFlowForce.Y) * 2.5f;
if (!gap.IsRoomToRoom) { gapFlow *= 2.0f; }
if (gapFlow < 10.0f) { continue; }
@@ -1123,7 +1126,11 @@ namespace Barotrauma
tempList.Add(s);
}
}
tempList.GetRandom().sound?.Play(1.0f, range, position, muffle: ShouldMuffleSound(Character.Controlled, position, range, null));
var damageSound = tempList.GetRandom();
if (damageSound.sound != null)
{
damageSound.sound.Play(1.0f, range, position, muffle: !damageSound.ignoreMuffling && ShouldMuffleSound(Character.Controlled, position, range, null));
}
}
public static void PlayUISound(GUISoundType soundType)

View File

@@ -63,10 +63,8 @@ namespace Barotrauma.Sounds
get { return soundChannel?.CurrentAmplitude ?? 0.0f; }
}
public VoipSound(string name, SoundManager owner, VoipQueue q) : base(owner, "voip", true, true)
public VoipSound(string name, SoundManager owner, VoipQueue q) : base(owner, $"VoIP ({name})", true, true, getFullPath: false)
{
Filename = $"VoIP ({name})";
VoipConfig.SetupEncoding();
ALFormat = Al.FormatMono16;

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product>
<Version>0.1300.0.2</Version>
<Version>0.1300.0.3</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product>
<Version>0.1300.0.2</Version>
<Version>0.1300.0.3</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>

View File

@@ -26,6 +26,8 @@ sampler TextureSampler : register (s0) = sampler_state { Texture = <xTexture>; }
Texture2D xLosTexture;
sampler LosSampler = sampler_state { Texture = <xLosTexture>; };
float xLosAlpha;
float4 xColor;
float4 mainPS(VertexShaderOutput input) : COLOR0
@@ -39,7 +41,7 @@ float4 mainPS(VertexShaderOutput input) : COLOR0
sampleColor.r * xColor.r,
sampleColor.g * xColor.g,
sampleColor.b * xColor.b,
obscureAmount);
obscureAmount * xLosAlpha);
return outColor;
}

View File

@@ -26,6 +26,8 @@ sampler TextureSampler : register (s0) = sampler_state { Texture = <xTexture>; }
Texture xLosTexture;
sampler LosSampler = sampler_state { Texture = <xLosTexture>; };
float xLosAlpha;
float4 xColor;
float4 mainPS(VertexShaderOutput input) : COLOR0
@@ -39,7 +41,7 @@ float4 mainPS(VertexShaderOutput input) : COLOR0
sampleColor.r * xColor.r,
sampleColor.g * xColor.g,
sampleColor.b * xColor.b,
obscureAmount);
obscureAmount * xLosAlpha);
return outColor;
}

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product>
<Version>0.1300.0.2</Version>
<Version>0.1300.0.3</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product>
<Version>0.1300.0.2</Version>
<Version>0.1300.0.3</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName>

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product>
<Version>0.1300.0.2</Version>
<Version>0.1300.0.3</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName>

View File

@@ -25,12 +25,15 @@ namespace Barotrauma.Items.Components
}
}
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);
messageHistory.Add(input);
while (messageHistory.Count > MaxMessages)
{
messageHistory.RemoveAt(0);
}
}
}

View File

@@ -73,6 +73,7 @@ namespace Barotrauma.Networking
public NetworkConnection Connection { get; set; }
public bool SpectateOnly;
public bool? WaitForNextRoundRespawn;
public int KarmaKickCount;

View File

@@ -486,6 +486,11 @@ namespace Barotrauma.Networking
endRoundTimer += deltaTime;
#endif
}
else if (isCrewDead && (GameMain.GameSession?.GameMode is CampaignMode))
{
endRoundDelay = 1.0f;
endRoundTimer += deltaTime;
}
else
{
endRoundTimer = 0.0f;
@@ -505,10 +510,14 @@ namespace Barotrauma.Networking
{
Log("Ending round (submarine reached the end of the level)", ServerLog.MessageType.ServerMessage);
}
else
else if (respawnManager == null)
{
Log("Ending round (no living players left and respawning is not enabled during this round)", ServerLog.MessageType.ServerMessage);
}
else
{
Log("Ending round (no living players left)", ServerLog.MessageType.ServerMessage);
}
EndGame();
return;
}
@@ -819,6 +828,9 @@ namespace Barotrauma.Networking
case ClientPacketHeader.READY_CHECK:
ReadyCheck.ServerRead(inc, connectedClient);
break;
case ClientPacketHeader.READY_TO_SPAWN:
ReadReadyToSpawnMessage(inc, connectedClient);
break;
case ClientPacketHeader.FILE_REQUEST:
if (serverSettings.AllowFileTransfers)
{
@@ -1191,6 +1203,15 @@ namespace Barotrauma.Networking
mpCampaign.ServerReadCrew(inc, sender);
}
}
private void ReadReadyToSpawnMessage(IReadMessage inc, Client sender)
{
sender.SpectateOnly = inc.ReadBoolean() && (serverSettings.AllowSpectating || sender.Connection == OwnerConnection);
sender.WaitForNextRoundRespawn = inc.ReadBoolean();
if (!(GameMain.GameSession?.GameMode is CampaignMode))
{
sender.WaitForNextRoundRespawn = null;
}
}
private void ClientReadServerCommand(IReadMessage inc)
{
@@ -2145,12 +2166,12 @@ namespace Barotrauma.Networking
}
MissionMode missionMode = GameMain.GameSession.GameMode as MissionMode;
bool missionAllowRespawn = GameMain.GameSession.Campaign == null && (missionMode == null || !missionMode.Missions.Any(m => !m.AllowRespawn));
bool outpostAllowRespawn = GameMain.GameSession.Campaign != null && Level.Loaded?.Type == LevelData.LevelType.Outpost;
bool missionAllowRespawn = missionMode == null || !missionMode.Missions.Any(m => !m.AllowRespawn);
bool isOutpost = campaign != null && campaign.NextLevel?.Type == LevelData.LevelType.Outpost;
if (serverSettings.AllowRespawn && (missionAllowRespawn || outpostAllowRespawn))
if (serverSettings.AllowRespawn && missionAllowRespawn)
{
respawnManager = new RespawnManager(this, serverSettings.UseRespawnShuttle && !outpostAllowRespawn ? selectedShuttle : null);
respawnManager = new RespawnManager(this, serverSettings.UseRespawnShuttle && !isOutpost ? selectedShuttle : null);
}
Level.Loaded?.SpawnNPCs();
@@ -2400,9 +2421,8 @@ namespace Barotrauma.Networking
msg.Write((byte)ServerPacketHeader.STARTGAME);
msg.Write(seed);
msg.Write(gameSession.GameMode.Preset.Identifier);
bool missionAllowRespawn = campaign == null && (missionMode == null || !missionMode.Missions.Any(m => !m.AllowRespawn));
bool outpostAllowRespawn = campaign != null && campaign.NextLevel?.Type == LevelData.LevelType.Outpost;
msg.Write(serverSettings.AllowRespawn && (missionAllowRespawn || outpostAllowRespawn));
bool missionAllowRespawn = missionMode == null || !missionMode.Missions.Any(m => !m.AllowRespawn);
msg.Write(serverSettings.AllowRespawn && missionAllowRespawn);
msg.Write(serverSettings.AllowDisguises);
msg.Write(serverSettings.AllowRewiring);
msg.Write(serverSettings.LockAllDefaultWires);
@@ -2563,6 +2583,7 @@ namespace Barotrauma.Networking
client.Character = null;
client.HasSpawned = false;
client.InGame = false;
client.WaitForNextRoundRespawn = null;
}
}
@@ -2798,6 +2819,7 @@ namespace Barotrauma.Networking
client.Character = null;
client.HasSpawned = false;
client.WaitForNextRoundRespawn = null;
client.InGame = false;
if (string.IsNullOrWhiteSpace(msg)) { msg = $"ServerMessage.ClientLeftServer~[client]={ClientLogName(client)}"; }

View File

@@ -18,13 +18,12 @@ namespace Barotrauma.Networking
if (!c.InGame) { continue; }
if (c.SpectateOnly && (GameMain.Server.ServerSettings.AllowSpectating || GameMain.Server.OwnerConnection == c.Connection)) { continue; }
if (c.Character != null && !c.Character.IsDead) { continue; }
//don't allow respawning if the client has previously disconnected and their corpse is still present on the server
var matchingData = campaign?.GetClientCharacterData(c);
if (matchingData != null && matchingData.HasSpawned &&
Character.CharacterList.Any(c => c.Info == matchingData.CharacterInfo && c.CauseOfDeath?.Type == CauseOfDeathType.Disconnected))
if (matchingData != null && matchingData.HasSpawned)
{
continue;
if (!c.WaitForNextRoundRespawn.HasValue || c.WaitForNextRoundRespawn.Value) { continue; }
}
yield return c;
@@ -77,25 +76,24 @@ namespace Barotrauma.Networking
partial void UpdateWaiting(float deltaTime)
{
if (RespawnShuttle != null)
{
RespawnShuttle.Velocity = Vector2.Zero;
}
bool respawnPending = RespawnPending();
if (respawnPending != RespawnCountdownStarted)
{
RespawnCountdownStarted = respawnPending;
RespawnTime = DateTime.Now + new TimeSpan(0,0,0,0, (int)(GameMain.Server.ServerSettings.RespawnInterval * 1000.0f));
RespawnTime = DateTime.Now + new TimeSpan(0, 0, 0, 0, (int)(GameMain.Server.ServerSettings.RespawnInterval * 1000.0f));
GameMain.Server.CreateEntityEvent(this);
}
if (!RespawnCountdownStarted) { return; }
if (DateTime.Now > RespawnTime)
if (RespawnCountdownStarted && DateTime.Now > RespawnTime)
{
DispatchShuttle();
RespawnCountdownStarted = false;
}
if (RespawnShuttle == null) { return; }
RespawnShuttle.Velocity = Vector2.Zero;
}
private void DispatchShuttle()
@@ -183,7 +181,6 @@ namespace Barotrauma.Networking
partial void UpdateTransportingProjSpecific(float deltaTime)
{
if (!ReturnCountdownStarted)
{
//if there are no living chracters inside, transporting can be stopped immediately
@@ -230,6 +227,8 @@ namespace Barotrauma.Networking
//get rid of the existing character
c.Character?.DespawnNow();
c.WaitForNextRoundRespawn = null;
var matchingData = campaign?.GetClientCharacterData(c);
if (matchingData != null && !matchingData.HasSpawned)
{
@@ -332,6 +331,15 @@ namespace Barotrauma.Networking
}
var characterData = campaign?.GetClientCharacterData(clients[i]);
if (characterData != null && Level.Loaded?.Type != LevelData.LevelType.Outpost)
{
var respawnPenaltyAffliction = AfflictionPrefab.List.FirstOrDefault(a => a.AfflictionType.Equals("respawnpenalty", StringComparison.OrdinalIgnoreCase));
if (respawnPenaltyAffliction != null)
{
character.CharacterHealth.ApplyAffliction(targetLimb: null, respawnPenaltyAffliction.Instantiate(10.0f));
}
}
if (characterData == null || characterData.HasSpawned)
{
//give the character the items they would've gotten if they had spawned in the main sub

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product>
<Version>0.1300.0.2</Version>
<Version>0.1300.0.3</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName>

View File

@@ -1,5 +1,6 @@
using Microsoft.Xna.Framework;
using NLog.Targets;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -18,8 +19,11 @@ namespace Barotrauma
private readonly Alignment? cameraEndPos;
private readonly float? startZoom;
private readonly float? endZoom;
public readonly float Duration;
public readonly float WaitDuration;
public readonly float PanDuration;
public readonly bool FadeOut;
public readonly bool LosFadeIn;
private readonly CoroutineHandle updateCoroutine;
@@ -28,10 +32,12 @@ namespace Barotrauma
public bool AllowInterrupt = false;
public bool RemoveControlFromCharacter = true;
public CameraTransition(ISpatialEntity targetEntity, Camera cam, Alignment? cameraStartPos, Alignment? cameraEndPos, bool fadeOut = true, float duration = 10.0f, float? startZoom = null, float? endZoom = null)
public CameraTransition(ISpatialEntity targetEntity, Camera cam, Alignment? cameraStartPos, Alignment? cameraEndPos, bool fadeOut = true, bool losFadeIn = false, float waitDuration = 0f, float panDuration = 10.0f, float? startZoom = null, float? endZoom = null)
{
Duration = duration;
WaitDuration = waitDuration;
PanDuration = panDuration;
FadeOut = fadeOut;
LosFadeIn = losFadeIn;
this.cameraStartPos = cameraStartPos;
this.cameraEndPos = cameraEndPos;
this.startZoom = startZoom;
@@ -77,9 +83,12 @@ namespace Barotrauma
Vector2 initialCameraPos = cam.Position;
Vector2? initialTargetPos = targetEntity?.WorldPosition;
float timer = 0.0f;
while (timer < Duration)
float timer = -WaitDuration;
while (timer < PanDuration)
{
float clampedTimer = Math.Max(timer, 0f);
if (Screen.Selected != GameMain.GameScreen)
{
yield return new WaitForSeconds(0.1f);
@@ -136,14 +145,20 @@ namespace Barotrauma
MathHelper.Lerp(maxPos.Y, minPos.Y, (cameraEndPos.Value.ToVector2().Y + 1.0f) / 2.0f)) :
prevControlled?.WorldPosition ?? targetEntity.WorldPosition;
Vector2 cameraPos = Vector2.SmoothStep(startPos, endPos, timer / Duration);
Vector2 cameraPos = Vector2.SmoothStep(startPos, endPos, clampedTimer / PanDuration);
cam.Translate(cameraPos - cam.Position);
#if CLIENT
cam.Zoom = MathHelper.SmoothStep(startZoom, endZoom, timer / Duration);
if (timer / Duration > 0.9f)
cam.Zoom = MathHelper.SmoothStep(startZoom, endZoom, clampedTimer / PanDuration);
if (clampedTimer / PanDuration > 0.9f)
{
if (FadeOut) { GUI.ScreenOverlayColor = Color.Lerp(Color.TransparentBlack, Color.Black, ((timer / Duration) - 0.9f) * 10.0f); }
if (FadeOut) { GUI.ScreenOverlayColor = Color.Lerp(Color.TransparentBlack, Color.Black, ((clampedTimer / PanDuration) - 0.9f) * 10.0f); }
}
if (LosFadeIn && clampedTimer / PanDuration > 0.8f)
{
GameMain.LightManager.LosAlpha = ((clampedTimer / PanDuration) - 0.8f) * 5.0f;
Lights.LightManager.ViewTarget = prevControlled ?? (targetEntity as Entity);
GameMain.LightManager.LosEnabled = true;
}
#endif
timer += CoroutineManager.UnscaledDeltaTime;
@@ -158,6 +173,7 @@ namespace Barotrauma
#if CLIENT
GUI.ScreenOverlayColor = Color.TransparentBlack;
GameMain.LightManager.LosEnabled = true;
GameMain.LightManager.LosAlpha = 1f;
#endif
if (prevControlled != null && !prevControlled.Removed)

View File

@@ -172,6 +172,15 @@ namespace Barotrauma
}
}
/// <summary>
/// The monster won't try to damage these submarines
/// </summary>
public HashSet<Submarine> UnattackableSubmarines
{
get;
private set;
} = new HashSet<Submarine>();
public bool IsBeingChasedBy(Character c) => c.AIController is EnemyAIController enemyAI && enemyAI.SelectedAiTarget?.Entity is Character && (enemyAI.State == AIState.Aggressive || enemyAI.State == AIState.Attack);
private bool IsBeingChased => SelectedAiTarget?.Entity is Character targetCharacter && IsBeingChasedBy(targetCharacter);
@@ -2150,7 +2159,13 @@ namespace Barotrauma
else
{
// Ignore all structures, items, and hulls inside wrecks and beacons
if (aiTarget.Entity.Submarine != null && (aiTarget.Entity.Submarine.Info.IsWreck || aiTarget.Entity.Submarine.Info.IsBeacon)) { continue; }
if (aiTarget.Entity.Submarine != null)
{
if (aiTarget.Entity.Submarine.Info.IsWreck || aiTarget.Entity.Submarine.Info.IsBeacon || UnattackableSubmarines.Contains(aiTarget.Entity.Submarine))
{
continue;
}
}
if (aiTarget.Entity is Hull hull)
{
// Ignore the target if it's a room and the character is already inside a sub
@@ -2439,13 +2454,23 @@ namespace Barotrauma
}
}
}
// Don't target characters that are outside of the allowed zone, unless attacking or escaping
if (targetParams.State != AIState.Attack && targetParams.State != AIState.Escape && targetParams.State != AIState.Avoid)
// Don't target characters that are outside of the allowed zone, unless chasing or escaping.
switch (targetParams.State)
{
if (!IsPositionInsideAllowedZone(aiTarget.WorldPosition, out _))
{
continue;
}
case AIState.Escape:
case AIState.Avoid:
break;
default:
if (targetParams.State == AIState.Attack)
{
if (State == targetParams.State && SelectedAiTarget == aiTarget) { break; }
}
if (!IsPositionInsideAllowedZone(aiTarget.WorldPosition, out _))
{
continue;
}
break;
}
valueModifier *= targetMemory.Priority / (float)Math.Sqrt(dist);

View File

@@ -1012,11 +1012,6 @@ namespace Barotrauma
}
else
{
bool allowOffensive = HasItem(attacker, "handlocker", out _, requireEquipped: true);
if (attackResult.Afflictions.Any(a => a is AfflictionHusk))
{
cumulativeDamage = 100;
}
// Don't react to minor (accidental) dmg done by characters that are in the same team
if (cumulativeDamage < 10)
{
@@ -1027,7 +1022,7 @@ namespace Barotrauma
}
else
{
AddCombatObjective(DetermineCombatMode(Character, cumulativeDamage, dmgThreshold: 20, allowOffensive: allowOffensive), attacker, GetReactionTime() * 2);
AddCombatObjective(DetermineCombatMode(Character, cumulativeDamage, dmgThreshold: 50), attacker, GetReactionTime() * 2);
}
}
}
@@ -1056,7 +1051,7 @@ namespace Barotrauma
if (!otherHumanAI.IsFriendly(Character)) { continue; }
bool isWitnessing = otherHumanAI.VisibleHulls.Contains(Character.CurrentHull) || otherHumanAI.VisibleHulls.Contains(attacker.CurrentHull);
if (!isWitnessing && !CheckReportRange(Character, otherCharacter, ReportRange)) { continue; }
var combatMode = DetermineCombatMode(otherCharacter, cumulativeDamage, isWitnessing);
var combatMode = DetermineCombatMode(otherCharacter, cumulativeDamage, isWitnessing, dmgThreshold: attacker.TeamID == Character.TeamID ? 50 : 10);
float delay = isWitnessing ? GetReactionTime() : Rand.Range(2.0f, 5.0f, Rand.RandSync.Unsynced);
otherHumanAI.AddCombatObjective(combatMode, attacker, delay);
}
@@ -1099,6 +1094,15 @@ namespace Barotrauma
}
else
{
if (c.AIController is HumanAIController humanAI && humanAI.ObjectiveManager.GetActiveObjective<AIObjectiveCombat>()?.Enemy == attacker)
{
// Already targeting the attacker -> treat as a more serious threat.
cumulativeDamage *= 2;
}
if (attackResult.Afflictions.Any(a => a is AfflictionHusk))
{
cumulativeDamage = 100;
}
if (cumulativeDamage > dmgThreshold)
{
if (c.IsSecurity)
@@ -1154,7 +1158,7 @@ namespace Barotrauma
HoldPosition =
Character.Info?.Job?.Prefab.Identifier == "watchman" ||
Character.CurrentHull == null ||
Character.IsOnPlayerTeam && ObjectiveManager.GetActiveObjective<AIObjectiveGoTo>()?.Target is Character followTarget && followTarget.IsPlayer,
Character.IsOnPlayerTeam && !target.IsPlayer && ObjectiveManager.GetActiveObjective<AIObjectiveGoTo>()?.Target is Character followTarget && followTarget.IsPlayer,
abortCondition = abortCondition,
allowHoldFire = allowHoldFire,
};
@@ -1841,15 +1845,14 @@ namespace Barotrauma
if (character == null) { continue; }
if (c == character) { continue; }
if (c.IsDead || c.IsIncapacitated) { continue; }
if (c.SelectedConstruction != target.Item) { continue; }
if (!IsFriendly(character, c, onlySameTeam: true)) { continue; }
operatingCharacter = c;
// If the other character is player, don't try to operate
if (c.IsPlayer) { return true; }
if (c.AIController is HumanAIController controllingHumanAi)
{
Item otherTarget = controllingHumanAi.objectiveManager.GetActiveObjective<AIObjectiveOperateItem>()?.Component.Item ?? c.SelectedConstruction;
if (otherTarget != target.Item) { continue; }
// If the other character is player, don't try to operate
if (c.IsPlayer) { return true; }
// If the other character is ordered to operate the item, let him do it
if (controllingHumanAi.ObjectiveManager.IsCurrentOrder<AIObjectiveOperateItem>())
{
@@ -1874,8 +1877,7 @@ namespace Barotrauma
}
else
{
// Shouldn't go here, unless we allow non-humans to operate items
return false;
return c.SelectedConstruction == target.Item;
}
}

View File

@@ -178,7 +178,7 @@ namespace Barotrauma
{
if (Timing.TotalTime < GameMain.GameSession.RoundStartTime + 120.0f &&
speaker?.CurrentHull != null &&
speaker.TeamID == CharacterTeamType.FriendlyNPC &&
(speaker.TeamID == CharacterTeamType.FriendlyNPC || speaker.TeamID == CharacterTeamType.None) &&
Character.CharacterList.Any(c => c.TeamID != speaker.TeamID && c.CurrentHull == speaker.CurrentHull))
{
currentFlags.Add("EnterOutpost");
@@ -188,6 +188,11 @@ namespace Barotrauma
{
currentFlags.Add("Casual");
}
if (GameMain.GameSession.IsCurrentLocationRadiated())
{
currentFlags.Add("InRadiation");
}
}
if (speaker != null)

View File

@@ -50,6 +50,11 @@ namespace Barotrauma
float reduction = IsPriority ? 1 : isSelected ? 2 : 3;
float max = AIObjectiveManager.LowestOrderPriority - reduction;
Priority = MathHelper.Lerp(0, max, MathHelper.Clamp(devotion + (distanceFactor * PriorityModifier), 0, 1));
if (decontainObjective == null)
{
// Halve the priority until there's a decontain objective (a valid container was found).
Priority /= 2;
}
}
return Priority;
}
@@ -126,5 +131,13 @@ namespace Barotrauma
itemIndex = 0;
decontainObjective = null;
}
public void DropTarget()
{
if (item != null && character.HasItem(item))
{
item.Drop(character);
}
}
}
}

View File

@@ -2,6 +2,7 @@
using Barotrauma.Items.Components;
using System.Collections.Generic;
using System.Linq;
using System;
namespace Barotrauma
{
@@ -29,7 +30,21 @@ namespace Barotrauma
this.prioritizedItems.AddRange(prioritizedItems.Where(i => i != null));
}
protected override float TargetEvaluation() => Targets.Any() ? (objectiveManager.IsOrder(this) ? objectiveManager.GetOrderPriority(this) : AIObjectiveManager.RunPriority - 1) : 0;
protected override float TargetEvaluation()
{
if (Targets.None()) { return 0; }
if (objectiveManager.IsOrder(this))
{
float prio = objectiveManager.GetOrderPriority(this);
if (subObjectives.All(so => so.SubObjectives.None()))
{
// If none of the subobjectives have subobjectives, no valid container was found. In this case, let's reduce the priority below the run threshold.
prio = Math.Min(prio, AIObjectiveManager.RunPriority - 1);
}
return prio;
}
return AIObjectiveManager.RunPriority - 0.5f;
}
protected override bool Filter(Item target)
{
@@ -65,10 +80,10 @@ namespace Barotrauma
return true;
}
public static bool IsValidContainer(Item item, Character character) =>
!item.IgnoreByAI && item.IsInteractable(character) && item.HasTag("allowcleanup") && item.ParentInventory == null && item.OwnInventory != null && item.OwnInventory.AllItems.Any() && IsItemInsideValidSubmarine(item, character);
public static bool IsValidContainer(Item item, Character character, bool allowUnloading = true) =>
!item.IgnoreByAI && item.IsInteractable(character) && item.HasTag("allowcleanup") && allowUnloading && item.ParentInventory == null && item.OwnInventory != null && item.OwnInventory.AllItems.Any() && IsItemInsideValidSubmarine(item, character);
public static bool IsValidTarget(Item item, Character character, bool checkInventory)
public static bool IsValidTarget(Item item, Character character, bool checkInventory, bool allowUnloading = true)
{
if (item == null) { return false; }
if (item.IgnoreByAI) { return false; }
@@ -76,7 +91,7 @@ namespace Barotrauma
if (item.SpawnedInOutpost) { return false; }
if (item.ParentInventory != null)
{
if (item.Container == null || !IsValidContainer(item.Container, character)) { return false; }
if (item.Container == null || !IsValidContainer(item.Container, character, allowUnloading)) { return false; }
}
if (character != null && !IsItemInsideValidSubmarine(item, character)) { return false; }
var pickable = item.GetComponent<Pickable>();
@@ -127,5 +142,17 @@ namespace Barotrauma
}
return canEquip;
}
public override void OnDeselected()
{
base.OnDeselected();
foreach (var subObjective in SubObjectives)
{
if (subObjective is AIObjectiveCleanupItem cleanUpObjective)
{
cleanUpObjective.DropTarget();
}
}
}
}
}

View File

@@ -457,7 +457,7 @@ namespace Barotrauma
priority /= 2;
}
}
if (Enemy.Stun > 1)
if (Enemy.IsKnockedDown)
{
// Enemy is stunned, reduce the priority of stunner weapons.
Attack attack = GetAttackDefinition(weapon);
@@ -730,11 +730,12 @@ namespace Barotrauma
{
IgnoreIfTargetDead = true,
DialogueIdentifier = "dialogcannotreachtarget",
TargetName = Enemy.DisplayName
TargetName = Enemy.DisplayName,
AlwaysUseEuclideanDistance = false
},
onAbandon: () => Abandon = true);
if (followTargetObjective == null) { return; }
if (Mode == CombatMode.Arrest && Enemy.Stun > 2)
if (Mode == CombatMode.Arrest && (Enemy.Stun > 1 || Enemy.IsKnockedDown))
{
if (HumanAIController.HasItem(character, "handlocker", out _))
{
@@ -742,8 +743,8 @@ namespace Barotrauma
{
arrestingRegistered = true;
followTargetObjective.Completed += OnArrestTargetReached;
followTargetObjective.CloseEnough = 100;
}
followTargetObjective.CloseEnough = 100;
}
else
{
@@ -759,7 +760,7 @@ namespace Barotrauma
SteeringManager.Reset();
}
}
if (followTargetObjective != null)
if (!arrestingRegistered && followTargetObjective != null)
{
followTargetObjective.CloseEnough =
WeaponComponent is RangedWeapon ? 1000 :
@@ -782,7 +783,7 @@ namespace Barotrauma
private void OnArrestTargetReached()
{
if (HumanAIController.HasItem(character, "handlocker", out IEnumerable<Item> matchingItems) && Enemy.Stun > 0 && character.CanInteractWith(Enemy))
if (HumanAIController.HasItem(character, "handlocker", out IEnumerable<Item> matchingItems) && !Enemy.IsUnconscious && Enemy.IsKnockedDown && character.CanInteractWith(Enemy))
{
var handCuffs = matchingItems.First();
if (!HumanAIController.TakeItem(handCuffs, Enemy.Inventory, equip: true))
@@ -802,8 +803,8 @@ namespace Barotrauma
}
}
character.Speak(TextManager.Get("DialogTargetArrested"), null, 3.0f, "targetarrested", 30.0f);
IsCompleted = true;
}
IsCompleted = true;
}
/// <summary>
@@ -937,7 +938,14 @@ namespace Barotrauma
return;
}
if (reloadTimer > 0) { return; }
if (Mode == CombatMode.Arrest && isLethalWeapon && Enemy.Stun > 1) { return; }
if (Mode == CombatMode.Arrest)
{
// If the target is arrested or if it's stunned and we can't lock the target up, consider the objective done.
if (Enemy.IsKnockedDown && !HumanAIController.HasItem(character, "handlocker", out _, requireEquipped: false) || HumanAIController.HasItem(Enemy, "handlocker", out _, requireEquipped: true))
{
IsCompleted = true;
}
}
if (holdFireCondition != null && holdFireCondition()) { return; }
float sqrDist = Vector2.DistanceSquared(character.Position, Enemy.Position);
if (WeaponComponent is MeleeWeapon meleeWeapon)
@@ -1017,6 +1025,8 @@ namespace Barotrauma
private void UseWeapon(float deltaTime)
{
// Never allow to attack characters with deadly weapons while trying to arrest.
if (Mode == CombatMode.Arrest && isLethalWeapon) { return; }
float reloadTime = 0;
if (WeaponComponent is RangedWeapon rangedWeapon)
{
@@ -1038,10 +1048,16 @@ namespace Barotrauma
reloadTimer = Math.Max(reloadTime, reloadTime * Rand.Range(1f, 1.25f) / AimSpeed);
}
private bool ShouldUnequipWeapon =>
Weapon != null &&
character.Submarine != null &&
character.Submarine.TeamID == character.TeamID &&
Character.CharacterList.None(c => c.Submarine == character.Submarine && HumanAIController.IsActive(c) && !HumanAIController.IsFriendly(character, c) && HumanAIController.VisibleHulls.Contains(c.CurrentHull));
protected override void OnCompleted()
{
base.OnCompleted();
if (Weapon != null)
if (ShouldUnequipWeapon)
{
Unequip();
}
@@ -1051,7 +1067,7 @@ namespace Barotrauma
protected override void OnAbandon()
{
base.OnAbandon();
if (Weapon != null)
if (ShouldUnequipWeapon)
{
Unequip();
}

View File

@@ -38,7 +38,7 @@ namespace Barotrauma
Priority = 0;
Abandon = true;
}
else if (HumanAIController.IsTrueForAnyCrewMember(other => other != HumanAIController && other.ObjectiveManager.GetActiveObjective<AIObjectiveFixLeak>()?.Leak == Leak))
else if (HumanAIController.IsTrueForAnyCrewMember(other => other != HumanAIController && other.Character.IsBot && other.ObjectiveManager.GetActiveObjective<AIObjectiveFixLeak>()?.Leak == Leak))
{
Priority = 0;
Abandon = true;

View File

@@ -10,6 +10,8 @@ namespace Barotrauma
{
public override string DebugTag => "get item";
public override bool AbandonWhenCannotCompleteSubjectives => false;
private readonly bool equip;
public HashSet<Item> ignoredItems = new HashSet<Item>();
@@ -225,18 +227,8 @@ namespace Barotrauma
return new AIObjectiveGoTo(moveToTarget, character, objectiveManager, repeat: false, getDivingGearIfNeeded: AllowToFindDivingGear, closeEnough: DefaultReach)
{
// If the root container changes, the item is no longer where it was (taken by someone -> need to find another item)
abortCondition = obj =>
{
bool abort = targetItem == null || targetItem.GetRootInventoryOwner() != moveToTarget;
if (abort)
{
// Fail silently if someone takes the suit.
obj.speakIfFails = false;
}
return abort;
},
DialogueIdentifier = "dialogcannotreachtarget",
TargetName = (moveToTarget as MapEntity)?.Name ?? (moveToTarget as Character)?.Name ?? moveToTarget.ToString()
abortCondition = obj => targetItem == null || targetItem.GetRootInventoryOwner() != moveToTarget,
SpeakIfFails = false
};
},
onAbandon: () =>
@@ -263,13 +255,18 @@ namespace Barotrauma
if (targetItem == null)
{
#if DEBUG
DebugConsole.NewMessage($"{character.Name}: Cannot find the item, because neither identifiers nor item was defined.", Color.Red);
DebugConsole.NewMessage($"{character.Name}: Cannot find an item, because neither identifiers nor item was defined.", Color.Red);
#endif
Abandon = true;
}
return;
}
for (int i = 0; i < 10 && currSearchIndex < Item.ItemList.Count - 1; i++)
float priority = Math.Clamp(objectiveManager.GetCurrentPriority(), 10, 100);
bool checkPath = priority >= AIObjectiveManager.LowestOrderPriority && (objectiveManager.IsCurrentOrder<AIObjectiveFixLeaks>() || objectiveManager.CurrentOrder is AIObjectiveGoTo gotoOrder && gotoOrder.followControlledCharacter);
bool hasCalledPathFinder = false;
int itemsPerFrame = (int)priority;
for (int i = 0; i < itemsPerFrame && currSearchIndex < Item.ItemList.Count - 1; i++)
{
currSearchIndex++;
var item = Item.ItemList[currSearchIndex];
@@ -310,8 +307,18 @@ namespace Barotrauma
float distanceFactor = MathHelper.Lerp(1, 0, MathUtils.InverseLerp(0, 10000, dist));
itemPriority *= distanceFactor;
itemPriority *= item.Condition / item.MaxCondition;
//ignore if the item has a lower priority than the currently selected one
// Ignore if the item has a lower priority than the currently selected one
if (itemPriority < currItemPriority) { continue; }
if (!hasCalledPathFinder && PathSteering != null && checkPath)
{
// While following the player, let's ensure that there's a valid path to the target before accepting it.
// Otherwise it will take some time for us to find a valid item when there are multiple items that we can't reach and some that we can.
// This is relatively expensive, so let's do this only when it significantly improves the behavior.
// Only allow one path find call per frame.
hasCalledPathFinder = true;
var path = PathSteering.PathFinder.FindPath(character.SimPosition, item.SimPosition, errorMsgStr: $"AIObjectiveGetItem {character.DisplayName}", nodeFilter: node => node.Waypoint.CurrentHull != null);
if (path.Unreachable) { continue; }
}
currItemPriority = itemPriority;
targetItem = item;
moveToTarget = rootInventoryOwner ?? item;
@@ -326,7 +333,7 @@ namespace Barotrauma
if (!(MapEntityPrefab.List.FirstOrDefault(me => me is ItemPrefab ip && identifiersOrTags.Any(id => id == ip.Identifier || ip.Tags.Contains(id))) is ItemPrefab prefab))
{
#if DEBUG
DebugConsole.NewMessage($"{character.Name}: Cannot find the item with the following identifier(s) or tag(s): {string.Join(", ", identifiersOrTags)}, tried to spawn the item but no matching item prefabs were found.", Color.Yellow);
DebugConsole.NewMessage($"{character.Name}: Cannot find an item with the following identifier(s) or tag(s): {string.Join(", ", identifiersOrTags)}, tried to spawn the item but no matching item prefabs were found.", Color.Yellow);
#endif
Abandon = true;
}
@@ -345,7 +352,7 @@ namespace Barotrauma
else
{
#if DEBUG
DebugConsole.NewMessage($"{character.Name}: Cannot find the item with the following identifier(s) or tag(s): {string.Join(", ", identifiersOrTags)}", Color.Yellow);
DebugConsole.NewMessage($"{character.Name}: Cannot find an item with the following identifier(s) or tag(s): {string.Join(", ", identifiersOrTags)}", Color.Yellow);
#endif
Abandon = true;
}
@@ -393,11 +400,30 @@ namespace Barotrauma
/// </summary>
private void ResetInternal()
{
goToObjective = null;
RemoveSubObjective(ref goToObjective);
targetItem = originalTarget;
moveToTarget = targetItem?.GetRootInventoryOwner();
isDoneSeeking = false;
currSearchIndex = 0;
currItemPriority = 0;
}
protected override void OnAbandon()
{
base.OnAbandon();
if (moveToTarget == null) { return; }
#if DEBUG
DebugConsole.NewMessage($"{character.Name}: Get item failed to reach {moveToTarget}", Color.Yellow);
#endif
if (character.IsOnPlayerTeam && objectiveManager.CurrentOrder != null)
{
string TargetName = (moveToTarget as MapEntity)?.Name ?? (moveToTarget as Character)?.Name ?? moveToTarget.ToString();
string msg = TargetName == null ? TextManager.Get("dialogcannotreachtarget", true) : TextManager.GetWithVariable("dialogcannotreachtarget", "[name]", TargetName, formatCapitals: !(moveToTarget is Character));
if (msg != null)
{
character.Speak(msg, identifier: "dialogcannotreachtarget", minDurationBetweenSimilar: 20.0f);
}
}
}
}
}

View File

@@ -30,7 +30,7 @@ namespace Barotrauma
public bool followControlledCharacter;
public bool mimic;
public bool speakIfFails = true;
public bool SpeakIfFails { get; set; } = true;
public float extraDistanceWhileSwimming;
public float extraDistanceOutsideSub;
@@ -67,6 +67,8 @@ namespace Barotrauma
public bool IgnoreIfTargetDead { get; set; }
public bool AllowGoingOutside { get; set; }
public bool AlwaysUseEuclideanDistance { get; set; } = true;
public override bool AbandonWhenCannotCompleteSubjectives => !repeat;
public override bool AllowOutsideSubmarine => AllowGoingOutside;
@@ -88,12 +90,7 @@ namespace Barotrauma
Abandon = !isOrder;
return Priority;
}
if (followControlledCharacter && Character.Controlled == null)
{
Priority = 0;
Abandon = !isOrder;
}
if (Target is Entity e && e.Removed)
if (Target == null || Target is Entity e && e.Removed)
{
Priority = 0;
Abandon = !isOrder;
@@ -147,11 +144,10 @@ namespace Barotrauma
private void SpeakCannotReach()
{
if (!character.IsOnPlayerTeam) { return; }
#if DEBUG
DebugConsole.NewMessage($"{character.Name}: Cannot reach the target: {Target}", Color.Yellow);
#endif
if (objectiveManager.HasOrders() && DialogueIdentifier != null && speakIfFails)
if (character.IsOnPlayerTeam && objectiveManager.CurrentOrder != null && DialogueIdentifier != null && SpeakIfFails)
{
string msg = TargetName == null ? TextManager.Get(DialogueIdentifier, true) : TextManager.GetWithVariable(DialogueIdentifier, "[name]", TargetName, formatCapitals: !(Target is Character));
if (msg != null)
@@ -165,12 +161,15 @@ namespace Barotrauma
{
if (followControlledCharacter)
{
if (Character.Controlled == null || !HumanAIController.IsFriendly(Character.Controlled))
if (Character.Controlled != null && HumanAIController.IsFriendly(Character.Controlled))
{
Target = Character.Controlled;
}
if (Target == null)
{
Abandon = true;
return;
}
Target = Character.Controlled;
}
if (Target == character || character.SelectedBy != null && HumanAIController.IsFriendly(character.SelectedBy))
{
@@ -260,6 +259,7 @@ namespace Barotrauma
}
if (needsEquipment)
{
SteeringManager.Reset();
if (findDivingGear != null && !findDivingGear.CanBeCompleted)
{
TryAddSubObjective(ref findDivingGear, () => new AIObjectiveFindDivingGear(character, needsDivingSuit: false, objectiveManager),
@@ -288,9 +288,14 @@ namespace Barotrauma
}
}
}
float maxGapDistance = 500;
Character targetCharacter = Target as Character;
if (character.AnimController.InWater)
{
if (character.CurrentHull == null)
if (character.CurrentHull == null ||
followControlledCharacter &&
targetCharacter != null && (targetCharacter.CurrentHull == null) != (character.CurrentHull == null) &&
Vector2.DistanceSquared(character.WorldPosition, Target.WorldPosition) < maxGapDistance * maxGapDistance)
{
if (seekGapsTimer > 0)
{
@@ -298,7 +303,7 @@ namespace Barotrauma
}
else
{
SeekGaps(maxDistance: 500);
SeekGaps(maxGapDistance);
seekGapsTimer = seekGapsInterval * Rand.Range(0.1f, 1.1f);
if (TargetGap != null)
{
@@ -327,7 +332,7 @@ namespace Barotrauma
}
if (TargetGap != null)
{
if (TargetGap.FlowTargetHull != null && HumanAIController.SteerThroughGap(TargetGap, TargetGap.FlowTargetHull.WorldPosition, deltaTime))
if (TargetGap.FlowTargetHull != null && HumanAIController.SteerThroughGap(TargetGap, followControlledCharacter ? Target.WorldPosition : TargetGap.FlowTargetHull.WorldPosition, deltaTime))
{
SteeringManager.SteeringAvoid(deltaTime, avoidLookAheadDistance, weight: 1);
return;
@@ -347,7 +352,7 @@ namespace Barotrauma
float closeEnough = 250;
float squaredDistance = Vector2.DistanceSquared(character.WorldPosition, Target.WorldPosition);
bool shouldUseScooter = squaredDistance > closeEnough * closeEnough && (!mimic ||
(Target is Character targetCharacter && targetCharacter.HasEquippedItem(scooterTag, allowBroken: false)) || squaredDistance > Math.Pow(closeEnough * 2, 2));
(targetCharacter != null && targetCharacter.HasEquippedItem(scooterTag, allowBroken: false)) || squaredDistance > Math.Pow(closeEnough * 2, 2));
if (HumanAIController.HasItem(character, scooterTag, out IEnumerable<Item> equippedScooters, recursive: false, requireEquipped: true))
{
// Currently equipped scooter
@@ -528,17 +533,24 @@ namespace Barotrauma
{
Gap selectedGap = null;
float selectedDistance = -1;
Vector2 toTargetNormalized = Vector2.Normalize(Target.WorldPosition - character.WorldPosition);
foreach (Gap gap in Gap.GapList)
{
if (gap.Open < 1) { continue; }
if (gap.FlowTargetHull == null) { continue; }
if (gap.Submarine != Target.Submarine) { continue; }
float distance = Vector2.DistanceSquared(character.WorldPosition, gap.WorldPosition);
if (distance > maxDistance * maxDistance) { continue; }
if (selectedGap == null || distance < selectedDistance)
if (gap.Submarine == null) { continue; }
if (!followControlledCharacter)
{
if (gap.FlowTargetHull == null) { continue; }
if (gap.Submarine != Target.Submarine) { continue; }
}
Vector2 toGap = gap.WorldPosition - character.WorldPosition;
if (Vector2.Dot(Vector2.Normalize(toGap), toTargetNormalized) < 0) { continue; }
float squaredDistance = toGap.LengthSquared();
if (squaredDistance > maxDistance * maxDistance) { continue; }
if (selectedGap == null || squaredDistance < selectedDistance)
{
selectedGap = gap;
selectedDistance = distance;
selectedDistance = squaredDistance;
}
}
TargetGap = selectedGap;
@@ -555,7 +567,7 @@ namespace Barotrauma
//otherwise characters can let go of the ladders too soon once they're close enough to the target
if (PathSteering.CurrentPath.NextNode != null) { return false; }
}
if (!character.AnimController.InWater)
if (!AlwaysUseEuclideanDistance && !character.AnimController.InWater)
{
float yDiff = Math.Abs(Target.WorldPosition.Y - character.WorldPosition.Y);
if (yDiff > CloseEnough) { return false; }

View File

@@ -495,7 +495,7 @@ namespace Barotrauma
foreach (Item item in Item.ItemList)
{
if (item.CurrentHull != hull) { continue; }
if (AIObjectiveCleanupItems.IsValidTarget(item, character, checkInventory: true) && !ignoredItems.Contains(item))
if (AIObjectiveCleanupItems.IsValidTarget(item, character, checkInventory: true, allowUnloading: false) && !ignoredItems.Contains(item))
{
itemsToClean.Add(item);
}
@@ -540,5 +540,17 @@ namespace Barotrauma
ignoredItems.Clear();
autonomousObjectiveRetryTimer = 10;
}
public override void OnDeselected()
{
base.OnDeselected();
foreach (var subObjective in SubObjectives)
{
if (subObjective is AIObjectiveCleanupItem cleanUpObjective)
{
cleanUpObjective.DropTarget();
}
}
}
}
}

View File

@@ -445,7 +445,7 @@ namespace Barotrauma
extraDistanceWhileSwimming = 100,
AllowGoingOutside = true,
IgnoreIfTargetDead = true,
followControlledCharacter = orderGiver == character,
followControlledCharacter = true,
mimic = true,
DialogueIdentifier = "dialogcannotreachplace"
};

View File

@@ -69,10 +69,9 @@ namespace Barotrauma
{
if (!isOrder)
{
if (reactor.LastUserWasPlayer && character.TeamID != CharacterTeamType.FriendlyNPC ||
HumanAIController.IsTrueForAnyCrewMember(c =>
c.ObjectiveManager.CurrentOrder is AIObjectiveOperateItem operateOrder && operateOrder.GetTarget() == target))
if (reactor.LastUserWasPlayer && character.TeamID != CharacterTeamType.FriendlyNPC)
{
// The reactor was previously operated by a player -> ignore.
Priority = 0;
return Priority;
}
@@ -89,7 +88,6 @@ namespace Barotrauma
case "powerup":
// Check that we don't already have another order that is targeting the same item.
// Without this the autonomous objective will tell the bot to turn the reactor on again.
if (IsAnotherOrderTargetingSameItem(objectiveManager.ForcedOrder) || objectiveManager.CurrentOrders.Any(o => IsAnotherOrderTargetingSameItem(o.Objective)))
{
Priority = 0;
@@ -177,9 +175,9 @@ namespace Barotrauma
}
if (operateTarget != null)
{
if (HumanAIController.IsTrueForAnyCrewMember(other => other != HumanAIController && other.ObjectiveManager.GetActiveObjective() is AIObjectiveOperateItem operateObjective && operateObjective.operateTarget == operateTarget))
if (HumanAIController.IsTrueForAnyCrewMember(other => other != HumanAIController && other.Character.IsBot && other.ObjectiveManager.GetActiveObjective() is AIObjectiveOperateItem operateObjective && operateObjective.operateTarget == operateTarget))
{
// Another crew member is already targeting this entity.
// Another crew member is already targeting this entity (leak).
Abandon = true;
return;
}

View File

@@ -471,13 +471,15 @@ namespace Barotrauma
}
}
private double pressureProtectionLastSet;
private float pressureProtection;
public float PressureProtection
{
get { return pressureProtection; }
set
{
pressureProtection = MathHelper.Clamp(value, 0.0f, 100.0f);
pressureProtection = Math.Max(value, 0.0f);
pressureProtectionLastSet = Timing.TotalTime;
}
}
@@ -528,11 +530,10 @@ namespace Barotrauma
public float Stun
{
get { return IsRagdolled ? 1.0f : CharacterHealth.StunTimer; }
get { return IsRagdolled ? 1.0f : CharacterHealth.Stun; }
set
{
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) return;
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
SetStun(value, true);
}
}
@@ -697,7 +698,7 @@ namespace Barotrauma
{
if (!canBeDragged) { return false; }
if (Removed || !AnimController.Draggable) { return false; }
return IsDead || Stun > 0.0f || LockHands || IsIncapacitated || IsPet;
return IsKnockedDown || LockHands || IsPet;
}
set { canBeDragged = value; }
}
@@ -715,7 +716,7 @@ namespace Barotrauma
}
else
{
return IsDead || Stun > 0.0f || LockHands || IsIncapacitated;
return IsKnockedDown || LockHands;
}
}
set { canInventoryBeAccessed = value; }
@@ -1016,7 +1017,7 @@ namespace Barotrauma
{
// Get the non husked name and find the ragdoll with it
var matchingAffliction = AfflictionPrefab.List
.Where(p => p.AfflictionType == "huskinfection")
.Where(p => p is AfflictionPrefabHusk)
.Select(p => p as AfflictionPrefabHusk)
.FirstOrDefault(p => p.TargetSpecies.Any(t => t.Equals(AfflictionHusk.GetNonHuskedSpeciesName(speciesName, p), StringComparison.OrdinalIgnoreCase)));
string nonHuskedSpeciesName = string.Empty;
@@ -1052,7 +1053,7 @@ namespace Barotrauma
else
{
AnimController = new FishAnimController(this, seed, ragdollParams as FishRagdollParams);
PressureProtection = 100.0f;
PressureProtection = int.MaxValue;
}
AnimController.SetPosition(ConvertUnits.ToSimUnits(position));
@@ -1271,7 +1272,13 @@ namespace Barotrauma
public float GetSkillLevel(string skillIdentifier)
{
return (Info == null || Info.Job == null) ? 0.0f : Info.Job.GetSkillLevel(skillIdentifier);
if (Info?.Job == null) { return 0.0f; }
float skillLevel = Info.Job.GetSkillLevel(skillIdentifier);
foreach (Affliction affliction in CharacterHealth.GetAllAfflictions())
{
skillLevel *= affliction.GetSkillMultiplier();
}
return skillLevel;
}
// TODO: reposition? there's also the overrideTargetMovement variable, but it's not in the same manner
@@ -2466,11 +2473,8 @@ namespace Barotrauma
if (NeedsAir)
{
bool protectedFromPressure = PressureProtection > 0.0f;
//cannot be protected from pressure when below crush depth
protectedFromPressure = protectedFromPressure && WorldPosition.Y > CharacterHealth.CrushDepth;
//implode if not protected from pressure, and either outside or in a high-pressure hull
if (!protectedFromPressure &&
if (!IsProtectedFromPressure() &&
(AnimController.CurrentHull == null || AnimController.CurrentHull.LethalPressure >= 80.0f))
{
if (CharacterHealth.PressureKillDelay <= 0.0f)
@@ -2656,7 +2660,10 @@ namespace Barotrauma
{
if (NeedsAir)
{
PressureProtection -= deltaTime * 100.0f;
if (Timing.TotalTime > pressureProtectionLastSet + 0.1)
{
PressureProtection = 0.0f;
}
}
if (NeedsWater)
{
@@ -3281,7 +3288,7 @@ namespace Barotrauma
GameMain.Config.RecentlyEncounteredCreatures.Add(other.SpeciesName);
}
public AttackResult DamageLimb(Vector2 worldPosition, Limb hitLimb, IEnumerable<Affliction> afflictions, float stun, bool playSound, float attackImpulse, Character attacker = null, float damageMultiplier = 1)
public AttackResult DamageLimb(Vector2 worldPosition, Limb hitLimb, IEnumerable<Affliction> afflictions, float stun, bool playSound, float attackImpulse, Character attacker = null, float damageMultiplier = 1, bool allowStacking = true)
{
if (Removed) { return new AttackResult(); }
@@ -3327,7 +3334,7 @@ namespace Barotrauma
bool wasDead = IsDead;
Vector2 simPos = hitLimb.SimPosition + ConvertUnits.ToSimUnits(dir);
AttackResult attackResult = hitLimb.AddDamage(simPos, afflictions, playSound, damageMultiplier: damageMultiplier);
CharacterHealth.ApplyDamage(hitLimb, attackResult);
CharacterHealth.ApplyDamage(hitLimb, attackResult, allowStacking);
if (attacker != this)
{
OnAttacked?.Invoke(attacker, attackResult);
@@ -3382,6 +3389,12 @@ namespace Barotrauma
}
}
/// <summary>
/// Is the character knocked down regardless whether the technical state is dead, unconcious, paralyzed, or stunned.
/// With stunning, the parameter uses a half a second delay before the character is treated as knocked down. The purpose of this is to ignore minor stunning. If you don't want to to ignore any stun, use the Stun property.
/// </summary>
public bool IsKnockedDown => IsDead || IsIncapacitated || CharacterHealth.StunTimer > 0.5f;
public void SetStun(float newStun, bool allowStunDecrease = false, bool isNetworkMessage = false)
{
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient && !isNetworkMessage) { return; }
@@ -3391,7 +3404,7 @@ namespace Barotrauma
{
AnimController.ResetPullJoints();
}
CharacterHealth.StunTimer = newStun;
CharacterHealth.Stun = newStun;
if (newStun > 0.0f)
{
SelectedConstruction = null;
@@ -3964,5 +3977,10 @@ namespace Barotrauma
public bool IsWatchman => HasJob("watchman");
public bool HasJob(string identifier) => Info?.Job?.Prefab.Identifier == identifier;
public bool IsProtectedFromPressure()
{
return PressureProtection >= (Level.Loaded?.GetRealWorldDepth(WorldPosition.Y) ?? 0.0f);
}
}
}

View File

@@ -961,6 +961,20 @@ namespace Barotrauma
public void Rename(string newName)
{
if (string.IsNullOrEmpty(newName)) { return; }
// Replace the name tag of any existing id cards or duffel bags
foreach (var item in Item.ItemList)
{
if (item.Prefab.Identifier != "idcard" && !item.Tags.Contains("despawncontainer")) { continue; }
foreach (var tag in item.Tags.Split(','))
{
var splitTag = tag.Split(":");
if (splitTag.Length < 2) { continue; }
if (splitTag[0] != "name") { continue; }
if (splitTag[1] != Name) { continue; }
item.ReplaceTag(tag, $"name:{newName}");
break;
}
}
Name = newName;
}

View File

@@ -14,6 +14,9 @@ namespace Barotrauma
public Dictionary<string, SerializableProperty> SerializableProperties { get; set; }
public float PendingAdditionStrenght { get; set; }
public float AdditionStrength { get; set; }
protected float _strength;
[Serialize(0f, true), Editable]
@@ -26,7 +29,12 @@ namespace Barotrauma
{
_nonClampedStrength = value;
}
_strength = MathHelper.Clamp(value, 0.0f, Prefab.MaxStrength);
float newValue = MathHelper.Clamp(value, 0.0f, Prefab.MaxStrength);
if (newValue > _strength)
{
PendingAdditionStrenght = Prefab.GrainBurst;
}
_strength = newValue;
}
}
@@ -56,6 +64,7 @@ namespace Barotrauma
public Affliction(AfflictionPrefab prefab, float strength)
{
Prefab = prefab;
PendingAdditionStrenght = Prefab.GrainBurst;
_strength = strength;
Identifier = prefab?.Identifier;
@@ -109,18 +118,25 @@ namespace Barotrauma
if (currentEffect == null) { return 0.0f; }
if (MathUtils.NearlyEqual(currentEffect.MaxGrainStrength, 0f)) { return 0.0f; }
return MathHelper.Lerp(
float amount = MathHelper.Lerp(
currentEffect.MinGrainStrength,
currentEffect.MaxGrainStrength,
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength));
if (Prefab.GrainBurst > 0 && AdditionStrength > amount)
{
return AdditionStrength;
}
return amount;
}
public float GetScreenDistortStrength()
{
if (Strength < Prefab.ActivationThreshold) return 0.0f;
if (Strength < Prefab.ActivationThreshold) { return 0.0f; }
AfflictionPrefab.Effect currentEffect = Prefab.GetActiveEffect(Strength);
if (currentEffect == null) return 0.0f;
if (currentEffect.MaxScreenDistortStrength - currentEffect.MinScreenDistortStrength <= 0.0f) return 0.0f;
if (currentEffect == null) { return 0.0f; }
if (currentEffect.MaxScreenDistortStrength - currentEffect.MinScreenDistortStrength < 0.0f) { return 0.0f; }
return MathHelper.Lerp(
currentEffect.MinScreenDistortStrength,
@@ -130,10 +146,10 @@ namespace Barotrauma
public float GetRadialDistortStrength()
{
if (Strength < Prefab.ActivationThreshold) return 0.0f;
if (Strength < Prefab.ActivationThreshold) { return 0.0f; }
AfflictionPrefab.Effect currentEffect = Prefab.GetActiveEffect(Strength);
if (currentEffect == null) return 0.0f;
if (currentEffect.MaxRadialDistortStrength - currentEffect.MinRadialDistortStrength <= 0.0f) return 0.0f;
if (currentEffect == null) { return 0.0f; }
if (currentEffect.MaxRadialDistortStrength - currentEffect.MinRadialDistortStrength < 0.0f) { return 0.0f; }
return MathHelper.Lerp(
currentEffect.MinRadialDistortStrength,
@@ -143,10 +159,10 @@ namespace Barotrauma
public float GetChromaticAberrationStrength()
{
if (Strength < Prefab.ActivationThreshold) return 0.0f;
if (Strength < Prefab.ActivationThreshold) { return 0.0f; }
AfflictionPrefab.Effect currentEffect = Prefab.GetActiveEffect(Strength);
if (currentEffect == null) return 0.0f;
if (currentEffect.MaxChromaticAberrationStrength - currentEffect.MinChromaticAberrationStrength <= 0.0f) return 0.0f;
if (currentEffect == null) { return 0.0f; }
if (currentEffect.MaxChromaticAberrationStrength - currentEffect.MinChromaticAberrationStrength < 0.0f) { return 0.0f; }
return MathHelper.Lerp(
currentEffect.MinChromaticAberrationStrength,
@@ -156,10 +172,10 @@ namespace Barotrauma
public float GetScreenBlurStrength()
{
if (Strength < Prefab.ActivationThreshold) return 0.0f;
if (Strength < Prefab.ActivationThreshold) { return 0.0f; }
AfflictionPrefab.Effect currentEffect = Prefab.GetActiveEffect(Strength);
if (currentEffect == null) return 0.0f;
if (currentEffect.MaxScreenBlurStrength - currentEffect.MinScreenBlurStrength <= 0.0f) return 0.0f;
if (currentEffect == null) { return 0.0f; }
if (currentEffect.MaxScreenBlurStrength - currentEffect.MinScreenBlurStrength < 0.0f) { return 0.0f; }
return MathHelper.Lerp(
currentEffect.MinScreenBlurStrength,
@@ -167,6 +183,20 @@ namespace Barotrauma
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength));
}
public float GetSkillMultiplier()
{
if (Strength < Prefab.ActivationThreshold) { return 1.0f; }
AfflictionPrefab.Effect currentEffect = Prefab.GetActiveEffect(Strength);
if (currentEffect == null) { return 1.0f; }
float amount = MathHelper.Lerp(
currentEffect.MinSkillMultiplier,
currentEffect.MaxSkillMultiplier,
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength));
return amount;
}
public void CalculateDamagePerSecond(float currentVitalityDecrease)
{
DamagePerSecond = Math.Max(DamagePerSecond, currentVitalityDecrease - PreviousVitalityDecrease);
@@ -245,6 +275,21 @@ namespace Barotrauma
{
ApplyStatusEffect(statusEffect, deltaTime, characterHealth, targetLimb);
}
float amount = deltaTime;
if (Prefab.GrainBurst > 0)
{
amount /= Prefab.GrainBurst;
}
if (PendingAdditionStrenght >= 0)
{
AdditionStrength += amount;
PendingAdditionStrenght -= deltaTime;
}
else if (AdditionStrength > 0)
{
AdditionStrength -= amount;
}
}
public void ApplyStatusEffect(StatusEffect statusEffect, float deltaTime, CharacterHealth characterHealth, Limb targetLimb)
@@ -267,7 +312,7 @@ namespace Barotrauma
{
var targets = new List<ISerializableEntity>();
statusEffect.GetNearbyTargets(characterHealth.Character.WorldPosition, targets);
statusEffect.Apply(ActionType.OnActive, deltaTime, targetLimb.character, targets);
statusEffect.Apply(ActionType.OnActive, deltaTime, characterHealth.Character, targets);
}
}

View File

@@ -134,6 +134,8 @@ namespace Barotrauma
public float MinSpeedMultiplier, MaxSpeedMultiplier;
public float MinBuffMultiplier, MaxBuffMultiplier;
public float MinSkillMultiplier, MaxSkillMultiplier;
public float MinResistance, MaxResistance;
public string ResistanceFor;
public string DialogFlag;
@@ -172,6 +174,9 @@ namespace Barotrauma
MaxScreenBlurStrength = element.GetAttributeFloat("maxscreenblur", 0.0f);
MaxScreenBlurStrength = Math.Max(MinScreenBlurStrength, MaxScreenBlurStrength);
MinSkillMultiplier = element.GetAttributeFloat("minskillmultiplier", 1.0f);
MaxSkillMultiplier = element.GetAttributeFloat("maxskillmultiplier", 1.0f);
ResistanceFor = element.GetAttributeString("resistancefor", "");
MinResistance = element.GetAttributeFloat("minresistance", 0.0f);
MaxResistance = element.GetAttributeFloat("maxresistance", 0.0f);
@@ -292,6 +297,8 @@ namespace Barotrauma
public readonly float ShowIconToOthersThreshold = 0.05f;
public readonly float MaxStrength = 100.0f;
public readonly float GrainBurst;
//how high the strength has to be for the affliction icon to be shown with a health scanner
public readonly float ShowInHealthScannerThreshold = 0.05f;
@@ -451,6 +458,7 @@ namespace Barotrauma
prefab = new AfflictionPrefab(sourceElement, file.Path, typeof(AfflictionBleeding));
break;
case "huskinfection":
case "alieninfection":
prefab = new AfflictionPrefabHusk(sourceElement, file.Path, typeof(AfflictionHusk));
break;
case "cprsettings":
@@ -521,7 +529,7 @@ namespace Barotrauma
if (prefab != null)
{
loadedAfflictions.Add((prefab, element));
loadedAfflictions.Add((prefab, sourceElement));
Prefabs.Add(prefab, isOverride);
prefab.CalculatePrefabUIntIdentifier(Prefabs);
}
@@ -582,6 +590,7 @@ namespace Barotrauma
ShowIconThreshold = element.GetAttributeFloat("showiconthreshold", Math.Max(ActivationThreshold, 0.05f));
ShowIconToOthersThreshold = element.GetAttributeFloat("showicontoothersthreshold", ShowIconThreshold);
MaxStrength = element.GetAttributeFloat("maxstrength", 100.0f);
GrainBurst = element.GetAttributeFloat(nameof(GrainBurst).ToLower(), 0.0f);
ShowInHealthScannerThreshold = element.GetAttributeFloat("showinhealthscannerthreshold", Math.Max(ActivationThreshold, 0.05f));
TreatmentThreshold = element.GetAttributeFloat("treatmentthreshold", Math.Max(ActivationThreshold, 5.0f));

View File

@@ -190,12 +190,14 @@ namespace Barotrauma
set { bloodlossAffliction.Strength = MathHelper.Clamp(value, 0.0f, 100.0f); }
}
public float StunTimer
public float Stun
{
get { return stunAffliction.Strength; }
set { stunAffliction.Strength = MathHelper.Clamp(value, 0.0f, stunAffliction.Prefab.MaxStrength); }
}
public float StunTimer { get; private set; }
public Affliction PressureAffliction
{
get { return pressureAffliction; }
@@ -488,7 +490,7 @@ namespace Barotrauma
CalculateVitality();
}
public void ApplyDamage(Limb hitLimb, AttackResult attackResult)
public void ApplyDamage(Limb hitLimb, AttackResult attackResult, bool allowStacking = true)
{
if (Unkillable || Character.GodMode) { return; }
if (hitLimb.HealthIndex < 0 || hitLimb.HealthIndex >= limbHealths.Count)
@@ -502,11 +504,11 @@ namespace Barotrauma
{
if (newAffliction.Prefab.LimbSpecific)
{
AddLimbAffliction(hitLimb, newAffliction);
AddLimbAffliction(hitLimb, newAffliction, allowStacking);
}
else
{
AddAffliction(newAffliction);
AddAffliction(newAffliction, allowStacking);
}
}
}
@@ -573,7 +575,7 @@ namespace Barotrauma
CalculateVitality();
}
private void AddLimbAffliction(Limb limb, Affliction newAffliction)
private void AddLimbAffliction(Limb limb, Affliction newAffliction, bool allowStacking = true)
{
if (!newAffliction.Prefab.LimbSpecific || limb == null) { return; }
if (limb.HealthIndex < 0 || limb.HealthIndex >= limbHealths.Count)
@@ -582,10 +584,10 @@ namespace Barotrauma
"\" only has health configured for" + limbHealths.Count + " limbs but the limb " + limb.type + " is targeting index " + limb.HealthIndex);
return;
}
AddLimbAffliction(limbHealths[limb.HealthIndex], newAffliction);
AddLimbAffliction(limbHealths[limb.HealthIndex], newAffliction, allowStacking);
}
private void AddLimbAffliction(LimbHealth limbHealth, Affliction newAffliction)
private void AddLimbAffliction(LimbHealth limbHealth, Affliction newAffliction, bool allowStacking = true)
{
if (!DoesBleed && newAffliction is AfflictionBleeding) { return; }
if (!Character.NeedsOxygen && newAffliction.Prefab == AfflictionPrefab.OxygenLow) { return; }
@@ -594,7 +596,15 @@ namespace Barotrauma
{
if (newAffliction.Prefab == affliction.Prefab)
{
affliction.Strength = Math.Min(affliction.Prefab.MaxStrength, affliction.Strength + (newAffliction.Strength * (100.0f / MaxVitality) * (1f - GetResistance(affliction.Prefab.Identifier))));
float newStrength = newAffliction.Strength * (100.0f / MaxVitality) * (1f - GetResistance(affliction.Prefab.Identifier));
if (allowStacking)
{
// Add the existing strength
newStrength += affliction.Strength;
}
newStrength = Math.Min(affliction.Prefab.MaxStrength, newStrength);
if (affliction == stunAffliction) { Character.SetStun(newStrength, true, true); }
affliction.Strength = newStrength;
affliction.Source = newAffliction.Source;
CalculateVitality();
if (Vitality <= MinVitality)
@@ -624,13 +634,12 @@ namespace Barotrauma
#endif
}
private void AddAffliction(Affliction newAffliction)
private void AddAffliction(Affliction newAffliction, bool allowStacking = true)
{
if (!DoesBleed && newAffliction is AfflictionBleeding) { return; }
if (!Character.NeedsOxygen && newAffliction.Prefab == AfflictionPrefab.OxygenLow) { return; }
if (newAffliction.Prefab.AfflictionType == "huskinfection")
if (newAffliction.Prefab is AfflictionPrefabHusk huskPrefab)
{
var huskPrefab = newAffliction.Prefab as AfflictionPrefabHusk;
if (huskPrefab.TargetSpecies.None(s => s.Equals(Character.SpeciesName, StringComparison.OrdinalIgnoreCase)))
{
return;
@@ -640,7 +649,13 @@ namespace Barotrauma
{
if (newAffliction.Prefab == affliction.Prefab)
{
float newStrength = Math.Min(affliction.Prefab.MaxStrength, affliction.Strength + (newAffliction.Strength * (100.0f / MaxVitality) * (1f - GetResistance(affliction.Prefab.Identifier))));
float newStrength = newAffliction.Strength * (100.0f / MaxVitality) * (1f - GetResistance(affliction.Prefab.Identifier));
if (allowStacking)
{
// Add the existing strength
newStrength += affliction.Strength;
}
newStrength = Math.Min(affliction.Prefab.MaxStrength, newStrength);
if (affliction == stunAffliction) { Character.SetStun(newStrength, true, true); }
affliction.Strength = newStrength;
affliction.Source = newAffliction.Source;
@@ -676,6 +691,8 @@ namespace Barotrauma
{
UpdateOxygen(deltaTime);
StunTimer = Stun > 0 ? StunTimer + deltaTime : 0;
for (int i = 0; i < limbHealths.Count; i++)
{
for (int j = limbHealths[i].Afflictions.Count - 1; j >= 0; j--)
@@ -795,6 +812,13 @@ namespace Barotrauma
Vitality -= vitalityDecrease;
affliction.CalculateDamagePerSecond(vitalityDecrease);
}
#if CLIENT
if (IsUnconscious)
{
HintManager.OnCharacterUnconscious(Character);
}
#endif
}
private void Kill()

View File

@@ -79,6 +79,9 @@ namespace Barotrauma
[Serialize(0f, true), Editable]
public float SonarDisruption { get; set; }
[Serialize(0f, true), Editable]
public float DistantSonarRange { get; set; }
[Serialize(25000f, true, "If the character is farther than this (in pixels) from the sub and the players, it will be disabled. The halved value is used for triggering simple physics where the ragdoll is disabled and only the main collider is updated."), Editable(MinValueFloat = 10000f, MaxValueFloat = 100000f)]
public float DisableDistance { get; set; }

View File

@@ -609,7 +609,7 @@ namespace Barotrauma
commands.Add(new Command("giveaffliction", "giveaffliction [affliction name] [affliction strength] [character name]: Add an affliction to a character. If the name parameter is omitted, the affliction is added to the controlled character.", (string[] args) =>
{
if (args.Length < 2) return;
if (args.Length < 2) { return; }
AfflictionPrefab afflictionPrefab = AfflictionPrefab.List.FirstOrDefault(a =>
a.Name.Equals(args[0], StringComparison.OrdinalIgnoreCase) ||
@@ -626,9 +626,19 @@ namespace Barotrauma
return;
}
Character targetCharacter = (args.Length <= 2) ? Character.Controlled : FindMatchingCharacter(args.Skip(2).ToArray());
bool relativeStrength = false;
if (args.Length > 2)
{
bool.TryParse(args[2], out relativeStrength);
}
Character targetCharacter = (relativeStrength || args.Length <= 2) ? Character.Controlled : FindMatchingCharacter(args.Skip(2).ToArray());
if (targetCharacter != null)
{
if (relativeStrength)
{
afflictionStrength *= targetCharacter.MaxVitality / afflictionPrefab.MaxStrength;
}
targetCharacter.CharacterHealth.ApplyAffliction(targetCharacter.AnimController.MainLimb, afflictionPrefab.Instantiate(afflictionStrength));
}
},
@@ -734,19 +744,19 @@ namespace Barotrauma
List<EventPrefab> eventPrefabs = EventSet.GetAllEventPrefabs().Where(prefab => !string.IsNullOrWhiteSpace(prefab.Identifier)).ToList();
if (GameMain.GameSession?.EventManager != null && args.Length > 0)
{
EventPrefab newEvent = eventPrefabs.Find(prefab => string.Equals(prefab.Identifier, args[0], StringComparison.InvariantCultureIgnoreCase));
EventPrefab eventPrefab = eventPrefabs.Find(prefab => string.Equals(prefab.Identifier, args[0], StringComparison.InvariantCultureIgnoreCase));
if (newEvent != null)
if (eventPrefab != null)
{
var @event = newEvent.CreateInstance();
var newEvent = eventPrefab.CreateInstance();
if (newEvent == null)
{
NewMessage($"Could not initialize event {args[0]} because level did not meet requirements");
return;
}
GameMain.GameSession.EventManager.ActiveEvents.Add(@event);
@event.Init(true);
NewMessage($"Initialized event {newEvent.Identifier}", Color.Aqua);
GameMain.GameSession.EventManager.ActiveEvents.Add(newEvent);
newEvent.Init(true);
NewMessage($"Initialized event {eventPrefab.Identifier}", Color.Aqua);
return;
}
@@ -1002,7 +1012,7 @@ namespace Barotrauma
}
else
{
ThrowError("Could not set location reputation ({args[0]} is not a valid reputation value).");
ThrowError($"Could not set location reputation ({args[0]} is not a valid reputation value).");
}
}
else
@@ -1010,6 +1020,41 @@ namespace Barotrauma
ThrowError("Could not set location reputation (no active campaign).");
}
}, null, true));
commands.Add(new Command("setreputation", "setreputation [faction] [value]: Set the reputation of a cation to the specified value.", (string[] args) =>
{
if (args.Length < 2)
{
ThrowError("Insufficient arguments (expected 2)");
return;
}
if (GameMain.GameSession?.GameMode is CampaignMode campaign)
{
if (campaign.Factions.FirstOrDefault(f => f.Prefab.Identifier.Equals(args[0], StringComparison.OrdinalIgnoreCase)) is { } faction)
{
if (float.TryParse(args[1], NumberStyles.Any, CultureInfo.InvariantCulture, out float reputation))
{
faction.Reputation.Value = reputation;
}
else
{
ThrowError($"Could not set faction reputation ({args[1]} is not a valid reputation value).");
}
}
else
{
ThrowError($"Could not set faction reputation (faction {args[0]} not found).");
}
}
else
{
ThrowError("Could not set faction reputation (no active campaign).");
}
}, () =>
{
return new[] { FactionPrefab.Prefabs.Select(f => f.Identifier).ToArray() };
}, true));
commands.Add(new Command("fixitems", "fixitems: Repairs all items and restores them to full condition.", (string[] args) =>
{
@@ -1133,6 +1178,56 @@ namespace Barotrauma
UpgradePrefab.Prefabs.Select(c => c.Identifier).Distinct().ToArray()
};
}, true));
commands.Add(new Command("maxupgrades", "maxupgrades [category] [prefab]: Maxes out all upgrades or only specific one if given arguments.", args =>
{
UpgradeManager upgradeManager = GameMain.GameSession?.Campaign?.UpgradeManager;
if (upgradeManager == null)
{
ThrowError("This command can only be used in campaign.");
return;
}
string categoryIdentifier = null;
string prefabIdentifier = null;
switch (args.Length)
{
case 1:
categoryIdentifier = args[0];
break;
case 2:
categoryIdentifier = args[0];
prefabIdentifier = args[1];
break;
}
foreach (UpgradeCategory category in UpgradeCategory.Categories)
{
if (!string.IsNullOrWhiteSpace(categoryIdentifier) && !category.Identifier.Equals(categoryIdentifier, StringComparison.OrdinalIgnoreCase)) { continue; }
foreach (UpgradePrefab prefab in UpgradePrefab.Prefabs)
{
if (!prefab.UpgradeCategories.Contains(category)) { continue; }
if (!string.IsNullOrWhiteSpace(prefabIdentifier) && !prefab.Identifier.Equals(prefabIdentifier, StringComparison.OrdinalIgnoreCase)) { continue; }
int targetLevel = prefab.MaxLevel - upgradeManager.GetRealUpgradeLevel(prefab, category);
for (int i = 0; i < targetLevel; i++)
{
upgradeManager.PurchaseUpgrade(prefab, category, force: true);
}
NewMessage($"Upgraded {category.Identifier}.{prefab.Identifier} by {targetLevel} levels.", Color.DarkGreen);
}
}
NewMessage($"Start a new round to apply the upgrades.", Color.Lime);
}, () =>
{
return new[]
{
UpgradeCategory.Categories.Select(c => c.Identifier).Distinct().ToArray(),
UpgradePrefab.Prefabs.Select(c => c.Identifier).Distinct().ToArray()
};
}, true));
commands.Add(new Command("power", "power: Immediately powers up the submarine's nuclear reactor.", (string[] args) =>
{

View File

@@ -253,8 +253,8 @@ namespace Barotrauma
};
potentialSpawnPoints = potentialSpawnPoints.FindAll(wp => wp.ConnectedDoor == null && wp.Ladders == null && !wp.isObstructed);
var airlockSpawnPoints = potentialSpawnPoints.Where(wp => wp.CurrentHull?.OutpostModuleTags?.Contains("airlock") ?? false).ToList();
var airlockSpawnPoints = potentialSpawnPoints.Where(wp => wp.CurrentHull?.OutpostModuleTags?.Contains("airlock") ?? false).ToList();
if (moduleFlags != null && moduleFlags.Any())
{
List<WayPoint> spawnPoints = potentialSpawnPoints.Where(wp => wp.CurrentHull?.OutpostModuleTags?.Any(moduleFlags.Contains) ?? false).ToList();
@@ -303,6 +303,12 @@ namespace Barotrauma
return potentialSpawnPoints.GetRandom();
}
//avoid using waypoints if there's any actual spawnpoints available
if (validSpawnPoints.Any(wp => wp.SpawnType != SpawnType.Path))
{
validSpawnPoints = validSpawnPoints.Where(wp => wp.SpawnType != SpawnType.Path);
}
//if not trying to spawn at a tagged spawnpoint, favor spawnpoints without tags
if (spawnpointTags == null || !spawnpointTags.Any())
{

View File

@@ -6,12 +6,17 @@ namespace Barotrauma
{
class TagAction : EventAction
{
public enum SubType { Any= 0, Player = 1, Outpost = 2, Wreck = 4, BeaconStation = 8 }
[Serialize("", true)]
public string Criteria { get; set; }
[Serialize("", true)]
public string Tag { get; set; }
[Serialize(SubType.Any, true)]
public SubType SubmarineType { get; set; }
[Serialize(true, true)]
public bool IgnoreIncapacitatedCharacters { get; set; }
@@ -63,17 +68,37 @@ namespace Barotrauma
private void TagStructuresByIdentifier(string identifier)
{
ParentEvent.AddTargetPredicate(Tag, e => e is Structure s && s.Prefab.Identifier.Equals(identifier, StringComparison.InvariantCultureIgnoreCase));
ParentEvent.AddTargetPredicate(Tag, e => e is Structure s && SubmarineTypeMatches(s.Submarine) && s.Prefab.Identifier.Equals(identifier, StringComparison.InvariantCultureIgnoreCase));
}
private void TagItemsByIdentifier(string identifier)
{
ParentEvent.AddTargetPredicate(Tag, e => e is Item it && it.Prefab.Identifier.Equals(identifier, StringComparison.InvariantCultureIgnoreCase));
ParentEvent.AddTargetPredicate(Tag, e => e is Item it && SubmarineTypeMatches(it.Submarine) && it.Prefab.Identifier.Equals(identifier, StringComparison.InvariantCultureIgnoreCase));
}
private void TagItemsByTag(string tag)
{
ParentEvent.AddTargetPredicate(Tag, e => e is Item it && it.HasTag(tag));
ParentEvent.AddTargetPredicate(Tag, e => e is Item it && SubmarineTypeMatches(it.Submarine) && it.HasTag(tag));
}
private bool SubmarineTypeMatches(Submarine sub)
{
if (SubmarineType == SubType.Any) { return true; }
if (sub == null) { return false; }
switch (sub.Info.Type)
{
case Barotrauma.SubmarineType.Player:
return SubmarineType.HasFlag(SubType.Player);
case Barotrauma.SubmarineType.Outpost:
case Barotrauma.SubmarineType.OutpostModule:
return SubmarineType.HasFlag(SubType.Outpost);
case Barotrauma.SubmarineType.Wreck:
return SubmarineType.HasFlag(SubType.Wreck);
case Barotrauma.SubmarineType.BeaconStation:
return SubmarineType.HasFlag(SubType.BeaconStation);
default:
return false;
}
}
public override void Update(float deltaTime)
@@ -113,7 +138,7 @@ namespace Barotrauma
public override string ToDebugString()
{
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(TagAction)} -> (Criteria: {Criteria.ColorizeObject()}, Tag: {Tag.ColorizeObject()})";
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(TagAction)} -> (Criteria: {Criteria.ColorizeObject()}, Tag: {Tag.ColorizeObject()}, Sub: {SubmarineType.ColorizeObject()})";
}
}
}

View File

@@ -134,7 +134,7 @@ namespace Barotrauma
if (level?.LevelData?.Type == LevelData.LevelType.Outpost)
{
//if the outpost is connected to a locked connection, create an event to unlock it
if (level.StartLocation?.Connections.Any(c => c.Locked) ?? false)
if (level.StartLocation?.Connections.Any(c => c.Locked && level.StartLocation.MapPosition.X < c.OtherLocation(level.StartLocation).MapPosition.X) ?? false)
{
var unlockPathPrefabs = EventSet.PrefabList.FindAll(e => e.UnlockPathEvent);
var unlockPathPrefabsForBiome = unlockPathPrefabs.FindAll(e =>
@@ -166,11 +166,14 @@ namespace Barotrauma
void AddChildEvents(EventSet eventSet)
{
if (eventSet == null) { return; }
foreach (EventPrefab ep in eventSet.EventPrefabs.Select(e => e.First))
if (eventSet.OncePerOutpost)
{
if (!level.LevelData.NonRepeatableEvents.Contains(ep))
foreach (EventPrefab ep in eventSet.EventPrefabs.Select(e => e.First))
{
level.LevelData.NonRepeatableEvents.Add(ep);
if (!level.LevelData.NonRepeatableEvents.Contains(ep))
{
level.LevelData.NonRepeatableEvents.Add(ep);
}
}
}
foreach (EventSet childSet in eventSet.ChildSets)
@@ -373,6 +376,8 @@ namespace Barotrauma
private void CreateEvents(EventSet eventSet, Random rand)
{
if (level == null) { return; }
if (level.LevelData.HasHuntingGrounds && eventSet.DisableInHuntingGrounds) { return; }
int applyCount = 1;
List<Func<Level.InterestingPosition, bool>> spawnPosFilter = new List<Func<Level.InterestingPosition, bool>>();
if (eventSet.PerRuin)

View File

@@ -86,6 +86,7 @@ namespace Barotrauma
public readonly bool IgnoreCoolDown;
public readonly bool PerRuin, PerCave, PerWreck;
public readonly bool DisableInHuntingGrounds;
public readonly bool OncePerOutpost;
@@ -142,9 +143,10 @@ namespace Barotrauma
PerRuin = element.GetAttributeBool("perruin", false);
PerCave = element.GetAttributeBool("percave", false);
PerWreck = element.GetAttributeBool("perwreck", false);
DisableInHuntingGrounds = element.GetAttributeBool("disableinhuntinggrounds", false);
IgnoreCoolDown = element.GetAttributeBool("ignorecooldown", parentSet?.IgnoreCoolDown ?? (PerRuin || PerCave || PerWreck));
DelayWhenCrewAway = element.GetAttributeBool("delaywhencrewaway", !PerRuin && !PerCave && !PerWreck);
OncePerOutpost = element.GetAttributeBool("perwreck", false);
OncePerOutpost = element.GetAttributeBool("onceperoutpost", false);
TriggerEventCooldown = element.GetAttributeBool("triggereventcooldown", true);
Commonness[""] = 1.0f;

View File

@@ -54,10 +54,6 @@ namespace Barotrauma
requireRescue.Clear();
var submarine = Submarine.Loaded.Find(s => s.Info.Type == SubmarineType.Outpost) ?? Submarine.MainSub;
if (submarine.Info.Type == SubmarineType.Outpost)
{
submarine.TeamID = CharacterTeamType.None;
}
if (!IsClient)
{
InitCharacters(submarine);
@@ -148,6 +144,10 @@ namespace Barotrauma
}
humanPrefab.InitializeCharacter(spawnedCharacter, spawnPos);
humanPrefab.GiveItems(spawnedCharacter, Submarine.MainSub, Rand.RandSync.Server, createNetworkEvents: false);
if (spawnPos is WayPoint wp)
{
spawnedCharacter.GiveIdCardTags(wp);
}
if (element.GetAttributeBool("requirekill", false))
{
requireKill.Add(spawnedCharacter);
@@ -175,6 +175,15 @@ namespace Barotrauma
{
characterItems.Add(spawnedCharacter, spawnedCharacter.Inventory.FindAllItems(recursive: true));
}
if (submarine != null && spawnedCharacter.AIController is EnemyAIController enemyAi)
{
enemyAi.UnattackableSubmarines.Add(submarine);
enemyAi.UnattackableSubmarines.Add(Submarine.MainSub);
foreach (Submarine sub in Submarine.MainSub.DockedTo)
{
enemyAi.UnattackableSubmarines.Add(sub);
}
}
}

View File

@@ -1,4 +1,5 @@
using Barotrauma.Items.Components;
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using System.Collections.Generic;
namespace Barotrauma
@@ -99,18 +100,18 @@ namespace Barotrauma
}
subs = new Submarine[] { Submarine.MainSubs[0], Submarine.MainSubs[1] };
subs[0].TeamID = CharacterTeamType.Team1; subs[1].TeamID = CharacterTeamType.Team2;
subs[0].NeutralizeBallast(); subs[1].NeutralizeBallast();
subs[0].NeutralizeBallast();
subs[0].TeamID = CharacterTeamType.Team1;
subs[0].DockedTo.ForEach(s => s.TeamID = CharacterTeamType.Team1);
subs[1].NeutralizeBallast();
subs[1].TeamID = CharacterTeamType.Team2;
subs[1].DockedTo.ForEach(s => s.TeamID = CharacterTeamType.Team2);
subs[1].SetPosition(subs[1].FindSpawnPos(Level.Loaded.EndPosition));
subs[1].FlipX();
crews = new List<Character>[] { new List<Character>(), new List<Character>() };
foreach (Submarine submarine in Submarine.Loaded)
{
//hide all subs from sonar to make sneak attacks possible
submarine.ShowSonarMarker = false;
}
}
public override void End()

View File

@@ -107,6 +107,11 @@ namespace Barotrauma
}
public readonly Location[] Locations;
public int? Difficulty
{
get { return Prefab.Difficulty; }
}
public Mission(MissionPrefab prefab, Location[] locations)
{
@@ -186,6 +191,9 @@ namespace Barotrauma
public void Start(Level level)
{
#if CLIENT
shownMessages.Clear();
#endif
foreach (string categoryToShow in Prefab.UnhideEntitySubCategories)
{
foreach (MapEntity entityToShow in MapEntity.mapEntityList.Where(me => me.prefab?.HasSubCategory(categoryToShow) ?? false))

View File

@@ -72,6 +72,8 @@ namespace Barotrauma
public readonly List<Tuple<string, object, SetDataAction.OperationType>> DataRewards = new List<Tuple<string, object, SetDataAction.OperationType>>();
public readonly int Commonness;
public readonly int? Difficulty;
public const int MinDifficulty = 1, MaxDifficulty = 4;
public readonly int Reward;
@@ -156,6 +158,11 @@ namespace Barotrauma
AllowRetry = element.GetAttributeBool("allowretry", false);
IsSideObjective = element.GetAttributeBool("sideobjective", false);
Commonness = element.GetAttributeInt("commonness", 1);
if (element.GetAttribute("difficulty") != null)
{
int difficulty = element.GetAttributeInt("difficulty", MinDifficulty);
Difficulty = Math.Clamp(difficulty, MinDifficulty, MaxDifficulty);
}
SuccessMessage = TextManager.Get("MissionSuccess." + TextIdentifier, true) ?? element.GetAttributeString("successmessage", "Mission completed successfully");
FailureMessage = TextManager.Get("MissionFailure." + TextIdentifier, true) ?? "";

View File

@@ -228,7 +228,7 @@ namespace Barotrauma
}
GiveReward();
completed = true;
if (level?.LevelData != null && Prefab.Tags.Any(t => t.Equals("huntinggrounds", StringComparison.OrdinalIgnoreCase)))
if (level?.LevelData != null && Prefab.Tags.Any(t => t.Equals("huntinggrounds", StringComparison.OrdinalIgnoreCase) || t.Equals("huntinggroundsnoreward", StringComparison.OrdinalIgnoreCase)))
{
level.LevelData.HasHuntingGrounds = false;
}

View File

@@ -310,7 +310,7 @@ namespace Barotrauma
{
foreach (Character npc in Character.CharacterList)
{
if (npc.TeamID != CharacterTeamType.FriendlyNPC || npc.CurrentHull == null || npc.IsIncapacitated) { continue; }
if ((npc.TeamID != CharacterTeamType.FriendlyNPC && npc.TeamID != CharacterTeamType.None) || npc.CurrentHull == null || npc.IsIncapacitated) { continue; }
if (npc.AIController is HumanAIController humanAI && (humanAI.ObjectiveManager.IsCurrentObjective<AIObjectiveFindSafety>() || humanAI.ObjectiveManager.IsCurrentObjective<AIObjectiveCombat>()))
{
continue;
@@ -327,10 +327,12 @@ namespace Barotrauma
{
if (npc.TeamID == CharacterTeamType.None)
{
dialogFlags.Remove("OutpostNPC");
dialogFlags.Add("Bandit");
}
else if (npc.TeamID == CharacterTeamType.FriendlyNPC)
{
dialogFlags.Remove("OutpostNPC");
dialogFlags.Add("Hostage");
}
}

View File

@@ -144,7 +144,7 @@ namespace Barotrauma
new XAttribute("value", valueStr),
new XAttribute("type", value?.GetType())));
}
#if DEBUG || UNSTABLE
#if DEBUG
DebugConsole.Log(element.ToString());
#endif
modeElement.Add(element);

View File

@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Networking;
using Barotrauma.Extensions;
namespace Barotrauma
{
@@ -258,22 +259,32 @@ namespace Barotrauma
if (levelData.HasBeaconStation && !levelData.IsBeaconActive)
{
var beaconMissionPrefab = MissionPrefab.List.Find(m => m.Tags.Any(t => t.Equals("beaconnoreward", StringComparison.OrdinalIgnoreCase)));
if (beaconMissionPrefab != null && !Missions.Any(m => m.Prefab.Type == beaconMissionPrefab.Type))
var beaconMissionPrefabs = MissionPrefab.List.FindAll(m => m.Tags.Any(t => t.Equals("beaconnoreward", StringComparison.OrdinalIgnoreCase)));
if (beaconMissionPrefabs.Any())
{
extraMissions.Add(beaconMissionPrefab.Instantiate(Map.SelectedConnection.Locations));
Random rand = new MTRandom(ToolBox.StringToInt(levelData.Seed));
var beaconMissionPrefab = beaconMissionPrefabs.GetRandom(rand);
if (!Missions.Any(m => m.Prefab.Type == beaconMissionPrefab.Type))
{
extraMissions.Add(beaconMissionPrefab.Instantiate(Map.SelectedConnection.Locations));
}
}
}
if (levelData.HasHuntingGrounds)
{
var huntingGroundsMissionPrefab = MissionPrefab.List.Find(m => m.Tags.Any(t => t.Equals("huntinggroundsnoreward", StringComparison.OrdinalIgnoreCase)));
if (huntingGroundsMissionPrefab == null)
var huntingGroundsMissionPrefabs = MissionPrefab.List.FindAll(m => m.Tags.Any(t => t.Equals("huntinggroundsnoreward", StringComparison.OrdinalIgnoreCase)));
if (!huntingGroundsMissionPrefabs.Any())
{
DebugConsole.AddWarning("Could not find a hunting grounds mission for the level. No mission with the tag \"huntinggroundsnoreward\" found.");
}
else if (!Missions.Any(m => m.Prefab.Type == huntingGroundsMissionPrefab.Type))
else
{
extraMissions.Add(huntingGroundsMissionPrefab.Instantiate(Map.SelectedConnection.Locations));
Random rand = new MTRandom(ToolBox.StringToInt(levelData.Seed));
var huntingGroundsMissionPrefab = huntingGroundsMissionPrefabs.GetRandom(rand);
if (!Missions.Any(m => m.Prefab.Type == huntingGroundsMissionPrefab.Type))
{
extraMissions.Add(huntingGroundsMissionPrefab.Instantiate(Map.SelectedConnection.Locations));
}
}
}
}
@@ -524,11 +535,13 @@ namespace Barotrauma
takenItems.Add(item);
}
}
map.CurrentLocation.RegisterTakenItems(takenItems);
map.CurrentLocation.AddToStock(CargoManager.SoldItems);
CargoManager.ClearSoldItemsProjSpecific();
map.CurrentLocation.RemoveFromStock(CargoManager.PurchasedItems);
if (map != null && CargoManager != null)
{
map.CurrentLocation.RegisterTakenItems(takenItems);
map.CurrentLocation.AddToStock(CargoManager.SoldItems);
CargoManager.ClearSoldItemsProjSpecific();
map.CurrentLocation.RemoveFromStock(CargoManager.PurchasedItems);
}
if (GameMain.NetworkMember == null)
{
CargoManager.ClearItemsInBuyCrate();
@@ -538,11 +551,11 @@ namespace Barotrauma
{
if (GameMain.NetworkMember.IsServer)
{
CargoManager.ClearItemsInBuyCrate();
CargoManager?.ClearItemsInBuyCrate();
}
else if (GameMain.NetworkMember.IsClient)
{
CargoManager.ClearItemsInSellCrate();
CargoManager?.ClearItemsInSellCrate();
}
}
@@ -601,7 +614,11 @@ namespace Barotrauma
}
foreach (Location location in Map.Locations)
{
location.ChangeType(location.OriginalType);
if (location.Type != location.OriginalType)
{
location.ChangeType(location.OriginalType);
location.PendingLocationTypeChange = null;
}
location.CreateStore(force: true);
location.ClearMissions();
location.Discovered = false;

Some files were not shown because too many files have changed in this diff Show More