Merge branch 'master' of https://github.com/Regalis11/Barotrauma into develop

This commit is contained in:
EvilFactory
2023-12-14 11:56:39 -03:00
376 changed files with 7775 additions and 2879 deletions

View File

@@ -73,7 +73,7 @@ body:
label: Version
description: Which version of the game did the bug happen in? You can see the current version number in the bottom left corner of your screen in the main menu.
options:
- v1.1.19.3 (Treacherous Tides Hotfix 2)
- v1.2.6.0 (Winter Update)
- Other
validations:
required: true

View File

@@ -6,7 +6,7 @@ using System;
namespace Barotrauma
{
class Camera : IDisposable
class Camera
{
public static bool FollowSub = true;
@@ -147,21 +147,10 @@ namespace Barotrauma
position = Vector2.Zero;
CreateMatrices();
// TODO: this has the potential to cause a resource leak
// by sneakily creating a reference to cameras that we might
// fail to release.
GameMain.Instance.ResolutionChanged += CreateMatrices;
UpdateTransform(false);
}
private bool disposed = false;
public void Dispose()
{
if (!disposed) { GameMain.Instance.ResolutionChanged -= CreateMatrices; }
disposed = true;
}
public Vector2 TargetPos { get; set; }
public Vector2 GetPosition()
@@ -207,6 +196,12 @@ namespace Barotrauma
public void UpdateTransform(bool interpolate = true, bool updateListener = true)
{
if (GameMain.GraphicsWidth != Resolution.X ||
GameMain.GraphicsHeight != Resolution.Y)
{
CreateMatrices();
}
Vector2 interpolatedPosition = interpolate ? Timing.Interpolate(prevPosition, position) : position;
float interpolatedZoom = interpolate ? Timing.Interpolate(prevZoom, zoom) : zoom;

View File

@@ -136,6 +136,7 @@ namespace Barotrauma
set
{
if (!MathUtils.IsValid(value)) { return; }
if (this != Controlled) { return; }
if (Screen.Selected?.Cam != null)
{
Screen.Selected.Cam.Shake = value;
@@ -229,6 +230,8 @@ namespace Barotrauma
}
}
private float pressureEffectTimer;
private readonly List<ObjectiveEntity> activeObjectiveEntities = new List<ObjectiveEntity>();
public IEnumerable<ObjectiveEntity> ActiveObjectiveEntities
{
@@ -333,18 +336,22 @@ namespace Barotrauma
{
if (!IsProtectedFromPressure && (AnimController.CurrentHull == null || AnimController.CurrentHull.LethalPressure > 0.0f))
{
float pressure = AnimController.CurrentHull == null ? 100.0f : AnimController.CurrentHull.LethalPressure;
if (pressure > 0.0f)
//wait until the character has been in pressure for one second so the zoom doesn't
//"flicker" in and out if the pressure fluctuates around the minimum threshold
pressureEffectTimer += deltaTime;
if (pressureEffectTimer > 1.0f)
{
//lerp in during the 1st second of the pressure timer so the zoom doesn't
//"flicker" in and out if the pressure fluctuates around the minimum threshold
float timerMultiplier = (PressureTimer / 100.0f);
float zoomInEffectStrength = MathHelper.Clamp(pressure / 100.0f * timerMultiplier, 0.0f, 1.0f);
float pressure = AnimController.CurrentHull == null ? 100.0f : AnimController.CurrentHull.LethalPressure;
float zoomInEffectStrength = MathHelper.Clamp(pressure / 100.0f, 0.0f, 1.0f);
cam.Zoom = MathHelper.Lerp(cam.Zoom,
cam.DefaultZoom + (Math.Max(pressure, 10) / 150.0f) * Rand.Range(0.9f, 1.1f),
zoomInEffectStrength);
}
}
else
{
pressureEffectTimer = 0.0f;
}
if (IsHumanoid)
{
@@ -521,22 +528,25 @@ namespace Barotrauma
if (controlled == this)
{
controlled = null;
if (!(Screen.Selected?.Cam is null))
if (Screen.Selected?.Cam is not null)
{
Screen.Selected.Cam.TargetPos = Vector2.Zero;
Lights.LightManager.ViewTarget = null;
}
}
sounds.ForEach(s => s.Sound?.Dispose());
sounds.Clear();
if (GameMain.GameSession?.CrewManager != null &&
GameMain.GameSession.CrewManager.GetCharacters().Contains(this))
{
GameMain.GameSession.CrewManager.RemoveCharacter(this);
}
if (GameMain.Client?.Character == this) GameMain.Client.Character = null;
if (Lights.LightManager.ViewTarget == this) Lights.LightManager.ViewTarget = null;
if (GameMain.Client?.Character == this) { GameMain.Client.Character = null; }
if (Lights.LightManager.ViewTarget == this) { Lights.LightManager.ViewTarget = null; }
}

View File

@@ -894,7 +894,7 @@ namespace Barotrauma
if (!orderIndicatorCount.ContainsKey(target)) { orderIndicatorCount.Add(target, 0); }
Vector2 drawPos = target is Entity ? (target as Entity).DrawPosition :
Vector2 drawPos = target is Entity entity ? entity.DrawPosition :
target.Submarine == null ? target.Position : target.Position + target.Submarine.DrawPosition;
drawPos += Vector2.UnitX * order.SymbolSprite.size.X * 1.5f * orderIndicatorCount[target];
GUI.DrawIndicator(spriteBatch, drawPos, cam, 100.0f, order.SymbolSprite, order.Color * iconAlpha,

View File

@@ -217,6 +217,7 @@ namespace Barotrauma
if ((int)newLevel > (int)prevLevel)
{
Character.Controlled?.SelectedItem?.OnPlayerSkillsChanged();
int increase = Math.Max((int)newLevel - (int)prevLevel, 1);
Character?.AddMessage(
@@ -518,7 +519,7 @@ namespace Barotrauma
attachment.Sprite.Draw(spriteBatch, drawPos, color ?? Color.White, origin, rotate: 0, scale: scale, depth: depth, spriteEffect: spriteEffects);
}
public static CharacterInfo ClientRead(Identifier speciesName, IReadMessage inc)
public static CharacterInfo ClientRead(Identifier speciesName, IReadMessage inc, bool requireJobPrefabFound = true)
{
ushort infoID = inc.ReadUInt16();
string newName = inc.ReadString();
@@ -554,14 +555,19 @@ namespace Barotrauma
if (jobIdentifier > 0)
{
jobPrefab = JobPrefab.Prefabs.Find(jp => jp.UintIdentifier == jobIdentifier);
if (jobPrefab == null)
if (jobPrefab == null && requireJobPrefabFound)
{
throw new Exception($"Error while reading {nameof(CharacterInfo)} received from the server: could not find a job prefab with the identifier \"{jobIdentifier}\".");
}
foreach (SkillPrefab skillPrefab in jobPrefab.Skills.OrderBy(s => s.Identifier))
byte skillCount = inc.ReadByte();
List<SkillPrefab> jobSkills = jobPrefab?.Skills.OrderBy(s => s.Identifier).ToList();
for (int i = 0; i < skillCount; i++)
{
float skillLevel = inc.ReadSingle();
skillLevels.Add(skillPrefab.Identifier, skillLevel);
if (jobSkills != null && i < jobSkills.Count)
{
skillLevels.Add(jobSkills[i].Identifier, skillLevel);
}
}
}

View File

@@ -2170,6 +2170,8 @@ namespace Barotrauma
medUIExtra?.Remove();
medUIExtra = null;
Character.OnAttacked -= OnAttacked;
limbIndicatorOverlay?.Remove();
limbIndicatorOverlay = null;

View File

@@ -1216,7 +1216,7 @@ namespace Barotrauma
pos: new Vector2(body.DrawPosition.X, -body.DrawPosition.Y),
srcRect: w.Sprite.SourceRect,
color: Color.White,
rotation: rotation,
rotationRad: rotation,
origin: origin,
scale: new Vector2(scale, scale),
effects: spriteEffect,

View File

@@ -34,7 +34,7 @@ namespace Barotrauma
bool allowCheats = GameMain.NetworkMember == null && (GameMain.GameSession?.GameMode is TestGameMode || Screen.Selected is { IsEditor: true });
if (!allowCheats && !CheatsEnabled && IsCheat)
{
NewMessage("You need to enable cheats using the command \"enablecheats\" before you can use the command \"" + names[0] + "\".", Color.Red);
NewMessage("You need to enable cheats using the command \"enablecheats\" before you can use the command \"" + Names[0] + "\".", Color.Red);
#if USE_STEAM
NewMessage("Enabling cheats will disable Steam achievements during this play session.", Color.Red);
#endif
@@ -215,11 +215,11 @@ namespace Barotrauma
SoundPlayer.PlayUISound(GUISoundType.Select);
}
private static bool IsCommandPermitted(string command, GameClient client)
private static bool IsCommandPermitted(Identifier command, GameClient client)
{
if (GameMain.LuaCs.Game.IsCustomCommandPermitted(command)) { return true; }
switch (command)
switch (command.Value.ToLowerInvariant())
{
case "kick":
return client.HasPermission(ClientPermissions.Kick);
@@ -306,7 +306,7 @@ namespace Barotrauma
}
};
var textBlock = new GUITextBlock(new RectTransform(new Point(listBox.Content.Rect.Width - 5, 0), textContainer.RectTransform, Anchor.TopLeft) { AbsoluteOffset = new Point(2, 2) },
msg.Text, textAlignment: Alignment.TopLeft, font: GUIStyle.SmallFont, wrap: true)
RichString.Rich(msg.Text), textAlignment: Alignment.TopLeft, font: GUIStyle.SmallFont, wrap: true)
{
CanBeFocused = false,
TextColor = msg.Color
@@ -348,7 +348,7 @@ namespace Barotrauma
CanBeFocused = false
};
var textBlock = new GUITextBlock(new RectTransform(new Point(listBox.Content.Rect.Width - 170, 0), textContainer.RectTransform, Anchor.TopRight) { AbsoluteOffset = new Point(20, 0) },
command.help, textAlignment: Alignment.TopLeft, font: GUIStyle.SmallFont, wrap: true)
command.Help, textAlignment: Alignment.TopLeft, font: GUIStyle.SmallFont, wrap: true)
{
CanBeFocused = false,
TextColor = Color.White
@@ -356,7 +356,7 @@ namespace Barotrauma
textContainer.RectTransform.NonScaledSize = new Point(textContainer.RectTransform.NonScaledSize.X, textBlock.RectTransform.NonScaledSize.Y + 5);
textBlock.SetTextPos();
new GUITextBlock(new RectTransform(new Point(150, textContainer.Rect.Height), textContainer.RectTransform),
command.names[0], textAlignment: Alignment.TopLeft);
command.Names[0].Value, textAlignment: Alignment.TopLeft);
listBox.UpdateScrollBarSize();
listBox.BarScroll = 1.0f;
@@ -366,7 +366,7 @@ namespace Barotrauma
private static void AssignOnClientExecute(string names, Action<string[]> onClientExecute)
{
Command command = commands.Find(c => c.names.Intersect(names.Split('|')).Count() > 0);
Command command = commands.Find(c => c.Names.Intersect(names.Split('|').ToIdentifiers()).Any());
if (command == null)
{
throw new Exception("AssignOnClientExecute failed. Command matching the name(s) \"" + names + "\" not found.");
@@ -380,7 +380,7 @@ namespace Barotrauma
private static void AssignRelayToServer(string names, bool relay)
{
Command command = commands.Find(c => c.names.Intersect(names.Split('|')).Count() > 0);
Command command = commands.Find(c => c.Names.Intersect(names.Split('|').ToIdentifiers()).Any());
if (command == null)
{
DebugConsole.Log("Could not assign to relay to server: " + names);
@@ -691,6 +691,7 @@ namespace Barotrauma
AssignRelayToServer("savebinds", false);
AssignRelayToServer("spreadsheetexport", false);
#if DEBUG
AssignRelayToServer("listspamfilters", false);
AssignRelayToServer("crash", false);
AssignRelayToServer("showballastflorasprite", false);
AssignRelayToServer("simulatedlatency", false);
@@ -716,6 +717,8 @@ namespace Barotrauma
AssignRelayToServer("showmoney", true);
AssignRelayToServer("setskill", true);
AssignRelayToServer("readycheck", true);
commands.Add(new Command("debugjobassignment", "", (string[] args) => { }));
AssignRelayToServer("debugjobassignment", true);
AssignRelayToServer("givetalent", true);
AssignRelayToServer("unlocktalents", true);
@@ -2242,6 +2245,30 @@ namespace Barotrauma
}));
#if DEBUG
commands.Add(new Command("listspamfilters", "Lists filters that are in the global spam filter.", (string[] args) =>
{
if (!SpamServerFilters.GlobalSpamFilter.TryUnwrap(out var filter))
{
ThrowError("Global spam list is not initialized.");
return;
}
if (!filter.Filters.Any())
{
NewMessage("Global spam list is empty.", GUIStyle.Green);
return;
}
StringBuilder sb = new();
foreach (var f in filter.Filters)
{
sb.AppendLine(f.ToString());
}
NewMessage(sb.ToString(), GUIStyle.Green);
}));
commands.Add(new Command("setplanthealth", "setplanthealth [value]: Sets the health of the selected plant in sub editor.", (string[] args) =>
{
if (1 > args.Length || Screen.Selected != GameMain.SubEditorScreen) { return; }
@@ -3102,7 +3129,7 @@ namespace Barotrauma
int i = 0;
foreach (LocationConnection connection in campaign.Map.CurrentLocation.Connections)
{
NewMessage(" " + i + ". " + connection.OtherLocation(campaign.Map.CurrentLocation).Name, Color.White);
NewMessage(" " + i + ". " + connection.OtherLocation(campaign.Map.CurrentLocation).DisplayName, Color.White);
i++;
}
ShowQuestionPrompt("Select a destination (0 - " + (campaign.Map.CurrentLocation.Connections.Count - 1) + "):", (string selectedDestination) =>

View File

@@ -374,13 +374,12 @@ namespace Barotrauma
btn.RectTransform.MinSize = new Point(0, (int)(btn.TextBlock.Rect.Height * 1.2f));
}
textContent.RectTransform.MinSize = new Point(0, textContent.Children.Sum(c => c.Rect.Height) + GUI.IntScale(16));
textContent.RectTransform.MinSize = new Point(0, textContent.Children.Sum(c => c.Rect.Height + textContent.AbsoluteSpacing) + GUI.IntScale(16));
content.RectTransform.MinSize = new Point(0, content.Children.Sum(c => c.Rect.Height));
// Recalculate the text size as it is scaled up and no longer matching the text height due to the textContent's minSize increasing
textBlock.CalculateHeightFromText();
textBlock.TextAlignment = Alignment.TopLeft;
//content.RectTransform.MinSize = new Point(0, textContent.Rect.Height);
return buttons;
}

View File

@@ -16,6 +16,11 @@ partial class EventObjectiveAction : EventAction
int width = 450,
int height = 80)
{
if (Type == SegmentActionType.AddIfNotFound)
{
if (ObjectiveManager.IsSegmentActive(Identifier)) { return; }
}
ObjectiveManager.Segment? segment = null;
// Only need to create the segment when it's being triggered (otherwise the tutorial already has the segment instance)
if (Type == SegmentActionType.Trigger)
@@ -24,7 +29,8 @@ partial class EventObjectiveAction : EventAction
new ObjectiveManager.Segment.Text(TextTag, width, height, Anchor.Center),
new ObjectiveManager.Segment.Video(videoFile, TextTag, width, height));
}
else if (Type == SegmentActionType.Add)
else if (Type == SegmentActionType.Add ||
Type == SegmentActionType.AddIfNotFound)
{
segment = ObjectiveManager.Segment.CreateObjectiveSegment(Identifier, !ObjectiveTag.IsEmpty ? ObjectiveTag : Identifier);
}
@@ -33,10 +39,12 @@ partial class EventObjectiveAction : EventAction
segment.CanBeCompleted = CanBeCompleted;
segment.ParentId = ParentObjectiveId;
}
switch (Type)
{
case SegmentActionType.Trigger:
case SegmentActionType.Add:
case SegmentActionType.AddIfNotFound:
ObjectiveManager.TriggerSegment(segment);
break;
case SegmentActionType.Complete:

View File

@@ -1,22 +1,19 @@
#nullable enable
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma;
partial class TutorialHighlightAction : EventAction
partial class HighlightAction : EventAction
{
private static readonly Color highlightColor = Color.Orange;
partial void UpdateProjSpecific()
partial void SetHighlightProjSpecific(Entity entity, IEnumerable<Character>? targetCharacters)
{
if (GameMain.GameSession?.GameMode is not TutorialMode) { return; }
foreach (var target in ParentEvent.GetTargets(TargetTag))
if (targetCharacters != null && !targetCharacters.Contains(Character.Controlled))
{
SetHighlight(target);
return;
}
}
private void SetHighlight(Entity entity)
{
if (entity is Item i)
{
SetItemHighlight(i);

View File

@@ -47,7 +47,7 @@ namespace Barotrauma
}
float theoreticalMaxMonsterStrength = 10000;
float relativeMaxMonsterStrength = theoreticalMaxMonsterStrength * (GameMain.GameSession?.LevelData?.Difficulty ?? 0f) / 100;
float relativeMaxMonsterStrength = theoreticalMaxMonsterStrength * (GameMain.GameSession?.Level?.Difficulty ?? 0f) / 100;
float absoluteMonsterStrength = monsterStrength / theoreticalMaxMonsterStrength;
float relativeMonsterStrength = monsterStrength / relativeMaxMonsterStrength;
@@ -581,7 +581,14 @@ namespace Barotrauma
StatusEffect effect = StatusEffect.Load(subElement, $"EventManager.ClientRead ({eventIdentifier})");
foreach (Entity target in targets)
{
effect.Apply(effect.type, 1.0f, target, target as ISerializableEntity);
if (target is Item item)
{
effect.Apply(effect.type, 1.0f, item, item.AllPropertyObjects);
}
else
{
effect.Apply(effect.type, 1.0f, target, target as ISerializableEntity);
}
}
}
break;

View File

@@ -51,7 +51,8 @@ namespace Barotrauma
if (requiredDeliveryAmount == 0) { requiredDeliveryAmount = items.Count; }
if (requiredDeliveryAmount > items.Count)
{
DebugConsole.AddWarning($"Error in mission \"{Prefab.Identifier}\". Required delivery amount is {requiredDeliveryAmount} but there's only {items.Count} items to deliver.");
DebugConsole.AddWarning($"Error in mission \"{Prefab.Identifier}\". Required delivery amount is {requiredDeliveryAmount} but there's only {items.Count} items to deliver.",
contentPackage: Prefab.ContentPackage);
requiredDeliveryAmount = items.Count;
}
}

View File

@@ -126,7 +126,13 @@ namespace Barotrauma
void GiveMissionExperience(CharacterInfo info)
{
if (info == null) { return; }
var experienceGainMultiplierIndividual = new AbilityMissionExperienceGainMultiplier(this, 1f);
var experienceGainMultiplierIndividual = new AbilityMissionExperienceGainMultiplier(this, 1f, info.Character);
//check if anyone else in the crew has talents that could give a bonus to this one
foreach (var c in crew)
{
if (c == info.Character) { continue; }
c.CheckTalents(AbilityEffectType.OnAllyGainMissionExperience, experienceGainMultiplierIndividual);
}
info.Character?.CheckTalents(AbilityEffectType.OnGainMissionExperience, experienceGainMultiplierIndividual);
info.GiveExperience((int)(experienceGain * experienceGainMultiplierIndividual.Value));
}

View File

@@ -247,30 +247,33 @@ namespace Barotrauma
UpdateCrew();
}
public void UpdateHireables()
{
UpdateHireables(campaign?.CurrentLocation);
}
private void UpdateHireables(Location location)
{
if (hireableList != null)
if (hireableList == null) { return; }
hireableList.Content.Children.ToList().ForEach(c => hireableList.RemoveChild(c));
var hireableCharacters = location.GetHireableCharacters();
if (hireableCharacters.None())
{
hireableList.Content.Children.ToList().ForEach(c => hireableList.RemoveChild(c));
var hireableCharacters = location.GetHireableCharacters();
if (hireableCharacters.None())
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), hireableList.Content.RectTransform), TextManager.Get("HireUnavailable"), textAlignment: Alignment.Center)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), hireableList.Content.RectTransform), TextManager.Get("HireUnavailable"), textAlignment: Alignment.Center)
{
CanBeFocused = false
};
}
else
{
foreach (CharacterInfo c in hireableCharacters)
{
if (c == null) { continue; }
CreateCharacterFrame(c, hireableList);
}
}
sortingDropDown.SelectItem(SortingMethod.JobAsc);
hireableList.UpdateScrollBarSize();
CanBeFocused = false
};
}
else
{
foreach (CharacterInfo c in hireableCharacters)
{
if (c == null) { continue; }
CreateCharacterFrame(c, hireableList);
}
}
sortingDropDown.SelectItem(SortingMethod.JobAsc);
hireableList.UpdateScrollBarSize();
}
public void SetHireables(Location location, List<CharacterInfo> availableHires)
@@ -434,7 +437,7 @@ namespace Barotrauma
if (listBox != crewList)
{
new GUITextBlock(new RectTransform(new Vector2(width, 1.0f), mainGroup.RectTransform),
TextManager.FormatCurrency(characterInfo.Salary),
TextManager.FormatCurrency(HireManager.GetSalaryFor(characterInfo)),
textAlignment: Alignment.Center)
{
CanBeFocused = false
@@ -692,11 +695,8 @@ namespace Barotrauma
private void SetTotalHireCost()
{
if (pendingList == null || totalBlock == null || validateHiresButton == null) { return; }
int total = 0;
pendingList.Content.Children.ForEach(c =>
{
total += ((InfoSkill)c.UserData).CharacterInfo.Salary;
});
var infos = pendingList.Content.Children.Select(static c => ((InfoSkill)c.UserData).CharacterInfo).ToArray();
int total = HireManager.GetSalaryFor(infos);
totalBlock.Text = TextManager.FormatCurrency(total);
bool enoughMoney = campaign == null || campaign.CanAfford(total);
totalBlock.TextColor = enoughMoney ? Color.White : Color.Red;
@@ -718,14 +718,14 @@ namespace Barotrauma
if (nonDuplicateHires.None()) { return false; }
int total = nonDuplicateHires.Aggregate(0, (total, info) => total + info.Salary);
int total = HireManager.GetSalaryFor(nonDuplicateHires);
if (!campaign.CanAfford(total)) { return false; }
bool atLeastOneHired = false;
foreach (CharacterInfo ci in nonDuplicateHires)
{
if (campaign.TryHireCharacter(campaign.Map.CurrentLocation, ci))
if (campaign.TryHireCharacter(campaign.Map.CurrentLocation, ci, Character.Controlled))
{
atLeastOneHired = true;
}
@@ -741,7 +741,7 @@ namespace Barotrauma
SelectCharacter(null, null, null);
var dialog = new GUIMessageBox(
TextManager.Get("newcrewmembers"),
TextManager.GetWithVariable("crewhiredmessage", "[location]", campaignUI?.Campaign?.Map?.CurrentLocation?.Name),
TextManager.GetWithVariable("crewhiredmessage", "[location]", campaignUI?.Campaign?.Map?.CurrentLocation?.DisplayName),
new LocalizedString[] { TextManager.Get("Ok") });
dialog.Buttons[0].OnClicked += dialog.Close;
}

View File

@@ -512,10 +512,18 @@ namespace Barotrauma
soundStr += " (stopped)";
clr *= 0.5f;
}
else if (playingSoundChannel.Muffled)
else
{
soundStr += " (muffled)";
clr = Color.Lerp(clr, Color.LightGray, 0.5f);
if (playingSoundChannel.Muffled)
{
soundStr += " (muffled)";
clr = Color.Lerp(clr, Color.LightGray, 0.5f);
}
if (playingSoundChannel.FadingOutAndDisposing)
{
soundStr += ". Fading out...";
clr = Color.Lerp(clr, Color.Black, 0.15f);
}
}
}
@@ -2163,10 +2171,10 @@ namespace Barotrauma
};
}
public static GUIMessageBox AskForConfirmation(LocalizedString header, LocalizedString body, Action onConfirm, Action onDeny = null)
public static GUIMessageBox AskForConfirmation(LocalizedString header, LocalizedString body, Action onConfirm, Action onDeny = null, Vector2? relativeSize = null, Point? minSize = null)
{
LocalizedString[] buttons = { TextManager.Get("Ok"), TextManager.Get("Cancel") };
GUIMessageBox msgBox = new GUIMessageBox(header, body, buttons, new Vector2(0.2f, 0.175f), minSize: new Point(300, 175));
GUIMessageBox msgBox = new GUIMessageBox(header, body, buttons, relativeSize: relativeSize ?? new Vector2(0.2f, 0.175f), minSize: minSize ?? new Point(300, 175));
// Cancel button
msgBox.Buttons[1].OnClicked = delegate

View File

@@ -775,23 +775,30 @@ namespace Barotrauma
toolTipBlock.UserData = toolTip;
}
toolTipBlock.RectTransform.AbsoluteOffset =
RectTransform.CalculateAnchorPoint(anchor, targetElement) +
RectTransform.CalculatePivotOffset(pivot, toolTipBlock.RectTransform.NonScaledSize);
CalculateOffset();
if (toolTipBlock.Rect.Right > GameMain.GraphicsWidth - 10)
{
toolTipBlock.RectTransform.AbsoluteOffset -= new Point(toolTipBlock.Rect.Width + targetElement.Width, 0);
anchor = RectTransform.MoveAnchorLeft(anchor);
pivot = (Pivot)RectTransform.MoveAnchorRight((Anchor)pivot);
CalculateOffset();
}
if (toolTipBlock.Rect.Bottom > GameMain.GraphicsHeight - 10)
{
toolTipBlock.RectTransform.AbsoluteOffset -= new Point(
0,
toolTipBlock.Rect.Bottom - (GameMain.GraphicsHeight - 10));
anchor = RectTransform.MoveAnchorTop(anchor);
pivot = (Pivot)RectTransform.MoveAnchorBottom((Anchor)pivot);
CalculateOffset();
}
toolTipBlock.SetTextPos();
toolTipBlock.DrawManually(spriteBatch);
void CalculateOffset()
{
toolTipBlock.RectTransform.AbsoluteOffset =
RectTransform.CalculateAnchorPoint(anchor, targetElement) +
RectTransform.CalculatePivotOffset(pivot, toolTipBlock.RectTransform.NonScaledSize);
}
}
#endregion

View File

