Unstable 1.2.4.0
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
@@ -521,22 +522,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; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2170,6 +2170,8 @@ namespace Barotrauma
|
||||
medUIExtra?.Remove();
|
||||
medUIExtra = null;
|
||||
|
||||
Character.OnAttacked -= OnAttacked;
|
||||
|
||||
limbIndicatorOverlay?.Remove();
|
||||
limbIndicatorOverlay = null;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -681,6 +681,7 @@ namespace Barotrauma
|
||||
AssignRelayToServer("savebinds", false);
|
||||
AssignRelayToServer("spreadsheetexport", false);
|
||||
#if DEBUG
|
||||
AssignRelayToServer("listspamfilters", false);
|
||||
AssignRelayToServer("crash", false);
|
||||
AssignRelayToServer("showballastflorasprite", false);
|
||||
AssignRelayToServer("simulatedlatency", false);
|
||||
@@ -2234,6 +2235,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; }
|
||||
@@ -3094,7 +3119,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) =>
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -353,6 +353,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;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2127,11 +2127,10 @@ namespace Barotrauma
|
||||
{
|
||||
var dialog = new GUIMessageBox(
|
||||
TextManager.Get("newsupplies"),
|
||||
TextManager.GetWithVariable("suppliespurchasedmessage", "[location]", campaignUI?.Campaign?.Map?.CurrentLocation?.Name));
|
||||
TextManager.GetWithVariable("suppliespurchasedmessage", "[location]", campaignUI?.Campaign?.Map?.CurrentLocation?.DisplayName));
|
||||
dialog.Buttons[0].OnClicked += dialog.Close;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -123,6 +123,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;
|
||||
@@ -404,7 +408,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)
|
||||
|
||||
@@ -192,12 +192,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:
|
||||
@@ -205,7 +205,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;
|
||||
@@ -221,7 +221,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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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]";
|
||||
}
|
||||
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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; }
|
||||
@@ -761,6 +770,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void OnPlayerSkillsChanged() { }
|
||||
|
||||
public virtual void AddTooltipInfo(ref LocalizedString name, ref LocalizedString description) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,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;
|
||||
}
|
||||
@@ -571,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;
|
||||
}
|
||||
@@ -589,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>())
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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")
|
||||
{
|
||||
|
||||
@@ -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
|
||||
@@ -825,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)
|
||||
@@ -853,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'));
|
||||
@@ -865,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)
|
||||
{
|
||||
@@ -875,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');
|
||||
@@ -894,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 = "";
|
||||
@@ -921,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),
|
||||
@@ -946,7 +1004,6 @@ namespace Barotrauma.Items.Components
|
||||
font: GUIStyle.SmallFont);
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void HighlightRecipe(string identifier, Color color)
|
||||
@@ -1056,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; }
|
||||
|
||||
@@ -991,7 +991,7 @@ 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,
|
||||
@@ -1001,7 +1001,7 @@ namespace Barotrauma.Items.Components
|
||||
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,
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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,19 @@ 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)
|
||||
{
|
||||
if (!container.AllowDragAndDrop || !container.DrawInventory)
|
||||
{
|
||||
allowCombine = false;
|
||||
}
|
||||
}
|
||||
bool success = selectedInventory.TryPutItem(item, slotIndex, allowSwapping: !anySuccess, allowCombine, Character.Controlled);
|
||||
if (success)
|
||||
{
|
||||
anySuccess = true;
|
||||
@@ -1380,18 +1396,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 +1415,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 +1606,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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -386,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,
|
||||
@@ -401,20 +402,7 @@ namespace Barotrauma
|
||||
textureScale: Vector2.One * Scale,
|
||||
depth: d);
|
||||
}
|
||||
foreach (var decorativeSprite in Prefab.DecorativeSprites)
|
||||
{
|
||||
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
|
||||
|
||||
Color decorativeSpriteColor = GetSpriteColor(decorativeSprite.Color);
|
||||
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: decorativeSpriteColor,
|
||||
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,21 +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; }
|
||||
|
||||
Color decorativeSpriteColor = GetSpriteColor(decorativeSprite.Color);
|
||||
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)), decorativeSpriteColor,
|
||||
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)
|
||||
@@ -492,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)
|
||||
@@ -524,7 +485,6 @@ namespace Barotrauma
|
||||
rotation, decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, activeSprite.effects,
|
||||
depth: depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
activeSprite.effects = oldEffects;
|
||||
@@ -569,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)
|
||||
{
|
||||
@@ -629,6 +595,55 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (impact > 1.0f &&
|
||||
@@ -804,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();
|
||||
@@ -861,6 +889,11 @@ namespace Barotrauma
|
||||
CanBeFocused = true
|
||||
};
|
||||
|
||||
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"),
|
||||
@@ -873,6 +906,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (!SelectedList.Contains(this)) { FlipX(relativeToSub: false); }
|
||||
ColorFlipButton(button, FlippedX);
|
||||
if (rotationField != null) { rotationField.FloatValue = Rotation; }
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -889,6 +923,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (!SelectedList.Contains(this)) { FlipY(relativeToSub: false); }
|
||||
ColorFlipButton(button, FlippedY);
|
||||
if (rotationField != null) { rotationField.FloatValue = Rotation; }
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -1553,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}");
|
||||
}
|
||||
@@ -1953,5 +2005,13 @@ namespace Barotrauma
|
||||
Inventory.DraggingSlot = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPlayerSkillsChanged()
|
||||
{
|
||||
foreach (ItemComponent ic in components)
|
||||
{
|
||||
ic.OnPlayerSkillsChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,19 +371,27 @@ namespace Barotrauma
|
||||
{
|
||||
if (!ResizeHorizontal && !ResizeVertical)
|
||||
{
|
||||
sprite.Draw(spriteBatch, new Vector2(placeRect.Center.X, -(placeRect.Y - placeRect.Height / 2)), SpriteColor * 0.8f, scale: scale, rotate: rotation);
|
||||
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
|
||||
{
|
||||
Vector2 position = placeRect.Location.ToVector2();
|
||||
Vector2 placeSize = placeRect.Size.ToVector2();
|
||||
sprite?.DrawTiled(
|
||||
spriteBatch,
|
||||
new Vector2(position.X, -position.Y),
|
||||
placeSize,
|
||||
spriteBatch: spriteBatch,
|
||||
position: new Vector2(position.X, -position.Y),
|
||||
targetSize: placeSize,
|
||||
rotation: rotation,
|
||||
textureScale: Vector2.One * scale,
|
||||
color: SpriteColor * 0.8f);
|
||||
color: SpriteColor * 0.8f,
|
||||
spriteEffects: spriteEffects ^ sprite.effects);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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; }
|
||||
@@ -259,7 +268,8 @@ namespace Barotrauma.Lights
|
||||
{
|
||||
if (!light.Enabled) { continue; }
|
||||
if ((light.Color.A < 1 || light.Range < 1.0f) && !light.LightSourceParams.OverrideLightSpriteAlpha.HasValue) { continue; }
|
||||
|
||||
//above the top boundary of the level (in an inactive respawn shuttle?)
|
||||
if (Level.Loaded != null && light.WorldPosition.Y > Level.Loaded.Size.Y) { continue; }
|
||||
if (light.ParentBody != null)
|
||||
{
|
||||
light.ParentBody.UpdateDrawPosition();
|
||||
@@ -801,6 +811,8 @@ namespace Barotrauma.Lights
|
||||
|
||||
public void ClearLights()
|
||||
{
|
||||
activeLights.Clear();
|
||||
activeLightsWithLightVolume.Clear();
|
||||
lights.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
@@ -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; }
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -162,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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1332,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;
|
||||
|
||||
@@ -236,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;
|
||||
@@ -312,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)));
|
||||
}
|
||||
|
||||
|
||||
@@ -96,9 +96,6 @@ namespace Barotrauma
|
||||
|
||||
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;
|
||||
|
||||
@@ -109,9 +106,8 @@ namespace Barotrauma
|
||||
color: Color.White * 0.8f,
|
||||
origin: placeRect.Size.ToVector2() * 0.5f,
|
||||
rotation: rotation,
|
||||
textureScale: TextureScale * scale);
|
||||
|
||||
Sprite.effects = oldEffects;
|
||||
textureScale: TextureScale * scale,
|
||||
spriteEffects: spriteEffects ^ Sprite.effects);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2222,7 +2222,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);
|
||||
}
|
||||
|
||||
@@ -181,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()))
|
||||
@@ -263,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)
|
||||
|
||||
@@ -254,7 +254,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 +598,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:
|
||||
|
||||
@@ -258,8 +258,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);
|
||||
@@ -312,8 +312,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);
|
||||
@@ -379,8 +379,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);
|
||||
@@ -389,6 +389,8 @@ namespace Barotrauma
|
||||
c.DrawFront(spriteBatch, cam);
|
||||
}
|
||||
|
||||
GameMain.LightManager.DebugDrawVertices(spriteBatch);
|
||||
|
||||
Level.Loaded?.DrawDebugOverlay(spriteBatch, cam);
|
||||
if (GameMain.DebugDraw)
|
||||
{
|
||||
@@ -437,7 +439,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;
|
||||
}
|
||||
@@ -505,7 +507,7 @@ namespace Barotrauma
|
||||
graphics.DepthStencilState = DepthStencilState.None;
|
||||
if (string.IsNullOrEmpty(postProcessTechnique))
|
||||
{
|
||||
Quad.UseBasicEffect(renderTargetFinal);
|
||||
GraphicsQuad.UseBasicEffect(renderTargetFinal);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -514,7 +516,7 @@ namespace Barotrauma
|
||||
PostProcessEffect.CurrentTechnique = PostProcessEffect.Techniques[postProcessTechnique];
|
||||
PostProcessEffect.CurrentTechnique.Passes[0].Apply();
|
||||
}
|
||||
Quad.Render();
|
||||
GraphicsQuad.Render();
|
||||
|
||||
if (fadeToBlackState > 0.0f)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -165,6 +165,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
#else
|
||||
SpamServerFilters.RequestGlobalSpamFilter();
|
||||
FetchRemoteContent();
|
||||
#endif
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -655,6 +655,7 @@ namespace Barotrauma
|
||||
ScrollBarVisible = true,
|
||||
OnSelected = (btn, obj) =>
|
||||
{
|
||||
if (GUI.MouseOn is GUIButton) { return false; }
|
||||
if (obj is not ServerInfo serverInfo) { return false; }
|
||||
|
||||
joinButton.Enabled = true;
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
@@ -1560,8 +1557,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
|
||||
|
||||
@@ -3933,28 +3939,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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5485,9 +5478,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"))
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
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
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
330
Barotrauma/BarotraumaClient/ClientSource/SpamServerFilter.cs
Normal file
330
Barotrauma/BarotraumaClient/ClientSource/SpamServerFilter.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -284,13 +284,22 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
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; }
|
||||
|
||||
bool flipHorizontal = (effects & SpriteEffects.FlipHorizontally) != 0;
|
||||
bool flipVertical = (effects & SpriteEffects.FlipVertically) != 0;
|
||||
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; }
|
||||
@@ -311,7 +320,7 @@ namespace Barotrauma
|
||||
Vector2 transformedPos = slicePos - position;
|
||||
transformedPos = advanceX * transformedPos.X + advanceY * transformedPos.Y;
|
||||
transformedPos += position - transformedOrigin;
|
||||
spriteBatch.Draw(texture, transformedPos, sliceRect, drawColor, addedRotation, Vector2.Zero, scale, effects, depth ?? this.depth);
|
||||
spriteBatch.Draw(texture, transformedPos, sliceRect, drawColor, addedRotation, Vector2.Zero, scale, spriteEffects.Value, depth ?? this.depth);
|
||||
}
|
||||
|
||||
//wrap the drawOffset inside the sourceRect
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
@@ -116,6 +117,9 @@ namespace Barotrauma
|
||||
private readonly bool WasDeleted;
|
||||
private readonly List<AddOrDeleteCommand> ContainedItemsCommand = new List<AddOrDeleteCommand>();
|
||||
|
||||
// We need to 'snapshot' the state of the circuit box and the best way to do that is to save it to XML.
|
||||
private readonly List<XElement> CircuitBoxData = new List<XElement>();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a command where all entities share the same state.
|
||||
/// </summary>
|
||||
@@ -143,13 +147,17 @@ namespace Barotrauma
|
||||
List<MapEntity> itemsToDelete = new List<MapEntity>();
|
||||
foreach (MapEntity receiver in Receivers)
|
||||
{
|
||||
if (receiver is Item it)
|
||||
if (receiver is not Item it) { continue; }
|
||||
|
||||
foreach (var cb in it.GetComponents<CircuitBox>())
|
||||
{
|
||||
foreach (ItemContainer component in it.GetComponents<ItemContainer>())
|
||||
{
|
||||
if (component.Inventory == null) { continue; }
|
||||
itemsToDelete.AddRange(component.Inventory.AllItems.Where(item => !item.Removed));
|
||||
}
|
||||
CircuitBoxData.Add(cb.Save(new XElement("root")));
|
||||
}
|
||||
|
||||
foreach (ItemContainer component in it.GetComponents<ItemContainer>())
|
||||
{
|
||||
if (component.Inventory == null) { continue; }
|
||||
itemsToDelete.AddRange(component.Inventory.AllItems.Where(static item => !item.Removed));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,34 +200,50 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
public override void Execute()
|
||||
{
|
||||
var items = DeleteUndelete(true);
|
||||
ContainedItemsCommand?.ForEach(static cmd => cmd.Execute());
|
||||
CircuitBoxWorkaround(items);
|
||||
}
|
||||
=> Process(true);
|
||||
|
||||
public override void UnExecute()
|
||||
=> Process(false);
|
||||
|
||||
private void Process(bool redo)
|
||||
{
|
||||
var items = DeleteUndelete(false);
|
||||
ContainedItemsCommand?.ForEach(static cmd => cmd.UnExecute());
|
||||
CircuitBoxWorkaround(items);
|
||||
var items = DeleteUndelete(redo);
|
||||
foreach (var cmd in ContainedItemsCommand)
|
||||
{
|
||||
cmd.Process(redo);
|
||||
}
|
||||
ApplyCircuitBoxDataIfAny(items);
|
||||
}
|
||||
|
||||
// FIXME Temporary workaround for circuit boxes throwing console errors and breaking completely when undoing a deletion
|
||||
private static void CircuitBoxWorkaround(Option<ImmutableArray<MapEntity>> entitiesOption)
|
||||
/// <summary>
|
||||
/// We need to manually copy over the circuit box data because of how the undo handles inventory items.
|
||||
/// The undo system recursively deletes inventory items and creates a separate command for each one.
|
||||
/// This causes the circuit box to lose its internal inventory when it's cloned and then restored and make it
|
||||
/// unable to load the state from XML.
|
||||
///
|
||||
/// The workaround to this is to ignore the XML that is being loaded when the item is created and instead
|
||||
/// save the XML into the command and then load it back after the undo system has restored the items which
|
||||
/// is what this function does.
|
||||
/// </summary>
|
||||
private void ApplyCircuitBoxDataIfAny(ImmutableArray<Item> items)
|
||||
{
|
||||
if (!entitiesOption.TryUnwrap(out var entities)) { return; }
|
||||
|
||||
foreach (var entity in entities)
|
||||
int cbIndex = 0;
|
||||
foreach (var newItem in items)
|
||||
{
|
||||
if (entity is not Item it) { continue; }
|
||||
|
||||
if (it.GetComponent<CircuitBox>() is not null)
|
||||
foreach (ItemComponent component in newItem.Components)
|
||||
{
|
||||
foreach (var container in it.GetComponents<ItemContainer>())
|
||||
if (component is not CircuitBox cb) { continue; }
|
||||
|
||||
if (cbIndex < 0 || cbIndex >= CircuitBoxData.Count)
|
||||
{
|
||||
container.Inventory.DeleteAllItems();
|
||||
DebugConsole.ThrowError("Unable to restore wiring in circuit box, index out of range.");
|
||||
continue;
|
||||
}
|
||||
|
||||
var cbData = CircuitBoxData[cbIndex];
|
||||
cbIndex++;
|
||||
|
||||
cb.LoadFromXML(new ContentXElement(null, cbData));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -238,15 +262,19 @@ namespace Barotrauma
|
||||
Receivers.Clear();
|
||||
PreviousInventories?.Clear();
|
||||
ContainedItemsCommand?.ForEach(static cmd => cmd.Cleanup());
|
||||
CircuitBoxData.Clear();
|
||||
}
|
||||
|
||||
private Option<ImmutableArray<MapEntity>> DeleteUndelete(bool redo)
|
||||
private ImmutableArray<Item> DeleteUndelete(bool redo)
|
||||
{
|
||||
bool wasDeleted = WasDeleted;
|
||||
|
||||
// We are redoing instead of undoing, flip the behavior
|
||||
if (redo) { wasDeleted = !wasDeleted; }
|
||||
|
||||
// collect newly created items so we can update their circuit boxes if any
|
||||
var builder = ImmutableArray.CreateBuilder<Item>();
|
||||
|
||||
if (wasDeleted)
|
||||
{
|
||||
Debug.Assert(Receivers.All(static entity => entity.GetReplacementOrThis().Removed), "Tried to redo a deletion but some items were not deleted");
|
||||
@@ -260,6 +288,7 @@ namespace Barotrauma
|
||||
|
||||
if (receiver.GetReplacementOrThis() is Item item && clone is Item cloneItem)
|
||||
{
|
||||
builder.Add(cloneItem);
|
||||
foreach (ItemComponent ic in item.Components)
|
||||
{
|
||||
int index = item.GetComponentIndex(ic);
|
||||
@@ -303,7 +332,7 @@ namespace Barotrauma
|
||||
clone.Submarine = Submarine.MainSub;
|
||||
}
|
||||
|
||||
return Option.Some(clones.ToImmutableArray());
|
||||
return builder.ToImmutable();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -316,7 +345,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
return Option.None;
|
||||
return builder.ToImmutable();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ using System.Text;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
static class Quad
|
||||
static class GraphicsQuad
|
||||
{
|
||||
private static VertexBuffer vertexBuffer = null;
|
||||
private static IndexBuffer indexBuffer = null;
|
||||
@@ -35,7 +35,7 @@ namespace Barotrauma
|
||||
Vector2 pos,
|
||||
Rectangle srcRect,
|
||||
Color color,
|
||||
float rotation,
|
||||
float rotationRad,
|
||||
Vector2 origin,
|
||||
Vector2 scale,
|
||||
SpriteEffects effects,
|
||||
@@ -55,9 +55,8 @@ namespace Barotrauma
|
||||
(srcRectBottom, srcRectTop) = (srcRectTop, srcRectBottom);
|
||||
}
|
||||
|
||||
rotation = MathHelper.ToRadians(rotation);
|
||||
float sin = (float)Math.Sin(rotation);
|
||||
float cos = (float)Math.Cos(rotation);
|
||||
float sin = (float)Math.Sin(rotationRad);
|
||||
float cos = (float)Math.Cos(rotationRad);
|
||||
|
||||
var size = srcRect.Size.ToVector2() * scale;
|
||||
|
||||
@@ -183,11 +182,11 @@ namespace Barotrauma
|
||||
commandList.Add(command);
|
||||
}
|
||||
|
||||
public void Draw(Texture2D texture, Vector2 pos, Rectangle? srcRect, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float depth)
|
||||
public void Draw(Texture2D texture, Vector2 pos, Rectangle? srcRect, Color color, float rotationRad, Vector2 origin, Vector2 scale, SpriteEffects effects, float depth)
|
||||
{
|
||||
if (isDisposed) { return; }
|
||||
|
||||
var command = Command.FromTransform(texture, pos, srcRect ?? texture.Bounds, color, rotation, origin, scale, effects, depth, commandList.Count);
|
||||
var command = Command.FromTransform(texture, pos, srcRect ?? texture.Bounds, color, rotationRad, origin, scale, effects, depth, commandList.Count);
|
||||
AppendCommand(command);
|
||||
}
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace Barotrauma
|
||||
float zoom = (float)texWidth / (float)boundingBox.Width;
|
||||
int texHeight = (int)(zoom * boundingBox.Height);
|
||||
|
||||
using Camera cam = new Camera();
|
||||
Camera cam = new Camera();
|
||||
cam.SetResolution(new Point(texWidth, texHeight));
|
||||
cam.MaxZoom = zoom;
|
||||
cam.MinZoom = zoom * 0.5f;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>1.2.1.0</Version>
|
||||
<Version>1.2.4.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2023</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>1.2.1.0</Version>
|
||||
<Version>1.2.4.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2023</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>1.2.1.0</Version>
|
||||
<Version>1.2.4.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2023</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>1.2.1.0</Version>
|
||||
<Version>1.2.4.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2023</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>1.2.1.0</Version>
|
||||
<Version>1.2.4.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2023</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
|
||||
@@ -80,7 +80,9 @@ namespace Barotrauma
|
||||
{
|
||||
msg.WriteUInt32(Job.Prefab.UintIdentifier);
|
||||
msg.WriteByte((byte)Job.Variant);
|
||||
foreach (SkillPrefab skillPrefab in Job.Prefab.Skills.OrderBy(s => s.Identifier))
|
||||
var skills = Job.Prefab.Skills.OrderBy(s => s.Identifier);
|
||||
msg.WriteByte((byte)skills.Count());
|
||||
foreach (SkillPrefab skillPrefab in skills)
|
||||
{
|
||||
msg.WriteSingle(Job.GetSkill(skillPrefab.Identifier)?.Level ?? 0.0f);
|
||||
}
|
||||
|
||||
@@ -1134,7 +1134,12 @@ namespace Barotrauma
|
||||
createMessage("Traitors:");
|
||||
foreach (var ev in traitorManager.ActiveEvents)
|
||||
{
|
||||
createMessage($" - {ev.Traitor.Name}: {ev.TraitorEvent.Prefab.Identifier} ({ev.TraitorEvent.CurrentState})");
|
||||
string msg = $" - {ev.TraitorEvent.Prefab.Identifier} ({ev.TraitorEvent.CurrentState}): {ev.Traitor.Name}";
|
||||
if (ev.TraitorEvent.SecondaryTraitors.Any())
|
||||
{
|
||||
msg += $" secondary traitors: {string.Join(", ", ev.TraitorEvent.SecondaryTraitors.Select(t => t.Name))}";
|
||||
}
|
||||
createMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1416,7 +1421,7 @@ namespace Barotrauma
|
||||
if (GameMain.GameSession?.GameMode is MultiPlayerCampaign mpCampaign &&
|
||||
GameMain.NetLobbyScreen.SelectedMode == GameModePreset.MultiPlayerCampaign)
|
||||
{
|
||||
MultiPlayerCampaign.LoadCampaign(GameMain.GameSession.SavePath);
|
||||
MultiPlayerCampaign.LoadCampaign(GameMain.GameSession.SavePath, client: null);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1461,7 +1466,7 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
var location = GameMain.GameSession.Campaign.Map.Locations.FirstOrDefault(l => l.Name.Equals(args[0], StringComparison.OrdinalIgnoreCase));
|
||||
var location = GameMain.GameSession.Campaign.Map.Locations.FirstOrDefault(l => l.DisplayName.Equals(args[0], StringComparison.OrdinalIgnoreCase));
|
||||
if (location == null)
|
||||
{
|
||||
ThrowError($"Could not find a location with the name {args[0]}.");
|
||||
@@ -1484,7 +1489,7 @@ namespace Barotrauma
|
||||
|
||||
return new string[][]
|
||||
{
|
||||
GameMain.GameSession.Campaign.Map.Locations.Select(l => l.Name).ToArray(),
|
||||
GameMain.GameSession.Campaign.Map.Locations.Select(l => l.DisplayName.Value).ToArray(),
|
||||
LocationType.Prefabs.Select(lt => lt.Name.Value).ToArray()
|
||||
};
|
||||
}));
|
||||
@@ -2457,7 +2462,7 @@ namespace Barotrauma
|
||||
}
|
||||
Location location = campaign.Map.CurrentLocation.Connections[destinationIndex].OtherLocation(campaign.Map.CurrentLocation);
|
||||
campaign.Map.SelectLocation(location);
|
||||
GameMain.Server.SendConsoleMessage(location.Name + " selected.", senderClient);
|
||||
GameMain.Server.SendConsoleMessage($"{location.DisplayName.Value} selected.", senderClient);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#nullable enable
|
||||
using Barotrauma.Networking;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma;
|
||||
|
||||
partial class HighlightAction : EventAction
|
||||
{
|
||||
partial void SetHighlightProjSpecific(Entity entity, IEnumerable<Character>? targetCharacters)
|
||||
{
|
||||
if (entity is Item item && GameMain.Server != null)
|
||||
{
|
||||
IEnumerable<Client>? targetClients = null;
|
||||
if (targetCharacters != null)
|
||||
{
|
||||
targetClients = targetCharacters
|
||||
.Select(c => GameMain.Server.ConnectedClients.FirstOrDefault(client => client.Character == c))
|
||||
.Where(c => c != null)!;
|
||||
}
|
||||
GameMain.Server?.CreateEntityEvent(item, new Item.SetHighlightEventData(State, highlightColor, targetClients));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,7 +97,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);
|
||||
|
||||
int finalExperienceGain = (int)(experienceGain * experienceGainMultiplierIndividual.Value);
|
||||
|
||||
@@ -120,28 +120,41 @@ namespace Barotrauma
|
||||
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
|
||||
|
||||
DebugConsole.NewMessage("Campaign started!", Color.Cyan);
|
||||
DebugConsole.NewMessage("Current location: " + GameMain.GameSession.Map.CurrentLocation.Name, Color.Cyan);
|
||||
DebugConsole.NewMessage("Current location: " + GameMain.GameSession.Map.CurrentLocation.DisplayName, Color.Cyan);
|
||||
((MultiPlayerCampaign)GameMain.GameSession.GameMode).LoadInitialLevel();
|
||||
}
|
||||
|
||||
public static void LoadCampaign(string selectedSave)
|
||||
public static void LoadCampaign(string selectedSave, Client client)
|
||||
{
|
||||
GameMain.NetLobbyScreen.ToggleCampaignMode(true);
|
||||
SaveUtil.LoadGame(selectedSave);
|
||||
if (GameMain.GameSession.GameMode is MultiPlayerCampaign mpCampaign)
|
||||
try
|
||||
{
|
||||
mpCampaign.LastSaveID++;
|
||||
SaveUtil.LoadGame(selectedSave);
|
||||
if (GameMain.GameSession.GameMode is MultiPlayerCampaign mpCampaign)
|
||||
{
|
||||
mpCampaign.LastSaveID++;
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to load a campaign. Unexpected game mode: " + GameMain.GameSession.GameMode ?? "none");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Unexpected game mode: " + GameMain.GameSession.GameMode);
|
||||
string errorMsg = $"Error while loading the save {selectedSave}";
|
||||
if (client != null)
|
||||
{
|
||||
GameMain.Server?.SendDirectChatMessage($"{errorMsg}: {e.Message}\n{e.StackTrace}", client, ChatMessageType.Error);
|
||||
}
|
||||
DebugConsole.ThrowError(errorMsg, e);
|
||||
return;
|
||||
}
|
||||
DebugConsole.NewMessage("Campaign loaded!", Color.Cyan);
|
||||
DebugConsole.NewMessage(
|
||||
GameMain.GameSession.Map.SelectedLocation == null ?
|
||||
GameMain.GameSession.Map.CurrentLocation.Name :
|
||||
GameMain.GameSession.Map.CurrentLocation.Name + " -> " + GameMain.GameSession.Map.SelectedLocation.Name, Color.Cyan);
|
||||
GameMain.GameSession.Map.CurrentLocation.DisplayName :
|
||||
GameMain.GameSession.Map.CurrentLocation.DisplayName + " -> " + GameMain.GameSession.Map.SelectedLocation.DisplayName, Color.Cyan);
|
||||
}
|
||||
|
||||
protected override void LoadInitialLevel()
|
||||
@@ -188,7 +201,14 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
LoadCampaign(saveFiles[saveIndex].FilePath);
|
||||
try
|
||||
{
|
||||
LoadCampaign(saveFiles[saveIndex].FilePath, client: null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to load the campaign.", ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -377,7 +397,7 @@ namespace Barotrauma
|
||||
{
|
||||
PendingSubmarineSwitch = null;
|
||||
GameMain.Server.EndGame(TransitionType.None, wasSaved: false);
|
||||
LoadCampaign(GameMain.GameSession.SavePath);
|
||||
LoadCampaign(GameMain.GameSession.SavePath, client: null);
|
||||
LastSaveID++;
|
||||
IncrementAllLastUpdateIds();
|
||||
yield return CoroutineStatus.Success;
|
||||
@@ -1231,13 +1251,13 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (CharacterInfo hireInfo in location.HireManager.PendingHires)
|
||||
{
|
||||
if (TryHireCharacter(location, hireInfo, sender))
|
||||
if (TryHireCharacter(location, hireInfo, sender.Character, sender))
|
||||
{
|
||||
hiredCharacters.Add(hireInfo);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (updatePending)
|
||||
{
|
||||
List<CharacterInfo> pendingHireInfos = new List<CharacterInfo>();
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (Item item in slots[i].Items.ToList())
|
||||
{
|
||||
if (!receivedItemIdsFromClient[i].Contains(item.ID))
|
||||
if (!receivedItemIdsFromClient[i].Contains(item.ID) && item.IsInteractable(c.Character))
|
||||
{
|
||||
Item droppedItem = item;
|
||||
Entity prevOwner = Owner;
|
||||
@@ -107,8 +107,7 @@ namespace Barotrauma
|
||||
if (Entity.FindEntityByID(id) is not Item item || slots[i].Contains(item)) { continue; }
|
||||
|
||||
if (item.GetComponent<Pickable>() is not Pickable pickable ||
|
||||
(pickable.IsAttached && !pickable.PickingDone) ||
|
||||
item.AllowedSlots.None())
|
||||
(pickable.IsAttached && !pickable.PickingDone) || item.AllowedSlots.None() || !item.IsInteractable(c.Character))
|
||||
{
|
||||
DebugConsole.AddWarning($"Client {c.Name} tried to pick up a non-pickable item \"{item}\" (parent inventory: {item.ParentInventory?.Owner.ToString() ?? "null"})",
|
||||
item.Prefab.ContentPackage);
|
||||
|
||||
@@ -161,6 +161,20 @@ namespace Barotrauma
|
||||
msg.WriteUInt16(droppedItem.ID);
|
||||
}
|
||||
break;
|
||||
case SetHighlightEventData highlightEventData:
|
||||
bool isTargetedForClient =
|
||||
highlightEventData.TargetClients.IsEmpty ||
|
||||
highlightEventData.TargetClients.Contains(c);
|
||||
msg.WriteBoolean(isTargetedForClient);
|
||||
if (isTargetedForClient)
|
||||
{
|
||||
msg.WriteBoolean(highlightEventData.Highlighted);
|
||||
if (highlightEventData.Highlighted)
|
||||
{
|
||||
msg.WriteColorR8G8B8A8(highlightEventData.Color);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw error($"Unsupported event type {itemEventData.GetType().Name}");
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
#nullable enable
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
|
||||
@@ -16,4 +19,20 @@ partial class Item
|
||||
Items = items.Distinct().ToImmutableArray();
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct SetHighlightEventData : IEventData
|
||||
{
|
||||
public EventType EventType => EventType.SetHighlight;
|
||||
public readonly bool Highlighted;
|
||||
public readonly Color Color;
|
||||
|
||||
public readonly ImmutableArray<Client> TargetClients;
|
||||
|
||||
public SetHighlightEventData(bool highlighted, Color color, IEnumerable<Client>? targetClients)
|
||||
{
|
||||
Highlighted = highlighted;
|
||||
Color = color;
|
||||
TargetClients = (targetClients ?? Enumerable.Empty<Client>()).ToImmutableArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -804,7 +804,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
using (dosProtection.Pause(connectedClient))
|
||||
{
|
||||
MultiPlayerCampaign.LoadCampaign(saveName);
|
||||
MultiPlayerCampaign.LoadCampaign(saveName, connectedClient);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1230,11 +1230,6 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
c.LastRecvEntityEventID = lastRecvEntityEventID;
|
||||
#warning TODO: remove this later
|
||||
/*if (!CoroutineManager.IsCoroutineRunning("RoundRestartLoop"))
|
||||
{
|
||||
CoroutineManager.StartCoroutine(RoundRestartLoop(), "RoundRestartLoop");
|
||||
}*/
|
||||
}
|
||||
else if (lastRecvEntityEventID != c.LastRecvEntityEventID && GameSettings.CurrentConfig.VerboseLogging)
|
||||
{
|
||||
@@ -1484,7 +1479,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
using (dosProtection.Pause(sender))
|
||||
{
|
||||
MultiPlayerCampaign.LoadCampaign(GameMain.GameSession.SavePath);
|
||||
MultiPlayerCampaign.LoadCampaign(GameMain.GameSession.SavePath, sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3945,7 +3940,6 @@ namespace Barotrauma.Networking
|
||||
if (remainingJobs.None())
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to assign a suitable job for bot \"" + c.Name + "\" (all jobs already have the maximum numbers of players). Assigning a random job...");
|
||||
#warning TODO: is this randsync correct?
|
||||
c.Job = Job.Random(Rand.RandSync.ServerAndClient);
|
||||
assignedPlayerCount[c.Job.Prefab]++;
|
||||
}
|
||||
|
||||
@@ -426,8 +426,9 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
else
|
||||
{
|
||||
double midRoundSyncTimeOut = uniqueEvents.Count / 100 * server.UpdateInterval.TotalSeconds;
|
||||
midRoundSyncTimeOut = Math.Max(10.0f, midRoundSyncTimeOut * 10.0f);
|
||||
//assume we can get at least 10 events per second through
|
||||
double midRoundSyncTimeOut = uniqueEvents.Count / 10 * server.UpdateInterval.TotalSeconds;
|
||||
midRoundSyncTimeOut = Math.Max(midRoundSyncTimeOut, server.ServerSettings.MinimumMidRoundSyncTimeout);
|
||||
|
||||
client.UnreceivedEntityEventCount = (UInt16)uniqueEvents.Count;
|
||||
client.NeedsMidRoundSync = true;
|
||||
|
||||
@@ -537,7 +537,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
//add the ID card tags they should've gotten when spawning in the shuttle
|
||||
character.GiveIdCardTags(shuttleSpawnPoints[i], requireSpawnPointTagsNotGiven: false, createNetworkEvent: true);
|
||||
character.GiveIdCardTags(shuttleSpawnPoints[i], createNetworkEvent: true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -263,7 +263,7 @@ namespace Barotrauma
|
||||
if (amountToChoose > viableTraitors.Count)
|
||||
{
|
||||
DebugConsole.ThrowError(
|
||||
$"Error in traitor event {traitorEvent.Prefab.Identifier}. Not enough players to choose {amountToChoose} secondary traitors."+
|
||||
$"Error in traitor event {traitorEvent.Prefab.Identifier}. Not enough players to choose {amountToChoose} secondary traitors. " +
|
||||
$"Make sure the {nameof(traitorEvent.Prefab.MinPlayerCount)} of the event is high enough to support to desired amount of secondary traitors.",
|
||||
contentPackage: traitorEvent.Prefab.ContentPackage);
|
||||
amountToChoose = viableTraitors.Count;
|
||||
@@ -455,6 +455,15 @@ namespace Barotrauma
|
||||
activeEvent.TraitorEvent.Prefab,
|
||||
activeEvent.TraitorEvent.CurrentState,
|
||||
activeEvent.Traitor));
|
||||
|
||||
if (activeEvent.TraitorEvent.CurrentState == TraitorEvent.State.Completed)
|
||||
{
|
||||
SteamAchievementManager.OnTraitorWin(activeEvent.TraitorEvent.Traitor?.Character);
|
||||
foreach (var secondaryTraitor in activeEvent.TraitorEvent.SecondaryTraitors)
|
||||
{
|
||||
SteamAchievementManager.OnTraitorWin(secondaryTraitor?.Character);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (previousTraitorEvents.Count > MaxPreviousEventHistory)
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>1.2.1.0</Version>
|
||||
<Version>1.2.4.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2023</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
|
||||
@@ -91,6 +91,11 @@ namespace Barotrauma
|
||||
private IEnumerable<Hull> visibleHulls;
|
||||
private float hullVisibilityTimer;
|
||||
const float hullVisibilityInterval = 0.5f;
|
||||
|
||||
/// <summary>
|
||||
/// Returns hulls that are visible to the character, including the current hull.
|
||||
/// Note that this is not an accurate visibility check, it only checks for open gaps between the adjacent and linked hulls.
|
||||
/// </summary>
|
||||
public IEnumerable<Hull> VisibleHulls
|
||||
{
|
||||
get
|
||||
@@ -353,7 +358,7 @@ namespace Barotrauma
|
||||
public static void UnequipContainedItems(Character character, Item parentItem, Func<Item, bool> predicate, bool avoidDroppingInSea = true, int? unequipMax = null)
|
||||
{
|
||||
var inventory = parentItem.OwnInventory;
|
||||
if (inventory == null) { return; }
|
||||
if (inventory == null || !inventory.Container.DrawInventory) { return; }
|
||||
int removed = 0;
|
||||
if (predicate == null || inventory.AllItems.Any(predicate))
|
||||
{
|
||||
|
||||
@@ -262,7 +262,7 @@ namespace Barotrauma
|
||||
|
||||
if (aiElements.Count == 0)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in file \"" + c.Params.File + "\" - no AI element found.",
|
||||
DebugConsole.ThrowError("Error in file \"" + c.Params.File.Path + "\" - no AI element found.",
|
||||
contentPackage: c.Prefab?.ContentPackage);
|
||||
outsideSteering = new SteeringManager(this);
|
||||
insideSteering = new IndoorsSteeringManager(this, false, false);
|
||||
@@ -312,7 +312,7 @@ namespace Barotrauma
|
||||
}
|
||||
ReevaluateAttacks();
|
||||
outsideSteering = new SteeringManager(this);
|
||||
insideSteering = new IndoorsSteeringManager(this, Character.Params.AI.CanOpenDoors, canAttackDoors);
|
||||
insideSteering = new IndoorsSteeringManager(this, AIParams.CanOpenDoors, canAttackDoors);
|
||||
steeringManager = outsideSteering;
|
||||
State = AIState.Idle;
|
||||
requiredHoleCount = (int)Math.Ceiling(ConvertUnits.ToDisplayUnits(colliderWidth) / Structure.WallSectionSize);
|
||||
@@ -322,6 +322,10 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
private CharacterParams.AIParams _aiParams;
|
||||
/// <summary>
|
||||
/// Shorthand for <see cref="Character.Params.AI"/> with null checking.
|
||||
/// </summary>
|
||||
/// <returns><see cref="Character.Params.AI"/> or an empty params. Does not return nulls.</returns>
|
||||
public CharacterParams.AIParams AIParams
|
||||
{
|
||||
get
|
||||
@@ -565,7 +569,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (Character.Params.UsePathFinding && Character.Params.AI.UsePathFindingToGetInside && AIParams.CanOpenDoors)
|
||||
if (Character.Params.UsePathFinding && AIParams.UsePathFindingToGetInside && AIParams.CanOpenDoors)
|
||||
{
|
||||
// Meant for monsters outside the player sub that target something inside the sub and can use the doors to access the sub (Husk).
|
||||
bool IsCloseEnoughToTargetSub(float threshold) => SelectedAiTarget?.Entity?.Submarine is Submarine sub && sub != null && Vector2.DistanceSquared(Character.WorldPosition, sub.WorldPosition) < MathUtils.Pow(Math.Max(sub.Borders.Size.X, sub.Borders.Size.Y) / 2 + threshold, 2);
|
||||
@@ -3097,7 +3101,10 @@ namespace Barotrauma
|
||||
break;
|
||||
}
|
||||
|
||||
valueModifier *= targetMemory.Priority / (float)Math.Sqrt(dist);
|
||||
valueModifier *=
|
||||
targetMemory.Priority /
|
||||
//sqrt = the further the target is, the less the distance matters
|
||||
MathF.Sqrt(dist);
|
||||
|
||||
if (valueModifier > targetValue)
|
||||
{
|
||||
|
||||
@@ -685,8 +685,8 @@ namespace Barotrauma
|
||||
}
|
||||
if (removeDivingSuit)
|
||||
{
|
||||
var divingSuit = Character.Inventory.FindItemByTag(Tags.HeavyDivingGear);
|
||||
if (divingSuit != null && !divingSuit.HasTag(Tags.DivingGearWearableIndoors))
|
||||
var divingSuit = Character.Inventory.FindEquippedItemByTag(Tags.HeavyDivingGear);
|
||||
if (divingSuit != null && !divingSuit.HasTag(Tags.DivingGearWearableIndoors) && divingSuit.IsInteractable(Character))
|
||||
{
|
||||
if (shouldActOnSuffocation || Character.Submarine?.TeamID != Character.TeamID || ObjectiveManager.GetCurrentPriority() >= AIObjectiveManager.RunPriority)
|
||||
{
|
||||
@@ -727,54 +727,51 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
if (takeMaskOff)
|
||||
{
|
||||
if (Character.HasEquippedItem(Tags.LightDivingGear))
|
||||
{
|
||||
var mask = Character.Inventory.FindEquippedItemByTag(Tags.LightDivingGear);
|
||||
if (mask != null)
|
||||
{
|
||||
var mask = Character.Inventory.FindItemByTag(Tags.LightDivingGear);
|
||||
if (mask != null)
|
||||
if (!mask.AllowedSlots.Contains(InvSlotType.Any) || !Character.Inventory.TryPutItem(mask, Character, new List<InvSlotType>() { InvSlotType.Any }))
|
||||
{
|
||||
if (!mask.AllowedSlots.Contains(InvSlotType.Any) || !Character.Inventory.TryPutItem(mask, Character, new List<InvSlotType>() { InvSlotType.Any }))
|
||||
if (Character.Submarine?.TeamID != Character.TeamID || ObjectiveManager.GetCurrentPriority() >= AIObjectiveManager.RunPriority)
|
||||
{
|
||||
if (Character.Submarine?.TeamID != Character.TeamID || ObjectiveManager.GetCurrentPriority() >= AIObjectiveManager.RunPriority)
|
||||
mask.Drop(Character);
|
||||
HandleRelocation(mask);
|
||||
ReequipUnequipped();
|
||||
}
|
||||
else if (findItemState == FindItemState.None || findItemState == FindItemState.DivingMask)
|
||||
{
|
||||
findItemState = FindItemState.DivingMask;
|
||||
if (FindSuitableContainer(mask, out Item targetContainer))
|
||||
{
|
||||
mask.Drop(Character);
|
||||
HandleRelocation(mask);
|
||||
ReequipUnequipped();
|
||||
}
|
||||
else if (findItemState == FindItemState.None || findItemState == FindItemState.DivingMask)
|
||||
{
|
||||
findItemState = FindItemState.DivingMask;
|
||||
if (FindSuitableContainer(mask, out Item targetContainer))
|
||||
findItemState = FindItemState.None;
|
||||
itemIndex = 0;
|
||||
if (targetContainer != null)
|
||||
{
|
||||
findItemState = FindItemState.None;
|
||||
itemIndex = 0;
|
||||
if (targetContainer != null)
|
||||
var decontainObjective = new AIObjectiveDecontainItem(Character, mask, ObjectiveManager, targetContainer: targetContainer.GetComponent<ItemContainer>());
|
||||
decontainObjective.Abandoned += () =>
|
||||
{
|
||||
var decontainObjective = new AIObjectiveDecontainItem(Character, mask, ObjectiveManager, targetContainer: targetContainer.GetComponent<ItemContainer>());
|
||||
decontainObjective.Abandoned += () =>
|
||||
{
|
||||
ReequipUnequipped();
|
||||
IgnoredItems.Add(targetContainer);
|
||||
};
|
||||
decontainObjective.Completed += () => ReequipUnequipped();
|
||||
ObjectiveManager.CurrentObjective.AddSubObjective(decontainObjective, addFirst: true);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
mask.Drop(Character);
|
||||
HandleRelocation(mask);
|
||||
ReequipUnequipped();
|
||||
}
|
||||
IgnoredItems.Add(targetContainer);
|
||||
};
|
||||
decontainObjective.Completed += () => ReequipUnequipped();
|
||||
ObjectiveManager.CurrentObjective.AddSubObjective(decontainObjective, addFirst: true);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
mask.Drop(Character);
|
||||
HandleRelocation(mask);
|
||||
ReequipUnequipped();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ReequipUnequipped();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ReequipUnequipped();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -784,13 +781,11 @@ namespace Barotrauma
|
||||
|
||||
if (findItemState == FindItemState.None || findItemState == FindItemState.OtherItem)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
foreach (Item item in Character.HeldItems)
|
||||
{
|
||||
var hand = i == 0 ? InvSlotType.RightHand : InvSlotType.LeftHand;
|
||||
Item item = Character.Inventory.GetItemInLimbSlot(hand);
|
||||
if (item == null) { continue; }
|
||||
if (item == null || !item.IsInteractable(Character)) { continue; }
|
||||
|
||||
if (!item.AllowedSlots.Contains(InvSlotType.Any) || !Character.Inventory.TryPutItem(item, Character, new List<InvSlotType>() { InvSlotType.Any }) && Character.Submarine?.TeamID == Character.TeamID )
|
||||
if (!item.AllowedSlots.Contains(InvSlotType.Any) || !Character.Inventory.TryPutItem(item, Character, CharacterInventory.AnySlot) && Character.Submarine?.TeamID == Character.TeamID)
|
||||
{
|
||||
if (item.AllowedSlots.Contains(InvSlotType.Bag) && Character.Inventory.TryPutItem(item, Character, new List<InvSlotType>() { InvSlotType.Bag })) { continue; }
|
||||
findItemState = FindItemState.OtherItem;
|
||||
@@ -1389,7 +1384,10 @@ namespace Barotrauma
|
||||
// Don't react to friendly enemy AI attacking other characters. E.g. husks attacking someone when whe are a cultist.
|
||||
continue;
|
||||
}
|
||||
bool isWitnessing = otherHumanAI.VisibleHulls.Contains(Character.CurrentHull) || otherHumanAI.VisibleHulls.Contains(attacker.CurrentHull);
|
||||
bool isWitnessing =
|
||||
otherHumanAI.VisibleHulls.Contains(Character.CurrentHull) ||
|
||||
otherHumanAI.VisibleHulls.Contains(attacker.CurrentHull) ||
|
||||
otherCharacter.CanSeeTarget(attacker, seeThroughWindows: true);
|
||||
if (!isWitnessing)
|
||||
{
|
||||
//if the other character did not witness the attack, and the character is not within report range (or capable of reporting)
|
||||
@@ -1754,11 +1752,11 @@ namespace Barotrauma
|
||||
if (otherCharacter == character || otherCharacter.TeamID == character.TeamID || otherCharacter.IsDead ||
|
||||
otherCharacter.Info?.Job == null ||
|
||||
otherCharacter.AIController is not HumanAIController otherHumanAI ||
|
||||
!otherHumanAI.VisibleHulls.Contains(character.CurrentHull))
|
||||
Vector2.DistanceSquared(otherCharacter.WorldPosition, character.WorldPosition) > 1000.0f * 1000.0f)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!otherCharacter.CanSeeTarget(character)) { continue; }
|
||||
if (!otherCharacter.CanSeeTarget(character, seeThroughWindows: true)) { continue; }
|
||||
|
||||
if (!otherHumanAI.structureDamageAccumulator.ContainsKey(character)) { otherHumanAI.structureDamageAccumulator.Add(character, 0.0f); }
|
||||
float prevAccumulatedDamage = otherHumanAI.structureDamageAccumulator[character];
|
||||
@@ -1840,13 +1838,13 @@ namespace Barotrauma
|
||||
foreach (Character otherCharacter in Character.CharacterList)
|
||||
{
|
||||
if (otherCharacter == thief || otherCharacter.TeamID == thief.TeamID || otherCharacter.IsIncapacitated || otherCharacter.Stun > 0.0f ||
|
||||
otherCharacter.Info?.Job == null || !(otherCharacter.AIController is HumanAIController otherHumanAI) ||
|
||||
!otherHumanAI.VisibleHulls.Contains(thief.CurrentHull))
|
||||
otherCharacter.Info?.Job == null || otherCharacter.AIController is not HumanAIController otherHumanAI ||
|
||||
Vector2.DistanceSquared(otherCharacter.WorldPosition, thief.WorldPosition) > 1000.0f * 1000.0f)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
//if (!otherCharacter.IsFacing(thief.WorldPosition)) { continue; }
|
||||
if (!otherCharacter.CanSeeTarget(thief)) { continue; }
|
||||
if (!otherCharacter.CanSeeTarget(thief, seeThroughWindows: true)) { continue; }
|
||||
// Don't react if the player is taking an extinguisher and there's any fires on the sub, or diving gear when the sub is flooding
|
||||
// -> allow them to use the emergency items
|
||||
if (thief.Submarine != null)
|
||||
|
||||
@@ -1070,7 +1070,8 @@ namespace Barotrauma
|
||||
{
|
||||
// Try reload ammunition from inventory
|
||||
static bool IsInsideHeadset(Item i) => i.ParentInventory?.Owner is Item ownerItem && ownerItem.HasTag(Tags.MobileRadio);
|
||||
Item ammunition = character.Inventory.FindItem(i => i.HasIdentifierOrTags(ammunitionIdentifiers) && i.Condition > 0 && !IsInsideHeadset(i), recursive: true);
|
||||
Item ammunition = character.Inventory.FindItem(i =>
|
||||
i.HasIdentifierOrTags(ammunitionIdentifiers) && i.Condition > 0 && !IsInsideHeadset(i) && i.IsInteractable(character), recursive: true);
|
||||
if (ammunition != null)
|
||||
{
|
||||
var container = Weapon.GetComponent<ItemContainer>();
|
||||
@@ -1089,6 +1090,9 @@ namespace Barotrauma
|
||||
}
|
||||
else if (!HoldPosition && IsOffensiveOrArrest && seekAmmo && ammunitionIdentifiers != null)
|
||||
{
|
||||
// Inventory not drawn = it's not interactable
|
||||
// If the weapon is empty and the inventory is inaccessible, it can't be reloaded
|
||||
if (!Weapon.OwnInventory.Container.DrawInventory) { return false; }
|
||||
SeekAmmunition(ammunitionIdentifiers);
|
||||
}
|
||||
return false;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user