Build 0.21.6.0

This commit is contained in:
Markus Isberg
2023-01-31 18:01:29 +02:00
parent 697ec52120
commit 25fa5a9552
145 changed files with 2317 additions and 1145 deletions
@@ -154,9 +154,12 @@ namespace Barotrauma
}
if (LosFadeIn && clampedTimer / PanDuration > 0.8f)
{
GameMain.LightManager.LosAlpha = ((clampedTimer / PanDuration) - 0.8f) * 5.0f;
if (!GameMain.DevMode)
{
GameMain.LightManager.LosEnabled = true;
GameMain.LightManager.LosAlpha = ((clampedTimer / PanDuration) - 0.8f) * 5.0f;
}
Lights.LightManager.ViewTarget = prevControlled ?? (targetEntity as Entity);
GameMain.LightManager.LosEnabled = true;
}
#endif
timer += CoroutineManager.DeltaTime;
@@ -170,8 +173,11 @@ namespace Barotrauma
#if CLIENT
GUI.ScreenOverlayColor = Color.TransparentBlack;
GameMain.LightManager.LosEnabled = true;
GameMain.LightManager.LosAlpha = 1f;
if (!GameMain.DevMode)
{
GameMain.LightManager.LosEnabled = true;
GameMain.LightManager.LosAlpha = 1f;
}
#endif
if (prevControlled != null && !prevControlled.Removed)
@@ -724,6 +724,8 @@ namespace Barotrauma
}
bossHealthBar.TopHealthBar.BarSize = bossHealthBar.SideHealthBar.BarSize = health;
Color color = bossHealthBar.Character.CharacterHealth.GetAfflictionStrength("poison") > 0 || bossHealthBar.Character.CharacterHealth.GetAfflictionStrength("paralysis") > 0 ? GUIStyle.HealthBarColorPoisoned : GUIStyle.Red;
bossHealthBar.TopHealthBar.Color = bossHealthBar.SideHealthBar.Color = color;
if (bossHealthBar.Character.Removed || !bossHealthBar.Character.Enabled)
{
@@ -2071,6 +2071,8 @@ namespace Barotrauma
foreach (var periodicEffect in newPeriodicEffects)
{
if (!existingAffliction.Prefab.PeriodicEffects.Contains(periodicEffect.effect)) { continue; }
if (existingAffliction.Strength < periodicEffect.effect.MinStrength) { continue; }
if (periodicEffect.effect.MaxStrength > 0 && existingAffliction.Strength > periodicEffect.effect.MaxStrength) { continue; }
//timer has wrapped around, apply the effect
if (periodicEffect.timer - existingAffliction.PeriodicEffectTimers[periodicEffect.effect] > periodicEffect.effect.MinInterval / 2)
{
@@ -0,0 +1,22 @@
#nullable enable
using System;
namespace Barotrauma
{
internal static class HealingCooldown
{
public static float NormalizedCooldown => MathF.Min((float) (DateTimeOffset.UtcNow - OnCooldownUntil).TotalSeconds / CooldownDuration, 0f);
public static bool IsOnCooldown => DateTimeOffset.UtcNow < OnCooldownUntil;
private static DateTimeOffset OnCooldownUntil = DateTimeOffset.MinValue;
private const float CooldownDuration = 0.5f;
public static readonly Identifier MedicalItemTag = new Identifier("medical");
public static void PutOnCooldown()
{
OnCooldownUntil = DateTimeOffset.UtcNow.AddSeconds(CooldownDuration);
}
}
}
@@ -8,6 +8,7 @@ using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using Barotrauma.IO;
using Barotrauma.Utils;
using System.Linq;
using System.Xml.Linq;
using SpriteParams = Barotrauma.RagdollParams.SpriteParams;
@@ -260,9 +261,7 @@ namespace Barotrauma
{
if (enableHuskSprite)
{
List<WearableSprite> otherWearablesWithHusk = new List<WearableSprite>() { HuskSprite };
otherWearablesWithHusk.AddRange(OtherWearables);
OtherWearables = otherWearablesWithHusk;
OtherWearables.Insert(0, HuskSprite);
UpdateWearableTypesToHide();
}
else
@@ -546,7 +545,7 @@ namespace Barotrauma
{
foreach (var affliction in result.Afflictions)
{
if (affliction is AfflictionBleeding)
if (affliction is AfflictionBleeding bleeding && bleeding.Prefab.DamageParticles)
{
bleedingDamage += affliction.GetVitalityDecrease(null);
}
@@ -555,7 +554,7 @@ namespace Barotrauma
float damage = 0;
foreach (var affliction in result.Afflictions)
{
if (affliction.Prefab.AfflictionType == "damage")
if (affliction.Prefab.DamageParticles && affliction.Prefab.AfflictionType == "damage")
{
damage += affliction.GetVitalityDecrease(null);
}
@@ -732,7 +731,7 @@ namespace Barotrauma
bool hideLimb = Hide ||
OtherWearables.Any(w => w.HideLimb) ||
wearingItems.Any(w => w != null && w.HideLimb);
WearingItems.Any(w => w.HideLimb);
bool drawHuskSprite = HuskSprite != null && !wearableTypesToHide.Contains(WearableType.Husk);
@@ -828,7 +827,7 @@ namespace Barotrauma
LightSource.LightSpriteEffect = (dir == Direction.Right) ? SpriteEffects.None : SpriteEffects.FlipVertically;
}
float step = depthStep;
WearableSprite onlyDrawable = wearingItems.Find(w => w.HideOtherWearables);
WearableSprite onlyDrawable = WearingItems.Find(w => w.HideOtherWearables);
if (Params.MirrorHorizontally)
{
spriteEffect = spriteEffect == SpriteEffects.None ? SpriteEffects.FlipHorizontally : SpriteEffects.None;
@@ -965,31 +964,28 @@ namespace Barotrauma
public void UpdateWearableTypesToHide()
{
alphaClipEffectParams?.Clear();
wearableTypeHidingSprites.Clear();
if (WearingItems != null && WearingItems.Count > 0)
void addWearablesFrom(IReadOnlyList<WearableSprite> wearableSprites)
{
if (wearableSprites.Count <= 0) { return; }
wearableTypeHidingSprites.AddRange(
WearingItems.FindAll(w => w.HideWearablesOfType != null && w.HideWearablesOfType.Count > 0));
}
if (OtherWearables != null && OtherWearables.Count > 0)
{
wearableTypeHidingSprites.AddRange(
OtherWearables.FindAll(w => w.HideWearablesOfType != null && w.HideWearablesOfType.Count > 0));
wearableSprites.Where(w => w.HideWearablesOfType.Count > 0));
}
addWearablesFrom(WearingItems);
addWearablesFrom(OtherWearables);
wearableTypesToHide.Clear();
if (wearableTypeHidingSprites.Count > 0)
if (wearableTypeHidingSprites.Count <= 0) { return; }
foreach (WearableSprite sprite in wearableTypeHidingSprites)
{
foreach (WearableSprite sprite in wearableTypeHidingSprites)
{
foreach (WearableType type in sprite.HideWearablesOfType)
{
if (!wearableTypesToHide.Contains(type))
{
wearableTypesToHide.Add(type);
}
}
}
wearableTypesToHide.UnionWith(sprite.HideWearablesOfType);
}
}
@@ -1071,7 +1067,13 @@ namespace Barotrauma
}
}
private void DrawWearable(WearableSprite wearable, float depthStep, SpriteBatch spriteBatch, Color color, float alpha, SpriteEffects spriteEffect)
private (
Color FinalColor,
Vector2 Origin,
float Rotation,
float Scale,
float Depth)
CalculateDrawParameters(WearableSprite wearable, float depthStep, Color color, float alpha)
{
var sprite = ActiveSprite;
if (wearable.InheritSourceRect)
@@ -1163,27 +1165,118 @@ namespace Barotrauma
float finalAlpha = alpha * wearableColor.A;
Color finalColor = color.Multiply(wearableColor);
finalColor = new Color(finalColor.R, finalColor.G, finalColor.B, (byte)finalAlpha);
wearable.Sprite.Draw(spriteBatch, new Vector2(body.DrawPosition.X, -body.DrawPosition.Y), finalColor, origin, rotation, scale, spriteEffect, depth);
return (finalColor, origin, rotation, scale, depth);
}
private WearableSprite GetWearableSprite(WearableType type)//, bool random = false)
private static Effect alphaClipEffect;
private Dictionary<WearableSprite, Dictionary<string, object>> alphaClipEffectParams;
private void ApplyAlphaClip(SpriteBatch spriteBatch, WearableSprite wearable, WearableSprite alphaClipper, SpriteEffects spriteEffect)
{
SpriteRecorder.Command makeCommand(WearableSprite w)
{
var (_, origin, rotation, scale, _)
= CalculateDrawParameters(w, 0f, Color.White, 0f);
var command = SpriteRecorder.Command.FromTransform(
texture: w.Sprite.Texture,
pos: new Vector2(body.DrawPosition.X, -body.DrawPosition.Y),
srcRect: w.Sprite.SourceRect,
color: Color.White,
rotation: rotation,
origin: origin,
scale: new Vector2(scale, scale),
effects: spriteEffect,
depth: 0f,
index: 0);
return command;
}
void spacesFromCommand(WearableSprite w, SpriteRecorder.Command command, out CoordinateSpace2D textureSpace, out CoordinateSpace2D worldSpace)
{
var (topLeft, bottomLeft, topRight) = spriteEffect switch
{
SpriteEffects.None
=> (command.VertexTL, command.VertexBL, command.VertexTR),
SpriteEffects.FlipHorizontally | SpriteEffects.FlipVertically
=> (command.VertexBR, command.VertexTR, command.VertexBL),
SpriteEffects.FlipHorizontally
=> (command.VertexTR, command.VertexBR, command.VertexTL),
SpriteEffects.FlipVertically
=> (command.VertexBL, command.VertexTL, command.VertexBR)
};
textureSpace = new CoordinateSpace2D
{
Origin = topLeft.TextureCoordinate,
I = topRight.TextureCoordinate - topLeft.TextureCoordinate,
J = bottomLeft.TextureCoordinate - topLeft.TextureCoordinate
};
worldSpace = new CoordinateSpace2D
{
Origin = topLeft.Position.DiscardZ(),
I = topRight.Position.DiscardZ() - topLeft.Position.DiscardZ(),
J = bottomLeft.Position.DiscardZ() - topLeft.Position.DiscardZ()
};
}
var wearableCommand = makeCommand(wearable);
var clipperCommand = makeCommand(alphaClipper);
spacesFromCommand(wearable, wearableCommand, out var wearableTextureSpace, out var wearableWorldSpace);
spacesFromCommand(alphaClipper, clipperCommand, out var clipperTextureSpace, out var clipperWorldSpace);
var wearableUvToClipperUv =
wearableTextureSpace.CanonicalToLocal
* wearableWorldSpace.LocalToCanonical
* clipperWorldSpace.CanonicalToLocal
* clipperTextureSpace.LocalToCanonical;
alphaClipEffect ??= EffectLoader.Load("Effects/wearableclip");
alphaClipEffectParams ??= new Dictionary<WearableSprite, Dictionary<string, object>>();
if (!alphaClipEffectParams.ContainsKey(wearable)) { alphaClipEffectParams.Add(wearable, new Dictionary<string, object>()); }
var paramsToPass = new SpriteBatch.EffectWithParams
{
Effect = alphaClipEffect,
Params = alphaClipEffectParams[wearable]
};
paramsToPass.Params["wearableUvToClipperUv"] = wearableUvToClipperUv;
paramsToPass.Params["clipperTexelSize"] = 2f / alphaClipper.Sprite.Texture.Width;
paramsToPass.Params["aCutoff"] = 2f / 255f;
paramsToPass.Params["xTexture"] = wearable.Sprite.Texture;
paramsToPass.Params["xStencil"] = alphaClipper.Sprite.Texture;
spriteBatch.SwapEffect(paramsToPass);
}
private void DrawWearable(WearableSprite wearable, float depthStep, SpriteBatch spriteBatch, Color color, float alpha, SpriteEffects spriteEffect)
{
var (finalColor, origin, rotation, scale, depth)
= CalculateDrawParameters(wearable, depthStep, color, alpha);
var prevEffect = spriteBatch.GetCurrentEffect();
var alphaClipper = WearingItems.Find(w => w.AlphaClipOtherWearables);
bool shouldApplyAlphaClip = alphaClipper != null && wearable != alphaClipper;
if (shouldApplyAlphaClip)
{
ApplyAlphaClip(spriteBatch, wearable, alphaClipper, spriteEffect);
}
wearable.Sprite.Draw(spriteBatch, new Vector2(body.DrawPosition.X, -body.DrawPosition.Y), finalColor, origin, rotation, scale, spriteEffect, depth);
if (shouldApplyAlphaClip)
{
spriteBatch.SwapEffect(effect: prevEffect);
}
}
private WearableSprite GetWearableSprite(WearableType type)
{
var info = character.Info;
if (info == null) { return null; }
ContentXElement element;
/*if (random)
{
element = info.FilterElements(info.Wearables, info.Head.Preset.TagSet)?.GetRandom(Rand.RandSync.ClientOnly);
}
else
{*/
element = info.FilterElements(info.Wearables, info.Head.Preset.TagSet, type)?.FirstOrDefault();
//}
if (element != null)
{
return new WearableSprite(element.GetChildElement("sprite"), type);
}
return null;
ContentXElement element = info.FilterElements(info.Wearables, info.Head.Preset.TagSet, type)?.FirstOrDefault();
return element != null ? new WearableSprite(element.GetChildElement("sprite"), type) : null;
}
partial void RemoveProjSpecific()
@@ -1206,8 +1299,8 @@ namespace Barotrauma
LightSource?.Remove();
LightSource = null;
OtherWearables?.ForEach(w => w.Sprite.Remove());
OtherWearables = null;
OtherWearables.ForEach(w => w.Sprite.Remove());
OtherWearables.Clear();
HuskSprite?.Sprite.Remove();
HuskSprite = null;
@@ -92,7 +92,7 @@ namespace Barotrauma
public Option<ContentPackageId> UgcId = Option<ContentPackageId>.None();
public Option<DateTime> InstallTime = Option<DateTime>.None();
public Option<SerializableDateTime> InstallTime = Option<SerializableDateTime>.None();
public bool HasFile(File file)
=> Files.Any(f =>
@@ -120,7 +120,7 @@ namespace Barotrauma
public void DiscardHashAndInstallTime()
{
ExpectedHash = null;
InstallTime = Option<DateTime>.None();
InstallTime = Option<SerializableDateTime>.None();
}
public static string IncrementModVersion(string modVersion)
@@ -159,8 +159,8 @@ namespace Barotrauma
addRootAttribute("gameversion", GameMain.Version);
if (AltNames.Any()) { addRootAttribute("altnames", string.Join(",", AltNames)); }
if (ExpectedHash != null) { addRootAttribute("expectedhash", ExpectedHash.StringRepresentation); }
if (InstallTime.TryUnwrap(out var installTime)) { addRootAttribute("installtime", ToolBox.Epoch.FromDateTime(installTime)); }
if (InstallTime.TryUnwrap(out var installTime)) { addRootAttribute("installtime", installTime); }
files.ForEach(f => rootElement.Add(f.ToXElement()));
doc.Add(rootElement);
@@ -49,7 +49,7 @@ namespace Barotrauma
&& ugcId is SteamWorkshopId workshopId
&& item.Id == workshopId.Value
&& p.InstallTime.TryUnwrap(out var installTime)
&& item.LatestUpdateTime <= installTime))
&& item.LatestUpdateTime <= installTime.ToUtcValue()))
.ToArray();
if (!needInstalling.Any()) { return Enumerable.Empty<Steamworks.Ugc.Item>(); }
@@ -1128,6 +1128,28 @@ namespace Barotrauma
});
AssignRelayToServer("debugdraw", false);
AssignOnExecute("devmode", (string[] args) =>
{
if (args.None() || !bool.TryParse(args[0], out bool state))
{
state = !GameMain.DevMode;
}
GameMain.DevMode = state;
if (GameMain.DevMode)
{
GameMain.LightManager.LightingEnabled = false;
GameMain.LightManager.LosEnabled = false;
}
else
{
GameMain.LightManager.LightingEnabled = true;
GameMain.LightManager.LosEnabled = true;
GameMain.LightManager.LosAlpha = 1f;
}
NewMessage("Dev mode " + (GameMain.DevMode ? "enabled" : "disabled"), Color.White);
});
AssignRelayToServer("devmode", false);
AssignOnExecute("debugdrawlocalization", (string[] args) =>
{
if (args.None() || !bool.TryParse(args[0], out bool state))
@@ -1229,12 +1251,14 @@ namespace Barotrauma
HumanAIController.debugai = !HumanAIController.debugai;
if (HumanAIController.debugai)
{
GameMain.DevMode = true;
GameMain.DebugDraw = true;
GameMain.LightManager.LightingEnabled = false;
GameMain.LightManager.LosEnabled = false;
}
else
{
GameMain.DevMode = false;
GameMain.DebugDraw = false;
GameMain.LightManager.LightingEnabled = true;
GameMain.LightManager.LosEnabled = true;
@@ -8,7 +8,8 @@ partial class CheckObjectiveAction : BinaryOptionAction
public enum CheckType
{
Added,
Completed
Completed,
Incomplete
}
[Serialize(CheckType.Completed, IsPropertySaveable.Yes)]
@@ -30,8 +31,13 @@ partial class CheckObjectiveAction : BinaryOptionAction
{
CheckType.Added => true,
CheckType.Completed => segment.IsCompleted,
CheckType.Incomplete => !segment.IsCompleted,
_ => false
};
}
else if (Type == CheckType.Incomplete)
{
success = true;
}
}
}
@@ -1,5 +1,6 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma;
@@ -13,11 +14,22 @@ partial class UIHighlightAction : EventAction
bool useCircularFlash = false;
if (Id != ElementId.None)
{
FindAndFlashComponents(c => Equals(Id, c.UserData));
var predicate = (GUIComponent c) => c is not null && Equals(Id, c.UserData);
if (!FindAndFlashAddedComponents(predicate))
{
if (predicate(GUIMessageBox.VisibleBox))
{
Flash(GUIMessageBox.VisibleBox);
}
else
{
FindAndFlashMessageBoxComponents(predicate);
}
}
}
else if (!EntityIdentifier.IsEmpty)
{
FindAndFlashComponents(c =>
FindAndFlashAddedComponents(c =>
c.UserData is MapEntityPrefab mep && mep.Identifier == EntityIdentifier || c.UserData is MapEntity me && me.Prefab.Identifier == EntityIdentifier);
}
else if (!OrderIdentifier.IsEmpty)
@@ -26,26 +38,26 @@ partial class UIHighlightAction : EventAction
bool foundMinimapNode = false;
if (!OrderTargetTag.IsEmpty)
{
foundMinimapNode = FindAndFlashComponents(c =>
foundMinimapNode = FindAndFlashAddedComponents(c =>
c.UserData is CrewManager.MinimapNodeData nodeData && nodeData.Order is Order order &&
order.Identifier == OrderIdentifier && order.Option == OrderOption && order.TargetEntity is Item item && item.HasTag(OrderTargetTag));
}
if (!foundMinimapNode)
{
FindAndFlashComponents(c => c.UserData is Order order && order.Identifier == OrderIdentifier && order.Option == OrderOption,
FindAndFlashAddedComponents(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));
}
}
bool FindAndFlashComponents(params Func<GUIComponent, bool>[] predicates)
bool FindAndFlashComponents(IEnumerable<GUIComponent> components, params Func<GUIComponent, bool>[] predicates)
{
foreach (var predicate in predicates)
{
if (HighlightMultiple)
{
bool found = false;
foreach (var component in GUI.GetAdditions())
foreach (var component in components)
{
if (predicate(component))
{
@@ -55,7 +67,7 @@ partial class UIHighlightAction : EventAction
};
return found;
}
else if (GUI.GetAdditions().FirstOrDefault(predicate) is GUIComponent component)
else if (components.FirstOrDefault(predicate) is GUIComponent component)
{
Flash(component);
return true;
@@ -64,6 +76,10 @@ partial class UIHighlightAction : EventAction
return false;
}
bool FindAndFlashAddedComponents(params Func<GUIComponent, bool>[] predicates) => FindAndFlashComponents(GUI.GetAdditions(), predicates);
bool FindAndFlashMessageBoxComponents(params Func<GUIComponent, bool>[] predicates) => FindAndFlashComponents(GUIMessageBox.VisibleBox?.GetAllChildren() ?? Enumerable.Empty<GUIComponent>(), predicates);
void Flash(GUIComponent component)
{
if (component.FlashTimer <= 0.0f)
@@ -1143,14 +1143,13 @@ namespace Barotrauma
bool wrap = element.GetAttributeBool("wrap", true);
Alignment alignment =
element.GetAttributeEnum("alignment", text.Contains('\n') ? Alignment.Left : Alignment.Center);
GUIFont font;
if (!GUIStyle.Fonts.TryGetValue(element.GetAttributeIdentifier("font", "Font"), out font))
if (!GUIStyle.Fonts.TryGetValue(element.GetAttributeIdentifier("font", "Font"), out GUIFont font))
{
font = GUIStyle.Font;
}
var textBlock = new GUITextBlock(RectTransform.Load(element, parent),
text, color, font, alignment, wrap: wrap, style: style)
RichString.Rich(text), color, font, alignment, wrap: wrap, style: style)
{
TextScale = scale
};
@@ -236,7 +236,8 @@ namespace Barotrauma
new GUIButton(new RectTransform(new Vector2(0.3f, 0.5f), buttonContainer.RectTransform, Anchor.Center),
style: "UIToggleButton")
{
OnClicked = Close
OnClicked = Close,
UserData = UIHighlightAction.ElementId.MessageBoxCloseButton
}
};
InputType? closeInput = null;
@@ -140,6 +140,7 @@ namespace Barotrauma
public readonly static GUIColor HealthBarColorLow = new GUIColor("HealthBarColorLow");
public readonly static GUIColor HealthBarColorMedium = new GUIColor("HealthBarColorMedium");
public readonly static GUIColor HealthBarColorHigh = new GUIColor("HealthBarColorHigh");
public readonly static GUIColor HealthBarColorPoisoned = new GUIColor("HealthBarColorPoisoned");
public static Point ItemFrameMargin
{
@@ -615,7 +615,7 @@ namespace Barotrauma
listBackground.SetCrop(true);
GUIFont font = GUIStyle.Font;
info.CreateSpecsWindow(specsFrame, font);
info.CreateSpecsWindow(specsFrame, font, includeCrushDepth: true);
descriptionTextBlock.Text = info.Description;
descriptionTextBlock.CalculateHeightFromText();
}
@@ -1779,7 +1779,10 @@ namespace Barotrauma
{
CurrentSelectMode = GUIListBox.SelectMode.None
};
sub.Info.CreateSpecsWindow(specsListBox, GUIStyle.Font, includeTitle: false, includeClass: false, includeDescription: true);
sub.Info.CreateSpecsWindow(specsListBox, GUIStyle.Font,
includeTitle: false,
includeClass: false,
includeDescription: true);
}
}
@@ -1171,7 +1171,7 @@ namespace Barotrauma
materialCostList.Visible = false;
materialCostList.UserData = UpgradeStoreUserData.MaterialCostList;
var priceText = new GUITextBlock(rectT(0.2f, 1f, buyButtonLayout), formattedPrice)
var priceText = new GUITextBlock(rectT(0.2f, 1f, buyButtonLayout), formattedPrice, textAlignment: Alignment.Right)
{
UserData = UpgradeStoreUserData.PriceLabel,
//prices on swappable items are always visible, upgrade prices are enabled in UpdateUpgradeEntry for purchasable upgrades
@@ -23,9 +23,13 @@ namespace Barotrauma
{
class GameMain : Game
{
public static bool ShowFPS = false;
public static bool ShowPerf = false;
public static bool ShowFPS;
public static bool ShowPerf;
public static bool DebugDraw;
/// <summary>
/// Doesn't automatically enable los or bot AI or do anything like that. Probably not fully implemented.
/// </summary>
public static bool DevMode;
public static bool IsSingleplayer => NetworkMember == null;
public static bool IsMultiplayer => NetworkMember != null;
@@ -398,7 +402,7 @@ namespace Barotrauma
TextureLoader.Init(GraphicsDevice);
//do this here because we need it for the loading screen
WaterRenderer.Instance = new WaterRenderer(base.GraphicsDevice, Content);
WaterRenderer.Instance = new WaterRenderer(base.GraphicsDevice);
Quad.Init(GraphicsDevice);
@@ -512,10 +516,10 @@ namespace Barotrauma
TitleScreen.LoadState = 75.0f;
yield return CoroutineStatus.Running;
GameScreen = new GameScreen(GraphicsDeviceManager.GraphicsDevice, Content);
GameScreen = new GameScreen(GraphicsDeviceManager.GraphicsDevice);
ParticleManager = new ParticleManager(GameScreen.Cam);
LightManager = new Lights.LightManager(base.GraphicsDevice, Content);
LightManager = new Lights.LightManager(base.GraphicsDevice);
TitleScreen.LoadState = 80.0f;
yield return CoroutineStatus.Running;
@@ -788,7 +788,6 @@ namespace Barotrauma
{
return;
}
if (ws != null)
{
hull = Hull.FindHull(ws.WorldPosition);
@@ -802,7 +801,6 @@ namespace Barotrauma
hull = Hull.FindHull(se.WorldPosition);
}
}
if (IsSinglePlayer)
{
order.OrderGiver?.Speak(order.GetChatMessage("", hull?.DisplayName?.Value, givingOrderToSelf: character == order.OrderGiver, isNewOrder: isNewOrder), ChatMessageType.Order);
@@ -817,13 +815,13 @@ namespace Barotrauma
{
//can't issue an order if no characters are available
if (character == null) { return; }
var orderGiver = order?.OrderGiver;
if (IsSinglePlayer)
{
character.SetOrder(order, isNewOrder, speak: orderGiver != character);
string message = order?.GetChatMessage(character.Name, orderGiver?.CurrentHull?.DisplayName?.Value, givingOrderToSelf: character == orderGiver, orderOption: order?.Option ?? Identifier.Empty, isNewOrder: isNewOrder);
orderGiver?.Speak(message);
bool isGivingOrderToSelf = orderGiver == character;
character.SetOrder(order, isNewOrder, speak: !isGivingOrderToSelf);
string message = order?.GetChatMessage(character.Name, orderGiver?.CurrentHull?.DisplayName?.Value, isGivingOrderToSelf, orderOption: order?.Option ?? Identifier.Empty, isNewOrder: isNewOrder);
orderGiver?.Speak(message);
}
else if (orderGiver != null)
{
@@ -965,7 +965,7 @@ namespace Barotrauma
break;
case QuickUseAction.PutToEquippedItem:
//order by the condition of the contained item to prefer putting into the item with the emptiest ammo/battery/tank
foreach (Item heldItem in character.HeldItems.OrderBy(it => it.GetComponent<ItemContainer>()?.GetContainedIndicatorState() ?? 0.0f))
foreach (Item heldItem in character.HeldItems.OrderByDescending(heldItem => GetContainPriority(item, heldItem)))
{
if (heldItem.OwnInventory == null) { continue; }
//don't allow swapping if we're moving items into an item with 1 slot holding a stack of items
@@ -986,6 +986,22 @@ namespace Barotrauma
}
}
break;
static float GetContainPriority(Item item, Item containerItem)
{
var container = containerItem.GetComponent<ItemContainer>();
if (container == null) { return 0.0f; }
for (int i = 0; i < container.Inventory.Capacity; i++)
{
var containedItems = container.Inventory.GetItemsAt(i);
if (containedItems.Any() && container.Inventory.CanBePutInSlot(item, i))
{
//if there's a stack in the contained item that we can add the item to, prefer that
return 10.0f;
}
}
return -container.GetContainedIndicatorState();
}
}
if (success)
@@ -216,16 +216,19 @@ namespace Barotrauma.Items.Components
if (brokenSprite == null || !IsBroken)
{
spriteBatch.Draw(doorSprite.Texture, pos,
getSourceRect(doorSprite, openState, IsHorizontal),
color, 0.0f, doorSprite.Origin, item.Scale, item.SpriteEffects, doorSprite.Depth);
if (doorSprite?.Texture != null)
{
spriteBatch.Draw(doorSprite.Texture, pos,
getSourceRect(doorSprite, openState, IsHorizontal),
color, 0.0f, doorSprite.Origin, item.Scale, item.SpriteEffects, doorSprite.Depth);
}
}
float maxCondition = item.Repairables.Any() ?
item.Repairables.Min(r => r.RepairThreshold) / 100.0f * item.MaxCondition :
item.MaxCondition;
float healthRatio = item.Health / maxCondition;
if (brokenSprite != null && healthRatio < 1.0f)
if (brokenSprite?.Texture != null && healthRatio < 1.0f)
{
Vector2 scale = scaleBrokenSprite ? new Vector2(1.0f - healthRatio) : Vector2.One;
if (IsHorizontal) { scale.X = 1; } else { scale.Y = 1; }
@@ -285,34 +288,45 @@ namespace Barotrauma.Items.Components
//sent by the server, or reverting it back to its old state if no msg from server was received
PredictedState = open;
resetPredictionTimer = CorrectionDelay;
if (stateChanged) PlaySound(forcedOpen ? ActionType.OnPicked : ActionType.OnUse);
if (stateChanged && !IsBroken)
{
PlayInteractionSound();
}
}
else
{
isOpen = open;
if (!isNetworkMessage || open != PredictedState)
{
StopPicking(null);
ActionType actionType = ActionType.OnUse;
if (forcedOpen)
StopPicking(null);
if (!IsBroken)
{
actionType = ActionType.OnPicked;
PlayInteractionSound();
}
else
{
if (open && HasSoundsOfType[(int)ActionType.OnOpen])
{
actionType = ActionType.OnOpen;
}
else if (!open && HasSoundsOfType[(int)ActionType.OnClose])
{
actionType = ActionType.OnClose;
}
}
PlaySound(actionType);
if (isOpen) { stuck = MathHelper.Clamp(stuck - StuckReductionOnOpen, 0.0f, 100.0f); }
}
}
}
void PlayInteractionSound()
{
ActionType actionType = ActionType.OnUse;
if (forcedOpen)
{
actionType = ActionType.OnPicked;
}
else
{
if (open && HasSoundsOfType[(int)ActionType.OnOpen])
{
actionType = ActionType.OnOpen;
}
else if (!open && HasSoundsOfType[(int)ActionType.OnClose])
{
actionType = ActionType.OnClose;
}
}
PlaySound(actionType);
}
}
public override void ClientEventRead(IReadMessage msg, float sendingTime)
@@ -78,14 +78,21 @@ namespace Barotrauma.Items.Components
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
{
if (Light.LightSprite != null && (item.body == null || item.body.Enabled) && lightBrightness > 0.0f && IsOn && Light.Enabled)
if (Light?.LightSprite == null) { return; }
if ((item.body == null || item.body.Enabled) && lightBrightness > 0.0f && IsOn && Light.Enabled)
{
Vector2 origin = Light.LightSprite.Origin;
if ((Light.LightSpriteEffect & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally) { origin.X = Light.LightSprite.SourceRect.Width - origin.X; }
if ((Light.LightSpriteEffect & SpriteEffects.FlipVertically) == SpriteEffects.FlipVertically) { origin.Y = Light.LightSprite.SourceRect.Height - origin.Y; }
Vector2 drawPos = item.body?.DrawPosition ?? item.DrawPosition;
Light.LightSprite.Draw(spriteBatch, new Vector2(drawPos.X, -drawPos.Y), lightColor * lightBrightness, origin, -Light.Rotation, item.Scale, Light.LightSpriteEffect, itemDepth - 0.0001f);
Color color = lightColor;
if (Light.OverrideLightSpriteAlpha.HasValue)
{
color = new Color(lightColor, Light.OverrideLightSpriteAlpha.Value);
}
Light.LightSprite.Draw(spriteBatch, new Vector2(drawPos.X, -drawPos.Y), color * lightBrightness, origin, -Light.Rotation, item.Scale, Light.LightSpriteEffect, itemDepth - 0.0001f);
}
}
@@ -413,7 +413,7 @@ namespace Barotrauma.Items.Components
var wire = it.GetComponent<Wire>();
if (wire != null && wire.Connections.Any(c => c != null)) { return false; }
if (it.Container?.GetComponent<ItemContainer>() is { DrawInventory: false }) { return false; }
if (it.Container?.GetComponent<ItemContainer>() is { DrawInventory: false } or { AllowAccess: false }) { return false; }
if (it.HasTag("traitormissionitem")) { return false; }
@@ -72,7 +72,9 @@ namespace Barotrauma.Items.Components
public override bool RecreateGUIOnResolutionChange => true;
public bool TriggerInfographic { get; set; }
public bool IsInfographicVisible => infographic != null && infographic.Visible;
partial void InitProjSpecific(ContentXElement element)
{
CreateGUI();
@@ -108,6 +110,9 @@ namespace Barotrauma.Items.Components
{ AbsoluteOffset = GUIStyle.ItemFrameOffset },
isHorizontal: true)
{
CanBeFocused = true,
HoverCursor = CursorState.Default,
AlwaysOverrideCursor = true,
RelativeSpacing = 0.012f,
Stretch = true
};
@@ -675,7 +680,7 @@ namespace Barotrauma.Items.Components
}
}
if (TriggerInfographic)
if (GuiFrame is not null && GuiFrame.Visible && TriggerInfographic)
{
CreateInfrographic();
TriggerInfographic = false;
@@ -851,8 +856,9 @@ namespace Barotrauma.Items.Components
{
AbsoluteOffset = new Point(0, -50).Multiply(GUI.Scale)
};
new GUIButton(closeButtonRt, TextManager.Get("close"))
new GUIButton(closeButtonRt, TextManager.Get("closeinfographic"))
{
UserData = UIHighlightAction.ElementId.CloseButton,
OnClicked = (_, _) =>
{
CloseInfographic(Character.Controlled);
@@ -871,6 +877,7 @@ namespace Barotrauma.Items.Components
string style = arrowStyle == InfographicArrowStyle.Straight ? "InfographicArrow" : "InfographicArrowCurved";
return new GUIImage(rt, style)
{
CanBeFocused = false,
Rotation = MathHelper.ToRadians(rotationDegrees),
SpriteEffects = spriteEffects
};
@@ -329,6 +329,7 @@ namespace Barotrauma.Items.Components
partial void UpdateSignalsProjSpecific()
{
if (signals == null) { return; }
for (int i = 0; i < signals.Length && i < uiElements.Count; i++)
{
if (uiElements[i] is GUITextBox tb)
@@ -9,7 +9,7 @@ namespace Barotrauma.Items.Components
{
private static void GetDamageModifierText(ref LocalizedString description, DamageModifier damageModifier, Identifier afflictionIdentifier)
{
int roundedValue = (int)Math.Round((1 - damageModifier.DamageMultiplier * damageModifier.ProbabilityMultiplier) * 100);
int roundedValue = (int)Math.Round((1 - Math.Min(damageModifier.DamageMultiplier, damageModifier.ProbabilityMultiplier)) * 100);
if (roundedValue == 0) { return; }
string colorStr = XMLExtensions.ToStringHex(GUIStyle.Green);
@@ -18,7 +18,7 @@ namespace Barotrauma.Items.Components
TextManager.Get($"afflictiontype.{afflictionIdentifier}").Fallback(afflictionIdentifier.Value);
if (!description.IsNullOrWhiteSpace()) { description += '\n'; }
description += $" ‖color:{colorStr}‖{roundedValue.ToString("-0;+#")}%‖color:end‖ {afflictionName}";
description += $" ‖color:{colorStr}‖{roundedValue:-0;+#}%‖color:end‖ {afflictionName}";
}
public override void AddTooltipInfo(ref LocalizedString name, ref LocalizedString description)
@@ -36,7 +36,6 @@ namespace Barotrauma.Items.Components
{
continue;
}
foreach (Identifier afflictionIdentifier in damageModifier.ParsedAfflictionIdentifiers)
{
GetDamageModifierText(ref description, damageModifier, afflictionIdentifier);
@@ -1713,6 +1713,15 @@ namespace Barotrauma
GUIStyle.SmallFont.DrawString(spriteBatch, stackCountText, stackCountPos, Color.White);
}
}
if (HealingCooldown.IsOnCooldown && item.HasTag(HealingCooldown.MedicalItemTag))
{
RectangleF cdRect = rect;
// shrink the rect from top to bottom depending on HealingCooldown.NormalizedCooldown
cdRect.Height *= HealingCooldown.NormalizedCooldown;
cdRect.Y += rect.Height;
GUI.DrawFilledRectangle(spriteBatch, cdRect, Color.White * 0.5f);
}
}
if (inventory != null &&
@@ -203,7 +203,7 @@ namespace Barotrauma
}
}
partial void InitProjSpecific()
public void InitSpriteStates()
{
Prefab.Sprite?.EnsureLazyLoaded();
Prefab.InventoryIcon?.EnsureLazyLoaded();
@@ -211,7 +211,6 @@ namespace Barotrauma
{
brokenSprite.Sprite.EnsureLazyLoaded();
}
foreach (var decorativeSprite in Prefab.DecorativeSprites)
{
decorativeSprite.Sprite.EnsureLazyLoaded();
@@ -221,6 +220,11 @@ namespace Barotrauma
UpdateSpriteStates(0.0f);
}
partial void InitProjSpecific()
{
InitSpriteStates();
}
private Rectangle? cachedVisibleExtents;
public void ResetCachedVisibleSize()
@@ -1409,7 +1413,7 @@ namespace Barotrauma
if (targetComponent == null)
{
ApplyStatusEffects(actionType, 1.0f, targetCharacter, targetLimb, useTarget, true, worldPosition: worldPosition);
ApplyStatusEffects(actionType, 1.0f, targetCharacter, targetLimb, useTarget, isNetworkEvent: true, worldPosition: worldPosition);
}
else
{
@@ -236,6 +236,16 @@ namespace Barotrauma
DecorativeSprites = decorativeSprites.ToImmutableArray();
ContainedSprites = containedSprites.ToImmutableArray();
DecorativeSpriteGroups = decorativeSpriteGroups.Select(kvp => (kvp.Key, kvp.Value.ToImmutableArray())).ToImmutableDictionary();
#if CLIENT
foreach (Item item in Item.ItemList)
{
if (item.Prefab == this)
{
item.InitSpriteStates();
}
}
#endif
}
public bool CanCharacterBuy()
@@ -1,5 +1,4 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
@@ -65,15 +64,9 @@ namespace Barotrauma
public Texture2D WaterTexture { get; }
public WaterRenderer(GraphicsDevice graphicsDevice, ContentManager content)
public WaterRenderer(GraphicsDevice graphicsDevice)
{
#if WINDOWS
WaterEffect = content.Load<Effect>("Effects/watershader");
#endif
#if LINUX || OSX
WaterEffect = content.Load<Effect>("Effects/watershader_opengl");
#endif
WaterEffect = EffectLoader.Load("Effects/watershader");
WaterTexture = TextureLoader.FromFile("Content/Effects/waterbump.png");
WaterEffect.Parameters["xWaterBumpMap"].SetValue(WaterTexture);
@@ -1,6 +1,5 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using System.Collections.Generic;
using System.Linq;
using System;
@@ -73,12 +72,14 @@ namespace Barotrauma.Lights
private int recalculationCount;
private float time;
public IEnumerable<LightSource> Lights
{
get { return lights; }
}
public LightManager(GraphicsDevice graphics, ContentManager content)
public LightManager(GraphicsDevice graphics)
{
lights = new List<LightSource>(100);
@@ -96,13 +97,8 @@ namespace Barotrauma.Lights
{
CreateRenderTargets(graphics);
#if WINDOWS
LosEffect = content.Load<Effect>("Effects/losshader");
SolidColorEffect = content.Load<Effect>("Effects/solidcolor");
#else
LosEffect = content.Load<Effect>("Effects/losshader_opengl");
SolidColorEffect = content.Load<Effect>("Effects/solidcolor_opengl");
#endif
LosEffect = EffectLoader.Load("Effects/losshader");
SolidColorEffect = EffectLoader.Load("Effects/solidcolor");
if (lightEffect == null)
{
@@ -171,10 +167,12 @@ namespace Barotrauma.Lights
public void Update(float deltaTime)
{
//wrap around if the timer gets very large, otherwise we'd start running into floating point accuracy issues
time = (time + deltaTime) % 100000.0f;
foreach (LightSource light in activeLights)
{
if (!light.Enabled) { continue; }
light.Update(deltaTime);
light.Update(time);
}
}
@@ -200,8 +200,6 @@ namespace Barotrauma.Lights
private static Texture2D lightTexture;
private float blinkTimer, flickerState, pulseState;
private VertexPositionColorTexture[] vertices;
private short[] indices;
@@ -486,12 +484,12 @@ namespace Barotrauma.Lights
if (addLight) { GameMain.LightManager.AddLight(this); }
}
public void Update(float deltaTime)
public void Update(float time)
{
float brightness = 1.0f;
if (lightSourceParams.BlinkFrequency > 0.0f)
{
blinkTimer = (blinkTimer + deltaTime * lightSourceParams.BlinkFrequency) % 1.0f;
float blinkTimer = (time * lightSourceParams.BlinkFrequency) % 1.0f;
if (blinkTimer > 0.5f)
{
CurrentBrightness = 0.0f;
@@ -500,14 +498,13 @@ namespace Barotrauma.Lights
}
if (lightSourceParams.PulseFrequency > 0.0f && lightSourceParams.PulseAmount > 0.0f)
{
pulseState = (pulseState + deltaTime * lightSourceParams.PulseFrequency) % 1.0f;
float pulseState = (time * lightSourceParams.PulseFrequency) % 1.0f;
//oscillate between 0-1
brightness *= 1.0f - (float)(Math.Sin(pulseState * MathHelper.TwoPi) + 1.0f) / 2.0f * lightSourceParams.PulseAmount;
}
if (lightSourceParams.Flicker > 0.0f)
if (lightSourceParams.Flicker > 0.0f && lightSourceParams.FlickerSpeed > 0.0f)
{
flickerState += deltaTime * lightSourceParams.FlickerSpeed;
flickerState %= 255;
float flickerState = (time * lightSourceParams.FlickerSpeed) % 255;
brightness *= 1.0f - PerlinNoise.GetPerlin(flickerState, flickerState * 0.5f) * lightSourceParams.Flicker;
}
CurrentBrightness = brightness;
@@ -68,7 +68,7 @@ namespace Barotrauma
private (Rectangle targetArea, RichString tip)? tooltip;
private (SubmarineInfo pendingSub, float realWorldCrushDepth) pendingSubInfo;
private SubmarineInfo.PendingSubInfo pendingSubInfo;
private RichString beaconStationActiveText, beaconStationInactiveText;
@@ -936,39 +936,8 @@ namespace Barotrauma
if (connection.LevelData.HasHuntingGrounds) { iconCount++; }
if (connection.Locked) { iconCount++; }
string tooltip = null;
float subCrushDepth = Level.DefaultRealWorldCrushDepth;
var currentOrPendingSub = SubmarineSelection.CurrentOrPendingSubmarine();
if (Submarine.MainSub != null && Submarine.MainSub.Info == currentOrPendingSub)
{
subCrushDepth = Submarine.MainSub.RealWorldCrushDepth;
}
else if (currentOrPendingSub != null)
{
if (pendingSubInfo.pendingSub != currentOrPendingSub)
{
// Store the real world crush depth for the pending sub so that we don't have to calculate it again every time
pendingSubInfo = (currentOrPendingSub, currentOrPendingSub.GetRealWorldCrushDepth());
}
subCrushDepth = pendingSubInfo.realWorldCrushDepth;
}
if (GameMain.GameSession?.Campaign?.UpgradeManager != null)
{
var hullUpgradePrefab = UpgradePrefab.Find("increasewallhealth".ToIdentifier());
if (hullUpgradePrefab != null)
{
int pendingLevel = GameMain.GameSession.Campaign.UpgradeManager.GetUpgradeLevel(hullUpgradePrefab, hullUpgradePrefab.UpgradeCategories.First());
int currentLevel = GameMain.GameSession.Campaign.UpgradeManager.GetRealUpgradeLevel(hullUpgradePrefab, hullUpgradePrefab.UpgradeCategories.First());
if (pendingLevel > currentLevel)
{
string updateValueStr = hullUpgradePrefab.SourceElement?.GetChildElement("Structure")?.GetAttributeString("crushdepth", null);
if (!string.IsNullOrEmpty(updateValueStr))
{
subCrushDepth = PropertyReference.CalculateUpgrade(subCrushDepth, pendingLevel - currentLevel, updateValueStr);
}
}
}
}
float subCrushDepth = SubmarineInfo.GetSubCrushDepth(SubmarineSelection.CurrentOrPendingSubmarine(), ref pendingSubInfo);
string crushDepthWarningIconStyle = null;
if (connection.LevelData.InitialDepth * Physics.DisplayToRealWorldRatio > subCrushDepth)
{
@@ -1125,6 +1094,14 @@ namespace Barotrauma
}
}
/// <summary>
/// Resets <see cref="pendingSubInfo"/> and forces crush depth to be calculated again for icon displaying purposes
/// </summary>
public void ResetPendingSub()
{
pendingSubInfo = new SubmarineInfo.PendingSubInfo();
}
partial void RemoveProjSpecific()
{
noiseOverlay?.Remove();
@@ -89,7 +89,11 @@ namespace Barotrauma
CreateSpecsWindow(descriptionBox, font, includeDescription: true);
}
public void CreateSpecsWindow(GUIListBox parent, GUIFont font, bool includeTitle = true, bool includeClass = true, bool includeDescription = false)
public void CreateSpecsWindow(GUIListBox parent, GUIFont font,
bool includeTitle = true,
bool includeClass = true,
bool includeDescription = false,
bool includeCrushDepth = false)
{
float leftPanelWidth = 0.6f;
float rightPanelWidth = 0.4f / leftPanelWidth;
@@ -155,6 +159,22 @@ namespace Barotrauma
{ CanBeFocused = false };
cargoCapacityText.RectTransform.MinSize = new Point(0, cargoCapacityText.Children.First().Rect.Height);
if (includeCrushDepth)
{
var crushDepthText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), parent.Content.RectTransform),
TextManager.Get("CrushDepth"), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{
CanBeFocused = false
};
new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), crushDepthText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
TextManager.GetWithVariable("meterformat", "[meters]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", GetSubCrushDepth())),
textAlignment: Alignment.TopLeft, font: font, wrap: true)
{
CanBeFocused = false
};
crushDepthText.RectTransform.MinSize = new Point(0, crushDepthText.Children.First().Rect.Height);
}
if (RecommendedCrewSizeMax > 0)
{
var crewSizeText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), parent.Content.RectTransform),
@@ -227,5 +247,57 @@ namespace Barotrauma
GUITextBlock.AutoScaleAndNormalize(parent.Content.GetAllChildren<GUITextBlock>().Where(c => c != submarineNameText && c != descBlock));
parent.ForceLayoutRecalculation();
}
public readonly record struct PendingSubInfo(SubmarineInfo PendingSub = null, bool StructuresDefineRealWorldCrushDepth = false, float RealWorldCrushDepth = Level.DefaultRealWorldCrushDepth);
private float GetSubCrushDepth()
{
var pendingSubInfo = new PendingSubInfo();
return GetSubCrushDepth(this, ref pendingSubInfo);
}
public static float GetSubCrushDepth(SubmarineInfo subInfo, ref PendingSubInfo pendingSubInfo)
{
float subCrushDepth = Level.DefaultRealWorldCrushDepth;
if (Submarine.MainSub != null && Submarine.MainSub.Info == subInfo)
{
subCrushDepth = Submarine.MainSub.RealWorldCrushDepth;
}
else if (subInfo != null)
{
if (pendingSubInfo.PendingSub != subInfo)
{
// Store the real world crush depth for the pending sub so that we don't have to calculate it again every time
pendingSubInfo = new PendingSubInfo(subInfo, subInfo.IsCrushDepthDefinedInStructures(out float realWorldCrushDepth), realWorldCrushDepth);
}
subCrushDepth = pendingSubInfo.RealWorldCrushDepth;
}
if (GameMain.GameSession?.Campaign?.UpgradeManager != null && UpgradePrefab.Find("increasewallhealth".ToIdentifier()) is UpgradePrefab hullUpgradePrefab)
{
int pendingLevel = GameMain.GameSession.Campaign.UpgradeManager.GetUpgradeLevel(hullUpgradePrefab, hullUpgradePrefab.UpgradeCategories.First(), info: subInfo);
// If there is a sub switch pending, unless its structures have crush depth defined in their elements,
// calculate the value based on the default crush depth and pending upgrade level
int currentLevel = 0;
if (pendingSubInfo.PendingSub is null || pendingSubInfo.StructuresDefineRealWorldCrushDepth)
{
currentLevel = GameMain.GameSession.Campaign.UpgradeManager.GetRealUpgradeLevelForSub(hullUpgradePrefab, hullUpgradePrefab.UpgradeCategories.First(), subInfo);
}
if (pendingLevel > currentLevel)
{
string updateValueStr = hullUpgradePrefab.SourceElement?.GetChildElement("Structure")?.GetAttributeString("crushdepth", null);
if (!string.IsNullOrEmpty(updateValueStr))
{
if (currentLevel > 0)
{
// If the current level is greater than 0, reset the crush depth value back to the base value before calculating the upgrade
int upgradePercentage = UpgradePrefab.ParsePercentage(updateValueStr, Identifier.Empty, suppressWarnings: true);
subCrushDepth /= (1f + (upgradePercentage / 100f * currentLevel));
}
subCrushDepth = PropertyReference.CalculateUpgrade(subCrushDepth, pendingLevel, updateValueStr);
}
}
}
return subCrushDepth;
}
}
}
@@ -45,7 +45,7 @@ namespace Barotrauma
}
}
private struct Door
private readonly struct Door
{
public readonly Rectangle Rect;
@@ -153,7 +153,9 @@ namespace Barotrauma
ScrollBarVisible = false,
Spacing = GUI.IntScale(5)
};
subInfo.CreateSpecsWindow(specsContainer, GUIStyle.Font, includeTitle: false, includeDescription: true);
subInfo.CreateSpecsWindow(specsContainer, GUIStyle.Font,
includeTitle: false,
includeDescription: true);
int width = specsContainer.Rect.Width;
void recalculateSpecsContainerHeight()
{
@@ -191,6 +193,7 @@ namespace Barotrauma
TaskPool.Add(nameof(GeneratePreviewMeshes), GeneratePreviewMeshes(), _ =>
{
if (isDisposed) { return; }
// Reset the camera's position on the main thread,
// because the Camera class is not thread-safe and
// it's possible for its state to not get updated
@@ -12,7 +12,7 @@ namespace Barotrauma.Networking
string name,
Either<Address, AccountId> addressOrAccountId,
string reason,
DateTime? expiration)
Option<SerializableDateTime> expiration)
{
this.Name = name;
this.AddressOrAccountId = addressOrAccountId;
@@ -94,8 +94,9 @@ namespace Barotrauma.Networking
topArea.ForceLayoutRecalculation();
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedPlayerFrame.RectTransform),
bannedPlayer.ExpirationTime == null ?
TextManager.Get("BanPermanent") : TextManager.GetWithVariable("BanExpires", "[time]", bannedPlayer.ExpirationTime.Value.ToString()),
bannedPlayer.ExpirationTime.TryUnwrap(out var expirationTime)
? TextManager.GetWithVariable("BanExpires", "[time]", expirationTime.ToLocalUserString())
: TextManager.Get("BanPermanent"),
font: GUIStyle.SmallFont);
LocalizedString reason = TextManager.GetServerMessage(bannedPlayer.Reason).Fallback(bannedPlayer.Reason);
@@ -149,11 +150,11 @@ namespace Barotrauma.Networking
bool includesExpiration = incMsg.ReadBoolean();
incMsg.ReadPadBits();
DateTime? expiration = null;
Option<SerializableDateTime> expiration = Option<SerializableDateTime>.None();
if (includesExpiration)
{
double hoursFromNow = incMsg.ReadDouble();
expiration = DateTime.Now + TimeSpan.FromHours(hoursFromNow);
expiration = Option<SerializableDateTime>.Some(SerializableDateTime.LocalNow + TimeSpan.FromHours(hoursFromNow));
}
string reason = incMsg.ReadString();
@@ -26,8 +26,8 @@ namespace Barotrauma.Networking
if (type != ChatMessageType.Order)
{
changeType = (PlayerConnectionChangeType)msg.ReadByte();
txt = msg.ReadString();
}
txt = msg.ReadString();
string senderName = msg.ReadString();
Character senderCharacter = null;
@@ -87,11 +87,6 @@ namespace Barotrauma.Networking
targetRoom = senderCharacter?.CurrentHull?.DisplayName?.Value;
}
txt = orderPrefab.GetChatMessage(orderMessageInfo.TargetCharacter?.Name, targetRoom,
givingOrderToSelf: orderMessageInfo.TargetCharacter == senderCharacter,
orderOption: orderOption,
isNewOrder: orderMessageInfo.IsNewOrder);
if (GameMain.Client.GameStarted && Screen.Selected == GameMain.GameScreen)
{
Order order = null;
@@ -82,6 +82,11 @@ namespace Barotrauma.Networking
Initialization = ConnectionInitialization.SteamTicketAndVersion
};
if (steamAuthTicket is { Canceled: true })
{
throw new InvalidOperationException("ReadConnectionInitializationStep failed: Steam auth ticket has been cancelled.");
}
ClientSteamTicketAndVersionPacket body = new ClientSteamTicketAndVersionPacket
{
Name = GameMain.Client.Name,
@@ -120,6 +120,7 @@ namespace Barotrauma.Networking
client.Character.ShowSpeechBubble(1.25f, ChatMessage.MessageColor[(int)messageType]);
client.VoipSound.UseRadioFilter = messageType == ChatMessageType.Radio && !GameSettings.CurrentConfig.Audio.DisableVoiceChatFilters;
client.RadioNoise = 0.0f;
if (messageType == ChatMessageType.Radio)
{
client.VoipSound.SetRange(radio.Range * RangeNear * speechImpedimentMultiplier * rangeMultiplier, radio.Range * speechImpedimentMultiplier * rangeMultiplier);
@@ -131,7 +132,6 @@ namespace Barotrauma.Networking
}
else
{
client.VoipSound.SetRange(ChatMessage.SpeakRange * RangeNear * speechImpedimentMultiplier * rangeMultiplier, ChatMessage.SpeakRange * speechImpedimentMultiplier * rangeMultiplier);
}
client.VoipSound.UseMuffleFilter =
@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic;
using Barotrauma.IO;
using System.Collections.Immutable;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;
@@ -58,7 +59,7 @@ namespace Barotrauma
var saveFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.1f), saveList.Content.RectTransform) { MinSize = new Point(0, 45) }, style: "ListBoxElement")
{
UserData = saveInfo.FilePath
UserData = saveInfo
};
var nameText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), saveFrame.RectTransform), Path.GetFileNameWithoutExtension(saveInfo.FilePath),
@@ -87,10 +88,9 @@ namespace Barotrauma
};
string saveTimeStr = string.Empty;
if (saveInfo.SaveTime > 0)
if (saveInfo.SaveTime.TryUnwrap(out var time))
{
DateTime time = ToolBox.Epoch.ToDateTime(saveInfo.SaveTime);
saveTimeStr = time.ToString();
saveTimeStr = time.ToLocalUserString();
}
new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), saveFrame.RectTransform),
text: saveTimeStr, textAlignment: Alignment.Right, font: GUIStyle.SmallFont)
@@ -102,6 +102,26 @@ namespace Barotrauma
return saveFrame;
}
protected void SortSaveList()
{
saveList.Content.RectTransform.SortChildren((c1, c2) =>
{
if (c1.GUIComponent.UserData is not CampaignMode.SaveInfo file1
|| c2.GUIComponent.UserData is not CampaignMode.SaveInfo file2)
{
return 0;
}
if (!file1.SaveTime.TryUnwrap(out var file1WriteTime)
|| !file2.SaveTime.TryUnwrap(out var file2WriteTime))
{
return 0;
}
return file2WriteTime.CompareTo(file1WriteTime);
});
}
public struct CampaignSettingElements
{
public SettingValue<bool> TutorialEnabled;
@@ -367,5 +387,25 @@ namespace Barotrauma
return inputContainer;
}
}
public abstract void UpdateLoadMenu(IEnumerable<CampaignMode.SaveInfo> saveFiles = null);
protected bool DeleteSave(GUIButton button, object obj)
{
if (obj is not CampaignMode.SaveInfo saveInfo) { return false; }
var header = TextManager.Get("deletedialoglabel");
var body = TextManager.GetWithVariable("deletedialogquestion", "[file]", Path.GetFileNameWithoutExtension(saveInfo.FilePath));
EventEditorScreen.AskForConfirmation(header, body, () =>
{
SaveUtil.DeleteSave(saveInfo.FilePath);
prevSaveFiles?.RemoveAll(s => s.FilePath == saveInfo.FilePath);
UpdateLoadMenu(prevSaveFiles.ToList());
return true;
});
return true;
}
}
}
@@ -192,7 +192,7 @@ namespace Barotrauma
yield return CoroutineStatus.Success;
}
public void UpdateLoadMenu(IEnumerable<CampaignMode.SaveInfo> saveFiles = null)
public override void UpdateLoadMenu(IEnumerable<CampaignMode.SaveInfo> saveFiles = null)
{
prevSaveFiles?.Clear();
prevSaveFiles = null;
@@ -220,37 +220,16 @@ namespace Barotrauma
CreateSaveElement(saveInfo);
}
saveList.Content.RectTransform.SortChildren((c1, c2) =>
{
string file1 = c1.GUIComponent.UserData as string;
string file2 = c2.GUIComponent.UserData as string;
DateTime file1WriteTime = DateTime.MinValue;
DateTime file2WriteTime = DateTime.MinValue;
try
{
file1WriteTime = File.GetLastWriteTime(file1);
}
catch
{
//do nothing - DateTime.MinValue will be used and the element will get sorted at the bottom of the list
};
try
{
file2WriteTime = File.GetLastWriteTime(file2);
}
catch
{
//do nothing - DateTime.MinValue will be used and the element will get sorted at the bottom of the list
};
return file2WriteTime.CompareTo(file1WriteTime);
});
SortSaveList();
loadGameButton = new GUIButton(new RectTransform(new Vector2(0.45f, 0.12f), loadGameContainer.RectTransform, Anchor.BottomRight), TextManager.Get("LoadButton"))
{
OnClicked = (btn, obj) =>
{
if (string.IsNullOrWhiteSpace(saveList.SelectedData as string)) { return false; }
LoadGame?.Invoke(saveList.SelectedData as string);
if (saveList.SelectedData is not CampaignMode.SaveInfo saveInfo) { return false; }
if (string.IsNullOrWhiteSpace(saveInfo.FilePath)) { return false; }
LoadGame?.Invoke(saveInfo.FilePath);
CoroutineManager.StartCoroutine(WaitForCampaignSetup(), "WaitForCampaignSetup");
return true;
},
@@ -264,37 +243,20 @@ namespace Barotrauma
};
}
private bool SelectSaveFile(GUIComponent component, object obj)
{
string fileName = (string)obj;
if (obj is not CampaignMode.SaveInfo saveInfo) { return true; }
string fileName = saveInfo.FilePath;
loadGameButton.Enabled = true;
deleteMpSaveButton.Visible = deleteMpSaveButton.Enabled = GameMain.Client.IsServerOwner;
deleteMpSaveButton.Enabled = GameMain.GameSession?.SavePath != fileName;
if (deleteMpSaveButton.Visible)
{
deleteMpSaveButton.UserData = obj as string;
deleteMpSaveButton.UserData = saveInfo;
}
return true;
}
private bool DeleteSave(GUIButton button, object obj)
{
string saveFile = obj as string;
if (obj == null) { return false; }
var header = TextManager.Get("deletedialoglabel");
var body = TextManager.GetWithVariable("deletedialogquestion", "[file]", Path.GetFileNameWithoutExtension(saveFile));
EventEditorScreen.AskForConfirmation(header, body, () =>
{
SaveUtil.DeleteSave(saveFile);
prevSaveFiles?.RemoveAll(s => s.FilePath == saveFile);
UpdateLoadMenu(prevSaveFiles.ToList());
return true;
});
return true;
}
}
}
@@ -581,7 +581,7 @@ namespace Barotrauma
}
}
public void UpdateLoadMenu(IEnumerable<CampaignMode.SaveInfo> saveFiles = null)
public override void UpdateLoadMenu(IEnumerable<CampaignMode.SaveInfo> saveFiles = null)
{
prevSaveFiles?.Clear();
prevSaveFiles = null;
@@ -649,46 +649,27 @@ namespace Barotrauma
}
}
saveList.Content.RectTransform.SortChildren((c1, c2) =>
{
string file1 = c1.GUIComponent.UserData as string;
string file2 = c2.GUIComponent.UserData as string;
DateTime file1WriteTime = DateTime.MinValue;
DateTime file2WriteTime = DateTime.MinValue;
try
{
file1WriteTime = File.GetLastWriteTime(file1);
}
catch
{
//do nothing - DateTime.MinValue will be used and the element will get sorted at the bottom of the list
};
try
{
file2WriteTime = File.GetLastWriteTime(file2);
}
catch
{
//do nothing - DateTime.MinValue will be used and the element will get sorted at the bottom of the list
};
return file2WriteTime.CompareTo(file1WriteTime);
});
SortSaveList();
loadGameButton = new GUIButton(new RectTransform(new Vector2(0.45f, 0.12f), loadGameContainer.RectTransform, Anchor.BottomRight), TextManager.Get("LoadButton"))
{
OnClicked = (btn, obj) =>
{
if (string.IsNullOrWhiteSpace(saveList.SelectedData as string)) { return false; }
LoadGame?.Invoke(saveList.SelectedData as string);
if (saveList.SelectedData is not CampaignMode.SaveInfo saveInfo) { return false; }
if (string.IsNullOrWhiteSpace(saveInfo.FilePath)) { return false; }
LoadGame?.Invoke(saveInfo.FilePath);
return true;
},
Enabled = false
};
}
}
private bool SelectSaveFile(GUIComponent component, object obj)
{
string fileName = (string)obj;
if (obj is not CampaignMode.SaveInfo saveInfo) { return true; }
string fileName = saveInfo.FilePath;
XDocument doc = SaveUtil.LoadGameSessionDoc(fileName);
if (doc?.Root == null)
@@ -701,72 +682,55 @@ namespace Barotrauma
RemoveSaveFrame();
string subName = doc.Root.GetAttributeString("submarine", "");
string saveTime = doc.Root.GetAttributeString("savetime", "unknown");
DateTime? time = null;
if (long.TryParse(saveTime, out long unixTime))
{
time = ToolBox.Epoch.ToDateTime(unixTime);
saveTime = time.ToString();
}
string subName = saveInfo.SubmarineName;
LocalizedString saveTime = saveInfo.SaveTime
.Select(t => (LocalizedString)t.ToLocalUserString())
.Fallback(TextManager.Get("Unknown"));
string mapseed = doc.Root.GetAttributeString("mapseed", "unknown");
var saveFileFrame = new GUIFrame(new RectTransform(new Vector2(0.45f, 0.6f), loadGameContainer.RectTransform, Anchor.TopRight)
{
RelativeOffset = new Vector2(0.0f, 0.1f)
}, style: "InnerFrame")
var saveFileFrame = new GUIFrame(
new RectTransform(new Vector2(0.45f, 0.6f), loadGameContainer.RectTransform, Anchor.TopRight)
{
RelativeOffset = new Vector2(0.0f, 0.1f)
}, style: "InnerFrame")
{
UserData = "savefileframe"
};
var titleText = new GUITextBlock(new RectTransform(new Vector2(0.9f, 0.2f), saveFileFrame.RectTransform, Anchor.TopCenter)
{
RelativeOffset = new Vector2(0, 0.05f)
},
var titleText = new GUITextBlock(
new RectTransform(new Vector2(0.9f, 0.2f), saveFileFrame.RectTransform, Anchor.TopCenter)
{
RelativeOffset = new Vector2(0, 0.05f)
},
Path.GetFileNameWithoutExtension(fileName), font: GUIStyle.LargeFont, textAlignment: Alignment.Center);
titleText.Text = ToolBox.LimitString(titleText.Text, titleText.Font, titleText.Rect.Width);
var layoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 0.5f), saveFileFrame.RectTransform, Anchor.Center)
{
RelativeOffset = new Vector2(0, 0.1f)
});
var layoutGroup = new GUILayoutGroup(
new RectTransform(new Vector2(0.8f, 0.5f), saveFileFrame.RectTransform, Anchor.Center)
{
RelativeOffset = new Vector2(0, 0.1f)
});
new GUITextBlock(new RectTransform(new Vector2(1, 0), layoutGroup.RectTransform), $"{TextManager.Get("Submarine")} : {subName}", font: GUIStyle.SmallFont);
new GUITextBlock(new RectTransform(new Vector2(1, 0), layoutGroup.RectTransform), $"{TextManager.Get("LastSaved")} : {saveTime}", font: GUIStyle.SmallFont);
new GUITextBlock(new RectTransform(new Vector2(1, 0), layoutGroup.RectTransform), $"{TextManager.Get("MapSeed")} : {mapseed}", font: GUIStyle.SmallFont);
new GUITextBlock(new RectTransform(new Vector2(1, 0), layoutGroup.RectTransform),
$"{TextManager.Get("Submarine")} : {subName}", font: GUIStyle.SmallFont);
new GUITextBlock(new RectTransform(new Vector2(1, 0), layoutGroup.RectTransform),
$"{TextManager.Get("LastSaved")} : {saveTime}", font: GUIStyle.SmallFont);
new GUITextBlock(new RectTransform(new Vector2(1, 0), layoutGroup.RectTransform),
$"{TextManager.Get("MapSeed")} : {mapseed}", font: GUIStyle.SmallFont);
new GUIButton(new RectTransform(new Vector2(0.4f, 0.15f), saveFileFrame.RectTransform, Anchor.BottomCenter)
{
RelativeOffset = new Vector2(0, 0.1f)
}, TextManager.Get("Delete"), style: "GUIButtonSmall")
{
UserData = fileName,
UserData = saveInfo,
OnClicked = DeleteSave
};
return true;
}
private bool DeleteSave(GUIButton button, object obj)
{
string saveFile = obj as string;
if (obj == null) { return false; }
LocalizedString header = TextManager.Get("deletedialoglabel");
LocalizedString body = TextManager.GetWithVariable("deletedialogquestion", "[file]", Path.GetFileNameWithoutExtension(saveFile));
EventEditorScreen.AskForConfirmation(header, body, () =>
{
SaveUtil.DeleteSave(saveFile);
prevSaveFiles?.RemoveAll(s => s.FilePath == saveFile);
UpdateLoadMenu(prevSaveFiles.ToList());
return true;
});
return true;
}
private void RemoveSaveFrame()
{
GUIComponent prevFrame = null;
@@ -551,6 +551,7 @@ namespace Barotrauma
submarineSelection.RefreshSubmarineDisplay(true, setTransferOptionToTrue: true);
break;
case CampaignMode.InteractionType.Map:
GameMain.GameSession?.Map?.ResetPendingSub();
//refresh mission rewards (may have been changed by e.g. a pending submarine switch)
foreach (GUITextBlock rewardText in missionRewardTexts)
{
@@ -25,14 +25,11 @@ namespace Barotrauma.CharacterEditor
{
get
{
if (cam == null)
cam ??= new Camera()
{
cam = new Camera()
{
MinZoom = 0.1f,
MaxZoom = 5.0f
};
}
MinZoom = 0.1f,
MaxZoom = 5.0f
};
return cam;
}
}
@@ -262,7 +259,10 @@ namespace Barotrauma.CharacterEditor
#endif
}
GameMain.Instance.ResolutionChanged -= OnResolutionChanged;
GameMain.LightManager.LightingEnabled = true;
if (!GameMain.DevMode)
{
GameMain.LightManager.LightingEnabled = true;
}
ClearWidgets();
ClearSelection();
}
@@ -280,6 +280,7 @@ namespace Barotrauma.CharacterEditor
#region Main methods
public override void AddToGUIUpdateList()
{
if (rightArea == null || leftArea == null) { return; }
rightArea.AddToGUIUpdateList();
leftArea.AddToGUIUpdateList();
@@ -778,7 +779,7 @@ namespace Barotrauma.CharacterEditor
scaledMouseSpeed = PlayerInput.MouseSpeedPerSecond * (float)deltaTime;
Cam.UpdateTransform(true);
Submarine.CullEntities(Cam);
Submarine.MainSub.UpdateTransform();
Submarine.MainSub?.UpdateTransform();
// Lightmaps
if (GameMain.LightManager.LightingEnabled)
@@ -1570,10 +1571,7 @@ namespace Barotrauma.CharacterEditor
{
wayPoint = WayPoint.GetRandom(spawnType: SpawnType.Human, sub: Submarine.MainSub);
}
if (wayPoint == null)
{
wayPoint = WayPoint.GetRandom(sub: Submarine.MainSub);
}
wayPoint ??= WayPoint.GetRandom(sub: Submarine.MainSub);
spawnPosition = wayPoint.WorldPosition;
}
@@ -3998,7 +3996,7 @@ namespace Barotrauma.CharacterEditor
};
}).Draw(spriteBatch, deltaTime);
}
else
else if (groundedParams != null)
{
GetAnimationWidget("HeadPosition", color, Color.Black, initMethod: w =>
{
@@ -4107,7 +4105,7 @@ namespace Barotrauma.CharacterEditor
};
}).Draw(spriteBatch, deltaTime);
}
else
else if (groundedParams != null)
{
GetAnimationWidget("TorsoPosition", color, Color.Black, initMethod: w =>
{
@@ -1,6 +1,5 @@
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Diagnostics;
@@ -27,7 +26,7 @@ namespace Barotrauma
public Effect ThresholdTintEffect { get; private set; }
public Effect BlueprintEffect { get; set; }
public GameScreen(GraphicsDevice graphics, ContentManager content)
public GameScreen(GraphicsDevice graphics)
{
cam = new Camera();
cam.Translate(new Vector2(-10.0f, 50.0f));
@@ -38,20 +37,13 @@ namespace Barotrauma
CreateRenderTargets(graphics);
};
Effect LoadEffect(string path)
=> content.Load<Effect>(path
#if LINUX || OSX
+"_opengl"
#endif
);
//var blurEffect = LoadEffect("Effects/blurshader");
damageEffect = LoadEffect("Effects/damageshader");
PostProcessEffect = LoadEffect("Effects/postprocess");
GradientEffect = LoadEffect("Effects/gradientshader");
GrainEffect = LoadEffect("Effects/grainshader");
ThresholdTintEffect = LoadEffect("Effects/thresholdtint");
BlueprintEffect = LoadEffect("Effects/blueprintshader");
damageEffect = EffectLoader.Load("Effects/damageshader");
PostProcessEffect = EffectLoader.Load("Effects/postprocess");
GradientEffect = EffectLoader.Load("Effects/gradientshader");
GrainEffect = EffectLoader.Load("Effects/grainshader");
ThresholdTintEffect = EffectLoader.Load("Effects/thresholdtint");
BlueprintEffect = EffectLoader.Load("Effects/blueprintshader");
damageStencil = TextureLoader.FromFile("Content/Map/walldamage.png");
damageEffect.Parameters["xStencil"].SetValue(damageStencil);
@@ -994,7 +994,7 @@ namespace Barotrauma
|| item.IsDownloadPending
|| (item.InstallTime.TryGetValue(out var workshopInstallTime)
&& pkg.InstallTime.TryUnwrap(out var localInstallTime)
&& localInstallTime < workshopInstallTime)));
&& localInstallTime.ToUtcValue() < workshopInstallTime)));
modUpdateStatus = (DateTime.Now + ModUpdateInterval, count);
}
@@ -1,6 +1,5 @@
#nullable enable
using System.Linq;
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
@@ -34,7 +33,7 @@ namespace Barotrauma
{
BlueprintEffect.Dispose();
GameMain.Instance.Content.Unload();
BlueprintEffect = GameMain.Instance.Content.Load<Effect>("Effects/blueprintshader_opengl");
BlueprintEffect = EffectLoader.Load("Effects/blueprintshader");
GameMain.GameScreen.BlueprintEffect = BlueprintEffect;
return true;
}
@@ -9,7 +9,7 @@ using System.Diagnostics;
namespace Barotrauma
{
class SerializableEntityEditor : GUIComponent
sealed class SerializableEntityEditor : GUIComponent
{
private readonly int elementHeight;
private readonly GUILayoutGroup layoutGroup;
@@ -399,10 +399,6 @@ namespace Barotrauma
{
propertyField = CreateBoolField(entity, property, boolVal, displayName, toolTip);
}
else if (value is string stringVal)
{
propertyField = CreateStringField(entity, property, stringVal, displayName, toolTip);
}
else if (value.GetType().IsEnum)
{
if (value.GetType().IsDefined(typeof(FlagsAttribute), inherit: false))
@@ -450,6 +446,10 @@ namespace Barotrauma
{
propertyField = CreateStringArrayField(entity, property, a, displayName, toolTip);
}
else if (value is string or Identifier)
{
propertyField = CreateStringField(entity, property, value.ToString(), displayName, toolTip);
}
return propertyField;
}
@@ -696,7 +696,7 @@ namespace Barotrauma
propertyBox.OnEnterPressed += (box, text) => OnApply(box);
refresh += () =>
{
if (!propertyBox.Selected) { propertyBox.Text = (string)property.GetValue(entity); }
if (!propertyBox.Selected) { propertyBox.Text = property.GetValue(entity).ToString(); }
};
bool OnApply(GUITextBox textBox)
@@ -714,7 +714,7 @@ namespace Barotrauma
if (SetPropertyValue(property, entity, textBox.Text))
{
TrySendNetworkUpdate(entity, property);
textBox.Text = (string) property.GetValue(entity);
textBox.Text = property.GetValue(entity).ToString();
textBox.Flash(GUIStyle.Green, flashDuration: 1f);
}
//restore the entities that were selected before applying
@@ -42,12 +42,7 @@ namespace Barotrauma
{
if (effect == null)
{
#if WINDOWS
effect = GameMain.Instance.Content.Load<Effect>("Effects/deformshader");
#endif
#if LINUX || OSX
effect = GameMain.Instance.Content.Load<Effect>("Effects/deformshader_opengl");
#endif
effect = EffectLoader.Load("Effects/deformshader");
}
Invert = invert;
@@ -202,6 +202,7 @@ namespace Barotrauma.Steam
ContentPackageManager.EnabledPackages.Core!,
(p) => { },
heightScale: 1.0f / 13.0f);
enabledCoreDropdown.AllowNonText = true;
Label(topLeft, "", GUIStyle.SubHeadingFont, heightScale: 1.0f);
topRight.ChildAnchor = Anchor.CenterLeft;
@@ -535,34 +536,119 @@ namespace Barotrauma.Steam
bulkUpdateButton.Enabled = false;
bulkUpdateButton.ToolTip = "";
ContentPackageManager.UpdateContentPackageList();
var corePackages = ContentPackageManager.CorePackages.ToArray();
var currentCore = ContentPackageManager.EnabledPackages.Core!;
SwapDropdownValues<CorePackage>(enabledCoreDropdown,
(p) => p.Name,
ContentPackageManager.CorePackages.ToArray(),
ContentPackageManager.EnabledPackages.Core!,
corePackages,
currentCore,
(p) =>
{
// Manually set dropdown text because
// adding buttons to the elements breaks
// this part of the dropdown code
enabledCoreDropdown.Text = p.Name;
enabledCoreDropdown.ButtonTextColor =
p.HasAnyErrors
? GUIStyle.Red
: GUIStyle.TextColorNormal;
});
enabledCoreDropdown.ListBox.Content.Children
.OfType<GUITextBlock>()
.ForEach(tb =>
CreateModErrorInfo(
(tb.UserData as ContentPackage)!,
tb,
tb));
void addRegularModToList(RegularPackage mod, GUIListBox list)
void addButtonForMod(ContentPackage mod, GUILayoutGroup parent)
{
var modFrame = new GUIFrame(new RectTransform((1.0f, 0.08f), list.Content.RectTransform),
if (ContentPackageManager.LocalPackages.Contains(mod))
{
var editButton = new GUIButton(new RectTransform(Vector2.One, parent.RectTransform, scaleBasis: ScaleBasis.Smallest), "",
style: "WorkshopMenu.EditButton")
{
OnClicked = (button, o) =>
{
ToolBox.OpenFileWithShell(mod.Dir);
return false;
},
ToolTip = TextManager.Get("OpenLocalModInExplorer")
};
}
else if (ContentPackageManager.WorkshopPackages.Contains(mod))
{
var infoButton = new GUIButton(
new RectTransform(Vector2.One, parent.RectTransform, scaleBasis: ScaleBasis.Smallest), "",
style: null)
{
CanBeSelected = false,
OnClicked = (button, o) =>
{
PrepareToShowModInfo(mod);
return false;
}
};
if (!SteamManager.IsInitialized)
{
infoButton.Enabled = false;
}
TaskPool.AddIfNotFound(
$"DetermineUpdateRequired{mod.UgcId}",
mod.IsUpToDate(),
t =>
{
if (!t.TryGetResult(out bool isUpToDate)) { return; }
if (isUpToDate) { return; }
infoButton.CanBeSelected = true;
infoButton.ApplyStyle(GUIStyle.ComponentStyles["WorkshopMenu.InfoButtonUpdate"]);
infoButton.ToolTip = TextManager.Get("ViewModDetailsUpdateAvailable");
bulkUpdateButton.Enabled = true;
bulkUpdateButton.ToolTip = TextManager.Get("ModUpdatesAvailable");
});
}
}
GUILayoutGroup createBaseModListUi(ContentPackage mod, GUIListBox listBox, float height)
{
var modFrame = new GUIFrame(new RectTransform((1.0f, height), listBox.Content.RectTransform),
style: "ListBoxElement")
{
UserData = mod
};
var frameContent = new GUILayoutGroup(new RectTransform((0.95f, 0.9f), modFrame.RectTransform, Anchor.Center), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
Stretch = true,
RelativeSpacing = 0.02f
};
var modNameScissor = new GUIScissorComponent(new RectTransform((0.8f, 1.0f), frameContent.RectTransform))
{
CanBeFocused = false
};
var modName = new GUITextBlock(new RectTransform(Vector2.One, modNameScissor.Content.RectTransform),
text: mod.Name)
{
CanBeFocused = false
};
CreateModErrorInfo(mod, modFrame, modName);
addButtonForMod(mod, frameContent);
return frameContent;
}
foreach (var element in enabledCoreDropdown.ListBox.Content.Children.ToArray())
{
enabledCoreDropdown.ListBox.RemoveChild(element);
if (element.UserData is not ContentPackage mod) { continue; }
createBaseModListUi(mod, enabledCoreDropdown.ListBox, 0.24f);
}
enabledCoreDropdown.Select(corePackages.IndexOf(currentCore));
void addRegularModToList(RegularPackage mod, GUIListBox list)
{
var frameContent = createBaseModListUi(mod, list, 0.08f);
var modFrame = frameContent.Parent;
var contextMenuHandler = new GUICustomComponent(new RectTransform(Vector2.Zero, modFrame.RectTransform),
onUpdate: (f, component) =>
{
@@ -639,76 +725,13 @@ namespace Barotrauma.Steam
contextMenuOptions.ToArray());
}
});
var frameContent = new GUILayoutGroup(new RectTransform((0.95f, 0.9f), modFrame.RectTransform, Anchor.Center), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
Stretch = true,
RelativeSpacing = 0.02f
};
var dragIndicator = new GUIButton(new RectTransform((0.5f, 0.5f), frameContent.RectTransform, scaleBasis: ScaleBasis.BothHeight),
style: "GUIDragIndicator")
{
CanBeFocused = false
};
var modNameScissor = new GUIScissorComponent(new RectTransform((0.8f, 1.0f), frameContent.RectTransform))
{
CanBeFocused = false
};
var modName = new GUITextBlock(new RectTransform(Vector2.One, modNameScissor.Content.RectTransform),
text: mod.Name)
{
CanBeFocused = false
};
CreateModErrorInfo(mod, modFrame, modName);
if (ContentPackageManager.LocalPackages.Contains(mod))
{
var editButton = new GUIButton(new RectTransform(Vector2.One, frameContent.RectTransform, scaleBasis: ScaleBasis.Smallest), "",
style: "WorkshopMenu.EditButton")
{
OnClicked = (button, o) =>
{
ToolBox.OpenFileWithShell(mod.Dir);
return false;
},
ToolTip = TextManager.Get("OpenLocalModInExplorer")
};
}
else if (ContentPackageManager.WorkshopPackages.Contains(mod))
{
var infoButton = new GUIButton(
new RectTransform(Vector2.One, frameContent.RectTransform, scaleBasis: ScaleBasis.Smallest), "",
style: null)
{
CanBeSelected = false,
OnClicked = (button, o) =>
{
PrepareToShowModInfo(mod);
return false;
}
};
if (!SteamManager.IsInitialized)
{
infoButton.Enabled = false;
}
TaskPool.AddIfNotFound(
$"DetermineUpdateRequired{mod.UgcId}",
mod.IsUpToDate(),
t =>
{
if (!t.TryGetResult(out bool isUpToDate)) { return; }
if (!isUpToDate)
{
infoButton.CanBeSelected = true;
infoButton.ApplyStyle(GUIStyle.ComponentStyles["WorkshopMenu.InfoButtonUpdate"]);
infoButton.ToolTip = TextManager.Get("ViewModDetailsUpdateAvailable");
bulkUpdateButton.Enabled = true;
bulkUpdateButton.ToolTip = TextManager.Get("ModUpdatesAvailable");
}
});
}
dragIndicator.RectTransform.SetAsFirstChild();
}
void addRegularModsToList(IEnumerable<RegularPackage> mods, GUIListBox list)
@@ -729,7 +752,7 @@ namespace Barotrauma.Steam
.Where(p => ContentPackageManager.RegularPackages.Contains(p)))
.ToArray();
var disabledMods = ContentPackageManager.RegularPackages.Where(p => !enabledMods.Contains(p));
addRegularModsToList(enabledMods, enabledRegularModsList);
if (refreshDisabled) { addRegularModsToList(disabledMods, disabledRegularModsList); }
@@ -747,7 +770,7 @@ namespace Barotrauma.Steam
var mod = child.UserData as RegularPackage;
if (mod is null || !ContentPackageManager.WorkshopPackages.Contains(mod)) { continue; }
if (!mod.UgcId.TryUnwrap(out var ugcId)) { continue; }
if (!(ugcId is SteamWorkshopId workshopId)) { continue; }
if (ugcId is not SteamWorkshopId workshopId) { continue; }
var btn = child.GetChild<GUILayoutGroup>()?.GetAllChildren<GUIButton>().Last();
if (btn is null) { continue; }
@@ -68,10 +68,13 @@ namespace Barotrauma.Steam
}
protected static void SwapDropdownValues<T>(
GUIDropDown dropdown, Func<T, LocalizedString> textFunc, IReadOnlyList<T> values, T currentValue,
GUIDropDown dropdown,
Func<T, LocalizedString> textFunc,
IReadOnlyList<T> values,
T currentValue,
Action<T> setter)
{
if (dropdown.ListBox.Content.Children.Any(c => !(c.UserData is T)))
if (dropdown.ListBox.Content.Children.Any(c => c.UserData is not T))
{
throw new Exception("SwapValues must preserve the type of the dropdown's userdata");
}
@@ -0,0 +1,12 @@
using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma;
static class EffectLoader
{
public static Effect Load(string path)
=> GameMain.Instance.Content.Load<Effect>(path
#if LINUX || OSX
+"_opengl"
#endif
);
}
@@ -9,7 +9,7 @@ namespace Barotrauma
{
sealed class SpriteRecorder : ISpriteBatch, IDisposable
{
private readonly record struct Command(
public readonly record struct Command(
Texture2D Texture,
VertexPositionColorTexture VertexBL,
VertexPositionColorTexture VertexBR,
@@ -346,7 +346,7 @@ namespace Barotrauma
}
recordedBuffers.Clear();
commandList.Clear();
indexBuffer.Dispose(); indexBuffer = null;
indexBuffer?.Dispose(); indexBuffer = null;
ReadyToRender = false;
}
}