@@ -3,6 +3,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Barotrauma.Extensions;
using Microsoft.Xna.Framework.Input;
namespace Barotrauma
{
@@ -81,6 +82,8 @@ namespace Barotrauma
public bool FlashOnAutoCloseCondition { get; set; }
public Action OnEnterPressed { get; set; }
public Type MessageBoxType => type;
public static GUIComponent VisibleBox => MessageBoxes.LastOrDefault();
@@ -89,6 +92,10 @@ namespace Barotrauma
: this(headerText, text, new LocalizedString[] { "OK" }, relativeSize, minSize, type: type)
{
this.Buttons[0].OnClicked = Close;
OnEnterPressed = () =>
{
Buttons[0].OnClicked(Buttons[0], Buttons[0].UserData);
};
}
public GUIMessageBox(RichString headerText, RichString text, LocalizedString[] buttons,
@@ -516,6 +523,11 @@ namespace Barotrauma
protected override void Update(float deltaTime)
{
if (PlayerInput.KeyHit(Keys.Enter))
{
OnEnterPressed?.Invoke();
}
if (Draggable)
{
GUIComponent parent = GUI.MouseOn?.Parent?.Parent;

View File

@@ -18,6 +18,20 @@ namespace Barotrauma
public GUIButton PlusButton { get; private set; }
public GUIButton MinusButton { get; private set; }
private void UpdatePlusMinusButtonVisibility()
{
if (ForceShowPlusMinusButtons
|| inputType == NumberType.Int
|| (inputType == NumberType.Float && MinValueFloat > float.MinValue && MaxValueFloat < float.MaxValue))
{
ShowPlusMinusButtons();
}
else
{
HidePlusMinusButtons();
}
}
private NumberType inputType;
public NumberType InputType
{
@@ -26,15 +40,7 @@ namespace Barotrauma
{
if (inputType == value) { return; }
inputType = value;
if (inputType == NumberType.Int ||
(inputType == NumberType.Float && MinValueFloat > float.MinValue && MaxValueFloat < float.MaxValue))
{
ShowPlusMinusButtons();
}
else
{
HidePlusMinusButtons();
}
UpdatePlusMinusButtonVisibility();
}
}
@@ -46,15 +52,7 @@ namespace Barotrauma
{
minValueFloat = value;
ClampFloatValue();
if (inputType == NumberType.Int ||
(inputType == NumberType.Float && MinValueFloat > float.MinValue && MaxValueFloat < float.MaxValue))
{
ShowPlusMinusButtons();
}
else
{
HidePlusMinusButtons();
}
UpdatePlusMinusButtonVisibility();
}
}
public float? MaxValueFloat
@@ -64,15 +62,7 @@ namespace Barotrauma
{
maxValueFloat = value;
ClampFloatValue();
if (inputType == NumberType.Int ||
(inputType == NumberType.Float && MinValueFloat > float.MinValue && MaxValueFloat < float.MaxValue))
{
ShowPlusMinusButtons();
}
else
{
HidePlusMinusButtons();
}
UpdatePlusMinusButtonVisibility();
}
}
@@ -96,6 +86,19 @@ namespace Barotrauma
}
}
private bool forceShowPlusMinusButtons;
public bool ForceShowPlusMinusButtons
{
get { return forceShowPlusMinusButtons; }
set
{
if (forceShowPlusMinusButtons == value) { return; }
forceShowPlusMinusButtons = value;
UpdatePlusMinusButtonVisibility();
}
}
private int decimalsToDisplay = 1;
public int DecimalsToDisplay
{
@@ -184,7 +187,7 @@ namespace Barotrauma
/// </summary>
public bool WrapAround;
public float valueStep;
public float ValueStep;
private float pressedTimer;
private readonly float pressedDelay = 0.5f;
@@ -339,12 +342,12 @@ namespace Barotrauma
{
if (inputType == NumberType.Int)
{
IntValue -= valueStep > 0 ? (int)valueStep : 1;
IntValue -= ValueStep > 0 ? (int)ValueStep : 1;
ClampIntValue();
}
else if (maxValueFloat.HasValue && minValueFloat.HasValue)
{
FloatValue -= valueStep > 0 ? valueStep : Round();
FloatValue -= ValueStep > 0 ? ValueStep : Round();
ClampFloatValue();
}
}
@@ -353,12 +356,12 @@ namespace Barotrauma
{
if (inputType == NumberType.Int)
{
IntValue += valueStep > 0 ? (int)valueStep : 1;
IntValue += ValueStep > 0 ? (int)ValueStep : 1;
ClampIntValue();
}
else if (inputType == NumberType.Float)
{
FloatValue += valueStep > 0 ? valueStep : Round();
FloatValue += ValueStep > 0 ? ValueStep : Round();
ClampFloatValue();
}
}

View File

@@ -313,13 +313,18 @@ namespace Barotrauma
public class GUIColor : GUISelector<GUIColorPrefab>
{
public GUIColor(string identifier) : base(identifier) { }
private readonly Color fallbackColor;
public GUIColor(string identifier, Color fallbackColor) : base(identifier)
{
this.fallbackColor = fallbackColor;
}
public Color Value
{
get
{
return Prefabs.ActivePrefab.Color;
return Prefabs?.ActivePrefab?.Color ?? fallbackColor;
}
}

View File

@@ -75,72 +75,72 @@ namespace Barotrauma
/// <summary>
/// General green color used for elements whose colors are set from code
/// </summary>
public readonly static GUIColor Green = new GUIColor("Green");
public readonly static GUIColor Green = new GUIColor("Green", new Color(154, 213, 163, 255));
/// <summary>
/// General red color used for elements whose colors are set from code
/// </summary>
public readonly static GUIColor Orange = new GUIColor("Orange");
public readonly static GUIColor Orange = new GUIColor("Orange", new Color(243, 162, 50, 255));
/// <summary>
/// General red color used for elements whose colors are set from code
/// </summary>
public readonly static GUIColor Red = new GUIColor("Red");
public readonly static GUIColor Red = new GUIColor("Red", new Color(245, 105, 105, 255));
/// <summary>
/// General blue color used for elements whose colors are set from code
/// </summary>
public readonly static GUIColor Blue = new GUIColor("Blue");
public readonly static GUIColor Blue = new GUIColor("Blue", new Color(126, 211, 224, 255));
/// <summary>
/// General yellow color used for elements whose colors are set from code
/// </summary>
public readonly static GUIColor Yellow = new GUIColor("Yellow");
public readonly static GUIColor Yellow = new GUIColor("Yellow", new Color(255, 255, 0, 255));
/// <summary>
/// Color to display the name of modded servers in the server list.
/// </summary>
public readonly static GUIColor ModdedServerColor = new GUIColor("ModdedServerColor");
public readonly static GUIColor ModdedServerColor = new GUIColor("ModdedServerColor", new Color(154, 185, 160, 255));
public readonly static GUIColor ColorInventoryEmpty = new GUIColor("ColorInventoryEmpty");
public readonly static GUIColor ColorInventoryHalf = new GUIColor("ColorInventoryHalf");
public readonly static GUIColor ColorInventoryFull = new GUIColor("ColorInventoryFull");
public readonly static GUIColor ColorInventoryBackground = new GUIColor("ColorInventoryBackground");
public readonly static GUIColor ColorInventoryEmptyOverlay = new GUIColor("ColorInventoryEmptyOverlay");
public readonly static GUIColor ColorInventoryEmpty = new GUIColor("ColorInventoryEmpty", new Color(245, 105, 105, 255));
public readonly static GUIColor ColorInventoryHalf = new GUIColor("ColorInventoryHalf", new Color(243, 162, 50, 255));
public readonly static GUIColor ColorInventoryFull = new GUIColor("ColorInventoryFull", new Color(96, 222, 146, 255));
public readonly static GUIColor ColorInventoryBackground = new GUIColor("ColorInventoryBackground", new Color(56, 56, 56, 255));
public readonly static GUIColor ColorInventoryEmptyOverlay = new GUIColor("ColorInventoryEmptyOverlay", new Color(125, 125, 125, 255));
public readonly static GUIColor TextColorNormal = new GUIColor("TextColorNormal");
public readonly static GUIColor TextColorBright = new GUIColor("TextColorBright");
public readonly static GUIColor TextColorDark = new GUIColor("TextColorDark");
public readonly static GUIColor TextColorDim = new GUIColor("TextColorDim");
public readonly static GUIColor TextColorNormal = new GUIColor("TextColorNormal", new Color(228, 217, 167, 255));
public readonly static GUIColor TextColorBright = new GUIColor("TextColorBright", new Color(255, 255, 255, 255));
public readonly static GUIColor TextColorDark = new GUIColor("TextColorDark", new Color(0, 0, 0, 230));
public readonly static GUIColor TextColorDim = new GUIColor("TextColorDim", new Color(153, 153, 153, 153));
public readonly static GUIColor ItemQualityColorPoor = new GUIColor("ItemQualityColorPoor");
public readonly static GUIColor ItemQualityColorNormal = new GUIColor("ItemQualityColorNormal");
public readonly static GUIColor ItemQualityColorGood = new GUIColor("ItemQualityColorGood");
public readonly static GUIColor ItemQualityColorExcellent = new GUIColor("ItemQualityColorExcellent");
public readonly static GUIColor ItemQualityColorMasterwork = new GUIColor("ItemQualityColorMasterwork");
public readonly static GUIColor ItemQualityColorPoor = new GUIColor("ItemQualityColorPoor", new Color(128, 128, 128, 255));
public readonly static GUIColor ItemQualityColorNormal = new GUIColor("ItemQualityColorNormal", new Color(255, 255, 255, 255));
public readonly static GUIColor ItemQualityColorGood = new GUIColor("ItemQualityColorGood", new Color(144, 238, 144, 255));
public readonly static GUIColor ItemQualityColorExcellent = new GUIColor("ItemQualityColorExcellent", new Color(173, 216, 230, 255));
public readonly static GUIColor ItemQualityColorMasterwork = new GUIColor("ItemQualityColorMasterwork", new Color(147, 112, 219, 255));
public readonly static GUIColor ColorReputationVeryLow = new GUIColor("ColorReputationVeryLow");
public readonly static GUIColor ColorReputationLow = new GUIColor("ColorReputationLow");
public readonly static GUIColor ColorReputationNeutral = new GUIColor("ColorReputationNeutral");
public readonly static GUIColor ColorReputationHigh = new GUIColor("ColorReputationHigh");
public readonly static GUIColor ColorReputationVeryHigh = new GUIColor("ColorReputationVeryHigh");
public readonly static GUIColor ColorReputationVeryLow = new GUIColor("ColorReputationVeryLow", new Color(192, 60, 60, 255));
public readonly static GUIColor ColorReputationLow = new GUIColor("ColorReputationLow", new Color(203, 145, 23, 255));
public readonly static GUIColor ColorReputationNeutral = new GUIColor("ColorReputationNeutral", new Color(228, 217, 167, 255));
public readonly static GUIColor ColorReputationHigh = new GUIColor("ColorReputationHigh", new Color(51, 152, 64, 255));
public readonly static GUIColor ColorReputationVeryHigh = new GUIColor("ColorReputationVeryHigh", new Color(71, 160, 164, 255));
// Inventory
public readonly static GUIColor EquipmentSlotIconColor = new GUIColor("EquipmentSlotIconColor");
public readonly static GUIColor EquipmentSlotIconColor = new GUIColor("EquipmentSlotIconColor", new Color(99, 70, 64, 255));
// Health HUD
public readonly static GUIColor BuffColorLow = new GUIColor("BuffColorLow");
public readonly static GUIColor BuffColorMedium = new GUIColor("BuffColorMedium");
public readonly static GUIColor BuffColorHigh = new GUIColor("BuffColorHigh");
public readonly static GUIColor BuffColorLow = new GUIColor("BuffColorLow", new Color(66, 170, 73, 255));
public readonly static GUIColor BuffColorMedium = new GUIColor("BuffColorMedium", new Color(110, 168, 118, 255));
public readonly static GUIColor BuffColorHigh = new GUIColor("BuffColorHigh", new Color(154, 213, 163, 255));
public readonly static GUIColor DebuffColorLow = new GUIColor("DebuffColorLow");
public readonly static GUIColor DebuffColorMedium = new GUIColor("DebuffColorMedium");
public readonly static GUIColor DebuffColorHigh = new GUIColor("DebuffColorHigh");
public readonly static GUIColor DebuffColorLow = new GUIColor("DebuffColorLow", new Color(243, 162, 50, 255));
public readonly static GUIColor DebuffColorMedium = new GUIColor("DebuffColorMedium", new Color(155, 55, 55, 255));
public readonly static GUIColor DebuffColorHigh = new GUIColor("DebuffColorHigh", new Color(228, 27, 27, 255));
public readonly static GUIColor HealthBarColorLow = new GUIColor("HealthBarColorLow");
public readonly static GUIColor HealthBarColorMedium = new GUIColor("HealthBarColorMedium");
public readonly static GUIColor HealthBarColorHigh = new GUIColor("HealthBarColorHigh");
public readonly static GUIColor HealthBarColorPoisoned = new GUIColor("HealthBarColorPoisoned");
public readonly static GUIColor HealthBarColorLow = new GUIColor("HealthBarColorLow", new Color(255, 0, 0, 255));
public readonly static GUIColor HealthBarColorMedium = new GUIColor("HealthBarColorMedium", new Color(255, 165, 0, 255));
public readonly static GUIColor HealthBarColorHigh = new GUIColor("HealthBarColorHigh", new Color(78, 114, 88));
public readonly static GUIColor HealthBarColorPoisoned = new GUIColor("HealthBarColorPoisoned", new Color(100, 150, 0, 255));
private readonly static Point defaultItemFrameMargin = new Point(50, 56);

View File

@@ -461,14 +461,16 @@ namespace Barotrauma
}
private ImmutableArray<Vector2> cachedCaretPositions = ImmutableArray<Vector2>.Empty;
//which text were the cached caret positions calculated for?
private string cachedCaretPositionsText;
public ImmutableArray<Vector2> GetAllCaretPositions()
{
if (cachedCaretPositions.Any())
string textDrawn = Censor ? CensoredText : Text.SanitizedValue;
if (cachedCaretPositions.Any() &&
textDrawn == cachedCaretPositionsText)
{
return cachedCaretPositions;
}
string textDrawn = Censor ? CensoredText : Text.SanitizedValue;
float w = Wrap
? (Rect.Width - Padding.X - Padding.Z) / TextScale
: float.PositiveInfinity;
@@ -482,6 +484,7 @@ namespace Barotrauma
.Select(p => p - new Vector2(alignmentXDiff, 0))
.Select(p => p * TextScale + TextPos - Origin * TextScale)
.ToImmutableArray();
cachedCaretPositionsText = textDrawn;
return cachedCaretPositions;
}

View File

@@ -363,6 +363,10 @@ namespace Barotrauma
{
CaretIndex = Math.Clamp(CaretIndex, 0, textBlock.Text.Length);
var caretPositions = textBlock.GetAllCaretPositions();
if (CaretIndex >= caretPositions.Length)
{
throw new Exception($"Caret index was outside the bounds of the calculated caret positions. Index: {CaretIndex}, caret positions: {caretPositions.Length}, text: {textBlock.Text}");
}
caretPos = caretPositions[CaretIndex];
caretPosDirty = false;
}

View File

@@ -784,11 +784,95 @@ namespace Barotrauma
#region Static methods
public static Pivot MatchPivotToAnchor(Anchor anchor)
{
if (!Enum.TryParse(anchor.ToString(), out Pivot pivot))
return (Pivot)anchor;
}
public static Anchor MatchAnchorToPivot(Pivot pivot)
{
return (Anchor)pivot;
}
/// <summary>
/// Moves the anchor to the left, keeping the vertical position unchanged (e.g. CenterRight -> CenterLeft)
/// </summary>
public static Anchor MoveAnchorLeft(Anchor anchor)
{
switch (anchor)
{
throw new Exception($"[RectTransform] Cannot match pivot to anchor {anchor}");
case Anchor.TopCenter:
case Anchor.TopRight:
return Anchor.TopLeft;
case Anchor.Center:
case Anchor.CenterRight:
return Anchor.CenterLeft;
case Anchor.BottomCenter:
case Anchor.BottomRight:
return Anchor.BottomLeft;
default:
return anchor;
}
}
/// <summary>
/// Moves the anchor to the right, keeping the vertical position unchanged (e.g. CenterLeft -> CenterRight)
/// </summary>
public static Anchor MoveAnchorRight(Anchor anchor)
{
switch (anchor)
{
case Anchor.TopCenter:
case Anchor.TopLeft:
return Anchor.TopRight;
case Anchor.Center:
case Anchor.CenterLeft:
return Anchor.CenterRight;
case Anchor.BottomCenter:
case Anchor.BottomLeft:
return Anchor.BottomRight;
default:
return anchor;
}
}
/// <summary>
/// Moves the anchor to the top, keeping the horizontal position unchanged (e.g. BottomCenter -> TopCenter)
/// </summary>
public static Anchor MoveAnchorTop(Anchor anchor)
{
switch (anchor)
{
case Anchor.CenterLeft:
case Anchor.BottomLeft:
return Anchor.TopLeft;
case Anchor.Center:
case Anchor.BottomCenter:
return Anchor.TopCenter;
case Anchor.CenterRight:
case Anchor.BottomRight:
return Anchor.TopRight;
default:
return anchor;
}
}
/// <summary>
/// Moves the anchor to the bottom, keeping the horizontal position unchanged (e.g. TopCenter -> BottomCenter)
/// </summary>
public static Anchor MoveAnchorBottom(Anchor anchor)
{
switch (anchor)
{
case Anchor.CenterLeft:
case Anchor.TopLeft:
return Anchor.BottomLeft;
case Anchor.Center:
case Anchor.TopCenter:
return Anchor.BottomCenter;
case Anchor.CenterRight:
case Anchor.TopRight:
return Anchor.BottomRight;
default:
return anchor;
}
return pivot;
}
/// <summary>
@@ -811,11 +895,11 @@ namespace Barotrauma
}
}
public static Point CalculatePivotOffset(Pivot pivot, Point size)
public static Point CalculatePivotOffset(Pivot anchor, Point size)
{
int width = size.X;
int height = size.Y;
switch (pivot)
switch (anchor)
{
case Pivot.TopLeft:
return Point.Zero;
@@ -836,7 +920,7 @@ namespace Barotrauma
case Pivot.BottomRight:
return new Point(-width, -height);
default:
throw new NotImplementedException(pivot.ToString());
throw new NotImplementedException(anchor.ToString());
}
}

View File

@@ -40,6 +40,8 @@ namespace Barotrauma
private readonly List<PurchasedItem> itemsToSell = new List<PurchasedItem>();
private readonly List<PurchasedItem> itemsToSellFromSub = new List<PurchasedItem>();
private GUIMessageBox deliveryPrompt;
private StoreTab activeTab = StoreTab.Buy;
private MapEntityCategory? selectedItemCategory;
private bool suppressBuySell;
@@ -341,9 +343,9 @@ namespace Barotrauma
};
var panelMaxWidth = (int)(GUI.xScale * (GUI.HorizontalAspectRatio < 1.4f ? 650 : 560));
var storeContent = new GUILayoutGroup(new RectTransform(new Vector2(0.45f, 1.0f), campaignUI.GetTabContainer(CampaignMode.InteractionType.Store).RectTransform)
var storeContent = new GUILayoutGroup(new RectTransform(new Vector2(0.45f, 1.0f), campaignUI.GetTabContainer(CampaignMode.InteractionType.Store).RectTransform, Anchor.BottomLeft)
{
MaxSize = new Point(panelMaxWidth, campaignUI.GetTabContainer(CampaignMode.InteractionType.Store).Rect.Height)
MaxSize = new Point(panelMaxWidth, campaignUI.GetTabContainer(CampaignMode.InteractionType.Store).Rect.Height - HUDLayoutSettings.ButtonAreaTop.Bottom)
})
{
Stretch = true,
@@ -583,9 +585,9 @@ namespace Barotrauma
// Shopping Crate ------------------------------------------------------------------------------------------------------------------------------------------
var shoppingCrateContent = new GUILayoutGroup(new RectTransform(new Vector2(0.45f, 1.0f), campaignUI.GetTabContainer(CampaignMode.InteractionType.Store).RectTransform, anchor: Anchor.TopRight)
var shoppingCrateContent = new GUILayoutGroup(new RectTransform(new Vector2(0.45f, 1.0f), campaignUI.GetTabContainer(CampaignMode.InteractionType.Store).RectTransform, anchor: Anchor.BottomRight)
{
MaxSize = new Point(panelMaxWidth, campaignUI.GetTabContainer(CampaignMode.InteractionType.Store).Rect.Height)
MaxSize = new Point(panelMaxWidth, campaignUI.GetTabContainer(CampaignMode.InteractionType.Store).Rect.Height - HUDLayoutSettings.ButtonAreaTop.Bottom)
})
{
Stretch = true,
@@ -922,15 +924,12 @@ namespace Barotrauma
{
if (itemPrefab.CanBeBoughtFrom(ActiveStore, out PriceInfo priceInfo) && itemPrefab.CanCharacterBuy())
{
bool isDailySpecial = ActiveStore.DailySpecials.Contains(itemPrefab);
var itemFrame = isDailySpecial ?
storeDailySpecialsGroup.FindChild(c => c.UserData is PurchasedItem pi && pi.ItemPrefab == itemPrefab) :
storeBuyList.Content.FindChild(c => c.UserData is PurchasedItem pi && pi.ItemPrefab == itemPrefab);
if (CargoManager.GetPurchasedItem(ActiveStore, itemPrefab) is { } purchasedItem)
{
quantity = Math.Max(quantity - purchasedItem.Quantity, 0);
}
quantity = Math.Max(quantity - CargoManager.GetPurchasedItemCount(ActiveStore, itemPrefab), 0);
if (CargoManager.GetBuyCrateItem(ActiveStore, itemPrefab) is { } buyCrateItem)
{
quantity = Math.Max(quantity - buyCrateItem.Quantity, 0);
@@ -1245,9 +1244,9 @@ namespace Barotrauma
int totalPrice = 0;
if (ActiveStore != null)
{
foreach (PurchasedItem item in items)
foreach (PurchasedItem item in items.ToList())
{
if (!(item.ItemPrefab.GetPriceInfo(ActiveStore) is { } priceInfo)) { continue; }
if (item.ItemPrefab.GetPriceInfo(ActiveStore) is not { } priceInfo) { continue; }
GUINumberInput numInput = null;
if (!(listBox.Content.FindChild(c => c.UserData is PurchasedItem pi && pi.ItemPrefab.Identifier == item.ItemPrefab.Identifier) is { } itemFrame))
{
@@ -1749,7 +1748,7 @@ namespace Barotrauma
}
// Add items already purchased
CargoManager?.GetPurchasedItems(ActiveStore).ForEach(pi => AddNonEmptyOwnedItems(pi));
CargoManager?.GetPurchasedItems(ActiveStore).Where(pi => !pi.DeliverImmediately).ForEach(pi => AddNonEmptyOwnedItems(pi));
ownedItemsUpdateTimer = 0.0f;
@@ -1959,14 +1958,13 @@ namespace Barotrauma
}
catch (NotImplementedException e)
{
DebugConsole.LogError($"Error getting item availability: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
DebugConsole.LogError($"Error getting item availability: Unknown store tab type. {e.StackTrace.CleanupStackTrace()}");
}
if (list != null && list.Find(i => i.ItemPrefab == itemPrefab) is PurchasedItem item)
{
if (mode == StoreTab.Buy)
{
var purchasedItem = CargoManager.GetPurchasedItem(ActiveStore, item.ItemPrefab);
if (purchasedItem != null) { return Math.Max(item.Quantity - purchasedItem.Quantity, 0); }
return Math.Max(item.Quantity - CargoManager.GetPurchasedItemCount(ActiveStore, item.ItemPrefab), 0);
}
return item.Quantity;
}
@@ -2093,16 +2091,57 @@ namespace Barotrauma
}
itemsToRemove.ForEach(i => itemsToPurchase.Remove(i));
if (itemsToPurchase.None() || Balance < totalPrice) { return false; }
CargoManager.PurchaseItems(ActiveStore.Identifier, itemsToPurchase, true);
GameMain.Client?.SendCampaignState();
var dialog = new GUIMessageBox(
TextManager.Get("newsupplies"),
TextManager.GetWithVariable("suppliespurchasedmessage", "[location]", campaignUI?.Campaign?.Map?.CurrentLocation?.Name),
new LocalizedString[] { TextManager.Get("Ok") });
dialog.Buttons[0].OnClicked += dialog.Close;
if (CampaignMode.AllowImmediateItemDelivery())
{
deliveryPrompt = new GUIMessageBox(
TextManager.Get("newsupplies"),
TextManager.Get("suppliespurchased.deliverymethod"),
new LocalizedString[]
{
TextManager.Get("suppliespurchased.deliverymethod.deliverimmediately"),
TextManager.Get("suppliespurchased.deliverymethod.delivertosub")
});
deliveryPrompt.Buttons[0].OnClicked = (btn, userdata) =>
{
ConfirmPurchase(deliverImmediately: true);
deliveryPrompt.Close();
return true;
};
deliveryPrompt.Buttons[1].OnClicked = (btn, userdata) =>
{
ConfirmPurchase(deliverImmediately: false);
deliveryPrompt.Close();
return true;
};
}
else
{
ConfirmPurchase(deliverImmediately: false);
}
void ConfirmPurchase(bool deliverImmediately)
{
itemsToPurchase.ForEach(it => it.DeliverImmediately = deliverImmediately);
CargoManager.PurchaseItems(ActiveStore.Identifier, itemsToPurchase, removeFromCrate: true);
GameMain.Client?.SendCampaignState();
if (!deliverImmediately)
{
var dialog = new GUIMessageBox(
TextManager.Get("newsupplies"),
TextManager.GetWithVariable("suppliespurchasedmessage", "[location]", campaignUI?.Campaign?.Map?.CurrentLocation?.DisplayName));
dialog.Buttons[0].OnClicked += dialog.Close;
}
}
return false;
}
public void OnDeselected()
{
deliveryPrompt?.Close();
deliveryPrompt = null;
}
private bool SellItems()
{
if (!HasActiveTabPermissions()) { return false; }
@@ -2118,7 +2157,7 @@ namespace Barotrauma
}
catch (NotImplementedException e)
{
DebugConsole.LogError($"Error confirming the store transaction: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
DebugConsole.LogError($"Error confirming the store transaction: Unknown store tab type. {e.StackTrace.CleanupStackTrace()}");
return false;
}
var itemsToRemove = new List<PurchasedItem>();

View File

@@ -130,7 +130,7 @@ namespace Barotrauma
};
content = new GUILayoutGroup(new RectTransform(new Point(background.Rect.Width - HUDLayoutSettings.Padding * 4, background.Rect.Height - HUDLayoutSettings.Padding * 4), background.RectTransform, Anchor.Center)) { AbsoluteSpacing = (int)(HUDLayoutSettings.Padding * 1.5f) };
GUITextBlock header = new GUITextBlock(new RectTransform(new Vector2(1f, 0.0f), content.RectTransform), transferService ? TextManager.Get("switchsubmarineheader") : TextManager.GetWithVariable("outpostshipyard", "[location]", GameMain.GameSession.Map.CurrentLocation.Name), font: GUIStyle.LargeFont);
GUITextBlock header = new GUITextBlock(new RectTransform(new Vector2(1f, 0.0f), content.RectTransform), transferService ? TextManager.Get("switchsubmarineheader") : TextManager.GetWithVariable("outpostshipyard", "[location]", GameMain.GameSession.Map.CurrentLocation.DisplayName), font: GUIStyle.LargeFont);
header.CalculateHeightFromText(0, true);
playerBalanceElement = CampaignUI.AddBalanceElement(header, new Vector2(1.0f, 1.5f));

View File

@@ -165,6 +165,11 @@ namespace Barotrauma
public TabMenu()
{
if (!initialized) { Initialize(); }
if (Level.Loaded == null)
{
//make sure we're not trying to view e.g. mission or reputation info if the tab menu is opened in the test mode
SelectedTab = InfoFrameTab.Crew;
}
CreateInfoFrame(SelectedTab);
SelectInfoFrameTab(SelectedTab);
}
@@ -303,7 +308,7 @@ namespace Barotrauma
{
var missionBtn = createTabButton(InfoFrameTab.Mission, "mission");
eventLogNotification = GameSession.CreateNotificationIcon(missionBtn);
eventLogNotification.Visible = GameMain.GameSession.EventManager?.EventLog?.UnreadEntries ?? false;
eventLogNotification.Visible = GameMain.GameSession?.EventManager?.EventLog?.UnreadEntries ?? false;
if (eventLogNotification.Visible)
{
eventLogNotification.Pulsate(Vector2.One, Vector2.One * 2, 1.0f);
@@ -1508,7 +1513,7 @@ namespace Barotrauma
portraitImage.RectTransform.NonScaledSize = new Point(Math.Min((int)(portraitImage.Rect.Size.Y * portraitAspectRatio), portraitImage.Rect.Width), portraitImage.Rect.Size.Y);
}
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), locationInfoContainer.RectTransform), location.Name, font: GUIStyle.LargeFont);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), locationInfoContainer.RectTransform), location.DisplayName, font: GUIStyle.LargeFont);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), locationInfoContainer.RectTransform), location.Type.Name, font: GUIStyle.SubHeadingFont);
if (location.Faction?.Prefab != null)

View File

@@ -163,7 +163,7 @@ namespace Barotrauma
else if (Tile)
{
Vector2 startPos = new Vector2(rect.X, rect.Y);
Sprite.DrawTiled(spriteBatch, startPos, new Vector2(rect.Width, rect.Height), color, startOffset: uvOffset);
Sprite.DrawTiled(spriteBatch, startPos, new Vector2(rect.Width, rect.Height), color: color, startOffset: uvOffset);
}
else
{

View File

@@ -1110,7 +1110,7 @@ namespace Barotrauma
public static UpgradeFrame CreateUpgradeFrame(UpgradePrefab prefab, UpgradeCategory category, CampaignMode campaign, RectTransform rectTransform, bool addBuyButton = true)
{
int price = prefab.Price.GetBuyPrice(campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation, characterList);
int price = prefab.Price.GetBuyPrice(prefab, campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation, characterList);
return CreateUpgradeEntry(rectTransform, prefab.Sprite, prefab.Name, prefab.Description, price, new CategoryData(category, prefab), addBuyButton, upgradePrefab: prefab, currentLevel: campaign.UpgradeManager.GetUpgradeLevel(prefab, category));
}
@@ -1267,7 +1267,7 @@ namespace Barotrauma
{
LocalizedString promptBody = TextManager.GetWithVariables("Upgrades.PurchasePromptBody",
("[upgradename]", prefab.Name),
("[amount]", prefab.Price.GetBuyPrice(Campaign.UpgradeManager.GetUpgradeLevel(prefab, category), Campaign.Map?.CurrentLocation, characterList).ToString()));
("[amount]", prefab.Price.GetBuyPrice(prefab, Campaign.UpgradeManager.GetUpgradeLevel(prefab, category), Campaign.Map?.CurrentLocation, characterList).ToString()));
currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get("Upgrades.PurchasePromptTitle"), promptBody, () =>
{
if (GameMain.NetworkMember != null)
@@ -1682,7 +1682,7 @@ namespace Barotrauma
GUITextBlock priceLabel = (GUITextBlock)buttonParent.FindChild(UpgradeStoreUserData.PriceLabel, recursive: true);
priceLabel.Visible = true;
int price = prefab.Price.GetBuyPrice(campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation, characterList);
int price = prefab.Price.GetBuyPrice(prefab, campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation, characterList);
if (!WaitForServerUpdate)
{

View File

@@ -124,6 +124,10 @@ namespace Barotrauma
private Viewport defaultViewport;
/// <summary>
/// NOTE: Use very carefully. You need to ensure that you ALWAYS unsubscribe from this when you no longer need the subscriber!
/// If you're subscribing to this from something else than a singleton or something that there's only ever one instance of, you're probably in dangerous territory.
/// </summary>
public event Action ResolutionChanged;
private bool exiting;
@@ -407,7 +411,7 @@ namespace Barotrauma
//do this here because we need it for the loading screen
WaterRenderer.Instance = new WaterRenderer(base.GraphicsDevice);
Quad.Init(GraphicsDevice);
GraphicsQuad.Init(GraphicsDevice);
loadingScreenOpen = true;
TitleScreen = new LoadingScreen(GraphicsDevice)

View File

@@ -147,7 +147,7 @@ namespace Barotrauma
}
catch (NotImplementedException e)
{
DebugConsole.LogError($"Error selling items: uknown store tab type \"{sellingMode}\".\n{e.StackTrace.CleanupStackTrace()}");
DebugConsole.LogError($"Error selling items: unknown store tab type \"{sellingMode}\".\n{e.StackTrace.CleanupStackTrace()}");
return;
}
bool canAddToRemoveQueue = campaign.IsSinglePlayer && Entity.Spawner != null;

View File

@@ -72,6 +72,9 @@ namespace Barotrauma
case InteractionType.MedicalClinic:
CampaignUI.MedicalClinic?.OnDeselected();
break;
case InteractionType.Store:
CampaignUI.Store?.OnDeselected();
break;
}
}
@@ -121,6 +124,16 @@ namespace Barotrauma
{
return AllowedToManageCampaign(ClientPermissions.ManageMoney);
}
public static bool AllowImmediateItemDelivery()
{
if (GameMain.Client == null) { return true; }
return
GameMain.Client.ServerSettings.AllowImmediateItemDelivery ||
GameMain.Client.HasPermission(ClientPermissions.ManageCampaign) ||
GameMain.Client.IsServerOwner;
}
protected GUIButton CreateEndRoundButton()
{
int buttonWidth = (int)(450 * GUI.xScale * (GUI.IsUltrawide ? 3.0f : 1.0f));
@@ -182,12 +195,12 @@ namespace Barotrauma
if (Level.Loaded.EndOutpost == null || !Level.Loaded.EndOutpost.DockedTo.Contains(leavingSub))
{
string textTag = availableTransition == TransitionType.ProgressToNextLocation ? "EnterLocation" : "EnterEmptyLocation";
buttonText = TextManager.GetWithVariable(textTag, "[locationname]", Level.Loaded.EndLocation?.Name ?? "[ERROR]");
buttonText = TextManager.GetWithVariable(textTag, "[locationname]", Level.Loaded.EndLocation?.DisplayName ?? "[ERROR]");
allowEndingRound = !ForceMapUI && !ShowCampaignUI;
}
break;
case TransitionType.LeaveLocation:
buttonText = TextManager.GetWithVariable("LeaveLocation", "[locationname]", Level.Loaded.StartLocation?.Name ?? "[ERROR]");
buttonText = TextManager.GetWithVariable("LeaveLocation", "[locationname]", Level.Loaded.StartLocation?.DisplayName ?? "[ERROR]");
allowEndingRound = !ForceMapUI && !ShowCampaignUI;
break;
case TransitionType.ReturnToPreviousLocation:
@@ -195,7 +208,7 @@ namespace Barotrauma
if (Level.Loaded.StartOutpost == null || !Level.Loaded.StartOutpost.DockedTo.Contains(leavingSub))
{
string textTag = availableTransition == TransitionType.ReturnToPreviousLocation ? "EnterLocation" : "EnterEmptyLocation";
buttonText = TextManager.GetWithVariable(textTag, "[locationname]", Level.Loaded.StartLocation?.Name ?? "[ERROR]");
buttonText = TextManager.GetWithVariable(textTag, "[locationname]", Level.Loaded.StartLocation?.DisplayName ?? "[ERROR]");
allowEndingRound = !ForceMapUI && !ShowCampaignUI;
}
break;
@@ -211,7 +224,7 @@ namespace Barotrauma
endRoundButton.Color = GUIStyle.Red * 0.7f;
endRoundButton.HoverColor = GUIStyle.Red;
}
buttonText = TextManager.GetWithVariable("LeaveLocation", "[locationname]", Level.Loaded.StartLocation?.Name ?? "[ERROR]");
buttonText = TextManager.GetWithVariable("LeaveLocation", "[locationname]", Level.Loaded.StartLocation?.DisplayName ?? "[ERROR]");
allowEndingRound = !ForceMapUI && !ShowCampaignUI;
}
else

View File

