Release 1.11.4.1 (Winter Update)

This commit is contained in:
Markus Isberg
2025-12-08 14:56:47 +00:00
parent 21e34e5cd8
commit 598966f200
121 changed files with 1614 additions and 819 deletions
@@ -9,7 +9,7 @@ namespace Barotrauma
{
public override void DebugDraw(SpriteBatch spriteBatch)
{
if (Character.IsUnconscious || !Character.Enabled || !Enabled) { return; }
if (Character.IsUnconscious || !Character.Enabled || !Enabled || Screen.Selected?.Cam is { Zoom: < 0.4f }) { return; }
Vector2 pos = Character.DrawPosition;
pos.Y = -pos.Y;
@@ -11,7 +11,7 @@ namespace Barotrauma
public override void DebugDraw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch)
{
if (Character == Character.Controlled) { return; }
if (!DebugAI) { return; }
if (!DebugAI || Screen.Selected?.Cam is { Zoom: < 0.4f }) { return; }
Vector2 pos = Character.DrawPosition;
pos.Y = -pos.Y;
Vector2 textOffset = new Vector2(-40, -160);
@@ -35,11 +35,7 @@ namespace Barotrauma
CharacterStateInfo serverPos = character.MemState.Last();
if (!character.isSynced)
{
SetPosition(serverPos.Position, lerp: false);
Collider.LinearVelocity = Vector2.Zero;
character.MemLocalState.Clear();
character.LastNetworkUpdateID = serverPos.ID;
character.isSynced = true;
SyncPosition(serverPos);
return;
}
@@ -198,11 +194,7 @@ namespace Barotrauma
if (!character.isSynced)
{
SetPosition(serverPos.Position, lerp: false);
Collider.LinearVelocity = Vector2.Zero;
character.MemLocalState.Clear();
character.LastNetworkUpdateID = serverPos.ID;
character.isSynced = true;
SyncPosition(serverPos);
return;
}
@@ -319,6 +311,15 @@ namespace Barotrauma
if (character.MemLocalState.Count > 120) { character.MemLocalState.RemoveRange(0, character.MemLocalState.Count - 120); }
character.MemState.Clear();
}
void SyncPosition(CharacterStateInfo serverPos)
{
SetPosition(serverPos.Position, lerp: false);
Collider.LinearVelocity = Vector2.Zero;
character.MemLocalState.Clear();
character.LastNetworkUpdateID = serverPos.ID;
character.isSynced = true;
}
}
/// <summary>
@@ -657,7 +658,7 @@ namespace Barotrauma
public void DebugDraw(SpriteBatch spriteBatch)
{
if (!GameMain.DebugDraw || !character.Enabled) { return; }
if (!GameMain.DebugDraw || !character.Enabled || Screen.Selected?.Cam is { Zoom: < 0.2f }) { return; }
if (simplePhysicsEnabled) { return; }
foreach (Limb limb in Limbs)
@@ -54,15 +54,29 @@ namespace Barotrauma
get { return controlled; }
set
{
if (controlled == value) return;
if ((!(controlled is null)) && (!(Screen.Selected?.Cam is null)) && value is null)
if (controlled == value && controlled == null)
{
Screen.Selected.Cam.TargetPos = Vector2.Zero;
Lights.LightManager.ViewTarget = null;
// Return early, but only when setting the controlled to null, because on controlling a character, we'll want to ensure that the target is both enabled and unfrozen.
return;
}
if (controlled != value)
{
CharacterHealth.OpenHealthWindow = null;
if (controlled != null && value == null)
{
if (Screen.Selected?.Cam is Camera camera)
{
camera.TargetPos = Vector2.Zero;;
}
Lights.LightManager.ViewTarget = null;
}
}
controlled = value;
if (controlled != null) controlled.Enabled = true;
CharacterHealth.OpenHealthWindow = null;
if (controlled != null)
{
controlled.Enabled = true;
controlled.AnimController.Frozen = false;
}
}
}
@@ -442,7 +456,7 @@ namespace Barotrauma
{
cam.OffsetAmount = targetOffsetAmount = maxOffset;
}
else if (SelectedItem != null && ViewTarget == null &&
else if (SelectedItem != null && ViewTarget == null && !IsIncapacitated &&
SelectedItem.Components.Any(ic => ic?.GuiFrame != null && ic.ShouldDrawHUD(this)))
{
cam.OffsetAmount = targetOffsetAmount = 0.0f;
@@ -456,7 +470,7 @@ namespace Barotrauma
}
else if (Lights.LightManager.ViewTarget == this)
{
if (GUI.PauseMenuOpen || IsUnconscious)
if (GUI.PauseMenuOpen || IsIncapacitated)
{
if (deltaTime > 0.0f)
{
@@ -23,8 +23,8 @@ namespace Barotrauma
memState.Clear();
return;
}
//freeze AI characters if more than x seconds have passed since last update from the server
//freeze other characters (than the controlled) if more than x seconds have passed since last update from the server
if (lastRecvPositionUpdateTime < Lidgren.Network.NetTime.Now - NetConfig.FreezeCharacterIfPositionDataMissingDelay)
{
AnimController.Frozen = true;
@@ -104,6 +104,8 @@ namespace Barotrauma
private GUILayoutGroup treatmentLayout;
private GUIListBox recommendedTreatmentContainer;
private LocalizedString prevHighlightedAfflictionDescription;
/// <summary>
/// Timer for updating visuals (limb tints and overlays) caused by the affliction
/// </summary>
@@ -120,7 +122,7 @@ namespace Barotrauma
private float updateDisplayedAfflictionsTimer;
private const float UpdateDisplayedAfflictionsInterval = 0.5f;
private List<Affliction> currentDisplayedAfflictions = new List<Affliction>();
private readonly List<Affliction> currentDisplayedAfflictions = new List<Affliction>();
public float DisplayedVitality, DisplayVitalityDelay;
@@ -662,7 +664,8 @@ namespace Barotrauma
else
{
forceAfflictionContainerUpdate = true;
currentDisplayedAfflictions = GetAllAfflictions(mergeSameAfflictions: true, predicate: a => a.ShouldShowIcon(Character) && a.Prefab.Icon != null);
currentDisplayedAfflictions.Clear();
currentDisplayedAfflictions.AddRange(GetAllAfflictions(mergeSameAfflictions: true, predicate: a => a.ShouldShowIcon(Character) && a.Prefab.Icon != null));
currentDisplayedAfflictions.Sort((a1, a2) =>
{
int dmgPerSecond = Math.Sign(a1.DamagePerSecond - a2.DamagePerSecond);
@@ -1165,7 +1168,6 @@ namespace Barotrauma
statusIconVisibleTime[afflictionPrefab] += deltaTime;
Color color = GetAfflictionIconColor(afflictionPrefab, affliction);
var matchingIcon =
afflictionIconContainer.GetChildByUserData(afflictionPrefab) ??
hiddenAfflictionIconContainer.GetChildByUserData(afflictionPrefab);
@@ -1177,9 +1179,20 @@ namespace Barotrauma
ToolTip = $"‖color:{color.ToStringHex()}‖{affliction.Prefab.Name}‖color:end‖",
CanBeSelected = false
};
new GUIImage(new RectTransform(Vector2.One, matchingIcon.RectTransform, Anchor.BottomCenter), afflictionPrefab.Icon, scaleToFit: true)
{
CanBeFocused = false
};
}
if (afflictionPrefab.HideIconAfterDelay && statusIconVisibleTime[afflictionPrefab] > HideStatusIconDelay)
{
matchingIcon.RectTransform.Parent = hiddenAfflictionIconContainer.RectTransform;
}
else
{
if (affliction.Prefab.ShowDescriptionInTooltip)
{
matchingIcon.ToolTip = matchingIcon.ToolTip + "\n" + affliction.Prefab.GetDescription(affliction.Strength, AfflictionPrefab.Description.TargetType.Self);
matchingIcon.ToolTip = $"‖color:{color.ToStringHex()}‖{affliction.Prefab.Name}‖color:end‖" + "\n" + affliction.Prefab.GetDescription(affliction.Strength, AfflictionPrefab.Description.TargetType.Self);
}
if (affliction == pressureAffliction)
{
@@ -1191,14 +1204,6 @@ namespace Barotrauma
}
matchingIcon.ToolTip = RichString.Rich(matchingIcon.ToolTip);
new GUIImage(new RectTransform(Vector2.One, matchingIcon.RectTransform, Anchor.BottomCenter), afflictionPrefab.Icon, scaleToFit: true)
{
CanBeFocused = false
};
}
if (afflictionPrefab.HideIconAfterDelay && statusIconVisibleTime[afflictionPrefab] > HideStatusIconDelay)
{
matchingIcon.RectTransform.Parent = hiddenAfflictionIconContainer.RectTransform;
}
var image = matchingIcon.GetChild<GUIImage>();
image.Color = color;
@@ -1574,12 +1579,14 @@ namespace Barotrauma
CanBeFocused = false
};
prevHighlightedAfflictionDescription = affliction.Prefab.GetDescription(
affliction.Strength,
Character == Character.Controlled ? AfflictionPrefab.Description.TargetType.Self : AfflictionPrefab.Description.TargetType.OtherCharacter);
var description = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), parent.RectTransform),
RichString.Rich(affliction.Prefab.GetDescription(
affliction.Strength,
Character == Character.Controlled ? AfflictionPrefab.Description.TargetType.Self : AfflictionPrefab.Description.TargetType.OtherCharacter)),
RichString.Rich(prevHighlightedAfflictionDescription),
textAlignment: Alignment.TopLeft, wrap: true)
{
UserData = "description",
CanBeFocused = false
};
@@ -1724,7 +1731,6 @@ namespace Barotrauma
var labelContainer = parent.GetChildByUserData("label");
var strengthText = labelContainer.GetChildByUserData("strength") as GUITextBlock;
strengthText.Text = affliction.GetStrengthText();
strengthText.TextColor = Color.Lerp(GUIStyle.Orange, GUIStyle.Red,
@@ -1743,6 +1749,19 @@ namespace Barotrauma
vitalityText.TextColor = vitalityDecrease <= 0 ? GUIStyle.Green :
Color.Lerp(GUIStyle.Orange, GUIStyle.Red, affliction.Strength / affliction.Prefab.MaxStrength);
}
var newDescription =
affliction.Prefab.GetDescription(
affliction.Strength,
Character == Character.Controlled ? AfflictionPrefab.Description.TargetType.Self : AfflictionPrefab.Description.TargetType.OtherCharacter);
if (newDescription != prevHighlightedAfflictionDescription)
{
if (parent.GetChildByUserData("description") is GUITextBlock descriptionText)
{
descriptionText.Text = RichString.Rich(newDescription);
}
prevHighlightedAfflictionDescription = newDescription;
}
}
public bool OnItemDropped(Item item, bool ignoreMousePos)
@@ -1,78 +1,48 @@
using Microsoft.Xna.Framework;
using Barotrauma.IO;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
namespace Barotrauma
{
partial class MissionPrefab : PrefabWithUintIdentifier
internal sealed partial class MissionPrefab : PrefabWithUintIdentifier
{
private ImmutableArray<Sprite> portraits = new ImmutableArray<Sprite>();
private ImmutableArray<Sprite> portraits = [];
public bool HasPortraits => portraits.Length > 0;
public Sprite Icon
{
get;
private set;
}
public Sprite Icon { get; private set; }
public Color IconColor { get; private set; }
public Color IconColor
{
get;
private set;
}
public bool DisplayTargetHudIcons
{
get;
private set;
}
public float HudIconMaxDistance
{
get;
private set;
}
public Sprite HudIcon
{
get
{
return hudIcon ?? Icon;
}
}
public Color HudIconColor
{
get
{
return hudIconColor ?? IconColor;
}
}
public Color ProgressBarColor { get; private set; }
public bool DisplayTargetHudIcons { get; private set; }
public float HudIconMaxDistance { get; private set; }
private Sprite hudIcon;
public Sprite HudIcon => hudIcon ?? Icon;
private Color? hudIconColor;
public Color HudIconColor => hudIconColor ?? IconColor;
public Color ProgressBarColor { get; private set; }
private ImmutableDictionary<int, Identifier> overrideMusicOnState;
partial void InitProjSpecific(ContentXElement element)
private void ParseConfigElementClient(ContentXElement element, MissionPrefab variantOf = null)
{
DisplayTargetHudIcons = element.GetAttributeBool("displaytargethudicons", false);
HudIconMaxDistance = element.GetAttributeFloat("hudiconmaxdistance", 1000.0f);
Dictionary<int, Identifier> overrideMusic = new Dictionary<int, Identifier>();
List<Sprite> portraits = new List<Sprite>();
foreach (var subElement in element.Elements())
HudIconMaxDistance = element.GetAttributeFloat("hudiconmaxdistance", 1000f);
Dictionary<int, Identifier> overrideMusic = [];
List<Sprite> portraits = [];
foreach (ContentXElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "icon":
Icon = new Sprite(subElement);
Icon = new Sprite(subElement, GetTexturePath(subElement, variantOf));
IconColor = subElement.GetAttributeColor("color", Color.White);
break;
case "hudicon":
hudIcon = new Sprite(subElement);
hudIcon = new Sprite(subElement, GetTexturePath(subElement, variantOf));
hudIconColor = subElement.GetAttributeColor("color");
break;
case "overridemusic":
@@ -81,7 +51,7 @@ namespace Barotrauma
subElement.GetAttributeIdentifier("type", Identifier.Empty));
break;
case "portrait":
var portrait = new Sprite(subElement, lazyLoad: true);
Sprite portrait = new(subElement, GetTexturePath(subElement, variantOf), lazyLoad: true);
if (portrait != null)
{
portraits.Add(portrait);
@@ -89,7 +59,7 @@ namespace Barotrauma
break;
}
}
this.portraits = portraits.ToImmutableArray();
this.portraits = [.. portraits];
overrideMusicOnState = overrideMusic.ToImmutableDictionary();
ProgressBarColor = element.GetAttributeColor(nameof(ProgressBarColor), GUIStyle.Blue);
}
@@ -109,6 +79,11 @@ namespace Barotrauma
return portraits[Math.Abs(randomSeed) % portraits.Length];
}
public string GetTexturePath(ContentXElement subElement, MissionPrefab variantOf = null)
=> subElement.DoesAttributeReferenceFileNameAlone("texture")
? Path.GetDirectoryName(variantOf?.ContentFile.Path ?? ContentFile.Path)
: "";
partial void DisposeProjectSpecific()
{
Icon?.Remove();
@@ -3338,7 +3338,13 @@ namespace Barotrauma
if (!CanIssueOrders) { return false; }
var character = userData as Character;
int priority = GetManualOrderPriority(character, order);
SetCharacterOrder(character, order.WithManualPriority(priority).WithOrderGiver(Character.Controlled));
Item targetEntity = null;
if (order.MustSetTarget && order.TargetEntity == null)
{
var matchingItems = order.GetMatchingItems(GetTargetSubmarine(), true, interactableFor: characterContext ?? Character.Controlled);
targetEntity = matchingItems.FirstOrDefault();
}
SetCharacterOrder(character, order.WithItemComponent(targetEntity).WithManualPriority(priority).WithOrderGiver(Character.Controlled));
DisableCommandUI();
return true;
}
@@ -93,7 +93,7 @@ namespace Barotrauma
public override void ShowStartMessage()
{
foreach (Mission mission in Missions.ToList())
foreach (Mission mission in Missions.OrderBy(m => m.Prefab.IsSideObjective).ToList())
{
if (!mission.Prefab.ShowStartMessage) { continue; }
new GUIMessageBox(
@@ -112,8 +112,16 @@ namespace Barotrauma
GUITextBlock.AutoScaleAndNormalize(newCampaignButton.TextBlock, loadCampaignButton.TextBlock);
GameMain.NetLobbyScreen.CampaignSetupUI.StartNewGame = GameMain.Client.SetupNewCampaign;
GameMain.NetLobbyScreen.CampaignSetupUI.LoadGame = GameMain.Client.SetupLoadCampaign;
GameMain.NetLobbyScreen.CampaignSetupUI.StartNewGame = (SubmarineInfo sub, string saveName, string mapSeed, CampaignSettings settings) =>
{
GameMain.NetLobbyScreen.SetAFKSelected(false);
GameMain.Client.SetupNewCampaign(sub, saveName, mapSeed, settings);
};
GameMain.NetLobbyScreen.CampaignSetupUI.LoadGame = (string filePath, Option<uint> backupIndex) =>
{
GameMain.NetLobbyScreen.SetAFKSelected(false);
GameMain.Client.SetupLoadCampaign(filePath, backupIndex);
};
}
partial void InitProjSpecific()
@@ -169,6 +177,7 @@ namespace Barotrauma
{
StartRound = () =>
{
GameMain.NetLobbyScreen.SetAFKSelected(false);
GameMain.Client.RequestStartRound();
}
};
@@ -1,9 +1,9 @@
using Microsoft.Xna.Framework;
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
namespace Barotrauma
{
@@ -22,6 +22,9 @@ namespace Barotrauma
private GUIButton createEventButton;
public LevelGenerationParams BackgroundParams { get; private set; }
public Vector2 WaterParticleOffset;
public override void Start()
{
base.Start();
@@ -68,6 +71,12 @@ namespace Barotrauma
}
};
}
if (Level.Loaded == null)
{
BackgroundParams ??= LevelGenerationParams.LevelParams.Where(lp => !lp.AllowedBiomeIdentifiers.Contains("endzone")).GetRandom(Rand.RandSync.Unsynced);
GameMain.LightManager.AmbientLight = BackgroundParams.AmbientLightColor;
}
}
public override void AddToGUIUpdateList()
@@ -93,6 +102,8 @@ namespace Barotrauma
sEvent.Update(deltaTime);
}
}
BackgroundParams?.UpdateWaterParticleOffset(ref WaterParticleOffset, BackgroundParams.WaterParticleVelocity, deltaTime);
}
private void GenerateOutpost(Submarine submarine)
@@ -173,7 +173,8 @@ namespace Barotrauma
List<Mission> missionsToDisplay = new List<Mission>(selectedMissions.Where(m => m.Prefab.ShowInMenus));
if (startLocation != null)
{
foreach (Mission mission in startLocation.SelectedMissions)
//side objectives can't be selected manually (they're always selected), we need to add them separately from SelectedMissions
foreach (Mission mission in startLocation.SelectedMissions.Union(startLocation.AvailableMissions.Where(m => m.Prefab.IsSideObjective)))
{
if (missionsToDisplay.Contains(mission)) { continue; }
if (!mission.Prefab.ShowInMenus) { continue; }
@@ -119,8 +119,7 @@ namespace Barotrauma.Items.Components
if (!attached)
{
Drop(false, null);
item.SetTransform(simPosition, 0.0f);
item.Submarine = sub;
item.SetTransform(simPosition, 0.0f, forceSubmarine: sub);
AttachToWall();
PlaySound(ActionType.OnUse, attacher);
ApplyStatusEffects(ActionType.OnUse, (float)Timing.Step, character: attacher, user: attacher);
@@ -142,8 +141,7 @@ namespace Barotrauma.Items.Components
}
else
{
item.SetTransform(simPosition, 0.0f);
item.Submarine = sub;
item.SetTransform(simPosition, 0.0f, forceSubmarine: sub);
}
}
}
@@ -28,8 +28,8 @@ namespace Barotrauma.Items.Components
set
{
_chargeSoundWindupPitchSlide = new Vector2(
Math.Max(value.X, SoundChannel.MinFrequencyMultiplier),
Math.Min(value.Y, SoundChannel.MaxFrequencyMultiplier));
MathHelper.Clamp(value.X, SoundChannel.MinFrequencyMultiplier, SoundChannel.MaxFrequencyMultiplier),
MathHelper.Clamp(value.Y, SoundChannel.MinFrequencyMultiplier, SoundChannel.MaxFrequencyMultiplier));
}
}
private Vector2 _chargeSoundWindupPitchSlide;
@@ -409,10 +409,10 @@ namespace Barotrauma.Items.Components
loopingSoundChannel.Looping = true;
loopingSoundChannel.Near = loopingSound.Range * 0.4f;
loopingSoundChannel.Far = loopingSound.Range;
}
if (loopingSound.RoundSound.Stream)
{
loopingSoundChannel.StreamSeekPos = loopingSound.RoundSound.LastStreamSeekPos;
if (loopingSound.RoundSound.Stream)
{
loopingSoundChannel.StreamSeekPos = loopingSound.RoundSound.LastStreamSeekPos;
}
}
}
}
@@ -434,12 +434,10 @@ namespace Barotrauma.Items.Components
foreach (FabricationRecipe fi in fabricationRecipes.Values)
{
RichString recipeTooltip = RichString.Rich(fi.TargetItem.Description);
if (fi.RequiresRecipe)
{
recipeTooltip += "\n\n" + $"‖color:{XMLExtensions.ToStringHex(GUIStyle.Red)}‖{TextManager.Get("fabricatorrequiresrecipe")}‖color:end‖";
}
recipeTooltip = RichString.Rich(recipeTooltip);
RichString recipeTooltip =
fi.RequiresRecipe ?
RichString.Rich(fi.TargetItem.Description + "\n\n" + $"‖color:{XMLExtensions.ToStringHex(GUIStyle.Red)}‖{TextManager.Get("fabricatorrequiresrecipe")}‖color:end‖") :
RichString.Rich(fi.TargetItem.Description);
var frame = new GUIFrame(new RectTransform(new Point(itemList.Content.Rect.Width, (int)(40 * GUI.yScale)), itemList.Content.RectTransform), style: null)
{
@@ -894,10 +892,11 @@ namespace Barotrauma.Items.Components
if (outputContainer.Inventory.IsEmpty())
{
var itemIcon = targetItem.TargetItem.InventoryIcon ?? targetItem.TargetItem.Sprite;
Color iconColor = itemIcon == targetItem.TargetItem.Sprite ? targetItem.TargetItem.SpriteColor : targetItem.TargetItem.InventoryIconColor;
itemIcon.Draw(
spriteBatch,
slotRect.Center.ToVector2(),
color: Color.Lerp(targetItem.TargetItem.InventoryIconColor, Color.TransparentBlack, 0.5f),
color: Color.Lerp(iconColor, Color.TransparentBlack, 0.5f),
scale: Math.Min(slotRect.Width / itemIcon.size.X, slotRect.Height / itemIcon.size.Y) * 0.9f);
}
}
@@ -1132,24 +1131,27 @@ namespace Barotrauma.Items.Components
if (!selectedRecipe.TargetItem.Description.IsNullOrEmpty())
{
RichString richDescription = RichString.Rich(selectedRecipe.TargetItem.Description);
var descriptionParent = largeUI ? paddedReqFrame : paddedFrame;
var description = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), descriptionParent.RectTransform),
RichString.Rich(selectedRecipe.TargetItem.Description),
richDescription,
font: GUIStyle.SmallFont, wrap: true);
if (!largeUI)
{
description.Padding = new Vector4(0, description.Padding.Y, description.Padding.Z, description.Padding.W);
}
while (description.Rect.Height + nameBlock.Rect.Height > descriptionParent.Rect.Height)
while (description.Rect.Height + nameBlock.Rect.Height > descriptionParent.Rect.Height / 2)
{
var lines = description.WrappedText.Split('\n');
if (lines.Count <= 1) { break; }
var newString = string.Join('\n', lines.Take(lines.Count - 1));
string newString = string.Join('\n', lines.Take(lines.Count - 1));
description.Text = newString.Substring(0, newString.Length - 4) + "...";
description.CalculateHeightFromText();
description.ToolTip = selectedRecipe.TargetItem.Description;
description.ToolTip = richDescription;
}
description.Text.RetrieveValue();
}
IEnumerable<Skill> inadequateSkills = Enumerable.Empty<Skill>();
@@ -1328,7 +1330,10 @@ namespace Barotrauma.Items.Components
var childContainer = child.GetChild<GUILayoutGroup>();
childContainer.GetChild<GUITextBlock>().TextColor = baseColor * (canBeFabricated ? 1.0f : 0.5f);
childContainer.GetChild<GUIImage>().Color = recipe.TargetItem.InventoryIconColor * (canBeFabricated ? 1.0f : 0.5f);
GUIImage icon = childContainer.GetChild<GUIImage>();
Color iconColor = icon.Sprite == recipe.TargetItem.Sprite ? recipe.TargetItem.SpriteColor : recipe.TargetItem.InventoryIconColor;
childContainer.GetChild<GUIImage>().Color = iconColor * (canBeFabricated ? 1.0f : 0.5f);
var limitReachedText = child.FindChild(nameof(FabricationLimitReachedText));
limitReachedText.Visible = !canBeFabricated && fabricationLimits.TryGetValue(recipe.RecipeHash, out int amount) && amount <= 0;
@@ -137,46 +137,30 @@ namespace Barotrauma.Items.Components
{
foreach (var (position, emitter) in pumpOutEmitters)
{
if (item.CurrentHull != null && item.CurrentHull.Surface < item.Rect.Location.Y + position.Y) { continue; }
//only emit "pump out" particles when underwater
Vector2 relativeParticlePos = (item.WorldRect.Location.ToVector2() + position * item.Scale) - item.WorldPosition;
relativeParticlePos = MathUtils.RotatePoint(relativeParticlePos, item.FlippedX ? item.RotationRad : -item.RotationRad);
float angle = -item.RotationRad;
if (item.FlippedX)
{
relativeParticlePos.X = -relativeParticlePos.X;
angle += MathHelper.Pi;
}
if (item.FlippedY)
{
relativeParticlePos.Y = -relativeParticlePos.Y;
}
emitter.Emit(deltaTime, item.WorldPosition + relativeParticlePos, item.CurrentHull, angle,
velocityMultiplier: MathHelper.Lerp(0.5f, 1.0f, -currFlow / maxFlow));
if (item.CurrentHull != null && item.CurrentHull.Surface < item.Rect.Location.Y + position.Y) { continue; }
Emit(position, emitter);
}
}
else if (currFlow > 0f)
{
foreach (var (position, emitter) in pumpInEmitters)
{
Vector2 relativeParticlePos = (item.WorldRect.Location.ToVector2() + position * item.Scale) - item.WorldPosition;
relativeParticlePos = MathUtils.RotatePoint(relativeParticlePos, item.FlippedX ? item.RotationRad : -item.RotationRad);
float angle = -item.RotationRad;
if (item.FlippedX)
{
relativeParticlePos.X = -relativeParticlePos.X;
angle += MathHelper.Pi;
}
if (item.FlippedY)
{
relativeParticlePos.Y = -relativeParticlePos.Y;
}
emitter.Emit(deltaTime, item.WorldPosition + relativeParticlePos, item.CurrentHull, angle,
velocityMultiplier: MathHelper.Lerp(0.5f, 1.0f, currFlow / maxFlow));
Emit(position, emitter);
}
}
void Emit(Vector2 position, ParticleEmitter emitter)
{
Vector2 relativeParticlePos = (item.WorldRect.Location.ToVector2() + position * item.Scale) - item.WorldPosition;
if (item.FlippedX) { relativeParticlePos.X = -relativeParticlePos.X; }
if (item.FlippedY) { relativeParticlePos.Y = -relativeParticlePos.Y; }
relativeParticlePos = MathUtils.RotatePoint(relativeParticlePos, -item.RotationRad);
float angle = -item.RotationRad;
if (item.FlippedX) { angle += MathHelper.Pi; }
emitter.Emit(deltaTime, item.WorldPosition + relativeParticlePos, item.CurrentHull, angle,
velocityMultiplier: MathHelper.Lerp(0.5f, 1.0f, currFlow / maxFlow), mirrorAngle: item.FlippedX ^ item.FlippedY);
}
}
private float flickerTimer;
@@ -1406,7 +1406,8 @@ namespace Barotrauma.Items.Components
public void RegisterExplosion(Explosion explosion, Vector2 worldPosition)
{
if (Character.Controlled?.SelectedItem != item) { return; }
if (Character.Controlled?.SelectedItem == null) { return; }
if (Character.Controlled.SelectedItem != Item && !Character.Controlled.SelectedItem.linkedTo.Contains(Item)) { return; }
if (explosion.Attack.StructureDamage <= 0 && explosion.Attack.ItemDamage <= 0 && explosion.EmpStrength <= 0) { return; }
Vector2 transducerCenter = GetTransducerPos();
if (Vector2.DistanceSquared(worldPosition, transducerCenter) > range * range) { return; }
@@ -1564,7 +1565,15 @@ namespace Barotrauma.Items.Components
foreach (Item item in Item.SonarVisibleItems)
{
System.Diagnostics.Debug.Assert(item.Prefab.SonarSize > 0.0f);
if (item.CurrentHull == null)
if (item.CurrentHull != null) { continue; }
bool isItemVisible =
item.ParentInventory == null ||
item.GetComponent<Holdable>() is { IsActive: true } ||
item.Container?.GetComponent<ItemContainer>() is { HideItems: false };
if (isItemVisible)
{
float pointDist = ((item.WorldPosition - pingSource) * displayScale).LengthSquared();
if (pointDist > prevPingRadiusSqr && pointDist < pingRadiusSqr)
@@ -80,7 +80,7 @@ namespace Barotrauma.Items.Components
int connectorIntervalLeft = GetConnectorIntervalLeft(height, panel);
int connectorIntervalRight = GetConnectorIntervalRight(height, panel);
foreach (Connection c in panel.Connections)
foreach (Connection c in panel.Connections.OrderBy(static c => c.DisplayOrder))
{
//if dragging a wire, let the Inventory know so that the wire can be
//dropped or dragged from the panel to the players inventory
@@ -192,7 +192,7 @@ namespace Barotrauma.Items.Components
{
DrawWire(spriteBatch, equippedWire, new Vector2(x + width / 2, y + height - 150 * GUI.Scale),
new Vector2(x + width / 2, y + height),
null, panel, "");
null, panel, label: GetWireLabel(connection: null, wire: equippedWire));
if (DraggingConnected == equippedWire)
{
@@ -209,13 +209,9 @@ namespace Barotrauma.Items.Components
{
if (wire == DraggingConnected && mouseInRect) { continue; }
if (wire.HiddenInGame && Screen.Selected == GameMain.GameScreen) { continue; }
Connection recipient = wire.OtherConnection(null);
LocalizedString label = recipient == null ? "" : recipient.item.Name + $" ({recipient.DisplayName})";
if (wire.Locked) { label += "\n" + TextManager.Get("ConnectionLocked"); }
DrawWire(spriteBatch, wire, new Vector2(x, y + height - 100 * GUI.Scale),
new Vector2(x, y + height),
null, panel, label);
null, panel, label: GetWireLabel(connection: null, wire));
x += (int)step;
}
@@ -290,21 +286,7 @@ namespace Barotrauma.Items.Components
{
if (wire.Hidden || (DraggingConnected == wire && (mouseIn || Screen.Selected == GameMain.SubEditorScreen))) { continue; }
if (wire.HiddenInGame && Screen.Selected == GameMain.GameScreen) { continue; }
Connection recipient = wire.OtherConnection(this);
LocalizedString label;
if (wire.Item.IsLayerHidden)
{
label = TextManager.Get("ConnectionLocked");
}
else
{
label = recipient == null ? "" : recipient.item.Name + $" ({recipient.DisplayName})";
if (wire.Locked) { label += "\n" + TextManager.Get("ConnectionLocked"); }
}
DrawWire(spriteBatch, wire, position, wirePosition, equippedWire, panel, label);
DrawWire(spriteBatch, wire, position, wirePosition, equippedWire, panel, label: GetWireLabel(connection: this, wire: wire));
wirePosition.Y += wireInterval;
}
@@ -362,6 +344,22 @@ namespace Barotrauma.Items.Components
}
}
private static LocalizedString GetWireLabel(Connection connection, Wire wire)
{
Connection recipient = wire.OtherConnection(connection);
LocalizedString label;
if (wire.Item.IsLayerHidden)
{
label = TextManager.Get("ConnectionLocked");
}
else
{
label = recipient == null ? "" : recipient.item.Name + $" ({recipient.DisplayName})";
if (wire.Locked) { label += "\n" + TextManager.Get("ConnectionLocked"); }
}
return label;
}
public void Flash(Color? color = null, float flashDuration = 1.5f)
{
FlashTimer = flashDuration;
@@ -0,0 +1,12 @@
using Barotrauma.Networking;
namespace Barotrauma.Items.Components
{
partial class ConnectionSelectorComponent : ItemComponent
{
public void ClientEventRead(IReadMessage msg, float sendingTime)
{
SelectedConnection = msg.ReadRangedInteger(0, 255);
}
}
}
@@ -733,7 +733,7 @@ namespace Barotrauma
{
updateableComponents.Add(ic);
}
isActive = true;
IsActive = true;
}
}
@@ -2170,7 +2170,7 @@ namespace Barotrauma
return;
}
isActive = true;
IsActive = true;
if (positionBuffer.Count > 0)
{
@@ -2515,8 +2515,7 @@ namespace Barotrauma
{
inventory.TryPutItem(item, user: null, allowedSlots: item.AllowedSlots, createNetworkEvent: false);
}
item.SetTransform(inventory.Owner.SimPosition, 0.0f);
item.Submarine = inventory.Owner.Submarine;
item.SetTransform(inventory.Owner.SimPosition, 0.0f, forceSubmarine: inventory.Owner.Submarine);
if (inventory.Owner is Character { Enabled: false } && item.body != null)
{
item.body.Enabled = false;
@@ -230,7 +230,12 @@ namespace Barotrauma
if (!primaryMouseButtonHeld && !secondaryMouseButtonHeld && !doubleClicked && !secondaryDoubleClicked) { return; }
Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
Hull hull = FindHull(position);
Hull hull =
Screen.Selected is { IsEditor: true } ?
//use the unoptimized version in the editor to make sure newly placed hulls are found
//(FindHull uses the "EntityGrid" which is generated after loading the sub)
FindHullUnoptimized(position) :
FindHull(position);
if (hull == null || hull.IdFreed) { return; }
if (EditWater)
@@ -361,7 +366,7 @@ namespace Barotrauma
new Rectangle(drawRect.X, -drawRect.Y, rect.Width, rect.Height),
GUIStyle.Red * ((100.0f - OxygenPercentage) / 400.0f) * alpha, true, 0, (int)Math.Max(MathF.Ceiling(1.5f / Screen.Selected.Cam.Zoom), 1.0f));
if (GameMain.DebugDraw)
if (GameMain.DebugDraw && Screen.Selected?.Cam is { Zoom: > 0.5f })
{
GUIStyle.SmallFont.DrawString(spriteBatch, "Pressure: " + ((int)pressure - rect.Y).ToString() +
" - Oxygen: " + ((int)OxygenPercentage), new Vector2(drawRect.X + 5, -drawRect.Y + 5), Color.White);
@@ -0,0 +1,74 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
namespace Barotrauma;
internal partial class LevelGenerationParams : PrefabWithUintIdentifier
{
/// <remarks>Doesn't call SpriteBatch.Begin and SpriteBatch.End; They must be called manually.</remarks>
public void DrawBackgrounds(SpriteBatch spriteBatch, Camera cam)
{
if (BackgroundTopSprite == null) { return; }
Vector2 backgroundPos = cam.WorldViewCenter.FlipY() * 0.05f;
int backgroundSize = (int)BackgroundTopSprite.size.Y;
if (backgroundPos.Y >= backgroundSize) { return; }
if (backgroundPos.Y < 0f)
{
BackgroundTopSprite.SourceRect = new Rectangle((int)backgroundPos.X, (int)backgroundPos.Y, backgroundSize, (int)Math.Min(-backgroundPos.Y, backgroundSize));
BackgroundTopSprite.DrawTiled(spriteBatch, Vector2.Zero, new Vector2(GameMain.GraphicsWidth, Math.Min(-backgroundPos.Y, GameMain.GraphicsHeight)),
color: BackgroundTextureColor);
}
if (-backgroundPos.Y < GameMain.GraphicsHeight && BackgroundSprite != null)
{
BackgroundSprite.SourceRect = new Rectangle((int)backgroundPos.X, (int)Math.Max(backgroundPos.Y, 0), backgroundSize, backgroundSize);
BackgroundSprite.DrawTiled(spriteBatch, (backgroundPos.Y < 0f) ? new Vector2(0f, (int)-backgroundPos.Y) : Vector2.Zero,
new Vector2(GameMain.GraphicsWidth, (int)Math.Min(Math.Ceiling(backgroundSize - backgroundPos.Y), backgroundSize)),
color: BackgroundTextureColor);
}
}
/// <remarks>Doesn't call SpriteBatch.Begin and SpriteBatch.End; They must be called manually.</remarks>
public void DrawWaterParticles(SpriteBatch spriteBatch, Camera cam, Vector2 offset)
{
if (WaterParticles == null || cam.Zoom <= 0.05f) { return; }
float textureScale = WaterParticleScale;
Vector2 textureSize = new Vector2(WaterParticles.Texture.Width, WaterParticles.Texture.Height);
Vector2 origin = new Vector2(cam.WorldView.X, -cam.WorldView.Y);
offset -= origin;
// Draw 4 layers of particles.
for (int i = 0; i < 4; i++)
{
float scale = 1f - i * 0.2f;
float alpha = MathUtils.InverseLerp(0.05f, 0.1f, cam.Zoom * scale);
if (alpha == 0f) { continue; }
Vector2 newOffset = offset * scale;
newOffset += cam.WorldView.Size.ToVector2() * (1f - scale) * 0.5f;
newOffset -= new Vector2(256f * i);
float newTextureScale = scale * textureScale;
Vector2 newSize = textureSize * scale;
while (newOffset.X <= -newSize.X) { newOffset.X += newSize.X; }
while (newOffset.X > 0f) { newOffset.X -= newSize.X; }
while (newOffset.Y <= -newSize.Y) { newOffset.Y += newSize.Y; }
while (newOffset.Y > 0f) { newOffset.Y -= newSize.Y; }
WaterParticles.DrawTiled(spriteBatch, origin + newOffset, cam.WorldView.Size.ToVector2() - newOffset,
color: WaterParticleColor * alpha, textureScale: new Vector2(newTextureScale));
}
}
public void UpdateWaterParticleOffset(ref Vector2 offset, Vector2 velocity, float deltaTime)
{
if (WaterParticles == null) { return; }
Vector2 waterTextureSize = WaterParticles.size * WaterParticleScale;
offset += velocity.FlipY() * WaterParticleScale * deltaTime;
offset.X %= waterTextureSize.X;
offset.Y %= waterTextureSize.Y;
}
}
@@ -234,15 +234,7 @@ namespace Barotrauma
WaterRenderer.Instance?.ScrollWater(waterParticleVelocity, deltaTime);
if (level.GenerationParams.WaterParticles != null)
{
Vector2 waterTextureSize = level.GenerationParams.WaterParticles.size * level.GenerationParams.WaterParticleScale;
waterParticleOffset += new Vector2(waterParticleVelocity.X, -waterParticleVelocity.Y) * level.GenerationParams.WaterParticleScale * deltaTime;
while (waterParticleOffset.X <= -waterTextureSize.X) { waterParticleOffset.X += waterTextureSize.X; }
while (waterParticleOffset.X >= waterTextureSize.X){ waterParticleOffset.X -= waterTextureSize.X; }
while (waterParticleOffset.Y <= -waterTextureSize.Y) { waterParticleOffset.Y += waterTextureSize.Y; }
while (waterParticleOffset.Y >= waterTextureSize.Y) { waterParticleOffset.Y -= waterTextureSize.Y; }
}
level.GenerationParams.UpdateWaterParticleOffset(ref waterParticleOffset, waterParticleVelocity, deltaTime);
}
public static VertexPositionColorTexture[] GetColoredVertices(VertexPositionTexture[] vertices, Color color)
@@ -274,36 +266,7 @@ namespace Barotrauma
ParticleManager particleManager = null)
{
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.LinearWrap);
Vector2 backgroundPos = cam.WorldViewCenter;
backgroundPos.Y = -backgroundPos.Y;
backgroundPos *= 0.05f;
if (level.GenerationParams.BackgroundTopSprite != null)
{
int backgroundSize = (int)level.GenerationParams.BackgroundTopSprite.size.Y;
if (backgroundPos.Y < backgroundSize)
{
if (backgroundPos.Y < 0)
{
var backgroundTop = level.GenerationParams.BackgroundTopSprite;
backgroundTop.SourceRect = new Rectangle((int)backgroundPos.X, (int)backgroundPos.Y, backgroundSize, (int)Math.Min(-backgroundPos.Y, backgroundSize));
backgroundTop.DrawTiled(spriteBatch, Vector2.Zero, new Vector2(GameMain.GraphicsWidth, Math.Min(-backgroundPos.Y, GameMain.GraphicsHeight)),
color: level.BackgroundTextureColor);
}
if (-backgroundPos.Y < GameMain.GraphicsHeight && level.GenerationParams.BackgroundSprite != null)
{
var background = level.GenerationParams.BackgroundSprite;
background.SourceRect = new Rectangle((int)backgroundPos.X, (int)Math.Max(backgroundPos.Y, 0), backgroundSize, backgroundSize);
background.DrawTiled(spriteBatch,
(backgroundPos.Y < 0) ? new Vector2(0.0f, (int)-backgroundPos.Y) : Vector2.Zero,
new Vector2(GameMain.GraphicsWidth, (int)Math.Min(Math.Ceiling(backgroundSize - backgroundPos.Y), backgroundSize)),
color: level.BackgroundTextureColor);
}
}
}
level.GenerationParams.DrawBackgrounds(spriteBatch, cam);
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.Deferred,
@@ -317,42 +280,7 @@ namespace Barotrauma
backgroundCreatureManager?.Draw(spriteBatch, cam);
}
if (level.GenerationParams.WaterParticles != null && cam.Zoom > 0.05f)
{
float textureScale = level.GenerationParams.WaterParticleScale;
Rectangle srcRect = new Rectangle(0, 0, 2048, 2048);
Vector2 origin = new Vector2(cam.WorldView.X, -cam.WorldView.Y);
Vector2 offset = -origin + waterParticleOffset;
while (offset.X <= -srcRect.Width * textureScale) offset.X += srcRect.Width * textureScale;
while (offset.X > 0.0f) offset.X -= srcRect.Width * textureScale;
while (offset.Y <= -srcRect.Height * textureScale) offset.Y += srcRect.Height * textureScale;
while (offset.Y > 0.0f) offset.Y -= srcRect.Height * textureScale;
for (int i = 0; i < 4; i++)
{
float scale = (1.0f - i * 0.2f);
//alpha goes from 1.0 to 0.0 when scale is in the range of 0.1 - 0.05
float alpha = (cam.Zoom * scale) < 0.1f ? (cam.Zoom * scale - 0.05f) * 20.0f : 1.0f;
if (alpha <= 0.0f) continue;
Vector2 offsetS = offset * scale
+ new Vector2(cam.WorldView.Width, cam.WorldView.Height) * (1.0f - scale) * 0.5f
- new Vector2(256.0f * i);
float texScale = scale * textureScale;
while (offsetS.X <= -srcRect.Width * texScale) offsetS.X += srcRect.Width * texScale;
while (offsetS.X > 0.0f) offsetS.X -= srcRect.Width * texScale;
while (offsetS.Y <= -srcRect.Height * texScale) offsetS.Y += srcRect.Height * texScale;
while (offsetS.Y > 0.0f) offsetS.Y -= srcRect.Height * texScale;
level.GenerationParams.WaterParticles.DrawTiled(
spriteBatch, origin + offsetS,
new Vector2(cam.WorldView.Width - offsetS.X, cam.WorldView.Height - offsetS.Y),
color: level.GenerationParams.WaterParticleColor * alpha, textureScale: new Vector2(texScale));
}
}
level.GenerationParams.DrawWaterParticles(spriteBatch, cam, waterParticleOffset);
GameMain.ParticleManager?.Draw(spriteBatch, inWater: true, inSub: false, ParticleBlendState.AlphaBlend, background: true);
@@ -89,7 +89,7 @@ namespace Barotrauma.Lights
}
private float pulseAmount;
[Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f, DecimalCount = 2), Serialize(0.0f, IsPropertySaveable.Yes, description: "How much light pulsates (in Hz). 0 = not at all, 1 = alternates between full brightness and off.")]
[Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f, DecimalCount = 2), Serialize(0.0f, IsPropertySaveable.Yes, description: "How much light pulsates. 0 = not at all, 1 = alternates between full brightness and off.")]
public float PulseAmount
{
get { return pulseAmount; }
@@ -397,7 +397,7 @@ namespace Barotrauma
}
if (!description.IsNullOrEmpty())
{
CreateTextWithIcon(description, locationTypeToDisplay.Sprite);
CreateTextWithIcon(description, iconSprite: locationTypeToDisplay.Sprite);
}
int highestSubTier = location.HighestSubmarineTierAvailable();
@@ -417,32 +417,29 @@ namespace Barotrauma
}
if (highestSubTier > 0)
{
CreateTextWithIcon(TextManager.GetWithVariable("advancedsub.all", "[tiernumber]", highestSubTier.ToString()), icon: null, style: "LocationOverlaySubmarineIcon");
CreateTextWithIcon(TextManager.GetWithVariable("advancedsub.all", "[tiernumber]", highestSubTier.ToString()), iconStyle: "LocationOverlaySubmarineIcon");
}
if (overrideTiers != null)
{
foreach (var (subClass, tier) in overrideTiers)
{
CreateTextWithIcon(TextManager.GetWithVariable($"advancedsub.{subClass}", "[tiernumber]", tier.ToString()), icon: null, style: "LocationOverlaySubmarineIcon");
CreateTextWithIcon(TextManager.GetWithVariable($"advancedsub.{subClass}", "[tiernumber]", tier.ToString()), iconStyle: "LocationOverlaySubmarineIcon");
}
}
CreateSpacing(10);
void CreateTextWithIcon(LocalizedString text, Sprite icon, string style = null)
void CreateTextWithIcon(LocalizedString text, Sprite iconSprite = null, string iconStyle = null)
{
var textHolder = new GUILayoutGroup(new RectTransform(new Point(content.Rect.Width, (int)GUIStyle.Font.MeasureString(text).Y), content.RectTransform), isHorizontal: true)
{
Stretch = true,
CanBeFocused = true
};
var guiIcon =
style == null ?
new GUIImage(new RectTransform(Vector2.One * 1.25f, textHolder.RectTransform, scaleBasis: ScaleBasis.BothHeight), icon) :
new GUIImage(new RectTransform(Vector2.One * 1.25f, textHolder.RectTransform, scaleBasis: ScaleBasis.BothHeight), style);
var textBlock = new GUITextBlock(new RectTransform(new Vector2(0.9f, 1.0f), textHolder.RectTransform), text);
textBlock.RectTransform.MinSize = new Point((int)textBlock.TextSize.X, 0);
textHolder.RectTransform.MinSize = new Point((int)textBlock.TextSize.X + guiIcon.Rect.Width, 0);
GUITextBlock textBox = new(new RectTransform(new Vector2(0.9f, 0f), content.RectTransform), text, wrap: true);
if (iconSprite == null && iconStyle == null) { return; }
float iconSize = GUIStyle.Font.LineHeight * 1.5f;
textBox.Padding = textBox.Padding with { X = iconSize + 5f };
RectTransform iconTF = new(new Point((int)iconSize), textBox.RectTransform, Anchor.CenterLeft) { IsFixedSize = true };
if (iconSprite != null) { new GUIImage(iconTF, iconSprite, scaleToFit: true); }
if (iconStyle != null) { new GUIImage(iconTF, iconStyle, scaleToFit: true); }
}
void CreateSpacing(int height)
@@ -486,10 +483,11 @@ namespace Barotrauma
CreateSpacing(20);
}
locationInfoOverlay.RectTransform.NonScaledSize =
new Point(
Math.Max(locationInfoOverlay.Rect.Width, (int)(content.Children.Max(c => c is GUITextBlock textBlock ? textBlock.TextSize.X : c.RectTransform.MinSize.X) * 1.2f)),
(int)(content.Children.Sum(c => c.Rect.Height) / content.RectTransform.RelativeSize.Y));
float childWidth = Math.Max(locationInfoOverlay.Rect.Width, content.Children.Max(c => c is GUITextBlock textBlock ? textBlock.TextSize.X + textBlock.Padding.X + textBlock.Padding.Z : c.RectTransform.MinSize.X));
childWidth = Math.Max(locationInfoOverlay.Rect.Width, childWidth);
float childHeight = content.Children.Sum(c => c.Rect.Height);
Vector2 childSize = new Vector2(childWidth, childHeight) / content.RectTransform.RelativeSize;
locationInfoOverlay.RectTransform.NonScaledSize = childSize.ToPoint();
}
partial void ClearAnimQueue()
@@ -34,6 +34,8 @@ namespace Barotrauma
//which entities have been selected for editing
public static HashSet<MapEntity> SelectedList { get; private set; } = new HashSet<MapEntity>();
private static List<Rectangle> oldRects = new List<Rectangle>();
private static Vector2 entityMovementNudge;
public static List<MapEntity> CopiedList = new List<MapEntity>();
@@ -267,7 +269,7 @@ namespace Barotrauma
i++;
}
highlightedEntities.Insert(i, e);
if (i == 0) highLightedEntity = e;
if (i == 0) { highLightedEntity = e; }
}
}
UpdateHighlighting(highlightedEntities);
@@ -278,10 +280,19 @@ namespace Barotrauma
if (GUI.KeyboardDispatcher.Subscriber == null)
{
Vector2 nudge = GetNudgeAmount();
if (nudge != Vector2.Zero)
Vector2 previousNudge = entityMovementNudge;
entityMovementNudge = GetNudgeAmount();
if (entityMovementNudge != Vector2.Zero)
{
foreach (MapEntity entityToNudge in SelectedList) { entityToNudge.Move(nudge); }
if (previousNudge == Vector2.Zero)
{
oldRects = SelectedList.Select(entity => entity.Rect).ToList();
}
foreach (MapEntity entityToNudge in SelectedList) { entityToNudge.Move(entityMovementNudge); }
}
else if (previousNudge != Vector2.Zero)
{
SubEditorScreen.StoreCommand(new TransformCommand(new List<MapEntity>(SelectedList), SelectedList.Select(entity => entity.Rect).ToList(), oldRects, resized: false));
}
}
else
@@ -352,7 +363,7 @@ namespace Barotrauma
}
}
SubEditorScreen.StoreCommand(new TransformCommand(new List<MapEntity>(SelectedList),SelectedList.Select(entity => entity.Rect).ToList(), oldRects, false));
SubEditorScreen.StoreCommand(new TransformCommand(new List<MapEntity>(SelectedList), SelectedList.Select(entity => entity.Rect).ToList(), oldRects, resized: false));
if (deposited.Any() && deposited.Any(entity => entity is Item))
{
var depositedItems = deposited.Where(entity => entity is Item).Cast<Item>().ToList();
@@ -508,7 +519,7 @@ namespace Barotrauma
selectionSize = Vector2.Zero;
selectionPos = Vector2.Zero;
}
public static Vector2 GetNudgeAmount(bool doHold = true)
{
Vector2 nudgeAmount = Vector2.Zero;
@@ -532,10 +543,10 @@ namespace Barotrauma
}
}
if (PlayerInput.KeyHit(Keys.Up)) nudgeAmount.Y = 1f;
if (PlayerInput.KeyHit(Keys.Down)) nudgeAmount.Y = -1f;
if (PlayerInput.KeyHit(Keys.Left)) nudgeAmount.X = -1f;
if (PlayerInput.KeyHit(Keys.Right)) nudgeAmount.X = 1f;
if (PlayerInput.KeyHit(Keys.Up)) { nudgeAmount.Y = 1f; }
if (PlayerInput.KeyHit(Keys.Down)) { nudgeAmount.Y = -1f; }
if (PlayerInput.KeyHit(Keys.Left)) { nudgeAmount.X = -1f;}
if (PlayerInput.KeyHit(Keys.Right)) { nudgeAmount.X = 1f; }
return nudgeAmount;
}
@@ -172,16 +172,19 @@ namespace Barotrauma
GUI.DrawRectangle(spriteBatch, drawPos - ExitPointSize.ToVector2() / 2, ExitPointSize.ToVector2(), Color.Cyan, thickness: 5);
}
GUIStyle.SmallFont.DrawString(spriteBatch,
ID.ToString(),
new Vector2(DrawPosition.X - 10, -DrawPosition.Y - 30),
color);
if (Tunnel?.Type != null)
if (Screen.Selected?.Cam is { Zoom: > 0.4f })
{
GUIStyle.SmallFont.DrawString(spriteBatch,
Tunnel.Type.ToString(),
new Vector2(DrawPosition.X - 10, -DrawPosition.Y - 45),
color);
ID.ToString(),
new Vector2(DrawPosition.X - 10, -DrawPosition.Y - 30),
color);
if (Tunnel?.Type != null)
{
GUIStyle.SmallFont.DrawString(spriteBatch,
Tunnel.Type.ToString(),
new Vector2(DrawPosition.X - 10, -DrawPosition.Y - 45),
color);
}
}
}
@@ -239,6 +239,13 @@ namespace Barotrauma.Networking
}
return;
}
//if we're still downloading mods, we're not ready to receive the campaign save
if (fileType == (byte)FileTransferType.CampaignSave && Screen.Selected is ModDownloadScreen)
{
GameMain.Client.CancelFileTransfer(transferId);
return;
}
if (!ValidateInitialData(fileType, fileName, fileSize, out string errorMsg))
{
@@ -562,6 +562,11 @@ namespace Barotrauma.Networking
{
SendLobbyUpdate();
}
if (Timing.TotalTime > LastMissingCampaignSubRequestTime)
{
TryRequestMissingCampaignSubs();
LastMissingCampaignSubRequestTime = Timing.TotalTime + MissingCampaignSubRequestInterval;
}
}
if (ServerSettings.VoiceChatEnabled)
@@ -2282,13 +2287,7 @@ namespace Barotrauma.Networking
if (GameMain.Client.IsServerOwner) { RequestSelectMode(modeIndex); }
}
if (GameMain.NetLobbyScreen.SelectedMode == GameModePreset.MultiPlayerCampaign)
{
foreach (SubmarineInfo sub in ServerSubmarines.Where(s => !ServerSettings.HiddenSubs.Contains(s.Name)))
{
GameMain.NetLobbyScreen.CheckIfCampaignSubMatches(sub, NetLobbyScreen.SubmarineDeliveryData.Campaign);
}
}
TryRequestMissingCampaignSubs();
GameMain.NetLobbyScreen.SetAllowSpectating(allowSpectating);
GameMain.NetLobbyScreen.SetAllowAFK(allowAFK);
@@ -2644,6 +2643,21 @@ namespace Barotrauma.Networking
ClientPeer?.Send(msg, DeliveryMethod.Reliable);
}
private double LastMissingCampaignSubRequestTime;
const double MissingCampaignSubRequestInterval = 10.0f;
private void TryRequestMissingCampaignSubs()
{
if (GameMain.NetLobbyScreen.SelectedMode == GameModePreset.MultiPlayerCampaign)
{
foreach (SubmarineInfo sub in ServerSubmarines.Where(s => !ServerSettings.HiddenSubs.Contains(s.Name)))
{
GameMain.NetLobbyScreen.CheckIfCampaignSubMatches(sub, NetLobbyScreen.SubmarineDeliveryData.Campaign);
}
}
}
public void RequestFile(FileTransferType fileType, string file, string fileHash)
{
DebugConsole.Log(
@@ -1,4 +1,4 @@
#nullable enable
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
@@ -91,7 +91,7 @@ namespace Barotrauma.Networking
});
initializationStep = ConnectionInitialization.AuthInfoAndVersion;
timeout = NetworkConnection.TimeoutThreshold;
timeout = NetworkConnection.TimeoutThresholdNotInGame;
heartbeatTimer = 1.0;
isActive = true;
@@ -139,7 +139,7 @@ namespace Barotrauma.Networking
timeout = Screen.Selected == GameMain.GameScreen
? NetworkConnection.TimeoutThresholdInGame
: NetworkConnection.TimeoutThreshold;
: NetworkConnection.TimeoutThresholdNotInGame;
var (_, packetHeader, initialization) = INetSerializableStruct.Read<PeerPacketHeaders>(inc);
@@ -354,9 +354,11 @@ namespace Barotrauma
{
List<Mission> availableMissions = currentDisplayLocation.GetMissionsInConnection(connection).Where(m => m.Prefab.ShowInMenus || GameMain.DebugDraw).ToList();
if (!availableMissions.Any()) { availableMissions.Insert(0, null); }
if (availableMissions.None()) { availableMissions.Insert(0, null); }
availableMissions.AddRange(location.AvailableMissions.Where(m => m.Locations[0] == m.Locations[1]));
//show side objectives last
availableMissions.Sort((m1, m2) => (m1?.Prefab.IsSideObjective ?? false).CompareTo(m2?.Prefab.IsSideObjective ?? false));
missionList.Content.ClearChildren();
@@ -390,6 +392,10 @@ namespace Barotrauma
};
LocalizedString missionName = mission?.Name ?? TextManager.Get("NoMission");
if (mission is { Prefab.IsSideObjective: true })
{
missionName = TextManager.AddPunctuation(':', TextManager.Get("sideobjective"), missionName);
}
if (GameMain.DebugDraw && mission != null)
{
if (!mission.Prefab.ShowInMenus) { missionName = $"[HIDDEN] {missionName}"; }
@@ -406,7 +412,7 @@ namespace Barotrauma
else
{
GUITickBox tickBox = null;
if (!isMissionInNextLocation && mission.Prefab.ShowInMenus)
if (!isMissionInNextLocation && mission.Prefab.ShowInMenus && !mission.Prefab.IsSideObjective)
{
tickBox = new GUITickBox(new RectTransform(Vector2.One * 0.9f, missionNameBlock.RectTransform, anchor: Anchor.CenterLeft, scaleBasis: ScaleBasis.Smallest) { AbsoluteOffset = new Point((int)missionNameBlock.Padding.X, 0) }, label: string.Empty)
{
@@ -539,7 +545,7 @@ namespace Barotrauma
int missionCount = 0;
if (GameMain.GameSession != null && Campaign.Map?.CurrentLocation?.SelectedMissions != null)
{
missionCount = Campaign.Map.CurrentLocation.SelectedMissions.Count(m => m.Locations.Contains(location) && !GameMain.GameSession.Missions.Contains(m));
missionCount = Campaign.Map.CurrentLocation.SelectedMissions.Count(m => m.Locations.Contains(location) && !GameMain.GameSession.Missions.Contains(m) && !m.Prefab.IsSideObjective);
}
return TextManager.AddPunctuation(':', TextManager.Get("Missions"), $"{missionCount}/{Campaign.Settings.TotalMaxMissionCount}");
}
@@ -551,9 +557,9 @@ namespace Barotrauma
OnClicked = (GUIButton btn, object obj) =>
{
if (missionList.Content.FindChild(c => c is GUITickBox tickBox && tickBox.Selected, recursive: true) == null &&
missionList.Content.Children.Any(c => c.UserData is Mission { Prefab.ShowInMenus: true } mission && mission.Locations.Contains(Campaign?.Map?.CurrentLocation)))
missionList.Content.Children.Any(c => c.UserData is Mission { Prefab.ShowInMenus: true, Prefab.IsSideObjective: false } mission && mission.Locations.Contains(Campaign?.Map?.CurrentLocation)))
{
var noMissionVerification = new GUIMessageBox(string.Empty, TextManager.Get("nomissionprompt"), new LocalizedString[] { TextManager.Get("yes"), TextManager.Get("no") });
var noMissionVerification = new GUIMessageBox(string.Empty, TextManager.Get("nomissionprompt"), [TextManager.Get("yes"), TextManager.Get("no")]);
noMissionVerification.Buttons[0].OnClicked = (btn, userdata) =>
{
StartRound?.Invoke();
@@ -648,7 +654,7 @@ namespace Barotrauma
private void UpdateMaxMissions(Location location)
{
hasMaxMissions = Campaign.NumberOfMissionsAtLocation(location) >= Campaign.Settings.TotalMaxMissionCount;
hasMaxMissions = Campaign.NumberOfSelectableMissionsAtLocation(location) >= Campaign.Settings.TotalMaxMissionCount;
}
public readonly struct PlayerBalanceElement
@@ -5,7 +5,6 @@ using Microsoft.Xna.Framework.Graphics;
using System;
using System.Diagnostics;
using System.Linq;
using System.Transactions;
namespace Barotrauma
{
@@ -348,14 +347,25 @@ namespace Barotrauma
//------------------------------------------------------------------------
graphics.SetRenderTarget(renderTargetBackground);
if (Level.Loaded == null)
if (Level.Loaded != null)
{
graphics.Clear(new Color(11, 18, 26, 255));
Level.Loaded.DrawBack(graphics, spriteBatch, cam);
}
else if (GameMain.GameSession.GameMode is TestGameMode testMode)
{
graphics.Clear(testMode.BackgroundParams.BackgroundColor);
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.LinearWrap);
testMode.BackgroundParams.DrawBackgrounds(spriteBatch, cam);
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.LinearWrap, DepthStencilState.DepthRead, null, null, cam.Transform);
testMode.BackgroundParams.DrawWaterParticles(spriteBatch, cam, testMode.WaterParticleOffset);
spriteBatch.End();
}
else
{
//graphics.Clear(new Color(255, 255, 255, 255));
Level.Loaded.DrawBack(graphics, spriteBatch, cam);
graphics.Clear(new Color(11, 18, 26, 255));
}
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, depthStencilState: DepthStencilState.None, transformMatrix: cam.Transform);
@@ -380,6 +380,10 @@ namespace Barotrauma
GameMain.NetLobbyScreen.Select();
}
}
else if (GameMain.Client.FileReceiver.ActiveTransfers.None())
{
GameMain.Client.RequestFile(FileTransferType.Mod, currentDownload.Name, currentDownload.Hash.StringRepresentation);
}
}
public void CurrentDownloadFinished(FileReceiver.FileTransferIn transfer)
@@ -2221,11 +2221,7 @@ namespace Barotrauma
if (GameMain.Client == null) { return true; }
//the player presumably no longer wants to be afk if they clicked the start button
if (afkBox.Selected)
{
afkBox.Flash(GUIStyle.Green);
}
afkBox.Selected = false;
SetAFKSelected(false);
if (CampaignSetupFrame.Visible && CampaignSetupUI != null)
{
@@ -2357,7 +2353,7 @@ namespace Barotrauma
if (GameMain.Client != null)
{
afkBox.Visible = !GameMain.Client.IsServerOwner && GameMain.Client.ServerSettings.AllowAFK;
afkBox.Visible = GameMain.Client.IsServerOwner || GameMain.Client.ServerSettings.AllowAFK;
GameMain.Client.Voting.ResetVotes(GameMain.Client.ConnectedClients);
joinOnGoingRoundButton.OnClicked = (btn, userdata) =>
{
@@ -3082,6 +3078,15 @@ namespace Barotrauma
}
}
public void SetAFKSelected(bool selected)
{
if (afkBox.Selected != selected)
{
afkBox.Flash(GUIStyle.Green);
afkBox.Selected = selected;
}
}
public void SetAutoRestart(bool enabled, float timer = 0.0f)
{
autoRestartBox.Selected = enabled;
@@ -4994,7 +4999,7 @@ namespace Barotrauma
};
micIcon = new GUIImage(new RectTransform(new Vector2(0.05f, 1.0f), chatRow.RectTransform), style: "GUIMicrophoneUnavailable");
chatInput.Select();
chatInput.Select(ignoreSelectSound: true);
}
//this needs to be done even if we're using the existing chatinput instance instead of creating a new one,
@@ -1126,6 +1126,10 @@ namespace Barotrauma
{
new GUIMessageBox(TextManager.Get("error"), TextManager.Get("CannotJoinSteamServer.SteamNotInitialized"));
}
else if (endpoint is EosP2PEndpoint && !EosInterface.Core.IsInitialized)
{
new GUIMessageBox(TextManager.Get("error"), TextManager.Get("EosStatus.NotInitialized"));
}
else
{
JoinServer(endpoint.ToEnumerable().ToImmutableArray(), "");
@@ -1183,9 +1187,29 @@ namespace Barotrauma
endpointBox.OnTextChanged += (textBox, text) =>
{
okButton.Enabled = favoriteButton.Enabled = !string.IsNullOrEmpty(text);
okButton.Enabled = favoriteButton.Enabled = !string.IsNullOrEmpty(text);
return true;
};
// Connect on enter press, gotta go fast
endpointBox.OnEnterPressed += (textBox, text) =>
{
if (okButton.Enabled)
{
if (okButton.PlaySoundOnSelect)
{
SoundPlayer.PlayUISound(okButton.ClickSound);
}
okButton.OnClicked.Invoke(okButton, okButton.UserData);
}
return true;
};
// Focus on and select all the text, for easier input/deletion (after the dialog is shown, apparently it takes a moment)
CoroutineManager.Invoke(() =>
{
endpointBox.Select(ignoreSelectSound: true);
}, 0.1f);
}
private void RemoveMsgFromServerList()
@@ -40,6 +40,8 @@ namespace Barotrauma
#region Transform Editor
private const float TransformWidgetOffset = 300f;
private const float RotationSnapIncrement = MathF.PI / 36f;
private const float ScaleSnapIncrement = 0.1f;
private GUITickBox rotateToolToggle, scaleToolToggle;
public bool TransformWidgetSelected => TransformWidget.IsSelected;
@@ -141,6 +143,7 @@ namespace Barotrauma
if (rotateToolToggle.Selected)
{
transformCommand.RotationRad = MathUtils.VectorToAngle(PlayerInput.MousePosition - Cam.WorldToScreen(transformCommand.Pivot));
if (!PlayerInput.IsShiftDown()) { transformCommand.RotationRad = MathUtils.RoundTowardsClosest(transformCommand.RotationRad.Value, RotationSnapIncrement); }
rotationString = TextManager.GetWithVariable("SubEditor.TransformWidget.Rotation", "[value]", MathHelper.ToDegrees(transformCommand.RotationRad.Value).ToString("0.000", CultureInfo.CurrentCulture));
}
@@ -148,7 +151,9 @@ namespace Barotrauma
LocalizedString scaleString = null;
if (scaleToolToggle.Selected)
{
transformCommand.ScaleMult = Math.Clamp(Vector2.Distance(PlayerInput.MousePosition, Cam.WorldToScreen(transformCommand.Pivot)) / (TransformWidgetOffset * GUI.Scale), transformCommand.MinScale, transformCommand.MaxScale);
transformCommand.ScaleMult = Vector2.Distance(PlayerInput.MousePosition, Cam.WorldToScreen(transformCommand.Pivot)) / (TransformWidgetOffset * GUI.Scale);
if (!PlayerInput.IsShiftDown()) { transformCommand.ScaleMult = MathUtils.RoundTowardsClosest(transformCommand.ScaleMult.Value, ScaleSnapIncrement); }
transformCommand.ScaleMult = Math.Clamp(transformCommand.ScaleMult.Value, transformCommand.MinScale, transformCommand.MaxScale);
scaleString = TextManager.GetWithVariable("SubEditor.TransformWidget.Scale", "[value]", transformCommand.ScaleMult.Value.ToString("0.000", CultureInfo.CurrentCulture));
}
@@ -260,8 +260,16 @@ namespace Barotrauma
{
continue;
}
split[j] = split[j].Replace(" & ", " &amp; ");
xmlContentByLanguage[languageName].Add($"<{split[0]}>{split[j]}</{split[0]}>");
string textContent = split[j];
if (textContent == "#NAME?")
{
throw new Exception(
$"Error while converting csv to xml: #NAME? value found on line {row}, language: {languageName}." +
" This indicates a missing value in the csv file (some text got accidentally converted to a broken formula in the localization sheet?).");
}
textContent = textContent.Replace(" & ", " &amp; ");
xmlContentByLanguage[languageName].Add($"<{split[0]}>{textContent}</{split[0]}>");
}
}
}