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)
|
if (Controlled == null || Controlled == this || (Controlled.CharacterHealth.GetAffliction("psychosis")?.Strength ?? 0.0f) <= 0.0f)
|
||||||
{
|
{
|
||||||
InvisibleTimer = 0.0f;
|
InvisibleTimer = Math.Min(InvisibleTimer, 1.0f);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
InvisibleTimer -= deltaTime;
|
|
||||||
}
|
}
|
||||||
|
InvisibleTimer -= deltaTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (GUIMessage message in guiMessages)
|
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")
|
showHiddenAfflictionsButton = new GUIButton(new RectTransform(new Point(afflictionIconContainer.Rect.Height), afflictionIconContainer.RectTransform), style: "GUIButtonCircular")
|
||||||
{
|
{
|
||||||
|
Visible = false,
|
||||||
CanBeFocused = 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) =>
|
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;
|
float defaultZoom = Screen.Selected.Cam.DefaultZoom;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Barotrauma;
|
namespace Barotrauma;
|
||||||
@@ -10,37 +11,66 @@ partial class UIHighlightAction : EventAction
|
|||||||
partial void UpdateProjSpecific()
|
partial void UpdateProjSpecific()
|
||||||
{
|
{
|
||||||
bool useCircularFlash = false;
|
bool useCircularFlash = false;
|
||||||
GUIComponent component = null;
|
|
||||||
|
|
||||||
if (Id != ElementId.None)
|
if (Id != ElementId.None)
|
||||||
{
|
{
|
||||||
component = GUI.GetAdditions().FirstOrDefault(c => Equals(Id, c.UserData));
|
FindAndFlashComponents(c => Equals(Id, c.UserData));
|
||||||
}
|
}
|
||||||
else if (!EntityIdentifier.IsEmpty)
|
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);
|
c.UserData is MapEntityPrefab mep && mep.Identifier == EntityIdentifier || c.UserData is MapEntity me && me.Prefab.Identifier == EntityIdentifier);
|
||||||
}
|
}
|
||||||
else if (!OrderIdentifier.IsEmpty)
|
else if (!OrderIdentifier.IsEmpty)
|
||||||
{
|
{
|
||||||
useCircularFlash = true;
|
useCircularFlash = true;
|
||||||
|
bool foundMinimapNode = false;
|
||||||
if (!OrderTargetTag.IsEmpty)
|
if (!OrderTargetTag.IsEmpty)
|
||||||
{
|
{
|
||||||
component =
|
foundMinimapNode = FindAndFlashComponents(c =>
|
||||||
GUI.GetAdditions().FirstOrDefault(c =>
|
c.UserData is CrewManager.MinimapNodeData nodeData && nodeData.Order is Order order &&
|
||||||
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));
|
||||||
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);
|
foreach (var predicate in predicates)
|
||||||
component.Bounce |= Bounce;
|
{
|
||||||
|
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.Collections.Immutable;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Xml.Linq;
|
||||||
using Barotrauma.Threading;
|
using Barotrauma.Threading;
|
||||||
|
|
||||||
namespace Barotrauma
|
namespace Barotrauma
|
||||||
@@ -37,7 +38,7 @@ namespace Barotrauma
|
|||||||
private set;
|
private set;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsCJK
|
public TextManager.SpeciallyHandledCharCategory SpeciallyHandledCharCategory
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
private set;
|
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)
|
public ScalableFont(ContentXElement element, GraphicsDevice gd = null)
|
||||||
: this(
|
: this(
|
||||||
element.GetAttributeContentPath("file")?.Value,
|
element.GetAttributeContentPath("file")?.Value,
|
||||||
(uint)element.GetAttributeInt("size", 14),
|
(uint)element.GetAttributeInt("size", 14),
|
||||||
gd,
|
gd,
|
||||||
element.GetAttributeBool("dynamicloading", false),
|
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)
|
lock (globalMutex)
|
||||||
{
|
{
|
||||||
@@ -120,7 +139,7 @@ namespace Barotrauma
|
|||||||
this.textures = new List<Texture2D>();
|
this.textures = new List<Texture2D>();
|
||||||
this.texCoords = new Dictionary<uint, GlyphData>();
|
this.texCoords = new Dictionary<uint, GlyphData>();
|
||||||
this.DynamicLoading = dynamicLoading;
|
this.DynamicLoading = dynamicLoading;
|
||||||
this.IsCJK = isCJK;
|
this.SpeciallyHandledCharCategory = speciallyHandledCharCategory;
|
||||||
this.graphicsDevice = gd;
|
this.graphicsDevice = gd;
|
||||||
|
|
||||||
if (gd != null && !dynamicLoading)
|
if (gd != null && !dynamicLoading)
|
||||||
|
|||||||
@@ -150,7 +150,6 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: fix implicit hiding
|
|
||||||
public override bool Selected
|
public override bool Selected
|
||||||
{
|
{
|
||||||
get { return isSelected; }
|
get { return isSelected; }
|
||||||
|
|||||||
@@ -179,6 +179,11 @@ namespace Barotrauma
|
|||||||
private set;
|
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;
|
public float valueStep;
|
||||||
|
|
||||||
private float pressedTimer;
|
private float pressedTimer;
|
||||||
@@ -403,13 +408,19 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
if (MinValueFloat != null)
|
if (MinValueFloat != null)
|
||||||
{
|
{
|
||||||
floatValue = Math.Max(floatValue, MinValueFloat.Value);
|
floatValue =
|
||||||
MinusButton.Enabled = floatValue > MinValueFloat;
|
WrapAround && MinValueFloat.HasValue && floatValue < MinValueFloat.Value ?
|
||||||
|
MaxValueFloat.Value :
|
||||||
|
Math.Max(floatValue, MinValueFloat.Value);
|
||||||
|
MinusButton.Enabled = WrapAround || floatValue > MinValueFloat;
|
||||||
}
|
}
|
||||||
if (MaxValueFloat != null)
|
if (MaxValueFloat != null)
|
||||||
{
|
{
|
||||||
floatValue = Math.Min(floatValue, MaxValueFloat.Value);
|
floatValue =
|
||||||
PlusButton.Enabled = floatValue < MaxValueFloat;
|
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)
|
if (MinValueInt != null && intValue < MinValueInt.Value)
|
||||||
{
|
{
|
||||||
intValue = Math.Max(intValue, MinValueInt.Value);
|
intValue = WrapAround && MaxValueInt.HasValue ? MaxValueInt.Value : Math.Max(intValue, MinValueInt.Value);
|
||||||
UpdateText();
|
UpdateText();
|
||||||
}
|
}
|
||||||
if (MaxValueInt != null && intValue > MaxValueInt.Value)
|
if (MaxValueInt != null && intValue > MaxValueInt.Value)
|
||||||
{
|
{
|
||||||
intValue = Math.Min(intValue, MaxValueInt.Value);
|
intValue = WrapAround && MinValueInt.HasValue ? MinValueInt.Value : Math.Min(intValue, MaxValueInt.Value);
|
||||||
UpdateText();
|
UpdateText();
|
||||||
}
|
}
|
||||||
PlusButton.Enabled = MaxValueInt == null || intValue < MaxValueInt;
|
PlusButton.Enabled = WrapAround || MaxValueInt == null || intValue < MaxValueInt;
|
||||||
MinusButton.Enabled = MinValueInt == null || intValue > MinValueInt;
|
MinusButton.Enabled = WrapAround || MinValueInt == null || intValue > MinValueInt;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateText()
|
private void UpdateText()
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using System.Diagnostics.CodeAnalysis;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
|
using Barotrauma.Extensions;
|
||||||
|
|
||||||
namespace Barotrauma
|
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.SpeciallyHandledCharCategory.HasFlag(category)) { return font; }
|
||||||
if (Language != GameSettings.CurrentConfig.Language) { LoadFont(); }
|
return specialHandlingFonts.TryGetValue(category, out var resultFont)
|
||||||
if (font.IsCJK) { return font; }
|
? resultFont
|
||||||
return cjkFont;
|
: specialHandlingFonts.TryGetValue(TextManager.SpeciallyHandledCharCategory.CJK, out resultFont)
|
||||||
}
|
? resultFont
|
||||||
|
: font;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LanguageIdentifier Language { get; private set; }
|
public LanguageIdentifier Language { get; private set; }
|
||||||
@@ -70,40 +72,68 @@ namespace Barotrauma
|
|||||||
string fontPath = GetFontFilePath(element);
|
string fontPath = GetFontFilePath(element);
|
||||||
uint size = GetFontSize(element);
|
uint size = GetFontSize(element);
|
||||||
bool dynamicLoading = GetFontDynamicLoading(element);
|
bool dynamicLoading = GetFontDynamicLoading(element);
|
||||||
bool isCJK = GetIsCJK(element);
|
var shcc = GetShcc(element);
|
||||||
font?.Dispose();
|
font?.Dispose();
|
||||||
cjkFont?.Dispose();
|
specialHandlingFonts?.Values.ForEach(f => f.Dispose());
|
||||||
font = new ScalableFont(fontPath, size, GameMain.Instance.GraphicsDevice, dynamicLoading, isCJK)
|
font = new ScalableFont(
|
||||||
|
fontPath,
|
||||||
|
size,
|
||||||
|
GameMain.Instance.GraphicsDevice,
|
||||||
|
dynamicLoading,
|
||||||
|
shcc)
|
||||||
{
|
{
|
||||||
ForceUpperCase = element.GetAttributeBool("forceuppercase", false)
|
ForceUpperCase = element.GetAttributeBool("forceuppercase", false)
|
||||||
};
|
};
|
||||||
if (!isCJK)
|
|
||||||
|
var fallbackFonts = new Dictionary<TextManager.SpeciallyHandledCharCategory, ScalableFont>();
|
||||||
|
foreach (var flag in TextManager.SpeciallyHandledCharCategories)
|
||||||
{
|
{
|
||||||
cjkFont = ExtractCjkFont(element)
|
if (shcc.HasFlag(flag)) { continue; }
|
||||||
?? new ScalableFont("Content/Fonts/NotoSans/NotoSansCJKsc-Bold.otf",
|
|
||||||
font.Size, GameMain.Instance.GraphicsDevice, dynamicLoading: true, isCJK: true);
|
var extractedFont = ExtractFont(flag, element);
|
||||||
cjkFont.ForceUpperCase = font.ForceUpperCase;
|
if (extractedFont is null) { continue; }
|
||||||
|
fallbackFonts.Add(flag, extractedFont);
|
||||||
}
|
}
|
||||||
|
fallbackFonts.Values.ForEach(ff => ff.ForceUpperCase = font.ForceUpperCase);
|
||||||
|
specialHandlingFonts = fallbackFonts.ToImmutableDictionary();
|
||||||
Language = GameSettings.CurrentConfig.Language;
|
Language = GameSettings.CurrentConfig.Language;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Dispose()
|
public override void Dispose()
|
||||||
{
|
{
|
||||||
font?.Dispose(); font = null;
|
font?.Dispose();
|
||||||
cjkFont?.Dispose(); cjkFont = null;
|
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())
|
foreach (var subElement in element.Elements().Reverse())
|
||||||
{
|
{
|
||||||
if (subElement.NameAsIdentifier() != "override") { continue; }
|
if (subElement.NameAsIdentifier() != "override") { continue; }
|
||||||
if (subElement.GetAttributeBool("iscjk", false))
|
if (ScalableFont.ExtractShccFromXElement(subElement).HasFlag(flag))
|
||||||
{
|
{
|
||||||
return new ScalableFont(subElement, GameMain.Instance.GraphicsDevice);
|
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)
|
private string GetFontFilePath(ContentXElement element)
|
||||||
@@ -154,21 +184,21 @@ namespace Barotrauma
|
|||||||
return element.GetAttributeBool("dynamicloading", false);
|
return element.GetAttributeBool("dynamicloading", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool GetIsCJK(XElement element)
|
private TextManager.SpeciallyHandledCharCategory GetShcc(XElement element)
|
||||||
{
|
{
|
||||||
foreach (var subElement in element.Elements())
|
foreach (var subElement in element.Elements())
|
||||||
{
|
{
|
||||||
if (IsValidOverride(subElement))
|
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)
|
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>());
|
var languages = element.GetAttributeIdentifierArray("language", Array.Empty<Identifier>());
|
||||||
return languages.Any(l => l.ToLanguageIdentifier() == GameSettings.CurrentConfig.Language);
|
return languages.Any(l => l.ToLanguageIdentifier() == GameSettings.CurrentConfig.Language);
|
||||||
}
|
}
|
||||||
@@ -191,7 +221,7 @@ namespace Barotrauma
|
|||||||
private ScalableFont GetFontForStr(LocalizedString str) => GetFontForStr(str.Value);
|
private ScalableFont GetFontForStr(LocalizedString str) => GetFontForStr(str.Value);
|
||||||
|
|
||||||
public ScalableFont GetFontForStr(string str) =>
|
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)
|
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
|
Stretch = true
|
||||||
};
|
};
|
||||||
var shoppingCrateListContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.8f), shoppingCrateInventoryContainer.RectTransform), style: null);
|
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 };
|
shoppingCrateBuyList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false, KeepSpaceForScrollBar = true };
|
||||||
shoppingCrateSellList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false };
|
shoppingCrateSellList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false, KeepSpaceForScrollBar = true };
|
||||||
shoppingCrateSellFromSubList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false };
|
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)
|
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();
|
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);
|
GUITextBlock.AutoScaleAndNormalize(subTreeNames);
|
||||||
|
|
||||||
@@ -440,6 +440,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
if (character is null) { return false; }
|
if (character is null) { return false; }
|
||||||
|
|
||||||
|
Identifier talentIdentifier = (Identifier)userData;
|
||||||
if (talentOption.MaxChosenTalents is 1)
|
if (talentOption.MaxChosenTalents is 1)
|
||||||
{
|
{
|
||||||
// deselect other buttons in tier by removing their selected talents from pool
|
// deselect other buttons in tier by removing their selected talents from pool
|
||||||
@@ -454,9 +455,11 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Identifier talentIdentifier = (Identifier)userData;
|
if (character.HasTalent(talentIdentifier))
|
||||||
|
{
|
||||||
if (IsViableTalentForCharacter(info.Character, talentIdentifier, selectedTalents))
|
return true;
|
||||||
|
}
|
||||||
|
else if (IsViableTalentForCharacter(info.Character, talentIdentifier, selectedTalents))
|
||||||
{
|
{
|
||||||
if (!selectedTalents.Contains(talentIdentifier))
|
if (!selectedTalents.Contains(talentIdentifier))
|
||||||
{
|
{
|
||||||
@@ -467,7 +470,7 @@ namespace Barotrauma
|
|||||||
selectedTalents.Remove(talentIdentifier);
|
selectedTalents.Remove(talentIdentifier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!character.HasTalent(talentIdentifier))
|
else
|
||||||
{
|
{
|
||||||
selectedTalents.Remove(talentIdentifier);
|
selectedTalents.Remove(talentIdentifier);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -314,7 +314,7 @@ namespace Barotrauma
|
|||||||
GraphicsDeviceManager.SynchronizeWithVerticalRetrace = GameSettings.CurrentConfig.Graphics.VSync;
|
GraphicsDeviceManager.SynchronizeWithVerticalRetrace = GameSettings.CurrentConfig.Graphics.VSync;
|
||||||
SetWindowMode(GameSettings.CurrentConfig.Graphics.DisplayMode);
|
SetWindowMode(GameSettings.CurrentConfig.Graphics.DisplayMode);
|
||||||
|
|
||||||
defaultViewport = GraphicsDevice.Viewport;
|
defaultViewport = new Viewport(0, 0, GraphicsWidth, GraphicsHeight);
|
||||||
|
|
||||||
if (recalculateFontsAndStyles)
|
if (recalculateFontsAndStyles)
|
||||||
{
|
{
|
||||||
@@ -353,6 +353,7 @@ namespace Barotrauma
|
|||||||
public void ResetViewPort()
|
public void ResetViewPort()
|
||||||
{
|
{
|
||||||
GraphicsDevice.Viewport = defaultViewport;
|
GraphicsDevice.Viewport = defaultViewport;
|
||||||
|
GraphicsDevice.ScissorRectangle = defaultViewport.Bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -28,18 +28,27 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
if (node.ParentIndex > -1)
|
if (node.ParentIndex > -1)
|
||||||
{
|
{
|
||||||
Vector2 diff = nodes[node.ParentIndex].WorldPosition - node.WorldPosition;
|
CreateParticlesBetween(nodes[node.ParentIndex].WorldPosition, node.WorldPosition);
|
||||||
float dist = diff.Length();
|
}
|
||||||
Vector2 normalizedDiff = diff / dist;
|
}
|
||||||
for (float x = 0.0f; x < dist; x += 50.0f)
|
foreach (var character in charactersInRange)
|
||||||
{
|
{
|
||||||
var spark = GameMain.ParticleManager.CreateParticle("ElectricShock", node.WorldPosition + normalizedDiff * x, Vector2.Zero);
|
CreateParticlesBetween(character.character.WorldPosition, character.node.WorldPosition);
|
||||||
if (spark != null)
|
}
|
||||||
{
|
|
||||||
spark.Size *= 0.3f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ namespace Barotrauma.Items.Components
|
|||||||
private GUIFrame selectedItemFrame;
|
private GUIFrame selectedItemFrame;
|
||||||
private GUIFrame selectedItemReqsFrame;
|
private GUIFrame selectedItemReqsFrame;
|
||||||
|
|
||||||
|
private GUITextBlock amountTextMin, amountTextMax;
|
||||||
|
private GUIScrollBar amountInput;
|
||||||
|
|
||||||
public GUIButton ActivateButton
|
public GUIButton ActivateButton
|
||||||
{
|
{
|
||||||
get { return activateButton; }
|
get { return activateButton; }
|
||||||
@@ -160,14 +163,46 @@ namespace Barotrauma.Items.Components
|
|||||||
new GUICustomComponent(new RectTransform(Vector2.One, inputInventoryHolder.RectTransform), DrawInputOverLay) { CanBeFocused = false };
|
new GUICustomComponent(new RectTransform(Vector2.One, inputInventoryHolder.RectTransform), DrawInputOverLay) { CanBeFocused = false };
|
||||||
|
|
||||||
// === ACTIVATE BUTTON === //
|
// === ACTIVATE BUTTON === //
|
||||||
var buttonFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.3f, 0.8f), inputArea.RectTransform), childAnchor: Anchor.CenterRight);
|
var buttonFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.3f, 0.9f), inputArea.RectTransform))
|
||||||
activateButton = new GUIButton(new RectTransform(new Vector2(1f, 0.6f), buttonFrame.RectTransform),
|
{
|
||||||
TextManager.Get(CreateButtonText), style: "DeviceButtonFixedSize")
|
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,
|
OnClicked = StartButtonClicked,
|
||||||
UserData = selectedItem,
|
UserData = selectedItem,
|
||||||
Enabled = false
|
Enabled = false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//spacing
|
||||||
|
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.01f), buttonFrame.RectTransform), style: null);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -192,6 +227,21 @@ namespace Barotrauma.Items.Components
|
|||||||
CreateRecipes();
|
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()
|
partial void CreateRecipes()
|
||||||
{
|
{
|
||||||
itemList.Content.RectTransform.ClearChildren();
|
itemList.Content.RectTransform.ClearChildren();
|
||||||
@@ -247,7 +297,7 @@ namespace Barotrauma.Items.Components
|
|||||||
outputContainer.Inventory.RectTransform = outputInventoryHolder.RectTransform;
|
outputContainer.Inventory.RectTransform = outputInventoryHolder.RectTransform;
|
||||||
}
|
}
|
||||||
|
|
||||||
private LocalizedString GetRecipeNameAndAmount(FabricationRecipe fabricationRecipe)
|
private static LocalizedString GetRecipeNameAndAmount(FabricationRecipe fabricationRecipe)
|
||||||
{
|
{
|
||||||
if (fabricationRecipe == null) { return ""; }
|
if (fabricationRecipe == null) { return ""; }
|
||||||
if (fabricationRecipe.Amount > 1)
|
if (fabricationRecipe.Amount > 1)
|
||||||
@@ -269,7 +319,7 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
partial void SelectProjSpecific(Character character)
|
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));
|
nonItems.ForEach(i => itemList.Content.RemoveChild(i));
|
||||||
|
|
||||||
itemList.Content.RectTransform.SortChildren((c1, c2) =>
|
itemList.Content.RectTransform.SortChildren((c1, c2) =>
|
||||||
@@ -567,7 +617,7 @@ namespace Barotrauma.Items.Components
|
|||||||
bool recipeVisible = false;
|
bool recipeVisible = false;
|
||||||
foreach (GUIComponent child in itemList.Content.Children.Reverse())
|
foreach (GUIComponent child in itemList.Content.Children.Reverse())
|
||||||
{
|
{
|
||||||
if (!(child.UserData is FabricationRecipe recipe))
|
if (child.UserData is not FabricationRecipe recipe)
|
||||||
{
|
{
|
||||||
if (child.Enabled)
|
if (child.Enabled)
|
||||||
{
|
{
|
||||||
@@ -598,6 +648,20 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
this.selectedItem = selectedItem;
|
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();
|
selectedItemFrame.ClearChildren();
|
||||||
selectedItemReqsFrame.ClearChildren();
|
selectedItemReqsFrame.ClearChildren();
|
||||||
|
|
||||||
@@ -735,6 +799,8 @@ namespace Barotrauma.Items.Components
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
amountRemaining = AmountToFabricate;
|
||||||
|
|
||||||
if (GameMain.Client != null)
|
if (GameMain.Client != null)
|
||||||
{
|
{
|
||||||
pendingFabricatedItem = fabricatedItem != null ? null : selectedItem;
|
pendingFabricatedItem = fabricatedItem != null ? null : selectedItem;
|
||||||
@@ -777,7 +843,7 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
foreach (GUIComponent child in itemList.Content.Children)
|
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 &&
|
if (recipe != selectedItem &&
|
||||||
(child.Rect.Y > itemList.Rect.Bottom || child.Rect.Bottom < itemList.Rect.Y))
|
(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;
|
uint recipeHash = pendingFabricatedItem?.RecipeHash ?? 0;
|
||||||
msg.WriteUInt32(recipeHash);
|
msg.WriteUInt32(recipeHash);
|
||||||
|
msg.WriteRangedInteger(AmountToFabricate, 1, MaxAmountToFabricate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||||
{
|
{
|
||||||
FabricatorState newState = (FabricatorState)msg.ReadByte();
|
FabricatorState newState = (FabricatorState)msg.ReadByte();
|
||||||
|
int amountToFabricate = msg.ReadRangedInteger(0, MaxAmountToFabricate);
|
||||||
|
int amountRemaining = msg.ReadRangedInteger(0, MaxAmountToFabricate);
|
||||||
float newTimeUntilReady = msg.ReadSingle();
|
float newTimeUntilReady = msg.ReadSingle();
|
||||||
uint recipeHash = msg.ReadUInt32();
|
uint recipeHash = msg.ReadUInt32();
|
||||||
UInt16 userID = msg.ReadUInt16();
|
UInt16 userID = msg.ReadUInt16();
|
||||||
@@ -828,6 +897,8 @@ namespace Barotrauma.Items.Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
State = newState;
|
State = newState;
|
||||||
|
this.amountToFabricate = amountToFabricate;
|
||||||
|
this.amountRemaining = amountRemaining;
|
||||||
if (newState == FabricatorState.Stopped || recipeHash == 0)
|
if (newState == FabricatorState.Stopped || recipeHash == 0)
|
||||||
{
|
{
|
||||||
CancelFabricating();
|
CancelFabricating();
|
||||||
|
|||||||
@@ -802,6 +802,7 @@ namespace Barotrauma.Items.Components
|
|||||||
if (passivePingRadius > 0.0f)
|
if (passivePingRadius > 0.0f)
|
||||||
{
|
{
|
||||||
if (activePingsCount == 0) { disruptedDirections.Clear(); }
|
if (activePingsCount == 0) { disruptedDirections.Clear(); }
|
||||||
|
//emit "pings" from nearby sound-emitting AITargets to reveal what's around them
|
||||||
foreach (AITarget t in AITarget.List)
|
foreach (AITarget t in AITarget.List)
|
||||||
{
|
{
|
||||||
if (t.Entity is Character c && !c.IsUnconscious && c.Params.HideInSonar) { continue; }
|
if (t.Entity is Character c && !c.IsUnconscious && c.Params.HideInSonar) { continue; }
|
||||||
@@ -811,12 +812,24 @@ namespace Barotrauma.Items.Components
|
|||||||
if (distSqr > t.SoundRange * t.SoundRange * 2) { continue; }
|
if (distSqr > t.SoundRange * t.SoundRange * 2) { continue; }
|
||||||
|
|
||||||
float dist = (float)Math.Sqrt(distSqr);
|
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,
|
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);
|
passive: true, pingStrength: 0.5f);
|
||||||
sonarBlips.Add(new SonarBlip(t.WorldPosition, 1.0f, 1.0f));
|
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);
|
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; }
|
if (!editing || GUI.DisableHUD || !item.IsSelected) { return; }
|
||||||
|
|
||||||
const float widgetRadius = 60.0f;
|
const float widgetRadius = 60.0f;
|
||||||
|
|||||||
@@ -343,38 +343,31 @@ namespace Barotrauma.Lights
|
|||||||
{
|
{
|
||||||
SolidColorEffect.CurrentTechnique = SolidColorEffect.Techniques["SolidVertexColor"];
|
SolidColorEffect.CurrentTechnique = SolidColorEffect.Techniques["SolidVertexColor"];
|
||||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, effect: SolidColorEffect, transformMatrix: spriteBatchTransform);
|
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, effect: SolidColorEffect, transformMatrix: spriteBatchTransform);
|
||||||
foreach (Character character in Character.CharacterList)
|
DrawCharacters(spriteBatch, cam, drawDeformSprites: false);
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
spriteBatch.End();
|
spriteBatch.End();
|
||||||
|
|
||||||
DeformableSprite.Effect.CurrentTechnique = DeformableSprite.Effect.Techniques["DeformShaderSolidVertexColor"];
|
DeformableSprite.Effect.CurrentTechnique = DeformableSprite.Effect.Techniques["DeformShaderSolidVertexColor"];
|
||||||
DeformableSprite.Effect.CurrentTechnique.Passes[0].Apply();
|
DeformableSprite.Effect.CurrentTechnique.Passes[0].Apply();
|
||||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, transformMatrix: spriteBatchTransform);
|
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)
|
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; }
|
if (Character.Controlled?.FocusedCharacter == character) { continue; }
|
||||||
Color lightColor = character.CurrentHull.AmbientLight == Color.TransparentBlack ?
|
Color lightColor = character.CurrentHull.AmbientLight == Color.TransparentBlack ?
|
||||||
Color.Black :
|
Color.Black :
|
||||||
character.CurrentHull.AmbientLight.Multiply(character.CurrentHull.AmbientLight.A / 255.0f).Opaque();
|
character.CurrentHull.AmbientLight.Multiply(character.CurrentHull.AmbientLight.A / 255.0f).Opaque();
|
||||||
foreach (Limb limb in character.AnimController.Limbs)
|
foreach (Limb limb in character.AnimController.Limbs)
|
||||||
{
|
{
|
||||||
if (limb.DeformSprite == null) { continue; }
|
if (drawDeformSprites == (limb.DeformSprite == null)) { continue; }
|
||||||
limb.Draw(spriteBatch, cam, lightColor);
|
limb.Draw(spriteBatch, cam, lightColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
spriteBatch.End();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DeformableSprite.Effect.CurrentTechnique = DeformableSprite.Effect.Techniques["DeformShader"];
|
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);
|
Vector2 size = new Vector2(Math.Max(nameSize.X, Math.Max(typeSize.X, descSize.X)), nameSize.Y + typeSize.Y + descSize.Y);
|
||||||
|
|
||||||
int highestSubTier = HighlightedLocation.HighestSubmarineTierAvailable();
|
int highestSubTier = HighlightedLocation.HighestSubmarineTierAvailable();
|
||||||
var overrideTiers = new List<(SubmarineClass subClass, int tier)>();
|
List<(SubmarineClass subClass, int tier)> overrideTiers = null;
|
||||||
foreach (SubmarineClass subClass in Enum.GetValues(typeof(SubmarineClass)))
|
if (HighlightedLocation.CanHaveSubsForSale())
|
||||||
{
|
{
|
||||||
if (subClass == SubmarineClass.Undefined) { continue; }
|
overrideTiers = new List<(SubmarineClass subClass, int tier)>();
|
||||||
int highestClassTier = HighlightedLocation.HighestSubmarineTierAvailable(subClass);
|
foreach (SubmarineClass subClass in Enum.GetValues(typeof(SubmarineClass)))
|
||||||
if (highestClassTier > 0 && highestClassTier > highestSubTier)
|
|
||||||
{
|
{
|
||||||
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;
|
bool showReputation = hudVisibility > 0.0f && HighlightedLocation.Discovered && HighlightedLocation.Type.HasOutpost && HighlightedLocation.Reputation != null;
|
||||||
LocalizedString repLabelText = null, repValueText = null;
|
LocalizedString repLabelText = null, repValueText = null;
|
||||||
@@ -745,9 +750,12 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
DrawSubAvailabilityText("advancedsub.all", highestSubTier);
|
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)
|
void DrawSubAvailabilityText(string tag, int tier)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -14,6 +14,16 @@ namespace Barotrauma.Networking
|
|||||||
set;
|
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;
|
private bool mutedLocally;
|
||||||
public bool MutedLocally
|
public bool MutedLocally
|
||||||
{
|
{
|
||||||
@@ -42,35 +52,64 @@ namespace Barotrauma.Networking
|
|||||||
!HasPermission(ClientPermissions.Kick) &&
|
!HasPermission(ClientPermissions.Kick) &&
|
||||||
!HasPermission(ClientPermissions.Unban);
|
!HasPermission(ClientPermissions.Unban);
|
||||||
|
|
||||||
public void UpdateSoundPosition()
|
public void UpdateVoipSound()
|
||||||
{
|
{
|
||||||
if (VoipSound == null) { return; }
|
if (VoipSound == null || !VoipSound.IsPlaying)
|
||||||
|
|
||||||
if (!VoipSound.IsPlaying)
|
|
||||||
{
|
{
|
||||||
DebugConsole.Log("Destroying voipsound");
|
radioNoiseChannel?.Dispose();
|
||||||
VoipSound.Dispose();
|
radioNoiseChannel = null;
|
||||||
|
if (VoipSound != null)
|
||||||
|
{
|
||||||
|
DebugConsole.Log("Destroying voipsound");
|
||||||
|
VoipSound.Dispose();
|
||||||
|
}
|
||||||
VoipSound = null;
|
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 (character != null)
|
||||||
{
|
{
|
||||||
if (GameSettings.CurrentConfig.Audio.UseDirectionalVoiceChat)
|
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
|
else
|
||||||
{
|
{
|
||||||
VoipSound.SetPosition(null);
|
|
||||||
float dist = Vector3.Distance(new Vector3(character.WorldPosition, 0.0f), GameMain.SoundManager.ListenerPosition);
|
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);
|
if (radioNoiseChannel == null || !radioNoiseChannel.IsPlaying)
|
||||||
VoipSound.Gain = 1.0f;
|
{
|
||||||
|
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.Dispose();
|
||||||
VoipSound = null;
|
VoipSound = null;
|
||||||
}
|
}
|
||||||
|
if (radioNoiseChannel != null)
|
||||||
|
{
|
||||||
|
radioNoiseChannel.Dispose();
|
||||||
|
radioNoiseChannel = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -449,7 +449,7 @@ namespace Barotrauma.Networking
|
|||||||
foreach (Client c in ConnectedClients)
|
foreach (Client c in ConnectedClients)
|
||||||
{
|
{
|
||||||
if (c.Character != null && c.Character.Removed) { c.Character = null; }
|
if (c.Character != null && c.Character.Removed) { c.Character = null; }
|
||||||
c.UpdateSoundPosition();
|
c.UpdateVoipSound();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (VoipCapture.Instance != null)
|
if (VoipCapture.Instance != null)
|
||||||
|
|||||||
+1
-1
@@ -111,7 +111,7 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
foreach (NetIncomingMessage inc in incomingLidgrenMessages)
|
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}");
|
DebugConsole.AddWarning($"Mismatched endpoint: expected {lidgrenEndpoint.NetEndpoint}, got {inc.SenderConnection.RemoteEndPoint}");
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -260,6 +260,13 @@ namespace Barotrauma.Networking
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Screen.Selected is ModDownloadScreen)
|
||||||
|
{
|
||||||
|
allowEnqueue = false;
|
||||||
|
captureTimer = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (allowEnqueue || captureTimer > 0)
|
if (allowEnqueue || captureTimer > 0)
|
||||||
{
|
{
|
||||||
LastEnqueueAudio = DateTime.Now;
|
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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.Xna.Framework;
|
|
||||||
using Barotrauma.Items.Components;
|
|
||||||
|
|
||||||
namespace Barotrauma.Networking
|
namespace Barotrauma.Networking
|
||||||
{
|
{
|
||||||
class VoipClient : IDisposable
|
class VoipClient : IDisposable
|
||||||
{
|
{
|
||||||
private GameClient gameClient;
|
/// <summary>
|
||||||
private ClientPeer netClient;
|
/// 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 DateTime lastSendTime;
|
||||||
private List<VoipQueue> queues;
|
private readonly List<VoipQueue> queues;
|
||||||
|
|
||||||
private UInt16 storedBufferID = 0;
|
private UInt16 storedBufferID = 0;
|
||||||
|
|
||||||
@@ -32,13 +35,13 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
public void RegisterQueue(VoipQueue queue)
|
public void RegisterQueue(VoipQueue queue)
|
||||||
{
|
{
|
||||||
if (queue == VoipCapture.Instance) return;
|
if (queue == VoipCapture.Instance) { return; }
|
||||||
if (!queues.Contains(queue)) queues.Add(queue);
|
if (!queues.Contains(queue)) { queues.Add(queue); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UnregisterQueue(VoipQueue queue)
|
public void UnregisterQueue(VoipQueue queue)
|
||||||
{
|
{
|
||||||
if (queues.Contains(queue)) queues.Remove(queue);
|
if (queues.Contains(queue)) { queues.Remove(queue); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendToServer()
|
public void SendToServer()
|
||||||
@@ -85,6 +88,7 @@ namespace Barotrauma.Networking
|
|||||||
public void Read(IReadMessage msg)
|
public void Read(IReadMessage msg)
|
||||||
{
|
{
|
||||||
byte queueId = msg.ReadByte();
|
byte queueId = msg.ReadByte();
|
||||||
|
float distanceFactor = msg.ReadRangedSingle(0.0f, 1.0f, 8);
|
||||||
VoipQueue queue = queues.Find(q => q.QueueID == queueId);
|
VoipQueue queue = queues.Find(q => q.QueueID == queueId);
|
||||||
|
|
||||||
if (queue == null)
|
if (queue == null)
|
||||||
@@ -105,9 +109,12 @@ namespace Barotrauma.Networking
|
|||||||
client.VoipSound = new VoipSound(client.Name, GameMain.SoundManager, client.VoipQueue);
|
client.VoipSound = new VoipSound(client.Name, GameMain.SoundManager, client.VoipQueue);
|
||||||
}
|
}
|
||||||
GameMain.SoundManager.ForceStreamUpdate();
|
GameMain.SoundManager.ForceStreamUpdate();
|
||||||
|
client.RadioNoise = 0.0f;
|
||||||
if (client.Character != null && !client.Character.IsDead && !client.Character.Removed && client.Character.SpeechImpediment <= 100.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;
|
WifiComponent radio = null;
|
||||||
var messageType = !client.VoipQueue.ForceLocal && ChatMessage.CanUseRadio(client.Character, out radio) ? ChatMessageType.Radio : ChatMessageType.Default;
|
var messageType = !client.VoipQueue.ForceLocal && ChatMessage.CanUseRadio(client.Character, out radio) ? ChatMessageType.Radio : ChatMessageType.Default;
|
||||||
client.Character.ShowSpeechBubble(1.25f, ChatMessage.MessageColor[(int)messageType]);
|
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;
|
client.VoipSound.UseRadioFilter = messageType == ChatMessageType.Radio && !GameSettings.CurrentConfig.Audio.DisableVoiceChatFilters;
|
||||||
if (messageType == ChatMessageType.Radio)
|
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
|
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 =
|
client.VoipSound.UseMuffleFilter =
|
||||||
messageType != ChatMessageType.Radio && Character.Controlled != null && !GameSettings.CurrentConfig.Audio.DisableVoiceChatFilters &&
|
messageType != ChatMessageType.Radio && Character.Controlled != null && !GameSettings.CurrentConfig.Audio.DisableVoiceChatFilters &&
|
||||||
|
|||||||
+4
-4
@@ -35,14 +35,14 @@ namespace Barotrauma
|
|||||||
};
|
};
|
||||||
|
|
||||||
// New game
|
// 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);
|
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) { MinSize = new Point(0, 20) }, string.Empty)
|
saveNameBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.03f), nameSeedLayout.RectTransform), string.Empty)
|
||||||
{
|
{
|
||||||
textFilterFunction = ToolBox.RemoveInvalidFileNameChars
|
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);
|
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) { MinSize = new Point(0, 20) }, ToolBox.RandomSeed(8));
|
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));
|
nameSeedLayout.RectTransform.MinSize = new Point(0, nameSeedLayout.Children.Sum(c => c.RectTransform.MinSize.Y));
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace Barotrauma.Sounds
|
|||||||
private VorbisReader reader;
|
private VorbisReader reader;
|
||||||
|
|
||||||
//key = sample rate, value = filter
|
//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 static List<float> playbackAmplitude;
|
||||||
private const int AMPLITUDE_SAMPLE_COUNT = 4410; //100ms in a 44100hz file
|
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)
|
public override int FillStreamBuffer(int samplePos, short[] buffer)
|
||||||
{
|
{
|
||||||
if (!Stream) throw new Exception("Called FillStreamBuffer on a non-streamed sound!");
|
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 (reader == null) { throw new Exception("Called FillStreamBuffer when the reader is null!"); }
|
||||||
|
|
||||||
if (samplePos >= reader.TotalSamples * reader.Channels * 2) return 0;
|
if (samplePos >= reader.TotalSamples * reader.Channels * 2) return 0;
|
||||||
|
|
||||||
|
|||||||
@@ -24,11 +24,12 @@ namespace Barotrauma.Sounds
|
|||||||
|
|
||||||
public readonly bool StreamsReliably;
|
public readonly bool StreamsReliably;
|
||||||
|
|
||||||
|
private readonly SoundManager.SourcePoolIndex sourcePoolIndex = SoundManager.SourcePoolIndex.Default;
|
||||||
public virtual SoundManager.SourcePoolIndex SourcePoolIndex
|
public virtual SoundManager.SourcePoolIndex SourcePoolIndex
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return SoundManager.SourcePoolIndex.Default;
|
return sourcePoolIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,13 +60,14 @@ namespace Barotrauma.Sounds
|
|||||||
public float BaseNear;
|
public float BaseNear;
|
||||||
public float BaseFar;
|
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;
|
Owner = owner;
|
||||||
Filename = getFullPath ? Path.GetFullPath(filename.CleanUpPath()).CleanUpPath() : filename;
|
Filename = getFullPath ? Path.GetFullPath(filename.CleanUpPath()).CleanUpPath() : filename;
|
||||||
Stream = stream;
|
Stream = stream;
|
||||||
StreamsReliably = streamsReliably;
|
StreamsReliably = streamsReliably;
|
||||||
XElement = xElement;
|
XElement = xElement;
|
||||||
|
sourcePoolIndex = XElement.GetAttributeEnum("sourcepool", SoundManager.SourcePoolIndex.Default);
|
||||||
|
|
||||||
BaseGain = 1.0f;
|
BaseGain = 1.0f;
|
||||||
BaseNear = 100.0f;
|
BaseNear = 100.0f;
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
using System;
|
using Microsoft.Xna.Framework;
|
||||||
using OpenAL;
|
using OpenAL;
|
||||||
using Microsoft.Xna.Framework;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace Barotrauma.Sounds
|
namespace Barotrauma.Sounds
|
||||||
{
|
{
|
||||||
@@ -17,7 +15,7 @@ namespace Barotrauma.Sounds
|
|||||||
|
|
||||||
public SoundSourcePool(int sourceCount = SoundManager.SOURCE_COUNT)
|
public SoundSourcePool(int sourceCount = SoundManager.SOURCE_COUNT)
|
||||||
{
|
{
|
||||||
int alError = Al.NoError;
|
int alError;
|
||||||
|
|
||||||
ALSources = new uint[sourceCount];
|
ALSources = new uint[sourceCount];
|
||||||
for (int i = 0; i < sourceCount; i++)
|
for (int i = 0; i < sourceCount; i++)
|
||||||
@@ -83,7 +81,7 @@ namespace Barotrauma.Sounds
|
|||||||
class SoundChannel : IDisposable
|
class SoundChannel : IDisposable
|
||||||
{
|
{
|
||||||
private const int STREAM_BUFFER_SIZE = 8820;
|
private const int STREAM_BUFFER_SIZE = 8820;
|
||||||
private short[] streamShortBuffer;
|
private readonly short[] streamShortBuffer;
|
||||||
|
|
||||||
private string debugName = "SoundChannel";
|
private string debugName = "SoundChannel";
|
||||||
|
|
||||||
@@ -312,12 +310,12 @@ namespace Barotrauma.Sounds
|
|||||||
|
|
||||||
if (ALSourceIndex < 0) { return; }
|
if (ALSourceIndex < 0) { return; }
|
||||||
|
|
||||||
if (!IsPlaying) return;
|
if (!IsPlaying) { return; }
|
||||||
|
|
||||||
if (!IsStream)
|
if (!IsStream)
|
||||||
{
|
{
|
||||||
uint alSource = Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex);
|
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();
|
int alError = Al.GetError();
|
||||||
if (alError != Al.NoError)
|
if (alError != Al.NoError)
|
||||||
{
|
{
|
||||||
@@ -379,7 +377,7 @@ namespace Barotrauma.Sounds
|
|||||||
|
|
||||||
if (!IsStream)
|
if (!IsStream)
|
||||||
{
|
{
|
||||||
int playbackPos; Al.GetSourcei(alSource, Al.SampleOffset, out playbackPos);
|
Al.GetSourcei(alSource, Al.SampleOffset, out int playbackPos);
|
||||||
int alError = Al.GetError();
|
int alError = Al.GetError();
|
||||||
if (alError != Al.NoError)
|
if (alError != Al.NoError)
|
||||||
{
|
{
|
||||||
@@ -390,7 +388,7 @@ namespace Barotrauma.Sounds
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
float retVal = -1.0f;
|
float retVal;
|
||||||
Monitor.Enter(mutex);
|
Monitor.Enter(mutex);
|
||||||
retVal = streamAmplitude;
|
retVal = streamAmplitude;
|
||||||
Monitor.Exit(mutex);
|
Monitor.Exit(mutex);
|
||||||
@@ -432,8 +430,8 @@ namespace Barotrauma.Sounds
|
|||||||
private bool reachedEndSample;
|
private bool reachedEndSample;
|
||||||
private int queueStartIndex;
|
private int queueStartIndex;
|
||||||
private readonly uint[] streamBuffers;
|
private readonly uint[] streamBuffers;
|
||||||
private uint[] unqueuedBuffers;
|
private readonly uint[] unqueuedBuffers;
|
||||||
private float[] streamBufferAmplitudes;
|
private readonly float[] streamBufferAmplitudes;
|
||||||
|
|
||||||
public int StreamSeekPos
|
public int StreamSeekPos
|
||||||
{
|
{
|
||||||
@@ -448,18 +446,17 @@ namespace Barotrauma.Sounds
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private object mutex;
|
private readonly object mutex;
|
||||||
|
|
||||||
public bool IsPlaying
|
public bool IsPlaying
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (ALSourceIndex < 0) return false;
|
if (ALSourceIndex < 0) { return false; }
|
||||||
if (IsStream && !reachedEndSample) return true;
|
if (IsStream && !reachedEndSample) { return true; }
|
||||||
int state;
|
|
||||||
uint alSource = Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex);
|
uint alSource = Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex);
|
||||||
if (!Al.IsSource(alSource)) return false;
|
if (!Al.IsSource(alSource)) { return false; }
|
||||||
Al.GetSourcei(alSource, Al.SourceState, out state);
|
Al.GetSourcei(alSource, Al.SourceState, out int state);
|
||||||
int alError = Al.GetError();
|
int alError = Al.GetError();
|
||||||
if (alError != Al.NoError)
|
if (alError != Al.NoError)
|
||||||
{
|
{
|
||||||
@@ -710,8 +707,7 @@ namespace Barotrauma.Sounds
|
|||||||
{
|
{
|
||||||
uint alSource = Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex);
|
uint alSource = Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex);
|
||||||
|
|
||||||
int state;
|
Al.GetSourcei(alSource, Al.SourceState, out int state);
|
||||||
Al.GetSourcei(alSource, Al.SourceState, out state);
|
|
||||||
bool playing = state == Al.Playing;
|
bool playing = state == Al.Playing;
|
||||||
int alError = Al.GetError();
|
int alError = Al.GetError();
|
||||||
if (alError != Al.NoError)
|
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));
|
throw new Exception("Failed to determine playing state from streamed source: " + debugName + ", " + Al.GetErrorString(alError));
|
||||||
}
|
}
|
||||||
|
|
||||||
int unqueuedBufferCount;
|
Al.GetSourcei(alSource, Al.BuffersProcessed, out int unqueuedBufferCount);
|
||||||
Al.GetSourcei(alSource, Al.BuffersProcessed, out unqueuedBufferCount);
|
|
||||||
alError = Al.GetError();
|
alError = Al.GetError();
|
||||||
if (alError != Al.NoError)
|
if (alError != Al.NoError)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
using Barotrauma.IO;
|
using Barotrauma.Networking;
|
||||||
using Barotrauma.Networking;
|
|
||||||
using Concentus.Structs;
|
using Concentus.Structs;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using OpenAL;
|
using OpenAL;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Barotrauma.Sounds
|
namespace Barotrauma.Sounds
|
||||||
{
|
{
|
||||||
@@ -26,12 +24,12 @@ namespace Barotrauma.Sounds
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private VoipQueue queue;
|
private readonly VoipQueue queue;
|
||||||
private int bufferID = 0;
|
private int bufferID = 0;
|
||||||
|
|
||||||
private SoundChannel soundChannel;
|
private SoundChannel soundChannel;
|
||||||
|
|
||||||
private OpusDecoder decoder;
|
private readonly OpusDecoder decoder;
|
||||||
|
|
||||||
public bool UseRadioFilter;
|
public bool UseRadioFilter;
|
||||||
public bool UseMuffleFilter;
|
public bool UseMuffleFilter;
|
||||||
@@ -39,11 +37,11 @@ namespace Barotrauma.Sounds
|
|||||||
public float Near { get; private set; }
|
public float Near { get; private set; }
|
||||||
public float Far { 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)
|
new LowpassFilter(VoipConfig.FREQUENCY, 800)
|
||||||
};
|
};
|
||||||
private BiQuad[] radioFilters = new BiQuad[]
|
private readonly BiQuad[] radioFilters = new BiQuad[]
|
||||||
{
|
{
|
||||||
new BandpassFilter(VoipConfig.FREQUENCY, 2000)
|
new BandpassFilter(VoipConfig.FREQUENCY, 2000)
|
||||||
};
|
};
|
||||||
@@ -101,13 +99,14 @@ namespace Barotrauma.Sounds
|
|||||||
|
|
||||||
public void ApplyFilters(short[] buffer, int readSamples)
|
public void ApplyFilters(short[] buffer, int readSamples)
|
||||||
{
|
{
|
||||||
|
float finalGain = gain * GameSettings.CurrentConfig.Audio.VoiceChatVolume;
|
||||||
for (int i = 0; i < readSamples; i++)
|
for (int i = 0; i < readSamples; i++)
|
||||||
{
|
{
|
||||||
float fVal = ShortToFloat(buffer[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)
|
if (UseMuffleFilter)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<RootNamespace>Barotrauma</RootNamespace>
|
<RootNamespace>Barotrauma</RootNamespace>
|
||||||
<Authors>FakeFish, Undertow Games</Authors>
|
<Authors>FakeFish, Undertow Games</Authors>
|
||||||
<Product>Barotrauma</Product>
|
<Product>Barotrauma</Product>
|
||||||
<Version>0.20.6.0</Version>
|
<Version>0.20.7.0</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
<AssemblyName>Barotrauma</AssemblyName>
|
<AssemblyName>Barotrauma</AssemblyName>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<RootNamespace>Barotrauma</RootNamespace>
|
<RootNamespace>Barotrauma</RootNamespace>
|
||||||
<Authors>FakeFish, Undertow Games</Authors>
|
<Authors>FakeFish, Undertow Games</Authors>
|
||||||
<Product>Barotrauma</Product>
|
<Product>Barotrauma</Product>
|
||||||
<Version>0.20.6.0</Version>
|
<Version>0.20.7.0</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
<AssemblyName>Barotrauma</AssemblyName>
|
<AssemblyName>Barotrauma</AssemblyName>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<RootNamespace>Barotrauma</RootNamespace>
|
<RootNamespace>Barotrauma</RootNamespace>
|
||||||
<Authors>FakeFish, Undertow Games</Authors>
|
<Authors>FakeFish, Undertow Games</Authors>
|
||||||
<Product>Barotrauma</Product>
|
<Product>Barotrauma</Product>
|
||||||
<Version>0.20.6.0</Version>
|
<Version>0.20.7.0</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
<AssemblyName>Barotrauma</AssemblyName>
|
<AssemblyName>Barotrauma</AssemblyName>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<RootNamespace>Barotrauma</RootNamespace>
|
<RootNamespace>Barotrauma</RootNamespace>
|
||||||
<Authors>FakeFish, Undertow Games</Authors>
|
<Authors>FakeFish, Undertow Games</Authors>
|
||||||
<Product>Barotrauma Dedicated Server</Product>
|
<Product>Barotrauma Dedicated Server</Product>
|
||||||
<Version>0.20.6.0</Version>
|
<Version>0.20.7.0</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
<AssemblyName>DedicatedServer</AssemblyName>
|
<AssemblyName>DedicatedServer</AssemblyName>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<RootNamespace>Barotrauma</RootNamespace>
|
<RootNamespace>Barotrauma</RootNamespace>
|
||||||
<Authors>FakeFish, Undertow Games</Authors>
|
<Authors>FakeFish, Undertow Games</Authors>
|
||||||
<Product>Barotrauma Dedicated Server</Product>
|
<Product>Barotrauma Dedicated Server</Product>
|
||||||
<Version>0.20.6.0</Version>
|
<Version>0.20.7.0</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
<AssemblyName>DedicatedServer</AssemblyName>
|
<AssemblyName>DedicatedServer</AssemblyName>
|
||||||
|
|||||||
@@ -9,11 +9,12 @@ namespace Barotrauma.Items.Components
|
|||||||
public void ServerEventRead(IReadMessage msg, Client c)
|
public void ServerEventRead(IReadMessage msg, Client c)
|
||||||
{
|
{
|
||||||
uint recipeHash = msg.ReadUInt32();
|
uint recipeHash = msg.ReadUInt32();
|
||||||
|
int amountToFabricate = msg.ReadRangedInteger(1, MaxAmountToFabricate);
|
||||||
item.CreateServerEvent(this);
|
item.CreateServerEvent(this);
|
||||||
|
|
||||||
if (!item.CanClientAccess(c)) { return; }
|
if (!item.CanClientAccess(c)) { return; }
|
||||||
|
|
||||||
|
AmountToFabricate = amountToFabricate;
|
||||||
if (recipeHash == 0)
|
if (recipeHash == 0)
|
||||||
{
|
{
|
||||||
CancelFabricating(c.Character);
|
CancelFabricating(c.Character);
|
||||||
@@ -24,6 +25,8 @@ namespace Barotrauma.Items.Components
|
|||||||
if (fabricatedItem != null && fabricatedItem.RecipeHash == recipeHash) { return; }
|
if (fabricatedItem != null && fabricatedItem.RecipeHash == recipeHash) { return; }
|
||||||
if (recipeHash == 0) { return; }
|
if (recipeHash == 0) { return; }
|
||||||
|
|
||||||
|
amountRemaining = AmountToFabricate;
|
||||||
|
|
||||||
StartFabricating(fabricationRecipes[recipeHash], c.Character);
|
StartFabricating(fabricationRecipes[recipeHash], c.Character);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,6 +59,8 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
var componentData = ExtractEventData<EventData>(extraData);
|
var componentData = ExtractEventData<EventData>(extraData);
|
||||||
msg.WriteByte((byte)componentData.State);
|
msg.WriteByte((byte)componentData.State);
|
||||||
|
msg.WriteRangedInteger(AmountToFabricate, 0, MaxAmountToFabricate);
|
||||||
|
msg.WriteRangedInteger(amountRemaining, 0, MaxAmountToFabricate);
|
||||||
msg.WriteSingle(timeUntilReady);
|
msg.WriteSingle(timeUntilReady);
|
||||||
uint recipeHash = fabricatedItem?.RecipeHash ?? 0;
|
uint recipeHash = fabricatedItem?.RecipeHash ?? 0;
|
||||||
msg.WriteUInt32(recipeHash);
|
msg.WriteUInt32(recipeHash);
|
||||||
|
|||||||
@@ -2,19 +2,18 @@
|
|||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Barotrauma.Networking
|
namespace Barotrauma.Networking
|
||||||
{
|
{
|
||||||
class VoipServer
|
class VoipServer
|
||||||
{
|
{
|
||||||
private ServerPeer netServer;
|
private readonly ServerPeer netServer;
|
||||||
private List<VoipQueue> queues;
|
private readonly List<VoipQueue> queues;
|
||||||
private Dictionary<VoipQueue,DateTime> lastSendTime;
|
private readonly Dictionary<VoipQueue,DateTime> lastSendTime;
|
||||||
|
|
||||||
public VoipServer(ServerPeer server)
|
public VoipServer(ServerPeer server)
|
||||||
{
|
{
|
||||||
this.netServer = server;
|
netServer = server;
|
||||||
queues = new List<VoipQueue>();
|
queues = new List<VoipQueue>();
|
||||||
lastSendTime = new Dictionary<VoipQueue, DateTime>();
|
lastSendTime = new Dictionary<VoipQueue, DateTime>();
|
||||||
}
|
}
|
||||||
@@ -46,17 +45,19 @@ namespace Barotrauma.Networking
|
|||||||
}
|
}
|
||||||
|
|
||||||
Client sender = clients.Find(c => c.VoipQueue == queue);
|
Client sender = clients.Find(c => c.VoipQueue == queue);
|
||||||
|
if (sender == null) { return; }
|
||||||
|
|
||||||
foreach (Client recipient in clients)
|
foreach (Client recipient in clients)
|
||||||
{
|
{
|
||||||
if (recipient == sender) { continue; }
|
if (recipient == sender) { continue; }
|
||||||
|
|
||||||
if (!CanReceive(sender, recipient)) { continue; }
|
if (!CanReceive(sender, recipient, out float distanceFactor)) { continue; }
|
||||||
|
|
||||||
IWriteMessage msg = new WriteOnlyMessage();
|
IWriteMessage msg = new WriteOnlyMessage();
|
||||||
|
|
||||||
msg.WriteByte((byte)ServerPacketHeader.VOICE);
|
msg.WriteByte((byte)ServerPacketHeader.VOICE);
|
||||||
msg.WriteByte((byte)queue.QueueID);
|
msg.WriteByte((byte)queue.QueueID);
|
||||||
|
msg.WriteRangedSingle(distanceFactor, 0.0f, 1.0f, 8);
|
||||||
queue.Write(msg);
|
queue.Write(msg);
|
||||||
|
|
||||||
netServer.Send(msg, recipient.Connection, DeliveryMethod.Unreliable);
|
netServer.Send(msg, recipient.Connection, DeliveryMethod.Unreliable);
|
||||||
@@ -64,9 +65,15 @@ namespace Barotrauma.Networking
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool CanReceive(Client sender, Client recipient)
|
private static bool CanReceive(Client sender, Client recipient, out float distanceFactor)
|
||||||
{
|
{
|
||||||
if (Screen.Selected != GameMain.GameScreen) { return true; }
|
if (Screen.Selected != GameMain.GameScreen)
|
||||||
|
{
|
||||||
|
distanceFactor = 0.0f;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
distanceFactor = 0.0f;
|
||||||
|
|
||||||
//no-one can hear muted players
|
//no-one can hear muted players
|
||||||
if (sender.Muted) { return false; }
|
if (sender.Muted) { return false; }
|
||||||
@@ -74,30 +81,47 @@ namespace Barotrauma.Networking
|
|||||||
bool recipientSpectating = recipient.Character == null || recipient.Character.IsDead;
|
bool recipientSpectating = recipient.Character == null || recipient.Character.IsDead;
|
||||||
bool senderSpectating = sender.Character == null || sender.Character.IsDead;
|
bool senderSpectating = sender.Character == null || sender.Character.IsDead;
|
||||||
|
|
||||||
//TODO: only allow spectators to hear the voice chat if close enough to the speaker?
|
//non-spectators cannot hear spectators, and spectators can always hear spectators
|
||||||
|
if (senderSpectating)
|
||||||
//non-spectators cannot hear spectators
|
{
|
||||||
if (senderSpectating && !recipientSpectating) { return false; }
|
return recipientSpectating;
|
||||||
|
}
|
||||||
//both spectating, no need to do radio/distance checks
|
|
||||||
if (recipientSpectating && senderSpectating) { return true; }
|
|
||||||
|
|
||||||
//spectators can hear non-spectators
|
|
||||||
if (!senderSpectating && recipientSpectating) { return true; }
|
|
||||||
|
|
||||||
//sender can't speak
|
//sender can't speak
|
||||||
if (sender.Character != null && sender.Character.SpeechImpediment >= 100.0f) { return false; }
|
if (sender.Character != null && sender.Character.SpeechImpediment >= 100.0f) { return false; }
|
||||||
|
|
||||||
//check if the message can be sent via radio
|
//check if the message can be sent via radio
|
||||||
|
WifiComponent recipientRadio = null;
|
||||||
if (!sender.VoipQueue.ForceLocal &&
|
if (!sender.VoipQueue.ForceLocal &&
|
||||||
ChatMessage.CanUseRadio(sender.Character, out WifiComponent senderRadio) &&
|
ChatMessage.CanUseRadio(sender.Character, out WifiComponent senderRadio) &&
|
||||||
ChatMessage.CanUseRadio(recipient.Character, out WifiComponent recipientRadio))
|
(recipientSpectating || ChatMessage.CanUseRadio(recipient.Character, out recipientRadio)))
|
||||||
{
|
{
|
||||||
if (recipientRadio.CanReceive(senderRadio)) { return true; }
|
if (recipientSpectating)
|
||||||
|
{
|
||||||
|
if (recipient.SpectatePos == null) { return true; }
|
||||||
|
distanceFactor = MathHelper.Clamp(Vector2.Distance(sender.Character.WorldPosition, recipient.SpectatePos.Value) / senderRadio.Range, 0.0f, 1.0f);
|
||||||
|
return distanceFactor < 1.0f;
|
||||||
|
}
|
||||||
|
else if (recipientRadio != null && recipientRadio.CanReceive(senderRadio))
|
||||||
|
{
|
||||||
|
distanceFactor = MathHelper.Clamp(Vector2.Distance(sender.Character.WorldPosition, recipient.Character.WorldPosition) / senderRadio.Range, 0.0f, 1.0f);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//otherwise do a distance check
|
if (recipientSpectating)
|
||||||
return ChatMessage.GetGarbleAmount(recipient.Character, sender.Character, ChatMessage.SpeakRange) < 1.0f;
|
{
|
||||||
|
if (recipient.SpectatePos == null) { return true; }
|
||||||
|
distanceFactor = MathHelper.Clamp(Vector2.Distance(sender.Character.WorldPosition, recipient.SpectatePos.Value) / ChatMessage.SpeakRange, 0.0f, 1.0f);
|
||||||
|
return distanceFactor < 1.0f;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//otherwise do a distance check
|
||||||
|
float garbleAmount = ChatMessage.GetGarbleAmount(recipient.Character, sender.Character, ChatMessage.SpeakRange);
|
||||||
|
distanceFactor = garbleAmount;
|
||||||
|
return garbleAmount < 1.0f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<RootNamespace>Barotrauma</RootNamespace>
|
<RootNamespace>Barotrauma</RootNamespace>
|
||||||
<Authors>FakeFish, Undertow Games</Authors>
|
<Authors>FakeFish, Undertow Games</Authors>
|
||||||
<Product>Barotrauma Dedicated Server</Product>
|
<Product>Barotrauma Dedicated Server</Product>
|
||||||
<Version>0.20.6.0</Version>
|
<Version>0.20.7.0</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
<AssemblyName>DedicatedServer</AssemblyName>
|
<AssemblyName>DedicatedServer</AssemblyName>
|
||||||
|
|||||||
@@ -102,6 +102,8 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private bool inDetectable;
|
private bool inDetectable;
|
||||||
|
|
||||||
|
public double InDetectableSetTime;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Should be reset to false each frame and kept indetectable by e.g. a status effect.
|
/// Should be reset to false each frame and kept indetectable by e.g. a status effect.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -116,6 +118,7 @@ namespace Barotrauma
|
|||||||
inDetectable = value;
|
inDetectable = value;
|
||||||
if (inDetectable)
|
if (inDetectable)
|
||||||
{
|
{
|
||||||
|
InDetectableSetTime = Timing.TotalTime;
|
||||||
NeedsUpdate = true;
|
NeedsUpdate = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -257,9 +260,14 @@ namespace Barotrauma
|
|||||||
SightRange -= speed * deltaTime * (MaxSightRange / FadeOutTime);
|
SightRange -= speed * deltaTime * (MaxSightRange / FadeOutTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool HasSector()
|
||||||
|
{
|
||||||
|
return sectorRad < MathHelper.TwoPi;
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsWithinSector(Vector2 worldPosition)
|
public bool IsWithinSector(Vector2 worldPosition)
|
||||||
{
|
{
|
||||||
if (sectorRad >= MathHelper.TwoPi) { return true; }
|
if (!HasSector()) { return true; }
|
||||||
Vector2 diff = worldPosition - WorldPosition;
|
Vector2 diff = worldPosition - WorldPosition;
|
||||||
return Math.Abs(MathUtils.GetShortestAngle(MathUtils.VectorToAngle(diff), MathUtils.VectorToAngle(sectorDir))) <= sectorRad * 0.5f;
|
return Math.Abs(MathUtils.GetShortestAngle(MathUtils.VectorToAngle(diff), MathUtils.VectorToAngle(sectorDir))) <= sectorRad * 0.5f;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1083,7 +1083,7 @@ namespace Barotrauma
|
|||||||
State = AIState.Idle;
|
State = AIState.Idle;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else if (!owner.HasAbilityFlag(AbilityFlags.IgnoredByEnemyAI))
|
||||||
{
|
{
|
||||||
SelectedAiTarget = owner.AiTarget;
|
SelectedAiTarget = owner.AiTarget;
|
||||||
}
|
}
|
||||||
@@ -2147,7 +2147,6 @@ namespace Barotrauma
|
|||||||
if (SelectedAiTarget?.Entity == null) { return false; }
|
if (SelectedAiTarget?.Entity == null) { return false; }
|
||||||
if (AttackLimb?.attack == null) { return false; }
|
if (AttackLimb?.attack == null) { return false; }
|
||||||
if (damageTarget == null) { return false; }
|
if (damageTarget == null) { return false; }
|
||||||
ActiveAttack = AttackLimb.attack;
|
|
||||||
if (wallTarget != null)
|
if (wallTarget != null)
|
||||||
{
|
{
|
||||||
// If the selected target is not the wall target, make the wall target the selected target.
|
// If the selected target is not the wall target, make the wall target the selected target.
|
||||||
@@ -2156,9 +2155,10 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
SelectTarget(aiTarget, GetTargetMemory(SelectedAiTarget, addIfNotFound: true).Priority);
|
SelectTarget(aiTarget, GetTargetMemory(SelectedAiTarget, addIfNotFound: true).Priority);
|
||||||
State = AIState.Attack;
|
State = AIState.Attack;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (damageTarget == null) { return false; }
|
ActiveAttack = AttackLimb.attack;
|
||||||
if (ActiveAttack.Ranged && ActiveAttack.RequiredAngleToShoot > 0)
|
if (ActiveAttack.Ranged && ActiveAttack.RequiredAngleToShoot > 0)
|
||||||
{
|
{
|
||||||
Limb referenceLimb = GetLimbToRotate(ActiveAttack);
|
Limb referenceLimb = GetLimbToRotate(ActiveAttack);
|
||||||
@@ -3810,6 +3810,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
SelectTarget(door.Item.AiTarget, SelectedTargetMemory.Priority);
|
SelectTarget(door.Item.AiTarget, SelectedTargetMemory.Priority);
|
||||||
State = AIState.Attack;
|
State = AIState.Attack;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3881,9 +3882,8 @@ namespace Barotrauma
|
|||||||
return targetLimb;
|
return targetLimb;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Character GetOwner(Item item)
|
private static Character GetOwner(Item item)
|
||||||
{
|
{
|
||||||
// If the item is held by a character, attack the character instead.
|
|
||||||
var pickable = item.GetComponent<Pickable>();
|
var pickable = item.GetComponent<Pickable>();
|
||||||
if (pickable != null)
|
if (pickable != null)
|
||||||
{
|
{
|
||||||
|
|||||||
+1
-19
@@ -416,25 +416,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private void ApplyTreatment(Affliction affliction, Item item)
|
private void ApplyTreatment(Affliction affliction, Item item)
|
||||||
{
|
{
|
||||||
var targetLimb = targetCharacter.CharacterHealth.GetAfflictionLimb(affliction);
|
item.ApplyTreatment(character, targetCharacter, targetCharacter.CharacterHealth.GetAfflictionLimb(affliction));
|
||||||
bool remove = false;
|
|
||||||
foreach (ItemComponent ic in item.Components)
|
|
||||||
{
|
|
||||||
if (!ic.HasRequiredContainedItems(user: character, addMessage: false)) { continue; }
|
|
||||||
#if CLIENT
|
|
||||||
ic.PlaySound(ActionType.OnUse, character);
|
|
||||||
#endif
|
|
||||||
ic.WasUsed = true;
|
|
||||||
ic.ApplyStatusEffects(ActionType.OnUse, 1.0f, targetCharacter, targetLimb, user: character);
|
|
||||||
if (ic.DeleteOnUse)
|
|
||||||
{
|
|
||||||
remove = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (remove)
|
|
||||||
{
|
|
||||||
Entity.Spawner?.AddItemToRemoveQueue(item);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool CheckObjectiveSpecific()
|
protected override bool CheckObjectiveSpecific()
|
||||||
|
|||||||
+16
-14
@@ -1395,6 +1395,16 @@ namespace Barotrauma
|
|||||||
float cprBoost = character.GetStatValue(StatTypes.CPRBoost);
|
float cprBoost = character.GetStatValue(StatTypes.CPRBoost);
|
||||||
|
|
||||||
int skill = (int)character.GetSkillLevel("medical");
|
int skill = (int)character.GetSkillLevel("medical");
|
||||||
|
|
||||||
|
if (GameMain.NetworkMember is not { IsClient: true })
|
||||||
|
{
|
||||||
|
if (cprBoost >= 1f)
|
||||||
|
{
|
||||||
|
//prevent the patient from suffocating no matter how fast their oxygen level is dropping
|
||||||
|
target.Oxygen = Math.Max(target.Oxygen, -10.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//pump for 15 seconds (cprAnimTimer 0-15), then do mouth-to-mouth for 2 seconds (cprAnimTimer 15-17)
|
//pump for 15 seconds (cprAnimTimer 0-15), then do mouth-to-mouth for 2 seconds (cprAnimTimer 15-17)
|
||||||
if (cprAnimTimer > 15.0f && targetHead != null && head != null)
|
if (cprAnimTimer > 15.0f && targetHead != null && head != null)
|
||||||
{
|
{
|
||||||
@@ -1405,23 +1415,15 @@ namespace Barotrauma
|
|||||||
torso.PullJointEnabled = true;
|
torso.PullJointEnabled = true;
|
||||||
|
|
||||||
//Serverside code
|
//Serverside code
|
||||||
if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
|
if (GameMain.NetworkMember is not { IsClient: true })
|
||||||
{
|
{
|
||||||
if (target.Oxygen < -10.0f)
|
if (target.Oxygen < -10.0f)
|
||||||
{
|
{
|
||||||
if (cprBoost >= 1f)
|
//stabilize the oxygen level but don't allow it to go positive and revive the character yet
|
||||||
{
|
float stabilizationAmount = skill * CPRSettings.Active.StabilizationPerSkill;
|
||||||
//prevent the patient from suffocating no matter how fast their oxygen level is dropping
|
stabilizationAmount = MathHelper.Clamp(stabilizationAmount, CPRSettings.Active.StabilizationMin, CPRSettings.Active.StabilizationMax);
|
||||||
target.Oxygen = Math.Max(target.Oxygen, -10.0f);
|
character.Oxygen -= 1.0f / stabilizationAmount * deltaTime; //Worse skill = more oxygen required
|
||||||
}
|
if (character.Oxygen > 0.0f) { target.Oxygen += stabilizationAmount * deltaTime; } //we didn't suffocate yet did we
|
||||||
else
|
|
||||||
{
|
|
||||||
//stabilize the oxygen level but don't allow it to go positive and revive the character yet
|
|
||||||
float stabilizationAmount = skill * CPRSettings.Active.StabilizationPerSkill;
|
|
||||||
stabilizationAmount = MathHelper.Clamp(stabilizationAmount, CPRSettings.Active.StabilizationMin, CPRSettings.Active.StabilizationMax);
|
|
||||||
character.Oxygen -= 1.0f / stabilizationAmount * deltaTime; //Worse skill = more oxygen required
|
|
||||||
if (character.Oxygen > 0.0f) { target.Oxygen += stabilizationAmount * deltaTime; } //we didn't suffocate yet did we
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ namespace Barotrauma
|
|||||||
FriendlyNPC = 3
|
FriendlyNPC = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public readonly record struct TalentResistanceIdentifier(Identifier ResistanceIdentifier, Identifier TalentIdentifier);
|
||||||
|
|
||||||
partial class Character : Entity, IDamageable, ISerializableEntity, IClientSerializable, IServerPositionSync
|
partial class Character : Entity, IDamageable, ISerializableEntity, IClientSerializable, IServerPositionSync
|
||||||
{
|
{
|
||||||
public readonly static List<Character> CharacterList = new List<Character>();
|
public readonly static List<Character> CharacterList = new List<Character>();
|
||||||
@@ -347,7 +349,7 @@ namespace Barotrauma
|
|||||||
private readonly Dictionary<ItemPrefab, double> itemSelectedDurations = new Dictionary<ItemPrefab, double>();
|
private readonly Dictionary<ItemPrefab, double> itemSelectedDurations = new Dictionary<ItemPrefab, double>();
|
||||||
private double itemSelectedTime;
|
private double itemSelectedTime;
|
||||||
|
|
||||||
public float InvisibleTimer;
|
public float InvisibleTimer { get; set; }
|
||||||
|
|
||||||
public readonly CharacterPrefab Prefab;
|
public readonly CharacterPrefab Prefab;
|
||||||
|
|
||||||
@@ -1372,7 +1374,28 @@ namespace Barotrauma
|
|||||||
tags.RemoveWhere(t => t.StartsWith("variant"));
|
tags.RemoveWhere(t => t.StartsWith("variant"));
|
||||||
tags.Add($"variant{headId.Value}".ToIdentifier());
|
tags.Add($"variant{headId.Value}".ToIdentifier());
|
||||||
}
|
}
|
||||||
|
var oldHeadInfo = Info.Head;
|
||||||
Info.RecreateHead(tags.ToImmutableHashSet(), hairIndex, beardIndex, moustacheIndex, faceAttachmentIndex);
|
Info.RecreateHead(tags.ToImmutableHashSet(), hairIndex, beardIndex, moustacheIndex, faceAttachmentIndex);
|
||||||
|
if (hairIndex == -1)
|
||||||
|
{
|
||||||
|
Info.Head.HairIndex = oldHeadInfo.HairIndex;
|
||||||
|
}
|
||||||
|
if (beardIndex == -1)
|
||||||
|
{
|
||||||
|
Info.Head.BeardIndex = oldHeadInfo.BeardIndex;
|
||||||
|
}
|
||||||
|
if (moustacheIndex == -1)
|
||||||
|
{
|
||||||
|
Info.Head.MoustacheIndex = oldHeadInfo.MoustacheIndex;
|
||||||
|
}
|
||||||
|
if (faceAttachmentIndex == -1)
|
||||||
|
{
|
||||||
|
Info.Head.FaceAttachmentIndex = oldHeadInfo.FaceAttachmentIndex;
|
||||||
|
}
|
||||||
|
Info.Head.SkinColor = oldHeadInfo.SkinColor;
|
||||||
|
Info.Head.HairColor = oldHeadInfo.HairColor;
|
||||||
|
Info.Head.FacialHairColor = oldHeadInfo.FacialHairColor;
|
||||||
|
Info.CheckColors();
|
||||||
#if CLIENT
|
#if CLIENT
|
||||||
head.RecreateSprites();
|
head.RecreateSprites();
|
||||||
#endif
|
#endif
|
||||||
@@ -3050,7 +3073,8 @@ namespace Barotrauma
|
|||||||
ApplyStatusEffects(AnimController.InWater ? ActionType.InWater : ActionType.NotInWater, deltaTime);
|
ApplyStatusEffects(AnimController.InWater ? ActionType.InWater : ActionType.NotInWater, deltaTime);
|
||||||
ApplyStatusEffects(ActionType.OnActive, deltaTime);
|
ApplyStatusEffects(ActionType.OnActive, deltaTime);
|
||||||
|
|
||||||
if (aiTarget != null)
|
//wait 0.1 seconds so status effects that continuously set InDetectable to true can keep the character InDetectable
|
||||||
|
if (aiTarget != null && Timing.TotalTime > aiTarget.InDetectableSetTime + 0.1f)
|
||||||
{
|
{
|
||||||
aiTarget.InDetectable = false;
|
aiTarget.InDetectable = false;
|
||||||
}
|
}
|
||||||
@@ -5087,25 +5111,48 @@ namespace Barotrauma
|
|||||||
return abilityFlags.HasFlag(abilityFlag) || CharacterHealth.HasFlag(abilityFlag);
|
return abilityFlags.HasFlag(abilityFlag) || CharacterHealth.HasFlag(abilityFlag);
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly Dictionary<Identifier, float> abilityResistances = new Dictionary<Identifier, float>();
|
private readonly Dictionary<TalentResistanceIdentifier, float> abilityResistances = new();
|
||||||
|
|
||||||
public float GetAbilityResistance(AfflictionPrefab affliction)
|
public float GetAbilityResistance(AfflictionPrefab affliction)
|
||||||
{
|
{
|
||||||
return abilityResistances.TryGetValue(affliction.Identifier, out float value) ? value : abilityResistances.TryGetValue(affliction.AfflictionType, out float typeValue) ? typeValue : 1f;
|
float resistance = 0f;
|
||||||
|
bool hadResistance = false;
|
||||||
|
|
||||||
|
foreach (var (key, value) in abilityResistances)
|
||||||
|
{
|
||||||
|
if (key.ResistanceIdentifier == affliction.AfflictionType ||
|
||||||
|
key.ResistanceIdentifier == affliction.Identifier)
|
||||||
|
{
|
||||||
|
resistance += value;
|
||||||
|
hadResistance = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hadResistance ? resistance : 1f;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ChangeAbilityResistance(Identifier resistanceId, float value)
|
public void ChangeAbilityResistance(TalentResistanceIdentifier identifier, float value)
|
||||||
{
|
{
|
||||||
if (abilityResistances.ContainsKey(resistanceId))
|
if (!MathUtils.IsValid(value))
|
||||||
{
|
{
|
||||||
abilityResistances[resistanceId] *= value;
|
#if DEBUG
|
||||||
|
DebugConsole.ThrowError($"Attempted to set ability resistance to an invalid value ({value})\n" + Environment.StackTrace.CleanupStackTrace());
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (abilityResistances.ContainsKey(identifier))
|
||||||
|
{
|
||||||
|
abilityResistances[identifier] *= value;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
abilityResistances.Add(resistanceId, value);
|
abilityResistances.Add(identifier, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RemoveAbilityResistance(TalentResistanceIdentifier identifier) => abilityResistances.Remove(identifier);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Compares just the species name and the group, ignores teams. There's a more complex version found in HumanAIController.cs
|
/// Compares just the species name and the group, ignores teams. There's a more complex version found in HumanAIController.cs
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -63,27 +63,34 @@ namespace Barotrauma
|
|||||||
public readonly CharacterInfo CharacterInfo;
|
public readonly CharacterInfo CharacterInfo;
|
||||||
public readonly HeadPreset Preset;
|
public readonly HeadPreset Preset;
|
||||||
|
|
||||||
private int hairIndex;
|
public int HairIndex { get; set; }
|
||||||
|
|
||||||
public int HairIndex
|
private int? hairWithHatIndex;
|
||||||
|
|
||||||
|
public void SetHairWithHatIndex()
|
||||||
{
|
{
|
||||||
get => hairIndex;
|
if (CharacterInfo.Hairs is null)
|
||||||
set
|
|
||||||
{
|
{
|
||||||
hairIndex = value;
|
if (HairIndex == -1)
|
||||||
if (CharacterInfo.Hairs is null)
|
|
||||||
{
|
{
|
||||||
HairWithHatIndex = value;
|
#if DEBUG
|
||||||
return;
|
DebugConsole.ThrowError("Setting \"hairWithHatIndex\" before \"Hairs\" are defined!");
|
||||||
|
#else
|
||||||
|
DebugConsole.AddWarning("Setting \"hairWithHatIndex\" before \"Hairs\" are defined!");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
HairWithHatIndex = HairElement?.GetAttributeInt("replacewhenwearinghat", hairIndex) ?? -1;
|
hairWithHatIndex = HairIndex;
|
||||||
if (HairWithHatIndex < 0 || HairWithHatIndex >= CharacterInfo.Hairs.Count)
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
hairWithHatIndex = HairElement?.GetAttributeInt("replacewhenwearinghat", HairIndex) ?? -1;
|
||||||
|
if (hairWithHatIndex < 0 || hairWithHatIndex >= CharacterInfo.Hairs.Count)
|
||||||
{
|
{
|
||||||
HairWithHatIndex = hairIndex;
|
hairWithHatIndex = HairIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public int HairWithHatIndex { get; private set; }
|
|
||||||
public int BeardIndex;
|
public int BeardIndex;
|
||||||
public int MoustacheIndex;
|
public int MoustacheIndex;
|
||||||
public int FaceAttachmentIndex;
|
public int FaceAttachmentIndex;
|
||||||
@@ -99,26 +106,29 @@ namespace Barotrauma
|
|||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (CharacterInfo.Hairs == null) { return null; }
|
if (CharacterInfo.Hairs == null) { return null; }
|
||||||
if (hairIndex >= CharacterInfo.Hairs.Count)
|
if (HairIndex >= CharacterInfo.Hairs.Count)
|
||||||
{
|
{
|
||||||
DebugConsole.AddWarning($"Hair index out of range (character: {CharacterInfo?.Name ?? "null"}, index: {hairIndex})");
|
DebugConsole.AddWarning($"Hair index out of range (character: {CharacterInfo?.Name ?? "null"}, index: {HairIndex})");
|
||||||
}
|
}
|
||||||
return CharacterInfo.Hairs.ElementAtOrDefault(hairIndex);
|
return CharacterInfo.Hairs.ElementAtOrDefault(HairIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public ContentXElement HairWithHatElement
|
public ContentXElement HairWithHatElement
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (CharacterInfo.Hairs == null) { return null; }
|
if (hairWithHatIndex == null)
|
||||||
if (HairWithHatIndex >= CharacterInfo.Hairs.Count)
|
|
||||||
{
|
{
|
||||||
DebugConsole.AddWarning($"Hair with hat index out of range (character: {CharacterInfo?.Name ?? "null"}, index: {HairWithHatIndex})");
|
SetHairWithHatIndex();
|
||||||
}
|
}
|
||||||
return CharacterInfo.Hairs.ElementAtOrDefault(HairWithHatIndex);
|
if (CharacterInfo.Hairs == null) { return null; }
|
||||||
|
if (hairWithHatIndex >= CharacterInfo.Hairs.Count)
|
||||||
|
{
|
||||||
|
DebugConsole.AddWarning($"Hair with hat index out of range (character: {CharacterInfo?.Name ?? "null"}, index: {hairWithHatIndex})");
|
||||||
|
}
|
||||||
|
return CharacterInfo.Hairs.ElementAtOrDefault(hairWithHatIndex.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ContentXElement BeardElement
|
public ContentXElement BeardElement
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -711,7 +721,7 @@ namespace Barotrauma
|
|||||||
private bool IsColorValid(in Color clr)
|
private bool IsColorValid(in Color clr)
|
||||||
=> clr.R != 0 || clr.G != 0 || clr.B != 0;
|
=> clr.R != 0 || clr.G != 0 || clr.B != 0;
|
||||||
|
|
||||||
private void CheckColors()
|
public void CheckColors()
|
||||||
{
|
{
|
||||||
if (!IsColorValid(Head.HairColor))
|
if (!IsColorValid(Head.HairColor))
|
||||||
{
|
{
|
||||||
|
|||||||
+10
-2
@@ -27,6 +27,14 @@ namespace Barotrauma
|
|||||||
get { return _strength; }
|
get { return _strength; }
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
if (!MathUtils.IsValid(value))
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
DebugConsole.ThrowError($"Attempted to set an affliction to an invalid strength ({value})\n" + Environment.StackTrace.CleanupStackTrace());
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (_nonClampedStrength < 0 && value > 0)
|
if (_nonClampedStrength < 0 && value > 0)
|
||||||
{
|
{
|
||||||
_nonClampedStrength = value;
|
_nonClampedStrength = value;
|
||||||
@@ -440,9 +448,9 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
statusEffect.Apply(type, deltaTime, characterHealth.Character, targetLimb);
|
statusEffect.Apply(type, deltaTime, characterHealth.Character, targetLimb);
|
||||||
}
|
}
|
||||||
if (targetLimb != null && statusEffect.HasTargetType(StatusEffect.TargetType.AllLimbs))
|
if (characterHealth?.Character?.AnimController?.Limbs != null && statusEffect.HasTargetType(StatusEffect.TargetType.AllLimbs))
|
||||||
{
|
{
|
||||||
statusEffect.Apply(type, deltaTime, targetLimb.character, targets: targetLimb.character.AnimController.Limbs);
|
statusEffect.Apply(type, deltaTime, characterHealth.Character, targets: characterHealth.Character.AnimController.Limbs);
|
||||||
}
|
}
|
||||||
if (statusEffect.HasTargetType(StatusEffect.TargetType.NearbyItems) ||
|
if (statusEffect.HasTargetType(StatusEffect.TargetType.NearbyItems) ||
|
||||||
statusEffect.HasTargetType(StatusEffect.TargetType.NearbyCharacters))
|
statusEffect.HasTargetType(StatusEffect.TargetType.NearbyCharacters))
|
||||||
|
|||||||
+39
-31
@@ -405,44 +405,52 @@ namespace Barotrauma
|
|||||||
|
|
||||||
var root = doc.Root.FromPackage(pathToAppendage.ContentPackage);
|
var root = doc.Root.FromPackage(pathToAppendage.ContentPackage);
|
||||||
var limbElements = root.GetChildElements("limb").ToDictionary(e => e.GetAttributeString("id", null), e => e);
|
var limbElements = root.GetChildElements("limb").ToDictionary(e => e.GetAttributeString("id", null), e => e);
|
||||||
|
//the IDs may need to be offset if the character has other extra appendages (e.g. from gene splicing)
|
||||||
|
//that take up the IDs of this appendage
|
||||||
|
int idOffset = 0;
|
||||||
foreach (var jointElement in root.GetChildElements("joint"))
|
foreach (var jointElement in root.GetChildElements("joint"))
|
||||||
{
|
{
|
||||||
if (limbElements.TryGetValue(jointElement.GetAttributeString("limb2", null), out ContentXElement limbElement))
|
if (!limbElements.TryGetValue(jointElement.GetAttributeString("limb2", null), out ContentXElement limbElement)) { continue; }
|
||||||
|
|
||||||
|
var jointParams = new RagdollParams.JointParams(jointElement, ragdoll.RagdollParams);
|
||||||
|
Limb attachLimb = null;
|
||||||
|
if (matchingAffliction.AttachLimbId > -1)
|
||||||
{
|
{
|
||||||
var jointParams = new RagdollParams.JointParams(jointElement, ragdoll.RagdollParams);
|
attachLimb = ragdoll.Limbs.FirstOrDefault(l => !l.IsSevered && l.Params.ID == matchingAffliction.AttachLimbId);
|
||||||
Limb attachLimb = null;
|
}
|
||||||
if (matchingAffliction.AttachLimbId > -1)
|
else if (matchingAffliction.AttachLimbName != null)
|
||||||
|
{
|
||||||
|
attachLimb = ragdoll.Limbs.FirstOrDefault(l => !l.IsSevered && l.Name == matchingAffliction.AttachLimbName);
|
||||||
|
}
|
||||||
|
else if (matchingAffliction.AttachLimbType != LimbType.None)
|
||||||
|
{
|
||||||
|
attachLimb = ragdoll.Limbs.FirstOrDefault(l => !l.IsSevered && l.type == matchingAffliction.AttachLimbType);
|
||||||
|
}
|
||||||
|
if (attachLimb == null)
|
||||||
|
{
|
||||||
|
attachLimb = ragdoll.Limbs.FirstOrDefault(l => !l.IsSevered && l.Params.ID == jointParams.Limb1);
|
||||||
|
}
|
||||||
|
if (attachLimb != null)
|
||||||
|
{
|
||||||
|
jointParams.Limb1 = attachLimb.Params.ID;
|
||||||
|
//the joint attaches to a limb outside the character's normal limb count = to another part of the appendage
|
||||||
|
// -> if the appendage's IDs have been offset, we need to take that into account to attach to the correct limb
|
||||||
|
if (jointParams.Limb1 >= ragdoll.RagdollParams.Limbs.Count)
|
||||||
{
|
{
|
||||||
attachLimb = ragdoll.Limbs.FirstOrDefault(l => !l.IsSevered && l.Params.ID == matchingAffliction.AttachLimbId);
|
jointParams.Limb1 += idOffset;
|
||||||
}
|
}
|
||||||
else if (matchingAffliction.AttachLimbName != null)
|
var appendageLimbParams = new RagdollParams.LimbParams(limbElement, ragdoll.RagdollParams);
|
||||||
|
if (idOffset == 0)
|
||||||
{
|
{
|
||||||
attachLimb = ragdoll.Limbs.FirstOrDefault(l => !l.IsSevered && l.Name == matchingAffliction.AttachLimbName);
|
idOffset = ragdoll.Limbs.Length - appendageLimbParams.ID;
|
||||||
}
|
|
||||||
else if (matchingAffliction.AttachLimbType != LimbType.None)
|
|
||||||
{
|
|
||||||
attachLimb = ragdoll.Limbs.FirstOrDefault(l => !l.IsSevered && l.type == matchingAffliction.AttachLimbType);
|
|
||||||
}
|
|
||||||
if (attachLimb == null)
|
|
||||||
{
|
|
||||||
attachLimb = ragdoll.Limbs.FirstOrDefault(l => !l.IsSevered && l.Params.ID == jointParams.Limb1);
|
|
||||||
}
|
|
||||||
if (attachLimb != null)
|
|
||||||
{
|
|
||||||
jointParams.Limb1 = attachLimb.Params.ID;
|
|
||||||
var appendageLimbParams = new RagdollParams.LimbParams(limbElement, ragdoll.RagdollParams)
|
|
||||||
{
|
|
||||||
// Ensure that we have a valid id for the new limb
|
|
||||||
ID = ragdoll.Limbs.Length
|
|
||||||
};
|
|
||||||
jointParams.Limb2 = appendageLimbParams.ID;
|
|
||||||
Limb huskAppendage = new Limb(ragdoll, character, appendageLimbParams);
|
|
||||||
huskAppendage.body.Submarine = character.Submarine;
|
|
||||||
huskAppendage.body.SetTransform(attachLimb.SimPosition, attachLimb.Rotation);
|
|
||||||
ragdoll.AddLimb(huskAppendage);
|
|
||||||
ragdoll.AddJoint(jointParams);
|
|
||||||
appendage.Add(huskAppendage);
|
|
||||||
}
|
}
|
||||||
|
jointParams.Limb2 = appendageLimbParams.ID = ragdoll.Limbs.Length;
|
||||||
|
Limb huskAppendage = new Limb(ragdoll, character, appendageLimbParams);
|
||||||
|
huskAppendage.body.Submarine = character.Submarine;
|
||||||
|
huskAppendage.body.SetTransform(attachLimb.SimPosition, attachLimb.Rotation);
|
||||||
|
ragdoll.AddLimb(huskAppendage);
|
||||||
|
ragdoll.AddJoint(jointParams);
|
||||||
|
appendage.Add(huskAppendage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return appendage;
|
return appendage;
|
||||||
|
|||||||
@@ -895,10 +895,16 @@ namespace Barotrauma
|
|||||||
float increaseSpeed = 10.0f;
|
float increaseSpeed = 10.0f;
|
||||||
decreaseSpeed *= (1f - oxygenlowResistance);
|
decreaseSpeed *= (1f - oxygenlowResistance);
|
||||||
increaseSpeed *= (1f + oxygenlowResistance);
|
increaseSpeed *= (1f + oxygenlowResistance);
|
||||||
|
float holdBreathMultiplier = Character.GetStatValue(StatTypes.HoldBreathMultiplier);
|
||||||
float holdBreathMultiplier = 1f + GetStatValue(StatTypes.HoldBreathMultiplier);
|
if (holdBreathMultiplier <= -1.0f)
|
||||||
decreaseSpeed *= holdBreathMultiplier;
|
{
|
||||||
OxygenAmount = MathHelper.Clamp(OxygenAmount + deltaTime * (Character.OxygenAvailable < InsufficientOxygenThreshold ? decreaseSpeed : increaseSpeed), -100.0f, 100.0f);
|
OxygenAmount = -100.0f;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
decreaseSpeed /= 1.0f + Character.GetStatValue(StatTypes.HoldBreathMultiplier);
|
||||||
|
OxygenAmount = MathHelper.Clamp(OxygenAmount + deltaTime * (Character.OxygenAvailable < InsufficientOxygenThreshold ? decreaseSpeed : increaseSpeed), -100.0f, 100.0f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateOxygenProjSpecific(prevOxygen, deltaTime);
|
UpdateOxygenProjSpecific(prevOxygen, deltaTime);
|
||||||
|
|||||||
-1
@@ -2,7 +2,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Xml.Linq;
|
|
||||||
|
|
||||||
namespace Barotrauma.Abilities
|
namespace Barotrauma.Abilities
|
||||||
{
|
{
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
using System.Collections.Immutable;
|
using Microsoft.Xna.Framework;
|
||||||
using Microsoft.Xna.Framework;
|
using System.Collections.Immutable;
|
||||||
|
|
||||||
namespace Barotrauma.Abilities
|
namespace Barotrauma.Abilities
|
||||||
{
|
{
|
||||||
|
|||||||
+8
-8
@@ -1,7 +1,4 @@
|
|||||||
using Microsoft.Xna.Framework;
|
namespace Barotrauma.Abilities
|
||||||
using System.Xml.Linq;
|
|
||||||
|
|
||||||
namespace Barotrauma.Abilities
|
|
||||||
{
|
{
|
||||||
class CharacterAbilityGainSimultaneousSkill : CharacterAbility
|
class CharacterAbilityGainSimultaneousSkill : CharacterAbility
|
||||||
{
|
{
|
||||||
@@ -15,6 +12,10 @@ namespace Barotrauma.Abilities
|
|||||||
skillIdentifier = abilityElement.GetAttributeIdentifier("skillidentifier", "");
|
skillIdentifier = abilityElement.GetAttributeIdentifier("skillidentifier", "");
|
||||||
ignoreAbilitySkillGain = abilityElement.GetAttributeBool("ignoreabilityskillgain", true);
|
ignoreAbilitySkillGain = abilityElement.GetAttributeBool("ignoreabilityskillgain", true);
|
||||||
targetAllies = abilityElement.GetAttributeBool("targetallies", false);
|
targetAllies = abilityElement.GetAttributeBool("targetallies", false);
|
||||||
|
if (skillIdentifier.IsEmpty)
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}: skill identifier not defined.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ApplyEffect(AbilityObject abilityObject)
|
protected override void ApplyEffect(AbilityObject abilityObject)
|
||||||
@@ -23,13 +24,12 @@ namespace Barotrauma.Abilities
|
|||||||
{
|
{
|
||||||
if (ignoreAbilitySkillGain && abilitySkillGain.GainedFromAbility) { return; }
|
if (ignoreAbilitySkillGain && abilitySkillGain.GainedFromAbility) { return; }
|
||||||
Identifier identifier = skillIdentifier == "inherit" ? abilitySkillGain.SkillIdentifier : skillIdentifier;
|
Identifier identifier = skillIdentifier == "inherit" ? abilitySkillGain.SkillIdentifier : skillIdentifier;
|
||||||
|
|
||||||
if (targetAllies)
|
if (targetAllies)
|
||||||
{
|
{
|
||||||
foreach (Character character in Character.GetFriendlyCrew(Character))
|
foreach (Character otherCharacter in Character.GetFriendlyCrew(Character))
|
||||||
{
|
{
|
||||||
if (character == Character) { continue; }
|
if (otherCharacter == Character) { continue; }
|
||||||
Character.Info?.IncreaseSkillLevel(identifier, abilitySkillGain.Value, gainedFromAbility: true);
|
otherCharacter.Info?.IncreaseSkillLevel(identifier, abilitySkillGain.Value, gainedFromAbility: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
+2
-4
@@ -1,6 +1,4 @@
|
|||||||
using System.Xml.Linq;
|
namespace Barotrauma.Abilities
|
||||||
|
|
||||||
namespace Barotrauma.Abilities
|
|
||||||
{
|
{
|
||||||
class CharacterAbilityGiveAffliction : CharacterAbility
|
class CharacterAbilityGiveAffliction : CharacterAbility
|
||||||
{
|
{
|
||||||
@@ -18,7 +16,7 @@ namespace Barotrauma.Abilities
|
|||||||
|
|
||||||
if (afflictionId.IsEmpty)
|
if (afflictionId.IsEmpty)
|
||||||
{
|
{
|
||||||
DebugConsole.ThrowError("Error in CharacterAbilityGiveAffliction - affliction identifier not set.");
|
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, CharacterAbilityGiveAffliction - affliction identifier not set.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-3
@@ -1,6 +1,4 @@
|
|||||||
using System.Xml.Linq;
|
namespace Barotrauma.Abilities
|
||||||
|
|
||||||
namespace Barotrauma.Abilities
|
|
||||||
{
|
{
|
||||||
class CharacterAbilityGiveFlag : CharacterAbility
|
class CharacterAbilityGiveFlag : CharacterAbility
|
||||||
{
|
{
|
||||||
|
|||||||
+6
-3
@@ -1,6 +1,4 @@
|
|||||||
using System.Xml.Linq;
|
namespace Barotrauma.Abilities
|
||||||
|
|
||||||
namespace Barotrauma.Abilities
|
|
||||||
{
|
{
|
||||||
class CharacterAbilityGiveMoney : CharacterAbility
|
class CharacterAbilityGiveMoney : CharacterAbility
|
||||||
{
|
{
|
||||||
@@ -13,6 +11,11 @@ namespace Barotrauma.Abilities
|
|||||||
{
|
{
|
||||||
amount = abilityElement.GetAttributeInt("amount", 0);
|
amount = abilityElement.GetAttributeInt("amount", 0);
|
||||||
scalingStatIdentifier = abilityElement.GetAttributeIdentifier("scalingstatidentifier", Identifier.Empty);
|
scalingStatIdentifier = abilityElement.GetAttributeIdentifier("scalingstatidentifier", Identifier.Empty);
|
||||||
|
|
||||||
|
if (amount == 0)
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, CharacterAbilityGiveMoney - amount of money set to 0.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplyEffectSpecific(Character targetCharacter)
|
private void ApplyEffectSpecific(Character targetCharacter)
|
||||||
|
|||||||
+5
-3
@@ -1,6 +1,4 @@
|
|||||||
using System;
|
namespace Barotrauma.Abilities
|
||||||
|
|
||||||
namespace Barotrauma.Abilities
|
|
||||||
{
|
{
|
||||||
public enum PermanentStatPlaceholder
|
public enum PermanentStatPlaceholder
|
||||||
{
|
{
|
||||||
@@ -28,6 +26,10 @@ namespace Barotrauma.Abilities
|
|||||||
public CharacterAbilityGivePermanentStat(CharacterAbilityGroup characterAbilityGroup, ContentXElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
public CharacterAbilityGivePermanentStat(CharacterAbilityGroup characterAbilityGroup, ContentXElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||||
{
|
{
|
||||||
statIdentifier = abilityElement.GetAttributeIdentifier("statidentifier", Identifier.Empty);
|
statIdentifier = abilityElement.GetAttributeIdentifier("statidentifier", Identifier.Empty);
|
||||||
|
if (statIdentifier.IsEmpty)
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError($"Error in talent \"{CharacterTalent.DebugIdentifier}\" - stat identifier not defined.");
|
||||||
|
}
|
||||||
string statTypeName = abilityElement.GetAttributeString("stattype", string.Empty);
|
string statTypeName = abilityElement.GetAttributeString("stattype", string.Empty);
|
||||||
statType = string.IsNullOrEmpty(statTypeName) ? StatTypes.None : CharacterAbilityGroup.ParseStatType(statTypeName, CharacterTalent.DebugIdentifier);
|
statType = string.IsNullOrEmpty(statTypeName) ? StatTypes.None : CharacterAbilityGroup.ParseStatType(statTypeName, CharacterTalent.DebugIdentifier);
|
||||||
value = abilityElement.GetAttributeFloat("value", 0f);
|
value = abilityElement.GetAttributeFloat("value", 0f);
|
||||||
|
|||||||
+8
@@ -11,6 +11,14 @@ namespace Barotrauma.Abilities
|
|||||||
{
|
{
|
||||||
factionIdentifier = abilityElement.GetAttributeIdentifier("identifier", Identifier.Empty);
|
factionIdentifier = abilityElement.GetAttributeIdentifier("identifier", Identifier.Empty);
|
||||||
amount = abilityElement.GetAttributeFloat("amount", 0f);
|
amount = abilityElement.GetAttributeFloat("amount", 0f);
|
||||||
|
if (factionIdentifier.IsEmpty)
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, faction identifier not defined.");
|
||||||
|
}
|
||||||
|
if (amount == 0)
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, amount of reputation to give is 0.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ApplyEffect()
|
protected override void ApplyEffect()
|
||||||
|
|||||||
+9
-5
@@ -1,6 +1,4 @@
|
|||||||
using System.Xml.Linq;
|
namespace Barotrauma.Abilities
|
||||||
|
|
||||||
namespace Barotrauma.Abilities
|
|
||||||
{
|
{
|
||||||
class CharacterAbilityGiveResistance : CharacterAbility
|
class CharacterAbilityGiveResistance : CharacterAbility
|
||||||
{
|
{
|
||||||
@@ -10,17 +8,23 @@ namespace Barotrauma.Abilities
|
|||||||
public CharacterAbilityGiveResistance(CharacterAbilityGroup characterAbilityGroup, ContentXElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
public CharacterAbilityGiveResistance(CharacterAbilityGroup characterAbilityGroup, ContentXElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||||
{
|
{
|
||||||
resistanceId = abilityElement.GetAttributeIdentifier("resistanceid", abilityElement.GetAttributeIdentifier("resistance", Identifier.Empty));
|
resistanceId = abilityElement.GetAttributeIdentifier("resistanceid", abilityElement.GetAttributeIdentifier("resistance", Identifier.Empty));
|
||||||
multiplier = abilityElement.GetAttributeFloat("multiplier", 1f); // rename this to resistance for consistency
|
multiplier = abilityElement.GetAttributeFloat("multiplier", 1f);
|
||||||
|
|
||||||
if (resistanceId.IsEmpty)
|
if (resistanceId.IsEmpty)
|
||||||
{
|
{
|
||||||
DebugConsole.ThrowError("Error in CharacterAbilityGiveResistance - resistance identifier not set.");
|
DebugConsole.ThrowError("Error in CharacterAbilityGiveResistance - resistance identifier not set.");
|
||||||
}
|
}
|
||||||
|
if (MathUtils.NearlyEqual(multiplier, 1))
|
||||||
|
{
|
||||||
|
DebugConsole.AddWarning($"Possible error in talent {CharacterTalent.DebugIdentifier} - multiplier set to 1, which will do nothing.");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void InitializeAbility(bool addingFirstTime)
|
public override void InitializeAbility(bool addingFirstTime)
|
||||||
{
|
{
|
||||||
Character.ChangeAbilityResistance(resistanceId, multiplier);
|
TalentResistanceIdentifier identifier = new(resistanceId, CharacterTalent.Prefab.Identifier);
|
||||||
|
Character.ChangeAbilityResistance(identifier, multiplier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-3
@@ -1,6 +1,4 @@
|
|||||||
using System.Xml.Linq;
|
namespace Barotrauma.Abilities
|
||||||
|
|
||||||
namespace Barotrauma.Abilities
|
|
||||||
{
|
{
|
||||||
class CharacterAbilityGiveStat : CharacterAbility
|
class CharacterAbilityGiveStat : CharacterAbility
|
||||||
{
|
{
|
||||||
|
|||||||
+5
-4
@@ -1,7 +1,4 @@
|
|||||||
using Barotrauma.Extensions;
|
namespace Barotrauma.Abilities
|
||||||
using System.Xml.Linq;
|
|
||||||
|
|
||||||
namespace Barotrauma.Abilities
|
|
||||||
{
|
{
|
||||||
class CharacterAbilityGiveTalentPoints : CharacterAbility
|
class CharacterAbilityGiveTalentPoints : CharacterAbility
|
||||||
{
|
{
|
||||||
@@ -10,6 +7,10 @@ namespace Barotrauma.Abilities
|
|||||||
public CharacterAbilityGiveTalentPoints(CharacterAbilityGroup characterAbilityGroup, ContentXElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
public CharacterAbilityGiveTalentPoints(CharacterAbilityGroup characterAbilityGroup, ContentXElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||||
{
|
{
|
||||||
amount = abilityElement.GetAttributeInt("amount", 0);
|
amount = abilityElement.GetAttributeInt("amount", 0);
|
||||||
|
if (amount == 0)
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, amount of talent points to give is 0.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void InitializeAbility(bool addingFirstTime)
|
public override void InitializeAbility(bool addingFirstTime)
|
||||||
|
|||||||
+4
@@ -9,6 +9,10 @@ namespace Barotrauma.Abilities
|
|||||||
public CharacterAbilityGiveTalentPointsToAllies(CharacterAbilityGroup characterAbilityGroup, ContentXElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
public CharacterAbilityGiveTalentPointsToAllies(CharacterAbilityGroup characterAbilityGroup, ContentXElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||||
{
|
{
|
||||||
amount = abilityElement.GetAttributeInt("amount", 0);
|
amount = abilityElement.GetAttributeInt("amount", 0);
|
||||||
|
if (amount == 0)
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, amount of talent points to give is 0.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void InitializeAbility(bool addingFirstTime)
|
public override void InitializeAbility(bool addingFirstTime)
|
||||||
|
|||||||
-2
@@ -1,6 +1,4 @@
|
|||||||
using Barotrauma.Extensions;
|
using Barotrauma.Extensions;
|
||||||
using Microsoft.Xna.Framework;
|
|
||||||
using System.Xml.Linq;
|
|
||||||
|
|
||||||
namespace Barotrauma.Abilities
|
namespace Barotrauma.Abilities
|
||||||
{
|
{
|
||||||
|
|||||||
+4
@@ -6,6 +6,10 @@ namespace Barotrauma.Abilities
|
|||||||
public CharacterAbilityMarkAsLooted(CharacterAbilityGroup characterAbilityGroup, ContentXElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
public CharacterAbilityMarkAsLooted(CharacterAbilityGroup characterAbilityGroup, ContentXElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||||
{
|
{
|
||||||
identifier = abilityElement.GetAttributeIdentifier("identifier", Identifier.Empty);
|
identifier = abilityElement.GetAttributeIdentifier("identifier", Identifier.Empty);
|
||||||
|
if (identifier.IsEmpty)
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, identifier is empty in {nameof(CharacterAbilityMarkAsLooted)}.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ApplyEffect(AbilityObject abilityObject)
|
protected override void ApplyEffect(AbilityObject abilityObject)
|
||||||
|
|||||||
+1
-4
@@ -1,7 +1,4 @@
|
|||||||
using System;
|
namespace Barotrauma.Abilities
|
||||||
using System.Xml.Linq;
|
|
||||||
|
|
||||||
namespace Barotrauma.Abilities
|
|
||||||
{
|
{
|
||||||
class CharacterAbilityModifyFlag : CharacterAbility
|
class CharacterAbilityModifyFlag : CharacterAbility
|
||||||
{
|
{
|
||||||
|
|||||||
+18
-8
@@ -1,23 +1,25 @@
|
|||||||
using System.Xml.Linq;
|
namespace Barotrauma.Abilities
|
||||||
|
|
||||||
namespace Barotrauma.Abilities
|
|
||||||
{
|
{
|
||||||
class CharacterAbilityModifyResistance : CharacterAbility
|
class CharacterAbilityModifyResistance : CharacterAbility
|
||||||
{
|
{
|
||||||
private readonly Identifier resistanceId;
|
private readonly Identifier resistanceId;
|
||||||
private readonly float resistance;
|
private readonly float multiplier;
|
||||||
bool lastState;
|
bool lastState;
|
||||||
public override bool AllowClientSimulation => true;
|
public override bool AllowClientSimulation => true;
|
||||||
|
|
||||||
// should probably be split to different classes
|
// should probably be split to different classes
|
||||||
public CharacterAbilityModifyResistance(CharacterAbilityGroup characterAbilityGroup, ContentXElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
public CharacterAbilityModifyResistance(CharacterAbilityGroup characterAbilityGroup, ContentXElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||||
{
|
{
|
||||||
resistanceId = abilityElement.GetAttributeIdentifier("resistanceid", "");
|
resistanceId = abilityElement.GetAttributeIdentifier("resistanceid", abilityElement.GetAttributeIdentifier("resistance", Identifier.Empty));
|
||||||
resistance = abilityElement.GetAttributeFloat("resistance", 1f);
|
multiplier = abilityElement.GetAttributeFloat("multiplier", 1f);
|
||||||
|
|
||||||
if (resistanceId.IsEmpty)
|
if (resistanceId.IsEmpty)
|
||||||
{
|
{
|
||||||
DebugConsole.ThrowError("Error in CharacterAbilityModifyResistance - resistance identifier not set.");
|
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier} - resistance identifier not set in {nameof(CharacterAbilityModifyResistance)}.");
|
||||||
|
}
|
||||||
|
if (MathUtils.NearlyEqual(multiplier, 1.0f))
|
||||||
|
{
|
||||||
|
DebugConsole.AddWarning($"Possible error in talent {CharacterTalent.DebugIdentifier} - resistance set to 1, which will do nothing.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,7 +27,15 @@ namespace Barotrauma.Abilities
|
|||||||
{
|
{
|
||||||
if (conditionsMatched != lastState)
|
if (conditionsMatched != lastState)
|
||||||
{
|
{
|
||||||
Character.ChangeAbilityResistance(resistanceId, conditionsMatched ? resistance : 1 / resistance);
|
TalentResistanceIdentifier identifier = new(resistanceId, CharacterTalent.Prefab.Identifier);
|
||||||
|
if (conditionsMatched)
|
||||||
|
{
|
||||||
|
Character.ChangeAbilityResistance(identifier, multiplier);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Character.RemoveAbilityResistance(identifier);
|
||||||
|
}
|
||||||
lastState = conditionsMatched;
|
lastState = conditionsMatched;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-3
@@ -1,6 +1,4 @@
|
|||||||
using System.Xml.Linq;
|
namespace Barotrauma.Abilities
|
||||||
|
|
||||||
namespace Barotrauma.Abilities
|
|
||||||
{
|
{
|
||||||
class CharacterAbilityModifyStat : CharacterAbility
|
class CharacterAbilityModifyStat : CharacterAbility
|
||||||
{
|
{
|
||||||
|
|||||||
-1
@@ -1,5 +1,4 @@
|
|||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using System.Xml.Linq;
|
|
||||||
|
|
||||||
namespace Barotrauma.Abilities
|
namespace Barotrauma.Abilities
|
||||||
{
|
{
|
||||||
|
|||||||
-1
@@ -1,5 +1,4 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Xml.Linq;
|
|
||||||
|
|
||||||
namespace Barotrauma.Abilities
|
namespace Barotrauma.Abilities
|
||||||
{
|
{
|
||||||
|
|||||||
+5
-3
@@ -1,6 +1,4 @@
|
|||||||
using System.Xml.Linq;
|
namespace Barotrauma.Abilities
|
||||||
|
|
||||||
namespace Barotrauma.Abilities
|
|
||||||
{
|
{
|
||||||
class CharacterAbilityModifyValue : CharacterAbility
|
class CharacterAbilityModifyValue : CharacterAbility
|
||||||
{
|
{
|
||||||
@@ -11,6 +9,10 @@ namespace Barotrauma.Abilities
|
|||||||
{
|
{
|
||||||
addedValue = abilityElement.GetAttributeFloat("addedvalue", 0f);
|
addedValue = abilityElement.GetAttributeFloat("addedvalue", 0f);
|
||||||
multiplyValue = abilityElement.GetAttributeFloat("multiplyvalue", 1f);
|
multiplyValue = abilityElement.GetAttributeFloat("multiplyvalue", 1f);
|
||||||
|
if (MathUtils.NearlyEqual(addedValue, 0.0f) && MathUtils.NearlyEqual(multiplyValue, 1.0f))
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, {nameof(CharacterAbilityModifyValue)} - added value is 0 and multiplier is 1, the ability will do nothing.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ApplyEffect(AbilityObject abilityObject)
|
protected override void ApplyEffect(AbilityObject abilityObject)
|
||||||
|
|||||||
+1
-3
@@ -1,6 +1,4 @@
|
|||||||
using System.Xml.Linq;
|
namespace Barotrauma.Abilities
|
||||||
|
|
||||||
namespace Barotrauma.Abilities
|
|
||||||
{
|
{
|
||||||
class CharacterAbilityPutItem : CharacterAbility
|
class CharacterAbilityPutItem : CharacterAbility
|
||||||
{
|
{
|
||||||
|
|||||||
+4
@@ -10,6 +10,10 @@ namespace Barotrauma.Abilities
|
|||||||
public CharacterAbilityResetPermanentStat(CharacterAbilityGroup characterAbilityGroup, ContentXElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
public CharacterAbilityResetPermanentStat(CharacterAbilityGroup characterAbilityGroup, ContentXElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||||
{
|
{
|
||||||
statIdentifier = abilityElement.GetAttributeIdentifier("statidentifier", Identifier.Empty);
|
statIdentifier = abilityElement.GetAttributeIdentifier("statidentifier", Identifier.Empty);
|
||||||
|
if (statIdentifier.IsEmpty)
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, {nameof(CharacterAbilityResetPermanentStat)} - statIdentifier is empty.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
protected override void ApplyEffect(AbilityObject abilityObject)
|
protected override void ApplyEffect(AbilityObject abilityObject)
|
||||||
{
|
{
|
||||||
|
|||||||
+1
-4
@@ -1,7 +1,4 @@
|
|||||||
using Microsoft.Xna.Framework;
|
namespace Barotrauma.Abilities
|
||||||
using System.Xml.Linq;
|
|
||||||
|
|
||||||
namespace Barotrauma.Abilities
|
|
||||||
{
|
{
|
||||||
class CharacterAbilityRevive : CharacterAbility
|
class CharacterAbilityRevive : CharacterAbility
|
||||||
{
|
{
|
||||||
|
|||||||
+4
@@ -11,6 +11,10 @@ namespace Barotrauma.Abilities
|
|||||||
{
|
{
|
||||||
identifier = abilityElement.GetAttributeIdentifier("identifier", Identifier.Empty);
|
identifier = abilityElement.GetAttributeIdentifier("identifier", Identifier.Empty);
|
||||||
value = abilityElement.GetAttributeInt("value", 0);
|
value = abilityElement.GetAttributeInt("value", 0);
|
||||||
|
if (identifier.IsEmpty)
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, {nameof(CharacterAbilitySetMetadataInt)} - identifier is empty.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void InitializeAbility(bool addingFirstTime)
|
public override void InitializeAbility(bool addingFirstTime)
|
||||||
|
|||||||
-1
@@ -1,5 +1,4 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Xml.Linq;
|
|
||||||
|
|
||||||
namespace Barotrauma.Abilities
|
namespace Barotrauma.Abilities
|
||||||
{
|
{
|
||||||
|
|||||||
-2
@@ -1,7 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Xml.Linq;
|
|
||||||
|
|
||||||
namespace Barotrauma.Abilities
|
namespace Barotrauma.Abilities
|
||||||
{
|
{
|
||||||
|
|||||||
+1
-3
@@ -1,6 +1,4 @@
|
|||||||
using System.Xml.Linq;
|
namespace Barotrauma.Abilities
|
||||||
|
|
||||||
namespace Barotrauma.Abilities
|
|
||||||
{
|
{
|
||||||
class CharacterAbilityApprenticeship : CharacterAbility
|
class CharacterAbilityApprenticeship : CharacterAbility
|
||||||
{
|
{
|
||||||
|
|||||||
-3
@@ -1,20 +1,17 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Xml.Linq;
|
|
||||||
|
|
||||||
namespace Barotrauma.Abilities
|
namespace Barotrauma.Abilities
|
||||||
{
|
{
|
||||||
class CharacterAbilityAtmosMachine : CharacterAbility
|
class CharacterAbilityAtmosMachine : CharacterAbility
|
||||||
{
|
{
|
||||||
private readonly float addedValue;
|
private readonly float addedValue;
|
||||||
private readonly float multiplyValue;
|
|
||||||
private readonly Identifier[] tags;
|
private readonly Identifier[] tags;
|
||||||
private readonly int maxMultiplyCount;
|
private readonly int maxMultiplyCount;
|
||||||
|
|
||||||
public CharacterAbilityAtmosMachine(CharacterAbilityGroup characterAbilityGroup, ContentXElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
public CharacterAbilityAtmosMachine(CharacterAbilityGroup characterAbilityGroup, ContentXElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||||
{
|
{
|
||||||
addedValue = abilityElement.GetAttributeFloat("addedvalue", 0f);
|
addedValue = abilityElement.GetAttributeFloat("addedvalue", 0f);
|
||||||
multiplyValue = abilityElement.GetAttributeFloat("multiplyvalue", 1f);
|
|
||||||
tags = abilityElement.GetAttributeIdentifierArray("tags", Array.Empty<Identifier>());
|
tags = abilityElement.GetAttributeIdentifierArray("tags", Array.Empty<Identifier>());
|
||||||
maxMultiplyCount = abilityElement.GetAttributeInt("maxmultiplycount", int.MaxValue);
|
maxMultiplyCount = abilityElement.GetAttributeInt("maxmultiplycount", int.MaxValue);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-5
@@ -1,11 +1,8 @@
|
|||||||
using System.Collections.Generic;
|
namespace Barotrauma.Abilities
|
||||||
using System.Xml.Linq;
|
|
||||||
|
|
||||||
namespace Barotrauma.Abilities
|
|
||||||
{
|
{
|
||||||
class CharacterAbilityBountyHunter : CharacterAbility
|
class CharacterAbilityBountyHunter : CharacterAbility
|
||||||
{
|
{
|
||||||
private float vitalityPercentage;
|
private readonly float vitalityPercentage;
|
||||||
|
|
||||||
public CharacterAbilityBountyHunter(CharacterAbilityGroup characterAbilityGroup, ContentXElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
public CharacterAbilityBountyHunter(CharacterAbilityGroup characterAbilityGroup, ContentXElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||||
{
|
{
|
||||||
|
|||||||
-1
@@ -1,6 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Xml.Linq;
|
|
||||||
|
|
||||||
namespace Barotrauma.Abilities
|
namespace Barotrauma.Abilities
|
||||||
{
|
{
|
||||||
|
|||||||
+1
-4
@@ -1,7 +1,4 @@
|
|||||||
using Microsoft.Xna.Framework;
|
namespace Barotrauma.Abilities
|
||||||
using System.Xml.Linq;
|
|
||||||
|
|
||||||
namespace Barotrauma.Abilities
|
|
||||||
{
|
{
|
||||||
class CharacterAbilityMultitasker : CharacterAbility
|
class CharacterAbilityMultitasker : CharacterAbility
|
||||||
{
|
{
|
||||||
|
|||||||
+4
-6
@@ -1,12 +1,10 @@
|
|||||||
using System.Xml.Linq;
|
namespace Barotrauma.Abilities
|
||||||
|
|
||||||
namespace Barotrauma.Abilities
|
|
||||||
{
|
{
|
||||||
class CharacterAbilityPsychoClown : CharacterAbility
|
class CharacterAbilityPsychoClown : CharacterAbility
|
||||||
{
|
{
|
||||||
private StatTypes statType;
|
private readonly StatTypes statType;
|
||||||
private float maxValue;
|
private readonly float maxValue;
|
||||||
private string afflictionIdentifier;
|
private readonly string afflictionIdentifier;
|
||||||
private float lastValue = 0f;
|
private float lastValue = 0f;
|
||||||
public override bool AllowClientSimulation => true;
|
public override bool AllowClientSimulation => true;
|
||||||
|
|
||||||
|
|||||||
-3
@@ -1,8 +1,5 @@
|
|||||||
using Barotrauma.Items.Components;
|
using Barotrauma.Items.Components;
|
||||||
using Microsoft.Xna.Framework;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Xml.Linq;
|
|
||||||
|
|
||||||
namespace Barotrauma.Abilities
|
namespace Barotrauma.Abilities
|
||||||
{
|
{
|
||||||
|
|||||||
+1
-1
@@ -1,8 +1,8 @@
|
|||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
|
using Barotrauma.Extensions;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using Barotrauma.Extensions;
|
|
||||||
|
|
||||||
namespace Barotrauma.Abilities
|
namespace Barotrauma.Abilities
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -66,8 +66,8 @@ namespace Barotrauma
|
|||||||
IEnumerable<TalentSubTree> blockingSubTrees = tree.TalentSubTrees.Where(tst => tst.BlockedTrees.Contains(targetTree.Identifier)),
|
IEnumerable<TalentSubTree> blockingSubTrees = tree.TalentSubTrees.Where(tst => tst.BlockedTrees.Contains(targetTree.Identifier)),
|
||||||
requiredSubTrees = tree.TalentSubTrees.Where(tst => targetTree.RequiredTrees.Contains(tst.Identifier));
|
requiredSubTrees = tree.TalentSubTrees.Where(tst => targetTree.RequiredTrees.Contains(tst.Identifier));
|
||||||
|
|
||||||
return requiredSubTrees.All(tst => tst.IsCompleted(selectedTalents)) && // check if we meet requirements
|
return requiredSubTrees.All(tst => tst.HasEnoughTalents(selectedTalents)) && // check if we meet requirements
|
||||||
!blockingSubTrees.Any(tst => tst.HasAnyTalent(selectedTalents)); // check if any other talent trees are blocking this one
|
!blockingSubTrees.Any(tst => tst.HasAnyTalent(selectedTalents) && !tst.HasMaxTalents(selectedTalents)); // check if any other talent trees are blocking this one
|
||||||
}
|
}
|
||||||
|
|
||||||
// i hate this function - markus
|
// i hate this function - markus
|
||||||
@@ -128,16 +128,26 @@ namespace Barotrauma
|
|||||||
public static bool IsViableTalentForCharacter(Character character, Identifier talentIdentifier, IReadOnlyCollection<Identifier> selectedTalents)
|
public static bool IsViableTalentForCharacter(Character character, Identifier talentIdentifier, IReadOnlyCollection<Identifier> selectedTalents)
|
||||||
{
|
{
|
||||||
if (character?.Info?.Job.Prefab == null) { return false; }
|
if (character?.Info?.Job.Prefab == null) { return false; }
|
||||||
|
|
||||||
if (character.Info.GetTotalTalentPoints() - selectedTalents.Count <= 0) { return false; }
|
if (character.Info.GetTotalTalentPoints() - selectedTalents.Count <= 0) { return false; }
|
||||||
|
|
||||||
if (!JobTalentTrees.TryGet(character.Info.Job.Prefab.Identifier, out TalentTree talentTree)) { return false; }
|
if (!JobTalentTrees.TryGet(character.Info.Job.Prefab.Identifier, out TalentTree talentTree)) { return false; }
|
||||||
|
|
||||||
foreach (var subTree in talentTree!.TalentSubTrees)
|
foreach (var subTree in talentTree!.TalentSubTrees)
|
||||||
{
|
{
|
||||||
|
if (subTree.AllTalentIdentifiers.Contains(talentIdentifier) && subTree.HasMaxTalents(selectedTalents)) { return false; }
|
||||||
|
|
||||||
foreach (var talentOptionStage in subTree.TalentOptionStages)
|
foreach (var talentOptionStage in subTree.TalentOptionStages)
|
||||||
{
|
{
|
||||||
bool hasTalentInThisTier = talentOptionStage.HasEnoughTalents(selectedTalents);
|
if (talentOptionStage.TalentIdentifiers.Contains(talentIdentifier))
|
||||||
|
{
|
||||||
|
return TalentTreeMeetsRequirements(talentTree, subTree, selectedTalents);
|
||||||
|
}
|
||||||
|
bool optionStageCompleted = talentOptionStage.HasEnoughTalents(selectedTalents);
|
||||||
|
if (!optionStageCompleted)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*bool hasTalentInThisTier = talentOptionStage.HasMaxTalents(selectedTalents);
|
||||||
if (!hasTalentInThisTier)
|
if (!hasTalentInThisTier)
|
||||||
{
|
{
|
||||||
if (talentOptionStage.TalentIdentifiers.Contains(talentIdentifier))
|
if (talentOptionStage.TalentIdentifiers.Contains(talentIdentifier))
|
||||||
@@ -145,7 +155,7 @@ namespace Barotrauma
|
|||||||
return TalentTreeMeetsRequirements(talentTree, subTree, selectedTalents);
|
return TalentTreeMeetsRequirements(talentTree, subTree, selectedTalents);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,7 +206,8 @@ namespace Barotrauma
|
|||||||
public readonly ImmutableHashSet<Identifier> RequiredTrees;
|
public readonly ImmutableHashSet<Identifier> RequiredTrees;
|
||||||
public readonly ImmutableHashSet<Identifier> BlockedTrees;
|
public readonly ImmutableHashSet<Identifier> BlockedTrees;
|
||||||
|
|
||||||
public bool IsCompleted(IReadOnlyCollection<Identifier> talents) => TalentOptionStages.All(option => option.HasEnoughTalents(talents));
|
public bool HasEnoughTalents(IReadOnlyCollection<Identifier> talents) => TalentOptionStages.All(option => option.HasEnoughTalents(talents));
|
||||||
|
public bool HasMaxTalents(IReadOnlyCollection<Identifier> talents) => TalentOptionStages.All(option => option.HasMaxTalents(talents));
|
||||||
public bool HasAnyTalent(IReadOnlyCollection<Identifier> talents) => TalentOptionStages.Any(option => option.HasSelectedTalent(talents));
|
public bool HasAnyTalent(IReadOnlyCollection<Identifier> talents) => TalentOptionStages.Any(option => option.HasSelectedTalent(talents));
|
||||||
|
|
||||||
public TalentSubTree(ContentXElement subTreeElement)
|
public TalentSubTree(ContentXElement subTreeElement)
|
||||||
@@ -228,6 +239,13 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public IEnumerable<Identifier> TalentIdentifiers => talentIdentifiers;
|
public IEnumerable<Identifier> TalentIdentifiers => talentIdentifiers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How many talents need to be unlocked to consider this tree completed
|
||||||
|
/// </summary>
|
||||||
|
public readonly int RequiredTalents;
|
||||||
|
/// <summary>
|
||||||
|
/// How many talents can be unlocked in total
|
||||||
|
/// </summary>
|
||||||
public readonly int MaxChosenTalents;
|
public readonly int MaxChosenTalents;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -236,8 +254,9 @@ namespace Barotrauma
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly Dictionary<Identifier, ImmutableHashSet<Identifier>> ShowCaseTalents = new Dictionary<Identifier, ImmutableHashSet<Identifier>>();
|
public readonly Dictionary<Identifier, ImmutableHashSet<Identifier>> ShowCaseTalents = new Dictionary<Identifier, ImmutableHashSet<Identifier>>();
|
||||||
|
|
||||||
public bool HasEnoughTalents(CharacterInfo character) => CountMatchingTalents(character.UnlockedTalents) >= MaxChosenTalents;
|
public bool HasEnoughTalents(CharacterInfo character) => CountMatchingTalents(character.UnlockedTalents) >= RequiredTalents;
|
||||||
public bool HasEnoughTalents(IReadOnlyCollection<Identifier> selectedTalents) => CountMatchingTalents(selectedTalents) >= MaxChosenTalents;
|
public bool HasEnoughTalents(IReadOnlyCollection<Identifier> selectedTalents) => CountMatchingTalents(selectedTalents) >= RequiredTalents;
|
||||||
|
public bool HasMaxTalents(IReadOnlyCollection<Identifier> selectedTalents) => CountMatchingTalents(selectedTalents) >= MaxChosenTalents;
|
||||||
|
|
||||||
// No LINQ
|
// No LINQ
|
||||||
public bool HasSelectedTalent(IReadOnlyCollection<Identifier> selectedTalents)
|
public bool HasSelectedTalent(IReadOnlyCollection<Identifier> selectedTalents)
|
||||||
@@ -267,10 +286,15 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public TalentOption(ContentXElement talentOptionsElement, Identifier debugIdentifier)
|
public TalentOption(ContentXElement talentOptionsElement, Identifier debugIdentifier)
|
||||||
{
|
{
|
||||||
MaxChosenTalents = talentOptionsElement.GetAttributeInt("maxchosentalents", 1);
|
MaxChosenTalents = talentOptionsElement.GetAttributeInt(nameof(MaxChosenTalents), 1);
|
||||||
|
RequiredTalents = talentOptionsElement.GetAttributeInt(nameof(RequiredTalents), MaxChosenTalents);
|
||||||
|
|
||||||
|
if (RequiredTalents > MaxChosenTalents)
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError($"Error in talent tree {debugIdentifier} - MaxChosenTalents is larger than RequiredTalents.");
|
||||||
|
}
|
||||||
|
|
||||||
HashSet<Identifier> identifiers = new HashSet<Identifier>();
|
HashSet<Identifier> identifiers = new HashSet<Identifier>();
|
||||||
|
|
||||||
foreach (ContentXElement talentOptionElement in talentOptionsElement.Elements())
|
foreach (ContentXElement talentOptionElement in talentOptionsElement.Elements())
|
||||||
{
|
{
|
||||||
Identifier elementName = talentOptionElement.Name.ToIdentifier();
|
Identifier elementName = talentOptionElement.Name.ToIdentifier();
|
||||||
@@ -293,6 +317,15 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
|
|
||||||
talentIdentifiers = identifiers.ToImmutableHashSet();
|
talentIdentifiers = identifiers.ToImmutableHashSet();
|
||||||
|
|
||||||
|
if (RequiredTalents > talentIdentifiers.Count)
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError($"Error in talent tree {debugIdentifier} - completing a stage of the tree requires more talents than there are in the stage.");
|
||||||
|
}
|
||||||
|
if (MaxChosenTalents > talentIdentifiers.Count)
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError($"Error in talent tree {debugIdentifier} - maximum number of talents to choose is larger than the number of talents.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@ namespace Barotrauma
|
|||||||
[Serialize("", IsPropertySaveable.Yes)]
|
[Serialize("", IsPropertySaveable.Yes)]
|
||||||
public Identifier OrderTargetTag { get; set; }
|
public Identifier OrderTargetTag { get; set; }
|
||||||
|
|
||||||
[Serialize(OrderPriority.Top, IsPropertySaveable.Yes)]
|
[Serialize(OrderPriority.Any, IsPropertySaveable.Yes)]
|
||||||
public OrderPriority Priority { get; set; }
|
public OrderPriority Priority { get; set; }
|
||||||
|
|
||||||
public CheckOrderAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
|
public CheckOrderAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ partial class UIHighlightAction : EventAction
|
|||||||
[Serialize(true, IsPropertySaveable.Yes)]
|
[Serialize(true, IsPropertySaveable.Yes)]
|
||||||
public bool Bounce { get; set; }
|
public bool Bounce { get; set; }
|
||||||
|
|
||||||
|
[Serialize(false, IsPropertySaveable.Yes)]
|
||||||
|
public bool HighlightMultiple { get; set; }
|
||||||
|
|
||||||
private bool isFinished;
|
private bool isFinished;
|
||||||
|
|
||||||
public UIHighlightAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
|
public UIHighlightAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
|
||||||
|
|||||||
@@ -207,24 +207,22 @@ namespace Barotrauma
|
|||||||
level.StartLocation.Connections.ForEach(c => c.Locked = false);
|
level.StartLocation.Connections.ForEach(c => c.Locked = false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
AddChildEvents(initialEventSet);
|
RegisterNonRepeatableChildEvents(initialEventSet);
|
||||||
|
void RegisterNonRepeatableChildEvents(EventSet eventSet)
|
||||||
void AddChildEvents(EventSet eventSet)
|
{
|
||||||
|
if (eventSet == null) { return; }
|
||||||
|
if (eventSet.OncePerLevel)
|
||||||
{
|
{
|
||||||
if (eventSet == null) { return; }
|
foreach (EventPrefab ep in eventSet.EventPrefabs.SelectMany(e => e.EventPrefabs))
|
||||||
if (eventSet.OncePerOutpost)
|
|
||||||
{
|
{
|
||||||
foreach (EventPrefab ep in eventSet.EventPrefabs.SelectMany(e => e.EventPrefabs))
|
nonRepeatableEvents.Add(ep);
|
||||||
{
|
|
||||||
nonRepeatableEvents.Add(ep);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach (EventSet childSet in eventSet.ChildSets)
|
|
||||||
{
|
|
||||||
AddChildEvents(childSet);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
foreach (EventSet childSet in eventSet.ChildSets)
|
||||||
|
{
|
||||||
|
RegisterNonRepeatableChildEvents(childSet);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -374,26 +372,18 @@ namespace Barotrauma
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void RegisterEventHistory()
|
public void RegisterEventHistory()
|
||||||
{
|
{
|
||||||
if (level?.LevelData != null)
|
if (level?.LevelData == null) { return; }
|
||||||
|
|
||||||
|
level.LevelData.EventsExhausted = true;
|
||||||
|
if (level.LevelData.Type == LevelData.LevelType.Outpost)
|
||||||
{
|
{
|
||||||
level.LevelData.EventsExhausted = true;
|
level.LevelData.EventHistory.AddRange(selectedEvents.Values.SelectMany(v => v).Select(e => e.Prefab).Where(e => !level.LevelData.EventHistory.Contains(e)));
|
||||||
if (level.LevelData.Type == LevelData.LevelType.Outpost)
|
if (level.LevelData.EventHistory.Count > MaxEventHistory)
|
||||||
{
|
{
|
||||||
level.LevelData.EventHistory.AddRange(selectedEvents.Values.SelectMany(v => v).Select(e => e.Prefab).Where(e => !level.LevelData.EventHistory.Contains(e)));
|
level.LevelData.EventHistory.RemoveRange(0, level.LevelData.EventHistory.Count - MaxEventHistory);
|
||||||
if (level.LevelData.EventHistory.Count > MaxEventHistory)
|
|
||||||
{
|
|
||||||
level.LevelData.EventHistory.RemoveRange(0, level.LevelData.EventHistory.Count - MaxEventHistory);
|
|
||||||
}
|
|
||||||
level.LevelData.NonRepeatableEvents.AddRange(nonRepeatableEvents.Where(e => !level.LevelData.NonRepeatableEvents.Contains(e)));
|
|
||||||
}
|
|
||||||
foreach (var usedUniqueSet in usedUniqueSets)
|
|
||||||
{
|
|
||||||
if (!level.LevelData.UsedUniqueSets.Contains(usedUniqueSet.Identifier))
|
|
||||||
{
|
|
||||||
level.LevelData.UsedUniqueSets.Add(usedUniqueSet.Identifier);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
level.LevelData.NonRepeatableEvents.AddRange(nonRepeatableEvents.Where(e => !level.LevelData.NonRepeatableEvents.Contains(e)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SkipEventCooldown()
|
public void SkipEventCooldown()
|
||||||
@@ -418,16 +408,11 @@ namespace Barotrauma
|
|||||||
|
|
||||||
DebugConsole.NewMessage($"Loading event set {eventSet.Identifier}", Color.LightBlue, debugOnly: true);
|
DebugConsole.NewMessage($"Loading event set {eventSet.Identifier}", Color.LightBlue, debugOnly: true);
|
||||||
|
|
||||||
if (eventSet.Unique && !usedUniqueSets.Contains(eventSet))
|
|
||||||
{
|
|
||||||
usedUniqueSets.Add(eventSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
int applyCount = 1;
|
int applyCount = 1;
|
||||||
List<Func<Level.InterestingPosition, bool>> spawnPosFilter = new List<Func<Level.InterestingPosition, bool>>();
|
List<Func<Level.InterestingPosition, bool>> spawnPosFilter = new List<Func<Level.InterestingPosition, bool>>();
|
||||||
if (eventSet.PerRuin)
|
if (eventSet.PerRuin)
|
||||||
{
|
{
|
||||||
applyCount = level.Ruins.Count();
|
applyCount = level.Ruins.Count;
|
||||||
foreach (var ruin in level.Ruins)
|
foreach (var ruin in level.Ruins)
|
||||||
{
|
{
|
||||||
spawnPosFilter.Add(pos => pos.Ruin == ruin);
|
spawnPosFilter.Add(pos => pos.Ruin == ruin);
|
||||||
@@ -435,7 +420,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
else if (eventSet.PerCave)
|
else if (eventSet.PerCave)
|
||||||
{
|
{
|
||||||
applyCount = level.Caves.Count();
|
applyCount = level.Caves.Count;
|
||||||
foreach (var cave in level.Caves)
|
foreach (var cave in level.Caves)
|
||||||
{
|
{
|
||||||
spawnPosFilter.Add(pos => pos.Cave == cave);
|
spawnPosFilter.Add(pos => pos.Cave == cave);
|
||||||
@@ -452,8 +437,8 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool isPrefabSuitable(EventPrefab e)
|
bool isPrefabSuitable(EventPrefab e)
|
||||||
=> e.BiomeIdentifier.IsEmpty ||
|
=> (e.BiomeIdentifier.IsEmpty || e.BiomeIdentifier == level.LevelData?.Biome?.Identifier) &&
|
||||||
e.BiomeIdentifier == level.LevelData?.Biome?.Identifier;
|
!level.LevelData.NonRepeatableEvents.Contains(e);
|
||||||
|
|
||||||
foreach (var subEventPrefab in eventSet.EventPrefabs)
|
foreach (var subEventPrefab in eventSet.EventPrefabs)
|
||||||
{
|
{
|
||||||
@@ -610,8 +595,7 @@ namespace Barotrauma
|
|||||||
return
|
return
|
||||||
level.Difficulty >= eventSet.MinLevelDifficulty && level.Difficulty <= eventSet.MaxLevelDifficulty &&
|
level.Difficulty >= eventSet.MinLevelDifficulty && level.Difficulty <= eventSet.MaxLevelDifficulty &&
|
||||||
level.LevelData.Type == eventSet.LevelType &&
|
level.LevelData.Type == eventSet.LevelType &&
|
||||||
(eventSet.BiomeIdentifier.IsEmpty || eventSet.BiomeIdentifier == level.LevelData.Biome.Identifier) &&
|
(eventSet.BiomeIdentifier.IsEmpty || eventSet.BiomeIdentifier == level.LevelData.Biome.Identifier);
|
||||||
(!eventSet.Unique || !level.LevelData.UsedUniqueSets.Contains(eventSet.Identifier));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsValidForLocation(EventSet eventSet, Location location)
|
private bool IsValidForLocation(EventSet eventSet, Location location)
|
||||||
|
|||||||
@@ -114,15 +114,9 @@ namespace Barotrauma
|
|||||||
public readonly bool DisableInHuntingGrounds;
|
public readonly bool DisableInHuntingGrounds;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If true, events from this set shouldn't be selected again as long as they remain in <see cref="LevelData.NonRepeatableEvents"/> which has a limited size.
|
/// If true, events from this set can only occur once in the level.
|
||||||
/// Use <see cref="Unique"/> to prevent selecting the whole set again altogether.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly bool OncePerOutpost;
|
public readonly bool OncePerLevel;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If true, the whole set can only be selected once for a level.
|
|
||||||
/// </summary>
|
|
||||||
public readonly bool Unique;
|
|
||||||
|
|
||||||
public readonly bool DelayWhenCrewAway;
|
public readonly bool DelayWhenCrewAway;
|
||||||
|
|
||||||
@@ -289,8 +283,7 @@ namespace Barotrauma
|
|||||||
DisableInHuntingGrounds = element.GetAttributeBool("disableinhuntinggrounds", false);
|
DisableInHuntingGrounds = element.GetAttributeBool("disableinhuntinggrounds", false);
|
||||||
IgnoreCoolDown = element.GetAttributeBool("ignorecooldown", parentSet?.IgnoreCoolDown ?? (PerRuin || PerCave || PerWreck));
|
IgnoreCoolDown = element.GetAttributeBool("ignorecooldown", parentSet?.IgnoreCoolDown ?? (PerRuin || PerCave || PerWreck));
|
||||||
DelayWhenCrewAway = element.GetAttributeBool("delaywhencrewaway", !PerRuin && !PerCave && !PerWreck);
|
DelayWhenCrewAway = element.GetAttributeBool("delaywhencrewaway", !PerRuin && !PerCave && !PerWreck);
|
||||||
OncePerOutpost = element.GetAttributeBool("onceperoutpost", false);
|
OncePerLevel = element.GetAttributeBool("onceperlevel", element.GetAttributeBool("onceperoutpost", false));
|
||||||
Unique = element.GetAttributeBool("unique", false);
|
|
||||||
TriggerEventCooldown = element.GetAttributeBool("triggereventcooldown", true);
|
TriggerEventCooldown = element.GetAttributeBool("triggereventcooldown", true);
|
||||||
IsCampaignSet = element.GetAttributeBool("campaign", LevelType == LevelData.LevelType.Outpost || (parentSet?.IsCampaignSet ?? false));
|
IsCampaignSet = element.GetAttributeBool("campaign", LevelType == LevelData.LevelType.Outpost || (parentSet?.IsCampaignSet ?? false));
|
||||||
ResetTime = element.GetAttributeFloat("resettime", 0);
|
ResetTime = element.GetAttributeFloat("resettime", 0);
|
||||||
|
|||||||
@@ -386,23 +386,27 @@ namespace Barotrauma
|
|||||||
#if CLIENT
|
#if CLIENT
|
||||||
foreach (Character character in crewCharacters)
|
foreach (Character character in crewCharacters)
|
||||||
{
|
{
|
||||||
var experienceGainMultiplierIndividual = new AbilityMissionExperienceGainMultiplier(this, 1f);
|
GiveMissionExperience(character.Info);
|
||||||
character.CheckTalents(AbilityEffectType.OnGainMissionExperience, experienceGainMultiplierIndividual);
|
|
||||||
character.Info?.GiveExperience(experienceGain, isMissionExperience: true);
|
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
foreach (Barotrauma.Networking.Client c in GameMain.Server.ConnectedClients)
|
foreach (Barotrauma.Networking.Client c in GameMain.Server.ConnectedClients)
|
||||||
{
|
{
|
||||||
//give the experience to the stored characterinfo if the client isn't currently controlling a character
|
//give the experience to the stored characterinfo if the client isn't currently controlling a character
|
||||||
CharacterInfo info = c.Character?.Info ?? c.CharacterInfo;
|
GiveMissionExperience(c.Character?.Info ?? c.CharacterInfo);
|
||||||
|
}
|
||||||
var experienceGainMultiplierIndividual = new AbilityMissionExperienceGainMultiplier(this, 1f);
|
foreach (Character bot in GameSession.GetSessionCrewCharacters(CharacterType.Bot))
|
||||||
info?.Character?.CheckTalents(AbilityEffectType.OnGainMissionExperience, experienceGainMultiplierIndividual);
|
{
|
||||||
|
GiveMissionExperience(bot.Info);
|
||||||
info?.GiveExperience((int)(experienceGain * experienceGainMultiplier.Value), isMissionExperience: true);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void GiveMissionExperience(CharacterInfo info)
|
||||||
|
{
|
||||||
|
var experienceGainMultiplierIndividual = new AbilityMissionExperienceGainMultiplier(this, 1f);
|
||||||
|
info?.Character?.CheckTalents(AbilityEffectType.OnGainMissionExperience, experienceGainMultiplierIndividual);
|
||||||
|
info?.GiveExperience((int)(experienceGain * experienceGainMultiplier.Value), isMissionExperience: true);
|
||||||
|
}
|
||||||
|
|
||||||
// apply money gains afterwards to prevent them from affecting XP gains
|
// apply money gains afterwards to prevent them from affecting XP gains
|
||||||
var missionMoneyGainMultiplier = new AbilityMissionMoneyGainMultiplier(this, 1f);
|
var missionMoneyGainMultiplier = new AbilityMissionMoneyGainMultiplier(this, 1f);
|
||||||
crewCharacters.ForEach(c => c.CheckTalents(AbilityEffectType.OnGainMissionMoney, missionMoneyGainMultiplier));
|
crewCharacters.ForEach(c => c.CheckTalents(AbilityEffectType.OnGainMissionMoney, missionMoneyGainMultiplier));
|
||||||
|
|||||||
@@ -306,14 +306,6 @@ namespace Barotrauma
|
|||||||
return validContainers;
|
return validContainers;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly (int quality, float commonness)[] qualityCommonnesses = new (int quality, float commonness)[Quality.MaxQuality + 1]
|
|
||||||
{
|
|
||||||
(0, 1.0f),
|
|
||||||
(1, 0.0f),
|
|
||||||
(2, 0.0f),
|
|
||||||
(3, 0.0f),
|
|
||||||
};
|
|
||||||
|
|
||||||
private static List<Item> CreateItems(ItemPrefab itemPrefab, List<ItemContainer> containers, KeyValuePair<ItemContainer, PreferredContainer> validContainer)
|
private static List<Item> CreateItems(ItemPrefab itemPrefab, List<ItemContainer> containers, KeyValuePair<ItemContainer, PreferredContainer> validContainer)
|
||||||
{
|
{
|
||||||
List<Item> newItems = new List<Item>();
|
List<Item> newItems = new List<Item>();
|
||||||
@@ -336,11 +328,7 @@ namespace Barotrauma
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
var existingItem = validContainer.Key.Inventory.AllItems.FirstOrDefault(it => it.Prefab == itemPrefab);
|
var existingItem = validContainer.Key.Inventory.AllItems.FirstOrDefault(it => it.Prefab == itemPrefab);
|
||||||
int quality =
|
int quality = existingItem?.Quality ?? Quality.GetSpawnedItemQuality(validContainer.Key.Item.Submarine, Level.Loaded, Rand.RandSync.ServerAndClient);
|
||||||
existingItem?.Quality ??
|
|
||||||
ToolBox.SelectWeightedRandom(
|
|
||||||
qualityCommonnesses.Select(q => q.quality).ToList(),
|
|
||||||
qualityCommonnesses.Select(q => q.commonness).ToList(), Rand.RandSync.ServerAndClient);
|
|
||||||
if (!validContainer.Key.Inventory.CanBePut(itemPrefab, quality: quality)) { break; }
|
if (!validContainer.Key.Inventory.CanBePut(itemPrefab, quality: quality)) { break; }
|
||||||
var item = new Item(itemPrefab, validContainer.Key.Item.Position, validContainer.Key.Item.Submarine, callOnItemLoaded: false)
|
var item = new Item(itemPrefab, validContainer.Key.Item.Position, validContainer.Key.Item.Submarine, callOnItemLoaded: false)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -276,7 +276,7 @@ namespace Barotrauma.Items.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//get all walls within range
|
//get all walls within range the arc could potentially hit
|
||||||
List<Entity> entitiesInRange = new List<Entity>(100);
|
List<Entity> entitiesInRange = new List<Entity>(100);
|
||||||
foreach (Structure structure in Structure.WallList)
|
foreach (Structure structure in Structure.WallList)
|
||||||
{
|
{
|
||||||
@@ -284,10 +284,10 @@ namespace Barotrauma.Items.Components
|
|||||||
if (structure.Submarine != null&& !submarinesInRange.Contains(structure.Submarine)) { continue; }
|
if (structure.Submarine != null&& !submarinesInRange.Contains(structure.Submarine)) { continue; }
|
||||||
|
|
||||||
var structureWorldRect = structure.WorldRect;
|
var structureWorldRect = structure.WorldRect;
|
||||||
if (worldPosition.X < structureWorldRect.X - range) continue;
|
if (worldPosition.X < structureWorldRect.X - range) { continue; }
|
||||||
if (worldPosition.X > structureWorldRect.Right + range) continue;
|
if (worldPosition.X > structureWorldRect.Right + range) { continue; }
|
||||||
if (worldPosition.Y > structureWorldRect.Y + range) continue;
|
if (worldPosition.Y > structureWorldRect.Y + range) { continue; }
|
||||||
if (worldPosition.Y < structureWorldRect.Y -structureWorldRect.Height - range) continue;
|
if (worldPosition.Y < structureWorldRect.Y - structureWorldRect.Height - range) { continue; }
|
||||||
|
|
||||||
if (structure.Submarine != null)
|
if (structure.Submarine != null)
|
||||||
{
|
{
|
||||||
@@ -317,6 +317,7 @@ namespace Barotrauma.Items.Components
|
|||||||
nodes.Add(new Node(worldPosition, -1));
|
nodes.Add(new Node(worldPosition, -1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//get all characters within range the arc could potentially hit
|
||||||
float totalRange = RaycastRange + range;
|
float totalRange = RaycastRange + range;
|
||||||
foreach (Character character in Character.CharacterList)
|
foreach (Character character in Character.CharacterList)
|
||||||
{
|
{
|
||||||
@@ -325,11 +326,20 @@ namespace Barotrauma.Items.Components
|
|||||||
if (OutdoorsOnly && character.Submarine != null) { continue; }
|
if (OutdoorsOnly && character.Submarine != null) { continue; }
|
||||||
if (character.Submarine != null && !submarinesInRange.Contains(character.Submarine)) { continue; }
|
if (character.Submarine != null && !submarinesInRange.Contains(character.Submarine)) { continue; }
|
||||||
|
|
||||||
if (Vector2.DistanceSquared(character.WorldPosition, worldPosition) < totalRange * totalRange * RangeMultiplierInWalls ||
|
if (Vector2.DistanceSquared(character.WorldPosition, worldPosition) < totalRange * totalRange * RangeMultiplierInWalls)
|
||||||
(RaycastRange > 0.0f && MathUtils.LineToPointDistanceSquared(worldPosition, item.WorldPosition, character.WorldPosition) < range * range * RangeMultiplierInWalls))
|
|
||||||
{
|
{
|
||||||
entitiesInRange.Add(character);
|
entitiesInRange.Add(character);
|
||||||
charactersInRange.Add((character, nodes[0]));
|
}
|
||||||
|
//if the weapon does a raycast, check distance to the ray too (not just the end of the ray)
|
||||||
|
if (RaycastRange > 0)
|
||||||
|
{
|
||||||
|
float distSqr = MathUtils.LineSegmentToPointDistanceSquared(worldPosition, item.WorldPosition, character.WorldPosition);
|
||||||
|
//if the distance from the initial raycast to the character is small (e.g. goes through the character), we know it must hit
|
||||||
|
if (distSqr < range * range * RangeMultiplierInWalls)
|
||||||
|
{
|
||||||
|
if (!entitiesInRange.Contains(character)) { entitiesInRange.Add(character); }
|
||||||
|
charactersInRange.Add((character, nodes.First()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -378,7 +388,7 @@ namespace Barotrauma.Items.Components
|
|||||||
}
|
}
|
||||||
else if (entitiesInRange[i] is Character character)
|
else if (entitiesInRange[i] is Character character)
|
||||||
{
|
{
|
||||||
dist = Vector2.Distance(character.WorldPosition, currPos);
|
dist = MathUtils.LineSegmentToPointDistanceSquared(currPos, nodes[parentNodeIndex].WorldPosition, character.WorldPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dist < closestDist)
|
if (dist < closestDist)
|
||||||
@@ -494,17 +504,31 @@ namespace Barotrauma.Items.Components
|
|||||||
if (IgnoreUser && character == user) { continue; }
|
if (IgnoreUser && character == user) { continue; }
|
||||||
if (OutdoorsOnly && character.Submarine != null) { continue; }
|
if (OutdoorsOnly && character.Submarine != null) { continue; }
|
||||||
|
|
||||||
|
Vector2 characterMin = new Vector2(character.AnimController.Limbs.Min(l => l.WorldPosition.X), character.AnimController.Limbs.Min(l => l.WorldPosition.Y));
|
||||||
|
Vector2 characterMax = new Vector2(character.AnimController.Limbs.Max(l => l.WorldPosition.X), character.AnimController.Limbs.Max(l => l.WorldPosition.Y));
|
||||||
if (targetStructure.IsHorizontal)
|
if (targetStructure.IsHorizontal)
|
||||||
{
|
{
|
||||||
if (otherEntity.WorldPosition.X < targetStructure.WorldRect.X) { continue; }
|
if (characterMax.X < targetStructure.WorldRect.X) { continue; }
|
||||||
if (otherEntity.WorldPosition.X > targetStructure.WorldRect.Right) { continue; }
|
if (characterMin.X > targetStructure.WorldRect.Right) { continue; }
|
||||||
if (Math.Abs(otherEntity.WorldPosition.Y - targetStructure.WorldPosition.Y) > currentRange) { continue; }
|
if (Math.Abs(characterMin.Y - targetStructure.WorldPosition.Y) > currentRange &&
|
||||||
|
Math.Abs(characterMax.Y - targetStructure.WorldPosition.Y) > currentRange)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (otherEntity.WorldPosition.Y < targetStructure.WorldRect.Y - targetStructure.Rect.Height) { continue; }
|
if (characterMax.Y < targetStructure.WorldRect.Y - targetStructure.Rect.Height) { continue; }
|
||||||
if (otherEntity.WorldPosition.Y > targetStructure.WorldRect.Y) { continue; }
|
if (characterMin.Y > targetStructure.WorldRect.Y) { continue; }
|
||||||
if (Math.Abs(otherEntity.WorldPosition.X - targetStructure.WorldPosition.X) > currentRange) { continue; }
|
if (Math.Abs(characterMin.X - targetStructure.WorldPosition.X) > currentRange &&
|
||||||
|
Math.Abs(characterMax.X - targetStructure.WorldPosition.X) > currentRange)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!charactersInRange.Any(c => c.character == character))
|
||||||
|
{
|
||||||
|
charactersInRange.Add((character, nodes[parentNodeIndex]));
|
||||||
}
|
}
|
||||||
float closestNodeDistSqr = float.MaxValue;
|
float closestNodeDistSqr = float.MaxValue;
|
||||||
int closestNodeIndex = -1;
|
int closestNodeIndex = -1;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
public enum UseEnvironment
|
public enum UseEnvironment
|
||||||
{
|
{
|
||||||
Air, Water, Both
|
Air, Water, Both, None
|
||||||
};
|
};
|
||||||
|
|
||||||
private float useState;
|
private float useState;
|
||||||
@@ -44,6 +44,7 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
if (character == null || character.Removed) { return false; }
|
if (character == null || character.Removed) { return false; }
|
||||||
if (!character.IsKeyDown(InputType.Aim) || character.Stun > 0.0f) { return false; }
|
if (!character.IsKeyDown(InputType.Aim) || character.Stun > 0.0f) { return false; }
|
||||||
|
if (UsableIn == UseEnvironment.None) { return false; }
|
||||||
|
|
||||||
IsActive = true;
|
IsActive = true;
|
||||||
useState = 0.1f;
|
useState = 0.1f;
|
||||||
|
|||||||
@@ -476,6 +476,7 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
foreach (Item item in Inventory.AllItemsMod)
|
foreach (Item item in Inventory.AllItemsMod)
|
||||||
{
|
{
|
||||||
|
item.ApplyStatusEffects(ActionType.OnSuccess, 1.0f, ownerCharacter);
|
||||||
item.ApplyStatusEffects(ActionType.OnUse, 1.0f, ownerCharacter);
|
item.ApplyStatusEffects(ActionType.OnUse, 1.0f, ownerCharacter);
|
||||||
item.GetComponent<GeneticMaterial>()?.Equip(ownerCharacter);
|
item.GetComponent<GeneticMaterial>()?.Equip(ownerCharacter);
|
||||||
autoInjectCooldown = AutoInjectInterval;
|
autoInjectCooldown = AutoInjectInterval;
|
||||||
|
|||||||
@@ -450,6 +450,7 @@ namespace Barotrauma.Items.Components
|
|||||||
public override bool Select(Character activator)
|
public override bool Select(Character activator)
|
||||||
{
|
{
|
||||||
if (activator == null || activator.Removed) { return false; }
|
if (activator == null || activator.Removed) { return false; }
|
||||||
|
if (Item.Condition <= 0.0f && !UpdateWhenInactive) { return false; }
|
||||||
|
|
||||||
if (UsableIn == UseEnvironment.Water && !activator.AnimController.InWater ||
|
if (UsableIn == UseEnvironment.Water && !activator.AnimController.InWater ||
|
||||||
UsableIn == UseEnvironment.Air && activator.AnimController.InWater)
|
UsableIn == UseEnvironment.Air && activator.AnimController.InWater)
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
private ImmutableDictionary<uint, FabricationRecipe> fabricationRecipes; //this is not readonly because tutorials fuck this up!!!!
|
private ImmutableDictionary<uint, FabricationRecipe> fabricationRecipes; //this is not readonly because tutorials fuck this up!!!!
|
||||||
|
|
||||||
|
private const int MaxAmountToFabricate = 99;
|
||||||
|
|
||||||
private FabricationRecipe fabricatedItem;
|
private FabricationRecipe fabricatedItem;
|
||||||
private float timeUntilReady;
|
private float timeUntilReady;
|
||||||
private float requiredTime;
|
private float requiredTime;
|
||||||
@@ -39,6 +41,16 @@ namespace Barotrauma.Items.Components
|
|||||||
[Serialize(1.0f, IsPropertySaveable.Yes)]
|
[Serialize(1.0f, IsPropertySaveable.Yes)]
|
||||||
public float SkillRequirementMultiplier { get; set; }
|
public float SkillRequirementMultiplier { get; set; }
|
||||||
|
|
||||||
|
private int amountToFabricate;
|
||||||
|
[Serialize(1, IsPropertySaveable.Yes)]
|
||||||
|
public int AmountToFabricate
|
||||||
|
{
|
||||||
|
get { return amountToFabricate; }
|
||||||
|
set { amountToFabricate = MathHelper.Clamp(value, 1, MaxAmountToFabricate); }
|
||||||
|
}
|
||||||
|
|
||||||
|
private int amountRemaining;
|
||||||
|
|
||||||
private const float TinkeringSpeedIncrease = 2.5f;
|
private const float TinkeringSpeedIncrease = 2.5f;
|
||||||
|
|
||||||
private enum FabricatorState
|
private enum FabricatorState
|
||||||
@@ -183,16 +195,20 @@ namespace Barotrauma.Items.Components
|
|||||||
if (selectedItem == null) { return; }
|
if (selectedItem == null) { return; }
|
||||||
if (!outputContainer.Inventory.CanBePut(selectedItem.TargetItem, selectedItem.OutCondition * selectedItem.TargetItem.Health)) { return; }
|
if (!outputContainer.Inventory.CanBePut(selectedItem.TargetItem, selectedItem.OutCondition * selectedItem.TargetItem.Health)) { return; }
|
||||||
|
|
||||||
#if CLIENT
|
|
||||||
itemList.Enabled = false;
|
|
||||||
activateButton.Text = TextManager.Get("FabricatorCancel");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
IsActive = true;
|
IsActive = true;
|
||||||
this.user = user;
|
this.user = user;
|
||||||
fabricatedItem = selectedItem;
|
fabricatedItem = selectedItem;
|
||||||
RefreshAvailableIngredients();
|
RefreshAvailableIngredients();
|
||||||
|
|
||||||
|
#if CLIENT
|
||||||
|
itemList.Enabled = false;
|
||||||
|
if (amountInput != null)
|
||||||
|
{
|
||||||
|
amountInput.Enabled = false;
|
||||||
|
}
|
||||||
|
RefreshActivateButtonText();
|
||||||
|
#endif
|
||||||
|
|
||||||
bool isClient = GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient;
|
bool isClient = GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient;
|
||||||
if (!isClient)
|
if (!isClient)
|
||||||
{
|
{
|
||||||
@@ -249,10 +265,11 @@ namespace Barotrauma.Items.Components
|
|||||||
}
|
}
|
||||||
#elif CLIENT
|
#elif CLIENT
|
||||||
itemList.Enabled = true;
|
itemList.Enabled = true;
|
||||||
if (activateButton != null)
|
if (amountInput != null)
|
||||||
{
|
{
|
||||||
activateButton.Text = TextManager.Get(CreateButtonText);
|
amountInput.Enabled = true;
|
||||||
}
|
}
|
||||||
|
RefreshActivateButtonText();
|
||||||
#endif
|
#endif
|
||||||
fabricatedItem = null;
|
fabricatedItem = null;
|
||||||
}
|
}
|
||||||
@@ -518,20 +535,16 @@ namespace Barotrauma.Items.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//disabled "continuous fabrication" for now
|
var prevFabricatedItem = fabricatedItem;
|
||||||
//before we enable it, there should be some UI controls for fabricating a specific number of items
|
|
||||||
|
|
||||||
/*var prevFabricatedItem = fabricatedItem;
|
|
||||||
var prevUser = user;
|
var prevUser = user;
|
||||||
CancelFabricating();
|
CancelFabricating();
|
||||||
if (CanBeFabricated(prevFabricatedItem))
|
|
||||||
|
amountRemaining--;
|
||||||
|
if (amountRemaining > 0 && CanBeFabricated(prevFabricatedItem, availableIngredients, prevUser))
|
||||||
{
|
{
|
||||||
//keep fabricating if we can fabricate more
|
//keep fabricating if we can fabricate more
|
||||||
StartFabricating(prevFabricatedItem, prevUser, addToServerLog: false);
|
StartFabricating(prevFabricatedItem, prevUser, addToServerLog: false);
|
||||||
}*/
|
}
|
||||||
|
|
||||||
|
|
||||||
CancelFabricating();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -594,7 +607,15 @@ namespace Barotrauma.Items.Components
|
|||||||
private bool CanBeFabricated(FabricationRecipe fabricableItem, IReadOnlyDictionary<Identifier, List<Item>> availableIngredients, Character character)
|
private bool CanBeFabricated(FabricationRecipe fabricableItem, IReadOnlyDictionary<Identifier, List<Item>> availableIngredients, Character character)
|
||||||
{
|
{
|
||||||
if (fabricableItem == null) { return false; }
|
if (fabricableItem == null) { return false; }
|
||||||
if (fabricableItem.RequiresRecipe && (character == null || !character.HasRecipeForItem(fabricableItem.TargetItem.Identifier))) { return false; }
|
if (fabricableItem.RequiresRecipe)
|
||||||
|
{
|
||||||
|
if (character == null) { return false; }
|
||||||
|
if (!character.HasRecipeForItem(fabricableItem.TargetItem.Identifier) &&
|
||||||
|
GameSession.GetSessionCrewCharacters(CharacterType.Bot).None(c => c.HasRecipeForItem(fabricableItem.TargetItem.Identifier)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (fabricableItem.RequiredMoney > 0)
|
if (fabricableItem.RequiredMoney > 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -151,7 +151,6 @@ namespace Barotrauma.Items.Components
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
bool changed = currentMode != value;
|
bool changed = currentMode != value;
|
||||||
|
|
||||||
currentMode = value;
|
currentMode = value;
|
||||||
#if CLIENT
|
#if CLIENT
|
||||||
if (changed) { prevPassivePingRadius = float.MaxValue; }
|
if (changed) { prevPassivePingRadius = float.MaxValue; }
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
|
|
||||||
namespace Barotrauma.Items.Components
|
namespace Barotrauma.Items.Components
|
||||||
@@ -9,14 +10,6 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
public const int MaxQuality = 3;
|
public const int MaxQuality = 3;
|
||||||
|
|
||||||
public static readonly float[] QualityCommonnesses = new float[]
|
|
||||||
{
|
|
||||||
0.8f,
|
|
||||||
0.15f,
|
|
||||||
0.045f,
|
|
||||||
0.005f,
|
|
||||||
};
|
|
||||||
|
|
||||||
public enum StatType
|
public enum StatType
|
||||||
{
|
{
|
||||||
Condition,
|
Condition,
|
||||||
@@ -81,5 +74,29 @@ namespace Barotrauma.Items.Components
|
|||||||
if (!statValues.ContainsKey(statType)) { return 0.0f; }
|
if (!statValues.ContainsKey(statType)) { return 0.0f; }
|
||||||
return statValues[statType] * qualityLevel;
|
return statValues[statType] * qualityLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a random quality for an item spawning in some sub, taking into account the type of the submarine and the difficulty of the current level
|
||||||
|
/// (high-quality items become more common as difficulty increases)
|
||||||
|
/// </summary>
|
||||||
|
public static int GetSpawnedItemQuality(Submarine submarine, Level level, Rand.RandSync randSync = Rand.RandSync.ServerAndClient)
|
||||||
|
{
|
||||||
|
if (submarine?.Info == null || level == null || submarine.Info.Type == SubmarineType.Player) { return 0; }
|
||||||
|
|
||||||
|
float difficultyFactor = MathHelper.Clamp(level.Difficulty, 0.0f, 1.0f);
|
||||||
|
return ToolBox.SelectWeightedRandom(Enumerable.Range(0, MaxQuality + 1), q => GetCommonness(q, difficultyFactor), randSync);
|
||||||
|
|
||||||
|
static float GetCommonness(int quality, float difficultyFactor)
|
||||||
|
{
|
||||||
|
return quality switch
|
||||||
|
{
|
||||||
|
0 => 1,
|
||||||
|
1 => MathHelper.Lerp(0.0f, 1f, difficultyFactor),
|
||||||
|
2 => MathHelper.Lerp(0.0f, 1f, Math.Max(difficultyFactor-0.15f, 0f)), //15 difficulty transition to next biome - unlock Excellent loot
|
||||||
|
3 => MathHelper.Lerp(0.0f, 1f, Math.Max(difficultyFactor-0.35f, 0f)), //35 difficulty transition to next biome - unlock Masterwork loot
|
||||||
|
_ => 0.0f,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1617,11 +1617,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public void ApplyStatusEffect(StatusEffect effect, ActionType type, float deltaTime, Character character = null, Limb limb = null, Entity useTarget = null, bool isNetworkEvent = false, bool checkCondition = true, Vector2? worldPosition = null)
|
public void ApplyStatusEffect(StatusEffect effect, ActionType type, float deltaTime, Character character = null, Limb limb = null, Entity useTarget = null, bool isNetworkEvent = false, bool checkCondition = true, Vector2? worldPosition = null)
|
||||||
{
|
{
|
||||||
if (effect.intervalTimer > 0.0f)
|
if (effect.ShouldWaitForInterval(this, deltaTime)) { return; }
|
||||||
{
|
|
||||||
effect.intervalTimer -= deltaTime;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!isNetworkEvent && checkCondition)
|
if (!isNetworkEvent && checkCondition)
|
||||||
{
|
{
|
||||||
if (condition == 0.0f && !effect.AllowWhenBroken && effect.type != ActionType.OnBroken) { return; }
|
if (condition == 0.0f && !effect.AllowWhenBroken && effect.type != ActionType.OnBroken) { return; }
|
||||||
@@ -2758,7 +2754,6 @@ namespace Barotrauma
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool remove = false;
|
bool remove = false;
|
||||||
foreach (ItemComponent ic in components)
|
foreach (ItemComponent ic in components)
|
||||||
{
|
{
|
||||||
@@ -2788,7 +2783,6 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
var abilityItem = new AbilityApplyTreatment(user, character, this);
|
var abilityItem = new AbilityApplyTreatment(user, character, this);
|
||||||
user.CheckTalents(AbilityEffectType.OnApplyTreatment, abilityItem);
|
user.CheckTalents(AbilityEffectType.OnApplyTreatment, abilityItem);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (remove) { Spawner?.AddItemToRemoveQueue(this); }
|
if (remove) { Spawner?.AddItemToRemoveQueue(this); }
|
||||||
|
|||||||
@@ -15,10 +15,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
|
|
||||||
[NetworkSerialize]
|
[NetworkSerialize]
|
||||||
public readonly record struct TalentStatIdentifier(ItemTalentStats Stat, Identifier TalentIdentifier, UInt32 CharacterID) : INetSerializableStruct
|
public readonly record struct TalentStatIdentifier(ItemTalentStats Stat, Identifier TalentIdentifier, UInt32 CharacterID) : INetSerializableStruct;
|
||||||
{
|
|
||||||
public override int GetHashCode() => HashCode.Combine(TalentIdentifier, CharacterID, Stat);
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly Dictionary<TalentStatIdentifier, float> talentStats = new();
|
private readonly Dictionary<TalentStatIdentifier, float> talentStats = new();
|
||||||
|
|
||||||
|
|||||||
@@ -3943,31 +3943,11 @@ namespace Barotrauma
|
|||||||
Submarine outpost = null;
|
Submarine outpost = null;
|
||||||
if (i == 0 && preSelectedStartOutpost == null || i == 1 && preSelectedEndOutpost == null)
|
if (i == 0 && preSelectedStartOutpost == null || i == 1 && preSelectedEndOutpost == null)
|
||||||
{
|
{
|
||||||
if (OutpostGenerationParams.OutpostParams.Any() || LevelData.ForceOutpostGenerationParams != null)
|
if (LevelData.OutpostGenerationParamsExist)
|
||||||
{
|
{
|
||||||
Location location = i == 0 ? StartLocation : EndLocation;
|
Location location = i == 0 ? StartLocation : EndLocation;
|
||||||
|
OutpostGenerationParams outpostGenerationParams = LevelData.ForceOutpostGenerationParams ??
|
||||||
OutpostGenerationParams outpostGenerationParams = null;
|
LevelData.GetSuitableOutpostGenerationParams(location).GetRandom(Rand.RandSync.ServerAndClient);
|
||||||
if (LevelData.ForceOutpostGenerationParams != null)
|
|
||||||
{
|
|
||||||
outpostGenerationParams = LevelData.ForceOutpostGenerationParams;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var suitableParams = OutpostGenerationParams.OutpostParams.Where(p => location == null || p.AllowedLocationTypes.Contains(location.Type.Identifier));
|
|
||||||
if (!suitableParams.Any())
|
|
||||||
{
|
|
||||||
suitableParams = OutpostGenerationParams.OutpostParams.Where(p => location == null || !p.AllowedLocationTypes.Any());
|
|
||||||
if (!suitableParams.Any())
|
|
||||||
{
|
|
||||||
DebugConsole.ThrowError($"No suitable outpost generation parameters found for the location type \"{location.Type.Identifier}\". Selecting random parameters.");
|
|
||||||
suitableParams = OutpostGenerationParams.OutpostParams;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
outpostGenerationParams = suitableParams.GetRandom(Rand.RandSync.ServerAndClient);
|
|
||||||
}
|
|
||||||
|
|
||||||
LocationType locationType = location?.Type;
|
LocationType locationType = location?.Type;
|
||||||
if (locationType == null)
|
if (locationType == null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -57,10 +57,19 @@ namespace Barotrauma
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int? MinMainPathWidth;
|
public int? MinMainPathWidth;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Events that have previously triggered in this level. Used for making events the player hasn't seen yet more likely to trigger when re-entering the level. Has a maximum size of <see cref="EventManager.MaxEventHistory"/>.
|
||||||
|
/// </summary>
|
||||||
public readonly List<EventPrefab> EventHistory = new List<EventPrefab>();
|
public readonly List<EventPrefab> EventHistory = new List<EventPrefab>();
|
||||||
public readonly List<EventPrefab> NonRepeatableEvents = new List<EventPrefab>();
|
|
||||||
public readonly HashSet<Identifier> UsedUniqueSets = new HashSet<Identifier>();
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Events that have already triggered in this level and can never trigger again. <see cref="EventSet.OncePerLevel"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly List<EventPrefab> NonRepeatableEvents = new List<EventPrefab>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 'Exhaustible' sets won't appear in the same level until after one world step (~10 min, see Map.ProgressWorld) has passed. <see cref="EventSet.Exhaustible"/>.
|
||||||
|
/// </summary>
|
||||||
public bool EventsExhausted { get; set; }
|
public bool EventsExhausted { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -144,8 +153,6 @@ namespace Barotrauma
|
|||||||
string[] nonRepeatablePrefabNames = element.GetAttributeStringArray("nonrepeatableevents", new string[] { });
|
string[] nonRepeatablePrefabNames = element.GetAttributeStringArray("nonrepeatableevents", new string[] { });
|
||||||
NonRepeatableEvents.AddRange(EventPrefab.Prefabs.Where(p => nonRepeatablePrefabNames.Any(n => p.Identifier == n)));
|
NonRepeatableEvents.AddRange(EventPrefab.Prefabs.Where(p => nonRepeatablePrefabNames.Any(n => p.Identifier == n)));
|
||||||
|
|
||||||
UsedUniqueSets = element.GetAttributeIdentifierArray(nameof(UsedUniqueSets), Array.Empty<Identifier>()).ToHashSet();
|
|
||||||
|
|
||||||
EventsExhausted = element.GetAttributeBool(nameof(EventsExhausted).ToLower(), false);
|
EventsExhausted = element.GetAttributeBool(nameof(EventsExhausted).ToLower(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,6 +252,23 @@ namespace Barotrauma
|
|||||||
return levelData;
|
return levelData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool OutpostGenerationParamsExist => ForceOutpostGenerationParams != null || OutpostGenerationParams.OutpostParams.Any();
|
||||||
|
|
||||||
|
public static IEnumerable<OutpostGenerationParams> GetSuitableOutpostGenerationParams(Location location)
|
||||||
|
{
|
||||||
|
var suitableParams = OutpostGenerationParams.OutpostParams.Where(p => location == null || p.AllowedLocationTypes.Contains(location.Type.Identifier));
|
||||||
|
if (!suitableParams.Any())
|
||||||
|
{
|
||||||
|
suitableParams = OutpostGenerationParams.OutpostParams.Where(p => location == null || !p.AllowedLocationTypes.Any());
|
||||||
|
if (!suitableParams.Any())
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError($"No suitable outpost generation parameters found for the location type \"{location.Type.Identifier}\". Selecting random parameters.");
|
||||||
|
suitableParams = OutpostGenerationParams.OutpostParams;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return suitableParams;
|
||||||
|
}
|
||||||
|
|
||||||
public void Save(XElement parentElement)
|
public void Save(XElement parentElement)
|
||||||
{
|
{
|
||||||
var newElement = new XElement("Level",
|
var newElement = new XElement("Level",
|
||||||
@@ -287,11 +311,6 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UsedUniqueSets.Any())
|
|
||||||
{
|
|
||||||
newElement.Add(new XAttribute(nameof(UsedUniqueSets), string.Join(',', UsedUniqueSets)));
|
|
||||||
}
|
|
||||||
|
|
||||||
parentElement.Add(newElement);
|
parentElement.Add(newElement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1291,19 +1291,32 @@ namespace Barotrauma
|
|||||||
return characters.Sum(c => (int)c.GetStatValue(StatTypes.ExtraSpecialSalesCount));
|
return characters.Sum(c => (int)c.GetStatValue(StatTypes.ExtraSpecialSalesCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
public int HighestSubmarineTierAvailable(SubmarineClass submarineClass)
|
public bool CanHaveSubsForSale()
|
||||||
{
|
{
|
||||||
if (!HasOutpost()) { return 0; }
|
return HasOutpost() && CanHaveCampaignInteraction(CampaignMode.InteractionType.PurchaseSub);
|
||||||
return Biome?.HighestSubmarineTierAvailable(submarineClass, Type.Identifier) ?? SubmarineInfo.HighestTier;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int HighestSubmarineTierAvailable() => HighestSubmarineTierAvailable(SubmarineClass.Undefined);
|
public int HighestSubmarineTierAvailable(SubmarineClass submarineClass = SubmarineClass.Undefined)
|
||||||
|
{
|
||||||
|
if (CanHaveSubsForSale())
|
||||||
|
{
|
||||||
|
return Biome?.HighestSubmarineTierAvailable(submarineClass, Type.Identifier) ?? SubmarineInfo.HighestTier;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsSubmarineAvailable(SubmarineInfo info)
|
public bool IsSubmarineAvailable(SubmarineInfo info)
|
||||||
{
|
{
|
||||||
return Biome?.IsSubmarineAvailable(info, Type.Identifier) ?? true;
|
return Biome?.IsSubmarineAvailable(info, Type.Identifier) ?? true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool CanHaveCampaignInteraction(CampaignMode.InteractionType interactionType)
|
||||||
|
{
|
||||||
|
return LevelData != null &&
|
||||||
|
LevelData.OutpostGenerationParamsExist &&
|
||||||
|
LevelData.GetSuitableOutpostGenerationParams(this).Any(p => p.CanHaveCampaignInteraction(interactionType));
|
||||||
|
}
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
if (Type != OriginalType)
|
if (Type != OriginalType)
|
||||||
|
|||||||
@@ -85,9 +85,9 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
|
|
||||||
public float StoreMaxReputationModifier { get; } = 0.1f;
|
public float StoreMaxReputationModifier { get; } = 0.1f;
|
||||||
public float StoreSellPriceModifier { get; } = 0.8f;
|
public float StoreSellPriceModifier { get; } = 0.3f;
|
||||||
public float DailySpecialPriceModifier { get; } = 0.5f;
|
public float DailySpecialPriceModifier { get; } = 0.5f;
|
||||||
public float RequestGoodPriceModifier { get; } = 1.5f;
|
public float RequestGoodPriceModifier { get; } = 2f;
|
||||||
public int StoreInitialBalance { get; } = 5000;
|
public int StoreInitialBalance { get; } = 5000;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// In percentages
|
/// In percentages
|
||||||
|
|||||||
@@ -260,6 +260,21 @@ namespace Barotrauma
|
|||||||
return humanPrefabCollections.GetRandom(randSync);
|
return humanPrefabCollections.GetRandom(randSync);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool CanHaveCampaignInteraction(CampaignMode.InteractionType interactionType)
|
||||||
|
{
|
||||||
|
foreach (var collection in humanPrefabCollections)
|
||||||
|
{
|
||||||
|
foreach (var prefab in collection)
|
||||||
|
{
|
||||||
|
if (prefab.CampaignInteractionType == interactionType)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public ImmutableHashSet<Identifier> GetStoreIdentifiers()
|
public ImmutableHashSet<Identifier> GetStoreIdentifiers()
|
||||||
{
|
{
|
||||||
if (StoreIdentifiers == null)
|
if (StoreIdentifiers == null)
|
||||||
|
|||||||
@@ -164,7 +164,11 @@ namespace Barotrauma.Networking
|
|||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float GetGarbleAmount(Entity listener, Entity sender, float range, float obstructionmult = 2.0f)
|
/// <summary>
|
||||||
|
/// How much messages sent by <paramref name="sender"/> should get garbled. Takes the distance between the entities and optionally the obstructions between them into account (see <paramref name="obstructionMultiplier"/>).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obstructionMultiplier">Values greater than or equal to 1 cause the message to get garbled more heavily when there's some obstruction between the characters. Values smaller than 1 mean the garbling only depends on distance.</param>
|
||||||
|
public static float GetGarbleAmount(Entity listener, Entity sender, float range, float obstructionMultiplier = 2.0f)
|
||||||
{
|
{
|
||||||
if (listener == null || sender == null)
|
if (listener == null || sender == null)
|
||||||
{
|
{
|
||||||
@@ -177,12 +181,12 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
Hull listenerHull = listener == null ? null : Hull.FindHull(listener.WorldPosition);
|
Hull listenerHull = listener == null ? null : Hull.FindHull(listener.WorldPosition);
|
||||||
Hull sourceHull = sender == null ? null : Hull.FindHull(sender.WorldPosition);
|
Hull sourceHull = sender == null ? null : Hull.FindHull(sender.WorldPosition);
|
||||||
if (sourceHull != listenerHull)
|
if (sourceHull != listenerHull && obstructionMultiplier >= 1.0f)
|
||||||
{
|
{
|
||||||
if ((sourceHull == null || !sourceHull.GetConnectedHulls(includingThis: false, searchDepth: 2, ignoreClosedGaps: true).Contains(listenerHull)) &&
|
if ((sourceHull == null || !sourceHull.GetConnectedHulls(includingThis: false, searchDepth: 2, ignoreClosedGaps: true).Contains(listenerHull)) &&
|
||||||
Submarine.CheckVisibility(listener.SimPosition, sender.SimPosition) != null)
|
Submarine.CheckVisibility(listener.SimPosition, sender.SimPosition) != null)
|
||||||
{
|
{
|
||||||
dist = (dist + 100f) * obstructionmult;
|
dist = (dist + 100f) * obstructionMultiplier;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (dist > range) { return 1.0f; }
|
if (dist > range) { return 1.0f; }
|
||||||
@@ -197,9 +201,9 @@ namespace Barotrauma.Networking
|
|||||||
return ApplyDistanceEffect(listener, Sender, Text, SpeakRange);
|
return ApplyDistanceEffect(listener, Sender, Text, SpeakRange);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string ApplyDistanceEffect(Entity listener, Entity sender, string text, float range, float obstructionmult = 2.0f)
|
public static string ApplyDistanceEffect(Entity listener, Entity sender, string text, float range, float obstructionMultiplier = 2.0f)
|
||||||
{
|
{
|
||||||
return ApplyDistanceEffect(text, GetGarbleAmount(listener, sender, range, obstructionmult));
|
return ApplyDistanceEffect(text, GetGarbleAmount(listener, sender, range, obstructionMultiplier));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string ApplyDistanceEffect(string text, float garbleAmount)
|
public static string ApplyDistanceEffect(string text, float garbleAmount)
|
||||||
@@ -252,7 +256,7 @@ namespace Barotrauma.Networking
|
|||||||
var senderRadio = senderItem.GetComponent<WifiComponent>();
|
var senderRadio = senderItem.GetComponent<WifiComponent>();
|
||||||
if (!receiverRadio.CanReceive(senderRadio)) { continue; }
|
if (!receiverRadio.CanReceive(senderRadio)) { continue; }
|
||||||
|
|
||||||
string msg = ApplyDistanceEffect(receiverItem, senderItem, message, senderRadio.Range);
|
string msg = ApplyDistanceEffect(receiverItem, senderItem, message, senderRadio.Range, obstructionMultiplier: 0);
|
||||||
if (sender.SpeechImpediment > 0.0f)
|
if (sender.SpeechImpediment > 0.0f)
|
||||||
{
|
{
|
||||||
//speech impediment doesn't reduce the range when using a radio, but adds extra garbling
|
//speech impediment doesn't reduce the range when using a radio, but adds extra garbling
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user