@@ -535,7 +535,7 @@ namespace Barotrauma
bool refreshCampaignUI = false;
if (!(GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign) || campaignID != campaign.CampaignID)
if (GameMain.GameSession?.GameMode is not MultiPlayerCampaign campaign || campaignID != campaign.CampaignID)
{
string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer);
@@ -614,7 +614,7 @@ namespace Barotrauma
{
if (availableMission.ConnectionIndex < 0 || availableMission.ConnectionIndex >= campaign.Map.CurrentLocation.Connections.Count)
{
DebugConsole.ThrowError($"Error when receiving campaign data from the server: connection index for mission \"{availableMission.Identifier}\" out of range (index: {availableMission.ConnectionIndex}, current location: {campaign.Map.CurrentLocation.Name}, connections: {campaign.Map.CurrentLocation.Connections.Count}).");
DebugConsole.ThrowError($"Error when receiving campaign data from the server: connection index for mission \"{availableMission.Identifier}\" out of range (index: {availableMission.ConnectionIndex}, current location: {campaign.Map.CurrentLocation.DisplayName}, connections: {campaign.Map.CurrentLocation.Connections.Count}).");
continue;
}
LocationConnection connection = campaign.Map.CurrentLocation.Connections[availableMission.ConnectionIndex];
@@ -647,7 +647,15 @@ namespace Barotrauma
{
if (ownedSubIndex >= GameMain.Client.ServerSubmarines.Count)
{
string errorMsg = $"Error in {nameof(MultiPlayerCampaign.ClientRead)}. Owned submarine index was out of bounds. Index: {ownedSubIndex}, submarines: {string.Join(", ", GameMain.Client.ServerSubmarines.Select(s => s.Name))}";
string errorMsg;
if (GameMain.Client.ServerSubmarines.None())
{
errorMsg = $"Error in {nameof(MultiPlayerCampaign.ClientRead)}. Owned submarine index was out of bounds (list of server submarines is empty).";
}
else
{
errorMsg = $"Error in {nameof(MultiPlayerCampaign.ClientRead)}. Owned submarine index was out of bounds. Index: {ownedSubIndex}, submarines: {string.Join(", ", GameMain.Client.ServerSubmarines.Select(s => s.Name))}";
}
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce(
"MultiPlayerCampaign.ClientRead.OwnerSubIndexOutOfBounds" + ownedSubIndex,
@@ -822,11 +830,12 @@ namespace Barotrauma
UInt16 id = msg.ReadUInt16();
bool hasCharacterData = msg.ReadBoolean();
CharacterInfo myCharacterInfo = null;
bool waitForModsDownloaded = Screen.Selected is ModDownloadScreen;
if (hasCharacterData)
{
myCharacterInfo = CharacterInfo.ClientRead(CharacterPrefab.HumanSpeciesName, msg);
myCharacterInfo = CharacterInfo.ClientRead(CharacterPrefab.HumanSpeciesName, msg, requireJobPrefabFound: !waitForModsDownloaded);
}
if (ShouldApply(NetFlags.CharacterInfo, id, requireUpToDateSave: true))
if (!waitForModsDownloaded && ShouldApply(NetFlags.CharacterInfo, id, requireUpToDateSave: true))
{
if (myCharacterInfo != null)
{

View File

@@ -548,9 +548,12 @@ namespace Barotrauma
}
else
{
//wasn't initially docked (sub doesn't have a docking port?)
// -> choose a destination when the sub is far enough from the start outpost
if (!Submarine.MainSub.AtStartExit && !Level.Loaded.StartOutpost.ExitPoints.Any())
//force the map to open if the sub is somehow not at the start of the outpost level
//UNLESS the level has specific exit points, in that case the sub needs to get to those
if (!Submarine.MainSub.AtStartExit &&
/*there should normally always be a start outpost in outpost levels,
* but that might not always be the case e.g. mods or outdated saves (see #13042)*/
Level.Loaded.StartOutpost is not { ExitPoints.Count: > 0 })
{
ForceMapUI = true;
CampaignUI.SelectTab(InteractionType.Map);

View File

@@ -48,6 +48,8 @@ namespace Barotrauma
private GUIImage eventLogNotification;
private Point prevTopLeftButtonsResolution;
private void CreateTopLeftButtons()
{
if (topLeftButtonGroup != null)
@@ -61,10 +63,6 @@ namespace Barotrauma
AbsoluteSpacing = HUDLayoutSettings.Padding,
CanBeFocused = false
};
topLeftButtonGroup.RectTransform.ParentChanged += (_) =>
{
GameMain.Instance.ResolutionChanged -= CreateTopLeftButtons;
};
int buttonHeight = GUI.IntScale(40);
Vector2 buttonSpriteSize = GUIStyle.GetComponentStyle("CrewListToggleButton").GetDefaultSprite().size;
int buttonWidth = (int)((buttonHeight / buttonSpriteSize.Y) * buttonSpriteSize.X);
@@ -98,8 +96,6 @@ namespace Barotrauma
talentPointNotification = CreateNotificationIcon(tabMenuButton);
eventLogNotification = CreateNotificationIcon(tabMenuButton);
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)
{
@@ -121,6 +117,7 @@ namespace Barotrauma
return true;
}
};
prevTopLeftButtonsResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
}
public void AddToGUIUpdateList()
@@ -133,7 +130,8 @@ namespace Barotrauma
if ((GameMode is not CampaignMode campaign || (!campaign.ForceMapUI && !campaign.ShowCampaignUI)) &&
!CoroutineManager.IsCoroutineRunning("LevelTransition") && !CoroutineManager.IsCoroutineRunning("SubmarineTransition"))
{
if (topLeftButtonGroup == null)
if (topLeftButtonGroup == null ||
prevTopLeftButtonsResolution.X != GameMain.GraphicsWidth || prevTopLeftButtonsResolution.Y != GameMain.GraphicsHeight)
{
CreateTopLeftButtons();
}

View File

@@ -139,6 +139,11 @@ static class ObjectiveManager
VideoPlayer.AddToGUIUpdateList(order: 100);
}
public static bool IsSegmentActive(Identifier segmentId)
{
return activeObjectives.Any(o => o.Id == segmentId);
}
public static void TriggerSegment(Segment segment, bool connectObjective = false)
{
if (segment.SegmentType != SegmentType.InfoBox)
@@ -361,9 +366,18 @@ static class ObjectiveManager
activeObjectives.IndexOf(parentSegment) + activeObjectives.Count(s => s.ParentId == segment.ParentId);
if (objectiveGroup.RectTransform.GetChildIndex(frameRt) != childIndex)
{
frameRt.RepositionChildInHierarchy(childIndex);
activeObjectives.Remove(segment);
activeObjectives.Insert(childIndex, segment);
if (childIndex < 0 || childIndex >= frameRt.Parent.CountChildren)
{
DebugConsole.ThrowError(
$"Error in {nameof(ObjectiveManager.AddToObjectiveList)}. " +
$"Failed to reposition an objective in the list. Text \"{segment.ObjectiveText}\", parentId: {segment.ParentId}, childIndex: {childIndex}");
}
else
{
frameRt.RepositionChildInHierarchy(childIndex);
activeObjectives.Remove(segment);
activeObjectives.Insert(childIndex, segment);
}
}
}
frameRt.AbsoluteOffset = GetObjectiveHiddenPosition();

View File

@@ -564,7 +564,7 @@ namespace Barotrauma
private LocalizedString GetHeaderText(bool gameOver, CampaignMode.TransitionType transitionType)
{
string locationName = Submarine.MainSub is { AtEndExit: true } ? endLocation?.Name : startLocation?.Name;
LocalizedString locationName = Submarine.MainSub is { AtEndExit: true } ? endLocation?.DisplayName : startLocation?.DisplayName;
string textTag;
if (gameOver)
@@ -576,23 +576,23 @@ namespace Barotrauma
switch (transitionType)
{
case CampaignMode.TransitionType.LeaveLocation:
locationName = startLocation?.Name;
locationName = startLocation?.DisplayName;
textTag = "RoundSummaryLeaving";
break;
case CampaignMode.TransitionType.ProgressToNextLocation:
locationName = endLocation?.Name;
locationName = endLocation?.DisplayName;
textTag = "RoundSummaryProgress";
break;
case CampaignMode.TransitionType.ProgressToNextEmptyLocation:
locationName = endLocation?.Name;
locationName = endLocation?.DisplayName;
textTag = "RoundSummaryProgressToEmptyLocation";
break;
case CampaignMode.TransitionType.ReturnToPreviousLocation:
locationName = startLocation?.Name;
locationName = startLocation?.DisplayName;
textTag = "RoundSummaryReturn";
break;
case CampaignMode.TransitionType.ReturnToPreviousEmptyLocation:
locationName = startLocation?.Name;
locationName = startLocation?.DisplayName;
textTag = "RoundSummaryReturnToEmptyLocation";
break;
default:
@@ -603,14 +603,14 @@ namespace Barotrauma
if (startLocation?.Biome != null && startLocation.Biome.IsEndBiome)
{
locationName ??= startLocation.Name;
locationName ??= startLocation.DisplayName;
}
if (textTag == null) { return ""; }
if (locationName == null)
{
DebugConsole.ThrowError($"Error while creating round summary: could not determine destination location. Start location: {startLocation?.Name ?? "null"}, end location: {endLocation?.Name ?? "null"}");
DebugConsole.ThrowError($"Error while creating round summary: could not determine destination location. Start location: {startLocation?.DisplayName ?? "null"}, end location: {endLocation?.DisplayName ?? "null"}");
locationName = "[UNKNOWN]";
}

View File

@@ -568,7 +568,7 @@ namespace Barotrauma
itemContainer.KeepOpenWhenEquippedBy(character) &&
!DraggingItems.Contains(item) &&
character.CanAccessInventory(itemContainer.Inventory) &&
!highlightedSubInventorySlots.Any(s => s.Inventory == itemContainer.Inventory))
!highlightedSubInventorySlots.Any(s => s.Inventory == itemContainer.Inventory && s.SlotIndex == i))
{
ShowSubInventory(new SlotReference(this, visualSlots[i], i, false, itemContainer.Inventory), deltaTime, cam, hideSubInventories, true);
}
@@ -709,11 +709,11 @@ namespace Barotrauma
private void ShowSubInventory(SlotReference slotRef, float deltaTime, Camera cam, List<SlotReference> hideSubInventories, bool isEquippedSubInventory)
{
Rectangle hoverArea = GetSubInventoryHoverArea(slotRef);
if (isEquippedSubInventory)
if (isEquippedSubInventory && slotRef.Inventory is not ItemInventory { Container.MovableFrame: true, Container.KeepOpenWhenEquipped: true })
{
foreach (SlotReference highlightedSubInventorySlot in highlightedSubInventorySlots)
{
if (highlightedSubInventorySlot == slotRef) continue;
if (highlightedSubInventorySlot == slotRef) { continue; }
if (hoverArea.Intersects(GetSubInventoryHoverArea(highlightedSubInventorySlot)))
{
return; // If an equipped one intersects with a currently active hover one, do not open
@@ -818,7 +818,8 @@ namespace Barotrauma
if (selectedContainer != null &&
selectedContainer.Inventory != null &&
!selectedContainer.Inventory.Locked &&
!selectedContainer.Inventory.Locked &&
selectedContainer.DrawInventory &&
allowInventorySwap)
{
//player has selected the inventory of another item -> attempt to move the item there
@@ -841,6 +842,7 @@ namespace Barotrauma
}
else if (character.HeldItems.FirstOrDefault(i =>
i.OwnInventory != null &&
i.OwnInventory.Container.DrawInventory &&
(i.OwnInventory.CanBePut(item) || ((i.OwnInventory.Capacity == 1 || i.OwnInventory.Container.HasSubContainers) && i.OwnInventory.AllowSwappingContainedItems && i.OwnInventory.Container.CanBeContained(item)))) is { } equippedContainer)
{
if (allowEquip)
@@ -1027,7 +1029,7 @@ namespace Barotrauma
//order by the condition of the contained item to prefer putting into the item with the emptiest ammo/battery/tank
foreach (Item heldItem in character.HeldItems.OrderByDescending(heldItem => GetContainPriority(item, heldItem)))
{
if (heldItem.OwnInventory == null) { continue; }
if (heldItem.OwnInventory == null || !heldItem.OwnInventory.Container.DrawInventory) { continue; }
//don't allow swapping if we're moving items into an item with 1 slot holding a stack of items
//(in that case, the quick action should just fill up the stack)
bool disallowSwapping =

View File

@@ -54,7 +54,9 @@ namespace Barotrauma.Items.Components
{
if (deconstructor.InputContainer.Inventory.AllItems.Count() == 2)
{
if (!deconstructor.InputContainer.Inventory.AllItems.All(it => it.Prefab == item.Prefab))
var otherGeneticMaterial =
deconstructor.InputContainer.Inventory.AllItems.FirstOrDefault(it => it != item && it.Prefab == item.Prefab)?.GetComponent<GeneticMaterial>();
if (otherGeneticMaterial == null)
{
buttonText = TextManager.Get("researchstation.combine");
infoText = TextManager.Get("researchstation.combine.infotext");
@@ -62,7 +64,7 @@ namespace Barotrauma.Items.Components
else
{
buttonText = TextManager.Get("researchstation.refine");
int taintedProbability = (int)(GetTaintedProbabilityOnRefine(Character.Controlled) * 100);
int taintedProbability = (int)(GetTaintedProbabilityOnRefine(otherGeneticMaterial, Character.Controlled) * 100);
infoText = TextManager.GetWithVariable("researchstation.refine.infotext", "[taintedprobability]", taintedProbability.ToString());
}
}

View File

@@ -260,6 +260,12 @@ namespace Barotrauma.Items.Components
if (!hasSoundsOfType[(int)type]) { return; }
if (GameMain.Client?.MidRoundSyncing ?? false) { return; }
//above the top boundary of the level (in an inactive respawn shuttle?)
if (item.Submarine != null && Level.Loaded != null && item.Submarine.WorldPosition.Y > Level.Loaded.Size.Y)
{
return;
}
if (loopingSound != null)
{
if (Vector3.DistanceSquared(GameMain.SoundManager.ListenerPosition, new Vector3(item.WorldPosition, 0.0f)) > loopingSound.Range * loopingSound.Range ||
@@ -388,12 +394,9 @@ namespace Barotrauma.Items.Components
}
}
public void StopSounds(ActionType type)
public void StopLoopingSound()
{
if (loopingSound == null) { return; }
if (loopingSound.Type != type) { return; }
if (loopingSoundChannel != null)
{
loopingSoundChannel.FadeOutAndDispose();
@@ -402,6 +405,12 @@ namespace Barotrauma.Items.Components
}
}
public void StopSounds(ActionType type)
{
if (loopingSound == null || loopingSound.Type != type) { return; }
StopLoopingSound();
}
private float GetSoundVolume(ItemSound sound)
{
if (sound == null) { return 0.0f; }
@@ -497,7 +506,8 @@ namespace Barotrauma.Items.Components
case "guiframe":
if (subElement.GetAttribute("rect") != null)
{
DebugConsole.ThrowError($"Error in item config \"{item.ConfigFilePath}\" - GUIFrame defined as rect, use RectTransform instead.");
DebugConsole.ThrowError($"Error in item config \"{item.ConfigFilePath}\" - GUIFrame defined as rect, use RectTransform instead.",
contentPackage: subElement.ContentPackage);
break;
}
GuiFrameSource = subElement;
@@ -516,7 +526,8 @@ namespace Barotrauma.Items.Components
if (filePath.IsNullOrEmpty())
{
DebugConsole.ThrowError(
$"Error when instantiating item \"{item.Name}\" - sound with no file path set");
$"Error when instantiating item \"{item.Name}\" - sound with no file path set",
contentPackage: subElement.ContentPackage);
break;
}
@@ -528,7 +539,8 @@ namespace Barotrauma.Items.Components
}
catch (Exception e)
{
DebugConsole.ThrowError($"Invalid sound type \"{typeStr}\" in item \"{item.Prefab.Identifier}\"!", e);
DebugConsole.ThrowError($"Invalid sound type \"{typeStr}\" in item \"{item.Prefab.Identifier}\"!", e,
contentPackage: subElement.ContentPackage);
break;
}
@@ -758,6 +770,8 @@ namespace Barotrauma.Items.Components
}
}
public virtual void OnPlayerSkillsChanged() { }
public virtual void AddTooltipInfo(ref LocalizedString name, ref LocalizedString description) { }
}
}

View File

@@ -158,7 +158,8 @@ namespace Barotrauma.Items.Components
IndicatorStyle = GUIStyle.GetComponentStyle("ContainedStateIndicator." + ContainedStateIndicatorStyle);
if (ContainedStateIndicator != null || ContainedStateIndicatorEmpty != null)
{
DebugConsole.AddWarning($"Item \"{item.Name}\" defines both a contained state indicator style and a custom indicator sprite. Will use the custom sprite...");
DebugConsole.AddWarning($"Item \"{item.Name}\" defines both a contained state indicator style and a custom indicator sprite. Will use the custom sprite...",
contentPackage: item.Prefab.ContentPackage);
}
}
if (GuiFrame == null)
@@ -345,9 +346,9 @@ namespace Barotrauma.Items.Components
public bool KeepOpenWhenEquippedBy(Character character)
{
if (!character.CanAccessInventory(Inventory) ||
!KeepOpenWhenEquipped ||
!character.HasEquippedItem(Item))
if (!KeepOpenWhenEquipped ||
!character.HasEquippedItem(Item) ||
!character.CanAccessInventory(Inventory))
{
return false;
}
@@ -570,11 +571,13 @@ namespace Barotrauma.Items.Components
{
spriteRotation = contained.Rotation;
}
if ((item.body != null && item.body.Dir == -1) || item.FlippedX)
bool flipX = (item.body != null && item.body.Dir == -1) || item.FlippedX;
if (flipX)
{
spriteEffects |= MathUtils.NearlyEqual(spriteRotation % 180, 90.0f) ? SpriteEffects.FlipVertically : SpriteEffects.FlipHorizontally;
}
if (item.FlippedY)
bool flipY = item.FlippedY;
if (flipY)
{
spriteEffects |= MathUtils.NearlyEqual(spriteRotation % 180, 90.0f) ? SpriteEffects.FlipHorizontally : SpriteEffects.FlipVertically;
}
@@ -588,6 +591,7 @@ namespace Barotrauma.Items.Components
contained.Item.Scale,
spriteEffects,
depth: containedSpriteDepth);
contained.Item.DrawDecorativeSprites(spriteBatch, itemPos, flipX,flipY, (contained.Item.body == null ? 0.0f : contained.Item.body.DrawRotation), containedSpriteDepth);
foreach (ItemContainer ic in contained.Item.GetComponents<ItemContainer>())
{

View File

@@ -227,7 +227,7 @@ namespace Barotrauma.Items.Components
switch (text)
{
case "[CurrentLocationName]":
SetDisplayText(Level.Loaded?.StartLocation?.Name ?? string.Empty);
SetDisplayText(Level.Loaded?.StartLocation?.DisplayName.Value ?? string.Empty);
break;
case "[CurrentBiomeName]":
SetDisplayText(Level.Loaded?.LevelData?.Biome?.DisplayName.Value ?? string.Empty);

View File

@@ -67,7 +67,7 @@ namespace Barotrauma.Items.Components
Light.Position = item.Position;
}
PhysicsBody body = Light.ParentBody;
if (body != null)
if (body != null && body.Enabled)
{
Light.Rotation = body.Dir > 0.0f ? body.DrawRotation : body.DrawRotation - MathHelper.Pi;
Light.LightSpriteEffect = (body.Dir > 0.0f) ? SpriteEffects.None : SpriteEffects.FlipVertically;

View File

@@ -66,7 +66,11 @@ namespace Barotrauma.Items.Components
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), sliderArea.RectTransform, Anchor.TopCenter), "", textColor: GUIStyle.TextColorNormal, font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Center)
{
AutoScaleHorizontal = true,
TextGetter = () => { return TextManager.AddPunctuation(':', powerLabel, (int)(targetForce) + " %"); }
TextGetter = () =>
{
return TextManager.AddPunctuation(':', powerLabel,
TextManager.GetWithVariable("percentageformat", "[value]", ((int)MathF.Round(targetForce)).ToString()));
}
};
forceSlider = new GUIScrollBar(new RectTransform(new Vector2(0.95f, 0.45f), sliderArea.RectTransform, Anchor.Center), barSize: 0.1f, style: "DeviceSlider")
{

View File

@@ -4,6 +4,7 @@ using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace Barotrauma.Items.Components
@@ -393,6 +394,8 @@ namespace Barotrauma.Items.Components
partial void SelectProjSpecific(Character character)
{
if (character != Character.Controlled) { return; }
var nonItems = itemList.Content.Children.Where(c => c.UserData is not FabricationRecipe).ToList();
nonItems.ForEach(i => itemList.Content.RemoveChild(i));
@@ -784,6 +787,7 @@ namespace Barotrauma.Items.Components
private void HideEmptyItemListCategories()
{
bool visibleElementsChanged = false;
//go through the elements backwards, and disable the labels ("insufficient skills to fabricate", "recipe required...") if there's no items below them
bool recipeVisible = false;
foreach (GUIComponent child in itemList.Content.Children.Reverse())
@@ -792,7 +796,11 @@ namespace Barotrauma.Items.Components
{
if (child.Enabled)
{
child.Visible = recipeVisible;
if (child.Visible != recipeVisible)
{
child.Visible = recipeVisible;
visibleElementsChanged = true;
}
}
recipeVisible = false;
}
@@ -802,8 +810,11 @@ namespace Barotrauma.Items.Components
}
}
itemList.UpdateScrollBarSize();
itemList.BarScroll = 0.0f;
if (visibleElementsChanged)
{
itemList.UpdateScrollBarSize();
itemList.BarScroll = 0.0f;
}
}
public bool ClearFilter()
@@ -815,11 +826,22 @@ namespace Barotrauma.Items.Components
return true;
}
private readonly record struct SelectedRecipe(Character User, FabricationRecipe SelectedItem, Option<float> OverrideRequiredTime);
private Option<SelectedRecipe> LastSelectedRecipe = Option.None;
private bool SelectItem(Character user, FabricationRecipe selectedItem, float? overrideRequiredTime = null)
{
this.selectedItem = selectedItem;
displayingForCharacter = user;
var selectedRecipe = new SelectedRecipe(user, selectedItem, overrideRequiredTime is null ? Option.None : Option.Some(overrideRequiredTime.Value));
LastSelectedRecipe = Option.Some(selectedRecipe);
CreateSelectedItemUI(selectedRecipe);
return true;
}
private void CreateSelectedItemUI(SelectedRecipe recipe)
{
var (user, selectedItem, overrideRequiredTime) = recipe;
int max = Math.Max(selectedItem.TargetItem.GetMaxStackSize(outputContainer.Inventory) / selectedItem.Amount, 1);
if (amountInput != null)
@@ -843,8 +865,10 @@ namespace Barotrauma.Items.Components
LocalizedString itemName = GetRecipeNameAndAmount(selectedItem);
LocalizedString name = itemName;
float quality = selectedItem.Quality ?? GetFabricatedItemQuality(selectedItem, user);
if (quality > 0)
QualityResult result = GetFabricatedItemQuality(selectedItem, user);
float quality = selectedItem.Quality ?? result.Quality;
if (quality > 0 || result.HasRandomQualityRollChance)
{
name = TextManager.GetWithVariable("itemname.quality" + (int)quality, "[itemname]", itemName + '\n')
.Fallback(TextManager.GetWithVariable("itemname.quality3", "[itemname]", itemName + '\n'));
@@ -855,6 +879,49 @@ namespace Barotrauma.Items.Components
{
AutoScaleHorizontal = true
};
if (result.HasRandomQualityRollChance)
{
var iconLayout = new GUIFrame(new RectTransform(new Vector2(0.4f, 1f), selectedItemFrame.RectTransform, anchor: Anchor.TopRight), style: null);
var icon = GameSession.CreateNotificationIcon(iconLayout, offset: true);
float percentage1 = result.TotalPlusOnePercentage;
float percentage2 = result.TotalPlusTwoPercentage;
string chance1text = percentage1.ToString("F1", CultureInfo.InvariantCulture);
string chance2text = percentage2.ToString("F1", CultureInfo.InvariantCulture);
int quality1 = Math.Clamp(result.Quality + 1, min: 0, max: 3);
int quality2 = Math.Clamp(result.Quality + 2, min: 0, max: 3);
LocalizedString quality1Text = TextManager.Get($"quality{quality1}");
LocalizedString quality2Text = TextManager.Get($"quality{quality2}");
string localizationTag = percentage2 > 0f && percentage1 > 0 && quality1 != quality2 ? "meetsbonusrequirementtwice" : "meetsbonusrequirement";
var variables = new (string Key, LocalizedString Value)[]
{
("[chance]", chance1text), ("[quality]", quality1Text),
("[chance2]", chance2text), ("[quality2]", quality2Text)
};
if (MathUtils.NearlyEqual(percentage1, 0))
{
variables = new[] { ("[chance]", chance2text), ("[quality]", quality2Text) };
}
if (quality1 == quality2)
{
LocalizedString rawPercentage = result.PlusOnePercentage.ToString("F1", CultureInfo.InvariantCulture);
variables = new[] { ("[chance]", rawPercentage), ("[quality]", quality1Text) };
}
LocalizedString qualityTooltip = TextManager.GetWithVariables(localizationTag, variables);
icon.ToolTip = RichString.Rich(qualityTooltip);
icon.Visible = icon.CanBeFocused = true;
}
nameBlock.Padding = new Vector4(0, nameBlock.Padding.Y, GUI.IntScale(5), nameBlock.Padding.W);
if (nameBlock.TextScale < 0.7f)
{
@@ -865,15 +932,15 @@ namespace Barotrauma.Items.Components
nameBlock.Wrap = true;
nameBlock.SetTextPos();
nameBlock.RectTransform.MinSize = new Point(0, (int)(nameBlock.TextSize.Y * nameBlock.TextScale));
}
}
if (!selectedItem.TargetItem.Description.IsNullOrEmpty())
{
var description = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform),
selectedItem.TargetItem.Description,
font: GUIStyle.SmallFont, wrap: true);
description.Padding = new Vector4(0, description.Padding.Y, description.Padding.Z, description.Padding.W);
while (description.Rect.Height + nameBlock.Rect.Height > paddedFrame.Rect.Height)
{
var lines = description.WrappedText.Split('\n');
@@ -884,13 +951,13 @@ namespace Barotrauma.Items.Components
description.ToolTip = selectedItem.TargetItem.Description;
}
}
IEnumerable<Skill> inadequateSkills = Enumerable.Empty<Skill>();
if (user != null)
{
inadequateSkills = selectedItem.RequiredSkills.Where(skill => user.GetSkillLevel(skill.Identifier) < Math.Round(skill.Level * SkillRequirementMultiplier));
}
if (selectedItem.RequiredSkills.Any())
{
LocalizedString text = "";
@@ -911,9 +978,10 @@ namespace Barotrauma.Items.Components
float degreeOfSuccess = user == null ? 0.0f : FabricationDegreeOfSuccess(user, selectedItem.RequiredSkills);
if (degreeOfSuccess > 0.5f) { degreeOfSuccess = 1.0f; }
float requiredTime = overrideRequiredTime ??
(user == null ? selectedItem.RequiredTime : GetRequiredTime(selectedItem, user));
float requiredTime = overrideRequiredTime.TryUnwrap(out var time)
? time
: (user == null ? selectedItem.RequiredTime : GetRequiredTime(selectedItem, user));
if ((int)requiredTime > 0)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedReqFrame.RectTransform),
@@ -936,7 +1004,6 @@ namespace Barotrauma.Items.Components
font: GUIStyle.SmallFont);
}
return true;
}
public void HighlightRecipe(string identifier, Color color)
@@ -1046,6 +1113,15 @@ namespace Barotrauma.Items.Components
}
}
public override void OnPlayerSkillsChanged()
=> RefreshSelectedItem();
public void RefreshSelectedItem()
{
if (!LastSelectedRecipe.TryUnwrap(out var lastSelected)) { return; }
CreateSelectedItemUI(lastSelected);
}
partial void UpdateRequiredTimeProjSpecific()
{
if (requiredTimeBlock == null) { return; }

View File

@@ -66,7 +66,15 @@ namespace Barotrauma.Items.Components
private float prevPassivePingRadius;
private Vector2 center;
private float displayScale;
/// <summary>
/// Current scale of the display, taking zoom into account. In other words, the scaling factor of world coordinates to coordinates on the display.
/// </summary>
public float DisplayScale
{
get;
private set;
} = 1.0f;
private const float DisruptionUpdateInterval = 0.2f;
private float disruptionUpdateTimer;
@@ -751,9 +759,9 @@ namespace Barotrauma.Items.Components
{
var activePing = activePings[pingIndex];
float pingRadius = DisplayRadius * activePing.State / zoom;
if (disruptionUpdateTimer <= 0.0f) { UpdateDisruptions(transducerCenter, pingRadius / displayScale); }
if (disruptionUpdateTimer <= 0.0f) { UpdateDisruptions(transducerCenter, pingRadius / DisplayScale); }
Ping(transducerCenter, transducerCenter,
pingRadius, activePing.PrevPingRadius, displayScale, range / zoom, passive: false, pingStrength: 2.0f);
pingRadius, activePing.PrevPingRadius, DisplayScale, range / zoom, passive: false, pingStrength: 2.0f);
activePing.PrevPingRadius = pingRadius;
}
if (disruptionUpdateTimer <= 0.0f)
@@ -770,7 +778,7 @@ namespace Barotrauma.Items.Components
if (c.Params.HideInSonar) { continue; }
if (!c.IsUnconscious && c.Params.DistantSonarRange > 0.0f &&
((c.WorldPosition - transducerCenter) * displayScale).LengthSquared() > DisplayRadius * DisplayRadius)
((c.WorldPosition - transducerCenter) * DisplayScale).LengthSquared() > DisplayRadius * DisplayRadius)
{
Vector2 targetVector = c.WorldPosition - transducerCenter;
if (targetVector.LengthSquared() > MathUtils.Pow2(c.Params.DistantSonarRange)) { continue; }
@@ -818,7 +826,7 @@ namespace Barotrauma.Items.Components
if (dist > prevPassivePingRadius * Range && dist <= passivePingRadius * Range && Rand.Int(sonarBlips.Count) < 500)
{
Ping(t.WorldPosition, transducerCenter,
t.SoundRange * displayScale, 0, displayScale, range,
t.SoundRange * DisplayScale, 0, DisplayScale, range,
passive: true, pingStrength: 0.5f, needsToBeInSector: t);
if (t.IsWithinSector(transducerCenter))
{
@@ -857,7 +865,7 @@ namespace Barotrauma.Items.Components
displayBorderSize = 0.2f;
center = rect.Center.ToVector2();
DisplayRadius = (rect.Width / 2.0f) * (1.0f - displayBorderSize);
displayScale = DisplayRadius / range * zoom;
DisplayScale = DisplayRadius / range * zoom;
screenBackground?.Draw(spriteBatch, center, 0.0f, rect.Width / screenBackground.size.X);
@@ -972,7 +980,7 @@ namespace Barotrauma.Items.Components
aiTarget.SonarIconIdentifier,
aiTarget,
aiTarget.WorldPosition, transducerCenter,
displayScale, center, DisplayRadius * 0.975f);
DisplayScale, center, DisplayRadius * 0.975f);
}
}
@@ -983,21 +991,21 @@ namespace Barotrauma.Items.Components
if (Level.Loaded.StartLocation?.Type is { ShowSonarMarker: true })
{
DrawMarker(spriteBatch,
Level.Loaded.StartLocation.Name,
Level.Loaded.StartLocation.DisplayName.Value,
(Level.Loaded.StartOutpost != null ? "outpost" : "location").ToIdentifier(),
"startlocation",
Level.Loaded.StartExitPosition, transducerCenter,
displayScale, center, DisplayRadius);
DisplayScale, center, DisplayRadius);
}
if (Level.Loaded is { EndLocation.Type.ShowSonarMarker: true, Type: LevelData.LevelType.LocationConnection })
{
DrawMarker(spriteBatch,
Level.Loaded.EndLocation.Name,
Level.Loaded.EndLocation.DisplayName.Value,
(Level.Loaded.EndOutpost != null ? "outpost" : "location").ToIdentifier(),
"endlocation",
Level.Loaded.EndExitPosition, transducerCenter,
displayScale, center, DisplayRadius);
DisplayScale, center, DisplayRadius);
}
for (int i = 0; i < Level.Loaded.Caves.Count; i++)
@@ -1009,7 +1017,7 @@ namespace Barotrauma.Items.Components
"cave".ToIdentifier(),
"cave" + i,
cave.StartPos.ToVector2(), transducerCenter,
displayScale, center, DisplayRadius);
DisplayScale, center, DisplayRadius);
}
}
@@ -1026,7 +1034,7 @@ namespace Barotrauma.Items.Components
mission.SonarIconIdentifier,
"mission" + missionIndex + ":" + i,
position, transducerCenter,
displayScale, center, DisplayRadius * 0.95f);
DisplayScale, center, DisplayRadius * 0.95f);
}
i++;
}
@@ -1059,7 +1067,7 @@ namespace Barotrauma.Items.Components
DrawMarker(spriteBatch,
i.Name, "mineral".ToIdentifier(), "mineralcluster" + i,
c.center, transducerCenter,
displayScale, center, DisplayRadius * 0.95f,
DisplayScale, center, DisplayRadius * 0.95f,
onlyShowTextOnMouseOver: true);
}
}
@@ -1088,19 +1096,19 @@ namespace Barotrauma.Items.Components
(sub.Info.HasTag(SubmarineTag.Shuttle) ? "shuttle" : "submarine").ToIdentifier(),
sub,
sub.WorldPosition, transducerCenter,
displayScale, center, DisplayRadius * 0.95f);
DisplayScale, center, DisplayRadius * 0.95f);
}
if (GameMain.DebugDraw)
{
var steering = item.GetComponent<Steering>();
steering?.DebugDrawHUD(spriteBatch, transducerCenter, displayScale, DisplayRadius, center);
steering?.DebugDrawHUD(spriteBatch, transducerCenter, DisplayScale, DisplayRadius, center);
}
}
private void DrawOwnSubmarineBorders(SpriteBatch spriteBatch, Vector2 transducerCenter, float signalStrength)
{
float simScale = displayScale * Physics.DisplayToSimRation * zoom;
float simScale = DisplayScale * Physics.DisplayToSimRation;
foreach (Submarine submarine in Submarine.Loaded)
{
@@ -1167,7 +1175,7 @@ namespace Barotrauma.Items.Components
private void DrawDockingPorts(SpriteBatch spriteBatch, Vector2 transducerCenter, float signalStrength)
{
float scale = displayScale * zoom;
float scale = DisplayScale;
Steering steering = item.GetComponent<Steering>();
if (steering != null && steering.DockingModeEnabled && steering.ActiveDockingSource != null)
@@ -1219,7 +1227,7 @@ namespace Barotrauma.Items.Components
private void DrawDockingIndicator(SpriteBatch spriteBatch, Steering steering, ref Vector2 transducerCenter)
{
float scale = displayScale * zoom;
float scale = DisplayScale;
Vector2 worldFocusPos = (steering.ActiveDockingSource.Item.WorldPosition + steering.DockingTarget.Item.WorldPosition) / 2.0f;
worldFocusPos.X = steering.DockingTarget.Item.WorldPosition.X;
@@ -1591,7 +1599,7 @@ namespace Barotrauma.Items.Components
{
lineStep /= zoom;
zStep /= zoom;
range *= displayScale;
range *= DisplayScale;
float length = (point1 - point2).Length();
Vector2 lineDir = (point2 - point1) / length;
for (float x = 0; x < length; x += lineStep * Rand.Range(0.8f, 1.2f))
@@ -1602,12 +1610,12 @@ namespace Barotrauma.Items.Components
//ignore if outside the display
Vector2 transducerDiff = point - transducerPos;
Vector2 transducerDisplayDiff = transducerDiff * displayScale;
Vector2 transducerDisplayDiff = transducerDiff * DisplayScale / zoom;
if (transducerDisplayDiff.LengthSquared() > DisplayRadius * DisplayRadius) { continue; }
//ignore if the point is not within the ping
Vector2 pointDiff = point - pingSource;
Vector2 displayPointDiff = pointDiff * displayScale;
Vector2 displayPointDiff = pointDiff * DisplayScale / zoom;
float displayPointDistSqr = displayPointDiff.LengthSquared();
if (displayPointDistSqr < prevPingRadius * prevPingRadius || displayPointDistSqr > pingRadius * pingRadius) { continue; }
@@ -1628,9 +1636,9 @@ namespace Barotrauma.Items.Components
float displayPointDist = (float)Math.Sqrt(displayPointDistSqr);
float alpha = pingStrength * Rand.Range(1.5f, 2.0f);
for (float z = 0; z < DisplayRadius - transducerDist * displayScale; z += zStep)
for (float z = 0; z < DisplayRadius - transducerDist * DisplayScale; z += zStep)
{
Vector2 pos = point + Rand.Vector(150.0f / zoom) + pingDirection * z / displayScale;
Vector2 pos = point + Rand.Vector(150.0f / zoom) + pingDirection * z / DisplayScale;
float fadeTimer = alpha * (1.0f - displayPointDist / range);
if (needsToBeInSector != null)
@@ -1697,7 +1705,7 @@ namespace Barotrauma.Items.Components
private bool CheckBlipVisibility(SonarBlip blip, Vector2 transducerPos)
{
Vector2 pos = (blip.Position - transducerPos) * displayScale * zoom;
Vector2 pos = (blip.Position - transducerPos) * DisplayScale;
pos.Y = -pos.Y;
float posDistSqr = pos.LengthSquared();
@@ -1731,7 +1739,7 @@ namespace Barotrauma.Items.Components
}
if (currentPingIndex != -1 && activePings[currentPingIndex].IsDirectional)
{
var pos = (resourcePos - transducerPos) * displayScale * zoom;
var pos = (resourcePos - transducerPos) * DisplayScale;
pos.Y = -pos.Y;
var length = pos.Length();
var dir = pos / length;
@@ -1749,7 +1757,7 @@ namespace Barotrauma.Items.Components
float distort = 1.0f - item.Condition / item.MaxCondition;
Vector2 pos = (blip.Position - transducerPos) * displayScale * zoom;
Vector2 pos = (blip.Position - transducerPos) * DisplayScale;
pos.Y = -pos.Y;
if (Rand.Range(0.5f, 2.0f) < distort) { pos.X = -pos.X; }
@@ -1825,14 +1833,13 @@ namespace Barotrauma.Items.Components
Vector2 position = worldPosition - transducerPosition;
position *= zoom;
position *= scale;
position.Y = -position.Y;
float textAlpha = MathHelper.Clamp(1.5f - dist / 50000.0f, 0.5f, 1.0f);
Vector2 dir = Vector2.Normalize(position);
Vector2 markerPos = (linearDist * zoom * scale > radius) ? dir * radius : position;
Vector2 markerPos = (linearDist * scale > radius) ? dir * radius : position;
markerPos += center;
markerPos.X = (int)markerPos.X;

View File

@@ -216,7 +216,7 @@ namespace Barotrauma.Items.Components
}
};
levelStartTickBox = new GUITickBox(new RectTransform(new Vector2(1, 0.333f), paddedAutoPilotControls.RectTransform, Anchor.Center),
GameMain.GameSession?.StartLocation == null ? "" : ToolBox.LimitString(GameMain.GameSession.StartLocation.Name, GUIStyle.SmallFont, textLimit),
GameMain.GameSession?.StartLocation == null ? "" : ToolBox.LimitString(GameMain.GameSession.StartLocation.DisplayName, GUIStyle.SmallFont, textLimit),
font: GUIStyle.SmallFont, style: "GUIRadioButton")
{
Enabled = autoPilot,
@@ -243,7 +243,7 @@ namespace Barotrauma.Items.Components
};
levelEndTickBox = new GUITickBox(new RectTransform(new Vector2(1, 0.333f), paddedAutoPilotControls.RectTransform, Anchor.BottomCenter),
(GameMain.GameSession?.EndLocation == null || Level.IsLoadedOutpost) ? "" : ToolBox.LimitString(GameMain.GameSession.EndLocation.Name, GUIStyle.SmallFont, textLimit),
(GameMain.GameSession?.EndLocation == null || Level.IsLoadedOutpost) ? "" : ToolBox.LimitString(GameMain.GameSession.EndLocation.DisplayName, GUIStyle.SmallFont, textLimit),
font: GUIStyle.SmallFont, style: "GUIRadioButton")
{
Enabled = autoPilot,
@@ -389,7 +389,7 @@ namespace Barotrauma.Items.Components
if (!ObjectiveManager.AllActiveObjectivesCompleted())
{
exitOutpostPrompt = new GUIMessageBox("",
TextManager.GetWithVariable("CampaignExitTutorialOutpostPrompt", "[locationname]", campaign.Map.CurrentLocation.Name),
TextManager.GetWithVariable("CampaignExitTutorialOutpostPrompt", "[locationname]", campaign.Map.CurrentLocation.DisplayName),
new LocalizedString[] { TextManager.Get("yes"), TextManager.Get("no") });
exitOutpostPrompt.Buttons[0].OnClicked += (_, _) =>
{
@@ -509,9 +509,9 @@ namespace Barotrauma.Items.Components
noPowerTip = TextManager.Get("SteeringNoPowerTip");
autoPilotMaintainPosTip = TextManager.Get("SteeringAutoPilotMaintainPosTip");
autoPilotLevelStartTip = TextManager.GetWithVariable("SteeringAutoPilotLocationTip", "[locationname]",
GameMain.GameSession?.StartLocation == null ? "Start" : GameMain.GameSession.StartLocation.Name);
GameMain.GameSession?.StartLocation == null ? "Start" : GameMain.GameSession.StartLocation.DisplayName);
autoPilotLevelEndTip = TextManager.GetWithVariable("SteeringAutoPilotLocationTip", "[locationname]",
GameMain.GameSession?.EndLocation == null ? "End" : GameMain.GameSession.EndLocation.Name);
GameMain.GameSession?.EndLocation == null ? "End" : GameMain.GameSession.EndLocation.DisplayName);
}
protected override void OnResolutionChanged()
@@ -589,7 +589,8 @@ namespace Barotrauma.Items.Components
Sonar sonar = item.GetComponent<Sonar>();
if (sonar != null && controlledSub != null)
{
Vector2 displayPosToMaintain = ((posToMaintain.Value - controlledSub.WorldPosition)) / sonar.Range * sonar.DisplayRadius * sonar.Zoom;
Vector2 displayPosToMaintain = ((posToMaintain.Value - controlledSub.WorldPosition)) * sonar.DisplayScale;
displayPosToMaintain.Y = -displayPosToMaintain.Y;
displayPosToMaintain = displayPosToMaintain.ClampLength(velRect.Width / 2);
displayPosToMaintain = steerArea.Rect.Center.ToVector2() + displayPosToMaintain;
@@ -670,14 +671,14 @@ namespace Barotrauma.Items.Components
pos2.Y = -pos2.Y;
pos2 += center;
GUI.DrawLine(spriteBatch,
pos1,
GUI.DrawLine(spriteBatch,
pos1,
pos2,
GUIStyle.Red * 0.6f, width: 3);
if (obstacle.Intersection.HasValue)
{
Vector2 intersectionPos = (obstacle.Intersection.Value - transducerCenter) *displayScale;
Vector2 intersectionPos = (obstacle.Intersection.Value - transducerCenter) * displayScale;
intersectionPos.Y = -intersectionPos.Y;
intersectionPos += center;
GUI.DrawRectangle(spriteBatch, intersectionPos - Vector2.One * 2, Vector2.One * 4, GUIStyle.Red);

View File

@@ -151,7 +151,7 @@ namespace Barotrauma.Items.Components
GUI.DrawLine(spriteBatch,
new Vector2(debugRayStartPos.X, -debugRayStartPos.Y),
new Vector2(debugRayEndPos.X, -debugRayEndPos.Y),
Color.Yellow);
Color.Yellow, width: 3f);
}
}
#endif

View File

@@ -95,7 +95,7 @@ namespace Barotrauma.Items.Components
MaxValueFloat = numberInputMax,
FloatValue = Math.Clamp(floatSignal, numberInputMin, numberInputMax),
DecimalsToDisplay = ciElement.NumberInputDecimalPlaces,
valueStep = numberInputStep,
ValueStep = numberInputStep,
OnValueChanged = (ni) =>
{
if (GameMain.Client == null)
@@ -121,7 +121,7 @@ namespace Barotrauma.Items.Components
MinValueInt = numberInputMin,
MaxValueInt = numberInputMax,
IntValue = Math.Clamp(intSignal, numberInputMin, numberInputMax),
valueStep = numberInputStep,
ValueStep = numberInputStep,
OnValueChanged = (ni) =>
{
if (GameMain.Client == null)
@@ -137,7 +137,8 @@ namespace Barotrauma.Items.Components
}
else
{
DebugConsole.LogError($"Error creating a CustomInterface component: unexpected NumberType \"{(ciElement.NumberType.HasValue ? ciElement.NumberType.Value.ToString() : "none")}\"");
DebugConsole.LogError($"Error creating a CustomInterface component: unexpected NumberType \"{(ciElement.NumberType.HasValue ? ciElement.NumberType.Value.ToString() : "none")}\"",
contentPackage: item.Prefab.ContentPackage);
}
if (numberInput != null)
{

View File

@@ -349,8 +349,8 @@ namespace Barotrauma.Items.Components
}
GUI.DrawString(spriteBatch, hudPos, texts[0].Value, textColors[0] * alpha, Color.Black * 0.7f * alpha, 2, GUIStyle.SubHeadingFont, ForceUpperCase.No);
hudPos.X += 5.0f;
hudPos.Y += 24.0f * GameSettings.CurrentConfig.Graphics.TextScale;
hudPos.X += 5.0f * GUI.Scale;
hudPos.Y += GUIStyle.SubHeadingFont.MeasureString(texts[0].Value).Y;
hudPos.X = (int)hudPos.X;
hudPos.Y = (int)hudPos.Y;
@@ -358,7 +358,7 @@ namespace Barotrauma.Items.Components
for (int i = 1; i < texts.Count; i++)
{
GUI.DrawString(spriteBatch, hudPos, texts[i], textColors[i] * alpha, Color.Black * 0.7f * alpha, 2, GUIStyle.SmallFont);
hudPos.Y += (int)(18.0f * GameSettings.CurrentConfig.Graphics.TextScale);
hudPos.Y += (int)(GUIStyle.SubHeadingFont.MeasureString(texts[i].Value).Y);
}
}
}

View File

@@ -16,6 +16,8 @@ namespace Barotrauma.Items.Components
private GUIProgressBar powerIndicator;
private Vector2? debugDrawTargetPos;
public int UIElementHeight
{
get
@@ -422,9 +424,23 @@ namespace Barotrauma.Items.Components
if (GameMain.DebugDraw)
{
Vector2 firingPos = GetRelativeFiringPosition();
Vector2 endPos = firingPos + 3500 * GetBarrelDir();
firingPos.Y = -firingPos.Y;
endPos.Y = -endPos.Y;
GUI.DrawLine(spriteBatch, firingPos - Vector2.UnitX * 5, firingPos + Vector2.UnitX * 5, Color.Red);
GUI.DrawLine(spriteBatch, firingPos - Vector2.UnitY * 5, firingPos + Vector2.UnitY * 5, Color.Red);
if (debugDrawTargetPos.HasValue)
{
Vector2 targetPos = debugDrawTargetPos.Value;
targetPos.Y = -targetPos.Y;
GUI.DrawLine(spriteBatch, targetPos - Vector2.UnitX * 5, targetPos + Vector2.UnitX * 5, Color.Magenta, width: 5);
GUI.DrawLine(spriteBatch, targetPos - Vector2.UnitY * 5, targetPos + Vector2.UnitY * 5, Color.Magenta, width: 5);
GUI.DrawLine(spriteBatch, firingPos, targetPos, Color.Magenta, width: 2);
}
GUI.DrawLine(spriteBatch, firingPos, endPos, Color.LightGray, width: 2);
}
if (!editing || GUI.DisableHUD || !item.IsSelected) { return; }

View File

@@ -258,7 +258,7 @@ namespace Barotrauma
else
{
LocalizedString description = item.Description;
if (item.HasTag(Tags.IdCard) || item.HasTag(Tags.DespawnContainer))
if (item.HasTag(Tags.IdCardTag) || item.HasTag(Tags.DespawnContainer))
{
string[] readTags = item.Tags.Split(',');
string idName = null;
@@ -730,7 +730,7 @@ namespace Barotrauma
DraggingInventory = null;
subInventory.savedPosition = PlayerInput.MousePosition.ToPoint();
}
else
else if (DraggingInventory == subInventory)
{
subInventory.savedPosition = PlayerInput.MousePosition.ToPoint();
}
@@ -901,7 +901,7 @@ namespace Barotrauma
if (IsOnInventorySlot(Character.Controlled.SelectedCharacter.Inventory)) { return true; }
}
bool IsOnInventorySlot(Inventory inventory)
static bool IsOnInventorySlot(Inventory inventory)
{
for (var i = 0; i < inventory.visualSlots.Length; i++)
{
@@ -1107,7 +1107,7 @@ namespace Barotrauma
if (container.MovableFrame && !IsInventoryHoverAvailable(Owner as Character, container))
{
if (positionUpdateQueued) // Wait a frame before updating the positioning of the container after a resolution change to have everything working
if (container.Inventory.positionUpdateQueued) // Wait a frame before updating the positioning of the container after a resolution change to have everything working
{
int height = (int)(movableFrameRectHeight * UIScale);
CreateSlots();
@@ -1116,7 +1116,7 @@ namespace Barotrauma
draggableIndicatorOffset = DraggableIndicator.size * draggableIndicatorScale / 2f;
draggableIndicatorOffset += new Vector2(height / 2f - draggableIndicatorOffset.Y);
container.Inventory.originalPos = container.Inventory.savedPosition = container.Inventory.movableFrameRect.Center;
positionUpdateQueued = false;
container.Inventory.positionUpdateQueued = false;
}
if (container.Inventory.movableFrameRect.Size == Point.Zero || GUI.HasSizeChanged(prevScreenResolution, prevUIScale, prevHUDScale))
@@ -1127,11 +1127,20 @@ namespace Barotrauma
prevScreenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
prevUIScale = UIScale;
prevHUDScale = GUI.Scale;
positionUpdateQueued = true;
container.Inventory.positionUpdateQueued = true;
}
else
{
GUI.DrawRectangle(spriteBatch, container.Inventory.movableFrameRect, movableFrameRectColor, true);
Color color = movableFrameRectColor;
if (DraggingInventory != null && DraggingInventory != container.Inventory)
{
color *= 0.7f;
}
else if (container.Inventory.movableFrameRect.Contains(PlayerInput.MousePosition))
{
color = Color.Lerp(color, PlayerInput.PrimaryMouseButtonHeld() ? Color.Black : Color.White, 0.25f);
}
GUI.DrawRectangle(spriteBatch, container.Inventory.movableFrameRect, color, true);
DraggableIndicator.Draw(spriteBatch, container.Inventory.movableFrameRect.Location.ToVector2() + draggableIndicatorOffset, 0, draggableIndicatorScale);
}
}
@@ -1269,12 +1278,20 @@ namespace Barotrauma
if (DraggingItems.Count(it => !it.IsFullCondition && it.Condition > 0.0f) > 1 ||
selectedInventory.GetItemsAt(slotIndex).Count(it => !it.IsFullCondition && it.Condition > 0.0f) > 1)
{
allowCombine = false;
allowCombine = false;
}
int itemCount = 0;
foreach (Item item in DraggingItems)
{
bool success = selectedInventory.TryPutItem(item, slotIndex, allowSwapping: !anySuccess, allowCombine, Character.Controlled);
if (selectedInventory.GetItemAt(slotIndex)?.OwnInventory?.Container is { } container &&
container.Inventory.CanBePut(item))
{
if (!container.AllowDragAndDrop || !container.DrawInventory)
{
allowCombine = false;
}
}
bool success = selectedInventory.TryPutItem(item, slotIndex, allowSwapping: !anySuccess, allowCombine, Character.Controlled);
if (success)
{
anySuccess = true;
@@ -1380,18 +1397,17 @@ namespace Barotrauma
protected static Rectangle GetSubInventoryHoverArea(SlotReference subSlot)
{
Rectangle hoverArea;
if ((Screen.Selected != GameMain.SubEditorScreen || GameMain.SubEditorScreen.DrawCharacterInventory) &&
(!subSlot.Inventory.Movable() ||
(Character.Controlled?.Inventory == subSlot.ParentInventory && !Character.Controlled.HasEquippedItem(subSlot.Item)) ||
(subSlot.ParentInventory is CharacterInventory characterInventory && characterInventory.CurrentLayout != CharacterInventory.Layout.Default)))
if (Character.Controlled == null)
{
//slot not visible as a separate, movable panel -> just use the area of the slot directly
hoverArea = subSlot.Slot.Rect;
hoverArea.Location += subSlot.Slot.DrawOffset.ToPoint();
hoverArea = Rectangle.Union(hoverArea, subSlot.Slot.EquipButtonRect);
return Rectangle.Empty;
}
else
Rectangle hoverArea;
bool isMovable = subSlot.Inventory.Movable() && !subSlot.ParentInventory.IsInventoryHoverAvailable(Character.Controlled, subSlot.Item?.GetComponent<ItemContainer>());
bool unEquipped = Character.Controlled.Inventory == subSlot.ParentInventory && !Character.Controlled.HasEquippedItem(subSlot.Item);
bool isDefaultLayout = subSlot.ParentInventory is not CharacterInventory characterInventory || characterInventory.CurrentLayout == CharacterInventory.Layout.Default;
bool subEditorCharacterInventoryHidden = Screen.Selected == GameMain.SubEditorScreen && !GameMain.SubEditorScreen.DrawCharacterInventory;
if (subEditorCharacterInventoryHidden || (isMovable && !unEquipped && isDefaultLayout))
{
hoverArea = subSlot.Inventory.BackgroundFrame;
hoverArea.Location += subSlot.Slot.DrawOffset.ToPoint();
@@ -1400,6 +1416,13 @@ namespace Barotrauma
hoverArea = Rectangle.Union(hoverArea, subSlot.Inventory.movableFrameRect);
}
}
else
{
//slot not visible as a separate, movable panel -> just use the area of the slot directly
hoverArea = subSlot.Slot.Rect;
hoverArea.Location += subSlot.Slot.DrawOffset.ToPoint();
hoverArea = Rectangle.Union(hoverArea, subSlot.Slot.EquipButtonRect);
}
if (subSlot.Inventory?.visualSlots != null)
{
@@ -1584,11 +1607,16 @@ namespace Barotrauma
if (DraggingItems.Any() && inventory != null && slotIndex > -1 && slotIndex < inventory.visualSlots.Length)
{
var itemInSlot = inventory.slots[slotIndex].FirstOrDefault();
if (inventory.CanBePutInSlot(DraggingItems.First(), slotIndex))
{
canBePut = true;
}
else if (inventory.slots[slotIndex].FirstOrDefault()?.OwnInventory?.CanBePut(DraggingItems.First()) ?? false)
else if
(itemInSlot?.OwnInventory != null &&
itemInSlot.OwnInventory.CanBePut(DraggingItems.First()) &&
itemInSlot.OwnInventory.Container.AllowDragAndDrop &&
itemInSlot.OwnInventory.Container.DrawInventory)
{
canBePut = true;
}
@@ -1922,9 +1950,25 @@ namespace Barotrauma
foreach (UInt16 id in receivedItemIDs[i])
{
if (Entity.FindEntityByID(id) is not Item item || slots[i].Contains(item)) { continue; }
if (Owner is Item thisItem && thisItem.Container == item)
{
//if this item is inside the item we're trying to contain inside it, we need to drop it (both items can't be inside each other!)
//can happen when a player swaps the items to be "the other way around", and we receive a message about the contained item
//before the message about the "parent item" being placed in some other inventory (like the player's inventory)
thisItem.Drop(null);
}
if (!TryPutItem(item, i, false, false, null, false))
{
ForceToSlot(item, i);
try
{
ForceToSlot(item, i);
}
catch (InvalidOperationException e)
{
DebugConsole.AddSafeError(e.Message + "\n" + e.StackTrace.CleanupStackTrace());
}
}
for (int j = 0; j < capacity; j++)
{

View File

@@ -132,9 +132,9 @@ namespace Barotrauma
return GetDrawDepth(SpriteDepth + DrawDepthOffset, Sprite);
}
public Color GetSpriteColor(bool withHighlight = false)
public Color GetSpriteColor(Color? defaultColor = null, bool withHighlight = false)
{
Color color = spriteColor;
Color color = defaultColor ?? spriteColor;
if (Prefab.UseContainedSpriteColor && ownInventory != null)
{
foreach (Item item in ContainedItems)
@@ -286,7 +286,8 @@ namespace Barotrauma
{
int padding = 100;
Vector2 min = new Vector2(-rect.Width / 2 - padding, -rect.Height / 2 - padding);
RectangleF boundingBox = GetTransformedQuad().BoundingAxisAlignedRectangle;
Vector2 min = new Vector2(-boundingBox.Width / 2 - padding, -boundingBox.Height / 2 - padding);
Vector2 max = -min;
foreach (IDrawableComponent drawable in drawableComponents)
@@ -333,9 +334,7 @@ namespace Barotrauma
else if (!ShowItems) { return; }
}
Color color =
overrideColor ??
(IsIncludedInSelection && editing ? GUIStyle.Blue : GetSpriteColor(withHighlight: true));
Color color = GetSpriteColor(spriteColor);
bool isWiringMode = editing && SubEditorScreen.TransparentWiringMode && SubEditorScreen.IsWiringMode() && !isWire && parentInventory == null;
bool renderTransparent = isWiringMode && GetComponent<ConnectionPanel>() == null;
@@ -388,9 +387,9 @@ namespace Barotrauma
{
if (Prefab.ResizeHorizontal || Prefab.ResizeVertical)
{
Vector2 size = new Vector2(rect.Width, rect.Height);
if (color.A > 0)
{
Vector2 size = new Vector2(rect.Width, rect.Height);
activeSprite.DrawTiled(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2)) + drawOffset,
size, color: color,
textureScale: Vector2.One * Scale,
@@ -403,18 +402,7 @@ namespace Barotrauma
textureScale: Vector2.One * Scale,
depth: d);
}
foreach (var decorativeSprite in Prefab.DecorativeSprites)
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier, flippedX && Prefab.CanSpriteFlipX ? RotationRad : -RotationRad) * Scale;
if (flippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; }
if (flippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; }
decorativeSprite.Sprite.DrawTiled(spriteBatch,
new Vector2(DrawPosition.X + offset.X - rect.Width / 2, -(DrawPosition.Y + offset.Y + rect.Height / 2)),
size, color: color,
textureScale: Vector2.One * Scale,
depth: Math.Min(depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth), 0.999f));
}
DrawDecorativeSprites(spriteBatch, DrawPosition, flippedX && Prefab.CanSpriteFlipX, flippedY && Prefab.CanSpriteFlipY, rotation: 0, depth);
}
}
else
@@ -434,19 +422,8 @@ namespace Barotrauma
Prefab.InfectedSprite?.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, color, Prefab.InfectedSprite.Origin, RotationRad, Scale, activeSprite.effects, depth - 0.001f);
Prefab.DamagedInfectedSprite?.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, Infector.HealthColor, Prefab.DamagedInfectedSprite.Origin, RotationRad, Scale, activeSprite.effects, depth - 0.002f);
}
foreach (var decorativeSprite in Prefab.DecorativeSprites)
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
float rot = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState, spriteAnimState[decorativeSprite].RandomRotationFactor);
bool flipX = flippedX && Prefab.CanSpriteFlipX;
bool flipY = flippedY && Prefab.CanSpriteFlipY;
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier, flipX ^ flipY ? RotationRad : -RotationRad) * Scale;
if (flipX) { offset.X = -offset.X; }
if (flipY) { offset.Y = -offset.Y; }
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + offset.X, -(DrawPosition.Y + offset.Y)), color,
RotationRad + rot, decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, activeSprite.effects,
depth: Math.Min(depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth), 0.999f));
}
DrawDecorativeSprites(spriteBatch, DrawPosition, flippedX && Prefab.CanSpriteFlipX, flippedY && Prefab.CanSpriteFlipY, -RotationRad, depth);
}
}
else if (body.Enabled)
@@ -490,21 +467,7 @@ namespace Barotrauma
float d = Math.Min(depth + (fadeInBrokenSprite.Sprite.Depth - activeSprite.Depth - 0.000001f), 0.999f);
body.Draw(spriteBatch, fadeInBrokenSprite.Sprite, color * fadeInBrokenSpriteAlpha, d, Scale);
}
foreach (var decorativeSprite in Prefab.DecorativeSprites)
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState, spriteAnimState[decorativeSprite].RandomRotationFactor);
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier, -RotationRad) * Scale;
if (flippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; }
if (flippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; }
var ca = MathF.Cos(-body.DrawRotation);
var sa = MathF.Sin(-body.DrawRotation);
Vector2 transformedOffset = new Vector2(ca * offset.X + sa * offset.Y, -sa * offset.X + ca * offset.Y);
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(body.DrawPosition.X + transformedOffset.X, -(body.DrawPosition.Y + transformedOffset.Y)), color,
-body.DrawRotation + rotation, decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, activeSprite.effects,
depth: depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth));
}
DrawDecorativeSprites(spriteBatch, body.DrawPosition, flipX: body.Dir < 0, flipY: false, rotation: body.Rotation, depth: depth);
}
foreach (var upgrade in Upgrades)
@@ -522,7 +485,6 @@ namespace Barotrauma
rotation, decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, activeSprite.effects,
depth: depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth));
}
}
activeSprite.effects = oldEffects;
@@ -567,8 +529,14 @@ namespace Barotrauma
Vector2 drawPos = new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2));
Vector2 drawSize = new Vector2(MathF.Ceiling(rect.Width + Math.Abs(drawPos.X - (int)drawPos.X)), MathF.Ceiling(rect.Height + Math.Abs(drawPos.Y - (int)drawPos.Y)));
drawPos = new Vector2(MathF.Floor(drawPos.X), MathF.Floor(drawPos.Y));
GUI.DrawRectangle(spriteBatch, drawPos, drawSize,
Color.White, false, 0, thickness: Math.Max(1, (int)(2 / Screen.Selected.Cam.Zoom)));
GUI.DrawRectangle(sb: spriteBatch,
center: drawPos + drawSize * 0.5f,
width: drawSize.X,
height: drawSize.Y,
rotation: RotationRad,
clr: Color.White,
depth: 0,
thickness: 2f / Screen.Selected.Cam.Zoom);
foreach (Rectangle t in Prefab.Triggers)
{
@@ -618,6 +586,62 @@ namespace Barotrauma
}
return origin;
}
Color GetSpriteColor(Color defaultColor)
{
return
overrideColor ??
(IsIncludedInSelection && editing ? GUIStyle.Blue : this.GetSpriteColor(defaultColor: defaultColor, withHighlight: true));
}
}
public void DrawDecorativeSprites(SpriteBatch spriteBatch, Vector2 drawPos, bool flipX, bool flipY, float rotation, float depth)
{
foreach (var decorativeSprite in Prefab.DecorativeSprites)
{
Color decorativeSpriteColor = GetSpriteColor(decorativeSprite.Color).Multiply(GetSpriteColor(spriteColor));
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier,
flipX ^ flipY ? -rotation : rotation) * Scale;
if (ResizeHorizontal || ResizeVertical)
{
decorativeSprite.Sprite.DrawTiled(spriteBatch,
new Vector2(DrawPosition.X + offset.X - rect.Width / 2, -(DrawPosition.Y + offset.Y + rect.Height / 2)),
new Vector2(rect.Width, rect.Height), color: decorativeSpriteColor,
textureScale: Vector2.One * Scale,
depth: Math.Min(depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth), 0.999f));
}
else
{
float spriteRotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState, spriteAnimState[decorativeSprite].RandomRotationFactor);
Vector2 origin = decorativeSprite.Sprite.Origin;
SpriteEffects spriteEffects = SpriteEffects.None;
if (flipX && Prefab.CanSpriteFlipX)
{
offset.X = -offset.X;
origin.X = -origin.X + decorativeSprite.Sprite.size.X;
spriteEffects = SpriteEffects.FlipHorizontally;
}
if (flipY && Prefab.CanSpriteFlipY)
{
offset.Y = -offset.Y;
origin.Y = -origin.Y + decorativeSprite.Sprite.size.Y;
spriteEffects |= SpriteEffects.FlipVertically;
}
if (body != null)
{
var ca = MathF.Cos(-body.DrawRotation);
var sa = MathF.Sin(-body.DrawRotation);
offset = new Vector2(ca * offset.X + sa * offset.Y, -sa * offset.X + ca * offset.Y);
}
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(drawPos.X + offset.X, -(drawPos.Y + offset.Y)), decorativeSpriteColor, origin,
-rotation + spriteRotation, decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, spriteEffects,
depth: depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth));
}
}
}
partial void OnCollisionProjSpecific(float impact)
@@ -795,6 +819,19 @@ namespace Barotrauma
}
}
public override bool IsMouseOn(Vector2 position)
{
Vector2 rectSize = rect.Size.ToVector2();
Vector2 bodyPos = WorldPosition;
Vector2 transformedMousePos = MathUtils.RotatePointAroundTarget(position, bodyPos, RotationRad);
return
Math.Abs(transformedMousePos.X - bodyPos.X) < rectSize.X / 2.0f &&
Math.Abs(transformedMousePos.Y - bodyPos.Y) < rectSize.Y / 2.0f;
}
public GUIComponent CreateEditingHUD(bool inGame = false)
{
activeEditors.Clear();
@@ -852,7 +889,12 @@ namespace Barotrauma
CanBeFocused = true
};
new GUIButton(new RectTransform(new Vector2(0.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityX"), style: "GUIButtonSmall")
GUINumberInput rotationField =
itemEditor.Fields.TryGetValue("Rotation".ToIdentifier(), out var rotationFieldComponents)
? rotationFieldComponents.OfType<GUINumberInput>().FirstOrDefault()
: null;
var mirrorX = new GUIButton(new RectTransform(new Vector2(0.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityX"), style: "GUIButtonSmall")
{
ToolTip = TextManager.Get("MirrorEntityXToolTip"),
Enabled = Prefab.CanFlipX,
@@ -863,10 +905,13 @@ namespace Barotrauma
me.FlipX(relativeToSub: false);
}
if (!SelectedList.Contains(this)) { FlipX(relativeToSub: false); }
ColorFlipButton(button, FlippedX);
if (rotationField != null) { rotationField.FloatValue = Rotation; }
return true;
}
};
new GUIButton(new RectTransform(new Vector2(0.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityY"), style: "GUIButtonSmall")
ColorFlipButton(mirrorX, FlippedX);
var mirrorY = new GUIButton(new RectTransform(new Vector2(0.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityY"), style: "GUIButtonSmall")
{
ToolTip = TextManager.Get("MirrorEntityYToolTip"),
Enabled = Prefab.CanFlipY,
@@ -877,9 +922,12 @@ namespace Barotrauma
me.FlipY(relativeToSub: false);
}
if (!SelectedList.Contains(this)) { FlipY(relativeToSub: false); }
ColorFlipButton(button, FlippedY);
if (rotationField != null) { rotationField.FloatValue = Rotation; }
return true;
}
};
ColorFlipButton(mirrorY, FlippedY);
if (Sprite != null)
{
var reloadTextureButton = new GUIButton(new RectTransform(new Vector2(0.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("ReloadSprite"), style: "GUIButtonSmall");
@@ -1540,6 +1588,23 @@ namespace Barotrauma
RemoveFromDroppedStack(allowClientExecute: true);
}
break;
case EventType.SetHighlight:
bool isTargetedForThisClient = msg.ReadBoolean();
if (isTargetedForThisClient)
{
bool highlight = msg.ReadBoolean();
ExternalHighlight = highlight;
if (highlight)
{
Color highlightColor = msg.ReadColorR8G8B8A8();
HighlightColor = highlightColor;
}
else
{
HighlightColor = null;
}
}
break;
default:
throw new Exception($"Malformed incoming item event: unsupported event type {eventType}");
}
@@ -1940,5 +2005,13 @@ namespace Barotrauma
Inventory.DraggingSlot = null;
}
}
public void OnPlayerSkillsChanged()
{
foreach (ItemComponent ic in components)
{
ic.OnPlayerSkillsChanged();
}
}
}
}

View File

@@ -321,10 +321,8 @@ namespace Barotrauma
}
else
{
if (ResizeHorizontal)
placeSize.X = Math.Max(position.X - placePosition.X, Size.X);
if (ResizeVertical)
placeSize.Y = Math.Max(placePosition.Y - position.Y, Size.Y);
if (ResizeHorizontal) { placeSize.X = Math.Max(position.X - placePosition.X, Size.X); }
if (ResizeVertical) { placeSize.Y = Math.Max(placePosition.Y - position.Y, Size.Y); }
if (PlayerInput.PrimaryMouseButtonReleased())
{
@@ -369,15 +367,31 @@ namespace Barotrauma
}
}
public override void DrawPlacing(SpriteBatch spriteBatch, Rectangle placeRect, float scale = 1.0f, SpriteEffects spriteEffects = SpriteEffects.None)
public override void DrawPlacing(SpriteBatch spriteBatch, Rectangle placeRect, float scale = 1.0f, float rotation = 0.0f, SpriteEffects spriteEffects = SpriteEffects.None)
{
if (!ResizeHorizontal && !ResizeVertical)
{
Sprite.Draw(spriteBatch, new Vector2(placeRect.Center.X, -(placeRect.Y - placeRect.Height / 2)), SpriteColor * 0.8f, scale: scale);
sprite.Draw(
spriteBatch: spriteBatch,
pos: new Vector2(placeRect.Center.X,
-(placeRect.Y - placeRect.Height / 2)),
color: SpriteColor * 0.8f,
scale: scale,
rotate: rotation,
spriteEffect: spriteEffects ^ sprite.effects);
}
else
{
Sprite.DrawTiled(spriteBatch, new Vector2(placeRect.X, -placeRect.Y), placeRect.Size.ToVector2(), SpriteColor * 0.8f);
Vector2 position = placeRect.Location.ToVector2();
Vector2 placeSize = placeRect.Size.ToVector2();
sprite?.DrawTiled(
spriteBatch: spriteBatch,
position: new Vector2(position.X, -position.Y),
targetSize: placeSize,
rotation: rotation,
textureScale: Vector2.One * scale,
color: SpriteColor * 0.8f,
spriteEffects: spriteEffects ^ sprite.effects);
}
}

View File

@@ -277,12 +277,21 @@ namespace Barotrauma
Rectangle drawRect =
Submarine == null ? rect : new Rectangle((int)(Submarine.DrawPosition.X + rect.X), (int)(Submarine.DrawPosition.Y + rect.Y), rect.Width, rect.Height);
if ((IsSelected || IsHighlighted) && editing)
if (editing)
{
if (IsSelected || IsHighlighted)
{
GUI.DrawRectangle(spriteBatch,
new Vector2(drawRect.X, -drawRect.Y),
new Vector2(rect.Width, rect.Height),
(IsHighlighted ? Color.LightBlue * 0.8f : GUIStyle.Red * 0.5f) * alpha, false, 0, (int)Math.Max(5.0f / Screen.Selected.Cam.Zoom, 1.0f));
}
float waterHeight = WaterVolume / rect.Width;
GUI.DrawRectangle(spriteBatch,
new Vector2(drawRect.X, -drawRect.Y),
new Vector2(rect.Width, rect.Height),
(IsHighlighted ? Color.LightBlue * 0.8f : GUIStyle.Red * 0.5f) * alpha, false, 0, (int)Math.Max(5.0f / Screen.Selected.Cam.Zoom, 1.0f));
new Vector2(drawRect.X, -drawRect.Y + drawRect.Height - waterHeight),
new Vector2(drawRect.Width, waterHeight),
Color.Blue * 0.25f, isFilled: true);
}
GUI.DrawRectangle(spriteBatch,
@@ -746,7 +755,7 @@ namespace Barotrauma
var newFire = i < FireSources.Count ?
FireSources[i] :
new FireSource(Submarine == null ? pos : pos + Submarine.Position, null, true);
new FireSource(Submarine == null ? pos : pos + Submarine.Position, sourceCharacter: null, isNetworkMessage: true);
newFire.Position = pos;
newFire.Size = new Vector2(size, newFire.Size.Y);

View File

@@ -86,7 +86,7 @@ namespace Barotrauma
MathUtils.RoundTowardsClosest(center.Y, Submarine.GridSize.Y) - center.Y - Submarine.GridSize.Y / 2);
MapEntity.SelectedList.Clear();
assemblyEntities.ForEach(e => MapEntity.AddSelection(e));
entities.ForEach(e => MapEntity.AddSelection(e));
foreach (MapEntity mapEntity in assemblyEntities)
{

View File

@@ -43,7 +43,7 @@ namespace Barotrauma
{
mainElement = mainElement.FirstElement();
prefabs.Clear();
DebugConsole.NewMessage($"Overriding all background creatures with '{configPath}'", Color.Yellow);
DebugConsole.NewMessage($"Overriding all background creatures with '{configPath}'", Color.MediumPurple);
}
else if (prefabs.Any())
{

View File

@@ -1,10 +1,9 @@
using Barotrauma.Networking;
using FarseerPhysics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Linq;
using System.Collections.Generic;
using FarseerPhysics;
namespace Barotrauma
{
@@ -53,7 +52,7 @@ namespace Barotrauma
foreach (InterestingPosition pos in PositionsOfInterest)
{
Color color = Color.Yellow;
if (pos.PositionType == PositionType.Cave)
if (pos.PositionType == PositionType.Cave || pos.PositionType == PositionType.AbyssCave)
{
color = Color.DarkOrange;
}
@@ -61,6 +60,10 @@ namespace Barotrauma
{
color = Color.LightGray;
}
if (!pos.IsValid)
{
color = Color.Red;
}
GUI.DrawRectangle(spriteBatch, new Vector2(pos.Position.X - 15.0f, -pos.Position.Y - 15.0f), new Vector2(30.0f, 30.0f), color, true);
}

View File

@@ -162,6 +162,8 @@ namespace Barotrauma
CanBeVisible =
Sprite != null ||
Prefab.DeformableSprite != null ||
ParticleEmitters is { Length: > 0 } ||
(GameMain.DebugDraw && Triggers is { Count: > 0 }) ||
Prefab.OverrideProperties.Any(p => p != null && (p.Sprites.Any() || p.DeformableSprite != null));
}

View File

@@ -23,7 +23,14 @@ namespace Barotrauma
private Rectangle currentGridIndices;
public bool ForceRefreshVisibleObjects;
partial void RemoveProjSpecific()
{
visibleObjectsBack.Clear();
visibleObjectsMid.Clear();
visibleObjectsFront.Clear();
}
partial void UpdateProjSpecific(float deltaTime)
{
foreach (LevelObject obj in visibleObjectsBack)

View File

@@ -133,6 +133,8 @@ namespace Barotrauma.Lights
public Rectangle BoundingBox { get; private set; }
public bool IsInvalid { get; private set; }
public ConvexHull(Rectangle rect, bool isHorizontal, MapEntity parent)
{
shadowEffect ??= new BasicEffect(GameMain.Instance.GraphicsDevice)
@@ -481,15 +483,34 @@ namespace Barotrauma.Lights
for (int i = 0; i < 4; i++)
{
vertices[i].WorldPos = vertices[i].Pos;
ValidateVertex(vertices[i].WorldPos, "vertices[i].Pos");
segments[i].Start.WorldPos = segments[i].Start.Pos;
ValidateVertex(segments[i].Start.WorldPos, "segments[i].Start.Pos");
segments[i].End.WorldPos = segments[i].End.Pos;
ValidateVertex(segments[i].End.WorldPos, "segments[i].End.Pos");
}
if (ParentEntity == null || ParentEntity.Submarine == null) { return; }
for (int i = 0; i < 4; i++)
{
vertices[i].WorldPos += ParentEntity.Submarine.DrawPosition;
ValidateVertex(vertices[i].WorldPos, "vertices[i].WorldPos");
segments[i].Start.WorldPos += ParentEntity.Submarine.DrawPosition;
ValidateVertex(segments[i].Start.WorldPos, "segments[i].Start.WorldPos");
segments[i].End.WorldPos += ParentEntity.Submarine.DrawPosition;
ValidateVertex(segments[i].End.WorldPos, "segments[i].End.WorldPos");
}
void ValidateVertex(Vector2 vertex, string debugName)
{
if (!MathUtils.IsValid(vertex))
{
IsInvalid = true;
string errorMsg = $"Invalid vertex on convex hull ({debugName}: {vertex}, parent entity: {ParentEntity?.ToString() ?? "null"}).";
#if DEBUG
DebugConsole.ThrowError(errorMsg);
#endif
GameAnalyticsManager.AddErrorEventOnce("ConvexHull.RefreshWorldPositions:InvalidVertex", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
}
}
}

View File

@@ -234,6 +234,15 @@ namespace Barotrauma.Lights
}
}
public void DebugDrawVertices(SpriteBatch spriteBatch)
{
foreach (LightSource light in lights)
{
if (!light.Enabled) { continue; }
light.DebugDrawVertices(spriteBatch);
}
}
public void RenderLightMap(GraphicsDevice graphics, SpriteBatch spriteBatch, Camera cam, RenderTarget2D backgroundObstructor = null)
{
if (!LightingEnabled) { return; }
@@ -269,6 +278,9 @@ namespace Barotrauma.Lights
light.Position = pos;
}
//above the top boundary of the level (in an inactive respawn shuttle?)
if (Level.Loaded != null && light.WorldPosition.Y > Level.Loaded.Size.Y) { continue; }
float range = light.LightSourceParams.TextureRange;
if (light.LightSprite != null)
{
@@ -801,6 +813,8 @@ namespace Barotrauma.Lights
public void ClearLights()
{
activeLights.Clear();
activeLightsWithLightVolume.Clear();
lights.Clear();
}
}

View File

@@ -37,9 +37,9 @@ namespace Barotrauma.Lights
TextureRange = range;
if (OverrideLightTexture != null)
{
TextureRange += Math.Max(
Math.Abs(OverrideLightTexture.RelativeOrigin.X - 0.5f) * OverrideLightTexture.size.X,
Math.Abs(OverrideLightTexture.RelativeOrigin.Y - 0.5f) * OverrideLightTexture.size.Y);
TextureRange *= 1.0f + Math.Max(
Math.Abs(OverrideLightTexture.RelativeOrigin.X - 0.5f),
Math.Abs(OverrideLightTexture.RelativeOrigin.Y - 0.5f));
}
}
}
@@ -238,7 +238,11 @@ namespace Barotrauma.Lights
private bool needsRecalculationWhenUpToDate;
public bool NeedsRecalculation
{
get { return needsRecalculation; }
get
{
if (ParentBody?.UserData is Item it && it.Prefab.Identifier == "flashlight") { return true; }
return needsRecalculation;
}
set
{
if (!needsRecalculation && value)
@@ -708,6 +712,7 @@ namespace Barotrauma.Lights
{
foreach (ConvexHull hull in chList.List)
{
if (hull.IsInvalid) { continue; }
if (!chList.IsHidden.Contains(hull))
{
//find convexhull segments that are close enough and facing towards the light source
@@ -735,6 +740,7 @@ namespace Barotrauma.Lights
GameMain.LightManager.AddRayCastTask(this, drawPos, rotation);
}
const float MinPointDistance = 6;
public void RayCastTask(Vector2 drawPos, float rotation)
{
@@ -877,12 +883,11 @@ namespace Barotrauma.Lights
}
}
const float MinPointDistance = 6;
//remove points that are very close to each other
for (int i = 0; i < points.Count; i++)
//+= 2 because the points are added in pairs above, i.e. 0 and 1 belong to the same segment
for (int i = 0; i < points.Count; i += 2)
{
for (int j = Math.Min(i + 4, points.Count - 1); j > i; j--)
for (int j = Math.Min(i + 2, points.Count - 1); j > i; j--)
{
if (Math.Abs(points[i].WorldPos.X - points[j].WorldPos.X) < MinPointDistance &&
Math.Abs(points[i].WorldPos.Y - points[j].WorldPos.Y) < MinPointDistance)
@@ -892,14 +897,14 @@ namespace Barotrauma.Lights
}
}
var compareCCW = new CompareSegmentPointCW(drawPos);
try
{
points.Sort(compareCCW);
var compareCW = new CompareSegmentPointCW(drawPos);
points.Sort(compareCW);
}
catch (Exception e)
{
StringBuilder sb = new StringBuilder("Constructing light volumes failed! Light pos: " + drawPos + ", Hull verts:\n");
StringBuilder sb = new StringBuilder($"Constructing light volumes failed ({nameof(CompareSegmentPointCW)})! Light pos: {drawPos}, Hull verts:\n");
foreach (SegmentPoint sp in points)
{
sb.AppendLine(sp.Pos.ToString());
@@ -914,7 +919,11 @@ namespace Barotrauma.Lights
verts.Clear();
foreach (SegmentPoint p in points)
{
Vector2 dir = Vector2.Normalize(p.WorldPos - drawPos);
Vector2 diff = p.WorldPos - drawPos;
float dist = diff.Length();
//light source exactly at the segment point, don't cast a shadow (normalizing the vector would lead to NaN)
if (dist <= 0.0001f) { continue; }
Vector2 dir = diff / dist;
Vector2 dirNormal = new Vector2(-dir.Y, dir.X) * MinPointDistance;
//do two slightly offset raycasts to hit the segment itself and whatever's behind it
@@ -940,9 +949,36 @@ namespace Barotrauma.Lights
{
//the raycasts landed on different segments
//we definitely want to generate new geometry here
verts.Add(isPoint1 ? p.WorldPos : intersection1.pos);
verts.Add(isPoint2 ? p.WorldPos : intersection2.pos);
markAsVisible = true;
if (isPoint1)
{
TryAddPoints(intersection2.pos, p.WorldPos, drawPos, verts);
markAsVisible = true;
}
else if (isPoint2)
{
TryAddPoints(intersection1.pos, p.WorldPos, drawPos, verts);
markAsVisible = true;
}
else
{
//didn't hit either point, completely obstructed
verts.Add(intersection1.pos);
verts.Add(intersection2.pos);
}
static void TryAddPoints(Vector2 intersection, Vector2 point, Vector2 refPos, List<Vector2> verts)
{
//* 0.8f because we don't care about obstacles that are very close (intersecting walls),
//only about obstacles that are clearly between the point and the refPos
bool intersectionCloserThanPoint = Vector2.DistanceSquared(intersection, refPos) < Vector2.DistanceSquared(point, refPos) * 0.8f;
//if the raycast hit a segment that's closer than the point we're aiming towards,
//it means we didn't hit a segment behind the point, but something that's obstructing it
//= we don't want to add vertex at that obstructed point, it could make the light go through obstacles
if (!intersectionCloserThanPoint)
{
verts.Add(point);
}
verts.Add(intersection);
}
}
if (markAsVisible)
{
@@ -959,15 +995,32 @@ namespace Barotrauma.Lights
//remove points that are very close to each other
for (int i = 0; i < verts.Count - 1; i++)
{
for (int j = Math.Min(i + 4, verts.Count - 1); j > i; j--)
for (int j = verts.Count - 1; j > i; j--)
{
if (Math.Abs(verts[i].X - verts[j].X) < 6 &&
Math.Abs(verts[i].Y - verts[j].Y) < 6)
if (Math.Abs(verts[i].X - verts[j].X) < MinPointDistance &&
Math.Abs(verts[i].Y - verts[j].Y) < MinPointDistance)
{
verts.RemoveAt(j);
}
}
}
try
{
var compareCW = new CompareCW(drawPos);
verts.Sort(compareCW);
}
catch (Exception e)
{
StringBuilder sb = new StringBuilder($"Constructing light volumes failed ({nameof(CompareSegmentPointCW)})! Light pos: {drawPos}, verts:\n");
foreach (Vector2 v in verts)
{
sb.AppendLine(v.ToString());
}
DebugConsole.ThrowError(sb.ToString(), e);
}
calculatedDrawPos = drawPos;
state = LightVertexState.PendingVertexRecalculation;
}
@@ -1114,7 +1167,7 @@ namespace Barotrauma.Lights
//add the normals together and use some magic numbers to create
//a somewhat useful/good-looking blur
float blurDistance = 40.0f;
float blurDistance = 25.0f;
Vector2 nDiff = nDiff1 * blurDistance;
if (MathUtils.GetLineIntersection(vertex + (nDiff1 * blurDistance), nextVertex + (nDiff1 * blurDistance), vertex + (nDiff2 * blurDistance), prevVertex + (nDiff2 * blurDistance), true, out Vector2 intersection))
{
@@ -1230,7 +1283,8 @@ namespace Barotrauma.Lights
/// <param name="spriteBatch"></param>
public void DrawSprite(SpriteBatch spriteBatch, Camera cam)
{
if (GameMain.DebugDraw)
//uncomment if you want to visualize the bounds of the light volume
/*if (GameMain.DebugDraw)
{
Vector2 drawPos = position;
if (ParentSub != null)
@@ -1269,7 +1323,7 @@ namespace Barotrauma.Lights
{
GUI.DrawLine(spriteBatch, boundaryCorners[i].Pos, boundaryCorners[(i + 1) % 4].Pos, Color.White, 0, 3);
}
}
}*/
if (DeformableLightSprite != null)
{
@@ -1318,7 +1372,7 @@ namespace Barotrauma.Lights
if (LightTextureTargetSize != Vector2.Zero)
{
LightSprite.DrawTiled(spriteBatch, drawPos, LightTextureTargetSize, color, startOffset: LightTextureOffset, textureScale: LightTextureScale);
LightSprite.DrawTiled(spriteBatch, drawPos, LightTextureTargetSize, color: color, startOffset: LightTextureOffset, textureScale: LightTextureScale);
}
else
{
@@ -1367,6 +1421,38 @@ namespace Barotrauma.Lights
}
}
public void DebugDrawVertices(SpriteBatch spriteBatch)
{
if (Range < 1.0f || Color.A < 1 || CurrentBrightness <= 0.0f) { return; }
//commented out because this is mostly just useful in very specific situations, otherwise it just makes debugdraw very messy
//(you may also need to add a condition here that only draws this for the specific light you're interested in)
if (GameMain.DebugDraw && vertices != null)
{
if (ParentBody?.UserData is Item it && it.Prefab.Identifier == "flashlight")
for (int i = 1; i < vertices.Length - 1; i += 2)
{
Vector2 vert1 = new Vector2(vertices[i].Position.X, vertices[i].Position.Y);
int nextIndex = (i + 2) % vertices.Length;
//the first vertex is the one at the position of the light source, skip that one
//(we just want to draw lines between the vertices at the circumference of the light volume)
if (nextIndex == 0) { nextIndex++; }
Vector2 vert2 = new Vector2(vertices[nextIndex].Position.X, vertices[nextIndex].Position.Y);
if (ParentSub != null)
{
vert1 += ParentSub.DrawPosition;
vert2 += ParentSub.DrawPosition;
}
vert1.Y = -vert1.Y;
vert2.Y = -vert2.Y;
var randomColor = ToolBox.GradientLerp(i / (float)vertices.Length, Color.Magenta, Color.Blue, Color.Yellow, Color.Green, Color.Cyan, Color.Red, Color.Purple, Color.Yellow);
GUI.DrawLine(spriteBatch, vert1, vert2, randomColor * 0.8f, width: 2);
}
}
}
public void DrawLightVolume(SpriteBatch spriteBatch, BasicEffect lightEffect, Matrix transform, bool allowRecalculation, ref int recalculationCount)
{
if (Range < 1.0f || Color.A < 1 || CurrentBrightness <= 0.0f) { return; }

View File

@@ -291,14 +291,14 @@ namespace Barotrauma
private readonly List<MapNotification> mapNotifications = new List<MapNotification>();
partial void ChangeLocationTypeProjSpecific(Location location, string prevName, LocationTypeChange change)
partial void ChangeLocationTypeProjSpecific(Location location, LocalizedString prevName, LocationTypeChange change)
{
var messages = change.GetMessages(location.Faction);
if (!messages.Any()) { return; }
string msg = messages.GetRandom(Rand.RandSync.Unsynced)
.Replace("[previousname]", $"‖color:gui.yellow‖{prevName}‖end‖")
.Replace("[name]", $"‖color:gui.yellow‖{location.Name}‖end‖");
.Replace("[name]", $"‖color:gui.yellow‖{location.DisplayName}‖end‖");
location.LastTypeChangeMessage = msg;
mapNotifications.Add(new MapNotification(msg, GUIStyle.SubHeadingFont, mapNotifications, location));
@@ -377,7 +377,7 @@ namespace Barotrauma
bool showReputation = hudVisibility > 0.0f && location.Type.HasOutpost && location.Reputation != null;
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), location.Name, font: GUIStyle.LargeFont) { Padding = Vector4.Zero };
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), location.DisplayName, font: GUIStyle.LargeFont) { Padding = Vector4.Zero };
if (!location.Type.Name.IsNullOrEmpty())
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), location.Type.Name, font: GUIStyle.SubHeadingFont) { Padding = Vector4.Zero };
@@ -1080,6 +1080,7 @@ namespace Barotrauma
}
float dist = Vector2.Distance(start, end);
var connectionSprite = connection.Passed ? generationParams.PassedConnectionSprite : generationParams.ConnectionSprite;
if (connectionSprite?.Texture == null) { continue; }
Color segmentColor = connectionColor;
int segmentWidth = width;
@@ -1092,9 +1093,6 @@ namespace Barotrauma
segmentWidth /= 2;
segmentColor = connection.Passed ? generationParams.ConnectionColor : generationParams.UnvisitedConnectionColor;
}
else
{
}
}
spriteBatch.Draw(connectionSprite.Texture,

View File

@@ -29,7 +29,7 @@ namespace Barotrauma
Vector2 spriteScale = new Vector2(zoom);
uiSprite.Sprite.DrawTiled(spriteBatch, topLeft, size, Params.RadiationAreaColor, Vector2.Zero, textureScale: spriteScale);
uiSprite.Sprite.DrawTiled(spriteBatch, topLeft, size, color: Params.RadiationAreaColor, startOffset: Vector2.Zero, textureScale: spriteScale);
Vector2 topRight = topLeft + Vector2.UnitX * size.X;

View File

@@ -8,6 +8,7 @@ using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Barotrauma.Lights;
using Microsoft.Xna.Framework;
namespace Barotrauma
{
@@ -65,9 +66,7 @@ namespace Barotrauma
disableSelect = value;
if (disableSelect)
{
startMovingPos = Vector2.Zero;
selectionSize = Vector2.Zero;
selectionPos = Vector2.Zero;
StopSelection();
}
}
}
@@ -163,21 +162,9 @@ namespace Barotrauma
{
if (SelectedAny)
{
if (SelectedList.Any(static t => t is Item it && it.GetComponent<CircuitBox>() is not null))
{
GUI.AskForConfirmation(SubEditorScreen.CircuitBoxDeletionWarningHeader, SubEditorScreen.CircuitBoxDeletionWarningBody, onConfirm: Delete);
}
else
{
Delete();
}
void Delete()
{
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(new List<MapEntity>(SelectedList), true));
SelectedList.ForEach(static e => { if (!e.Removed) { e.Remove(); } });
SelectedList.Clear();
}
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(new List<MapEntity>(SelectedList), true));
SelectedList.ForEachMod(static e => { if (!e.Removed) { e.Remove(); } });
SelectedList.Clear();
}
}
@@ -494,6 +481,13 @@ namespace Barotrauma
}
}
public static void StopSelection()
{
startMovingPos = Vector2.Zero;
selectionSize = Vector2.Zero;
selectionPos = Vector2.Zero;
}
public static Vector2 GetNudgeAmount(bool doHold = true)
{
Vector2 nudgeAmount = Vector2.Zero;
@@ -792,12 +786,16 @@ namespace Barotrauma
foreach (MapEntity e in SelectedList)
{
SpriteEffects spriteEffects = SpriteEffects.None;
float spriteRotation = 0.0f;
float rectangleRotation = 0.0f;
switch (e)
{
case Item item:
{
if (item.FlippedX && item.Prefab.CanSpriteFlipX) { spriteEffects ^= SpriteEffects.FlipHorizontally; }
if (item.flippedY && item.Prefab.CanSpriteFlipY) { spriteEffects ^= SpriteEffects.FlipVertically; }
if (item.FlippedY && item.Prefab.CanSpriteFlipY) { spriteEffects ^= SpriteEffects.FlipVertically; }
spriteRotation = MathHelper.ToRadians(item.Rotation);
rectangleRotation = spriteRotation;
var wire = item.GetComponent<Wire>();
if (wire != null && wire.Item.body != null && !wire.Item.body.Enabled)
{
@@ -809,7 +807,10 @@ namespace Barotrauma
case Structure structure:
{
if (structure.FlippedX && structure.Prefab.CanSpriteFlipX) { spriteEffects ^= SpriteEffects.FlipHorizontally; }
if (structure.flippedY && structure.Prefab.CanSpriteFlipY) { spriteEffects ^= SpriteEffects.FlipVertically; }
if (structure.FlippedY && structure.Prefab.CanSpriteFlipY) { spriteEffects ^= SpriteEffects.FlipVertically; }
spriteRotation = MathHelper.ToRadians(structure.Rotation);
rectangleRotation = spriteRotation;
if (structure.FlippedX != structure.FlippedY) { rectangleRotation = -rectangleRotation; }
break;
}
case WayPoint wayPoint:
@@ -831,11 +832,12 @@ namespace Barotrauma
}
}
e.Prefab?.DrawPlacing(spriteBatch,
new Rectangle(e.WorldRect.Location + new Point((int)moveAmount.X, (int)-moveAmount.Y), e.WorldRect.Size), e.Scale, spriteEffects);
new Rectangle(e.WorldRect.Location + new Point((int)moveAmount.X, (int)-moveAmount.Y), e.WorldRect.Size), e.Scale, spriteRotation, spriteEffects: spriteEffects);
GUI.DrawRectangle(spriteBatch,
new Vector2(e.WorldRect.X, -e.WorldRect.Y) + moveAmount,
new Vector2(e.rect.Width, e.rect.Height),
Color.White, false, 0, (int)Math.Max(3.0f / GameScreen.Selected.Cam.Zoom, 2.0f));
center: e.WorldRect.Center.ToVector2().FlipY() + moveAmount + new Vector2(0f, e.WorldRect.Height),
width: e.WorldRect.Width, height: e.WorldRect.Height,
rotation: rectangleRotation, clr: Color.White,
depth: 0f, thickness: Math.Max(3.0f / GameScreen.Selected.Cam.Zoom, 2.0f));
}
//stop dragging the "selection rectangle"
@@ -877,6 +879,23 @@ namespace Barotrauma
spriteBatch.DrawLine(corners[3] + offset, corners[0] - offset, color, thickness);
}
protected static void ColorFlipButton(GUIButton btn, bool flip)
{
var color = flip ? GUIStyle.Green : Color.White;
var hsv = ToolBox.RGBToHSV(color);
// Boost saturation and reduce value a bit because our default colors are too muted for this button's style
var hsvBase = hsv;
hsvBase.Y *= 4f;
hsvBase.Z *= 0.8f;
btn.Color = ToolBox.HSVToRGB(hsvBase.X, hsvBase.Y, hsvBase.Z);
btn.SelectedColor = ToolBox.HSVToRGB(hsvBase.X, hsvBase.Y, hsvBase.Z);
var hsvHover = hsv;
hsvHover.Z *= 1.2f;
btn.HoverColor = ToolBox.HSVToRGB(hsvHover.X, hsvHover.Y, hsvHover.Z);
}
public static List<MapEntity> FilteredSelectedList { get; private set; } = new List<MapEntity>();
public static void UpdateEditor(Camera cam, float deltaTime)
@@ -1105,6 +1124,25 @@ namespace Barotrauma
public virtual void DrawEditing(SpriteBatch spriteBatch, Camera cam) { }
private float RotationRad
=> MathHelper.ToRadians(
this switch
{
Structure s => s.Rotation,
Item it => it.Rotation,
_ => 0.0f
});
private Vector2 GetEditingHandlePos(int x, int y, Camera cam)
{
Vector2 handleDiff = new Vector2(x * (rect.Width * 0.5f), y * (rect.Height * 0.5f));
var rotation = -RotationRad;
handleDiff = MathUtils.RotatePoint(handleDiff, rotation);
if (FlippedX) { handleDiff = handleDiff.FlipX(); }
if (FlippedY) { handleDiff = handleDiff.FlipY(); }
return cam.WorldToScreen(Position + handleDiff);
}
float ResizeHandleSize => 10 * GUI.Scale;
float ResizeHandleHighlightDistance => 8 * GUI.Scale;
@@ -1119,9 +1157,10 @@ namespace Barotrauma
{
for (int y = startY; y < 2; y += 2)
{
Vector2 handlePos = cam.WorldToScreen(Position + new Vector2(x * (rect.Width * 0.5f), y * (rect.Height * 0.5f)));
Vector2 handlePos = GetEditingHandlePos(x, y, cam);
bool highlighted = Vector2.DistanceSquared(PlayerInput.MousePosition, handlePos) < ResizeHandleHighlightDistance * ResizeHandleHighlightDistance;
if (highlighted && PlayerInput.PrimaryMouseButtonDown())
{
selectionPos = Vector2.Zero;
@@ -1138,44 +1177,83 @@ namespace Barotrauma
{
if (prevRect == null)
{
prevRect = new Rectangle(Rect.Location, Rect.Size);
prevRect = Rect;
}
Vector2 placePosition = new Vector2(rect.X, rect.Y);
Vector2 placeSize = new Vector2(rect.Width, rect.Height);
Vector2 placePosition = prevRect.Value.Location.ToVector2();
Vector2 placeSize = prevRect.Value.Size.ToVector2();
Vector2 mousePos = Submarine.MouseToWorldGrid(cam, Submarine.MainSub, round: true);
if (PlayerInput.IsShiftDown())
static Vector2 flipThenRotate(Vector2 point, Vector2 center, float angle, bool flipX, bool flipY)
{
mousePos = cam.ScreenToWorld(PlayerInput.MousePosition);
if (flipX) { point = (point - center).FlipX() + center; }
if (flipY) { point = (point - center).FlipY() + center; }
point = MathUtils.RotatePointAroundTarget(point, center, angle);
return point;
}
static Vector2 rotateThenFlip(Vector2 point, Vector2 center, float angle, bool flipX, bool flipY)
{
point = MathUtils.RotatePointAroundTarget(point, center, angle);
if (flipX) { point = (point - center).FlipX() + center; }
if (flipY) { point = (point - center).FlipY() + center; }
return point;
}
Vector2 mousePos = cam.ScreenToWorld(PlayerInput.MousePosition);
Vector2 prevPos = placePosition;
Vector2 prevOppositeCorner = prevPos + placeSize.FlipY();
Vector2 prevCenter = placePosition + placeSize.FlipY() * 0.5f;
mousePos = flipThenRotate(mousePos, prevCenter, RotationRad, FlippedX, FlippedY);
if (!PlayerInput.IsShiftDown())
{
mousePos = Submarine.VectorToWorldGrid(mousePos, Submarine.MainSub, round: true);
}
if (resizeDirX > 0)
{
mousePos.X = Math.Max(mousePos.X, rect.X + Submarine.GridSize.X);
mousePos.X = Math.Max(mousePos.X, prevRect.Value.X + Submarine.GridSize.X);
placeSize.X = mousePos.X - placePosition.X;
}
else if (resizeDirX < 0)
{
mousePos.X = Math.Min(mousePos.X, rect.Right - Submarine.GridSize.X);
mousePos.X = Math.Min(mousePos.X, prevRect.Value.Right - Submarine.GridSize.X);
placeSize.X = MathF.Round((placePosition.X + placeSize.X) - mousePos.X);
placePosition.X = MathF.Round(mousePos.X);
}
if (resizeDirY < 0)
{
mousePos.Y = Math.Min(mousePos.Y, rect.Y - Submarine.GridSize.Y);
mousePos.Y = Math.Min(mousePos.Y, prevRect.Value.Y - Submarine.GridSize.Y);
placeSize.Y = placePosition.Y - mousePos.Y;
}
else if (resizeDirY > 0)
{
mousePos.Y = Math.Max(mousePos.Y, rect.Y - rect.Height + Submarine.GridSize.X);
mousePos.Y = Math.Max(mousePos.Y, prevRect.Value.Y - prevRect.Value.Height + Submarine.GridSize.Y);
placeSize.Y = mousePos.Y - (rect.Y - rect.Height);
placeSize.Y = mousePos.Y - (prevRect.Value.Y - prevRect.Value.Height);
placePosition.Y = mousePos.Y;
}
Vector2 newPos = placePosition;
Vector2 newOppositeCorner = placePosition + placeSize.FlipY();
Vector2 transformedCornerDiff = rotateThenFlip(newPos-prevPos, Vector2.Zero, -RotationRad, FlippedX, FlippedY);
Vector2 transformedOppositeCornerDiff = rotateThenFlip(newOppositeCorner-prevOppositeCorner, Vector2.Zero, -RotationRad, FlippedX, FlippedY);
Vector2 newPosTransformed = rotateThenFlip(prevPos, prevCenter, -RotationRad, FlippedX, FlippedY)
+ transformedCornerDiff;
Vector2 newOppositeTransformed = rotateThenFlip(prevOppositeCorner, prevCenter, -RotationRad, FlippedX, FlippedY)
+ transformedOppositeCornerDiff;
Vector2 newTransformedCenter = (newPosTransformed + newOppositeTransformed) * 0.5f;
var newDiff = (newOppositeCorner - newPos) * 0.5f;
placePosition = newTransformedCenter - newDiff;
if ((int)placePosition.X != rect.X || (int)placePosition.Y != rect.Y || (int)placeSize.X != rect.Width || (int)placeSize.Y != rect.Height)
{
Rect = new Rectangle((int)placePosition.X, (int)placePosition.Y, (int)placeSize.X, (int)placeSize.Y);
@@ -1210,15 +1288,16 @@ namespace Barotrauma
IsHighlighted = true;
int startX = ResizeHorizontal ? -1 : 0;
int StartY = ResizeVertical ? -1 : 0;
int startY = ResizeVertical ? -1 : 0;
for (int x = startX; x < 2; x += 2)
{
for (int y = StartY; y < 2; y += 2)
for (int y = startY; y < 2; y += 2)
{
Vector2 handlePos = cam.WorldToScreen(Position + new Vector2(x * (rect.Width * 0.5f), y * (rect.Height * 0.5f)));
Vector2 handlePos = GetEditingHandlePos(x, y, cam);
bool highlighted = Vector2.DistanceSquared(PlayerInput.MousePosition, handlePos) < ResizeHandleHighlightDistance * ResizeHandleHighlightDistance;
var color = Color.White * (highlighted ? 1.0f : 0.6f);
if (highlighted && !PlayerInput.PrimaryMouseButtonHeld())
{
GUI.MouseCursor = CursorState.Hand;
@@ -1226,7 +1305,7 @@ namespace Barotrauma
GUI.DrawRectangle(spriteBatch,
handlePos - new Vector2(ResizeHandleSize / 2),
new Vector2(ResizeHandleSize),
Color.White * (highlighted ? 1.0f : 0.6f),
color,
true, 0,
(int)Math.Max(1.5f / GameScreen.Selected.Cam.Zoom, 1.0f));
}
@@ -1241,12 +1320,15 @@ namespace Barotrauma
HashSet<MapEntity> foundEntities = new HashSet<MapEntity>();
Rectangle selectionRect = Submarine.AbsRect(pos, size);
Quad2D selectionQuad = Quad2D.FromSubmarineRectangle(selectionRect);
foreach (MapEntity entity in MapEntityList)
{
if (!entity.SelectableInEditor) { continue; }
if (Submarine.RectsOverlap(selectionRect, entity.rect))
Quad2D entityQuad = entity.GetTransformedQuad();
if (selectionQuad.Intersects(entityQuad))
{
foundEntities.Add(entity);
entity.IsIncludedInSelection = true;

View File

@@ -84,7 +84,7 @@ namespace Barotrauma
}
}
public virtual void DrawPlacing(SpriteBatch spriteBatch, Rectangle drawRect, float scale = 1.0f, SpriteEffects spriteEffects = SpriteEffects.None)
public virtual void DrawPlacing(SpriteBatch spriteBatch, Rectangle drawRect, float scale = 1.0f, float rotation = 0.0f, SpriteEffects spriteEffects = SpriteEffects.None)
{
if (Submarine.MainSub != null)
{

View File

@@ -1,10 +1,9 @@
#nullable enable
using Barotrauma.Sounds;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Xml.Linq;
using Barotrauma.Sounds;
using Microsoft.Xna.Framework;
namespace Barotrauma
{
@@ -45,7 +44,8 @@ namespace Barotrauma
}
if (FrequencyMultiplierRange.Y > 4.0f)
{
DebugConsole.ThrowError($"Loaded frequency range exceeds max value: {FrequencyMultiplierRange} (original string was \"{freqMultAttr}\")");
DebugConsole.ThrowError($"Loaded frequency range exceeds max value: {FrequencyMultiplierRange} (original string was \"{freqMultAttr}\")",
contentPackage: element.ContentPackage);
}
IgnoreMuffling = element.GetAttributeBool("dontmuffle", false);
}
@@ -65,7 +65,8 @@ namespace Barotrauma
if (filename is null)
{
string errorMsg = "Error when loading round sound (" + element + ") - file path not set";
DebugConsole.ThrowError(errorMsg);
DebugConsole.ThrowError(errorMsg,
contentPackage: element.ContentPackage);
GameAnalyticsManager.AddErrorEventOnce("RoundSound.LoadRoundSound:FilePathEmpty" + element.ToString(), GameAnalyticsManager.ErrorSeverity.Error, errorMsg + "\n" + Environment.StackTrace.CleanupStackTrace());
return null;
}
@@ -86,7 +87,8 @@ namespace Barotrauma
catch (System.IO.FileNotFoundException e)
{
string errorMsg = "Failed to load sound file \"" + filename + "\" (file not found).";
DebugConsole.ThrowError(errorMsg, e);
DebugConsole.ThrowError(errorMsg, e,
contentPackage: element.ContentPackage);
if (!ContentPackageManager.ModsEnabled)
{
GameAnalyticsManager.AddErrorEventOnce("RoundSound.LoadRoundSound:FileNotFound" + filename, GameAnalyticsManager.ErrorSeverity.Error, errorMsg + "\n" + Environment.StackTrace.CleanupStackTrace());
@@ -96,7 +98,8 @@ namespace Barotrauma
catch (System.IO.InvalidDataException e)
{
string errorMsg = "Failed to load sound file \"" + filename + "\" (invalid data).";
DebugConsole.ThrowError(errorMsg, e);
DebugConsole.ThrowError(errorMsg, e,
contentPackage: element.ContentPackage);
GameAnalyticsManager.AddErrorEventOnce("RoundSound.LoadRoundSound:InvalidData" + filename, GameAnalyticsManager.ErrorSeverity.Error, errorMsg + "\n" + Environment.StackTrace.CleanupStackTrace());
return null;
}
@@ -123,7 +126,8 @@ namespace Barotrauma
catch (System.IO.FileNotFoundException e)
{
string errorMsg = "Failed to load sound file \"" + roundSound.Filename + "\".";
DebugConsole.ThrowError(errorMsg, e);
DebugConsole.ThrowError(errorMsg, e,
contentPackage: roundSound.Sound?.XElement?.ContentPackage);
GameAnalyticsManager.AddErrorEventOnce("RoundSound.LoadRoundSound:FileNotFound" + roundSound.Filename, GameAnalyticsManager.ErrorSeverity.Error, errorMsg + "\n" + Environment.StackTrace.CleanupStackTrace());
return;
}

View File

@@ -150,7 +150,8 @@ namespace Barotrauma
Stretch = true,
RelativeSpacing = 0.01f
};
new GUIButton(new RectTransform(new Vector2(0.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityX"), style: "GUIButtonSmall")
var mirrorX = new GUIButton(new RectTransform(new Vector2(0.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityX"), style: "GUIButtonSmall")
{
ToolTip = TextManager.Get("MirrorEntityXToolTip"),
OnClicked = (button, data) =>
@@ -160,10 +161,12 @@ namespace Barotrauma
me.FlipX(relativeToSub: false);
}
if (!SelectedList.Contains(this)) { FlipX(relativeToSub: false); }
ColorFlipButton(button, FlippedX);
return true;
}
};
new GUIButton(new RectTransform(new Vector2(0.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityY"), style: "GUIButtonSmall")
ColorFlipButton(mirrorX, FlippedX);
var mirrorY = new GUIButton(new RectTransform(new Vector2(0.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityY"), style: "GUIButtonSmall")
{
ToolTip = TextManager.Get("MirrorEntityYToolTip"),
OnClicked = (button, data) =>
@@ -173,9 +176,11 @@ namespace Barotrauma
me.FlipY(relativeToSub: false);
}
if (!SelectedList.Contains(this)) { FlipY(relativeToSub: false); }
ColorFlipButton(button, FlippedY);
return true;
}
};
ColorFlipButton(mirrorY, FlippedY);
new GUIButton(new RectTransform(new Vector2(0.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("ReloadSprite"), style: "GUIButtonSmall")
{
OnClicked = (button, data) =>
@@ -231,11 +236,14 @@ namespace Barotrauma
public override bool IsVisible(Rectangle worldView)
{
Rectangle worldRect = WorldRect;
RectangleF worldRect = Quad2D.FromSubmarineRectangle(WorldRect).Rotated(
FlippedX != FlippedY
? rotationRad
: -rotationRad).BoundingAxisAlignedRectangle;
Vector2 worldPos = WorldPosition;
Vector2 min = new Vector2(worldRect.X, worldRect.Y - worldRect.Height);
Vector2 max = new Vector2(worldRect.Right, worldRect.Y);
Vector2 min = new Vector2(worldRect.X, worldRect.Y);
Vector2 max = new Vector2(worldRect.Right, worldRect.Y + worldRect.Height);
foreach (DecorativeSprite decorativeSprite in Prefab.DecorativeSprites)
{
float scale = decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale;
@@ -307,7 +315,12 @@ namespace Barotrauma
Vector2 bodyPos = WorldPosition + BodyOffset * Scale;
GUI.DrawRectangle(spriteBatch, new Vector2(bodyPos.X, -bodyPos.Y), rectSize.X, rectSize.Y, BodyRotation, Color.White,
GUI.DrawRectangle(sb: spriteBatch,
center: new Vector2(bodyPos.X, -bodyPos.Y),
width: rectSize.X,
height: rectSize.Y,
rotation: BodyRotation,
clr: Color.White,
thickness: Math.Max(1, (int)(2 / Screen.Selected.Cam.Zoom)));
}
@@ -357,8 +370,10 @@ namespace Barotrauma
Prefab.BackgroundSprite.DrawTiled(
spriteBatch,
new Vector2(rect.X + drawOffset.X, -(rect.Y + drawOffset.Y)),
new Vector2(rect.X + rect.Width / 2 + drawOffset.X, -(rect.Y - rect.Height / 2 + drawOffset.Y)),
new Vector2(rect.Width, rect.Height),
rotation: rotationRad,
origin: rect.Size.ToVector2() * new Vector2(0.5f, 0.5f),
color: Prefab.BackgroundSpriteColor,
textureScale: TextureScale * Scale,
startOffset: backGroundOffset,
@@ -368,8 +383,10 @@ namespace Barotrauma
{
Prefab.BackgroundSprite.DrawTiled(
spriteBatch,
new Vector2(rect.X + drawOffset.X, -(rect.Y + drawOffset.Y)) + dropShadowOffset,
new Vector2(rect.X + rect.Width / 2 + drawOffset.X, -(rect.Y - rect.Height / 2 + drawOffset.Y)) + dropShadowOffset,
new Vector2(rect.Width, rect.Height),
rotation: rotationRad,
origin: rect.Size.ToVector2() * new Vector2(0.5f, 0.5f),
color: Color.Black * 0.5f,
textureScale: TextureScale * Scale,
startOffset: backGroundOffset,
@@ -385,6 +402,13 @@ namespace Barotrauma
SpriteEffects oldEffects = Prefab.Sprite.effects;
Prefab.Sprite.effects ^= SpriteEffects;
Vector2 advanceX = MathUtils.RotatedUnitXRadians(this.rotationRad).FlipY();
Vector2 advanceY = advanceX.YX().FlipX();
if (FlippedX != FlippedY)
{
advanceX = advanceX.FlipY();
advanceY = advanceY.FlipX();
}
for (int i = 0; i < Sections.Length; i++)
{
Rectangle drawSection = Sections[i].rect;
@@ -409,7 +433,7 @@ namespace Barotrauma
drawSection = new Rectangle(
drawSection.X,
drawSection.Y,
Sections[Sections.Length -1 ].rect.Right - drawSection.X,
Sections[Sections.Length - 1].rect.Right - drawSection.X,
drawSection.Y - (Sections[Sections.Length - 1].rect.Y - Sections[Sections.Length - 1].rect.Height));
i = Sections.Length;
}
@@ -424,10 +448,18 @@ namespace Barotrauma
sectionOffset.X += MathUtils.PositiveModulo((int)-textureOffset.X, Prefab.Sprite.SourceRect.Width);
sectionOffset.Y += MathUtils.PositiveModulo((int)-textureOffset.Y, Prefab.Sprite.SourceRect.Height);
Vector2 pos = new Vector2(drawSection.X, drawSection.Y);
pos -= rect.Location.ToVector2();
pos = advanceX * pos.X + advanceY * pos.Y;
pos += rect.Location.ToVector2();
pos = new Vector2(pos.X + rect.Width / 2 + drawOffset.X, -(pos.Y - rect.Height / 2 + drawOffset.Y));
Prefab.Sprite.DrawTiled(
spriteBatch,
new Vector2(drawSection.X + drawOffset.X, -(drawSection.Y + drawOffset.Y)),
pos,
new Vector2(drawSection.Width, drawSection.Height),
rotation: rotationRad,
origin: rect.Size.ToVector2() * new Vector2(0.5f, 0.5f),
color: color,
startOffset: sectionOffset,
depth: depth,
@@ -437,7 +469,7 @@ namespace Barotrauma
foreach (var decorativeSprite in Prefab.DecorativeSprites)
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState, spriteAnimState[decorativeSprite].RandomRotationFactor);
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState, spriteAnimState[decorativeSprite].RandomRotationFactor) + this.rotationRad;
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier) * Scale;
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + offset.X, -(DrawPosition.Y + offset.Y)), color,
rotation, decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, Prefab.Sprite.effects,

View File

@@ -94,19 +94,20 @@ namespace Barotrauma
GUI.DrawRectangle(spriteBatch, new Rectangle(newRect.X, -newRect.Y - GameMain.GraphicsHeight, newRect.Width, newRect.Height + GameMain.GraphicsHeight * 2), Color.White);
}
public override void DrawPlacing(SpriteBatch spriteBatch, Rectangle placeRect, float scale = 1.0f, SpriteEffects spriteEffects = SpriteEffects.None)
public override void DrawPlacing(SpriteBatch spriteBatch, Rectangle placeRect, float scale = 1.0f, float rotation = 0.0f, SpriteEffects spriteEffects = SpriteEffects.None)
{
SpriteEffects oldEffects = Sprite.effects;
Sprite.effects ^= spriteEffects;
var position = placeRect.Location.ToVector2().FlipY();
position += placeRect.Size.ToVector2() * 0.5f;
Sprite.DrawTiled(
spriteBatch,
new Vector2(placeRect.X, -placeRect.Y),
new Vector2(placeRect.Width, placeRect.Height),
color: Color.White * 0.8f,
textureScale: TextureScale * scale);
Sprite.effects = oldEffects;
position,
placeRect.Size.ToVector2(),
color: Color.White * 0.8f,
origin: placeRect.Size.ToVector2() * 0.5f,
rotation: rotation,
textureScale: TextureScale * scale,
spriteEffects: spriteEffects ^ Sprite.effects);
}
}
}

View File

@@ -25,7 +25,7 @@ namespace Barotrauma
/// <summary>
/// Margin applied around the view area when culling entities (i.e. entities that are this far outside the view are still considered visible)
/// </summary>
private const int CullMargin = 500;
private const int CullMargin = 50;
/// <summary>
/// Update entity culling when any corner of the view has moved more than this
/// </summary>
@@ -713,18 +713,12 @@ namespace Barotrauma
return GameMain.LightManager.Lights.Count(l => l.CastShadows && !l.IsBackground) - disabledItemLightCount;
}
public static Vector2 MouseToWorldGrid(Camera cam, Submarine sub, bool round = false)
public static Vector2 MouseToWorldGrid(Camera cam, Submarine sub, Vector2? mousePos = null, bool round = false)
{
Vector2 position = PlayerInput.MousePosition;
Vector2 position = mousePos ?? PlayerInput.MousePosition;
position = cam.ScreenToWorld(position);
Vector2 worldGridPos = VectorToWorldGrid(position, round);
if (sub != null)
{
worldGridPos.X += sub.Position.X % GridSize.X;
worldGridPos.Y += sub.Position.Y % GridSize.Y;
}
Vector2 worldGridPos = VectorToWorldGrid(position, sub, round);
return worldGridPos;
}

View File

@@ -421,7 +421,7 @@ namespace Barotrauma
float scale = element.GetAttributeFloat("scale", 1f);
Color color = element.GetAttributeColor("spritecolor", Color.White);
float rotation = element.GetAttributeFloat("rotation", 0f);
float rotationRad = MathHelper.ToRadians(element.GetAttributeFloat("rotation", 0f));
MapEntityPrefab prefab;
if (element.NameAsIdentifier() == "item"
@@ -455,7 +455,7 @@ namespace Barotrauma
ItemPrefab itemPrefab = prefab as ItemPrefab;
if (itemPrefab != null)
{
BakeItemComponents(itemPrefab, rect, color, scale, rotation, depth, out overrideSprite);
BakeItemComponents(itemPrefab, rect, color, scale, rotationRad, depth, out overrideSprite);
}
if (!overrideSprite)
@@ -485,13 +485,15 @@ namespace Barotrauma
MathUtils.PositiveModulo((int)-textureOffset.Y, prefab.Sprite.SourceRect.Height));
prefab.Sprite.DrawTiled(
spriteRecorder,
rect.Location.ToVector2() * new Vector2(1f, -1f),
rect.Size.ToVector2(),
spriteBatch: spriteRecorder,
position: new Vector2(rect.X + rect.Width / 2, -(rect.Y - rect.Height / 2)),
targetSize: rect.Size.ToVector2(),
origin: rect.Size.ToVector2() * new Vector2(0.5f, 0.5f),
color: color,
startOffset: backGroundOffset,
textureScale: textureScale * scale,
depth: depth);
depth: depth,
rotation: rotationRad);
}
else if (itemPrefab != null)
{
@@ -552,7 +554,7 @@ namespace Barotrauma
spritePos * new Vector2(1f, -1f),
color,
prefab.Sprite.Origin,
rotation,
rotationRad,
scale,
prefab.Sprite.effects, depth);
@@ -564,7 +566,7 @@ namespace Barotrauma
if (flippedX) { offset.X = -offset.X; }
if (flippedY) { offset.Y = -offset.Y; }
decorativeSprite.Sprite.Draw(spriteRecorder, new Vector2(spritePos.X + offset.X, -(spritePos.Y + offset.Y)), color,
MathHelper.ToRadians(rotation) + rot, decorativeSprite.GetScale(0f) * scale, prefab.Sprite.effects,
rotationRad + rot, decorativeSprite.GetScale(0f) * scale, prefab.Sprite.effects,
depth: Math.Min(depth + (decorativeSprite.Sprite.Depth - prefab.Sprite.Depth), 0.999f));
}
}
@@ -577,7 +579,7 @@ namespace Barotrauma
private void BakeItemComponents(
ItemPrefab prefab,
Rectangle rect, Color color,
float scale, float rotation, float depth,
float scale, float rotationRad, float depth,
out bool overrideSprite)
{
overrideSprite = false;
@@ -607,7 +609,7 @@ namespace Barotrauma
Vector2 relativeBarrelPos = barrelPos * prefab.Scale - new Vector2(rect.Width / 2, rect.Height / 2);
var transformedBarrelPos = MathUtils.RotatePoint(
relativeBarrelPos,
MathHelper.ToRadians(rotation));
rotationRad);
Vector2 drawPos = new Vector2(rect.X + rect.Width * relativeScale / 2 + transformedBarrelPos.X * relativeScale, rect.Y - rect.Height * relativeScale / 2 - transformedBarrelPos.Y * relativeScale);
drawPos.Y = -drawPos.Y;
@@ -615,13 +617,13 @@ namespace Barotrauma
railSprite?.Draw(spriteRecorder,
drawPos,
color,
rotation + MathHelper.PiOver2, scale,
rotationRad, scale,
SpriteEffects.None, depth + (railSprite.Depth - prefab.Sprite.Depth));
barrelSprite?.Draw(spriteRecorder,
drawPos,
color,
rotation + MathHelper.PiOver2, scale,
rotationRad, scale,
SpriteEffects.None, depth + (barrelSprite.Depth - prefab.Sprite.Depth));
break;
@@ -781,7 +783,7 @@ namespace Barotrauma
previewFrame = null;
}
spriteRecorder?.Dispose(); spriteRecorder = null;
camera?.Dispose(); camera = null;
camera = null;
isDisposed = true;
}
}

View File

@@ -122,12 +122,12 @@ namespace Barotrauma.Networking
VoipSound = null;
}
public void SetPermissions(ClientPermissions permissions, IEnumerable<string> permittedConsoleCommands)
public void SetPermissions(ClientPermissions permissions, IEnumerable<Identifier> permittedConsoleCommands)
{
List<DebugConsole.Command> permittedCommands = new List<DebugConsole.Command>();
foreach (string commandName in permittedConsoleCommands)
foreach (Identifier commandName in permittedConsoleCommands)
{
var consoleCommand = DebugConsole.Commands.Find(c => c.names.Contains(commandName));
var consoleCommand = DebugConsole.Commands.Find(c => c.Names.Contains(commandName));
if (consoleCommand != null)
{
permittedCommands.Add(consoleCommand);

View File

@@ -65,7 +65,7 @@ namespace Barotrauma.Networking
public bool LateCampaignJoin = false;
private ClientPermissions permissions = ClientPermissions.None;
private List<string> permittedConsoleCommands = new List<string>();
private List<Identifier> permittedConsoleCommands = new List<Identifier>();
private bool connected;
@@ -170,9 +170,9 @@ namespace Barotrauma.Networking
internal readonly struct PermissionChangedEvent
{
public readonly ClientPermissions NewPermissions;
public readonly ImmutableArray<string> NewPermittedConsoleCommands;
public readonly ImmutableArray<Identifier> NewPermittedConsoleCommands;
public PermissionChangedEvent(ClientPermissions newPermissions, IReadOnlyList<string> newPermittedConsoleCommands)
public PermissionChangedEvent(ClientPermissions newPermissions, IReadOnlyList<Identifier> newPermittedConsoleCommands)
{
NewPermissions = newPermissions;
NewPermittedConsoleCommands = newPermittedConsoleCommands.ToImmutableArray();
@@ -1213,11 +1213,11 @@ namespace Barotrauma.Networking
targetClient?.SetPermissions(permissions, permittedCommands);
if (clientId == SessionId)
{
SetMyPermissions(permissions, permittedCommands.Select(command => command.names[0]));
SetMyPermissions(permissions, permittedCommands.Select(command => command.Names[0]));
}
}
private void SetMyPermissions(ClientPermissions newPermissions, IEnumerable<string> permittedConsoleCommands)
private void SetMyPermissions(ClientPermissions newPermissions, IEnumerable<Identifier> permittedConsoleCommands)
{
if (!(this.permittedConsoleCommands.Any(c => !permittedConsoleCommands.Contains(c)) ||
permittedConsoleCommands.Any(c => !this.permittedConsoleCommands.Contains(c))))
@@ -1229,7 +1229,7 @@ namespace Barotrauma.Networking
permissions.HasFlag(ClientPermissions.ManageRound) != newPermissions.HasFlag(ClientPermissions.ManageRound);
permissions = newPermissions;
this.permittedConsoleCommands = new List<string>(permittedConsoleCommands);
this.permittedConsoleCommands = permittedConsoleCommands.ToList();
//don't show the "permissions changed" popup if the client owns the server
if (!IsServerOwner)
{
@@ -1267,10 +1267,10 @@ namespace Barotrauma.Networking
var commandsLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), rightColumn.RectTransform),
TextManager.Get("PermittedConsoleCommands"), wrap: true, font: GUIStyle.SubHeadingFont);
var commandList = new GUIListBox(new RectTransform(new Vector2(1.0f, 1.0f), rightColumn.RectTransform));
foreach (string permittedCommand in permittedConsoleCommands)
foreach (Identifier permittedCommand in permittedConsoleCommands)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), commandList.Content.RectTransform, minSize: new Point(0, 15)),
permittedCommand, font: GUIStyle.SmallFont)
permittedCommand.Value, font: GUIStyle.SmallFont)
{
CanBeFocused = false
};
@@ -1350,6 +1350,7 @@ namespace Barotrauma.Networking
bool respawnAllowed = inc.ReadBoolean();
ServerSettings.AllowDisguises = inc.ReadBoolean();
ServerSettings.AllowRewiring = inc.ReadBoolean();
ServerSettings.AllowImmediateItemDelivery = inc.ReadBoolean();
ServerSettings.AllowFriendlyFire = inc.ReadBoolean();
ServerSettings.LockAllDefaultWires = inc.ReadBoolean();
ServerSettings.AllowLinkingWifiToChat = inc.ReadBoolean();
@@ -2223,7 +2224,7 @@ namespace Barotrauma.Networking
}
outmsg.WriteByte((byte)MultiplayerPreferences.Instance.TeamPreference);
if (!(GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign) || campaign.LastSaveID == 0)
if (GameMain.GameSession?.GameMode is not MultiPlayerCampaign campaign || campaign.LastSaveID == 0)
{
outmsg.WriteUInt16((UInt16)0);
}
@@ -2553,18 +2554,18 @@ namespace Barotrauma.Networking
return permissions.HasFlag(permission);
}
public bool HasConsoleCommandPermission(string commandName)
public bool HasConsoleCommandPermission(Identifier commandName)
{
if (!permissions.HasFlag(ClientPermissions.ConsoleCommands)) { return false; }
if (permittedConsoleCommands.Any(c => c.Equals(commandName, StringComparison.OrdinalIgnoreCase))) { return true; }
if (permittedConsoleCommands.Contains(commandName)) { return true; }
//check aliases
foreach (DebugConsole.Command command in DebugConsole.Commands)
{
if (command.names.Contains(commandName))
if (command.Names.Contains(commandName))
{
if (command.names.Intersect(permittedConsoleCommands).Any()) { return true; }
if (command.Names.Intersect(permittedConsoleCommands).Any()) { return true; }
break;
}
}

View File

@@ -75,6 +75,9 @@ namespace Barotrauma.Networking
[Serialize("", IsPropertySaveable.Yes)]
public LanguageIdentifier Language { get; set; }
[Serialize("", IsPropertySaveable.Yes)]
public string SelectedSub { get; set; } = string.Empty;
public Version GameVersion { get; set; } = new Version(0, 0, 0, 0);
public Option<int> Ping = Option<int>.None();
@@ -104,6 +107,8 @@ namespace Barotrauma.Networking
public ImmutableArray<ContentPackageInfo> ContentPackages;
public int ContentPackageCount;
public bool IsModded => ContentPackages.Any(p => !GameMain.VanillaContent.NameMatches(p.Name));
public ServerInfo(Endpoint endpoint)
@@ -176,33 +181,6 @@ namespace Barotrauma.Networking
};
title.Text = ToolBox.LimitString(title.Text, title.Font, (int)(title.Rect.Width * 0.85f));
bool isFavorite = serverListScreen.IsFavorite(this);
static LocalizedString favoriteTickBoxToolTip(bool isFavorite)
=> TextManager.Get(isFavorite ? "RemoveFromFavorites" : "AddToFavorites");
GUITickBox favoriteTickBox = new GUITickBox(new RectTransform(new Vector2(0.15f, 0.8f), title.RectTransform, Anchor.CenterRight),
"", null, "GUIServerListFavoriteTickBox")
{
UserData = this,
Selected = isFavorite,
ToolTip = favoriteTickBoxToolTip(isFavorite),
OnSelected = tickbox =>
{
ServerInfo info = (ServerInfo)tickbox.UserData;
if (tickbox.Selected)
{
GameMain.ServerListScreen.AddToFavoriteServers(info);
}
else
{
GameMain.ServerListScreen.RemoveFromFavoriteServers(info);
}
tickbox.ToolTip = favoriteTickBoxToolTip(tickbox.Selected);
return true;
}
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), frame.RectTransform),
TextManager.AddPunctuation(':', TextManager.Get("ServerListVersion"),
GameVersion == new Version(0, 0, 0, 0) ? TextManager.Get("Unknown") : GameVersion.ToString()))
@@ -258,6 +236,59 @@ namespace Barotrauma.Networking
{
Stretch = true
};
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 0.25f), playStyleBanner.RectTransform, Anchor.BottomRight),
isHorizontal: true, childAnchor: Anchor.BottomRight);
//shadow behind the buttons
new GUIFrame(new RectTransform(new Vector2(3.15f, 1.05f), buttonContainer.RectTransform, Anchor.BottomRight, scaleBasis: ScaleBasis.Smallest), style: null)
{
Color = Color.Black * 0.7f,
IgnoreLayoutGroups = true
};
bool isFavorite = serverListScreen.IsFavorite(this);
static LocalizedString favoriteTickBoxToolTip(bool isFavorite)
=> TextManager.Get(isFavorite ? "RemoveFromFavorites" : "AddToFavorites");
GUITickBox favoriteTickBox = new GUITickBox(new RectTransform(Vector2.One, buttonContainer.RectTransform, scaleBasis: ScaleBasis.Smallest),
"", null, "GUIServerListFavoriteTickBox")
{
UserData = this,
Selected = isFavorite,
ToolTip = favoriteTickBoxToolTip(isFavorite),
OnSelected = tickbox =>
{
ServerInfo info = (ServerInfo)tickbox.UserData;
if (tickbox.Selected)
{
GameMain.ServerListScreen.AddToFavoriteServers(info);
}
else
{
GameMain.ServerListScreen.RemoveFromFavoriteServers(info);
}
tickbox.ToolTip = favoriteTickBoxToolTip(tickbox.Selected);
return true;
}
};
new GUIButton(new RectTransform(Vector2.One, buttonContainer.RectTransform, scaleBasis: ScaleBasis.Smallest), style: "GUIServerListReportServer")
{
ToolTip = TextManager.Get("reportserver"),
OnClicked = (_, _) => {ServerListScreen.CreateReportPrompt(this); return true; }
};
new GUIButton(new RectTransform(Vector2.One, buttonContainer.RectTransform, scaleBasis: ScaleBasis.Smallest), style: "GUIServerListHideServer")
{
ToolTip = TextManager.Get("filterserver"),
OnClicked = (_, _) =>
{
ServerListScreen.CreateFilterServerPrompt(this);
return true;
}
};
// playstyle tags -----------------------------------------------------------------------------
var playStyleContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), content.RectTransform), isHorizontal: true)
@@ -309,6 +340,14 @@ namespace Barotrauma.Networking
TextManager.Get(GameMode.IsEmpty ? "Unknown" : "GameMode." + GameMode).Fallback(GameMode.Value),
textAlignment: Alignment.Right);
if (!string.IsNullOrEmpty(SelectedSub))
{
var submarineText = new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), content.RectTransform), TextManager.Get("Submarine"));
new GUITextBlock(new RectTransform(Vector2.One, submarineText.RectTransform),
SelectedSub,
textAlignment: Alignment.Right);
}
GUITextBlock playStyleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), content.RectTransform), TextManager.Get("serverplaystyle"));
new GUITextBlock(new RectTransform(Vector2.One, playStyleText.RectTransform), TextManager.Get("servertag." + playStyle), textAlignment: Alignment.Right);
@@ -385,6 +424,15 @@ namespace Barotrauma.Networking
}
}
}
if (ContentPackageCount > ContentPackages.Length)
{
new GUITextBlock(
new RectTransform(new Vector2(1.0f, 0.15f), contentPackageList.Content.RectTransform) { MinSize = new Point(0, 15) },
TextManager.GetWithVariable("workshopitemdownloadprompttruncated", "[number]", (ContentPackageCount - ContentPackages.Length).ToString()))
{
CanBeFocused = false
};
}
}
// -----------------------------------------------------------------------------
@@ -423,14 +471,16 @@ namespace Barotrauma.Networking
AllowSpectating = getBool("allowspectating");
AllowRespawn = getBool("allowrespawn");
VoipEnabled = getBool("voicechatenabled");
GameMode = valueGetter("gamemode")?.ToIdentifier() ?? Identifier.Empty;
if (float.TryParse(valueGetter("traitors"), NumberStyles.Any, CultureInfo.InvariantCulture, out float traitorProbability)) { TraitorProbability = traitorProbability; }
if (Enum.TryParse(valueGetter("playstyle"), out PlayStyle playStyle)) { PlayStyle = playStyle; }
Language = valueGetter("language")?.ToLanguageIdentifier() ?? LanguageIdentifier.None;
SelectedSub = valueGetter("submarine") ?? string.Empty;
ContentPackages = ExtractContentPackageInfo(ServerName, valueGetter).ToImmutableArray();
ContentPackageCount = ContentPackages.Length;
if (int.TryParse(valueGetter("packagecount"), out int packageCount)) { ContentPackageCount = packageCount; }
bool getBool(string key)
{
string? data = valueGetter(key);

View File

@@ -936,6 +936,10 @@ namespace Barotrauma.Networking
TextManager.Get("ServerSettingsAllowVoteKick"));
GetPropertyData(nameof(AllowVoteKick)).AssignGUIComponent(voteKickBox);
var allowImmediateItemDeliveryBox = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform),
TextManager.Get("ServerSettingsImmediateItemDelivery"));
GetPropertyData(nameof(AllowImmediateItemDelivery)).AssignGUIComponent(allowImmediateItemDeliveryBox);
GUITextBlock.AutoScaleAndNormalize(tickBoxContainer.Content.Children.Select(c => ((GUITickBox)c).TextBlock));
tickBoxContainer.RectTransform.MinSize = new Point(0, (int)(tickBoxContainer.Content.Children.First().Rect.Height * 2.0f + tickBoxContainer.Padding.Y + tickBoxContainer.Padding.W));

