Build 0.20.7.0
This commit is contained in:
@@ -657,12 +657,9 @@ namespace Barotrauma
|
||||
{
|
||||
if (Controlled == null || Controlled == this || (Controlled.CharacterHealth.GetAffliction("psychosis")?.Strength ?? 0.0f) <= 0.0f)
|
||||
{
|
||||
InvisibleTimer = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
InvisibleTimer -= deltaTime;
|
||||
InvisibleTimer = Math.Min(InvisibleTimer, 1.0f);
|
||||
}
|
||||
InvisibleTimer -= deltaTime;
|
||||
}
|
||||
|
||||
foreach (GUIMessage message in guiMessages)
|
||||
|
||||
@@ -394,6 +394,7 @@ namespace Barotrauma
|
||||
|
||||
showHiddenAfflictionsButton = new GUIButton(new RectTransform(new Point(afflictionIconContainer.Rect.Height), afflictionIconContainer.RectTransform), style: "GUIButtonCircular")
|
||||
{
|
||||
Visible = false,
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
|
||||
@@ -2219,6 +2219,15 @@ namespace Barotrauma
|
||||
}
|
||||
}));
|
||||
|
||||
commands.Add(new Command("spawnallitems", "", (string[] args) =>
|
||||
{
|
||||
var cursorPos = Screen.Selected.Cam?.ScreenToWorld(PlayerInput.MousePosition) ?? Vector2.Zero;
|
||||
foreach (ItemPrefab itemPrefab in ItemPrefab.Prefabs)
|
||||
{
|
||||
Entity.Spawner?.AddItemToSpawnQueue(itemPrefab, cursorPos);
|
||||
}
|
||||
}));
|
||||
|
||||
commands.Add(new Command("camerasettings", "camerasettings [defaultzoom] [zoomsmoothness] [movesmoothness] [minzoom] [maxzoom]: debug command for testing camera settings. The values default to 1.1, 8.0, 8.0, 0.1 and 2.0.", (string[] args) =>
|
||||
{
|
||||
float defaultZoom = Screen.Selected.Cam.DefaultZoom;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma;
|
||||
@@ -10,37 +11,66 @@ partial class UIHighlightAction : EventAction
|
||||
partial void UpdateProjSpecific()
|
||||
{
|
||||
bool useCircularFlash = false;
|
||||
GUIComponent component = null;
|
||||
|
||||
if (Id != ElementId.None)
|
||||
{
|
||||
component = GUI.GetAdditions().FirstOrDefault(c => Equals(Id, c.UserData));
|
||||
FindAndFlashComponents(c => Equals(Id, c.UserData));
|
||||
}
|
||||
else if (!EntityIdentifier.IsEmpty)
|
||||
{
|
||||
component = GUI.GetAdditions().FirstOrDefault(c =>
|
||||
FindAndFlashComponents(c =>
|
||||
c.UserData is MapEntityPrefab mep && mep.Identifier == EntityIdentifier || c.UserData is MapEntity me && me.Prefab.Identifier == EntityIdentifier);
|
||||
}
|
||||
else if (!OrderIdentifier.IsEmpty)
|
||||
{
|
||||
useCircularFlash = true;
|
||||
bool foundMinimapNode = false;
|
||||
if (!OrderTargetTag.IsEmpty)
|
||||
{
|
||||
component =
|
||||
GUI.GetAdditions().FirstOrDefault(c =>
|
||||
c.UserData is CrewManager.MinimapNodeData nodeData && nodeData.Order is Order order &&
|
||||
order.Identifier == OrderIdentifier && order.Option == OrderOption && order.TargetEntity is Item item && item.HasTag(OrderTargetTag));
|
||||
foundMinimapNode = FindAndFlashComponents(c =>
|
||||
c.UserData is CrewManager.MinimapNodeData nodeData && nodeData.Order is Order order &&
|
||||
order.Identifier == OrderIdentifier && order.Option == OrderOption && order.TargetEntity is Item item && item.HasTag(OrderTargetTag));
|
||||
}
|
||||
if (!foundMinimapNode)
|
||||
{
|
||||
FindAndFlashComponents(c => c.UserData is Order order && order.Identifier == OrderIdentifier && order.Option == OrderOption,
|
||||
c => c.UserData is Order order && order.Identifier == OrderIdentifier,
|
||||
c => Equals(OrderCategory, c.UserData));
|
||||
}
|
||||
component ??=
|
||||
GUI.GetAdditions().FirstOrDefault(c => c.UserData is Order order && order.Identifier == OrderIdentifier && order.Option == OrderOption) ??
|
||||
GUI.GetAdditions().FirstOrDefault(c => c.UserData is Order order && order.Identifier == OrderIdentifier) ??
|
||||
GUI.GetAdditions().FirstOrDefault(c => Equals(OrderCategory, c.UserData));
|
||||
}
|
||||
|
||||
if (component != null && component.FlashTimer <= 0.0f)
|
||||
bool FindAndFlashComponents(params Func<GUIComponent, bool>[] predicates)
|
||||
{
|
||||
component.Flash(highlightColor, useCircularFlash: useCircularFlash);
|
||||
component.Bounce |= Bounce;
|
||||
foreach (var predicate in predicates)
|
||||
{
|
||||
if (HighlightMultiple)
|
||||
{
|
||||
bool found = false;
|
||||
foreach (var component in GUI.GetAdditions())
|
||||
{
|
||||
if (predicate(component))
|
||||
{
|
||||
Flash(component);
|
||||
found = true;
|
||||
}
|
||||
};
|
||||
return found;
|
||||
}
|
||||
else if (GUI.GetAdditions().FirstOrDefault(predicate) is GUIComponent component)
|
||||
{
|
||||
Flash(component);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Flash(GUIComponent component)
|
||||
{
|
||||
if (component.FlashTimer <= 0.0f)
|
||||
{
|
||||
component.Flash(highlightColor, useCircularFlash: useCircularFlash);
|
||||
component.Bounce |= Bounce;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Threading;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -37,7 +38,7 @@ namespace Barotrauma
|
||||
private set;
|
||||
}
|
||||
|
||||
public bool IsCJK
|
||||
public TextManager.SpeciallyHandledCharCategory SpeciallyHandledCharCategory
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
@@ -84,17 +85,35 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public static TextManager.SpeciallyHandledCharCategory ExtractShccFromXElement(XElement element)
|
||||
=> TextManager.SpeciallyHandledCharCategories
|
||||
.Where(category => element.GetAttributeBool($"is{category}", category switch {
|
||||
// CJK isn't supported by default
|
||||
TextManager.SpeciallyHandledCharCategory.CJK => false,
|
||||
|
||||
// For backwards compatibility, we assume that Cyrillic is supported by default
|
||||
TextManager.SpeciallyHandledCharCategory.Cyrillic => true,
|
||||
|
||||
_ => throw new Exception("unreachable")
|
||||
}))
|
||||
.Aggregate(TextManager.SpeciallyHandledCharCategory.None, (current, category) => current | category);
|
||||
|
||||
public ScalableFont(ContentXElement element, GraphicsDevice gd = null)
|
||||
: this(
|
||||
element.GetAttributeContentPath("file")?.Value,
|
||||
(uint)element.GetAttributeInt("size", 14),
|
||||
gd,
|
||||
element.GetAttributeBool("dynamicloading", false),
|
||||
element.GetAttributeBool("iscjk", false))
|
||||
ExtractShccFromXElement(element))
|
||||
{
|
||||
}
|
||||
|
||||
public ScalableFont(string filename, uint size, GraphicsDevice gd = null, bool dynamicLoading = false, bool isCJK = false)
|
||||
public ScalableFont(
|
||||
string filename,
|
||||
uint size,
|
||||
GraphicsDevice gd = null,
|
||||
bool dynamicLoading = false,
|
||||
TextManager.SpeciallyHandledCharCategory speciallyHandledCharCategory = TextManager.SpeciallyHandledCharCategory.None)
|
||||
{
|
||||
lock (globalMutex)
|
||||
{
|
||||
@@ -120,7 +139,7 @@ namespace Barotrauma
|
||||
this.textures = new List<Texture2D>();
|
||||
this.texCoords = new Dictionary<uint, GlyphData>();
|
||||
this.DynamicLoading = dynamicLoading;
|
||||
this.IsCJK = isCJK;
|
||||
this.SpeciallyHandledCharCategory = speciallyHandledCharCategory;
|
||||
this.graphicsDevice = gd;
|
||||
|
||||
if (gd != null && !dynamicLoading)
|
||||
|
||||
@@ -150,7 +150,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: fix implicit hiding
|
||||
public override bool Selected
|
||||
{
|
||||
get { return isSelected; }
|
||||
|
||||
@@ -179,6 +179,11 @@ namespace Barotrauma
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If enabled, the value wraps around to Max when you go below Min, and vice versa
|
||||
/// </summary>
|
||||
public bool WrapAround;
|
||||
|
||||
public float valueStep;
|
||||
|
||||
private float pressedTimer;
|
||||
@@ -403,13 +408,19 @@ namespace Barotrauma
|
||||
{
|
||||
if (MinValueFloat != null)
|
||||
{
|
||||
floatValue = Math.Max(floatValue, MinValueFloat.Value);
|
||||
MinusButton.Enabled = floatValue > MinValueFloat;
|
||||
floatValue =
|
||||
WrapAround && MinValueFloat.HasValue && floatValue < MinValueFloat.Value ?
|
||||
MaxValueFloat.Value :
|
||||
Math.Max(floatValue, MinValueFloat.Value);
|
||||
MinusButton.Enabled = WrapAround || floatValue > MinValueFloat;
|
||||
}
|
||||
if (MaxValueFloat != null)
|
||||
{
|
||||
floatValue = Math.Min(floatValue, MaxValueFloat.Value);
|
||||
PlusButton.Enabled = floatValue < MaxValueFloat;
|
||||
floatValue =
|
||||
WrapAround && MaxValueFloat.HasValue && floatValue > MaxValueFloat.Value ?
|
||||
MinValueFloat.Value :
|
||||
Math.Min(floatValue, MaxValueFloat.Value);
|
||||
PlusButton.Enabled = WrapAround || floatValue < MaxValueFloat;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,16 +428,16 @@ namespace Barotrauma
|
||||
{
|
||||
if (MinValueInt != null && intValue < MinValueInt.Value)
|
||||
{
|
||||
intValue = Math.Max(intValue, MinValueInt.Value);
|
||||
intValue = WrapAround && MaxValueInt.HasValue ? MaxValueInt.Value : Math.Max(intValue, MinValueInt.Value);
|
||||
UpdateText();
|
||||
}
|
||||
if (MaxValueInt != null && intValue > MaxValueInt.Value)
|
||||
{
|
||||
intValue = Math.Min(intValue, MaxValueInt.Value);
|
||||
intValue = WrapAround && MinValueInt.HasValue ? MinValueInt.Value : Math.Min(intValue, MaxValueInt.Value);
|
||||
UpdateText();
|
||||
}
|
||||
PlusButton.Enabled = MaxValueInt == null || intValue < MaxValueInt;
|
||||
MinusButton.Enabled = MinValueInt == null || intValue > MinValueInt;
|
||||
PlusButton.Enabled = WrapAround || MaxValueInt == null || intValue < MaxValueInt;
|
||||
MinusButton.Enabled = WrapAround || MinValueInt == null || intValue > MinValueInt;
|
||||
}
|
||||
|
||||
private void UpdateText()
|
||||
|
||||
@@ -8,6 +8,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -45,16 +46,17 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private ScalableFont cjkFont;
|
||||
private ImmutableDictionary<TextManager.SpeciallyHandledCharCategory, ScalableFont> specialHandlingFonts;
|
||||
|
||||
public ScalableFont CjkFont
|
||||
public ScalableFont GetFontForCategory(TextManager.SpeciallyHandledCharCategory category)
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Language != GameSettings.CurrentConfig.Language) { LoadFont(); }
|
||||
if (font.IsCJK) { return font; }
|
||||
return cjkFont;
|
||||
}
|
||||
if (Language != GameSettings.CurrentConfig.Language) { LoadFont(); }
|
||||
if (font.SpeciallyHandledCharCategory.HasFlag(category)) { return font; }
|
||||
return specialHandlingFonts.TryGetValue(category, out var resultFont)
|
||||
? resultFont
|
||||
: specialHandlingFonts.TryGetValue(TextManager.SpeciallyHandledCharCategory.CJK, out resultFont)
|
||||
? resultFont
|
||||
: font;
|
||||
}
|
||||
|
||||
public LanguageIdentifier Language { get; private set; }
|
||||
@@ -70,40 +72,68 @@ namespace Barotrauma
|
||||
string fontPath = GetFontFilePath(element);
|
||||
uint size = GetFontSize(element);
|
||||
bool dynamicLoading = GetFontDynamicLoading(element);
|
||||
bool isCJK = GetIsCJK(element);
|
||||
var shcc = GetShcc(element);
|
||||
font?.Dispose();
|
||||
cjkFont?.Dispose();
|
||||
font = new ScalableFont(fontPath, size, GameMain.Instance.GraphicsDevice, dynamicLoading, isCJK)
|
||||
specialHandlingFonts?.Values.ForEach(f => f.Dispose());
|
||||
font = new ScalableFont(
|
||||
fontPath,
|
||||
size,
|
||||
GameMain.Instance.GraphicsDevice,
|
||||
dynamicLoading,
|
||||
shcc)
|
||||
{
|
||||
ForceUpperCase = element.GetAttributeBool("forceuppercase", false)
|
||||
};
|
||||
if (!isCJK)
|
||||
|
||||
var fallbackFonts = new Dictionary<TextManager.SpeciallyHandledCharCategory, ScalableFont>();
|
||||
foreach (var flag in TextManager.SpeciallyHandledCharCategories)
|
||||
{
|
||||
cjkFont = ExtractCjkFont(element)
|
||||
?? new ScalableFont("Content/Fonts/NotoSans/NotoSansCJKsc-Bold.otf",
|
||||
font.Size, GameMain.Instance.GraphicsDevice, dynamicLoading: true, isCJK: true);
|
||||
cjkFont.ForceUpperCase = font.ForceUpperCase;
|
||||
if (shcc.HasFlag(flag)) { continue; }
|
||||
|
||||
var extractedFont = ExtractFont(flag, element);
|
||||
if (extractedFont is null) { continue; }
|
||||
fallbackFonts.Add(flag, extractedFont);
|
||||
}
|
||||
fallbackFonts.Values.ForEach(ff => ff.ForceUpperCase = font.ForceUpperCase);
|
||||
specialHandlingFonts = fallbackFonts.ToImmutableDictionary();
|
||||
Language = GameSettings.CurrentConfig.Language;
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
font?.Dispose(); font = null;
|
||||
cjkFont?.Dispose(); cjkFont = null;
|
||||
font?.Dispose();
|
||||
font = null;
|
||||
specialHandlingFonts?.Values.ForEach(f => f.Dispose());
|
||||
specialHandlingFonts = null;
|
||||
}
|
||||
|
||||
private ScalableFont ExtractCjkFont(ContentXElement element)
|
||||
private ScalableFont ExtractFont(TextManager.SpeciallyHandledCharCategory flag, ContentXElement element)
|
||||
{
|
||||
foreach (var subElement in element.Elements().Reverse())
|
||||
{
|
||||
if (subElement.NameAsIdentifier() != "override") { continue; }
|
||||
if (subElement.GetAttributeBool("iscjk", false))
|
||||
if (ScalableFont.ExtractShccFromXElement(subElement).HasFlag(flag))
|
||||
{
|
||||
return new ScalableFont(subElement, GameMain.Instance.GraphicsDevice);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
ScalableFont hardcodedFallback(string path)
|
||||
=> new ScalableFont(
|
||||
path,
|
||||
font.Size,
|
||||
GameMain.Instance.GraphicsDevice,
|
||||
dynamicLoading: true,
|
||||
speciallyHandledCharCategory: flag);
|
||||
|
||||
return flag switch
|
||||
{
|
||||
TextManager.SpeciallyHandledCharCategory.CJK
|
||||
=> hardcodedFallback("Content/Fonts/NotoSans/NotoSansCJKsc-Bold.otf"),
|
||||
TextManager.SpeciallyHandledCharCategory.Cyrillic
|
||||
=> hardcodedFallback("Content/Fonts/Oswald-Bold.ttf"),
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
private string GetFontFilePath(ContentXElement element)
|
||||
@@ -154,21 +184,21 @@ namespace Barotrauma
|
||||
return element.GetAttributeBool("dynamicloading", false);
|
||||
}
|
||||
|
||||
private bool GetIsCJK(XElement element)
|
||||
private TextManager.SpeciallyHandledCharCategory GetShcc(XElement element)
|
||||
{
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
if (IsValidOverride(subElement))
|
||||
{
|
||||
return subElement.GetAttributeBool("iscjk", false);
|
||||
return ScalableFont.ExtractShccFromXElement(subElement);
|
||||
}
|
||||
}
|
||||
return element.GetAttributeBool("iscjk", false);
|
||||
return ScalableFont.ExtractShccFromXElement(element);
|
||||
}
|
||||
|
||||
private bool IsValidOverride(XElement element)
|
||||
{
|
||||
if (!element.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase)) { return false; }
|
||||
if (!element.IsOverride()) { return false; }
|
||||
var languages = element.GetAttributeIdentifierArray("language", Array.Empty<Identifier>());
|
||||
return languages.Any(l => l.ToLanguageIdentifier() == GameSettings.CurrentConfig.Language);
|
||||
}
|
||||
@@ -191,7 +221,7 @@ namespace Barotrauma
|
||||
private ScalableFont GetFontForStr(LocalizedString str) => GetFontForStr(str.Value);
|
||||
|
||||
public ScalableFont GetFontForStr(string str) =>
|
||||
TextManager.IsCJK(str) ? Prefabs.ActivePrefab.CjkFont : Prefabs.ActivePrefab.Font;
|
||||
Prefabs.ActivePrefab.GetFontForCategory(TextManager.GetSpeciallyHandledCategories(str));
|
||||
|
||||
public void DrawString(SpriteBatch sb, LocalizedString text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects se, float layerDepth)
|
||||
{
|
||||
|
||||
@@ -617,9 +617,9 @@ namespace Barotrauma
|
||||
Stretch = true
|
||||
};
|
||||
var shoppingCrateListContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.8f), shoppingCrateInventoryContainer.RectTransform), style: null);
|
||||
shoppingCrateBuyList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false };
|
||||
shoppingCrateSellList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false };
|
||||
shoppingCrateSellFromSubList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false };
|
||||
shoppingCrateBuyList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false, KeepSpaceForScrollBar = true };
|
||||
shoppingCrateSellList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false, KeepSpaceForScrollBar = true };
|
||||
shoppingCrateSellFromSubList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false, KeepSpaceForScrollBar = true };
|
||||
|
||||
var relevantBalanceContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), shoppingCrateInventoryContainer.RectTransform), isHorizontal: true)
|
||||
{
|
||||
|
||||
@@ -325,7 +325,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
var specializationList = GetSpecializationList();
|
||||
GetSpecializationList().RectTransform.Resize(new Point(specializationList.Content.Children.Sum(static c => c.Rect.Width), specializationList.Rect.Height));
|
||||
GetSpecializationList().Content.RectTransform.Resize(new Point(specializationList.Content.Children.Sum(static c => c.Rect.Width), specializationList.Rect.Height), resizeChildren: false);
|
||||
|
||||
GUITextBlock.AutoScaleAndNormalize(subTreeNames);
|
||||
|
||||
@@ -439,7 +439,8 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
if (character is null) { return false; }
|
||||
|
||||
|
||||
Identifier talentIdentifier = (Identifier)userData;
|
||||
if (talentOption.MaxChosenTalents is 1)
|
||||
{
|
||||
// deselect other buttons in tier by removing their selected talents from pool
|
||||
@@ -454,9 +455,11 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
Identifier talentIdentifier = (Identifier)userData;
|
||||
|
||||
if (IsViableTalentForCharacter(info.Character, talentIdentifier, selectedTalents))
|
||||
if (character.HasTalent(talentIdentifier))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (IsViableTalentForCharacter(info.Character, talentIdentifier, selectedTalents))
|
||||
{
|
||||
if (!selectedTalents.Contains(talentIdentifier))
|
||||
{
|
||||
@@ -467,7 +470,7 @@ namespace Barotrauma
|
||||
selectedTalents.Remove(talentIdentifier);
|
||||
}
|
||||
}
|
||||
else if (!character.HasTalent(talentIdentifier))
|
||||
else
|
||||
{
|
||||
selectedTalents.Remove(talentIdentifier);
|
||||
}
|
||||
|
||||
@@ -314,7 +314,7 @@ namespace Barotrauma
|
||||
GraphicsDeviceManager.SynchronizeWithVerticalRetrace = GameSettings.CurrentConfig.Graphics.VSync;
|
||||
SetWindowMode(GameSettings.CurrentConfig.Graphics.DisplayMode);
|
||||
|
||||
defaultViewport = GraphicsDevice.Viewport;
|
||||
defaultViewport = new Viewport(0, 0, GraphicsWidth, GraphicsHeight);
|
||||
|
||||
if (recalculateFontsAndStyles)
|
||||
{
|
||||
@@ -353,6 +353,7 @@ namespace Barotrauma
|
||||
public void ResetViewPort()
|
||||
{
|
||||
GraphicsDevice.Viewport = defaultViewport;
|
||||
GraphicsDevice.ScissorRectangle = defaultViewport.Bounds;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -28,18 +28,27 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (node.ParentIndex > -1)
|
||||
{
|
||||
Vector2 diff = nodes[node.ParentIndex].WorldPosition - node.WorldPosition;
|
||||
float dist = diff.Length();
|
||||
Vector2 normalizedDiff = diff / dist;
|
||||
for (float x = 0.0f; x < dist; x += 50.0f)
|
||||
{
|
||||
var spark = GameMain.ParticleManager.CreateParticle("ElectricShock", node.WorldPosition + normalizedDiff * x, Vector2.Zero);
|
||||
if (spark != null)
|
||||
{
|
||||
spark.Size *= 0.3f;
|
||||
}
|
||||
}
|
||||
CreateParticlesBetween(nodes[node.ParentIndex].WorldPosition, node.WorldPosition);
|
||||
}
|
||||
}
|
||||
foreach (var character in charactersInRange)
|
||||
{
|
||||
CreateParticlesBetween(character.character.WorldPosition, character.node.WorldPosition);
|
||||
}
|
||||
|
||||
static void CreateParticlesBetween(Vector2 start, Vector2 end)
|
||||
{
|
||||
const float ParticleInterval = 50.0f;
|
||||
Vector2 diff = end - start;
|
||||
float dist = diff.Length();
|
||||
Vector2 normalizedDiff = MathUtils.NearlyEqual(dist, 0.0f) ? Vector2.Zero : diff / dist;
|
||||
for (float x = 0.0f; x < dist; x += ParticleInterval)
|
||||
{
|
||||
var spark = GameMain.ParticleManager.CreateParticle("ElectricShock", start + normalizedDiff * x, Vector2.Zero);
|
||||
if (spark != null)
|
||||
{
|
||||
spark.Size *= 0.3f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,10 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private GUIFrame selectedItemFrame;
|
||||
private GUIFrame selectedItemReqsFrame;
|
||||
|
||||
|
||||
private GUITextBlock amountTextMin, amountTextMax;
|
||||
private GUIScrollBar amountInput;
|
||||
|
||||
public GUIButton ActivateButton
|
||||
{
|
||||
get { return activateButton; }
|
||||
@@ -160,14 +163,46 @@ namespace Barotrauma.Items.Components
|
||||
new GUICustomComponent(new RectTransform(Vector2.One, inputInventoryHolder.RectTransform), DrawInputOverLay) { CanBeFocused = false };
|
||||
|
||||
// === ACTIVATE BUTTON === //
|
||||
var buttonFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.3f, 0.8f), inputArea.RectTransform), childAnchor: Anchor.CenterRight);
|
||||
activateButton = new GUIButton(new RectTransform(new Vector2(1f, 0.6f), buttonFrame.RectTransform),
|
||||
TextManager.Get(CreateButtonText), style: "DeviceButtonFixedSize")
|
||||
var buttonFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.3f, 0.9f), inputArea.RectTransform))
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.05f
|
||||
};
|
||||
|
||||
var amountInputHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.4f), buttonFrame.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
amountTextMin = new GUITextBlock(new RectTransform(new Vector2(0.15f, 1.0f), amountInputHolder.RectTransform), "1", textAlignment: Alignment.Center);
|
||||
|
||||
amountInput = new GUIScrollBar(new RectTransform(new Vector2(0.7f, 1.0f), amountInputHolder.RectTransform), barSize: 0.1f, style: "GUISlider")
|
||||
{
|
||||
OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
|
||||
{
|
||||
scrollBar.Step = 1.0f / Math.Max(scrollBar.Range.Y - 1, 1);
|
||||
AmountToFabricate = (int)MathF.Round(scrollBar.BarScrollValue);
|
||||
RefreshActivateButtonText();
|
||||
if (GameMain.Client != null)
|
||||
{
|
||||
item.CreateClientEvent(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
amountTextMax = new GUITextBlock(new RectTransform(new Vector2(0.15f, 1.0f), amountInputHolder.RectTransform), "1", textAlignment: Alignment.Center);
|
||||
|
||||
activateButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.6f), buttonFrame.RectTransform),
|
||||
TextManager.Get(CreateButtonText), style: "DeviceButton")
|
||||
{
|
||||
OnClicked = StartButtonClicked,
|
||||
UserData = selectedItem,
|
||||
Enabled = false
|
||||
};
|
||||
};
|
||||
|
||||
//spacing
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.01f), buttonFrame.RectTransform), style: null);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -192,6 +227,21 @@ namespace Barotrauma.Items.Components
|
||||
CreateRecipes();
|
||||
}
|
||||
|
||||
private void RefreshActivateButtonText()
|
||||
{
|
||||
if (amountInput == null)
|
||||
{
|
||||
activateButton.Text = TextManager.Get(IsActive ? "FabricatorCancel" : CreateButtonText);
|
||||
}
|
||||
else
|
||||
{
|
||||
activateButton.Text =
|
||||
IsActive ?
|
||||
$"{TextManager.Get("FabricatorCancel")} ({amountRemaining})" :
|
||||
$"{TextManager.Get(CreateButtonText)} ({AmountToFabricate})";
|
||||
}
|
||||
}
|
||||
|
||||
partial void CreateRecipes()
|
||||
{
|
||||
itemList.Content.RectTransform.ClearChildren();
|
||||
@@ -247,7 +297,7 @@ namespace Barotrauma.Items.Components
|
||||
outputContainer.Inventory.RectTransform = outputInventoryHolder.RectTransform;
|
||||
}
|
||||
|
||||
private LocalizedString GetRecipeNameAndAmount(FabricationRecipe fabricationRecipe)
|
||||
private static LocalizedString GetRecipeNameAndAmount(FabricationRecipe fabricationRecipe)
|
||||
{
|
||||
if (fabricationRecipe == null) { return ""; }
|
||||
if (fabricationRecipe.Amount > 1)
|
||||
@@ -269,7 +319,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
partial void SelectProjSpecific(Character character)
|
||||
{
|
||||
var nonItems = itemList.Content.Children.Where(c => !(c.UserData is FabricationRecipe)).ToList();
|
||||
var nonItems = itemList.Content.Children.Where(c => c.UserData is not FabricationRecipe).ToList();
|
||||
nonItems.ForEach(i => itemList.Content.RemoveChild(i));
|
||||
|
||||
itemList.Content.RectTransform.SortChildren((c1, c2) =>
|
||||
@@ -567,7 +617,7 @@ namespace Barotrauma.Items.Components
|
||||
bool recipeVisible = false;
|
||||
foreach (GUIComponent child in itemList.Content.Children.Reverse())
|
||||
{
|
||||
if (!(child.UserData is FabricationRecipe recipe))
|
||||
if (child.UserData is not FabricationRecipe recipe)
|
||||
{
|
||||
if (child.Enabled)
|
||||
{
|
||||
@@ -598,9 +648,23 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
this.selectedItem = selectedItem;
|
||||
|
||||
int max = Math.Max(selectedItem.TargetItem.MaxStackSize / selectedItem.Amount, 1);
|
||||
|
||||
if (amountInput != null)
|
||||
{
|
||||
float prevBarScroll = amountInput.BarScroll;
|
||||
amountInput.Range = new Vector2(1, max);
|
||||
amountInput.BarScroll = prevBarScroll;
|
||||
|
||||
amountTextMax.Text = max.ToString();
|
||||
amountInput.Enabled = amountTextMax.Enabled = max > 1;
|
||||
AmountToFabricate = Math.Min((int)amountInput.BarScrollValue, max);
|
||||
}
|
||||
RefreshActivateButtonText();
|
||||
|
||||
selectedItemFrame.ClearChildren();
|
||||
selectedItemReqsFrame.ClearChildren();
|
||||
|
||||
|
||||
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.9f), selectedItemFrame.RectTransform, Anchor.Center)) { RelativeSpacing = 0.03f };
|
||||
var paddedReqFrame = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.9f), selectedItemReqsFrame.RectTransform, Anchor.Center)) { RelativeSpacing = 0.03f };
|
||||
|
||||
@@ -734,7 +798,9 @@ namespace Barotrauma.Items.Components
|
||||
outputSlot.Flash(GUIStyle.Red);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
amountRemaining = AmountToFabricate;
|
||||
|
||||
if (GameMain.Client != null)
|
||||
{
|
||||
pendingFabricatedItem = fabricatedItem != null ? null : selectedItem;
|
||||
@@ -777,7 +843,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
foreach (GUIComponent child in itemList.Content.Children)
|
||||
{
|
||||
if (!(child.UserData is FabricationRecipe recipe)) { continue; }
|
||||
if (child.UserData is not FabricationRecipe recipe) { continue; }
|
||||
|
||||
if (recipe != selectedItem &&
|
||||
(child.Rect.Y > itemList.Rect.Bottom || child.Rect.Bottom < itemList.Rect.Y))
|
||||
@@ -811,11 +877,14 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
uint recipeHash = pendingFabricatedItem?.RecipeHash ?? 0;
|
||||
msg.WriteUInt32(recipeHash);
|
||||
msg.WriteRangedInteger(AmountToFabricate, 1, MaxAmountToFabricate);
|
||||
}
|
||||
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
FabricatorState newState = (FabricatorState)msg.ReadByte();
|
||||
int amountToFabricate = msg.ReadRangedInteger(0, MaxAmountToFabricate);
|
||||
int amountRemaining = msg.ReadRangedInteger(0, MaxAmountToFabricate);
|
||||
float newTimeUntilReady = msg.ReadSingle();
|
||||
uint recipeHash = msg.ReadUInt32();
|
||||
UInt16 userID = msg.ReadUInt16();
|
||||
@@ -828,6 +897,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
State = newState;
|
||||
this.amountToFabricate = amountToFabricate;
|
||||
this.amountRemaining = amountRemaining;
|
||||
if (newState == FabricatorState.Stopped || recipeHash == 0)
|
||||
{
|
||||
CancelFabricating();
|
||||
|
||||
@@ -802,21 +802,34 @@ namespace Barotrauma.Items.Components
|
||||
if (passivePingRadius > 0.0f)
|
||||
{
|
||||
if (activePingsCount == 0) { disruptedDirections.Clear(); }
|
||||
//emit "pings" from nearby sound-emitting AITargets to reveal what's around them
|
||||
foreach (AITarget t in AITarget.List)
|
||||
{
|
||||
if (t.Entity is Character c && !c.IsUnconscious && c.Params.HideInSonar) { continue; }
|
||||
if (t.SoundRange <= 0.0f || float.IsNaN(t.SoundRange) || float.IsInfinity(t.SoundRange)) { continue; }
|
||||
|
||||
|
||||
float distSqr = Vector2.DistanceSquared(t.WorldPosition, transducerCenter);
|
||||
if (distSqr > t.SoundRange * t.SoundRange * 2) { continue; }
|
||||
|
||||
float dist = (float)Math.Sqrt(distSqr);
|
||||
if (dist > prevPassivePingRadius * Range && dist <= passivePingRadius * Range && Rand.Int(sonarBlips.Count) < 500 && t.IsWithinSector(transducerCenter))
|
||||
if (dist > prevPassivePingRadius * Range && dist <= passivePingRadius * Range && Rand.Int(sonarBlips.Count) < 500)
|
||||
{
|
||||
int prevBlipCount = sonarBlips.Count;
|
||||
Ping(t.WorldPosition, transducerCenter,
|
||||
Math.Min(t.SoundRange, range * 0.5f) * displayScale, 0, displayScale, Math.Min(t.SoundRange, range * 0.5f),
|
||||
Math.Min(t.SoundRange, range * 0.5f) * displayScale, 0, displayScale, Math.Min(t.SoundRange, range * 0.5f),
|
||||
passive: true, pingStrength: 0.5f);
|
||||
sonarBlips.Add(new SonarBlip(t.WorldPosition, 1.0f, 1.0f));
|
||||
//remove blips that weren't in the AITarget's sector
|
||||
if (t.HasSector())
|
||||
{
|
||||
for (int i = sonarBlips.Count - 1; i >= prevBlipCount; i--)
|
||||
{
|
||||
if (!t.IsWithinSector(sonarBlips[i].Position))
|
||||
{
|
||||
sonarBlips.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -411,6 +411,14 @@ namespace Barotrauma.Items.Components
|
||||
SpriteEffects.None, newDepth);
|
||||
}
|
||||
|
||||
if (GameMain.DebugDraw)
|
||||
{
|
||||
Vector2 firingPos = GetRelativeFiringPosition();
|
||||
firingPos.Y = -firingPos.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 (!editing || GUI.DisableHUD || !item.IsSelected) { return; }
|
||||
|
||||
const float widgetRadius = 60.0f;
|
||||
|
||||
@@ -343,38 +343,31 @@ namespace Barotrauma.Lights
|
||||
{
|
||||
SolidColorEffect.CurrentTechnique = SolidColorEffect.Techniques["SolidVertexColor"];
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, effect: SolidColorEffect, transformMatrix: spriteBatchTransform);
|
||||
foreach (Character character in Character.CharacterList)
|
||||
{
|
||||
if (character.CurrentHull == null || !character.Enabled || !character.IsVisible) { continue; }
|
||||
if (Character.Controlled?.FocusedCharacter == character) { continue; }
|
||||
Color lightColor = character.CurrentHull.AmbientLight == Color.TransparentBlack ?
|
||||
Color.Black :
|
||||
character.CurrentHull.AmbientLight.Multiply(character.CurrentHull.AmbientLight.A / 255.0f).Opaque();
|
||||
foreach (Limb limb in character.AnimController.Limbs)
|
||||
{
|
||||
if (limb.DeformSprite != null) { continue; }
|
||||
limb.Draw(spriteBatch, cam, lightColor);
|
||||
}
|
||||
}
|
||||
DrawCharacters(spriteBatch, cam, drawDeformSprites: false);
|
||||
spriteBatch.End();
|
||||
|
||||
DeformableSprite.Effect.CurrentTechnique = DeformableSprite.Effect.Techniques["DeformShaderSolidVertexColor"];
|
||||
DeformableSprite.Effect.CurrentTechnique.Passes[0].Apply();
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, transformMatrix: spriteBatchTransform);
|
||||
DrawCharacters(spriteBatch, cam, drawDeformSprites: true);
|
||||
spriteBatch.End();
|
||||
}
|
||||
|
||||
static void DrawCharacters(SpriteBatch spriteBatch, Camera cam, bool drawDeformSprites)
|
||||
{
|
||||
foreach (Character character in Character.CharacterList)
|
||||
{
|
||||
if (character.CurrentHull == null || !character.Enabled || !character.IsVisible) { continue; }
|
||||
if (character.CurrentHull == null || !character.Enabled || !character.IsVisible || character.InvisibleTimer > 0.0f) { continue; }
|
||||
if (Character.Controlled?.FocusedCharacter == character) { continue; }
|
||||
Color lightColor = character.CurrentHull.AmbientLight == Color.TransparentBlack ?
|
||||
Color.Black :
|
||||
character.CurrentHull.AmbientLight.Multiply(character.CurrentHull.AmbientLight.A / 255.0f).Opaque();
|
||||
foreach (Limb limb in character.AnimController.Limbs)
|
||||
{
|
||||
if (limb.DeformSprite == null) { continue; }
|
||||
if (drawDeformSprites == (limb.DeformSprite == null)) { continue; }
|
||||
limb.Draw(spriteBatch, cam, lightColor);
|
||||
}
|
||||
}
|
||||
spriteBatch.End();
|
||||
}
|
||||
|
||||
DeformableSprite.Effect.CurrentTechnique = DeformableSprite.Effect.Techniques["DeformShader"];
|
||||
|
||||
@@ -697,17 +697,22 @@ namespace Barotrauma
|
||||
Vector2 size = new Vector2(Math.Max(nameSize.X, Math.Max(typeSize.X, descSize.X)), nameSize.Y + typeSize.Y + descSize.Y);
|
||||
|
||||
int highestSubTier = HighlightedLocation.HighestSubmarineTierAvailable();
|
||||
var overrideTiers = new List<(SubmarineClass subClass, int tier)>();
|
||||
foreach (SubmarineClass subClass in Enum.GetValues(typeof(SubmarineClass)))
|
||||
List<(SubmarineClass subClass, int tier)> overrideTiers = null;
|
||||
if (HighlightedLocation.CanHaveSubsForSale())
|
||||
{
|
||||
if (subClass == SubmarineClass.Undefined) { continue; }
|
||||
int highestClassTier = HighlightedLocation.HighestSubmarineTierAvailable(subClass);
|
||||
if (highestClassTier > 0 && highestClassTier > highestSubTier)
|
||||
overrideTiers = new List<(SubmarineClass subClass, int tier)>();
|
||||
foreach (SubmarineClass subClass in Enum.GetValues(typeof(SubmarineClass)))
|
||||
{
|
||||
overrideTiers.Add((subClass, highestClassTier));
|
||||
if (subClass == SubmarineClass.Undefined) { continue; }
|
||||
int highestClassTier = HighlightedLocation.HighestSubmarineTierAvailable(subClass);
|
||||
if (highestClassTier > 0 && highestClassTier > highestSubTier)
|
||||
{
|
||||
overrideTiers.Add((subClass, highestClassTier));
|
||||
}
|
||||
}
|
||||
}
|
||||
size.Y += ((highestSubTier > 0 ? 1 : 0) + overrideTiers.Count) * GUIStyle.SmallFont.MeasureString(TextManager.Get("advancedsub.all")).Y;
|
||||
int subAvailabilityTextCount = (highestSubTier > 0 ? 1 : 0) + (overrideTiers?.Count ?? 0);
|
||||
size.Y += subAvailabilityTextCount * GUIStyle.SmallFont.MeasureString(TextManager.Get("advancedsub.all")).Y;
|
||||
|
||||
bool showReputation = hudVisibility > 0.0f && HighlightedLocation.Discovered && HighlightedLocation.Type.HasOutpost && HighlightedLocation.Reputation != null;
|
||||
LocalizedString repLabelText = null, repValueText = null;
|
||||
@@ -745,9 +750,12 @@ namespace Barotrauma
|
||||
{
|
||||
DrawSubAvailabilityText("advancedsub.all", highestSubTier);
|
||||
}
|
||||
foreach (var (subClass, tier) in overrideTiers)
|
||||
if (overrideTiers != null)
|
||||
{
|
||||
DrawSubAvailabilityText($"advancedsub.{subClass}", tier);
|
||||
foreach (var (subClass, tier) in overrideTiers)
|
||||
{
|
||||
DrawSubAvailabilityText($"advancedsub.{subClass}", tier);
|
||||
}
|
||||
}
|
||||
void DrawSubAvailabilityText(string tag, int tier)
|
||||
{
|
||||
|
||||
@@ -14,6 +14,16 @@ namespace Barotrauma.Networking
|
||||
set;
|
||||
}
|
||||
|
||||
private SoundChannel radioNoiseChannel;
|
||||
private float radioNoise;
|
||||
|
||||
public float RadioNoise
|
||||
{
|
||||
get { return radioNoise; }
|
||||
set { radioNoise = MathHelper.Clamp(value, 0.0f, 1.0f); }
|
||||
}
|
||||
|
||||
|
||||
private bool mutedLocally;
|
||||
public bool MutedLocally
|
||||
{
|
||||
@@ -42,35 +52,64 @@ namespace Barotrauma.Networking
|
||||
!HasPermission(ClientPermissions.Kick) &&
|
||||
!HasPermission(ClientPermissions.Unban);
|
||||
|
||||
public void UpdateSoundPosition()
|
||||
public void UpdateVoipSound()
|
||||
{
|
||||
if (VoipSound == null) { return; }
|
||||
|
||||
if (!VoipSound.IsPlaying)
|
||||
if (VoipSound == null || !VoipSound.IsPlaying)
|
||||
{
|
||||
DebugConsole.Log("Destroying voipsound");
|
||||
VoipSound.Dispose();
|
||||
radioNoiseChannel?.Dispose();
|
||||
radioNoiseChannel = null;
|
||||
if (VoipSound != null)
|
||||
{
|
||||
DebugConsole.Log("Destroying voipsound");
|
||||
VoipSound.Dispose();
|
||||
}
|
||||
VoipSound = null;
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Screen.Selected is ModDownloadScreen)
|
||||
{
|
||||
VoipSound.Gain = 0.0f;
|
||||
}
|
||||
|
||||
float gain = 1.0f;
|
||||
float noiseGain = 0.0f;
|
||||
Vector3? position = null;
|
||||
if (character != null)
|
||||
{
|
||||
if (GameSettings.CurrentConfig.Audio.UseDirectionalVoiceChat)
|
||||
{
|
||||
VoipSound.SetPosition(new Vector3(character.WorldPosition.X, character.WorldPosition.Y, 0.0f));
|
||||
position = new Vector3(character.WorldPosition.X, character.WorldPosition.Y, 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
VoipSound.SetPosition(null);
|
||||
float dist = Vector3.Distance(new Vector3(character.WorldPosition, 0.0f), GameMain.SoundManager.ListenerPosition);
|
||||
VoipSound.Gain = 1.0f - MathUtils.InverseLerp(VoipSound.Near, VoipSound.Far, dist);
|
||||
gain = 1.0f - MathUtils.InverseLerp(VoipSound.Near, VoipSound.Far, dist);
|
||||
}
|
||||
if (RadioNoise > 0.0f)
|
||||
{
|
||||
noiseGain = gain * RadioNoise;
|
||||
gain *= 1.0f - RadioNoise;
|
||||
}
|
||||
}
|
||||
else
|
||||
VoipSound.SetPosition(position);
|
||||
VoipSound.Gain = gain;
|
||||
if (noiseGain > 0.0f)
|
||||
{
|
||||
VoipSound.SetPosition(null);
|
||||
VoipSound.Gain = 1.0f;
|
||||
if (radioNoiseChannel == null || !radioNoiseChannel.IsPlaying)
|
||||
{
|
||||
radioNoiseChannel = SoundPlayer.PlaySound("radiostatic");
|
||||
radioNoiseChannel.Category = "voip";
|
||||
radioNoiseChannel.Looping = true;
|
||||
}
|
||||
radioNoiseChannel.Near = VoipSound.Near;
|
||||
radioNoiseChannel.Far = VoipSound.Far;
|
||||
radioNoiseChannel.Position = position;
|
||||
radioNoiseChannel.Gain = noiseGain;
|
||||
}
|
||||
else if (radioNoiseChannel != null)
|
||||
{
|
||||
radioNoiseChannel.Gain = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,6 +197,11 @@ namespace Barotrauma.Networking
|
||||
VoipSound.Dispose();
|
||||
VoipSound = null;
|
||||
}
|
||||
if (radioNoiseChannel != null)
|
||||
{
|
||||
radioNoiseChannel.Dispose();
|
||||
radioNoiseChannel = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -449,7 +449,7 @@ namespace Barotrauma.Networking
|
||||
foreach (Client c in ConnectedClients)
|
||||
{
|
||||
if (c.Character != null && c.Character.Removed) { c.Character = null; }
|
||||
c.UpdateSoundPosition();
|
||||
c.UpdateVoipSound();
|
||||
}
|
||||
|
||||
if (VoipCapture.Instance != null)
|
||||
|
||||
+1
-1
@@ -111,7 +111,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
foreach (NetIncomingMessage inc in incomingLidgrenMessages)
|
||||
{
|
||||
if (!inc.SenderConnection.RemoteEndPoint.Equals(lidgrenEndpoint.NetEndpoint))
|
||||
if (!inc.SenderConnection.RemoteEndPoint.EquivalentTo(lidgrenEndpoint.NetEndpoint))
|
||||
{
|
||||
DebugConsole.AddWarning($"Mismatched endpoint: expected {lidgrenEndpoint.NetEndpoint}, got {inc.SenderConnection.RemoteEndPoint}");
|
||||
continue;
|
||||
|
||||
@@ -260,6 +260,13 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Screen.Selected is ModDownloadScreen)
|
||||
{
|
||||
allowEnqueue = false;
|
||||
captureTimer = 0;
|
||||
}
|
||||
|
||||
if (allowEnqueue || captureTimer > 0)
|
||||
{
|
||||
LastEnqueueAudio = DateTime.Now;
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
using Barotrauma.Sounds;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Sounds;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Barotrauma.Items.Components;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
class VoipClient : IDisposable
|
||||
{
|
||||
private GameClient gameClient;
|
||||
private ClientPeer netClient;
|
||||
/// <summary>
|
||||
/// The "near" range of the voice chat (a percentage of either SpeakRange or radio range), further than this the volume starts to diminish
|
||||
/// </summary>
|
||||
const float RangeNear = 0.4f;
|
||||
|
||||
private readonly GameClient gameClient;
|
||||
private readonly ClientPeer netClient;
|
||||
private DateTime lastSendTime;
|
||||
private List<VoipQueue> queues;
|
||||
private readonly List<VoipQueue> queues;
|
||||
|
||||
private UInt16 storedBufferID = 0;
|
||||
|
||||
@@ -32,13 +35,13 @@ namespace Barotrauma.Networking
|
||||
|
||||
public void RegisterQueue(VoipQueue queue)
|
||||
{
|
||||
if (queue == VoipCapture.Instance) return;
|
||||
if (!queues.Contains(queue)) queues.Add(queue);
|
||||
if (queue == VoipCapture.Instance) { return; }
|
||||
if (!queues.Contains(queue)) { queues.Add(queue); }
|
||||
}
|
||||
|
||||
public void UnregisterQueue(VoipQueue queue)
|
||||
{
|
||||
if (queues.Contains(queue)) queues.Remove(queue);
|
||||
if (queues.Contains(queue)) { queues.Remove(queue); }
|
||||
}
|
||||
|
||||
public void SendToServer()
|
||||
@@ -85,6 +88,7 @@ namespace Barotrauma.Networking
|
||||
public void Read(IReadMessage msg)
|
||||
{
|
||||
byte queueId = msg.ReadByte();
|
||||
float distanceFactor = msg.ReadRangedSingle(0.0f, 1.0f, 8);
|
||||
VoipQueue queue = queues.Find(q => q.QueueID == queueId);
|
||||
|
||||
if (queue == null)
|
||||
@@ -105,9 +109,12 @@ namespace Barotrauma.Networking
|
||||
client.VoipSound = new VoipSound(client.Name, GameMain.SoundManager, client.VoipQueue);
|
||||
}
|
||||
GameMain.SoundManager.ForceStreamUpdate();
|
||||
|
||||
client.RadioNoise = 0.0f;
|
||||
if (client.Character != null && !client.Character.IsDead && !client.Character.Removed && client.Character.SpeechImpediment <= 100.0f)
|
||||
{
|
||||
float speechImpedimentMultiplier = 1.0f - client.Character.SpeechImpediment / 100.0f;
|
||||
bool spectating = Character.Controlled == null;
|
||||
float rangeMultiplier = spectating ? 2.0f : 1.0f;
|
||||
WifiComponent radio = null;
|
||||
var messageType = !client.VoipQueue.ForceLocal && ChatMessage.CanUseRadio(client.Character, out radio) ? ChatMessageType.Radio : ChatMessageType.Default;
|
||||
client.Character.ShowSpeechBubble(1.25f, ChatMessage.MessageColor[(int)messageType]);
|
||||
@@ -115,11 +122,17 @@ namespace Barotrauma.Networking
|
||||
client.VoipSound.UseRadioFilter = messageType == ChatMessageType.Radio && !GameSettings.CurrentConfig.Audio.DisableVoiceChatFilters;
|
||||
if (messageType == ChatMessageType.Radio)
|
||||
{
|
||||
client.VoipSound.SetRange(radio.Range * 0.8f, radio.Range);
|
||||
client.VoipSound.SetRange(radio.Range * RangeNear * speechImpedimentMultiplier * rangeMultiplier, radio.Range * speechImpedimentMultiplier * rangeMultiplier);
|
||||
if (distanceFactor > RangeNear && !spectating)
|
||||
{
|
||||
//noise starts increasing exponentially after 40% range
|
||||
client.RadioNoise = MathF.Pow(MathUtils.InverseLerp(RangeNear, 1.0f, distanceFactor), 2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
client.VoipSound.SetRange(ChatMessage.SpeakRange * 0.4f, ChatMessage.SpeakRange);
|
||||
|
||||
client.VoipSound.SetRange(ChatMessage.SpeakRange * RangeNear * speechImpedimentMultiplier * rangeMultiplier, ChatMessage.SpeakRange * speechImpedimentMultiplier * rangeMultiplier);
|
||||
}
|
||||
client.VoipSound.UseMuffleFilter =
|
||||
messageType != ChatMessageType.Radio && Character.Controlled != null && !GameSettings.CurrentConfig.Audio.DisableVoiceChatFilters &&
|
||||
|
||||
+4
-4
@@ -35,14 +35,14 @@ namespace Barotrauma
|
||||
};
|
||||
|
||||
// New game
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.03f), nameSeedLayout.RectTransform) { MinSize = new Point(0, 20) }, TextManager.Get("SaveName"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.BottomLeft);
|
||||
saveNameBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.03f), nameSeedLayout.RectTransform) { MinSize = new Point(0, 20) }, string.Empty)
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.03f), nameSeedLayout.RectTransform) { MinSize = new Point(0, GUI.IntScale(24)) }, TextManager.Get("SaveName"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.BottomLeft);
|
||||
saveNameBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.03f), nameSeedLayout.RectTransform), string.Empty)
|
||||
{
|
||||
textFilterFunction = ToolBox.RemoveInvalidFileNameChars
|
||||
};
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.03f), nameSeedLayout.RectTransform) { MinSize = new Point(0, 20) }, TextManager.Get("MapSeed"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.BottomLeft);
|
||||
seedBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.03f), nameSeedLayout.RectTransform) { MinSize = new Point(0, 20) }, ToolBox.RandomSeed(8));
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.03f), nameSeedLayout.RectTransform) { MinSize = new Point(0, GUI.IntScale(24)) }, TextManager.Get("MapSeed"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.BottomLeft);
|
||||
seedBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.03f), nameSeedLayout.RectTransform), ToolBox.RandomSeed(8));
|
||||
|
||||
nameSeedLayout.RectTransform.MinSize = new Point(0, nameSeedLayout.Children.Sum(c => c.RectTransform.MinSize.Y));
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Barotrauma.Sounds
|
||||
private VorbisReader reader;
|
||||
|
||||
//key = sample rate, value = filter
|
||||
private static Dictionary<int, BiQuad> muffleFilters = new Dictionary<int, BiQuad>();
|
||||
private static readonly Dictionary<int, BiQuad> muffleFilters = new Dictionary<int, BiQuad>();
|
||||
|
||||
private static List<float> playbackAmplitude;
|
||||
private const int AMPLITUDE_SAMPLE_COUNT = 4410; //100ms in a 44100hz file
|
||||
@@ -29,8 +29,8 @@ namespace Barotrauma.Sounds
|
||||
|
||||
public override int FillStreamBuffer(int samplePos, short[] buffer)
|
||||
{
|
||||
if (!Stream) throw new Exception("Called FillStreamBuffer on a non-streamed sound!");
|
||||
if (reader == null) throw new Exception("Called FillStreamBuffer when the reader is null!");
|
||||
if (!Stream) { throw new Exception("Called FillStreamBuffer on a non-streamed sound!"); }
|
||||
if (reader == null) { throw new Exception("Called FillStreamBuffer when the reader is null!"); }
|
||||
|
||||
if (samplePos >= reader.TotalSamples * reader.Channels * 2) return 0;
|
||||
|
||||
|
||||
@@ -24,11 +24,12 @@ namespace Barotrauma.Sounds
|
||||
|
||||
public readonly bool StreamsReliably;
|
||||
|
||||
private readonly SoundManager.SourcePoolIndex sourcePoolIndex = SoundManager.SourcePoolIndex.Default;
|
||||
public virtual SoundManager.SourcePoolIndex SourcePoolIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
return SoundManager.SourcePoolIndex.Default;
|
||||
return sourcePoolIndex;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,13 +60,14 @@ namespace Barotrauma.Sounds
|
||||
public float BaseNear;
|
||||
public float BaseFar;
|
||||
|
||||
public Sound(SoundManager owner, string filename, bool stream, bool streamsReliably, XElement xElement=null, bool getFullPath=true)
|
||||
public Sound(SoundManager owner, string filename, bool stream, bool streamsReliably, XElement xElement = null, bool getFullPath = true)
|
||||
{
|
||||
Owner = owner;
|
||||
Filename = getFullPath ? Path.GetFullPath(filename.CleanUpPath()).CleanUpPath() : filename;
|
||||
Stream = stream;
|
||||
StreamsReliably = streamsReliably;
|
||||
XElement = xElement;
|
||||
sourcePoolIndex = XElement.GetAttributeEnum("sourcepool", SoundManager.SourcePoolIndex.Default);
|
||||
|
||||
BaseGain = 1.0f;
|
||||
BaseNear = 100.0f;
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using OpenAL;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Barotrauma.Sounds
|
||||
{
|
||||
@@ -17,7 +15,7 @@ namespace Barotrauma.Sounds
|
||||
|
||||
public SoundSourcePool(int sourceCount = SoundManager.SOURCE_COUNT)
|
||||
{
|
||||
int alError = Al.NoError;
|
||||
int alError;
|
||||
|
||||
ALSources = new uint[sourceCount];
|
||||
for (int i = 0; i < sourceCount; i++)
|
||||
@@ -83,7 +81,7 @@ namespace Barotrauma.Sounds
|
||||
class SoundChannel : IDisposable
|
||||
{
|
||||
private const int STREAM_BUFFER_SIZE = 8820;
|
||||
private short[] streamShortBuffer;
|
||||
private readonly short[] streamShortBuffer;
|
||||
|
||||
private string debugName = "SoundChannel";
|
||||
|
||||
@@ -312,12 +310,12 @@ namespace Barotrauma.Sounds
|
||||
|
||||
if (ALSourceIndex < 0) { return; }
|
||||
|
||||
if (!IsPlaying) return;
|
||||
if (!IsPlaying) { return; }
|
||||
|
||||
if (!IsStream)
|
||||
{
|
||||
uint alSource = Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex);
|
||||
int playbackPos; Al.GetSourcei(alSource, Al.SampleOffset, out playbackPos);
|
||||
Al.GetSourcei(alSource, Al.SampleOffset, out int playbackPos);
|
||||
int alError = Al.GetError();
|
||||
if (alError != Al.NoError)
|
||||
{
|
||||
@@ -379,7 +377,7 @@ namespace Barotrauma.Sounds
|
||||
|
||||
if (!IsStream)
|
||||
{
|
||||
int playbackPos; Al.GetSourcei(alSource, Al.SampleOffset, out playbackPos);
|
||||
Al.GetSourcei(alSource, Al.SampleOffset, out int playbackPos);
|
||||
int alError = Al.GetError();
|
||||
if (alError != Al.NoError)
|
||||
{
|
||||
@@ -390,7 +388,7 @@ namespace Barotrauma.Sounds
|
||||
}
|
||||
else
|
||||
{
|
||||
float retVal = -1.0f;
|
||||
float retVal;
|
||||
Monitor.Enter(mutex);
|
||||
retVal = streamAmplitude;
|
||||
Monitor.Exit(mutex);
|
||||
@@ -432,8 +430,8 @@ namespace Barotrauma.Sounds
|
||||
private bool reachedEndSample;
|
||||
private int queueStartIndex;
|
||||
private readonly uint[] streamBuffers;
|
||||
private uint[] unqueuedBuffers;
|
||||
private float[] streamBufferAmplitudes;
|
||||
private readonly uint[] unqueuedBuffers;
|
||||
private readonly float[] streamBufferAmplitudes;
|
||||
|
||||
public int StreamSeekPos
|
||||
{
|
||||
@@ -448,18 +446,17 @@ namespace Barotrauma.Sounds
|
||||
}
|
||||
}
|
||||
|
||||
private object mutex;
|
||||
private readonly object mutex;
|
||||
|
||||
public bool IsPlaying
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ALSourceIndex < 0) return false;
|
||||
if (IsStream && !reachedEndSample) return true;
|
||||
int state;
|
||||
if (ALSourceIndex < 0) { return false; }
|
||||
if (IsStream && !reachedEndSample) { return true; }
|
||||
uint alSource = Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex);
|
||||
if (!Al.IsSource(alSource)) return false;
|
||||
Al.GetSourcei(alSource, Al.SourceState, out state);
|
||||
if (!Al.IsSource(alSource)) { return false; }
|
||||
Al.GetSourcei(alSource, Al.SourceState, out int state);
|
||||
int alError = Al.GetError();
|
||||
if (alError != Al.NoError)
|
||||
{
|
||||
@@ -710,8 +707,7 @@ namespace Barotrauma.Sounds
|
||||
{
|
||||
uint alSource = Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex);
|
||||
|
||||
int state;
|
||||
Al.GetSourcei(alSource, Al.SourceState, out state);
|
||||
Al.GetSourcei(alSource, Al.SourceState, out int state);
|
||||
bool playing = state == Al.Playing;
|
||||
int alError = Al.GetError();
|
||||
if (alError != Al.NoError)
|
||||
@@ -719,8 +715,7 @@ namespace Barotrauma.Sounds
|
||||
throw new Exception("Failed to determine playing state from streamed source: " + debugName + ", " + Al.GetErrorString(alError));
|
||||
}
|
||||
|
||||
int unqueuedBufferCount;
|
||||
Al.GetSourcei(alSource, Al.BuffersProcessed, out unqueuedBufferCount);
|
||||
Al.GetSourcei(alSource, Al.BuffersProcessed, out int unqueuedBufferCount);
|
||||
alError = Al.GetError();
|
||||
if (alError != Al.NoError)
|
||||
{
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
using Barotrauma.IO;
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Networking;
|
||||
using Concentus.Structs;
|
||||
using Microsoft.Xna.Framework;
|
||||
using OpenAL;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Barotrauma.Sounds
|
||||
{
|
||||
@@ -26,12 +24,12 @@ namespace Barotrauma.Sounds
|
||||
}
|
||||
}
|
||||
|
||||
private VoipQueue queue;
|
||||
private readonly VoipQueue queue;
|
||||
private int bufferID = 0;
|
||||
|
||||
private SoundChannel soundChannel;
|
||||
|
||||
private OpusDecoder decoder;
|
||||
private readonly OpusDecoder decoder;
|
||||
|
||||
public bool UseRadioFilter;
|
||||
public bool UseMuffleFilter;
|
||||
@@ -39,11 +37,11 @@ namespace Barotrauma.Sounds
|
||||
public float Near { get; private set; }
|
||||
public float Far { get; private set; }
|
||||
|
||||
private BiQuad[] muffleFilters = new BiQuad[]
|
||||
private readonly BiQuad[] muffleFilters = new BiQuad[]
|
||||
{
|
||||
new LowpassFilter(VoipConfig.FREQUENCY, 800)
|
||||
};
|
||||
private BiQuad[] radioFilters = new BiQuad[]
|
||||
private readonly BiQuad[] radioFilters = new BiQuad[]
|
||||
{
|
||||
new BandpassFilter(VoipConfig.FREQUENCY, 2000)
|
||||
};
|
||||
@@ -101,13 +99,14 @@ namespace Barotrauma.Sounds
|
||||
|
||||
public void ApplyFilters(short[] buffer, int readSamples)
|
||||
{
|
||||
float finalGain = gain * GameSettings.CurrentConfig.Audio.VoiceChatVolume;
|
||||
for (int i = 0; i < readSamples; i++)
|
||||
{
|
||||
float fVal = ShortToFloat(buffer[i]);
|
||||
|
||||
if (gain * GameSettings.CurrentConfig.Audio.VoiceChatVolume > 1.0f) //TODO: take distance into account?
|
||||
if (finalGain > 1.0f) //TODO: take distance into account?
|
||||
{
|
||||
fVal = Math.Clamp(fVal * gain * GameSettings.CurrentConfig.Audio.VoiceChatVolume, -1f, 1f);
|
||||
fVal = Math.Clamp(fVal * finalGain, -1f, 1f);
|
||||
}
|
||||
|
||||
if (UseMuffleFilter)
|
||||
|
||||
Reference in New Issue
Block a user