View File

@@ -1,4 +1,5 @@
using Microsoft.Xna.Framework;
#nullable enable
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Xml.Linq;
@@ -148,7 +149,7 @@ namespace Barotrauma.Particles
Prefab = prefab;
}
public void Emit(float deltaTime, Vector2 position, Hull hullGuess = null, float angle = 0.0f, float particleRotation = 0.0f, float velocityMultiplier = 1.0f, float sizeMultiplier = 1.0f, float amountMultiplier = 1.0f, Color? colorMultiplier = null, ParticlePrefab overrideParticle = null, bool mirrorAngle = false, Tuple<Vector2, Vector2> tracerPoints = null)
public void Emit(float deltaTime, Vector2 position, Hull? hullGuess = null, float angle = 0.0f, float particleRotation = 0.0f, float velocityMultiplier = 1.0f, float sizeMultiplier = 1.0f, float amountMultiplier = 1.0f, Color? colorMultiplier = null, ParticlePrefab? overrideParticle = null, bool mirrorAngle = false, Tuple<Vector2, Vector2>? tracerPoints = null)
{
if (GameMain.Client?.MidRoundSyncing ?? false) { return; }
@@ -191,16 +192,17 @@ namespace Barotrauma.Particles
burstEmitTimer = Prefab.Properties.EmitInterval;
for (int i = 0; i < Prefab.Properties.ParticleAmount * amountMultiplier; i++)
{
Emit(position, hullGuess, angle, particleRotation, velocityMultiplier, sizeMultiplier, colorMultiplier, overrideParticle, tracerPoints: tracerPoints);
Emit(position, hullGuess, angle, particleRotation, velocityMultiplier, sizeMultiplier, colorMultiplier, overrideParticle, mirrorAngle, tracerPoints: tracerPoints);
}
}
private void Emit(Vector2 position, Hull hullGuess, float angle, float particleRotation, float velocityMultiplier, float sizeMultiplier, Color? colorMultiplier = null, ParticlePrefab overrideParticle = null, bool mirrorAngle = false, Tuple<Vector2, Vector2> tracerPoints = null)
private void Emit(Vector2 position, Hull? hullGuess, float angle, float particleRotation, float velocityMultiplier, float sizeMultiplier, Color? colorMultiplier = null, ParticlePrefab? overrideParticle = null, bool mirrorAngle = false, Tuple<Vector2, Vector2>? tracerPoints = null)
{
var particlePrefab = overrideParticle ?? Prefab.ParticlePrefab;
if (particlePrefab == null)
{
DebugConsole.AddWarning($"Could not find the particle prefab \"{Prefab.ParticlePrefabName}\".");
DebugConsole.AddWarning($"Could not find the particle prefab \"{Prefab.ParticlePrefabName}\".",
contentPackage: Prefab.ContentPackage);
return;
}
@@ -271,7 +273,7 @@ namespace Barotrauma.Particles
{
public readonly Identifier ParticlePrefabName;
public ParticlePrefab ParticlePrefab
public ParticlePrefab? ParticlePrefab
{
get
{
@@ -282,12 +284,16 @@ namespace Barotrauma.Particles
public readonly ParticleEmitterProperties Properties;
public bool DrawOnTop => Properties.DrawOnTop || ParticlePrefab.DrawOnTop;
public readonly ContentPackage? ContentPackage;
public bool DrawOnTop => Properties.DrawOnTop || ParticlePrefab is { DrawOnTop: true };
public ParticleEmitterPrefab(ContentXElement element)
{
Properties = new ParticleEmitterProperties(element);
if (element == null) { throw new ArgumentNullException(nameof(element)); }
Properties = new ParticleEmitterProperties(element!);
ParticlePrefabName = element.GetAttributeIdentifier("particle", "");
ContentPackage = element.ContentPackage;
}
public ParticleEmitterPrefab(ParticlePrefab prefab, ParticleEmitterProperties properties)

View File

@@ -244,7 +244,8 @@ namespace Barotrauma.Particles
if (Sprites.Count == 0)
{
DebugConsole.ThrowError($"Particle prefab \"{Name}\" in the file \"{file}\" has no sprites defined!");
DebugConsole.ThrowError($"Particle prefab \"{Name}\" in the file \"{file}\" has no sprites defined!",
contentPackage: element.ContentPackage);
}
//if velocity change in water is not given, it defaults to the normal velocity change

View File

@@ -66,15 +66,22 @@ namespace Barotrauma
private static void CrashHandler(object sender, UnhandledExceptionEventArgs args)
{
Exception unhandledException = args.ExceptionObject as Exception;
try
{
Game?.Exit();
CrashDump(Game, "crashreport.log", (Exception)args.ExceptionObject);
CrashDump(Game, "crashreport.log", unhandledException);
Game?.Dispose();
}
catch (Exception e)
catch (Exception exceptionHandlerError)
{
Debug.WriteLine(e.Message);
Debug.WriteLine(exceptionHandlerError.Message);
string slimCrashReport = "Exception handler failed: " + exceptionHandlerError.Message + "\n" + exceptionHandlerError.StackTrace;
if (unhandledException != null)
{
slimCrashReport += "\n\nInitial exception: " + unhandledException.Message + "\n" + unhandledException.StackTrace;
}
File.WriteAllText("crashreportslim.log", slimCrashReport);
//exception handler is broken, we have a serious problem here!!
return;
}

View File

@@ -40,7 +40,8 @@ namespace Barotrauma
public CampaignMode Campaign { get; }
public CrewManagement CrewManagement { get; set; }
private Store Store { get; set; }
public Store Store { get; private set; }
public UpgradeStore UpgradeStore { get; set; }
@@ -254,7 +255,7 @@ namespace Barotrauma
RelativeSpacing = 0.02f,
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), location.Name, font: GUIStyle.LargeFont)
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), location.DisplayName, font: GUIStyle.LargeFont)
{
AutoScaleHorizontal = true
};
@@ -598,9 +599,10 @@ namespace Barotrauma
break;
case CampaignMode.InteractionType.Crew:
CrewManagement.UpdateCrew();
CrewManagement.UpdateHireables();
break;
case CampaignMode.InteractionType.PurchaseSub:
if (submarineSelection == null) submarineSelection = new SubmarineSelection(false, () => Campaign.ShowCampaignUI = false, tabs[(int)CampaignMode.InteractionType.PurchaseSub].RectTransform);
submarineSelection ??= new SubmarineSelection(false, () => Campaign.ShowCampaignUI = false, tabs[(int)CampaignMode.InteractionType.PurchaseSub].RectTransform);
submarineSelection.RefreshSubmarineDisplay(true, setTransferOptionToTrue: true);
break;
case CampaignMode.InteractionType.Map:

View File

@@ -259,8 +259,8 @@ namespace Barotrauma
graphics.BlendState = BlendState.NonPremultiplied;
graphics.SamplerStates[0] = SamplerState.LinearWrap;
Quad.UseBasicEffect(renderTargetBackground);
Quad.Render();
GraphicsQuad.UseBasicEffect(renderTargetBackground);
GraphicsQuad.Render();
//Draw the rest of the structures, characters and front structures
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform);
@@ -313,8 +313,8 @@ namespace Barotrauma
graphics.BlendState = BlendState.Opaque;
graphics.SamplerStates[0] = SamplerState.LinearWrap;
Quad.UseBasicEffect(renderTarget);
Quad.Render();
GraphicsQuad.UseBasicEffect(renderTarget);
GraphicsQuad.Render();
//draw alpha blended particles that are inside a sub
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.DepthRead, null, null, cam.Transform);
@@ -380,8 +380,8 @@ namespace Barotrauma
graphics.DepthStencilState = DepthStencilState.None;
graphics.SamplerStates[0] = SamplerState.LinearWrap;
graphics.BlendState = CustomBlendStates.Multiplicative;
Quad.UseBasicEffect(GameMain.LightManager.LightMap);
Quad.Render();
GraphicsQuad.UseBasicEffect(GameMain.LightManager.LightMap);
GraphicsQuad.Render();
}
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.LinearWrap, DepthStencilState.None, null, null, cam.Transform);
@@ -390,6 +390,8 @@ namespace Barotrauma
c.DrawFront(spriteBatch, cam);
}
GameMain.LightManager.DebugDrawVertices(spriteBatch);
Level.Loaded?.DrawDebugOverlay(spriteBatch, cam);
if (GameMain.DebugDraw)
{
@@ -438,7 +440,7 @@ namespace Barotrauma
graphics.SamplerStates[0] = SamplerState.PointClamp;
graphics.SamplerStates[1] = SamplerState.PointClamp;
GameMain.LightManager.LosEffect.CurrentTechnique.Passes[0].Apply();
Quad.Render();
GraphicsQuad.Render();
graphics.SamplerStates[0] = SamplerState.LinearWrap;
graphics.SamplerStates[1] = SamplerState.LinearWrap;
}
@@ -506,7 +508,7 @@ namespace Barotrauma
graphics.DepthStencilState = DepthStencilState.None;
if (string.IsNullOrEmpty(postProcessTechnique))
{
Quad.UseBasicEffect(renderTargetFinal);
GraphicsQuad.UseBasicEffect(renderTargetFinal);
}
else
{
@@ -515,7 +517,7 @@ namespace Barotrauma
PostProcessEffect.CurrentTechnique = PostProcessEffect.Techniques[postProcessTechnique];
PostProcessEffect.CurrentTechnique.Passes[0].Apply();
}
Quad.Render();
GraphicsQuad.Render();
if (fadeToBlackState > 0.0f)
{

View File

@@ -221,7 +221,17 @@ namespace Barotrauma
currentLevelData.AllowInvalidOutpost = allowInvalidOutpost.Selected;
var dummyLocations = GameSession.CreateDummyLocations(currentLevelData);
Level.Generate(currentLevelData, mirror: mirrorLevel.Selected, startLocation: dummyLocations[0], endLocation: dummyLocations[1]);
Submarine.MainSub?.SetPosition(Level.Loaded.StartPosition);
if (Submarine.MainSub != null)
{
Vector2 startPos = Level.Loaded.StartPosition;
if (Level.Loaded.StartOutpost != null)
{
startPos.Y -= Level.Loaded.StartOutpost.Borders.Height / 2 + Submarine.MainSub.Borders.Height / 2;
}
Submarine.MainSub?.SetPosition(startPos);
}
GameMain.LightManager.AddLight(pointerLightSource);
if (!wasLevelLoaded || Cam.Position.X < 0 || Cam.Position.Y < 0 || Cam.Position.Y > Level.Loaded.Size.X || Cam.Position.Y > Level.Loaded.Size.Y)
{

View File

@@ -76,6 +76,8 @@ namespace Barotrauma
private GUITextBlock tutorialHeader, tutorialDescription;
private GUIListBox tutorialList;
private GUIComponent versionMismatchWarning;
#region Creation
public MainMenuScreen(GameMain game)
{
@@ -105,6 +107,28 @@ namespace Barotrauma
}
};
versionMismatchWarning = new GUIFrame(new RectTransform(new Vector2(0.7f, 0.065f), Frame.RectTransform) { AbsoluteOffset = new Point(GUI.IntScale(15)) }, style: "InnerFrame", color: GUIStyle.Red)
{
IgnoreLayoutGroups = true,
Visible = false
};
var versionMismatchContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), versionMismatchWarning.RectTransform, Anchor.Center), isHorizontal: true)
{
RelativeSpacing = 0.05f,
};
new GUIImage(new RectTransform(new Vector2(1.0f), versionMismatchContent.RectTransform, scaleBasis: ScaleBasis.Smallest), style: "GUINotificationButton")
{
Color = GUIStyle.Orange
};
new GUITextBlock(new RectTransform(new Vector2(0.85f, 1.0f), versionMismatchContent.RectTransform),
TextManager.GetWithVariables("versionmismatchwarning",
("[gameversion]", GameMain.Version.ToString()),
("[contentversion]", ContentPackageManager.VanillaCorePackage.GameVersion.ToString())),
wrap: true)
{
TextColor = GUIStyle.Orange
};
new GUIImage(new RectTransform(new Vector2(0.4f, 0.25f), Frame.RectTransform, Anchor.BottomRight)
{ RelativeOffset = new Vector2(0.08f, 0.05f), AbsoluteOffset = new Point(-8, -8) },
style: "TitleText")
@@ -141,6 +165,7 @@ namespace Barotrauma
}
}
#else
SpamServerFilters.RequestGlobalSpamFilter();
FetchRemoteContent();
#endif
@@ -607,7 +632,9 @@ namespace Barotrauma
GameMain.SubEditorScreen?.ClearBackedUpSubInfo();
Submarine.Unload();
versionMismatchWarning.Visible = GameMain.Version < ContentPackageManager.VanillaCorePackage.GameVersion;
ResetButtonStates(null);
}
@@ -683,7 +710,18 @@ namespace Barotrauma
.ToArray();
foreach (var newServerExe in newServerExes)
{
serverExecutableDropdown.AddItem($"{newServerExe.ContentPackage.Name} - {Path.GetFileNameWithoutExtension(newServerExe.Path.Value)}", userData: newServerExe);
var serverExeEntry = serverExecutableDropdown.AddItem($"{newServerExe.ContentPackage.Name} - {Path.GetFileNameWithoutExtension(newServerExe.Path.Value)}", userData: newServerExe);
if (newServerExe.ContentPackage.GameVersion < GameMain.VanillaContent.GameVersion)
{
serverExeEntry.ToolTip =
TextManager.GetWithVariables("versionmismatchwarning",
("[gameversion]", newServerExe.ContentPackage.GameVersion.ToString()),
("[contentversion]", GameMain.VanillaContent.GameVersion.ToString()));
if (serverExeEntry is GUITextBlock serverExeText)
{
serverExeText.TextColor = GUIStyle.Red;
}
}
}
serverExecutableDropdown.ListBox.Content.Children.ForEach(c =>
{
@@ -1496,34 +1534,58 @@ namespace Barotrauma
{
OnClicked = (btn, userdata) =>
{
string name = serverNameBox.Text;
if (string.IsNullOrEmpty(name))
{
serverNameBox.Flash();
return false;
}
if (isPublicBox.Selected && ForbiddenWordFilter.IsForbidden(name, out string forbiddenWord))
{
var msgBox = new GUIMessageBox("",
TextManager.GetWithVariables("forbiddenservernameverification", ("[forbiddenword]", forbiddenWord), ("[servername]", name)),
new LocalizedString[] { TextManager.Get("yes"), TextManager.Get("no") });
msgBox.Buttons[0].OnClicked += (_, __) =>
{
TryStartServer();
msgBox.Close();
return true;
};
msgBox.Buttons[1].OnClicked += msgBox.Close;
}
else
{
TryStartServer();
}
CheckServerName();
return true;
}
};
void CheckServerName()
{
string name = serverNameBox.Text;
if (string.IsNullOrEmpty(name))
{
serverNameBox.Flash();
return;
}
if (isPublicBox.Selected && ForbiddenWordFilter.IsForbidden(name, out string forbiddenWord))
{
var msgBox = new GUIMessageBox("",
TextManager.GetWithVariables("forbiddenservernameverification", ("[forbiddenword]", forbiddenWord), ("[servername]", name)),
new LocalizedString[] { TextManager.Get("yes"), TextManager.Get("no") });
msgBox.Buttons[0].OnClicked += (_, __) =>
{
CheckServerExe();
msgBox.Close();
return true;
};
msgBox.Buttons[1].OnClicked += msgBox.Close;
return;
}
CheckServerExe();
}
void CheckServerExe()
{
if (serverExecutableDropdown?.SelectedData is ServerExecutableFile serverExe &&
serverExe.ContentPackage.GameVersion < GameMain.VanillaContent.GameVersion)
{
var msgBox = new GUIMessageBox(string.Empty,
TextManager.GetWithVariables("versionmismatchwarning",
("[gameversion]", serverExe.ContentPackage.GameVersion.ToString()),
("[contentversion]", GameMain.VanillaContent.GameVersion.ToString())) + "\n\n"+
TextManager.GetWithVariable("versionmismatch.verifylaunch", "[exename]", serverExe.ContentPackage.Name),
new LocalizedString[] { TextManager.Get("yes"), TextManager.Get("no") });
msgBox.Buttons[0].OnClicked += (_, __) =>
{
TryStartServer();
msgBox.Close();
return true;
};
msgBox.Buttons[1].OnClicked += msgBox.Close;
return;
}
TryStartServer();
}
}
private void SetServerPlayStyle(PlayStyle playStyle)

View File

@@ -1520,6 +1520,7 @@ namespace Barotrauma
};
bool nameChangePending = isGameRunning && GameMain.Client.PendingName != string.Empty && GameMain.Client?.Character?.Name != GameMain.Client.PendingName;
changesPendingText?.Parent?.RemoveChild(changesPendingText);
changesPendingText = null;
if (TabMenu.PendingChanges)
@@ -2389,10 +2390,20 @@ namespace Barotrauma
options.Add(kickOption);
}
options.Add(new ContextMenuOption("Ban", isEnabled: canBan, onSelected: delegate
if (GameMain.Client?.ServerSettings?.BanList?.BannedPlayers?.Any(bp => bp.MatchesClient(client)) ?? false)
{
GameMain.Client?.CreateKickReasonPrompt(client.Name, true);
}));
options.Add(new ContextMenuOption("clientpermission.unban", isEnabled: canBan, onSelected: delegate
{
GameMain.Client?.UnbanPlayer(client.Name);
}));
}
else
{
options.Add(new ContextMenuOption("Ban", isEnabled: canBan, onSelected: delegate
{
GameMain.Client?.CreateKickReasonPrompt(client.Name, true);
}));
}
GUIContextMenu.CreateContextMenu(null, client.Name, headerColor: clientColor, options.ToArray());
}
@@ -2591,11 +2602,11 @@ namespace Barotrauma
foreach (DebugConsole.Command command in DebugConsole.Commands)
{
var commandTickBox = new GUITickBox(new RectTransform(new Vector2(0.15f, 0.15f), commandList.Content.RectTransform),
command.names[0], font: GUIStyle.SmallFont)
command.Names[0].Value, font: GUIStyle.SmallFont)
{
Selected = selectedClient.PermittedConsoleCommands.Contains(command),
Enabled = !myClient,
ToolTip = command.help,
ToolTip = command.Help,
UserData = command
};
commandTickBox.OnSelected += (GUITickBox tickBox) =>
@@ -2630,12 +2641,25 @@ namespace Barotrauma
{
if (GameMain.Client.HasPermission(ClientPermissions.Ban))
{
var banButton = new GUIButton(new RectTransform(new Vector2(0.34f, 1.0f), buttonAreaTop.RectTransform),
TextManager.Get("Ban"))
GUIButton banButton;
if (GameMain.Client?.ServerSettings?.BanList?.BannedPlayers?.Any(bp => bp.MatchesClient(selectedClient)) ?? false)
{
UserData = selectedClient
};
banButton.OnClicked = (bt, userdata) => { BanPlayer(selectedClient); return true; };
banButton = new GUIButton(new RectTransform(new Vector2(0.34f, 1.0f), buttonAreaTop.RectTransform),
TextManager.Get("clientpermission.unban"))
{
UserData = selectedClient
};
banButton.OnClicked = (bt, userdata) => { GameMain.Client?.UnbanPlayer(selectedClient.Name); return true; };
}
else
{
banButton = new GUIButton(new RectTransform(new Vector2(0.34f, 1.0f), buttonAreaTop.RectTransform),
TextManager.Get("Ban"))
{
UserData = selectedClient
};
banButton.OnClicked = (bt, userdata) => { BanPlayer(selectedClient); return true; };
}
banButton.OnClicked += ClosePlayerFrame;
}
@@ -3147,12 +3171,12 @@ namespace Barotrauma
GUIButton jobButton = null;
var availableJobs = JobPrefab.Prefabs.Where(jobPrefab =>
jobPrefab.MaxNumber > 0 && JobList.Content.Children.All(c => !(c.UserData is JobVariant prefab) || prefab.Prefab != jobPrefab)
!jobPrefab.HiddenJob && jobPrefab.MaxNumber > 0 && JobList.Content.Children.All(c => c.UserData is not JobVariant prefab || prefab.Prefab != jobPrefab)
).Select(j => new JobVariant(j, 0));
availableJobs = availableJobs.Concat(
JobPrefab.Prefabs.Where(jobPrefab =>
jobPrefab.MaxNumber > 0 && JobList.Content.Children.Any(c => (c.UserData is JobVariant prefab) && prefab.Prefab == jobPrefab)
!jobPrefab.HiddenJob && jobPrefab.MaxNumber > 0 && JobList.Content.Children.Any(c => (c.UserData is JobVariant prefab) && prefab.Prefab == jobPrefab)
).Select(j => (JobVariant)JobList.Content.FindChild(c => (c.UserData is JobVariant prefab) && prefab.Prefab == j).UserData));
availableJobs = availableJobs.ToList();

View File

@@ -655,7 +655,8 @@ namespace Barotrauma
ScrollBarVisible = true,
OnSelected = (btn, obj) =>
{
if (!(obj is ServerInfo serverInfo)) { return false; }
if (GUI.MouseOn is GUIButton) { return false; }
if (obj is not ServerInfo serverInfo) { return false; }
joinButton.Enabled = true;
selectedServer = Option<ServerInfo>.Some(serverInfo);
@@ -852,6 +853,13 @@ namespace Barotrauma
});
}
public void HideServerPreview()
{
serverPreviewContainer.Visible = false;
panelAnimator.RightEnabled = false;
panelAnimator.RightVisible = false;
}
private void InsertServer(ServerInfo serverInfo, GUIComponent component)
{
var children = serverList.Content.RectTransform.Children.Reverse().ToList();
@@ -973,7 +981,7 @@ namespace Barotrauma
}
}
private void FilterServers()
public void FilterServers()
{
RemoveMsgFromServerList(MsgUserData.NoMatchingServers);
foreach (GUIComponent child in serverList.Content.Children)
@@ -1013,6 +1021,7 @@ namespace Barotrauma
return false;
}
#endif
if (SpamServerFilters.IsFiltered(serverInfo)) { return false; }
if (!string.IsNullOrEmpty(searchBox.Text) && !serverInfo.ServerName.Contains(searchBox.Text, StringComparison.OrdinalIgnoreCase)) { return false; }
@@ -1553,15 +1562,169 @@ namespace Barotrauma
var serverFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.06f), serverList.Content.RectTransform) { MinSize = new Point(0, 35) },
style: "ListBoxElement")
{
UserData = serverInfo
UserData = serverInfo,
};
serverFrame.OnSecondaryClicked += (_, data) =>
{
if (data is not ServerInfo info) { return false; }
CreateContextMenu(info);
return true;
};
new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), serverFrame.RectTransform, Anchor.Center), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
Stretch = false
};
UpdateServerInfoUI(serverInfo);
if (!skipPing) { PingUtils.GetServerPing(serverInfo, UpdateServerInfoUI); }
}
private static readonly Vector2 confirmPopupSize = new Vector2(0.2f, 0.2625f);
private static readonly Point confirmPopupMinSize = new Point(300, 300);
private void CreateContextMenu(ServerInfo info)
{
var favoriteOption = new ContextMenuOption(IsFavorite(info) ? "removefromfavorites" : "addtofavorites", isEnabled: true, () =>
{
if (IsFavorite(info))
{
RemoveFromFavoriteServers(info);
}
else
{
AddToFavoriteServers(info);
}
FilterServers();
});
var reportOption = new ContextMenuOption("reportserver", isEnabled: true, () => { CreateReportPrompt(info); });
var filterOption = new ContextMenuOption("filterserver", isEnabled: true, () =>
{
CreateFilterServerPrompt(info);
})
{
Tooltip = TextManager.Get("filterservertooltip")
};
GUIContextMenu.CreateContextMenu(favoriteOption, filterOption, reportOption);
}
public static void CreateFilterServerPrompt(ServerInfo info)
{
GUI.AskForConfirmation(
header: TextManager.Get("filterserver"),
body: TextManager.GetWithVariables("filterserverconfirm", ("[server]", info.ServerName), ("[filepath]", SpamServerFilter.SavePath)),
onConfirm: () =>
{
SpamServerFilters.AddServerToLocalSpamList(info);
if (GameMain.ServerListScreen is not { } serverListScreen) { return; }
if (serverListScreen.selectedServer.TryUnwrap(out var selectedServer) && selectedServer.Equals(info))
{
serverListScreen.HideServerPreview();
}
serverListScreen.FilterServers();
}, relativeSize: confirmPopupSize, minSize: confirmPopupMinSize);
}
private enum ReportReason
{
Spam,
Advertising,
Inappropriate
}
public static void CreateReportPrompt(ServerInfo info)
{
if (!GameAnalyticsManager.SendUserStatistics)
{
GUI.NotifyPrompt(TextManager.Get("reportserver"), TextManager.Get("reportserverdisabled"));
return;
}
var msgBox = new GUIMessageBox(
headerText: TextManager.Get("reportserver"),
text: string.Empty,
relativeSize: new Vector2(0.2f, 0.4f),
minSize: new Point(380, 430),
buttons: Array.Empty<LocalizedString>());
var layout = new GUILayoutGroup(new RectTransform(Vector2.One, msgBox.Content.RectTransform, Anchor.Center));
new GUITextBlock(new RectTransform(new Vector2(1f, 0.3f), layout.RectTransform), TextManager.GetWithVariable("reportserverexplanation", "[server]", info.ServerName), wrap: true)
{
ToolTip = TextManager.Get("reportserverprompttooltip")
};
var listBox = new GUIListBox(new RectTransform(new Vector2(1f, 0.3f), layout.RectTransform));
var enums = Enum.GetValues<ReportReason>();
foreach (ReportReason reason in enums)
{
new GUITickBox(new RectTransform(new Vector2(1f, 1f / enums.Length), listBox.Content.RectTransform), TextManager.Get($"reportreason.{reason}"))
{
UserData = reason
};
}
// padding
new GUIFrame(new RectTransform(new Vector2(1f, 0.05f), layout.RectTransform), style: null);
var buttonLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.3f), layout.RectTransform))
{
Stretch = true
};
var reportAndHideButton = new GUIButton(new RectTransform(new Vector2(1f, 0.333f), buttonLayout.RectTransform), TextManager.Get("reportoption.reportandhide"))
{
Enabled = false,
OnClicked = (_, _) =>
{
CreateFilterServerPrompt(info);
msgBox.Close();
return true;
}
};
var reportButton = new GUIButton(new RectTransform(new Vector2(1f, 0.333f), buttonLayout.RectTransform), TextManager.Get("reportoption.report"))
{
Enabled = false,
OnClicked = (_, _) =>
{
ReportServer(info, GetUserSelectedReasons());
msgBox.Close();
return true;
}
};
new GUIButton(new RectTransform(new Vector2(1f, 0.333f), buttonLayout.RectTransform), TextManager.Get("cancel"))
{
OnClicked = (_, _) =>
{
msgBox.Close();
return true;
}
};
foreach (var child in listBox.Content.GetAllChildren<GUITickBox>())
{
child.OnSelected += _ =>
{
reportAndHideButton.Enabled = reportButton.Enabled = GetUserSelectedReasons().Any();
return true;
};
}
IEnumerable<ReportReason> GetUserSelectedReasons()
=> listBox.Content.Children
.Where(static c => c.UserData is ReportReason && c.Selected)
.Select(static c => (ReportReason)c.UserData).ToArray();
}
private static void ReportServer(ServerInfo info, IEnumerable<ReportReason> reasons)
{
if (!reasons.Any()) { return; }
GameAnalyticsManager.AddErrorEvent(GameAnalyticsManager.ErrorSeverity.Info, $"[Spam] Reported server: Name: \"{info.ServerName}\", Message: \"{info.ServerMessage}\", Endpoint: \"{info.Endpoint.StringRepresentation}\". Reason: \"{string.Join(", ", reasons)}\".");
}
private void UpdateServerInfoUI(ServerInfo serverInfo)
@@ -1571,7 +1734,6 @@ namespace Barotrauma
serverFrame.UserData = serverInfo;
serverFrame.ToolTip = "";
var serverContent = serverFrame.Children.First() as GUILayoutGroup;
serverContent.ClearChildren();
@@ -1583,15 +1745,14 @@ namespace Barotrauma
new RectTransform(new Vector2(columns[label].RelativeWidth, 1.0f), serverContent.RectTransform),
style: null);
}
void errorTooltip(RichString toolTip)
void disableElementFocus()
{
sections.Values.ForEach(c =>
{
c.CanBeFocused = false;
c.Children.First().CanBeFocused = false;
});
serverFrame.ToolTip = toolTip;
}
RectTransform columnRT(ColumnLabel label, float scale = 0.95f)
@@ -1611,7 +1772,7 @@ namespace Barotrauma
NetworkMember.IsCompatible(GameMain.Version, serverInfo.GameVersion),
UserData = "compatible"
};
var passwordBox = new GUITickBox(columnRT(ColumnLabel.ServerListHasPassword, scale: 0.6f), label: "", style: "GUIServerListPasswordTickBox")
{
Selected = serverInfo.HasPassword,
@@ -1664,9 +1825,10 @@ namespace Barotrauma
serverPingText.TextColor = Color.DarkRed;
}
LocalizedString toolTip = "";
if (!serverInfo.Checked)
{
errorTooltip(TextManager.Get("ServerOffline"));
toolTip = TextManager.Get("ServerOffline");
serverName.TextColor *= 0.8f;
serverPlayers.TextColor *= 0.8f;
}
@@ -1681,7 +1843,6 @@ namespace Barotrauma
}
else if (!compatibleBox.Selected)
{
LocalizedString toolTip = "";
if (serverInfo.GameVersion != GameMain.Version)
{
toolTip = TextManager.GetWithVariable("ServerListIncompatibleVersion", "[version]", serverInfo.GameVersion.ToString());
@@ -1707,14 +1868,12 @@ namespace Barotrauma
toolTip += '\n' + TextManager.GetWithVariable("workshopitemdownloadprompttruncated", "[number]", (incompatibleModNames.Count - maxIncompatibleToList).ToString());
}
}
errorTooltip(toolTip);
serverName.TextColor *= 0.5f;
serverPlayers.TextColor *= 0.5f;
}
else
{
LocalizedString toolTip = "";
foreach (var contentPackage in serverInfo.ContentPackages)
{
if (ContentPackageManager.EnabledPackages.All.None(cp => cp.Hash.StringRepresentation == contentPackage.Hash))
@@ -1724,8 +1883,11 @@ namespace Barotrauma
break;
}
}
errorTooltip(toolTip);
}
disableElementFocus();
string separator = toolTip.IsNullOrWhiteSpace() ? "" : "\n\n";
serverFrame.ToolTip = RichString.Rich(toolTip + separator + $"‖color:gui.blue‖{TextManager.GetWithVariable("serverlisttooltip", "[button]", PlayerInput.SecondaryMouseLabel)}‖end‖");
foreach (var section in sections.Values)
{

View File

@@ -16,9 +16,6 @@ namespace Barotrauma
{
class SubEditorScreen : EditorScreen
{
public const string CircuitBoxDeletionWarningHeader = "Selection contains circuit boxes",
CircuitBoxDeletionWarningBody = "Are you sure you want to delete the selection? Any wiring inside circuit boxes will be lost and cannot be recovered.";
public const int MaxStructures = 2000;
public const int MaxWalls = 500;
public const int MaxItems = 5000;
@@ -1290,7 +1287,8 @@ namespace Barotrauma
if (legacy) { textBlock.TextColor *= 0.6f; }
if (name.IsNullOrEmpty())
{
DebugConsole.AddWarning($"Entity \"{ep.Identifier.Value}\" has no name!");
DebugConsole.AddWarning($"Entity \"{ep.Identifier.Value}\" has no name!",
contentPackage: ep.ContentPackage);
textBlock.Text = frame.ToolTip = ep.Identifier.Value;
textBlock.TextColor = GUIStyle.Red;
}
@@ -1562,8 +1560,17 @@ namespace Barotrauma
if (editorSelectedTime.TryUnwrap(out DateTime selectedTime))
{
TimeSpan timeInEditor = DateTime.Now - selectedTime;
SteamAchievementManager.IncrementStat("hoursineditor".ToIdentifier(), (float)timeInEditor.TotalHours);
editorSelectedTime = Option<DateTime>.None();
if (timeInEditor.TotalSeconds > Timing.TotalTime)
{
DebugConsole.ThrowErrorAndLogToGA(
"SubEditorScreen.DeselectEditorSpecific:InvalidTimeInEditor",
$"Error in sub editor screen. Calculated time in editor {timeInEditor} was larger than the time the game has run ({Timing.TotalTime} s).");
}
else
{
SteamAchievementManager.IncrementStat("hoursineditor".ToIdentifier(), (float)timeInEditor.TotalHours);
editorSelectedTime = Option<DateTime>.None();
}
}
#endif
@@ -2368,49 +2375,58 @@ namespace Barotrauma
//---------------------------------------
var beaconSettingsContainer = new GUILayoutGroup(new RectTransform(Vector2.One, subTypeDependentSettingFrame.RectTransform))
var extraSettingsContainer = new GUILayoutGroup(new RectTransform(new Vector2(1, 0.5f), subTypeDependentSettingFrame.RectTransform))
{
CanBeFocused = true,
Visible = false,
Stretch = true
};
// -------------------
var beaconMinDifficultyGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), beaconSettingsContainer.RectTransform), isHorizontal: true)
var minDifficultyGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), extraSettingsContainer.RectTransform), isHorizontal: true)
{
Stretch = true
};
new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), beaconMinDifficultyGroup.RectTransform),
new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), minDifficultyGroup.RectTransform),
TextManager.Get("minleveldifficulty"), textAlignment: Alignment.CenterLeft, wrap: true);
var numInput = new GUINumberInput(new RectTransform(new Vector2(0.4f, 1.0f), beaconMinDifficultyGroup.RectTransform), NumberType.Int)
var numInput = new GUINumberInput(new RectTransform(new Vector2(0.4f, 1.0f), minDifficultyGroup.RectTransform), NumberType.Int)
{
IntValue = (int)(MainSub?.Info?.BeaconStationInfo?.MinLevelDifficulty ?? 0),
IntValue = (int)(MainSub?.Info?.GetExtraSubmarineInfo?.MinLevelDifficulty ?? 0),
MinValueInt = 0,
MaxValueInt = 100,
OnValueChanged = (numberInput) =>
{
MainSub.Info.BeaconStationInfo.MinLevelDifficulty = numberInput.IntValue;
MainSub.Info.GetExtraSubmarineInfo.MinLevelDifficulty = numberInput.IntValue;
}
};
beaconMinDifficultyGroup.RectTransform.MaxSize = numInput.TextBox.RectTransform.MaxSize;
var beaconMaxDifficultyGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), beaconSettingsContainer.RectTransform), isHorizontal: true)
minDifficultyGroup.RectTransform.MaxSize = numInput.TextBox.RectTransform.MaxSize;
var maxDifficultyGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), extraSettingsContainer.RectTransform), isHorizontal: true)
{
Stretch = true
};
new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), beaconMaxDifficultyGroup.RectTransform),
new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), maxDifficultyGroup.RectTransform),
TextManager.Get("maxleveldifficulty"), textAlignment: Alignment.CenterLeft, wrap: true);
numInput = new GUINumberInput(new RectTransform(new Vector2(0.4f, 1.0f), beaconMaxDifficultyGroup.RectTransform), NumberType.Int)
numInput = new GUINumberInput(new RectTransform(new Vector2(0.4f, 1.0f), maxDifficultyGroup.RectTransform), NumberType.Int)
{
IntValue = (int)(MainSub?.Info?.BeaconStationInfo?.MaxLevelDifficulty ?? 100),
IntValue = (int)(MainSub?.Info?.GetExtraSubmarineInfo?.MaxLevelDifficulty ?? 100),
MinValueInt = 0,
MaxValueInt = 100,
OnValueChanged = (numberInput) =>
{
MainSub.Info.BeaconStationInfo.MaxLevelDifficulty = numberInput.IntValue;
MainSub.Info.GetExtraSubmarineInfo.MaxLevelDifficulty = numberInput.IntValue;
}
};
beaconMaxDifficultyGroup.RectTransform.MaxSize = numInput.TextBox.RectTransform.MaxSize;
maxDifficultyGroup.RectTransform.MaxSize = numInput.TextBox.RectTransform.MaxSize;
//---------------------------------------
var beaconSettingsContainer = new GUILayoutGroup(new RectTransform(Vector2.One, extraSettingsContainer.RectTransform))
{
CanBeFocused = true,
Visible = false,
Stretch = true
};
new GUITickBox(new RectTransform(new Vector2(1.0f, 0.25f), beaconSettingsContainer.RectTransform), TextManager.Get("allowdamagedwalls"))
{
Selected = MainSub?.Info?.BeaconStationInfo?.AllowDamagedWalls ?? true,
@@ -2672,8 +2688,13 @@ namespace Barotrauma
{
MainSub.Info.BeaconStationInfo ??= new BeaconStationInfo(MainSub.Info);
}
else if (type == SubmarineType.Wreck)
{
MainSub.Info.WreckInfo ??= new WreckInfo(MainSub.Info);
}
previewImageButtonHolder.Children.ForEach(c => c.Enabled = MainSub.Info.AllowPreviewImage);
outpostSettingsContainer.Visible = type == SubmarineType.OutpostModule;
extraSettingsContainer.Visible = type == SubmarineType.BeaconStation || type == SubmarineType.Wreck;
beaconSettingsContainer.Visible = type == SubmarineType.BeaconStation;
subSettingsContainer.Visible = type == SubmarineType.Player;
return true;
@@ -3921,28 +3942,15 @@ namespace Barotrauma
new ContextMenuOption("editor.cut", isEnabled: hasTargets, onSelected: () => MapEntity.Cut(targets)),
new ContextMenuOption("editor.copytoclipboard", isEnabled: hasTargets, onSelected: () => MapEntity.Copy(targets)),
new ContextMenuOption("editor.paste", isEnabled: MapEntity.CopiedList.Any(), onSelected: () => MapEntity.Paste(cam.ScreenToWorld(PlayerInput.MousePosition))),
new ContextMenuOption("delete", isEnabled: hasTargets, onSelected: () => RemoveEntitiesWithPossibleWarning(targets)),
new ContextMenuOption(TextManager.Get("editortip.shiftforextraoptions") + '\n' + TextManager.Get("editortip.altforruler"), isEnabled: false, onSelected: null));
}
}
public static void RemoveEntitiesWithPossibleWarning(List<MapEntity> targets)
{
if (targets.Any(static t => t is Item it && it.GetComponent<CircuitBox>() is not null))
{
GUI.AskForConfirmation(CircuitBoxDeletionWarningHeader, CircuitBoxDeletionWarningBody, onConfirm: Delete);
return;
}
Delete();
void Delete()
{
StoreCommand(new AddOrDeleteCommand(targets, true));
foreach (var me in targets)
{
if (!me.Removed) { me.Remove(); }
}
new ContextMenuOption("delete", isEnabled: hasTargets, onSelected: () =>
{
StoreCommand(new AddOrDeleteCommand(targets, true));
foreach (var me in targets)
{
if (!me.Removed) { me.Remove(); }
}
}),
new ContextMenuOption(TextManager.GetWithVariable("editortip.shiftforextraoptions", "[button]", PlayerInput.SecondaryMouseLabel) + '\n' + TextManager.Get("editortip.altforruler"), isEnabled: false, onSelected: null));
}
}
@@ -4442,6 +4450,7 @@ namespace Barotrauma
MapEntity.SelectEntity(itemContainer);
dummyCharacter.SelectedItem = itemContainer;
FilterEntities(entityFilterBox.Text);
MapEntity.StopSelection();
}
/// <summary>
@@ -5472,9 +5481,11 @@ namespace Barotrauma
{
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.Color =
item.body == null || item.body.Enabled ||
(item.ParentInventory is ItemInventory itemInventory && !itemInventory.Container.HideItems) ?
lightComponent.LightColor :
Color.Transparent;
lightComponent.Light.LightSpriteEffect = lightComponent.Item.SpriteEffects;
}
}
@@ -5559,11 +5570,32 @@ namespace Barotrauma
dummyCharacter.Submarine = MainSub;
}
// Deposit item from our "infinite stack" into inventory slots
var inv = dummyCharacter?.SelectedItem?.OwnInventory;
if (inv?.visualSlots != null && !PlayerInput.IsCtrlDown())
if (dummyCharacter?.SelectedItem != null)
{
var dragginMouse = MouseDragStart != Vector2.Zero && Vector2.Distance(PlayerInput.MousePosition, MouseDragStart) >= GUI.Scale * 20;
// Deposit item from our "infinite stack" into inventory slots
TryDragItemsToItem(dummyCharacter.SelectedItem);
foreach (Item linkedItem in dummyCharacter.SelectedItem.linkedTo.OfType<Item>())
{
TryDragItemsToItem(linkedItem);
}
}
void TryDragItemsToItem(Item item)
{
foreach (ItemContainer ic in item.GetComponents<ItemContainer>())
{
if (ic.Inventory?.visualSlots != null)
{
TryDragItemsToInventory(ic.Inventory);
}
}
}
void TryDragItemsToInventory(Inventory inv)
{
if (PlayerInput.IsCtrlDown()) { return; }
var draggingMouse = MouseDragStart != Vector2.Zero && Vector2.Distance(PlayerInput.MousePosition, MouseDragStart) >= GUI.Scale * 20;
// So we don't accidentally drag inventory items while doing this
if (DraggedItemPrefab != null) { Inventory.DraggingItems.Clear(); }
@@ -5571,134 +5603,134 @@ namespace Barotrauma
switch (DraggedItemPrefab)
{
// regular item prefabs
case ItemPrefab itemPrefab when PlayerInput.PrimaryMouseButtonClicked() || dragginMouse:
{
bool spawnedItem = false;
for (var i = 0; i < inv.Capacity; i++)
case ItemPrefab itemPrefab when PlayerInput.PrimaryMouseButtonClicked() || draggingMouse:
{
var slot = inv.visualSlots[i];
var itemContainer = inv.GetItemAt(i)?.GetComponent<ItemContainer>();
// check if the slot is empty or if we can place the item into a container, for example an oxygen tank into a diving suit
if (Inventory.IsMouseOnSlot(slot))
bool spawnedItem = false;
for (var i = 0; i < inv.Capacity; i++)
{
var newItem = new Item(itemPrefab, Vector2.Zero, MainSub);
var slot = inv.visualSlots[i];
var itemContainer = inv.GetItemAt(i)?.GetComponent<ItemContainer>();
if (inv.CanBePutInSlot(itemPrefab, i, condition: null))
// check if the slot is empty or if we can place the item into a container, for example an oxygen tank into a diving suit
if (Inventory.IsMouseOnSlot(slot))
{
bool placedItem = inv.TryPutItem(newItem, i, false, true, dummyCharacter);
spawnedItem |= placedItem;
var newItem = new Item(itemPrefab, Vector2.Zero, MainSub);
if (!placedItem)
if (inv.CanBePutInSlot(itemPrefab, i, condition: null))
{
newItem.Remove();
bool placedItem = inv.TryPutItem(newItem, i, false, true, dummyCharacter);
spawnedItem |= placedItem;
if (!placedItem)
{
newItem.Remove();
}
}
}
else if (itemContainer != null && itemContainer.Inventory.CanBePut(itemPrefab))
{
bool placedItem = itemContainer.Inventory.TryPutItem(newItem, dummyCharacter);
spawnedItem |= placedItem;
// try to place the item into the inventory of the item we are hovering over
if (!placedItem)
else if (itemContainer != null && itemContainer.Inventory.CanBePut(itemPrefab))
{
newItem.Remove();
bool placedItem = itemContainer.Inventory.TryPutItem(newItem, dummyCharacter);
spawnedItem |= placedItem;
// try to place the item into the inventory of the item we are hovering over
if (!placedItem)
{
newItem.Remove();
}
else
{
slot.ShowBorderHighlight(GUIStyle.Green, 0.1f, 0.4f);
}
}
else
{
slot.ShowBorderHighlight(GUIStyle.Green, 0.1f, 0.4f);
newItem.Remove();
slot.ShowBorderHighlight(GUIStyle.Red, 0.1f, 0.4f);
}
if (!newItem.Removed)
{
BulkItemBufferInUse = ItemAddMutex;
BulkItemBuffer.Add(new AddOrDeleteCommand(new List<MapEntity> { newItem }, false));
}
if (!draggingMouse)
{
SoundPlayer.PlayUISound(spawnedItem ? GUISoundType.PickItem : GUISoundType.PickItemFail);
}
}
else
{
newItem.Remove();
slot.ShowBorderHighlight(GUIStyle.Red, 0.1f, 0.4f);
}
if (!newItem.Removed)
{
BulkItemBufferInUse = ItemAddMutex;
BulkItemBuffer.Add(new AddOrDeleteCommand(new List<MapEntity> { newItem }, false));
}
if (!dragginMouse)
{
SoundPlayer.PlayUISound(spawnedItem ? GUISoundType.PickItem : GUISoundType.PickItemFail);
}
}
break;
}
break;
}
// item assemblies
case ItemAssemblyPrefab assemblyPrefab when PlayerInput.PrimaryMouseButtonClicked():
{
bool spawnedItems = false;
for (var i = 0; i < inv.visualSlots.Length; i++)
{
var slot = inv.visualSlots[i];
var item = inv?.GetItemAt(i);
var itemContainer = item?.GetComponent<ItemContainer>();
if (item == null && Inventory.IsMouseOnSlot(slot))
bool spawnedItems = false;
for (var i = 0; i < inv.visualSlots.Length; i++)
{
// load the items
var itemInstance = LoadItemAssemblyInventorySafe(assemblyPrefab);
// counter for items that failed so we so we known that slot remained empty
var failedCount = 0;
for (var j = 0; j < itemInstance.Count(); j++)
var slot = inv.visualSlots[i];
var item = inv?.GetItemAt(i);
var itemContainer = item?.GetComponent<ItemContainer>();
if (item == null && Inventory.IsMouseOnSlot(slot))
{
var newItem = itemInstance[j];
var newSpot = i + j - failedCount;
// load the items
var itemInstance = LoadItemAssemblyInventorySafe(assemblyPrefab);
// try to find a valid slot to put the items
while (inv.visualSlots.Length > newSpot)
// counter for items that failed so we so we known that slot remained empty
var failedCount = 0;
for (var j = 0; j < itemInstance.Count; j++)
{
if (inv.GetItemAt(newSpot) == null) { break; }
newSpot++;
}
var newItem = itemInstance[j];
var newSpot = i + j - failedCount;
// valid slot found
if (inv.visualSlots.Length > newSpot)
{
var placedItem = inv.TryPutItem(newItem, newSpot, false, true, dummyCharacter);
spawnedItems |= placedItem;
if (!placedItem)
// try to find a valid slot to put the items
while (inv.visualSlots.Length > newSpot)
{
failedCount++;
// delete the included items too so we don't get a popup asking if we want to keep them
newItem?.OwnInventory?.DeleteAllItems();
newItem.Remove();
if (inv.GetItemAt(newSpot) == null) { break; }
newSpot++;
}
// valid slot found
if (inv.visualSlots.Length > newSpot)
{
var placedItem = inv.TryPutItem(newItem, newSpot, false, true, dummyCharacter);
spawnedItems |= placedItem;
if (!placedItem)
{
failedCount++;
// delete the included items too so we don't get a popup asking if we want to keep them
newItem?.OwnInventory?.DeleteAllItems();
newItem.Remove();
}
}
else
{
var placedItem = inv.TryPutItem(newItem, dummyCharacter);
spawnedItems |= placedItem;
// if our while loop didn't find a valid slot then let the inventory decide where to put it as a last resort
if (!placedItem)
{
// delete the included items too so we don't get a popup asking if we want to keep them
newItem?.OwnInventory?.DeleteAllItems();
newItem.Remove();
}
}
}
else
List<MapEntity> placedEntities = itemInstance.Where(it => !it.Removed).Cast<MapEntity>().ToList();
if (placedEntities.Any())
{
var placedItem = inv.TryPutItem(newItem, dummyCharacter);
spawnedItems |= placedItem;
// if our while loop didn't find a valid slot then let the inventory decide where to put it as a last resort
if (!placedItem)
{
// delete the included items too so we don't get a popup asking if we want to keep them
newItem?.OwnInventory?.DeleteAllItems();
newItem.Remove();
}
BulkItemBufferInUse = ItemAddMutex;
BulkItemBuffer.Add(new AddOrDeleteCommand(placedEntities, false));
}
}
List<MapEntity> placedEntities = itemInstance.Where(it => !it.Removed).Cast<MapEntity>().ToList();
if (placedEntities.Any())
{
BulkItemBufferInUse = ItemAddMutex;
BulkItemBuffer.Add(new AddOrDeleteCommand(placedEntities, false));
}
}
}
SoundPlayer.PlayUISound(spawnedItems ? GUISoundType.PickItem : GUISoundType.PickItemFail);
break;
}
SoundPlayer.PlayUISound(spawnedItems ? GUISoundType.PickItem : GUISoundType.PickItemFail);
break;
}
}
}

View File

@@ -571,16 +571,37 @@ namespace Barotrauma
numberInput.MinValueFloat = editableAttribute.MinValueFloat;
numberInput.MaxValueFloat = editableAttribute.MaxValueFloat;
numberInput.DecimalsToDisplay = editableAttribute.DecimalCount;
numberInput.valueStep = editableAttribute.ValueStep;
numberInput.ValueStep = editableAttribute.ValueStep;
numberInput.ForceShowPlusMinusButtons = editableAttribute.ForceShowPlusMinusButtons;
numberInput.FloatValue = value;
numberInput.OnValueChanged += (numInput) =>
numberInput.OnValueChanged += numInput =>
{
if (SetPropertyValue(property, entity, numInput.FloatValue))
{
TrySendNetworkUpdate(entity, property);
}
};
// Lots of UI boilerplate to handle all(?) cases where the property's setter may be called
// and modify the input value (e.g. rotation value wrapping)
void HandleSetterModifyingInput(GUINumberInput numInput)
{
var inputFloatValue = numInput.FloatValue;
var resultingFloatValue = property.GetFloatValue(entity);
if (!MathUtils.NearlyEqual(resultingFloatValue, inputFloatValue))
{
numInput.FloatValue = resultingFloatValue;
}
}
bool HandleSetterModifyingInputOnButtonPressed() { HandleSetterModifyingInput(numberInput); return true; }
bool HandleSetterModifyingInputOnButtonClicked(GUIButton _, object __) { HandleSetterModifyingInput(numberInput); return true; }
numberInput.OnValueEntered += HandleSetterModifyingInput;
numberInput.PlusButton.OnPressed += HandleSetterModifyingInputOnButtonPressed;
numberInput.PlusButton.OnClicked += HandleSetterModifyingInputOnButtonClicked;
numberInput.MinusButton.OnPressed += HandleSetterModifyingInputOnButtonPressed;
numberInput.MinusButton.OnClicked += HandleSetterModifyingInputOnButtonClicked;
refresh += () =>
{
if (!numberInput.TextBox.Selected) { numberInput.FloatValue = (float)property.GetValue(entity); }
@@ -859,7 +880,7 @@ namespace Barotrauma
numberInput.MinValueFloat = editableAttribute.MinValueFloat;
numberInput.MaxValueFloat = editableAttribute.MaxValueFloat;
numberInput.DecimalsToDisplay = editableAttribute.DecimalCount;
numberInput.valueStep = editableAttribute.ValueStep;
numberInput.ValueStep = editableAttribute.ValueStep;
if (i == 0)
numberInput.FloatValue = value.X;
@@ -930,7 +951,7 @@ namespace Barotrauma
numberInput.MinValueFloat = editableAttribute.MinValueFloat;
numberInput.MaxValueFloat = editableAttribute.MaxValueFloat;
numberInput.DecimalsToDisplay = editableAttribute.DecimalCount;
numberInput.valueStep = editableAttribute.ValueStep;
numberInput.ValueStep = editableAttribute.ValueStep;
if (i == 0)
numberInput.FloatValue = value.X;
@@ -1006,7 +1027,7 @@ namespace Barotrauma
numberInput.MinValueFloat = editableAttribute.MinValueFloat;
numberInput.MaxValueFloat = editableAttribute.MaxValueFloat;
numberInput.DecimalsToDisplay = editableAttribute.DecimalCount;
numberInput.valueStep = editableAttribute.ValueStep;
numberInput.ValueStep = editableAttribute.ValueStep;
if (i == 0)
numberInput.FloatValue = value.X;

View File

@@ -727,7 +727,21 @@ namespace Barotrauma
Slider(layout, (0.75f, 1.25f), 51, Percentage, unsavedConfig.Graphics.InventoryScale, v => unsavedConfig.Graphics.InventoryScale = v);
Label(layout, TextManager.Get("TextScale"), GUIStyle.SubHeadingFont);
Slider(layout, (0.75f, 1.25f), 51, Percentage, unsavedConfig.Graphics.TextScale, v => unsavedConfig.Graphics.TextScale = v);
Spacer(layout);
var resetSpamListFilter =
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), layout.RectTransform),
TextManager.Get("clearserverlistfilters"), style: "GUIButtonSmall")
{
OnClicked = static (_, _) =>
{
GUI.AskForConfirmation(
header: TextManager.Get("clearserverlistfilters"),
body: TextManager.Get("clearserverlistfiltersconfirmation"),
onConfirm: SpamServerFilters.ClearLocalSpamFilter);
return true;
}
};
Spacer(layout);
#if !OSX
Spacer(layout);
var statisticsTickBox = new GUITickBox(NewItemRectT(layout), TextManager.Get("statisticsconsenttickbox"))

View File

@@ -1,22 +1,23 @@
using System;
using NVorbis;
using OpenAL;
using NVorbis;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace Barotrauma.Sounds
{
sealed class OggSound : Sound
{
private VorbisReader streamReader;
private readonly VorbisReader streamReader;
public long MaxStreamSamplePos => streamReader == null ? 0 : streamReader.TotalSamples * streamReader.Channels * 2;
private List<float> playbackAmplitude;
private const int AMPLITUDE_SAMPLE_COUNT = 4410; //100ms in a 44100hz file
private short[] sampleBuffer = Array.Empty<short>();
private short[] muffleBuffer = Array.Empty<short>();
public OggSound(SoundManager owner, string filename, bool stream, XElement xElement) : base(owner, filename,
public OggSound(SoundManager owner, string filename, bool stream, ContentXElement xElement) : base(owner, filename,
stream, true, xElement)
{
var reader = new VorbisReader(Filename);
@@ -101,7 +102,7 @@ namespace Barotrauma.Sounds
if (!Stream) { throw new Exception("Called FillStreamBuffer on a non-streamed sound!"); }
if (streamReader == null) { throw new Exception("Called FillStreamBuffer when the reader is null!"); }
if (samplePos >= streamReader.TotalSamples * streamReader.Channels * 2) return 0;
if (samplePos >= MaxStreamSamplePos) { return 0; }
samplePos /= streamReader.Channels * 2;
streamReader.DecodedPosition = samplePos;

View File

@@ -18,7 +18,7 @@ namespace Barotrauma.Sounds
public readonly string Filename;
public readonly XElement XElement;
public readonly ContentXElement XElement;
public readonly bool Stream;
@@ -60,14 +60,14 @@ namespace Barotrauma.Sounds
public float BaseNear;
public float BaseFar;
public Sound(SoundManager owner, string filename, bool stream, bool streamsReliably, XElement xElement = null, bool getFullPath = true)
public Sound(SoundManager owner, string filename, bool stream, bool streamsReliably, ContentXElement xElement = null, bool getFullPath = true)
{
Owner = owner;
Filename = getFullPath ? Path.GetFullPath(filename.CleanUpPath()).CleanUpPath() : filename;
Stream = stream;
StreamsReliably = streamsReliably;
XElement = xElement;
sourcePoolIndex = XElement.GetAttributeEnum("sourcepool", SoundManager.SourcePoolIndex.Default);
sourcePoolIndex = XElement?.GetAttributeEnum("sourcepool", SoundManager.SourcePoolIndex.Default) ?? SoundManager.SourcePoolIndex.Default;
BaseGain = 1.0f;
BaseNear = 100.0f;

View File

@@ -444,6 +444,18 @@ namespace Barotrauma.Sounds
}
}
public long MaxStreamSeekPos
{
get
{
if (!IsStream || Sound is not OggSound oggSound)
{
return 0;
}
return oggSound.MaxStreamSamplePos;
}
}
private readonly object mutex;
public bool IsPlaying
@@ -564,7 +576,7 @@ namespace Barotrauma.Sounds
throw new Exception("Generated streamBuffer[" + i.ToString() + "] is invalid! " + debugName);
}
}
Sound.Owner.InitStreamThread();
Sound.Owner.InitUpdateChannelThread();
SetProperties();
}
}
@@ -609,6 +621,7 @@ namespace Barotrauma.Sounds
public void FadeOutAndDispose()
{
FadingOutAndDisposing = true;
Sound.Owner.InitUpdateChannelThread();
}
public void Dispose()

View File

@@ -39,7 +39,7 @@ namespace Barotrauma.Sounds
public bool Disconnected { get; private set; }
private Thread streamingThread;
private Thread updateChannelsThread;
private Vector3 listenerPosition;
public Vector3 ListenerPosition
@@ -201,7 +201,7 @@ namespace Barotrauma.Sounds
public SoundManager()
{
loadedSounds = new List<Sound>();
streamingThread = null;
updateChannelsThread = null;
sourcePools = new SoundSourcePool[2];
playingChannels[(int)SourcePoolIndex.Default] = new SoundChannel[SOURCE_COUNT];
@@ -696,7 +696,7 @@ namespace Barotrauma.Sounds
CompressionDynamicRangeGain = 1.0f;
}
if (streamingThread == null || streamingThread.ThreadState.HasFlag(ThreadState.Stopped))
if (updateChannelsThread == null || updateChannelsThread.ThreadState.HasFlag(ThreadState.Stopped))
{
bool startedStreamThread = false;
for (int i = 0; i < playingChannels.Length; i++)
@@ -708,7 +708,7 @@ namespace Barotrauma.Sounds
if (playingChannels[i][j] == null) { continue; }
if (playingChannels[i][j].IsStream && playingChannels[i][j].IsPlaying)
{
InitStreamThread();
InitUpdateChannelThread();
startedStreamThread = true;
}
if (startedStreamThread) { break; }
@@ -727,37 +727,43 @@ namespace Barotrauma.Sounds
SetCategoryGainMultiplier("music", GameSettings.CurrentConfig.Audio.MusicVolume, 0);
SetCategoryGainMultiplier("voip", Math.Min(GameSettings.CurrentConfig.Audio.VoiceChatVolume, 1.0f), 0);
}
public void InitStreamThread()
/// <summary>
/// Initializes the thread that handles streaming audio and fading out and disposing channels that are no longer needed.
/// </summary>
public void InitUpdateChannelThread()
{
if (Disabled) { return; }
bool isStreamThreadDying;
bool isUpdateChannelsThreadDying;
lock (threadDeathMutex)
{
isStreamThreadDying = !areStreamsPlaying;
isUpdateChannelsThreadDying = !needsUpdateChannels;
}
if (streamingThread == null || streamingThread.ThreadState.HasFlag(ThreadState.Stopped) || isStreamThreadDying)
if (updateChannelsThread == null || updateChannelsThread.ThreadState.HasFlag(ThreadState.Stopped) || isUpdateChannelsThreadDying)
{
if (streamingThread != null && !streamingThread.Join(1000))
if (updateChannelsThread != null && !updateChannelsThread.Join(1000))
{
DebugConsole.ThrowError("Sound stream thread join timed out!");
DebugConsole.ThrowError("SoundManager.UpdateChannels thread join timed out!");
}
areStreamsPlaying = true;
streamingThread = new Thread(UpdateStreaming)
needsUpdateChannels = true;
updateChannelsThread = new Thread(UpdateChannels)
{
Name = "SoundManager Streaming Thread",
Name = "SoundManager.UpdateChannels Thread",
IsBackground = true //this should kill the thread if the game crashes
};
streamingThread.Start();
updateChannelsThread.Start();
}
}
bool areStreamsPlaying = false;
ManualResetEvent streamMre = null;
private bool needsUpdateChannels = false;
private ManualResetEvent updateChannelsMre = null;
void UpdateStreaming()
/// <summary>
/// Handles streaming audio and fading out and disposing channels that are no longer needed.
/// </summary>
private void UpdateChannels()
{
streamMre = new ManualResetEvent(false);
updateChannelsMre = new ManualResetEvent(false);
bool killThread = false;
while (!killThread)
{
@@ -784,6 +790,7 @@ namespace Barotrauma.Sounds
}
else if (playingChannels[i][j].FadingOutAndDisposing)
{
killThread = false;
playingChannels[i][j].Gain -= 0.1f;
if (playingChannels[i][j].Gain <= 0.0f)
{
@@ -794,18 +801,18 @@ namespace Barotrauma.Sounds
}
}
}
streamMre.WaitOne(10);
streamMre.Reset();
updateChannelsMre.WaitOne(10);
updateChannelsMre.Reset();
lock (threadDeathMutex)
{
areStreamsPlaying = !killThread;
needsUpdateChannels = !killThread;
}
}
}
public void ForceStreamUpdate()
{
streamMre?.Set();
updateChannelsMre?.Set();
}
private void ReloadSounds()
@@ -824,12 +831,12 @@ namespace Barotrauma.Sounds
{
for (int j = 0; j < playingChannels[i].Length; j++)
{
if (playingChannels[i][j] != null) playingChannels[i][j].Dispose();
playingChannels[i][j]?.Dispose();
}
}
}
streamingThread?.Join();
updateChannelsThread?.Join();
for (int i = loadedSounds.Count - 1; i >= 0; i--)
{
if (keepSounds)

View File

@@ -709,6 +709,11 @@ namespace Barotrauma
{
musicChannel[i].StreamSeekPos = targetMusic[i].PreviousTime;
}
else if (targetMusic[i].StartFromRandomTime)
{
musicChannel[i].StreamSeekPos =
(int)(musicChannel[i].MaxStreamSeekPos * Rand.Range(0.0f, 1.0f, Rand.RandSync.Unsynced));
}
musicChannel[i].Looping = true;
}
}

View File

@@ -241,6 +241,7 @@ namespace Barotrauma
public readonly bool MuteIntensityTracks;
public readonly float? ForceIntensityTrack;
public readonly bool StartFromRandomTime;
public readonly bool ContinueFromPreviousTime;
public int PreviousTime;
@@ -255,6 +256,7 @@ namespace Barotrauma
ForceIntensityTrack = element.GetAttributeFloat(nameof(ForceIntensityTrack), 0.0f);
}
Volume = element.GetAttributeFloat(nameof(Volume), 1.0f);
StartFromRandomTime = element.GetAttributeBool(nameof(StartFromRandomTime), false);
ContinueFromPreviousTime = element.GetAttributeBool(nameof(ContinueFromPreviousTime), false);
}
}

View File

@@ -0,0 +1,330 @@
#nullable enable
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Net;
using System.Net.Cache;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using Barotrauma.IO;
using Barotrauma.Networking;
using RestSharp;
using XmlWriter = Barotrauma.IO.XmlWriter;
namespace Barotrauma
{
public enum SpamServerFilterType
{
Invalid,
NameEquals,
NameContains,
MessageEquals,
MessageContains,
PlayerCountLarger,
PlayerCountExact,
MaxPlayersLarger,
MaxPlayersExact,
GameModeEquals,
PlayStyleEquals,
Endpoint,
LanguageEquals
}
internal readonly record struct SpamFilter(ImmutableHashSet<(SpamServerFilterType Type, string Value)> Filters)
{
public bool IsFiltered(ServerInfo info)
{
if (!Filters.Any()) { return false; }
foreach (var (type, value) in Filters)
{
if (!IsFiltered(info, type, value)) { return false; }
}
return true;
}
private static bool IsFiltered(ServerInfo info, SpamServerFilterType type, string value)
{
string desc = info.ServerMessage,
name = info.ServerName;
int.TryParse(value, out int parsedInt);
return type switch
{
SpamServerFilterType.NameEquals => CompareEquals(name, value),
SpamServerFilterType.NameContains => CompareContains(name, value),
SpamServerFilterType.MessageEquals => CompareEquals(desc, value),
SpamServerFilterType.MessageContains => CompareContains(desc, value),
SpamServerFilterType.Endpoint => info.Endpoint.StringRepresentation.Equals(value, StringComparison.OrdinalIgnoreCase),
SpamServerFilterType.PlayerCountLarger => info.PlayerCount > parsedInt,
SpamServerFilterType.PlayerCountExact => info.PlayerCount == parsedInt,
SpamServerFilterType.MaxPlayersLarger => info.MaxPlayers > parsedInt,
SpamServerFilterType.MaxPlayersExact => info.MaxPlayers == parsedInt,
SpamServerFilterType.GameModeEquals => info.GameMode == value,
SpamServerFilterType.PlayStyleEquals => info.PlayStyle.ToIdentifier() == value,
SpamServerFilterType.LanguageEquals => info.Language.Value == value,
_ => false
};
static bool CompareEquals(string a, string b)
=> a.Equals(b, StringComparison.OrdinalIgnoreCase) || Homoglyphs.Compare(a, b);
static bool CompareContains(string a, string b)
=> a.Contains(b, StringComparison.OrdinalIgnoreCase);
}
public XElement Serialize()
{
var element = new XElement("Filter");
foreach (var (type, value) in Filters)
{
element.Add(new XAttribute(type.ToString().ToLowerInvariant(), value));
}
return element;
}
public static bool TryParse(XElement element, out SpamFilter filter)
{
var builder = ImmutableHashSet.CreateBuilder<(SpamServerFilterType Type, string Value)>();
foreach (var attribute in element.Attributes())
{
if (!Enum.TryParse(attribute.Name.ToString(), ignoreCase: true, out SpamServerFilterType e))
{
DebugConsole.ThrowError($"Failed to parse spam filter attribute \"{attribute.Name}\"");
continue;
}
if (e is SpamServerFilterType.Invalid) { continue; }
builder.Add((e, attribute.Value));
}
if (builder.Any())
{
filter = new SpamFilter(builder.ToImmutable());
return true;
}
filter = default;
return false;
}
public override string ToString()
{
return !Filters.Any() ? "Invalid Filter" : string.Join(", ", Filters.Select(static f => $"{f.Type}: {f.Value}"));
}
}
internal sealed class SpamServerFilter
{
public readonly ImmutableArray<SpamFilter> Filters;
public bool IsFiltered(ServerInfo info)
{
foreach (var f in Filters)
{
if (f.IsFiltered(info)) { return true; }
}
return false;
}
public SpamServerFilter(XElement element)
{
var builder = ImmutableArray.CreateBuilder<SpamFilter>();
foreach (var subElement in element.Elements())
{
if (SpamFilter.TryParse(subElement, out var filter))
{
builder.Add(filter);
}
}
Filters = builder.ToImmutable();
}
public SpamServerFilter(ImmutableArray<SpamFilter> filters)
=> Filters = filters;
public readonly static string SavePath = Path.Combine("Data", "serverblacklist.xml");
public void Save(string path)
{
var comment = new XComment(SpamServerFilters.LocalFilterComment);
var doc = new XDocument(comment, new XElement("Filters"));
foreach (var filter in Filters)
{
doc.Root?.Add(filter.Serialize());
}
try
{
using var writer = XmlWriter.Create(path, new XmlWriterSettings { Indent = true });
doc.SaveSafe(writer);
}
catch (Exception e)
{
DebugConsole.ThrowError("Saving spam filter failed.", e);
}
}
}
internal static class SpamServerFilters
{
public static Option<SpamServerFilter> LocalSpamFilter;
public static Option<SpamServerFilter> GlobalSpamFilter;
public const string LocalFilterComment = @"
This file contains a list of filters that can be used to hide servers from the server list.
You can add filters by right-clicking a server in the server list and selecting ""Hide server"" or by reporting the server and choosing ""Report and hide server"".
The filters are saved in this file, which you can edit manually if you want to.
The available filter types are:
- NameEquals: The server name must equal the specified value. Homoglyphs are also checked.
- NameContains: The server name must contain the specified value.
- MessageEquals: The server description must equal the specified value. Homoglyphs are also checked.
- MessageContains: The server description must contain the specified value.
- PlayerCountLarger: The player count must be larger than the specified value.
- PlayerCountExact: The player count must match the specified value exactly.
- MaxPlayersLarger: The max player count must be larger than the specified value.
- MaxPlayersExact: The max player count must match the specified value exactly.
- GameModeEquals: The game mode identifier must match the specified value exactly.
- PlayStyleEquals: The play style must match the specified value exactly.
- Endpoint: The server endpoint, which is a Steam ID or an IP address, must match the specified value exactly. Steam ID is in the format of STEAM_X:Y:Z.
- LanguageEquals: The server language must match the specified value exactly.
The filter values are case-insensitive and adding multiple conditions on one filter will require all of them to be met.
Homoglyph comparison is used for NameEquals and MessageEquals filters, which means that it checks whether the words look the same, meaning you can't abuse identical-looking but different symbols to work around the filter. For example ""lmaobox"" and ""lmаobox"" (with a cyrillic a) are considered equal.
Examples:
<Filters>
<Filter namecontains=""discord.gg"" />
<Filter messagecontains=""discord.gg"" />
<Filter nameequals=""get good get lmaobox"" maxplayersexact=""999"" />
</Filters>
These will hide all servers that have a discord.gg link in their name or description and servers with the name ""get good get lmaobox"" that have 999 max players.
";
static SpamServerFilters()
{
XDocument? doc;
if (!File.Exists(SpamServerFilter.SavePath))
{
var comment = new XComment(LocalFilterComment);
doc = new XDocument(comment, new XElement("Filters"));
try
{
using var writer = XmlWriter.Create(SpamServerFilter.SavePath, new XmlWriterSettings { Indent = true });
doc.SaveSafe(writer);
}
catch (Exception e)
{
DebugConsole.ThrowError("Saving spam filter failed.", e);
}
}
else
{
doc = XMLExtensions.TryLoadXml(SpamServerFilter.SavePath);
}
if (doc?.Root is { } root)
{
LocalSpamFilter = Option.Some(new SpamServerFilter(root));
}
}
public static bool IsFiltered(ServerInfo info)
{
if (LocalSpamFilter.TryUnwrap(out var localFilter) && localFilter.IsFiltered(info)) { return true; }
if (GlobalSpamFilter.TryUnwrap(out var globalFilter) && globalFilter.IsFiltered(info)) { return true; }
return false;
}
public static void AddServerToLocalSpamList(ServerInfo info)
{
if (!LocalSpamFilter.TryUnwrap(out var localFilter)) { return; }
if (localFilter.IsFiltered(info)) { return; }
var filters = localFilter.Filters.Add(new SpamFilter(ImmutableHashSet.Create((NameExact: SpamServerFilterType.NameEquals, info.ServerName))));
var newFilter = new SpamServerFilter(filters);
newFilter.Save(SpamServerFilter.SavePath);
LocalSpamFilter = Option.Some(newFilter);
}
public static void ClearLocalSpamFilter()
{
var newFilter = new SpamServerFilter(ImmutableArray<SpamFilter>.Empty);
newFilter.Save(SpamServerFilter.SavePath);
LocalSpamFilter = Option.Some(newFilter);
}
public static void RequestGlobalSpamFilter()
{
if (GameSettings.CurrentConfig.DisableGlobalSpamList) { return; }
string remoteContentUrl = GameSettings.CurrentConfig.RemoteMainMenuContentUrl;
if (string.IsNullOrEmpty(remoteContentUrl)) { return; }
try
{
var client = new RestClient($"{remoteContentUrl}spamfilter")
{
CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore)
};
client.AddDefaultHeader("Cache-Control", "no-cache");
client.AddDefaultHeader("Pragma", "no-cache");
var request = new RestRequest("serve_spamlist.php", Method.GET);
TaskPool.Add("RequestGlobalSpamFilter", client.ExecuteAsync(request), RemoteContentReceived);
}
catch (Exception e)
{
#if DEBUG
DebugConsole.ThrowError("Fetching global spam list failed.", e);
#endif
GameAnalyticsManager.AddErrorEventOnce("SpamServerFilters.RequestGlobalSpamFilter:Exception", GameAnalyticsManager.ErrorSeverity.Error,
"Fetching global spam list failed. " + e.Message);
}
static void RemoteContentReceived(Task t)
{
try
{
if (!t.TryGetResult(out IRestResponse remoteContentResponse)) { throw new Exception("Task did not return a valid result"); }
if (remoteContentResponse.StatusCode != HttpStatusCode.OK)
{
DebugConsole.AddWarning(
"Failed to receive global spam filter." +
"There may be an issue with your internet connection, or the master server might be temporarily unavailable " +
$"(error code: {remoteContentResponse.StatusCode})");
return;
}
string data = remoteContentResponse.Content;
if (string.IsNullOrWhiteSpace(data)) { return; }
if (XDocument.Parse(data).Root is { } root)
{
GlobalSpamFilter = Option.Some(new SpamServerFilter(root));
}
}
catch (Exception e)
{
#if DEBUG
DebugConsole.ThrowError("Reading received global spam filter failed.", e);
#endif
GameAnalyticsManager.AddErrorEventOnce("SpamServerFilters.RemoteContentReceived:Exception", GameAnalyticsManager.ErrorSeverity.Error,
"Reading received global spam filter failed. " + e.Message);
}
}
}
}
}

View File

@@ -111,7 +111,7 @@ namespace Barotrauma
partial void LoadTexture(ref Vector4 sourceVector, ref bool shouldReturn)
{
texture = LoadTexture(FilePath.Value, Compress);
texture = LoadTexture(FilePath.Value, Compress, contentPackage: SourceElement?.ContentPackage);
if (texture == null)
{
@@ -175,7 +175,7 @@ namespace Barotrauma
return;
}
texture.Dispose();
texture = TextureLoader.FromFile(FilePath.Value, Compress);
texture = TextureLoader.FromFile(FilePath.Value, Compress, contentPackage: SourceElement?.ContentPackage);
Identifier pathKey = FullPath.ToIdentifier();
if (textureRefCounts.ContainsKey(pathKey))
{
@@ -195,7 +195,7 @@ namespace Barotrauma
sourceRect = new Rectangle(0, 0, texture.Width, texture.Height);
}
public static Texture2D LoadTexture(string file, bool compress = true)
public static Texture2D LoadTexture(string file, bool compress = true, ContentPackage contentPackage = null)
{
if (string.IsNullOrWhiteSpace(file))
{
@@ -221,11 +221,11 @@ namespace Barotrauma
if (!ToolBox.IsProperFilenameCase(file))
{
#if DEBUG
DebugConsole.ThrowError("Texture file \"" + file + "\" has incorrect case!");
DebugConsole.ThrowError("Texture file \"" + file + "\" has incorrect case!", contentPackage: contentPackage);
#endif
}
Texture2D newTexture = TextureLoader.FromFile(file, compress);
Texture2D newTexture = TextureLoader.FromFile(file, compress, contentPackage: contentPackage);
lock (list)
{
if (!textureRefCounts.TryAdd(fullPath,
@@ -284,17 +284,44 @@ namespace Barotrauma
}
}
public void DrawTiled(ISpriteBatch spriteBatch, Vector2 position, Vector2 targetSize,
Color? color = null, Vector2? startOffset = null, Vector2? textureScale = null, float? depth = null)
public void DrawTiled(ISpriteBatch spriteBatch,
Vector2 position,
Vector2 targetSize,
float rotation = 0f,
Vector2? origin = null,
Color? color = null,
Vector2? startOffset = null,
Vector2? textureScale = null,
float? depth = null,
SpriteEffects? spriteEffects = null)
{
if (Texture == null) { return; }
spriteEffects ??= effects;
bool flipHorizontal = (spriteEffects.Value & SpriteEffects.FlipHorizontally) != 0;
bool flipVertical = (spriteEffects.Value & SpriteEffects.FlipVertically) != 0;
float addedRotation = rotation + this.rotation;
if (flipHorizontal != flipVertical) { addedRotation = -addedRotation; }
Vector2 advanceX = addedRotation == 0.0f ? Vector2.UnitX : new Vector2((float)Math.Cos(addedRotation), (float)Math.Sin(addedRotation));
Vector2 advanceY = new Vector2(-advanceX.Y, advanceX.X);
//Init optional values
Vector2 drawOffset = startOffset ?? Vector2.Zero;
Vector2 scale = textureScale ?? Vector2.One;
Color drawColor = color ?? Color.White;
Vector2 transformedOrigin = origin ?? Vector2.Zero;
bool flipHorizontal = (effects & SpriteEffects.FlipHorizontally) != 0;
bool flipVertical = (effects & SpriteEffects.FlipVertically) != 0;
transformedOrigin = advanceX * transformedOrigin.X + advanceY * transformedOrigin.Y;
void drawSection(Vector2 slicePos, Rectangle sliceRect)
{
Vector2 transformedPos = slicePos - position;
transformedPos = advanceX * transformedPos.X + advanceY * transformedPos.Y;
transformedPos += position - transformedOrigin;
spriteBatch.Draw(texture, transformedPos, sliceRect, drawColor, addedRotation, Vector2.Zero, scale, spriteEffects.Value, depth ?? this.depth);
}
//wrap the drawOffset inside the sourceRect
drawOffset.X = (drawOffset.X / scale.X) % sourceRect.Width;
@@ -368,8 +395,8 @@ namespace Barotrauma
{
slicePos.Y += flippedDrawOffset.Y;
}
spriteBatch.Draw(texture, slicePos, sliceRect, drawColor, rotation, Vector2.Zero, scale, effects, depth ?? this.depth);
drawSection(slicePos, sliceRect);
currDrawPosition.X = slicePos.X + sliceWidth;
}
}
@@ -416,7 +443,7 @@ namespace Barotrauma
sliceRect.Y = SourceRect.Y;
sliceRect.Height = (int)(sliceHeight / scale.Y);
spriteBatch.Draw(texture, slicePos, sliceRect, drawColor, rotation, Vector2.Zero, scale, effects, depth ?? this.depth);
drawSection(slicePos, sliceRect);
currDrawPosition.Y = slicePos.Y + sliceHeight;
}
@@ -433,8 +460,7 @@ namespace Barotrauma
}
}
spriteBatch.Draw(texture, currDrawPosition,
texPerspective, drawColor, rotation, Vector2.Zero, scale, effects, depth ?? this.depth);
drawSection(currDrawPosition, texPerspective);
currDrawPosition.Y += texPerspective.Height * scale.Y;
}

View File

@@ -210,7 +210,7 @@ namespace Barotrauma
statusEffect.soundChannel.FadeOutAndDispose();
statusEffect.soundChannel = null;
}
else
else if (statusEffect.soundEmitter is { Removed: false })
{
statusEffect.soundChannel.Position = new Vector3(statusEffect.soundEmitter.WorldPosition, 0.0f);
if (doMuffleCheck && !statusEffect.ignoreMuffling)

View File

@@ -101,13 +101,36 @@ namespace Barotrauma.Steam
{
Color = GUIStyle.Green
};
var textShadow = new GUITextBlock(new RectTransform(Vector2.One, itemDownloadProgress.RectTransform) { AbsoluteOffset = new Point(GUI.IntScale(3)) }, "",
textColor: Color.Black, textAlignment: Alignment.Center);
var text = new GUITextBlock(new RectTransform(Vector2.One, itemDownloadProgress.RectTransform), "",
textAlignment: Alignment.Center);
var itemDownloadProgressUpdater = new GUICustomComponent(
new RectTransform(Vector2.Zero, msgBox.Content.RectTransform),
onUpdate: (f, component) =>
{
float progress = 0.0f;
if (item.IsDownloading) { progress = item.DownloadAmount; }
else if (itemDownloadProgress.BarSize > 0.0f) { progress = 1.0f; }
if (item.IsDownloading)
{
progress = item.DownloadAmount;
text.Text = textShadow.Text = TextManager.GetWithVariable(
"PublishPopupDownload",
"[percentage]",
((int)MathF.Round(item.DownloadAmount * 100)).ToString());
}
else if (itemDownloadProgress.BarSize > 0.0f)
{
if (!item.IsInstalled && !SteamManager.Workshop.CanBeInstalled(item.Id))
{
itemDownloadProgress.Color = GUIStyle.Red;
text.Text = textShadow.Text = TextManager.Get("workshopiteminstallfailed");
}
else
{
text.Text = textShadow.Text = TextManager.Get(item.IsInstalled ? "workshopiteminstalled" : "PublishPopupInstall");
}
progress = 1.0f;
}
itemDownloadProgress.BarSize = Math.Max(itemDownloadProgress.BarSize,
MathHelper.Lerp(itemDownloadProgress.BarSize, progress, 0.1f));
@@ -134,9 +157,16 @@ namespace Barotrauma.Steam
{
foreach (var item in itemsToDownload)
{
DebugConsole.Log($"Reinstalling {item.Title}...");
await SteamManager.Workshop.Reinstall(item);
if (!GUIMessageBox.MessageBoxes.Contains(msgBox)) { break; }
DebugConsole.Log($"Finished installing {item.Title}...");
if (!GUIMessageBox.MessageBoxes.Contains(msgBox))
{
DebugConsole.Log($"Download prompt closed, interrupting {nameof(DownloadItems)}.");
break;
}
}
DebugConsole.Log($"{nameof(DownloadItems)} finished.");
}
}
}

View File

@@ -120,6 +120,10 @@ namespace Barotrauma.Steam
currentLobby?.SetData("playstyle", serverSettings.PlayStyle.ToString());
currentLobby?.SetData("gamemode", GameMain.NetLobbyScreen?.SelectedMode?.Identifier.Value ?? "");
currentLobby?.SetData("language", serverSettings.Language.ToString());
if (GameMain.NetLobbyScreen?.SelectedSub != null)
{
currentLobby?.SetData("submarine", GameMain.NetLobbyScreen.SelectedSub.Name);
}
DebugConsole.Log("Lobby updated!");
}

View File

@@ -298,7 +298,7 @@ namespace Barotrauma.Steam
public static void OnItemDownloadComplete(ulong id, bool forceInstall = false)
{
if (!(Screen.Selected is MainMenuScreen) && !forceInstall)
if (Screen.Selected is not MainMenuScreen && !forceInstall)
{
if (!MainMenuScreen.WorkshopItemsToUpdate.Contains(id))
{
@@ -306,13 +306,26 @@ namespace Barotrauma.Steam
}
return;
}
else if (CanBeInstalled(id)
&& !ContentPackageManager.WorkshopPackages.Any(p =>
else if (!CanBeInstalled(id))
{
DebugConsole.Log($"Cannot install {id}");
InstallWaiter.StopWaiting(id);
}
else if (ContentPackageManager.WorkshopPackages.Any(p =>
p.UgcId.TryUnwrap(out var ugcId)
&& ugcId is SteamWorkshopId workshopId
&& workshopId.Value == id)
&& !InstallTaskCounter.IsInstalling(id))
&& workshopId.Value == id))
{
DebugConsole.Log($"Already installed {id}.");
InstallWaiter.StopWaiting(id);
}
else if (InstallTaskCounter.IsInstalling(id))
{
DebugConsole.Log($"Already installing {id}.");
}
else
{
DebugConsole.Log($"Finished downloading {id}, installing...");
TaskPool.Add($"InstallItem{id}", InstallMod(id), t => InstallWaiter.StopWaiting(id));
}
}

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