Build 0.18.0.0

This commit is contained in:
Markus Isberg
2022-05-13 00:55:52 +09:00
parent 15d18e6ff6
commit 7547a9b78a
218 changed files with 3881 additions and 2192 deletions
@@ -400,7 +400,7 @@ namespace Barotrauma
partial void UpdateControlled(float deltaTime, Camera cam)
{
if (controlled != this) return;
if (controlled != this) { return; }
ControlLocalPlayer(deltaTime, cam);
@@ -2008,8 +2008,27 @@ namespace Barotrauma
DisplayedVitality = Vitality;
}
partial void UpdateSkinTint()
{
if (!Character.IsVisible) { return; }
FaceTint = DefaultFaceTint;
BodyTint = Color.TransparentBlack;
if (!(Character?.Params?.Health.ApplyAfflictionColors ?? false)) { return; }
foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictions)
{
var affliction = kvp.Key;
Color faceTint = affliction.GetFaceTint();
if (faceTint.A > FaceTint.A) { FaceTint = faceTint; }
Color bodyTint = affliction.GetBodyTint();
if (bodyTint.A > BodyTint.A) { BodyTint = bodyTint; }
}
}
partial void UpdateLimbAfflictionOverlays()
{
if (!Character.IsVisible) { return; }
foreach (Limb limb in Character.AnimController.Limbs)
{
if (limb.HealthIndex < 0 || limb.HealthIndex >= limbHealths.Count) { continue; }
@@ -58,21 +58,19 @@ namespace Barotrauma
public class OutfitPreview
{
/// <summary>
/// Pair.First = sprite, Pair.Second = draw offset
/// </summary>
public readonly List<Pair<Sprite, Vector2>> Sprites;
public readonly List<(Sprite sprite, Vector2 drawOffset)> Sprites;
public Vector2 Dimensions;
public OutfitPreview()
{
Sprites = new List<Pair<Sprite, Vector2>>();
Sprites = new List<(Sprite sprite, Vector2 drawOffset)>();
Dimensions = Vector2.One;
}
public void AddSprite(Sprite sprite, Vector2 drawOffset)
{
Sprites.Add(new Pair<Sprite, Vector2>(sprite, drawOffset));
Sprites.Add((sprite, drawOffset));
}
}
@@ -224,14 +224,14 @@ namespace Barotrauma
public float DamageOverlayStrength
{
get { return damageOverlayStrength; }
set { damageOverlayStrength = MathHelper.Clamp(value, 0.0f, 100.0f); }
set { damageOverlayStrength = MathHelper.Clamp(value, 0.0f, 1.0f); }
}
private float burnOverLayStrength;
public float BurnOverlayStrength
{
get { return burnOverLayStrength; }
set { burnOverLayStrength = MathHelper.Clamp(value, 0.0f, 100.0f); }
set { burnOverLayStrength = MathHelper.Clamp(value, 0.0f, 1.0f); }
}
public string HitSoundTag => Params?.Sound?.Tag;
@@ -279,7 +279,7 @@ namespace Barotrauma
for (int i = 0; i < Params.decorativeSpriteParams.Count; i++)
{
var param = Params.decorativeSpriteParams[i];
var decorativeSprite = new DecorativeSprite(param.Element, file: GetSpritePath(param.Element, param));
var decorativeSprite = new DecorativeSprite(param.Element, file: GetSpritePath(param.Element, param, ref _texturePath));
DecorativeSprites.Add(decorativeSprite);
int groupID = decorativeSprite.RandomGroupID;
if (!DecorativeSpriteGroups.ContainsKey(groupID))
@@ -295,13 +295,13 @@ namespace Barotrauma
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "sprite":
Sprite = new Sprite(subElement, file: GetSpritePath(subElement, Params.normalSpriteParams));
Sprite = new Sprite(subElement, file: GetSpritePath(subElement, Params.normalSpriteParams, ref _texturePath));
break;
case "damagedsprite":
DamagedSprite = new Sprite(subElement, file: GetSpritePath(subElement, Params.damagedSpriteParams));
case "damagedsprite":
DamagedSprite = new Sprite(subElement, file: GetSpritePath(subElement, Params.damagedSpriteParams, ref _damagedTexturePath));
break;
case "conditionalsprite":
var conditionalSprite = new ConditionalSprite(subElement, GetConditionalTarget(), file: GetSpritePath(subElement, null));
var conditionalSprite = new ConditionalSprite(subElement, GetConditionalTarget(), file: GetSpritePath(subElement, null, ref _texturePath));
ConditionalSprites.Add(conditionalSprite);
if (conditionalSprite.DeformableSprite != null)
{
@@ -311,7 +311,7 @@ namespace Barotrauma
}
break;
case "deformablesprite":
_deformSprite = new DeformableSprite(subElement, filePath: GetSpritePath(subElement, Params.deformSpriteParams));
_deformSprite = new DeformableSprite(subElement, filePath: GetSpritePath(subElement, Params.deformSpriteParams, ref _texturePath));
var deformations = CreateDeformations(subElement);
Deformations.AddRange(deformations);
NonConditionalDeformations.AddRange(deformations);
@@ -435,33 +435,33 @@ namespace Barotrauma
{
Sprite.Remove();
var source = Sprite.SourceElement;
Sprite = new Sprite(source, file: GetSpritePath(source, Params.normalSpriteParams));
Sprite = new Sprite(source, file: GetSpritePath(source, Params.normalSpriteParams, ref _texturePath));
}
if (_deformSprite != null)
{
_deformSprite.Remove();
var source = _deformSprite.Sprite.SourceElement;
_deformSprite = new DeformableSprite(source, filePath: GetSpritePath(source, Params.deformSpriteParams));
_deformSprite = new DeformableSprite(source, filePath: GetSpritePath(source, Params.deformSpriteParams, ref _texturePath));
}
if (DamagedSprite != null)
{
DamagedSprite.Remove();
var source = DamagedSprite.SourceElement;
DamagedSprite = new Sprite(source, file: GetSpritePath(source, Params.damagedSpriteParams));
DamagedSprite = new Sprite(source, file: GetSpritePath(source, Params.damagedSpriteParams, ref _damagedTexturePath));
}
for (int i = 0; i < ConditionalSprites.Count; i++)
{
var conditionalSprite = ConditionalSprites[i];
var source = conditionalSprite.ActiveSprite.SourceElement;
conditionalSprite.Remove();
ConditionalSprites[i] = new ConditionalSprite(source, character, file: GetSpritePath(source, null));
ConditionalSprites[i] = new ConditionalSprite(source, character, file: GetSpritePath(source, null, ref _texturePath));
}
for (int i = 0; i < DecorativeSprites.Count; i++)
{
var decorativeSprite = DecorativeSprites[i];
decorativeSprite.Remove();
var source = decorativeSprite.Sprite.SourceElement;
DecorativeSprites[i] = new DecorativeSprite(source, file: GetSpritePath(source, Params.decorativeSpriteParams[i]));
DecorativeSprites[i] = new DecorativeSprite(source, file: GetSpritePath(source, Params.decorativeSpriteParams[i], ref _texturePath));
}
}
@@ -472,16 +472,17 @@ namespace Barotrauma
}
private string _texturePath;
private string GetSpritePath(ContentXElement element, SpriteParams spriteParams)
private string _damagedTexturePath;
private string GetSpritePath(ContentXElement element, SpriteParams spriteParams, ref string path)
{
if (_texturePath == null)
if (path == null)
{
if (spriteParams != null)
{
ContentPath texturePath =
character.Params.VariantFile?.Root?.GetAttributeContentPath("texture", character.Prefab.ContentPackage)
?? ContentPath.FromRaw(character.Prefab.ContentPackage, spriteParams.GetTexturePath());
_texturePath = GetSpritePath(texturePath);
path = GetSpritePath(texturePath);
}
else
{
@@ -489,10 +490,10 @@ namespace Barotrauma
texturePath = texturePath.IsNullOrWhiteSpace()
? ContentPath.FromRaw(character.Prefab.ContentPackage, ragdoll.RagdollParams.Texture)
: texturePath;
_texturePath = GetSpritePath(texturePath);
path = GetSpritePath(texturePath);
}
}
return _texturePath;
return path;
}
/// <summary>
@@ -625,12 +626,7 @@ namespace Barotrauma
{
if (!body.Enabled) { return; }
if (!IsDead)
{
DamageOverlayStrength -= deltaTime;
BurnOverlayStrength -= deltaTime;
}
else
if (IsDead)
{
var spriteParams = Params.GetSprite();
if (spriteParams != null && spriteParams.DeadColorTime > 0 && deadTimer < spriteParams.DeadColorTime)
@@ -688,7 +684,7 @@ namespace Barotrauma
public void Draw(SpriteBatch spriteBatch, Camera cam, Color? overrideColor = null, bool disableDeformations = false)
{
float brightness = 1.0f - (burnOverLayStrength / 100.0f) * 0.5f;
float brightness = Math.Max(1.0f - burnOverLayStrength, 0.2f);
var spriteParams = Params.GetSprite();
if (spriteParams == null) { return; }
@@ -831,32 +827,6 @@ namespace Barotrauma
{
LightSource.LightSpriteEffect = (dir == Direction.Right) ? SpriteEffects.None : SpriteEffects.FlipVertically;
}
if (damageOverlayStrength > 0.0f && DamagedSprite != null && !hideLimb)
{
DamagedSprite.Draw(spriteBatch,
new Vector2(body.DrawPosition.X, -body.DrawPosition.Y),
color * Math.Min(damageOverlayStrength, 1.0f), activeSprite.Origin,
-body.DrawRotation,
Scale, spriteEffect, activeSprite.Depth - (depthStep * 90));
}
foreach (var decorativeSprite in DecorativeSprites)
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
Color c = new Color(decorativeSprite.Color.R / 255f * brightness, decorativeSprite.Color.G / 255f * brightness, decorativeSprite.Color.B / 255f * brightness, decorativeSprite.Color.A / 255f);
if (deadTimer > 0)
{
c = Color.Lerp(c, spriteParams.DeadColor, MathUtils.InverseLerp(0, Params.GetSprite().DeadColorTime, deadTimer));
}
c = overrideColor ?? c;
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState, spriteAnimState[decorativeSprite].RandomRotationFactor);
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier) * Scale;
var ca = (float)Math.Cos(-body.Rotation);
var sa = (float)Math.Sin(-body.Rotation);
Vector2 transformedOffset = new Vector2(ca * offset.X + sa * offset.Y, -sa * offset.X + ca * offset.Y);
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(body.DrawPosition.X + transformedOffset.X, -(body.DrawPosition.Y + transformedOffset.Y)), c,
-body.Rotation + rotation, decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, spriteEffect,
depth: activeSprite.Depth - (depthStep * 100));
}
float step = depthStep;
WearableSprite onlyDrawable = wearingItems.Find(w => w.HideOtherWearables);
if (Params.MirrorHorizontally)
@@ -925,6 +895,36 @@ namespace Barotrauma
//if there are multiple sprites on this limb, make the successive ones be drawn in front
depthStep += step;
}
if (!Hide && onlyDrawable == null)
{
foreach (var decorativeSprite in DecorativeSprites)
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
Color c = new Color(decorativeSprite.Color.R / 255f * brightness, decorativeSprite.Color.G / 255f * brightness, decorativeSprite.Color.B / 255f * brightness, decorativeSprite.Color.A / 255f);
if (deadTimer > 0)
{
c = Color.Lerp(c, spriteParams.DeadColor, MathUtils.InverseLerp(0, Params.GetSprite().DeadColorTime, deadTimer));
}
c = overrideColor ?? c;
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState, spriteAnimState[decorativeSprite].RandomRotationFactor);
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier) * Scale;
var ca = (float)Math.Cos(-body.Rotation);
var sa = (float)Math.Sin(-body.Rotation);
Vector2 transformedOffset = new Vector2(ca * offset.X + sa * offset.Y, -sa * offset.X + ca * offset.Y);
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(body.DrawPosition.X + transformedOffset.X, -(body.DrawPosition.Y + transformedOffset.Y)), c,
-body.Rotation + rotation, decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, spriteEffect,
depth: activeSprite.Depth - depthStep);
depthStep += step;
}
if (damageOverlayStrength > 0.0f && DamagedSprite != null)
{
DamagedSprite.Draw(spriteBatch,
new Vector2(body.DrawPosition.X, -body.DrawPosition.Y),
color * damageOverlayStrength, activeSprite.Origin,
-body.DrawRotation,
Scale, spriteEffect, activeSprite.Depth - depthStep * Math.Max(1, WearingItems.Count * 2)); // Multiply by 2 to get rid of z-fighting with some clothing combos
}
}
if (GameMain.DebugDraw)
{
@@ -359,7 +359,7 @@ namespace Barotrauma.Transition
else
{
//copying a mod: we have a neat method for that!
await SteamManager.Workshop.CopyDirectory(path, Path.GetFileName(path), path, destPath);
await SteamManager.Workshop.CopyDirectory(path, Path.GetFileName(path), path, destPath, SteamManager.Workshop.ShouldCorrectPaths.Yes);
return null;
}
@@ -125,7 +125,7 @@ namespace Barotrauma
{
if (isOpen)
{
frame.AddToGUIUpdateList();
frame.AddToGUIUpdateList(order: 1);
}
}
@@ -1714,9 +1714,47 @@ namespace Barotrauma
//check missing mission texts
foreach (var missionPrefab in MissionPrefab.Prefabs)
{
Identifier missionId = (missionPrefab.ConfigElement.GetAttribute("textidentifier") == null ? missionPrefab.Identifier : missionPrefab.ConfigElement.GetAttributeIdentifier("textidentifier", Identifier.Empty));
addIfMissing($"missionname.{missionId}".ToIdentifier(), language);
addIfMissing($"missiondescription.{missionId}".ToIdentifier(), language);
Identifier missionId = missionPrefab.ConfigElement.GetAttribute("textidentifier") == null ?
missionPrefab.Identifier :
missionPrefab.ConfigElement.GetAttributeIdentifier("textidentifier", Identifier.Empty);
if (!tags[language].Contains(missionPrefab.ConfigElement.GetAttributeIdentifier("name", Identifier.Empty)))
{
addIfMissing($"missionname.{missionId}".ToIdentifier(), language);
}
if (missionPrefab.Type == MissionType.Combat)
{
addIfMissing($"MissionDescriptionNeutral.{missionId}".ToIdentifier(), language);
addIfMissing($"MissionDescription1.{missionId}".ToIdentifier(), language);
addIfMissing($"MissionDescription2.{missionId}".ToIdentifier(), language);
addIfMissing($"MissionTeam1.{missionId}".ToIdentifier(), language);
addIfMissing($"MissionTeam2.{missionId}".ToIdentifier(), language);
}
else
{
if (!tags[language].Contains(missionPrefab.ConfigElement.GetAttributeIdentifier("description", Identifier.Empty)))
{
addIfMissing($"missiondescription.{missionId}".ToIdentifier(), language);
}
if (!tags[language].Contains(missionPrefab.ConfigElement.GetAttributeIdentifier("successmessage", Identifier.Empty)))
{
addIfMissing($"missionsuccess.{missionId}".ToIdentifier(), language);
}
//only check failure message if there's something defined in the xml (otherwise we just use the generic "missionfailed" text)
if (missionPrefab.ConfigElement.GetAttribute("failuremessage") != null &&
!tags[language].Contains(missionPrefab.ConfigElement.GetAttributeIdentifier("failuremessage", Identifier.Empty)))
{
addIfMissing($"missionfailure.{missionId}".ToIdentifier(), language);
}
}
for (int i = 0; i<missionPrefab.Messages.Length; i++)
{
if (missionPrefab.Messages[i].IsNullOrWhiteSpace())
{
addIfMissing($"MissionMessage{i}.{missionId}".ToIdentifier(), language);
}
}
}
foreach (Type itemComponentType in typeof(ItemComponent).Assembly.GetTypes().Where(type => type.IsSubclassOf(typeof(ItemComponent))))
@@ -2503,7 +2541,7 @@ namespace Barotrauma
var entity = MapEntity.mapEntityList[i] as ISerializableEntity;
if (entity != null)
{
List<Pair<object, SerializableProperty>> allProperties = new List<Pair<object, SerializableProperty>>();
List<(object obj, SerializableProperty property)> allProperties = new List<(object obj, SerializableProperty property)>();
if (entity is Item item)
{
@@ -2518,14 +2556,14 @@ namespace Barotrauma
for (int k = 0; k < properties.Count; k++)
{
allProperties.Add(new Pair<object, SerializableProperty>(entity, properties[k]));
allProperties.Add((entity, properties[k]));
}
}
for (int j = 0; j < allProperties.Count; j++)
{
var property = allProperties[j].Second;
string propertyName = (allProperties[j].First.GetType().Name + "." + property.PropertyInfo.Name).ToLowerInvariant();
var property = allProperties[j].property;
string propertyName = (allProperties[j].obj.GetType().Name + "." + property.PropertyInfo.Name).ToLowerInvariant();
LocalizedString displayName = TextManager.Get($"sp.{propertyName}.name");
if (displayName.IsNullOrEmpty())
{
@@ -35,7 +35,6 @@ namespace Barotrauma
public TextGetterHandler TextGetter;
public bool Wrap;
private bool playerInput;
public bool RoundToNearestPixel = true;
@@ -287,8 +286,7 @@ namespace Barotrauma
/// If the rectT height is set 0, the height is calculated from the text.
/// </summary>
public GUITextBlock(RectTransform rectT, RichString text, Color? textColor = null, GUIFont font = null,
Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null,
bool playerInput = false)
Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null)
: base(style, rectT)
{
if (color.HasValue)
@@ -307,7 +305,6 @@ namespace Barotrauma
this.textAlignment = textAlignment;
this.Wrap = wrap;
this.Text = text ?? "";
this.playerInput = playerInput;
if (rectT.Rect.Height == 0 && !text.IsNullOrEmpty())
{
CalculateHeightFromText();
@@ -261,7 +261,7 @@ namespace Barotrauma
this.color = color ?? Color.White;
frame = new GUIFrame(new RectTransform(Vector2.One, rectT, Anchor.Center), style, color);
GUIStyle.Apply(frame, style == "" ? "GUITextBox" : style);
textBlock = new GUITextBlock(new RectTransform(Vector2.One, frame.RectTransform, Anchor.CenterLeft), text ?? "", textColor, font, textAlignment, wrap, playerInput: true);
textBlock = new GUITextBlock(new RectTransform(Vector2.One, frame.RectTransform, Anchor.CenterLeft), text ?? "", textColor, font, textAlignment, wrap);
GUIStyle.Apply(textBlock, "", this);
if (font != null) { textBlock.Font = font; }
CaretEnabled = true;
@@ -657,6 +657,7 @@ namespace Barotrauma
{
CanBeFocused = false
};
GUILayoutGroup parentLayout = new GUILayoutGroup(new RectTransform(Vector2.One, backgroundFrame.RectTransform), isHorizontal: true) { Stretch = true };
if (!(affliction.Prefab is { } prefab)) { return; }
@@ -676,7 +677,7 @@ namespace Barotrauma
GUIFrame textContainer = new GUIFrame(new RectTransform(new Vector2(0.6f, 1f), textLayout.RectTransform), style: null);
GUITextBlock afflictionName = new GUITextBlock(new RectTransform(Vector2.One, textContainer.RectTransform), name, font: GUIStyle.SubHeadingFont);
GUITextBlock healCost = new GUITextBlock(new RectTransform(new Vector2(0.2f, 1f), textLayout.RectTransform), TextManager.FormatCurrency(affliction.Price), textAlignment: Alignment.Center, font: GUIStyle.LargeFont)
GUITextBlock healCost = new GUITextBlock(new RectTransform(new Vector2(0.2f, 1f), textLayout.RectTransform), TextManager.FormatCurrency(affliction.Price), textAlignment: Alignment.Center, font: GUIStyle.SubHeadingFont)
{
Padding = Vector4.Zero
};
@@ -746,12 +747,12 @@ namespace Barotrauma
ClosePopup();
GUIFrame mainFrame = new GUIFrame(new RectTransform(new Vector2(0.28f, 0.45f), container.RectTransform)
GUIFrame mainFrame = new GUIFrame(new RectTransform(new Vector2(0.28f, 0.5f), container.RectTransform)
{
ScreenSpaceOffset = location.ToPoint()
});
GUILayoutGroup mainLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.95f), mainFrame.RectTransform, Anchor.Center)) { RelativeSpacing = 0.01f, Stretch = true };
GUILayoutGroup mainLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), mainFrame.RectTransform, Anchor.Center)) { RelativeSpacing = 0.01f, Stretch = true };
if (mainFrame.Rect.Bottom > GameMain.GraphicsHeight)
{
@@ -819,7 +820,9 @@ namespace Barotrauma
if (!(affliction.Prefab is { } prefab)) { return ImmutableArray<GUIComponent>.Empty; }
GUIFrame backgroundFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.33f), parent.RectTransform), style: "ListBoxElement");
GUILayoutGroup mainLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.95f), backgroundFrame.RectTransform, Anchor.Center))
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.01f), backgroundFrame.RectTransform, Anchor.BottomCenter), style: "HorizontalLine");
GUILayoutGroup mainLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), backgroundFrame.RectTransform, Anchor.Center))
{
RelativeSpacing = 0.05f
};
@@ -862,13 +865,27 @@ namespace Barotrauma
GUILayoutGroup bottomLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.66f), mainLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
GUILayoutGroup bottomTextLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 1f), bottomLayout.RectTransform));
GUITextBlock descriptionBlock = new GUITextBlock(new RectTransform(new Vector2(1f, 0.5f), bottomTextLayout.RectTransform), ToolBox.LimitString(prefab.Description, GUIStyle.Font, GUI.IntScale(64)), wrap: true)
GUILayoutGroup bottomTextLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 1f), bottomLayout.RectTransform))
{
RelativeSpacing = 0.05f
};
GUITextBlock descriptionBlock = new GUITextBlock(new RectTransform(new Vector2(1f, 0.6f), bottomTextLayout.RectTransform), prefab.Description, font: GUIStyle.SmallFont, wrap: true)
{
ToolTip = prefab.Description
};
bool truncated = false;
while (descriptionBlock.TextSize.Y > descriptionBlock.Rect.Height && descriptionBlock.WrappedText.Contains('\n'))
{
var split = descriptionBlock.WrappedText.Value.Split('\n');
descriptionBlock.Text = string.Join('\n', split.Take(split.Length - 1));
truncated = true;
}
if (truncated)
{
descriptionBlock.Text += "...";
}
GUITextBlock priceBlock = new GUITextBlock(new RectTransform(new Vector2(1f, 0.5f), bottomTextLayout.RectTransform), TextManager.FormatCurrency(affliction.Price), font: GUIStyle.LargeFont);
GUITextBlock priceBlock = new GUITextBlock(new RectTransform(new Vector2(1f, 0.25f), bottomTextLayout.RectTransform), TextManager.FormatCurrency(affliction.Price), font: GUIStyle.SubHeadingFont);
GUIButton buyButton = new GUIButton(new RectTransform(new Vector2(0.2f, 0.75f), bottomLayout.RectTransform), style: "CrewManagementAddButton");
@@ -923,6 +940,7 @@ namespace Barotrauma
});
}
#warning TODO: this doesn't seem like the right place for this, and it's not clear from the method signature how this differs from ToolBox.LimitString
public static void EnsureTextDoesntOverflow(string? text, GUITextBlock textBlock, Rectangle bounds, ImmutableArray<GUILayoutGroup>? layoutGroups = null)
{
if (string.IsNullOrWhiteSpace(text)) { return; }
@@ -46,9 +46,7 @@ namespace Barotrauma
private int buyTotal, sellTotal, sellFromSubTotal;
private GUITextBlock storeNameBlock;
private GUITextBlock merchantBalanceBlock;
private GUITextBlock currentSellValueBlock, newSellValueBlock;
private GUIImage sellValueChangeArrow;
private GUITextBlock reputationEffectBlock;
private GUIDropDown sortingDropDown;
private GUITextBox searchBox;
private GUILayoutGroup categoryButtonContainer;
@@ -376,41 +374,29 @@ namespace Barotrauma
AutoScaleVertical = true,
ForceUpperCase = ForceUpperCase.Yes
};
merchantBalanceBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), merchantBalanceContainer.RectTransform),
"", font: GUIStyle.SubHeadingFont)
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), merchantBalanceContainer.RectTransform), "",
color: Color.White, font: GUIStyle.SubHeadingFont)
{
AutoScaleVertical = true,
TextScale = 1.1f,
TextGetter = () =>
{
merchantBalanceBlock.TextColor = ActiveStore?.BalanceColor ?? Color.Red;
return GetMerchantBalanceText();
}
TextGetter = () => GetMerchantBalanceText()
};
// Item sell value ------------------------------------------------
var sellValueContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), balanceAndValueGroup.RectTransform))
var reputationEffectContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), balanceAndValueGroup.RectTransform))
{
CanBeFocused = true,
RelativeSpacing = 0.005f
RelativeSpacing = 0.005f,
ToolTip = TextManager.Get("campaignstore.reputationtooltip")
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), sellValueContainer.RectTransform),
TextManager.Get("campaignstore.sellvalue"), font: GUIStyle.Font, textAlignment: Alignment.BottomLeft)
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), reputationEffectContainer.RectTransform),
TextManager.Get("reputation"), font: GUIStyle.Font, textAlignment: Alignment.BottomLeft)
{
AutoScaleVertical = true,
CanBeFocused = false,
ForceUpperCase = ForceUpperCase.Yes
ForceUpperCase = ForceUpperCase.Yes,
};
var valueChangeGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), sellValueContainer.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
CanBeFocused = false,
RelativeSpacing = 0.02f
};
float blockWidth = GUI.IsFourByThree() ? 0.32f : 0.28f;
Point blockMaxSize = new Point((int)(GameSettings.CurrentConfig.Graphics.TextScale * 60), valueChangeGroup.Rect.Height);
currentSellValueBlock = new GUITextBlock(new RectTransform(new Vector2(blockWidth, 1.0f), valueChangeGroup.RectTransform) { MaxSize = blockMaxSize },
"", font: GUIStyle.SubHeadingFont)
reputationEffectBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), reputationEffectContainer.RectTransform), "", font: GUIStyle.SubHeadingFont)
{
AutoScaleVertical = true,
CanBeFocused = false,
@@ -419,64 +405,27 @@ namespace Barotrauma
{
if (CurrentLocation != null)
{
int balanceAfterTransaction = activeTab switch
Color textColor = GUIStyle.ColorReputationNeutral;
string sign = "";
int reputationModifier = (int)MathF.Round((CurrentLocation.GetStoreReputationModifier(activeTab == StoreTab.Buy) - 1) * 100);
if (reputationModifier > 0)
{
StoreTab.Buy => ActiveStore.Balance + buyTotal,
StoreTab.Sell => ActiveStore.Balance - sellTotal,
StoreTab.SellSub => ActiveStore.Balance - sellFromSubTotal,
_ => throw new NotImplementedException(),
};
if (balanceAfterTransaction != ActiveStore.Balance)
{
var newStatus = CurrentLocation.GetStoreBalanceStatus(balanceAfterTransaction);
if (ActiveStore.ActiveBalanceStatus.SellPriceModifier != newStatus.SellPriceModifier)
{
string tooltipTag = newStatus.SellPriceModifier > ActiveStore.ActiveBalanceStatus.SellPriceModifier ?
"campaingstore.valueincreasetooltip" : "campaingstore.valuedecreasetooltip";
sellValueContainer.ToolTip = TextManager.Get(tooltipTag);
currentSellValueBlock.TextColor = newStatus.Color;
sellValueChangeArrow.Color = newStatus.Color;
sellValueChangeArrow.Visible = true;
newSellValueBlock.TextColor = newStatus.Color;
newSellValueBlock.Text = $"{(newStatus.SellPriceModifier * 100).FormatZeroDecimal()} %";
return $"{(ActiveStore.ActiveBalanceStatus.SellPriceModifier * 100).FormatZeroDecimal()} %";
}
textColor = IsBuying ? GUIStyle.ColorReputationLow : GUIStyle.ColorReputationHigh;
sign = "+";
}
sellValueContainer.ToolTip = TextManager.Get("campaignstore.sellvaluetooltip");
currentSellValueBlock.TextColor = ActiveStore.BalanceColor;
sellValueChangeArrow.Visible = false;
newSellValueBlock.Text = null;
return $"{(ActiveStore.ActiveBalanceStatus.SellPriceModifier * 100).FormatZeroDecimal()} %";
else if (reputationModifier < 0)
{
textColor = IsBuying ? GUIStyle.ColorReputationHigh : GUIStyle.ColorReputationLow;
}
reputationEffectBlock.TextColor = textColor;
return $"{sign}{reputationModifier}%";
}
else
{
sellValueContainer.ToolTip = null;
sellValueChangeArrow.Visible = false;
newSellValueBlock.Text = null;
return null;
return "";
}
}
};
Vector4 newPadding = currentSellValueBlock.Padding;
newPadding.Z = 0;
currentSellValueBlock.Padding = newPadding;
float relativeHeight = 0.45f;
float relativeWidth = (relativeHeight * valueChangeGroup.Rect.Height) / valueChangeGroup.Rect.Width;
sellValueChangeArrow = new GUIImage(new RectTransform(new Vector2(relativeWidth, relativeHeight), valueChangeGroup.RectTransform), "StoreArrow", scaleToFit: true)
{
CanBeFocused = false,
Visible = false
};
newSellValueBlock = new GUITextBlock(new RectTransform(new Vector2(blockWidth, 1.0f), valueChangeGroup.RectTransform) { MaxSize = blockMaxSize },
"", font: GUIStyle.SubHeadingFont)
{
AutoScaleVertical = true,
CanBeFocused = false,
TextScale = 1.1f
};
newPadding = newSellValueBlock.Padding;
newPadding.X = 0;
newSellValueBlock.Padding = newPadding;
// Store mode buttons ------------------------------------------------
var modeButtonFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.4f / 14.0f), storeContent.RectTransform), style: null);
@@ -626,7 +626,7 @@ namespace Barotrauma
{
if (GameMain.Client == null)
{
SubmarineInfo newSub = GameMain.GameSession.SwitchSubmarine(selectedSubmarine, deliveryFee);
GameMain.GameSession.SwitchSubmarine(selectedSubmarine, deliveryFee);
RefreshSubmarineDisplay(true);
}
else
@@ -664,7 +664,7 @@ namespace Barotrauma
if (GameMain.Client == null)
{
GameMain.GameSession.PurchaseSubmarine(selectedSubmarine);
SubmarineInfo newSub = GameMain.GameSession.SwitchSubmarine(selectedSubmarine, 0);
GameMain.GameSession.SwitchSubmarine(selectedSubmarine, 0);
RefreshSubmarineDisplay(true);
}
else
@@ -54,47 +54,65 @@ namespace Barotrauma
private ushort currentPing;
private readonly Character character;
private readonly bool hasCharacter;
private readonly bool wasCharacterAlive;
private readonly GUITextBlock textBlock;
private readonly GUIFrame frame;
private readonly GUIImage permissionIcon;
public LinkedGUI(Client client, GUIFrame frame, bool hasCharacter, GUITextBlock textBlock, GUIImage permissionIcon)
public LinkedGUI(Client client, GUIFrame frame, GUITextBlock textBlock, GUIImage permissionIcon)
{
this.Client = client;
this.textBlock = textBlock;
this.frame = frame;
this.hasCharacter = hasCharacter;
this.permissionIcon = permissionIcon;
character = client?.Character;
wasCharacterAlive = client.Character != null && !client.Character.IsDead;
}
public LinkedGUI(Character character, GUIFrame frame, bool hasCharacter, GUITextBlock textBlock)
public LinkedGUI(Character character, GUIFrame frame, GUITextBlock textBlock)
{
this.character = character;
this.textBlock = textBlock;
this.frame = frame;
this.hasCharacter = hasCharacter;
wasCharacterAlive = character != null && !character.IsDead;
}
public bool HasMultiplayerCharacterChanged()
{
if (Client == null) { return false; }
bool characterState = Client.Character != null;
if (characterState && Client.Character.IsDead) characterState = false;
return hasCharacter != characterState;
if (GameSettings.CurrentConfig.VerboseLogging)
{
if (Client.Character != character)
{
DebugConsole.Log($"Refreshing tab menu crew list (client \"{Client.Name}\"'s character changed from \"{character?.Name ?? "null"}\" to \"{Client.Character?.Name ?? "null"}\")");
}
}
return Client.Character != character;
}
public bool HasMultiplayerCharacterDied()
{
if (Client == null || !hasCharacter || Client.Character == null) { return false; }
return Client.Character.IsDead;
}
public bool HasAICharacterDied()
public bool HasCharacterDied()
{
if (character == null) { return false; }
return character.IsDead;
bool isAlive = !(character?.IsDead ?? true);
if (GameSettings.CurrentConfig.VerboseLogging)
{
if (wasCharacterAlive && !isAlive)
{
DebugConsole.Log(Client == null ?
$"Refreshing tab menu crew list (character \"{character?.Name ?? "null"}\" died)" :
$"Refreshing tab menu crew list (client \"{Client.Name}\"'s character \"{character?.Name ?? "null"}\" died)");
}
else if (!wasCharacterAlive && isAlive)
{
DebugConsole.Log(Client == null ?
$"Refreshing tab menu crew list (character \"{character?.Name ?? "null"}\" came back to life)" :
$"Refreshing tab menu crew list (client \"{Client.Name}\"'s character \"{character?.Name ?? "null"}\" came back to life)");
}
}
return isAlive != wasCharacterAlive;
}
public void TryPingRefresh()
@@ -207,7 +225,7 @@ namespace Barotrauma
{
linkedGUIList[i].TryPingRefresh();
linkedGUIList[i].TryPermissionIconRefresh(GetPermissionIcon(linkedGUIList[i].Client));
if (linkedGUIList[i].HasMultiplayerCharacterChanged() || linkedGUIList[i].HasMultiplayerCharacterDied() || linkedGUIList[i].HasAICharacterDied())
if (linkedGUIList[i].HasMultiplayerCharacterChanged() || linkedGUIList[i].HasCharacterDied())
{
RemoveCurrentElements();
CreateMultiPlayerList(true);
@@ -219,10 +237,11 @@ namespace Barotrauma
{
for (int i = 0; i < linkedGUIList.Count; i++)
{
if (linkedGUIList[i].HasAICharacterDied())
if (linkedGUIList[i].HasCharacterDied())
{
RemoveCurrentElements();
CreateSinglePlayerList(true);
return;
}
}
}
@@ -297,6 +316,10 @@ namespace Barotrauma
var balanceFrame = new GUIFrame(new RectTransform(new Point(innerLayoutGroup.Rect.Width, innerLayoutGroup.Rect.Height - infoFrameHolderHeight), parent: innerLayoutGroup.RectTransform), style: "InnerFrame");
GUITextBlock balanceText = new GUITextBlock(new RectTransform(Vector2.One, balanceFrame.RectTransform), string.Empty, textAlignment: Alignment.Right);
if (GameMain.IsMultiplayer)
{
balanceText.ToolTip = TextManager.Get("bankdescription");
}
GUIFrame bottomDisclaimerFrame = new GUIFrame(new RectTransform(new Vector2(contentFrameSize.X, 0.1f), infoFrame.RectTransform)
{
AbsoluteOffset = new Point(contentFrame.Rect.X, contentFrame.Rect.Bottom + GUI.IntScale(8))
@@ -337,7 +360,7 @@ namespace Barotrauma
var talentsButton = createTabButton(InfoFrameTab.Talents, "tabmenu.character");
talentsButton.OnAddedToGUIUpdateList += (component) =>
{
talentsButton.Enabled = Character.Controlled?.Info != null;
talentsButton.Enabled = Character.Controlled?.Info != null || (GameMain.Client?.CharacterInfo != null && GameMain.GameSession?.GameMode is MultiPlayerCampaign);
if (!talentsButton.Enabled && selectedTab == InfoFrameTab.Talents)
{
SelectInfoFrameTab(InfoFrameTab.Crew);
@@ -560,7 +583,7 @@ namespace Barotrauma
GUITextBlock characterNameBlock = new GUITextBlock(new RectTransform(new Point(characterColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform),
ToolBox.LimitString(character.Info.Name, GUIStyle.Font, characterColumnWidth), textAlignment: Alignment.Center, textColor: character.Info.Job.Prefab.UIColor);
linkedGUIList.Add(new LinkedGUI(character, frame, !character.IsDead, textBlock: null));
linkedGUIList.Add(new LinkedGUI(character, frame, textBlock: null));
}
private void CreateMultiPlayerListContentHolder(GUILayoutGroup headerFrame)
@@ -657,7 +680,7 @@ namespace Barotrauma
if (client != null)
{
CreateNameWithPermissionIcon(client, paddedFrame, out GUIImage permissionIcon);
linkedGUIList.Add(new LinkedGUI(client, frame, true,
linkedGUIList.Add(new LinkedGUI(client, frame,
new GUITextBlock(new RectTransform(new Point(pingColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), client.Ping.ToString(), textAlignment: Alignment.Center),
permissionIcon));
}
@@ -668,12 +691,12 @@ namespace Barotrauma
if (character is AICharacter)
{
linkedGUIList.Add(new LinkedGUI(character, frame, !character.IsDead,
linkedGUIList.Add(new LinkedGUI(character, frame,
new GUITextBlock(new RectTransform(new Point(pingColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), TextManager.Get("tabmenu.bot"), textAlignment: Alignment.Center) { ForceUpperCase = ForceUpperCase.Yes }));
}
else
{
linkedGUIList.Add(new LinkedGUI(client: null, frame, true, textBlock: null, permissionIcon: null));
linkedGUIList.Add(new LinkedGUI(client: null, frame, textBlock: null, permissionIcon: null));
new GUICustomComponent(new RectTransform(new Point(pingColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform, Anchor.Center), onDraw: (sb, component) => DrawDisconnectedIcon(sb, component.Rect))
{
@@ -718,7 +741,7 @@ namespace Barotrauma
};
CreateNameWithPermissionIcon(client, paddedFrame, out GUIImage permissionIcon);
linkedGUIList.Add(new LinkedGUI(client, frame, false,
linkedGUIList.Add(new LinkedGUI(client, frame,
new GUITextBlock(new RectTransform(new Point(pingColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), client.Ping.ToString(), textAlignment: Alignment.Center),
permissionIcon));
@@ -775,19 +798,27 @@ namespace Barotrauma
Stretch = true
};
new GUIFrame(new RectTransform(Vector2.One, paddedLayoutGroup.RectTransform), style: null)
{
IgnoreLayoutGroups = true,
ToolTip = TextManager.Get("walletdescription")
};
if (character.IsBot) { return; }
Sprite walletSprite = GUIStyle.CrewWalletIconSmall.Value.Sprite;
GUIImage icon = new GUIImage(new RectTransform(Vector2.One, paddedLayoutGroup.RectTransform, scaleBasis: ScaleBasis.BothHeight), walletSprite, scaleToFit: true);
GUIImage icon = new GUIImage(new RectTransform(Vector2.One, paddedLayoutGroup.RectTransform, scaleBasis: ScaleBasis.BothHeight), walletSprite, scaleToFit: true) { CanBeFocused = false };
GUITextBlock walletBlock = new GUITextBlock(new RectTransform(Vector2.One, paddedLayoutGroup.RectTransform), string.Empty, textAlignment: Alignment.Right, font: GUIStyle.Font)
{
AutoScaleHorizontal = true,
Padding = Vector4.Zero
Padding = Vector4.Zero,
CanBeFocused = false
};
GUIImage largeIcon = new GUIImage(new RectTransform(Vector2.One, paddedLayoutGroup.RectTransform), walletSprite, scaleToFit: true)
{
CanBeFocused = false,
IgnoreLayoutGroups = true,
Visible = false
};
@@ -971,16 +1002,25 @@ namespace Barotrauma
float relativeX = icon.RectTransform.NonScaledSize.X / (float)icon.Parent.RectTransform.NonScaledSize.X;
GUILayoutGroup headerTextLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f - relativeX, 1f), headerLayout.RectTransform), isHorizontal: true) { Stretch = true };
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), headerTextLayout.RectTransform), TextManager.Get("crewwallet.wallet"), font: GUIStyle.LargeFont);
GUIFrame walletTooltipFrame = new GUIFrame(new RectTransform(Vector2.One, headerLayout.RectTransform), style: null)
{
IgnoreLayoutGroups = true,
ToolTip = TextManager.Get("walletdescription")
};
GUITextBlock moneyBlock = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), headerTextLayout.RectTransform), TextManager.FormatCurrency(targetWallet.Balance), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Right);
GUILayoutGroup middleLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.66f), walletLayout.RectTransform));
GUILayoutGroup salaryTextLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.5f), middleLayout.RectTransform), isHorizontal: true);
GUITextBlock salaryTitle = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), salaryTextLayout.RectTransform), TextManager.Get("crewwallet.salary"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.BottomLeft);
GUITextBlock rewardBlock = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), salaryTextLayout.RectTransform), string.Empty, textAlignment: Alignment.BottomRight);
GUIFrame salaryTooltipFrame = new GUIFrame(new RectTransform(Vector2.One, middleLayout.RectTransform), style: null)
{
IgnoreLayoutGroups = true,
ToolTip = TextManager.Get("crewwallet.salary.tooltip")
};
GUILayoutGroup sliderLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.5f), middleLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.Center);
GUIScrollBar salarySlider = new GUIScrollBar(new RectTransform(new Vector2(0.9f, 1f), sliderLayout.RectTransform), style: "GUISlider", barSize: 0.03f)
{
ToolTip = TextManager.Get("crewwallet.salary.tooltip"),
Range = new Vector2(0, 1),
BarScrollValue = targetWallet.RewardDistribution / 100f,
Step = 0.01f,
@@ -1050,149 +1090,195 @@ namespace Barotrauma
return true;
}
};
Identifier eventIdentifier = nameof(CreateWalletFrame).ToIdentifier();
ToggleTransferMenuIcon(transferMenuButton, open: isTransferMenuOpen);
ToggleCenterButton(centerButton, isSending);
if (!(Character.Controlled is { } myCharacter))
{
salarySlider.Enabled = false;
transferAmountInput.Enabled = false;
centerButton.Enabled = false;
confirmButton.Enabled = false;
return;
}
bool hasMoneyPermissions = CampaignMode.AllowedToManageWallets();
salarySlider.Enabled = hasMoneyPermissions;
Wallet otherWallet;
GameMain.Client?.OnPermissionChanged.RegisterOverwriteExisting(eventIdentifier, e => UpdateWalletInterface(registerEvents: false));
UpdateWalletInterface(registerEvents: true);
switch (hasMoneyPermissions)
void UpdateWalletInterface(bool registerEvents)
{
case true:
rightName.Text = TextManager.Get("crewwallet.bank");
otherWallet = campaign.Bank;
break;
case false when character == myCharacter:
rightName.Text = TextManager.Get("crewwallet.bank");
otherWallet = campaign.Bank;
isSending = true;
ToggleCenterButton(centerButton, isSending);
break;
default:
rightName.Text = myCharacter.Name;
otherWallet = campaign.PersonalWallet;
break;
}
MedicalClinicUI.EnsureTextDoesntOverflow(rightName.Text.ToString(), rightName, rightLayout.Rect, layoutGroups);
updateButtonText();
if (!hasMoneyPermissions)
{
if (character != Character.Controlled)
if (!(Character.Controlled is { } myCharacter))
{
centerButton.Enabled = centerButton.CanBeFocused = false;
salarySlider.Enabled = false;
transferAmountInput.Enabled = false;
centerButton.Enabled = false;
confirmButton.Enabled = false;
return;
}
salarySlider.Enabled = salarySlider.CanBeFocused = false;
}
leftBalance.Text = TextManager.FormatCurrency(otherWallet.Balance);
bool hasMoneyPermissions = CampaignMode.AllowedToManageWallets();
salarySlider.Enabled = hasMoneyPermissions;
UpdateAllInputs();
switch (hasMoneyPermissions)
{
case true:
rightName.Text = TextManager.Get("crewwallet.bank");
otherWallet = campaign.Bank;
break;
case false when character == myCharacter:
rightName.Text = TextManager.Get("crewwallet.bank");
otherWallet = campaign.Bank;
isSending = true;
ToggleCenterButton(centerButton, isSending);
break;
default:
rightName.Text = myCharacter.Name;
otherWallet = campaign.PersonalWallet;
break;
}
MedicalClinicUI.EnsureTextDoesntOverflow(rightName.Text.ToString(), rightName, rightLayout.Rect, layoutGroups);
UpdatedConfirmButtonText();
if (!hasMoneyPermissions)
{
if (character != Character.Controlled)
{
centerButton.Enabled = centerButton.CanBeFocused = false;
}
salarySlider.Enabled = salarySlider.CanBeFocused = false;
}
leftBalance.Text = TextManager.FormatCurrency(otherWallet.Balance);
centerButton.OnClicked = (btn, o) =>
{
isSending = !isSending;
updateButtonText();
ToggleCenterButton(btn, isSending);
UpdateAllInputs();
return true;
};
void updateButtonText()
{
confirmButton.Text = TextManager.Get(hasMoneyPermissions || isSending ? "confirm" : "crewwallet.requestmoney");
}
if (!registerEvents) { return; }
transferAmountInput.OnValueChanged = input =>
{
UpdateInputs();
};
transferAmountInput.OnValueEntered = input =>
{
UpdateAllInputs();
};
Identifier eventIdentifier = nameof(CreateWalletFrame).ToIdentifier();
campaign.OnMoneyChanged.RegisterOverwriteExisting(eventIdentifier, e =>
{
if (e.Wallet == targetWallet)
centerButton.OnClicked = (btn, o) =>
{
moneyBlock.Text = TextManager.FormatCurrency(e.Info.Balance);
salarySlider.BarScrollValue = e.Info.RewardDistribution / 100f;
isSending = !isSending;
UpdatedConfirmButtonText();
ToggleCenterButton(btn, isSending);
UpdateAllInputs();
return true;
};
transferAmountInput.OnValueChanged = input =>
{
UpdateInputs();
};
transferAmountInput.OnValueEntered = input =>
{
UpdateAllInputs();
};
resetButton.OnClicked = (button, o) =>
{
transferAmountInput.IntValue = 0;
UpdateAllInputs();
return true;
};
confirmButton.OnClicked = (button, o) =>
{
int amount = transferAmountInput.IntValue;
if (amount == 0) { return false; }
Option<Character> target1 = Option<Character>.Some(character),
target2 = otherWallet == campaign.Bank ? Option<Character>.None() : Option<Character>.Some(myCharacter);
if (isSending) { (target1, target2) = (target2, target1); }
SendTransaction(target1, target2, amount);
isTransferMenuOpen = false;
ToggleTransferMenuIcon(transferMenuButton, isTransferMenuOpen);
return true;
};
campaign.OnMoneyChanged.RegisterOverwriteExisting(eventIdentifier, e =>
{
if (e.Wallet == targetWallet)
{
moneyBlock.Text = TextManager.FormatCurrency(e.Info.Balance);
salarySlider.BarScrollValue = e.Info.RewardDistribution / 100f;
}
UpdateAllInputs();
});
registeredEvents.Add(eventIdentifier);
void UpdatedConfirmButtonText()
{
confirmButton.Text = TextManager.Get(hasMoneyPermissions || isSending ? "confirm" : "crewwallet.requestmoney");
}
UpdateAllInputs();
});
registeredEvents.Add(eventIdentifier);
resetButton.OnClicked = (button, o) =>
{
transferAmountInput.IntValue = 0;
UpdateAllInputs();
return true;
};
confirmButton.OnClicked = (button, o) =>
{
int amount = transferAmountInput.IntValue;
if (amount == 0) { return false; }
Option<Character> target1 = Option<Character>.Some(character),
target2 = otherWallet == campaign.Bank ? Option<Character>.None() : Option<Character>.Some(myCharacter);
if (isSending) { (target1, target2) = (target2, target1); }
SendTransaction(target1, target2, amount);
isTransferMenuOpen = false;
ToggleTransferMenuIcon(transferMenuButton, isTransferMenuOpen);
return true;
};
void UpdateAllInputs()
{
UpdateInputs();
UpdateMaxInput();
}
void UpdateInputs()
{
confirmButton.Enabled = resetButton.Enabled = transferAmountInput.IntValue > 0;
if (transferAmountInput.IntValue == 0)
void UpdateAllInputs()
{
rightBalance.Text = TextManager.FormatCurrency(otherWallet.Balance);
rightBalance.TextColor = GUIStyle.TextColorNormal;
leftBalance.Text = TextManager.FormatCurrency(targetWallet.Balance);
leftBalance.TextColor = GUIStyle.TextColorNormal;
UpdateInputs();
UpdateMaxInput();
}
else if (isSending)
void UpdateInputs()
{
rightBalance.Text = TextManager.FormatCurrency(otherWallet.Balance + transferAmountInput.IntValue);
rightBalance.TextColor = GUIStyle.Blue;
leftBalance.Text = TextManager.FormatCurrency(targetWallet.Balance - transferAmountInput.IntValue);
leftBalance.TextColor = GUIStyle.Red;
confirmButton.Enabled = resetButton.Enabled = transferAmountInput.IntValue > 0;
if (transferAmountInput.IntValue == 0)
{
rightBalance.Text = TextManager.FormatCurrency(otherWallet.Balance);
rightBalance.TextColor = GUIStyle.TextColorNormal;
leftBalance.Text = TextManager.FormatCurrency(targetWallet.Balance);
leftBalance.TextColor = GUIStyle.TextColorNormal;
}
else if (isSending)
{
rightBalance.Text = TextManager.FormatCurrency(otherWallet.Balance + transferAmountInput.IntValue);
rightBalance.TextColor = GUIStyle.Blue;
leftBalance.Text = TextManager.FormatCurrency(targetWallet.Balance - transferAmountInput.IntValue);
leftBalance.TextColor = GUIStyle.Red;
}
else
{
rightBalance.Text = TextManager.FormatCurrency(otherWallet.Balance - transferAmountInput.IntValue);
rightBalance.TextColor = GUIStyle.Red;
leftBalance.Text = TextManager.FormatCurrency(targetWallet.Balance + transferAmountInput.IntValue);
leftBalance.TextColor = GUIStyle.Blue;
}
}
else
void UpdateMaxInput()
{
rightBalance.Text = TextManager.FormatCurrency(otherWallet.Balance - transferAmountInput.IntValue);
rightBalance.TextColor = GUIStyle.Red;
leftBalance.Text = TextManager.FormatCurrency(targetWallet.Balance + transferAmountInput.IntValue);
leftBalance.TextColor = GUIStyle.Blue;
int maxValue = isSending ? targetWallet.Balance : otherWallet.Balance;
transferAmountInput.MaxValueInt = maxValue;
transferAmountInput.Enabled = true;
transferAmountInput.ToolTip = string.Empty;
if (!hasMoneyPermissions && GameMain.Client?.ServerSettings is { } serverSettings)
{
transferAmountInput.MaxValueInt = Math.Min(maxValue, serverSettings.MaximumTransferRequest);
if (serverSettings.MaximumTransferRequest <= 0)
{
transferAmountInput.Enabled = false;
transferAmountInput.ToolTip = TextManager.Get("wallettransferrequestdisabled");
}
}
}
}
void UpdateMaxInput()
void SetRewardText(int value, GUITextBlock block)
{
transferAmountInput.MaxValueInt = isSending ? targetWallet.Balance : otherWallet.Balance;
var (_, percentage, sum) = Mission.GetRewardShare(value, salaryCrew, Option<int>.None());
LocalizedString tooltip = string.Empty;
block.TextColor = GUIStyle.TextColorNormal;
if (sum > 100)
{
tooltip = TextManager.GetWithVariables("crewwallet.salary.over100toolitp", ("[sum]", $"{(int)sum}"), ("[newvalue]", $"{percentage}"));
block.TextColor = GUIStyle.Orange;
}
LocalizedString text = TextManager.GetWithVariable("percentageformat", "[value]", $"{value}");
block.Text = text;
block.ToolTip = RichString.Rich(tooltip);
}
static void ToggleTransferMenuIcon(GUIButton btn, bool open)
@@ -1235,24 +1321,6 @@ namespace Barotrauma
transfer.Write(msg);
GameMain.Client?.ClientPeer?.Send(msg, DeliveryMethod.Reliable);
}
void SetRewardText(int value, GUITextBlock block)
{
var (_, percentage, sum) = Mission.GetRewardShare(value, salaryCrew, Option<int>.None());
LocalizedString tooltip = string.Empty;
block.TextColor = GUIStyle.TextColorNormal;
if (sum > 100)
{
tooltip = TextManager.GetWithVariables("crewwallet.salary.over100toolitp", ("[sum]", $"{(int)sum}"), ("[newvalue]", $"{percentage}"));
block.TextColor = GUIStyle.Orange;
}
LocalizedString text = TextManager.GetWithVariable("percentageformat", "[value]", $"{value}");
block.Text = text;
block.ToolTip = RichString.Rich(tooltip);
}
}
private GUIComponent CreateClientInfoFrame(GUIFrame frame, Client client, Sprite permissionIcon = null)
@@ -1740,9 +1808,6 @@ namespace Barotrauma
talentButtons.Clear();
talentCornerIcons.Clear();
Character controlledCharacter = Character.Controlled;
if (controlledCharacter == null) { return; }
GUIFrame talentFrameBackground = new GUIFrame(new RectTransform(Vector2.One, infoFrame.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox");
int padding = GUI.IntScale(15);
GUIFrame talentFrameContent = new GUIFrame(new RectTransform(new Point(talentFrameBackground.Rect.Width - padding, talentFrameBackground.Rect.Height - padding), infoFrame.RectTransform, Anchor.Center), style: null);
@@ -1762,13 +1827,20 @@ namespace Barotrauma
GameMain.NetLobbyScreen.CreatePlayerFrame(playerFrame, alwaysAllowEditing: true, createPendingText: false);
}
/*Character controlledCharacter = Character.Controlled;
if (controlledCharacter == null) { return; }
if (controlledCharacter.Info is null)
{
DebugConsole.ThrowError("No character info found for talent UI");
return;
}
}*/
selectedTalents = controlledCharacter.Info.GetUnlockedTalentsInTree().ToList();
Character controlledCharacter = Character.Controlled;
CharacterInfo info = controlledCharacter?.Info ?? GameMain.Client?.CharacterInfo;
if (info == null) { return; }
Job job = info.Job;
GUILayoutGroup talentFrameLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), talentFrameMain.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter)
{
@@ -1776,9 +1848,7 @@ namespace Barotrauma
};
GUILayoutGroup talentInfoLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), talentFrameLayoutGroup.RectTransform, Anchor.Center), isHorizontal: true);
CharacterInfo info = controlledCharacter.Info;
Job job = info.Job;
new GUICustomComponent(new RectTransform(new Vector2(0.25f, 1f), talentInfoLayoutGroup.RectTransform), onDraw: (batch, component) =>
{
@@ -1801,11 +1871,11 @@ namespace Barotrauma
GUITextBlock traitBlock = new GUITextBlock(new RectTransform(Vector2.One, nameLayout.RectTransform), traitString, font: GUIStyle.SmallFont);
traitBlock.RectTransform.NonScaledSize = traitSize.Pad(traitBlock.Padding).ToPoint();
GUIFrame endocrineFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.35f), nameLayout.RectTransform, Anchor.BottomCenter), style: null);
GUIFrame talentsOutsideTreeFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.35f), nameLayout.RectTransform, Anchor.BottomCenter), style: null);
if (!(GameMain.NetworkMember is null))
{
GUIButton newCharacterBox = new GUIButton(new RectTransform(new Vector2(0.675f, 1f), endocrineFrame.RectTransform, Anchor.TopLeft), text: GameMain.NetLobbyScreen.CampaignCharacterDiscarded ? TextManager.Get("settings") : TextManager.Get("createnew"))
GUIButton newCharacterBox = new GUIButton(new RectTransform(new Vector2(0.675f, 1f), talentsOutsideTreeFrame.RectTransform, Anchor.TopLeft), text: GameMain.NetLobbyScreen.CampaignCharacterDiscarded ? TextManager.Get("settings") : TextManager.Get("createnew"))
{
IgnoreLayoutGroups = true
};
@@ -1852,13 +1922,14 @@ namespace Barotrauma
}
}
IEnumerable<TalentPrefab> endocrineTalents = info.GetEndocrineTalents().Select(e => TalentPrefab.TalentPrefabs.Find(c => c.Identifier == e));
IEnumerable<TalentPrefab> talentsOutsideTree = info.GetUnlockedTalentsOutsideTree().Select(e => TalentPrefab.TalentPrefabs.Find(c => c.Identifier == e));
if (endocrineTalents.Count() > 0)
if (talentsOutsideTree.Count() > 0)
{
GUIImage endocrineIcon = new GUIImage(new RectTransform(new Vector2(0.275f, 1f), endocrineFrame.RectTransform, anchor: Anchor.TopRight, scaleBasis: ScaleBasis.Normal), style: "EndocrineReminderIcon")
//TODO: replace with something more generic
GUIImage endocrineIcon = new GUIImage(new RectTransform(new Vector2(0.275f, 1f), talentsOutsideTreeFrame.RectTransform, anchor: Anchor.TopRight, scaleBasis: ScaleBasis.Normal), style: "EndocrineReminderIcon")
{
ToolTip = $"{TextManager.Get("afflictionname.endocrineboost")}\n\n{string.Join(", ", endocrineTalents.Select(e => e.DisplayName))}"
ToolTip = $"{TextManager.Get("afflictionname.endocrineboost")}\n\n{string.Join(", ", talentsOutsideTree.Select(e => e.DisplayName))}"
};
}
@@ -1870,49 +1941,55 @@ namespace Barotrauma
skillBlock.RectTransform.NonScaledSize = skillSize.Pad(skillBlock.Padding).ToPoint();
skillListBox = new GUIListBox(new RectTransform(new Vector2(1f, 1f - skillBlock.RectTransform.RelativeSize.Y), skillLayout.RectTransform), style: null);
CreateTalentSkillList(controlledCharacter, skillListBox);
CreateTalentSkillList(controlledCharacter, info, skillListBox);
if (!TalentTree.JobTalentTrees.TryGet(controlledCharacter.Info.Job.Prefab.Identifier, out TalentTree talentTree)) { return; }
new GUIFrame(new RectTransform(new Vector2(1f, 1f), talentFrameLayoutGroup.RectTransform), style: "HorizontalLine");
GUIListBox talentTreeListBox = new GUIListBox(new RectTransform(new Vector2(1f, 0.7f), talentFrameLayoutGroup.RectTransform, Anchor.TopCenter), isHorizontal: true, style: null);
List<GUITextBlock> subTreeNames = new List<GUITextBlock>();
foreach (var subTree in talentTree.TalentSubTrees)
if (controlledCharacter != null)
{
GUIFrame subTreeFrame = new GUIFrame(new RectTransform(new Vector2(0.333f, 1f), talentTreeListBox.Content.RectTransform, anchor: Anchor.TopLeft), style: null);
GUILayoutGroup subTreeLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 1f), subTreeFrame.RectTransform, Anchor.Center), false, childAnchor: Anchor.TopCenter);
if (!TalentTree.JobTalentTrees.TryGet(info.Job.Prefab.Identifier, out TalentTree talentTree)) { return; }
GUIFrame subtreeTitleFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.111f), subTreeLayoutGroup.RectTransform, anchor: Anchor.TopCenter), style: null);
int elementPadding = GUI.IntScale(8);
Point headerSize = subtreeTitleFrame.RectTransform.NonScaledSize;
GUIFrame subTreeTitleBackground = new GUIFrame(new RectTransform(new Point(headerSize.X - elementPadding, headerSize.Y), subtreeTitleFrame.RectTransform, anchor: Anchor.Center), style: "SubtreeHeader");
subTreeNames.Add(new GUITextBlock(new RectTransform(Vector2.One, subTreeTitleBackground.RectTransform, anchor: Anchor.TopCenter), subTree.DisplayName, font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Center));
new GUIFrame(new RectTransform(new Vector2(1f, 1f), talentFrameLayoutGroup.RectTransform), style: "HorizontalLine");
for (int i = 0; i < 4; i++)
GUIListBox talentTreeListBox = new GUIListBox(new RectTransform(new Vector2(1f, 0.7f), talentFrameLayoutGroup.RectTransform, Anchor.TopCenter), isHorizontal: true, style: null);
selectedTalents = info.GetUnlockedTalentsInTree().ToList();
List<GUITextBlock> subTreeNames = new List<GUITextBlock>();
foreach (var subTree in talentTree.TalentSubTrees)
{
GUIFrame talentOptionFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.222f), subTreeLayoutGroup.RectTransform, anchor: Anchor.TopCenter), style: null);
GUIFrame subTreeFrame = new GUIFrame(new RectTransform(new Vector2(0.333f, 1f), talentTreeListBox.Content.RectTransform, anchor: Anchor.TopLeft), style: null);
GUILayoutGroup subTreeLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 1f), subTreeFrame.RectTransform, Anchor.Center), false, childAnchor: Anchor.TopCenter);
Point talentFrameSize = talentOptionFrame.RectTransform.NonScaledSize;
GUIFrame subtreeTitleFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.111f), subTreeLayoutGroup.RectTransform, anchor: Anchor.TopCenter), style: null);
int elementPadding = GUI.IntScale(8);
Point headerSize = subtreeTitleFrame.RectTransform.NonScaledSize;
GUIFrame subTreeTitleBackground = new GUIFrame(new RectTransform(new Point(headerSize.X - elementPadding, headerSize.Y), subtreeTitleFrame.RectTransform, anchor: Anchor.Center), style: "SubtreeHeader");
subTreeNames.Add(new GUITextBlock(new RectTransform(Vector2.One, subTreeTitleBackground.RectTransform, anchor: Anchor.TopCenter), subTree.DisplayName, font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Center));
GUIFrame talentBackground = new GUIFrame(new RectTransform(new Point(talentFrameSize.X - elementPadding, talentFrameSize.Y - elementPadding), talentOptionFrame.RectTransform, anchor: Anchor.Center), style: "TalentBackground");
GUIFrame talentBackgroundHighlight = new GUIFrame(new RectTransform(Vector2.One, talentBackground.RectTransform, anchor: Anchor.Center), style: "TalentBackgroundGlow") { Visible = false };
GUIImage cornerIcon = new GUIImage(new RectTransform(new Vector2(0.2f), talentOptionFrame.RectTransform, anchor: Anchor.BottomRight, scaleBasis: ScaleBasis.BothHeight) { MaxSize = new Point(16) }, style: null)
for (int i = 0; i < 4; i++)
{
CanBeFocused = false
};
GUIFrame talentOptionFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.222f), subTreeLayoutGroup.RectTransform, anchor: Anchor.TopCenter), style: null);
Point iconSize = cornerIcon.RectTransform.NonScaledSize;
cornerIcon.RectTransform.AbsoluteOffset = new Point(iconSize.X / 2, iconSize.Y / 2);
Point talentFrameSize = talentOptionFrame.RectTransform.NonScaledSize;
GUIFrame talentBackground = new GUIFrame(new RectTransform(new Point(talentFrameSize.X - elementPadding, talentFrameSize.Y - elementPadding), talentOptionFrame.RectTransform, anchor: Anchor.Center), style: "TalentBackground")
{
Color = talentStageBackgroundColors[TalentTree.TalentTreeStageState.Locked]
};
GUIFrame talentBackgroundHighlight = new GUIFrame(new RectTransform(Vector2.One, talentBackground.RectTransform, anchor: Anchor.Center), style: "TalentBackgroundGlow") { Visible = false };
GUIImage cornerIcon = new GUIImage(new RectTransform(new Vector2(0.2f), talentOptionFrame.RectTransform, anchor: Anchor.BottomRight, scaleBasis: ScaleBasis.BothHeight) { MaxSize = new Point(16) }, style: null)
{
CanBeFocused = false,
Color = talentStageBackgroundColors[TalentTree.TalentTreeStageState.Locked]
};
Point iconSize = cornerIcon.RectTransform.NonScaledSize;
cornerIcon.RectTransform.AbsoluteOffset = new Point(iconSize.X / 2, iconSize.Y / 2);
if (subTree.TalentOptionStages.Count <= i) { continue; }
if (subTree.TalentOptionStages.Count > i)
{
TalentOption talentOption = subTree.TalentOptionStages[i];
GUILayoutGroup talentOptionCenterGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.75f, 0.7f), talentOptionFrame.RectTransform, Anchor.Center), childAnchor: Anchor.CenterLeft);
GUILayoutGroup talentOptionLayoutGroup = new GUILayoutGroup(new RectTransform(Vector2.One, talentOptionCenterGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { Stretch = true };
foreach (TalentPrefab talent in talentOption.Talents.OrderBy(t => t.Identifier))
@@ -1929,6 +2006,7 @@ namespace Barotrauma
ToolTip = RichString.Rich(talent.DisplayName + "\n\n" + talent.Description),
UserData = talent.Identifier,
PressedColor = pressedColor,
Enabled = controlledCharacter != null,
OnClicked = (button, userData) =>
{
// deselect other buttons in tier by removing their selected talents from pool
@@ -1961,7 +2039,7 @@ namespace Barotrauma
},
};
talentButton.Color = talentButton.HoverColor = talentButton.PressedColor = talentButton.SelectedColor = Color.Transparent;
talentButton.Color = talentButton.HoverColor = talentButton.PressedColor = talentButton.SelectedColor = talentButton.DisabledColor = Color.Transparent;
GUIComponent iconImage;
if (talent.Icon is null)
@@ -1971,6 +2049,7 @@ namespace Barotrauma
OutlineColor = GUIStyle.Red,
TextColor = GUIStyle.Red,
PressedColor = unselectableColor,
DisabledColor = unselectableColor,
CanBeFocused = false,
};
}
@@ -1979,63 +2058,63 @@ namespace Barotrauma
iconImage = new GUIImage(new RectTransform(Vector2.One, talentButton.RectTransform, anchor: Anchor.Center), sprite: talent.Icon, scaleToFit: true)
{
PressedColor = unselectableColor,
DisabledColor = unselectableColor * 0.5f,
CanBeFocused = false,
};
}
iconImage.Enabled = talentButton.Enabled;
talentButtons.Add((talentButton, iconImage));
}
talentCornerIcons.Add((subTree.Identifier, i, cornerIcon, talentBackground, talentBackgroundHighlight));
talentCornerIcons.Add((subTree.Identifier, i, cornerIcon, talentBackground, talentBackgroundHighlight));
}
}
}
GUITextBlock.AutoScaleAndNormalize(subTreeNames);
GUITextBlock.AutoScaleAndNormalize(subTreeNames);
GUILayoutGroup talentBottomFrame = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.07f), talentFrameLayoutGroup.RectTransform, Anchor.TopCenter), isHorizontal: true) { RelativeSpacing = 0.01f };
GUILayoutGroup talentBottomFrame = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.07f), talentFrameLayoutGroup.RectTransform, Anchor.TopCenter), isHorizontal: true) { RelativeSpacing = 0.01f };
GUILayoutGroup experienceLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.59f, 1f), talentBottomFrame.RectTransform));
GUIFrame experienceBarFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.5f), experienceLayout.RectTransform), style: null);
GUILayoutGroup experienceLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.59f, 1f), talentBottomFrame.RectTransform));
GUIFrame experienceBarFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.5f), experienceLayout.RectTransform), style: null);
experienceBar = new GUIProgressBar(new RectTransform(new Vector2(1f, 1f), experienceBarFrame.RectTransform, Anchor.CenterLeft),
barSize: controlledCharacter.Info.GetProgressTowardsNextLevel(), color: GUIStyle.Green)
{
IsHorizontal = true,
};
experienceBar = new GUIProgressBar(new RectTransform(new Vector2(1f, 1f), experienceBarFrame.RectTransform, Anchor.CenterLeft),
barSize: info.GetProgressTowardsNextLevel(), color: GUIStyle.Green)
{
IsHorizontal = true,
};
experienceText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), experienceBarFrame.RectTransform, anchor: Anchor.Center), "", font: GUIStyle.Font, textAlignment: Alignment.CenterRight)
{
Shadow = true,
ToolTip = TextManager.Get("experiencetooltip")
};
experienceText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), experienceBarFrame.RectTransform, anchor: Anchor.Center), "", font: GUIStyle.Font, textAlignment: Alignment.CenterRight)
{
Shadow = true,
ToolTip = TextManager.Get("experiencetooltip")
};
talentPointText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), experienceLayout.RectTransform, anchor: Anchor.Center), "", font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterRight) { AutoScaleVertical = true };
talentPointText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), experienceLayout.RectTransform, anchor: Anchor.Center), "", font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterRight) { AutoScaleVertical = true };
talentResetButton = new GUIButton(new RectTransform(new Vector2(0.19f, 1f), talentBottomFrame.RectTransform), text: TextManager.Get("reset"), style: "GUIButtonFreeScale")
{
OnClicked = ResetTalentSelection
};
talentApplyButton = new GUIButton(new RectTransform(new Vector2(0.19f, 1f), talentBottomFrame.RectTransform), text: TextManager.Get("applysettingsbutton"), style: "GUIButtonFreeScale")
{
OnClicked = ApplyTalentSelection,
};
GUITextBlock.AutoScaleAndNormalize(talentResetButton.TextBlock, talentApplyButton.TextBlock);
talentResetButton = new GUIButton(new RectTransform(new Vector2(0.19f, 1f), talentBottomFrame.RectTransform), text: TextManager.Get("reset"), style: "GUIButtonFreeScale")
{
OnClicked = ResetTalentSelection
};
talentApplyButton = new GUIButton(new RectTransform(new Vector2(0.19f, 1f), talentBottomFrame.RectTransform), text: TextManager.Get("applysettingsbutton"), style: "GUIButtonFreeScale")
{
OnClicked = ApplyTalentSelection,
};
GUITextBlock.AutoScaleAndNormalize(talentResetButton.TextBlock, talentApplyButton.TextBlock);
}
UpdateTalentInfo();
}
private void CreateTalentSkillList(Character character, GUIListBox parent)
private void CreateTalentSkillList(Character character, CharacterInfo info, GUIListBox parent)
{
parent.Content.ClearChildren();
List<GUITextBlock> skillNames = new List<GUITextBlock>();
foreach (Skill skill in character.Info.Job.GetSkills())
foreach (Skill skill in info.Job.GetSkills())
{
GUILayoutGroup skillContainer = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.2f), parent.Content.RectTransform), isHorizontal: true) { CanBeFocused = false };
skillNames.Add(new GUITextBlock(new RectTransform(new Vector2(0.7f, 1f), skillContainer.RectTransform), TextManager.Get($"skillname.{skill.Identifier}").Fallback(skill.Identifier.Value)));
new GUITextBlock(new RectTransform(new Vector2(0.15f, 1.0f), skillContainer.RectTransform), Math.Floor(skill.Level).ToString("F0"), textAlignment: Alignment.CenterRight) { Padding = new Vector4(0, 0, 4, 0) };
float modifiedSkillLevel = character.GetSkillLevel(skill.Identifier);
float modifiedSkillLevel = character?.GetSkillLevel(skill.Identifier) ?? skill.Level;
if (!MathUtils.NearlyEqual(MathF.Floor(modifiedSkillLevel), MathF.Floor(skill.Level)))
{
int skillChange = (int)MathF.Floor(modifiedSkillLevel - skill.Level);
@@ -2129,7 +2208,7 @@ namespace Barotrauma
talentButton.icon.HoverColor = hoverColor;
}
CreateTalentSkillList(controlledCharacter, skillListBox);
CreateTalentSkillList(controlledCharacter, controlledCharacter.Info, skillListBox);
}
private void ApplyTalents(Character controlledCharacter)
@@ -2157,6 +2236,7 @@ namespace Barotrauma
private bool ResetTalentSelection(GUIButton guiButton, object userData)
{
Character controlledCharacter = Character.Controlled;
if (controlledCharacter?.Info == null) { return false; }
selectedTalents = controlledCharacter.Info.GetUnlockedTalentsInTree().ToList();
UpdateTalentInfo();
return true;
@@ -546,6 +546,10 @@ namespace Barotrauma
yield return CoroutineStatus.Running;
#if DEBUG
LevelGenerationParams.CheckValidity();
#endif
MainMenuScreen.Select();
foreach (Identifier steamError in SteamManager.InitializationErrors)
@@ -1033,11 +1037,6 @@ namespace Barotrauma
{
GUI.SetSavingIndicatorState(true);
if (GameSession.Submarine != null && !GameSession.Submarine.Removed)
{
GameSession.SubmarineInfo = new SubmarineInfo(GameSession.Submarine);
}
// Update store stock when saving and quitting in an outpost (normally updated when CampaignMode.End() is called)
if (GameSession?.Campaign is SinglePlayerCampaign spCampaign && Level.IsLoadedOutpost && spCampaign.Map?.CurrentLocation != null && spCampaign.CargoManager != null)
{
@@ -81,7 +81,6 @@ namespace Barotrauma
: this(isSinglePlayer)
{
AddCharacterElements(element);
ActiveOrdersElement = element.GetChildElement("activeorders");
}
partial void InitProjectSpecific()
@@ -3661,9 +3660,9 @@ namespace Barotrauma
crewList.ClearChildren();
}
public void Save(XElement parentElement)
public XElement Save(XElement parentElement)
{
XElement element = new XElement("crew");
var element = new XElement("crew");
for (int i = 0; i < characterInfos.Count; i++)
{
var ci = characterInfos[i];
@@ -3674,8 +3673,8 @@ namespace Barotrauma
infoElement.Add(new XAttribute("crewlistindex", ci.CrewListIndex));
if (ci.LastControlled) { infoElement.Add(new XAttribute("lastcontrolled", true)); }
}
SaveActiveOrders(element);
parentElement.Add(element);
parentElement?.Add(element);
return element;
}
public static void ClientReadActiveOrders(IReadMessage inc)
@@ -13,6 +13,11 @@ namespace Barotrauma
partial void SettingsChanged(Option<int> balanceChanged, Option<int> rewardChanged)
{
if (Owner is Some<Character> { Value: var character })
{
if (!character.IsPlayer) { return; }
}
CampaignMode campaign = GameMain.GameSession?.Campaign;
WalletChangedData data = new WalletChangedData
{
@@ -92,6 +92,7 @@ namespace Barotrauma
break;
case "crew":
GameMain.GameSession.CrewManager = new CrewManager(subElement, true);
ActiveOrdersElement = element.GetChildElement("activeorders");
break;
case "map":
map = Map.Load(this, subElement, Settings);
@@ -242,11 +243,10 @@ namespace Barotrauma
crewDead = false;
endTimer = 5.0f;
CrewManager.InitSinglePlayerRound();
if (petsElement != null)
{
PetBehavior.LoadPets(petsElement);
}
CrewManager.LoadActiveOrders();
LoadPets();
LoadActiveOrders();
CargoManager.InitPurchasedIDCards();
GUI.DisableSavingIndicatorDelayed();
}
@@ -461,41 +461,7 @@ namespace Barotrauma
if (success)
{
if (leavingSub != Submarine.MainSub && !leavingSub.DockedTo.Contains(Submarine.MainSub))
{
Submarine.MainSub = leavingSub;
GameMain.GameSession.Submarine = leavingSub;
GameMain.GameSession.SubmarineInfo = leavingSub.Info;
leavingSub.Info.FilePath = System.IO.Path.Combine(SaveUtil.TempPath, leavingSub.Info.Name + ".sub");
var subsToLeaveBehind = GetSubsToLeaveBehind(leavingSub);
GameMain.GameSession.OwnedSubmarines.Add(leavingSub.Info);
foreach (Submarine sub in subsToLeaveBehind)
{
GameMain.GameSession.OwnedSubmarines.RemoveAll(s => s != leavingSub.Info && s.Name == sub.Info.Name);
MapEntity.mapEntityList.RemoveAll(e => e.Submarine == sub && e is LinkedSubmarine);
LinkedSubmarine.CreateDummy(leavingSub, sub);
}
}
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
if (PendingSubmarineSwitch != null)
{
SubmarineInfo previousSub = GameMain.GameSession.SubmarineInfo;
GameMain.GameSession.SubmarineInfo = PendingSubmarineSwitch;
for (int i = 0; i < GameMain.GameSession.OwnedSubmarines.Count; i++)
{
if (GameMain.GameSession.OwnedSubmarines[i].Name == previousSub.Name)
{
GameMain.GameSession.OwnedSubmarines[i] = previousSub;
break;
}
}
}
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
PendingSubmarineSwitch = null;
}
else
{
@@ -766,11 +732,10 @@ namespace Barotrauma
c.Info.SaveOrderData();
}
petsElement = new XElement("pets");
PetBehavior.SavePets(petsElement);
modeElement.Add(petsElement);
SavePets(modeElement);
var crewManagerElement = CrewManager.Save(modeElement);
SaveActiveOrders(crewManagerElement);
CrewManager.Save(modeElement);
CampaignMetadata.Save(modeElement);
Map.Save(modeElement);
CargoManager?.SavePurchasedItems(modeElement);
@@ -257,7 +257,7 @@ namespace Barotrauma.Tutorials
yield return new WaitForSeconds(2.0f);
}*/
TriggerTutorialSegment(0, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Select), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Deselect), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.ToggleInventory)); // Medical supplies objective
TriggerTutorialSegment(0, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Select), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Deselect)); // Medical supplies objective
do
{
@@ -275,7 +275,7 @@ namespace Barotrauma.Tutorials
do { yield return null; } while (!engineer_equipmentObjectiveSensor.MotionDetected);
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Engineer.Radio.Equipment"), ChatMessageType.Radio, null);
yield return new WaitForSeconds(0.5f, false);
TriggerTutorialSegment(0, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Select), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Deselect), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.ToggleInventory)); // Retrieve equipment
TriggerTutorialSegment(0, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Select), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Deselect)); // Retrieve equipment
bool firstSlotRemoved = false;
bool secondSlotRemoved = false;
bool thirdSlotRemoved = false;
@@ -330,7 +330,7 @@ namespace Barotrauma.Tutorials
yield return new WaitForSeconds(0.0f, false);
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Mechanic.Radio.Equipment"), ChatMessageType.Radio, null);
do { yield return null; } while (!mechanic_equipmentObjectiveSensor.MotionDetected);
TriggerTutorialSegment(1, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Select), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Deselect), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.ToggleInventory)); // Equipment & inventory objective
TriggerTutorialSegment(1, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Select), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Deselect)); // Equipment & inventory objective
SetHighlight(mechanic_equipmentCabinet.Item, true);
bool firstSlotRemoved = false;
bool secondSlotRemoved = false;
@@ -372,7 +372,7 @@ namespace Barotrauma.Tutorials
// Room 3
do { yield return null; } while (!mechanic_weldingObjectiveSensor.MotionDetected);
TriggerTutorialSegment(2, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Aim), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Shoot), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.ToggleInventory)); // Welding objective
TriggerTutorialSegment(2, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Aim), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Shoot)); // Welding objective
do
{
if (!mechanic.HasEquippedItem("divingmask".ToIdentifier()))
@@ -106,7 +106,7 @@ namespace Barotrauma.Tutorials
Character.Controlled = character;
character.GiveJobItems(null);
var idCard = character.Inventory.FindItemByIdentifier("idcard".ToIdentifier());
var idCard = character.Inventory.FindItemByTag("identitycard".ToIdentifier());
if (idCard == null)
{
DebugConsole.ThrowError("Item prefab \"ID Card\" not found!");
@@ -525,6 +525,7 @@ namespace Barotrauma
if (!AccessibleWhenAlive && !character.IsDead && !AccessibleByOwner)
{
syncItemsDelay = Math.Max(syncItemsDelay - deltaTime, 0.0f);
doubleClickedItems.Clear();
return;
}
@@ -931,7 +932,7 @@ namespace Barotrauma
// Move the item from the subinventory to the selected container
return QuickUseAction.PutToContainer;
}
else
else if (character.Inventory.AccessibleWhenAlive || character.Inventory.AccessibleByOwner)
{
// Take from the subinventory and place it in the character's main inventory if no target container is selected
return QuickUseAction.TakeFromContainer;
@@ -959,7 +960,8 @@ namespace Barotrauma
}
else if (character.SelectedBy?.Inventory != null &&
Character.Controlled == character.SelectedBy &&
!character.SelectedBy.Inventory.Locked &&
!character.SelectedBy.Inventory.Locked &&
(character.SelectedBy.Inventory.AccessibleWhenAlive || character.SelectedBy.Inventory.AccessibleByOwner) &&
allowInventorySwap)
{
return QuickUseAction.TakeFromCharacter;
@@ -66,6 +66,9 @@ namespace Barotrauma.Items.Components
rect.Height = (int)(rect.Height * (1.0f - openState));
}
//only merge the door's convex hull with overlapping wall segments if it's fully open or fully closed
//it's the heaviest part of changing the convex hull, and doesn't need to be done while the door is still in motion
bool mergeOverlappingSegments = openState <= 0.0f || openState >= 1.0f;
if (Window.Height > 0 && Window.Width > 0)
{
if (IsHorizontal)
@@ -88,7 +91,7 @@ namespace Barotrauma.Items.Components
else
{
convexHull2.Enabled = true;
convexHull2.SetVertices(GetConvexHullCorners(rect2));
convexHull2.SetVertices(GetConvexHullCorners(rect2), mergeOverlappingSegments);
}
}
}
@@ -112,7 +115,7 @@ namespace Barotrauma.Items.Components
else
{
convexHull2.Enabled = true;
convexHull2.SetVertices(GetConvexHullCorners(rect2));
convexHull2.SetVertices(GetConvexHullCorners(rect2), mergeOverlappingSegments);
}
}
}
@@ -127,7 +130,7 @@ namespace Barotrauma.Items.Components
else
{
convexHull.Enabled = true;
convexHull.SetVertices(GetConvexHullCorners(rect));
convexHull.SetVertices(GetConvexHullCorners(rect), mergeOverlappingSegments);
}
}
@@ -1,4 +1,5 @@
using Barotrauma.Networking;
using Barotrauma.Extensions;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Linq;
@@ -78,7 +79,7 @@ namespace Barotrauma.Items.Components
activateButton = new GUIButton(new RectTransform(new Vector2(0.95f, 0.8f), buttonContainer.RectTransform), TextManager.Get("DeconstructorDeconstruct"), style: "DeviceButton")
{
TextBlock = { AutoScaleHorizontal = true },
OnClicked = ToggleActive
OnClicked = OnActivateButtonClicked
};
inSufficientPowerWarning = new GUITextBlock(new RectTransform(Vector2.One, activateButton.RectTransform),
TextManager.Get("DeconstructorNoPower"), textColor: GUIStyle.Orange, textAlignment: Alignment.Center, color: Color.Black, style: "OuterGlow", wrap: true)
@@ -164,7 +165,7 @@ namespace Barotrauma.Items.Components
}
}
}
activateButton.Enabled = outputsFound;
activateButton.Enabled = outputsFound || !InputContainer.Inventory.IsEmpty();
activateButton.Text = TextManager.Get(ActivateButtonText);
};
}
@@ -236,8 +237,19 @@ namespace Barotrauma.Items.Components
inSufficientPowerWarning.Visible = IsActive && !hasPower;
}
private bool ToggleActive(GUIButton button, object obj)
private bool OnActivateButtonClicked(GUIButton button, object obj)
{
var disallowedItem = inputContainer.Inventory.FindItem(i => !i.AllowDeconstruct, recursive: false);
if (disallowedItem != null)
{
int index = inputContainer.Inventory.FindIndex(disallowedItem);
if (index >= 0 && index < inputContainer.Inventory.visualSlots.Length)
{
var slot = inputContainer.Inventory.visualSlots[index];
slot?.ShowBorderHighlight(GUIStyle.Red, 0.1f, 0.9f);
}
return true;
}
if (GameMain.Client != null)
{
pendingState = !IsActive;
@@ -247,7 +259,6 @@ namespace Barotrauma.Items.Components
{
SetActive(!IsActive, Character.Controlled);
}
return true;
}
@@ -1367,6 +1367,15 @@ namespace Barotrauma.Items.Components
pingRadius, prevPingRadius,
250.0f, 150.0f, range, pingStrength, passive);
}
if (pingSource.Y - Level.Loaded.BottomPos < range)
{
CreateBlipsForLine(
new Vector2(pingSource.X - range, Level.Loaded.BottomPos),
new Vector2(pingSource.X + range, Level.Loaded.BottomPos),
pingSource, transducerPos,
pingRadius, prevPingRadius,
250.0f, 150.0f, range, pingStrength, passive);
}
List<Voronoi2.VoronoiCell> cells = Level.Loaded.GetCells(pingSource, 7);
foreach (Voronoi2.VoronoiCell cell in cells)
@@ -23,10 +23,10 @@ namespace Barotrauma.Items.Components
}
#endif
private List<ParticleEmitter> particleEmitters = new List<ParticleEmitter>();
private List<ParticleEmitter> particleEmitterHitStructure = new List<ParticleEmitter>();
private List<ParticleEmitter> particleEmitterHitCharacter = new List<ParticleEmitter>();
private List<Pair<RelatedItem, ParticleEmitter>> particleEmitterHitItem = new List<Pair<RelatedItem, ParticleEmitter>>();
private readonly List<ParticleEmitter> particleEmitters = new List<ParticleEmitter>();
private readonly List<ParticleEmitter> particleEmitterHitStructure = new List<ParticleEmitter>();
private readonly List<ParticleEmitter> particleEmitterHitCharacter = new List<ParticleEmitter>();
private readonly List<(RelatedItem relatedItem, ParticleEmitter emitter)> particleEmitterHitItem = new List<(RelatedItem relatedItem, ParticleEmitter emitter)>();
private float prevProgressBarState;
private Item prevProgressBarTarget = null;
@@ -46,10 +46,7 @@ namespace Barotrauma.Items.Components
Identifier[] excludedIdentifiers = subElement.GetAttributeIdentifierArray("excludedidentifiers", Array.Empty<Identifier>());
if (excludedIdentifiers.Length == 0) { excludedIdentifiers = subElement.GetAttributeIdentifierArray("excludedidentifier", Array.Empty<Identifier>()); }
particleEmitterHitItem.Add(
new Pair<RelatedItem, ParticleEmitter>(
new RelatedItem(identifiers, excludedIdentifiers),
new ParticleEmitter(subElement)));
particleEmitterHitItem.Add((new RelatedItem(identifiers, excludedIdentifiers), new ParticleEmitter(subElement)));
break;
case "particleemitterhitstructure":
particleEmitterHitStructure.Add(new ParticleEmitter(subElement));
@@ -139,11 +136,11 @@ namespace Barotrauma.Items.Components
Vector2 particlePos = ConvertUnits.ToDisplayUnits(pickedPosition);
if (targetItem.Submarine != null) particlePos += targetItem.Submarine.DrawPosition;
foreach (var emitter in particleEmitterHitItem)
foreach ((RelatedItem relatedItem, ParticleEmitter emitter) in particleEmitterHitItem)
{
if (!emitter.First.MatchesItem(targetItem)) { continue; }
if (!relatedItem.MatchesItem(targetItem)) { continue; }
float particleAngle = item.body.Rotation + MathHelper.ToRadians(BarrelRotation) + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
emitter.Second.Emit(deltaTime, particlePos, item.CurrentHull, particleAngle + MathHelper.Pi, -particleAngle + MathHelper.Pi);
emitter.Emit(deltaTime, particlePos, item.CurrentHull, particleAngle + MathHelper.Pi, -particleAngle + MathHelper.Pi);
}
}
#if DEBUG
@@ -38,7 +38,7 @@ namespace Barotrauma.Items.Components
int totalWireCount = 0;
foreach (Connection c in panel.Connections)
{
totalWireCount += c.Wires.Count(w => w != null);
totalWireCount += c.Wires.Count;
}
Wire equippedWire = null;
@@ -87,8 +87,8 @@ namespace Barotrauma.Items.Components
(DraggingConnected.Connections[0] == null && DraggingConnected.Connections[1] == null) ||
(DraggingConnected.Connections.Contains(c) && DraggingConnected.Connections.Contains(null)))
{
int linkIndex = c.FindWireIndex(DraggingConnected.Item);
if (linkIndex > -1 || panel.DisconnectedWires.Contains(DraggingConnected))
var linkedWire = c.FindWireByItem(DraggingConnected.Item);
if (linkedWire != null || panel.DisconnectedWires.Contains(DraggingConnected))
{
Inventory.DraggingItems.Clear();
Inventory.DraggingItems.Add(DraggingConnected.Item);
@@ -108,7 +108,7 @@ namespace Barotrauma.Items.Components
c.DrawWires(spriteBatch, panel, rightPos, rightWirePos, mouseInRect, equippedWire, wireInterval);
}
rightPos.Y += connectorIntervalLeft;
rightWirePos.Y += c.Wires.Count(w => w != null) * wireInterval;
rightWirePos.Y += c.Wires.Count * wireInterval;
}
else
{
@@ -121,7 +121,7 @@ namespace Barotrauma.Items.Components
c.DrawWires(spriteBatch, panel, leftPos, leftWirePos, mouseInRect, equippedWire, wireInterval);
}
leftPos.Y += connectorIntervalRight;
leftWirePos.Y += c.Wires.Count(w => w != null) * wireInterval;
leftWirePos.Y += c.Wires.Count * wireInterval;
}
}
}
@@ -228,15 +228,15 @@ namespace Barotrauma.Items.Components
{
float connectorSpriteScale = (35.0f / connectionSprite.SourceRect.Width) * panel.Scale;
for (int i = 0; i < MaxWires; i++)
foreach (var wire in wires)
{
if (wires[i] == null || wires[i].Hidden || (DraggingConnected == wires[i] && (mouseIn || Screen.Selected == GameMain.SubEditorScreen))) { continue; }
if (wires[i].HiddenInGame && Screen.Selected == GameMain.GameScreen) { continue; }
if (wire.Hidden || (DraggingConnected == wire && (mouseIn || Screen.Selected == GameMain.SubEditorScreen))) { continue; }
if (wire.HiddenInGame && Screen.Selected == GameMain.GameScreen) { continue; }
Connection recipient = wires[i].OtherConnection(this);
Connection recipient = wire.OtherConnection(this);
LocalizedString label = recipient == null ? "" : recipient.item.Name + $" ({recipient.DisplayName})";
if (wires[i].Locked) { label += "\n" + TextManager.Get("ConnectionLocked"); }
DrawWire(spriteBatch, wires[i], position, wirePosition, equippedWire, panel, label);
if (wire.Locked) { label += "\n" + TextManager.Get("ConnectionLocked"); }
DrawWire(spriteBatch, wire, position, wirePosition, equippedWire, panel, label);
wirePosition.Y += wireInterval;
}
@@ -248,18 +248,17 @@ namespace Barotrauma.Items.Components
if (!PlayerInput.PrimaryMouseButtonHeld())
{
if ((GameMain.NetworkMember != null || panel.CheckCharacterSuccess(Character.Controlled)) &&
Wires.Count(w => w != null) < MaxPlayerConnectableWires)
Wires.Count < MaxPlayerConnectableWires)
{
//find an empty cell for the new connection
int index = FindEmptyIndex();
if (index > -1 && !Wires.Contains(DraggingConnected))
if (WireSlotsAvailable() && !Wires.Contains(DraggingConnected))
{
bool alreadyConnected = DraggingConnected.IsConnectedTo(panel.Item);
DraggingConnected.RemoveConnection(panel.Item);
if (DraggingConnected.Connect(this, !alreadyConnected, true))
{
var otherConnection = DraggingConnected.OtherConnection(this);
SetWire(index, DraggingConnected);
ConnectWire(DraggingConnected);
}
}
}
@@ -284,7 +283,7 @@ namespace Barotrauma.Items.Components
flashColor * (float)Math.Sin(FlashTimer % flashCycleDuration / flashCycleDuration * MathHelper.Pi * 0.8f), scale: connectorSpriteScale);
}
if (Wires.Any(w => w != null && w != DraggingConnected && !w.Hidden && (!w.HiddenInGame || Screen.Selected != GameMain.GameScreen)))
if (Wires.Any(w => w != DraggingConnected && !w.Hidden && (!w.HiddenInGame || Screen.Selected != GameMain.GameScreen)))
{
int screwIndex = (int)Math.Floor(position.Y / 30.0f) % screwSprites.Count;
screwSprites[screwIndex].Draw(spriteBatch, position, scale: connectorSpriteScale);
@@ -77,7 +77,7 @@ namespace Barotrauma.Items.Components
}
}
public override void Move(Vector2 amount)
public override void Move(Vector2 amount, bool ignoreContacts = false)
{
if (item.Submarine == null || item.Submarine.Loading || Screen.Selected != GameMain.SubEditorScreen) { return; }
MoveConnectedWires(amount);
@@ -173,9 +173,8 @@ namespace Barotrauma.Items.Components
private void ApplyRemoteState(IReadMessage msg)
{
List<Wire> prevWires = Connections.SelectMany(c => c.Wires.Where(w => w != null)).ToList();
List<Wire> newWires = new List<Wire>();
List<Wire> prevWires = Connections.SelectMany(c => c.Wires).ToList();
ushort userID = msg.ReadUInt16();
if (userID == 0)
@@ -195,7 +194,9 @@ namespace Barotrauma.Items.Components
foreach (Connection connection in Connections)
{
for (int i = 0; i < connection.MaxWires; i++)
HashSet<Wire> newWires = new HashSet<Wire>();
uint wireCount = msg.ReadVariableUInt32();
for (int i = 0; i < wireCount; i++)
{
ushort wireId = msg.ReadUInt16();
@@ -204,9 +205,18 @@ namespace Barotrauma.Items.Components
if (wireComponent == null) { continue; }
newWires.Add(wireComponent);
}
connection.SetWire(i, wireComponent);
wireComponent.Connect(connection, false);
Wire[] oldWires = connection.Wires.Where(w => !newWires.Contains(w)).ToArray();
foreach (var wire in oldWires)
{
connection.DisconnectWire(wire);
}
foreach (var wire in newWires.Where(w => !connection.Wires.Contains(w)).ToArray())
{
connection.ConnectWire(wire);
wire.Connect(connection, false);
}
}
@@ -205,7 +205,7 @@ namespace Barotrauma.Items.Components
foreach (var uiElement in uiElements)
{
if (!(uiElement.UserData is CustomInterfaceElement element)) { continue; }
bool visible = Screen.Selected == GameMain.SubEditorScreen || element.StatusEffects.Any() || element.HasPropertyName || (element.Connection != null && element.Connection.Wires.Any(w => w != null));
bool visible = Screen.Selected == GameMain.SubEditorScreen || element.StatusEffects.Any() || element.HasPropertyName || (element.Connection != null && element.Connection.Wires.Count > 0);
if (visible) { visibleElementCount++; }
if (uiElement.Visible != visible)
{
@@ -526,7 +526,7 @@ namespace Barotrauma.Items.Components
}
}
public override void Move(Vector2 amount)
public override void Move(Vector2 amount, bool ignoreContacts = false)
{
//only used in the sub editor, hence only in the client project
if (!item.IsSelected) { return; }
@@ -175,7 +175,7 @@ namespace Barotrauma.Items.Components
};
}
public override void Move(Vector2 amount)
public override void Move(Vector2 amount, bool ignoreContacts = false)
{
widgets.Clear();
}
@@ -6,8 +6,10 @@ using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma.Items.Components
{
partial class DockingPort : ItemComponent, IDrawableComponent, IServerSerializable
partial class DockingPort : ItemComponent, IDrawableComponent, IServerSerializable, IClientSerializable
{
private GUIMessageBox autodockingVerification;
public Vector2 DrawSize
{
//use the extents of the item as the draw size
@@ -180,5 +182,10 @@ namespace Barotrauma.Items.Components
Undock();
}
}
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
{
msg.Write((byte)allowOutpostAutoDocking);
}
}
}
@@ -265,7 +265,7 @@ namespace Barotrauma
else
{
LocalizedString description = item.Description;
if (item.Prefab.Identifier == "idcard" || item.Tags.Contains("despawncontainer"))
if (item.HasTag("identitycard") || item.HasTag("despawncontainer"))
{
string[] readTags = item.Tags.Split(',');
string idName = null;
@@ -1,4 +1,6 @@
using Barotrauma.Items.Components;
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using Barotrauma.MapCreatures.Behavior;
using Barotrauma.Networking;
using FarseerPhysics;
using Microsoft.Xna.Framework;
@@ -6,13 +8,8 @@ using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using Barotrauma.Extensions;
using Barotrauma.MapCreatures.Behavior;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Contacts;
using System.Collections.Immutable;
using System.Linq;
namespace Barotrauma
{
@@ -720,7 +717,7 @@ namespace Barotrauma
//remove identifiers from the available container tags
//(otherwise the list will include many irrelevant options,
//e.g. "weldingtool" because a welding fuel tank can be placed inside the container, etc)
.Where(t => !ItemPrefab.Prefabs.Any(ip => ip.Identifier == t))
.Where(t => !ItemPrefab.Prefabs.ContainsKey(t))
.ToImmutableHashSet();
new GUIButton(new RectTransform(new Vector2(0.1f, 1), tagsField.RectTransform, Anchor.TopRight), "...")
{
@@ -1174,7 +1171,7 @@ namespace Barotrauma
texts.Clear();
string nameText = Name;
if (Prefab.Identifier == "idcard" || Tags.Contains("despawncontainer"))
if (Prefab.Tags.Contains("identitycard") || Tags.Contains("despawncontainer"))
{
string[] readTags = Tags.Split(',');
string idName = null;
@@ -80,15 +80,17 @@ namespace Barotrauma
}
Vector2 center = new Vector2((minX + maxX) / 2.0f, (minY + maxY) / 2.0f);
if (Submarine.MainSub != null) { center -= Submarine.MainSub.HiddenSubPosition; }
center.X -= MathUtils.RoundTowardsClosest(center.X, Submarine.GridSize.X);
center.Y -= MathUtils.RoundTowardsClosest(center.Y, Submarine.GridSize.Y);
Vector2 offsetFromGrid = new Vector2(
MathUtils.RoundTowardsClosest(center.X, Submarine.GridSize.X) - center.X,
MathUtils.RoundTowardsClosest(center.Y, Submarine.GridSize.Y) - center.Y - Submarine.GridSize.Y / 2);
MapEntity.SelectedList.Clear();
assemblyEntities.ForEach(e => MapEntity.AddSelection(e));
foreach (MapEntity mapEntity in assemblyEntities)
{
mapEntity.Move(-center);
mapEntity.Move(-center - offsetFromGrid);
mapEntity.Submarine = Submarine.MainSub;
var entityElement = mapEntity.Save(element);
if (disabledEntities.Contains(mapEntity))
@@ -461,7 +461,7 @@ namespace Barotrauma.Lights
Matrix.CreateTranslation(-origin.X, -origin.Y, 0.0f) *
Matrix.CreateRotationZ(amount) *
Matrix.CreateTranslation(origin.X, origin.Y, 0.0f);
SetVertices(vertices.Select(v => v.Pos).ToArray(), rotationMatrix);
SetVertices(vertices.Select(v => v.Pos).ToArray(), rotationMatrix: rotationMatrix);
}
private void CalculateDimensions()
@@ -541,7 +541,7 @@ namespace Barotrauma.Lights
}
}
public void SetVertices(Vector2[] points, Matrix? rotationMatrix = null)
public void SetVertices(Vector2[] points, bool mergeOverlappingSegments = true, Matrix? rotationMatrix = null)
{
Debug.Assert(points.Length == 4, "Only rectangular convex hulls are supported");
@@ -594,13 +594,16 @@ namespace Barotrauma.Lights
if (ParentEntity == null) { return; }
var chList = HullLists.Find(h => h.Submarine == ParentEntity.Submarine);
if (chList != null)
if (mergeOverlappingSegments)
{
overlappingHulls.Clear();
foreach (ConvexHull ch in chList.List)
var chList = HullLists.Find(h => h.Submarine == ParentEntity.Submarine);
if (chList != null)
{
MergeOverlappingSegments(ch);
overlappingHulls.Clear();
foreach (ConvexHull ch in chList.List)
{
MergeOverlappingSegments(ch);
}
}
}
}
@@ -81,11 +81,18 @@ namespace Barotrauma
}
catch (System.IO.FileNotFoundException e)
{
string errorMsg = "Failed to load sound file \"" + filename + "\".";
string errorMsg = "Failed to load sound file \"" + filename + "\" (file not found).";
DebugConsole.ThrowError(errorMsg, e);
GameAnalyticsManager.AddErrorEventOnce("RoundSound.LoadRoundSound:FileNotFound" + filename, GameAnalyticsManager.ErrorSeverity.Error, errorMsg + "\n" + Environment.StackTrace.CleanupStackTrace());
return null;
}
catch (System.IO.InvalidDataException e)
{
string errorMsg = "Failed to load sound file \"" + filename + "\" (invalid data).";
DebugConsole.ThrowError(errorMsg, e);
GameAnalyticsManager.AddErrorEventOnce("RoundSound.LoadRoundSound:InvalidData" + filename, GameAnalyticsManager.ErrorSeverity.Error, errorMsg + "\n" + Environment.StackTrace.CleanupStackTrace());
return null;
}
}
RoundSound newSound = new RoundSound(element, existingSound);
@@ -525,7 +525,7 @@ namespace Barotrauma
Item.ItemList.Count(it2 => it2.linkedTo.Contains(item) && !item.linkedTo.Contains(it2));
for (int i = 0; i < item.Connections.Count; i++)
{
int wireCount = item.Connections[i].Wires.Count(w => w != null);
int wireCount = item.Connections[i].Wires.Count;
if (doorLinks + wireCount > item.Connections[i].MaxWires)
{
errorMsgs.Add(TextManager.GetWithVariables("InsufficientFreeConnectionsWarning",
@@ -1,4 +1,5 @@
using Microsoft.Xna.Framework;
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using System;
namespace Barotrauma.Networking
@@ -183,6 +184,11 @@ namespace Barotrauma.Networking
break;
default:
GameMain.Client.AddChatMessage(txt, type, senderName, senderClient, senderCharacter, changeType, textColor: textColor);
if (type == ChatMessageType.Radio && CanUseRadio(senderCharacter, out WifiComponent radio))
{
Signal s = new Signal(txt, sender: senderCharacter, source: radio.Item);
radio.TransmitSignal(s, sentFromChat: true);
}
break;
}
LastID = id;
@@ -3,6 +3,7 @@ using Barotrauma.Steam;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using Barotrauma.IO;
using System.IO.Compression;
using System.Linq;
@@ -11,6 +12,7 @@ using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using Barotrauma.Extensions;
using Microsoft.Xna.Framework.Input;
namespace Barotrauma.Networking
{
@@ -182,6 +184,20 @@ namespace Barotrauma.Networking
get { return ownerKey > 0 || steamP2POwner; }
}
internal readonly struct PermissionChangedEvent
{
public readonly ClientPermissions NewPermissions;
public readonly ImmutableArray<string> NewPermittedConsoleCommands;
public PermissionChangedEvent(ClientPermissions newPermissions, IReadOnlyList<string> newPermittedConsoleCommands)
{
NewPermissions = newPermissions;
NewPermittedConsoleCommands = newPermittedConsoleCommands.ToImmutableArray();
}
}
public readonly NamedEvent<PermissionChangedEvent> OnPermissionChanged = new NamedEvent<PermissionChangedEvent>();
public GameClient(string newName, string ip, UInt64 steamId, string serverName = null, int ownerKey = 0, bool steamP2POwner = false)
{
//TODO: gui stuff should probably not be here?
@@ -570,7 +586,12 @@ namespace Barotrauma.Networking
public override void Update(float deltaTime)
{
#if DEBUG
if (PlayerInput.GetKeyboardState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.P)) return;
if (PlayerInput.GetKeyboardState.IsKeyDown(Keys.P)) return;
if (PlayerInput.KeyHit(Keys.Home))
{
OnPermissionChanged.Invoke(new PermissionChangedEvent(permissions, permittedConsoleCommands));
}
#endif
foreach (Client c in ConnectedClients)
@@ -1019,40 +1040,25 @@ namespace Barotrauma.Networking
GameMain.GameSession.EnforceMissionOrder(serverMissionIdentifiers);
}
byte equalityCheckValueCount = inc.ReadByte();
List<int> levelEqualityCheckValues = new List<int>();
for (int i = 0; i < equalityCheckValueCount; i++)
var levelEqualityCheckValues = new Dictionary<Level.LevelGenStage, int>();
foreach (Level.LevelGenStage stage in Enum.GetValues(typeof(Level.LevelGenStage)).OfType<Level.LevelGenStage>().OrderBy(s => s))
{
levelEqualityCheckValues.Add(inc.ReadInt32());
levelEqualityCheckValues.Add(stage, inc.ReadInt32());
}
if (Level.Loaded.EqualityCheckValues.Count != levelEqualityCheckValues.Count)
foreach (var stage in levelEqualityCheckValues.Keys)
{
string errorMsg = "Level equality check failed. The level generated at your end doesn't match the level generated by the server" +
" (client value count: " + Level.Loaded.EqualityCheckValues.Count +
", level value count: " + levelEqualityCheckValues.Count +
", seed: " + Level.Loaded.Seed +
", sub: " + Submarine.MainSub.Info.Name + " (" + Submarine.MainSub.Info.MD5Hash.ShortRepresentation + ")" +
", mirrored: " + Level.Loaded.Mirrored + ").";
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:LevelsDontMatch" + Level.Loaded.Seed, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
throw new Exception(errorMsg);
}
else
{
for (int i = 0; i < equalityCheckValueCount; i++)
if (Level.Loaded.EqualityCheckValues[stage] != levelEqualityCheckValues[stage])
{
if (Level.Loaded.EqualityCheckValues[i] != levelEqualityCheckValues[i])
{
string errorMsg = "Level equality check failed. The level generated at your end doesn't match the level generated by the server" +
" (client value #" + i + ": " + Level.Loaded.EqualityCheckValues[i] +
", server value #" + i + ": " + levelEqualityCheckValues[i].ToString("X") +
", level value count: " + levelEqualityCheckValues.Count +
", seed: " + Level.Loaded.Seed +
", sub: " + Submarine.MainSub.Info.Name + " (" + Submarine.MainSub.Info.MD5Hash.ShortRepresentation + ")" +
", mirrored: " + Level.Loaded.Mirrored + ").";
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:LevelsDontMatch" + Level.Loaded.Seed, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
throw new Exception(errorMsg);
}
string errorMsg = "Level equality check failed. The level generated at your end doesn't match the level generated by the server" +
" (client value " + stage + ": " + Level.Loaded.EqualityCheckValues[stage].ToString("X") +
", server value " + stage + ": " + levelEqualityCheckValues[stage].ToString("X") +
", level value count: " + levelEqualityCheckValues.Count +
", seed: " + Level.Loaded.Seed +
", sub: " + Submarine.MainSub.Info.Name + " (" + Submarine.MainSub.Info.MD5Hash.ShortRepresentation + ")" +
", mirrored: " + Level.Loaded.Mirrored + ").";
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:LevelsDontMatch" + Level.Loaded.Seed, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
throw new Exception(errorMsg);
}
}
@@ -1076,6 +1082,7 @@ namespace Barotrauma.Networking
reconnectBox?.Close();
reconnectBox = null;
GameMain.ModDownloadScreen.Reset();
ContentPackageManager.EnabledPackages.Restore();
GUI.ClearCursorWait();
@@ -1380,18 +1387,13 @@ namespace Barotrauma.Networking
private void SetMyPermissions(ClientPermissions newPermissions, IEnumerable<string> permittedConsoleCommands)
{
if (!(this.permittedConsoleCommands.Any(c => !permittedConsoleCommands.Contains(c)) ||
permittedConsoleCommands.Any(c => !this.permittedConsoleCommands.Contains(c))))
permittedConsoleCommands.Any(c => !this.permittedConsoleCommands.Contains(c))))
{
if (newPermissions == permissions) return;
}
bool refreshCampaignUI = false;
if (permissions.HasFlag(ClientPermissions.ManageCampaign) != newPermissions.HasFlag(ClientPermissions.ManageCampaign) ||
permissions.HasFlag(ClientPermissions.ManageRound) != newPermissions.HasFlag(ClientPermissions.ManageRound))
{
refreshCampaignUI = true;
}
bool refreshCampaignUI = permissions.HasFlag(ClientPermissions.ManageCampaign) != newPermissions.HasFlag(ClientPermissions.ManageCampaign) ||
permissions.HasFlag(ClientPermissions.ManageRound) != newPermissions.HasFlag(ClientPermissions.ManageRound);
permissions = newPermissions;
this.permittedConsoleCommands = new List<string>(permittedConsoleCommands);
@@ -1430,7 +1432,7 @@ namespace Barotrauma.Networking
if (newPermissions.HasFlag(ClientPermissions.ConsoleCommands))
{
var commandsLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), rightColumn.RectTransform),
TextManager.Get("PermittedConsoleCommands"), wrap: true, font: GUIStyle.SubHeadingFont);
TextManager.Get("PermittedConsoleCommands"), wrap: true, font: GUIStyle.SubHeadingFont);
var commandList = new GUIListBox(new RectTransform(new Vector2(1.0f, 1.0f), rightColumn.RectTransform));
foreach (string permittedCommand in permittedConsoleCommands)
{
@@ -1469,6 +1471,7 @@ namespace Barotrauma.Networking
}
GameMain.NetLobbyScreen.RefreshEnabledElements();
OnPermissionChanged.Invoke(new PermissionChangedEvent(permissions, this.permittedConsoleCommands));
}
private IEnumerable<CoroutineStatus> StartGame(IReadMessage inc)
@@ -3680,7 +3683,9 @@ namespace Barotrauma.Networking
}
if (Level.Loaded != null)
{
errorLines.Add("Level: " + Level.Loaded.Seed + ", " + string.Join(", ", Level.Loaded.EqualityCheckValues.Select(cv => cv.ToString("X"))));
errorLines.Add("Level: " + Level.Loaded.Seed + ", "
+ string.Join("; ", Level.Loaded.EqualityCheckValues.Select(cv
=> cv.Key + "=" + cv.Value.ToString("X"))));
errorLines.Add("Entity count before generating level: " + Level.Loaded.EntityCountBeforeGenerate);
errorLines.Add("Entities:");
foreach (Entity e in Level.Loaded.EntitiesBeforeGenerate.OrderBy(e => e.CreationIndex))
@@ -621,7 +621,7 @@ namespace Barotrauma.Networking
{
Stretch = true
};
var losModeRadioButtonGroup = new GUIRadioButtonGroup();
LosMode[] losModes = (LosMode[])Enum.GetValues(typeof(LosMode));
for (int i = 0; i < losModes.Length; i++)
@@ -634,6 +634,14 @@ namespace Barotrauma.Networking
var traitorsMinPlayerCount = CreateLabeledNumberInput(roundsTab, "ServerSettingsTraitorsMinPlayerCount", 1, 16, "ServerSettingsTraitorsMinPlayerCountToolTip");
GetPropertyData(nameof(TraitorsMinPlayerCount)).AssignGUIComponent(traitorsMinPlayerCount);
var maximumTransferAmount = CreateLabeledNumberInput(roundsTab, "serversettingsmaximumtransferrequest", 0, CampaignMode.MaxMoney, "serversettingsmaximumtransferrequesttooltip");
GetPropertyData(nameof(MaximumTransferRequest)).AssignGUIComponent(maximumTransferAmount);
var lootedMoneyDestination = CreateLabeledDropdown(roundsTab, "serversettingslootedmoneydestination", numElements: 2, "serversettingslootedmoneydestinationtooltip");
lootedMoneyDestination.AddItem(TextManager.Get("lootedmoneydestination.bank"), LootedMoneyDestination.Bank);
lootedMoneyDestination.AddItem(TextManager.Get("lootedmoneydestination.wallet"), LootedMoneyDestination.Wallet);
GetPropertyData(nameof(LootedMoneyDestination)).AssignGUIComponent(lootedMoneyDestination);
var ragdollButtonBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), TextManager.Get("ServerSettingsAllowRagdollButton"));
GetPropertyData(nameof(AllowRagdollButton)).AssignGUIComponent(ragdollButtonBox);
@@ -991,6 +999,32 @@ namespace Barotrauma.Networking
return input;
}
private GUIDropDown CreateLabeledDropdown(GUIComponent parent, string labelTag, int numElements, string toolTipTag = null)
{
var container = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), parent.RectTransform), isHorizontal: true)
{
Stretch = true,
RelativeSpacing = 0.05f,
ToolTip = TextManager.Get(labelTag)
};
var label = new GUITextBlock(new RectTransform(new Vector2(0.7f, 1.0f), container.RectTransform),
TextManager.Get(labelTag), textAlignment: Alignment.CenterLeft, font: GUIStyle.SmallFont)
{
AutoScaleHorizontal = true
};
if (!string.IsNullOrEmpty(toolTipTag))
{
label.ToolTip = TextManager.Get(toolTipTag);
}
var input = new GUIDropDown(new RectTransform(new Vector2(0.3f, 1.0f), container.RectTransform), elementCount: numElements);
container.RectTransform.MinSize = new Point(0, input.RectTransform.MinSize.Y);
container.RectTransform.MaxSize = new Point(int.MaxValue, input.RectTransform.MaxSize.Y);
return input;
}
private bool SelectSettingsTab(GUIButton button, object obj)
{
settingsTabIndex = (int)obj;
@@ -103,7 +103,7 @@ namespace Barotrauma.Particles
{
return debugName;
}
public void Init(ParticlePrefab prefab, Vector2 position, Vector2 speed, float rotation, Hull hullGuess = null, bool drawOnTop = false, float collisionIgnoreTimer = 0f, Tuple<Vector2, Vector2> tracerPoints = null)
public void Init(ParticlePrefab prefab, Vector2 position, Vector2 speed, float rotation, Hull hullGuess = null, bool drawOnTop = false, float collisionIgnoreTimer = 0f, float lifeTimeMultiplier = 1f, Tuple<Vector2, Vector2> tracerPoints = null)
{
this.prefab = prefab;
#if DEBUG
@@ -149,13 +149,13 @@ namespace Barotrauma.Particles
if (prefab.LifeTimeMin <= 0.0f)
{
totalLifeTime = prefab.LifeTime;
lifeTime = prefab.LifeTime;
totalLifeTime = prefab.LifeTime * lifeTimeMultiplier;
lifeTime = prefab.LifeTime * lifeTimeMultiplier;
}
else
{
totalLifeTime = Rand.Range(prefab.LifeTimeMin, prefab.LifeTime);
lifeTime = totalLifeTime;
totalLifeTime = Rand.Range(prefab.LifeTimeMin, prefab.LifeTime) * lifeTimeMultiplier;
lifeTime = totalLifeTime * lifeTimeMultiplier;
}
startDelay = Rand.Range(prefab.StartDelayMin, prefab.StartDelayMax);
@@ -85,6 +85,9 @@ namespace Barotrauma.Particles
[Editable, Serialize("1,1,1,1", IsPropertySaveable.Yes)]
public Color ColorMultiplier { get; set; }
[Editable, Serialize(1f, IsPropertySaveable.Yes)]
public float LifeTimeMultiplier { get; set; }
[Editable, Serialize(false, IsPropertySaveable.Yes)]
public bool DrawOnTop { get; set; }
@@ -197,7 +200,7 @@ namespace Barotrauma.Particles
position += dir * Rand.Range(Prefab.Properties.DistanceMin, Prefab.Properties.DistanceMax);
}
var particle = GameMain.ParticleManager.CreateParticle(particlePrefab, position, velocity, particleRotation, hullGuess, Prefab.DrawOnTop, tracerPoints: tracerPoints);
var particle = GameMain.ParticleManager.CreateParticle(particlePrefab, position, velocity, particleRotation, hullGuess, Prefab.DrawOnTop, lifeTimeMultiplier: Prefab.Properties.LifeTimeMultiplier, tracerPoints: tracerPoints);
if (particle != null)
{
@@ -76,7 +76,7 @@ namespace Barotrauma.Particles
return CreateParticle(prefab, position, velocity, rotation, hullGuess, collisionIgnoreTimer: collisionIgnoreTimer, tracerPoints:tracerPoints);
}
public Particle CreateParticle(ParticlePrefab prefab, Vector2 position, Vector2 velocity, float rotation = 0.0f, Hull hullGuess = null, bool drawOnTop = false, float collisionIgnoreTimer = 0f, Tuple<Vector2, Vector2> tracerPoints = null)
public Particle CreateParticle(ParticlePrefab prefab, Vector2 position, Vector2 velocity, float rotation = 0.0f, Hull hullGuess = null, bool drawOnTop = false, float collisionIgnoreTimer = 0f, float lifeTimeMultiplier = 1f, Tuple<Vector2, Vector2> tracerPoints = null)
{
if (prefab == null || prefab.Sprites.Count == 0) { return null; }
@@ -115,7 +115,7 @@ namespace Barotrauma.Particles
if (particles[particleCount] == null) { particles[particleCount] = new Particle(); }
particles[particleCount].Init(prefab, position, velocity, rotation, hullGuess, drawOnTop, collisionIgnoreTimer, tracerPoints: tracerPoints);
particles[particleCount].Init(prefab, position, velocity, rotation, hullGuess, drawOnTop, collisionIgnoreTimer, lifeTimeMultiplier, tracerPoints: tracerPoints);
particleCount++;
@@ -77,9 +77,20 @@ namespace Barotrauma
if (remoteContentDoc?.Root != null)
{
remoteContentContainer.ClearChildren();
foreach (var subElement in remoteContentDoc.Root.Elements())
try
{
GUIComponent.FromXML(subElement.FromPackage(null), remoteContentContainer.RectTransform);
foreach (var subElement in remoteContentDoc.Root.Elements())
{
GUIComponent.FromXML(subElement.FromPackage(null), remoteContentContainer.RectTransform);
}
}
catch (Exception e)
{
#if DEBUG
DebugConsole.ThrowError("Reading received remote main menu content failed.", e);
#endif
GameAnalyticsManager.AddErrorEventOnce("MainMenuScreen.RemoteContentParse:Exception", GameAnalyticsManager.ErrorSeverity.Error,
"Reading received remote main menu content failed. " + e.Message);
}
}
};
@@ -20,10 +20,11 @@ namespace Barotrauma
private ServerContentPackage? currentDownload;
private readonly List<ContentPackage> downloadedPackages = new List<ContentPackage>();
public IEnumerable<ContentPackage> DownloadedPackages => downloadedPackages;
private bool confirmDownload;
private void Reset()
public void Reset()
{
pendingDownloads.Clear();
downloadedPackages.Clear();
@@ -255,12 +256,6 @@ namespace Barotrauma
}
}
public override void Deselect()
{
Reset();
base.Deselect();
}
public override void Update(double deltaTime)
{
base.Update(deltaTime);
@@ -1400,7 +1400,7 @@ namespace Barotrauma
public void CreatePlayerFrame(GUIComponent parent, bool createPendingText = true, bool alwaysAllowEditing = false)
{
UpdatePlayerFrame(
Character.Controlled?.Info ?? playerInfoContainer.Children?.First().UserData as CharacterInfo,
Character.Controlled?.Info ?? playerInfoContainer.Children?.First().UserData as CharacterInfo ?? GameMain.Client.CharacterInfo,
allowEditing: alwaysAllowEditing || campaignCharacterInfo == null,
parent: parent,
createPendingText: createPendingText);
@@ -3131,10 +3131,11 @@ namespace Barotrauma
retVal[i] = new GUIImage[outfitPreview.Sprites.Count];
for (int j = 0; j < outfitPreview.Sprites.Count; j++)
{
Pair<Sprite, Vector2> sprite = outfitPreview.Sprites[j];
Sprite sprite = outfitPreview.Sprites[j].sprite;
Vector2 drawOffset = outfitPreview.Sprites[j].drawOffset;
float aspectRatio = outfitPreview.Dimensions.Y / outfitPreview.Dimensions.X;
retVal[i][j] = new GUIImage(new RectTransform(new Vector2(0.7f / aspectRatio, 0.7f), innerFrame.RectTransform, Anchor.Center)
{ RelativeOffset = sprite.Second / outfitPreview.Dimensions }, sprite.First, scaleToFit: true)
{ RelativeOffset = drawOffset / outfitPreview.Dimensions }, sprite, scaleToFit: true)
{
PressedColor = Color.White,
CanBeFocused = false
@@ -1190,18 +1190,23 @@ namespace Barotrauma
frame.RectTransform.MaxSize = new Point(int.MaxValue, frame.Rect.Width);
LocalizedString name = legacy ? TextManager.GetWithVariable("legacyitemformat", "[name]", ep.Name) : ep.Name;
frame.ToolTip = ep.Description.IsNullOrEmpty() ? name : name + '\n' + ep.Description;
frame.ToolTip = $"{frame.ToolTip}\n‖color:{XMLExtensions.ToStringHex(GUIStyle.TextColorBright)}‖{name}‖color:end‖";
if (!ep.Description.IsNullOrEmpty())
{
frame.ToolTip += '\n' + ep.Description;
}
if (ep.ContentPackage != GameMain.VanillaContent && ep.ContentPackage != null)
{
frame.Color = Color.Magenta;
frame.ToolTip = RichString.Rich($"{frame.ToolTip}\n‖color:{XMLExtensions.ToStringHex(Color.MediumPurple)}‖{ep.ContentPackage?.Name}‖color:end‖");
frame.ToolTip = $"{frame.ToolTip}\n‖color:{XMLExtensions.ToStringHex(Color.MediumPurple)}‖{ep.ContentPackage?.Name}‖color:end‖";
}
if (ep.HideInMenus)
{
frame.Color = Color.Red;
name = "[HIDDEN] " + name;
}
frame.ToolTip = RichString.Rich(frame.ToolTip);
GUILayoutGroup paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 0.8f), frame.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter)
{
@@ -1976,7 +1981,7 @@ namespace Barotrauma
return true;
};
nameBox.Text = subNameLabel?.Text?.SanitizedValue ?? "";
nameBox.Text = MainSub?.Info.Name ?? "";
submarineNameCharacterCount.Text = nameBox.Text.Length + " / " + submarineNameLimit;
@@ -2750,6 +2755,8 @@ namespace Barotrauma
}
}
nameBox.Text = nameBox.Text.Trim();
bool hideInMenus = nameBox.Parent.GetChildByUserData("hideinmenus") is GUITickBox hideInMenusTickBox && hideInMenusTickBox.Selected;
string saveFolder = Path.Combine(ContentPackage.LocalModsDir, nameBox.Text);
string filePath = Path.Combine(saveFolder, $"{nameBox.Text}.xml").CleanUpPathCrossPlatform();
@@ -2884,8 +2891,30 @@ namespace Barotrauma
{
if (deleteButtonHolder.FindChild("delete") is GUIButton deleteBtn)
{
deleteBtn.Enabled = userData is SubmarineInfo subInfo
&& GetContentPackageIntrinsicallyTiedToSub(subInfo) != null;
deleteBtn.ToolTip = string.Empty;
if (!(userData is SubmarineInfo subInfo))
{
deleteBtn.Enabled = false;
return true;
}
var package = GetContentPackageIntrinsicallyTiedToSub(subInfo);
if (package != null)
{
deleteBtn.Enabled = true;
}
else
{
deleteBtn.Enabled = false;
if (ContentPackageManager.VanillaCorePackage?.Files.Any(f => f.Path == subInfo.FilePath) ?? false)
{
deleteBtn.ToolTip = TextManager.Get("cantdeletevanillasub");
}
else if (ContentPackageManager.AllPackages.FirstOrDefault(p => p.Files.Any(f => f.Path == subInfo.FilePath)) is ContentPackage subPackage)
{
deleteBtn.ToolTip = TextManager.GetWithVariable("cantdeletemodsub", "[modname]", subPackage.Name);
}
}
}
return true;
}
@@ -2925,6 +2954,21 @@ namespace Barotrauma
ToolTip = sub.FilePath
};
if (!(ContentPackageManager.VanillaCorePackage?.Files.Any(f => f.Path == sub.FilePath) ?? false))
{
if (GetContentPackageIntrinsicallyTiedToSub(sub) == null &&
ContentPackageManager.AllPackages.FirstOrDefault(p => p.Files.Any(f => f.Path == sub.FilePath)) is ContentPackage subPackage)
{
//workshop mod
textBlock.OverrideTextColor(Color.MediumPurple);
}
else
{
//local mod
textBlock.OverrideTextColor(GUIStyle.TextColorBright);
}
}
if (sub.HasTag(SubmarineTag.Shuttle))
{
var shuttleText = new GUITextBlock(new RectTransform(new Vector2(0.2f, 1.0f), textBlock.RectTransform, Anchor.CenterRight),
@@ -3037,6 +3081,24 @@ namespace Barotrauma
if (!(child.UserData is SubmarineInfo sub)) { continue; }
child.Visible = string.IsNullOrEmpty(filter) || sub.Name.ToLower().Contains(filter.ToLower());
}
//go through the elements backwards, and disable the labels for sub categories if there's no subs visible in them
bool subVisibleInCategory = false;
foreach (GUIComponent child in subList.Content.Children.Reverse())
{
if (!(child.UserData is SubmarineInfo sub))
{
if (child.Enabled)
{
child.Visible = subVisibleInCategory;
}
subVisibleInCategory = false;
}
else
{
subVisibleInCategory |= child.Visible;
}
}
}
/// <summary>
@@ -4828,7 +4890,7 @@ namespace Barotrauma
}
}
if (GameSettings.CurrentConfig.KeyMap.Bindings[InputType.ToggleInventory].IsHit() && mode == Mode.Default)
if (PlayerInput.KeyHit(Keys.Q) && mode == Mode.Default)
{
toggleEntityMenuButton.OnClicked?.Invoke(toggleEntityMenuButton, toggleEntityMenuButton.UserData);
}
@@ -233,16 +233,18 @@ namespace Barotrauma
float dist = diff.Length();
float distFallOff = dist / FlowSoundRange;
if (distFallOff >= 0.99f) continue;
if (distFallOff >= 0.99f) { continue; }
float gain = MathHelper.Clamp(gapFlow / 100.0f, 0.0f, 1.0f);
//flow at the left side
if (diff.X < 0)
{
targetFlowLeft[flowSoundIndex] += 1.0f - distFallOff;
targetFlowLeft[flowSoundIndex] += gain - distFallOff;
}
else
{
targetFlowRight[flowSoundIndex] += 1.0f - distFallOff;
targetFlowRight[flowSoundIndex] += gain - distFallOff;
}
}
}
@@ -287,7 +289,7 @@ namespace Barotrauma
flowSoundChannels[i] = FlowSounds[i].Sound.Play(1.0f, FlowSoundRange, soundPos);
flowSoundChannels[i].Looping = true;
}
flowSoundChannels[i].Gain = Math.Min(Math.Max(flowVolumeRight[i], flowVolumeLeft[i]), 1.0f);
flowSoundChannels[i].Gain = Math.Max(flowVolumeRight[i], flowVolumeLeft[i]);
flowSoundChannels[i].Position = new Vector3(soundPos, 0.0f);
}
}
@@ -853,7 +855,7 @@ namespace Barotrauma
if (SplashSounds.Count == 0) { return; }
int splashIndex = MathHelper.Clamp((int)(strength + Rand.Range(-2.0f, 2.0f)), 0, SplashSounds.Count - 1);
float range = 800.0f;
var channel = SplashSounds[splashIndex].Sound.Play(1.0f, range, worldPosition, muffle: ShouldMuffleSound(Character.Controlled, worldPosition, range, null));
SplashSounds[splashIndex].Sound?.Play(1.0f, range, worldPosition, muffle: ShouldMuffleSound(Character.Controlled, worldPosition, range, null));
}
public static void PlayDamageSound(string damageType, float damage, PhysicsBody body)
@@ -39,9 +39,8 @@ namespace Barotrauma
get { return texture != null && !cannotBeLoaded; }
}
public Sprite(Sprite other) : this(other.texture, other.sourceRect, other.offset, other.rotation)
public Sprite(Sprite other) : this(other.texture, other.sourceRect, other.offset, other.rotation, other.FilePath.Value)
{
FilePath = other.FilePath;
Compress = other.Compress;
size = other.size;
effects = other.effects;
@@ -58,6 +57,17 @@ namespace Barotrauma
rotation = newRotation;
FilePath = ContentPath.FromRaw(path);
AddToList(this);
if (!string.IsNullOrEmpty(path))
{
Identifier fullPath = Path.GetFullPath(path).CleanUpPathCrossPlatform(correctFilenameCase: false).ToIdentifier();
lock (list)
{
if (!textureRefCounts.TryAdd(fullPath, new TextureRefCounter { RefCount = 1, Texture = texture }))
{
textureRefCounts[fullPath].RefCount++;
}
}
}
}
partial void LoadTexture(ref Vector4 sourceVector, ref bool shouldReturn)
@@ -16,6 +16,8 @@ namespace Barotrauma.Steam
{
public static partial class Workshop
{
public const int MaxThumbnailSize = 1024 * 1024;
public static readonly ImmutableArray<Identifier> Tags = new []
{
"submarine",
@@ -177,7 +179,7 @@ namespace Barotrauma.Steam
DeletePublishStagingCopy();
Directory.CreateDirectory(PublishStagingDir);
await CopyDirectory(contentPackage.Dir, contentPackage.Name, Path.GetDirectoryName(contentPackage.Path)!, PublishStagingDir);
await CopyDirectory(contentPackage.Dir, contentPackage.Name, Path.GetDirectoryName(contentPackage.Path)!, PublishStagingDir, ShouldCorrectPaths.No);
//Load filelist.xml and write the hash into it so anyone downloading this mod knows what it should be
ModProject modProject = new ModProject(contentPackage)
@@ -218,7 +220,7 @@ namespace Barotrauma.Steam
throw new Exception($"{newPath} already exists");
}
await CopyDirectory(contentPackage.Dir, contentPackage.Name, Path.GetDirectoryName(contentPackage.Path)!, newPath);
await CopyDirectory(contentPackage.Dir, contentPackage.Name, Path.GetDirectoryName(contentPackage.Path)!, newPath, ShouldCorrectPaths.Yes);
ModProject modProject = new ModProject(contentPackage);
modProject.DiscardHashAndInstallTime();
@@ -197,6 +197,11 @@ namespace Barotrauma.Steam
FileSelection.OnFileSelected = (fn) =>
{
if (new FileInfo(fn).Length > SteamManager.Workshop.MaxThumbnailSize)
{
new GUIMessageBox(TextManager.Get("Error"), TextManager.Get("WorkshopItemPreviewImageTooLarge"));
return;
}
thumbnailPath = fn;
CreateLocalThumbnail(thumbnailPath, thumbnailContainer);
};
@@ -119,6 +119,11 @@ namespace Barotrauma.Steam
{
CanBeFocused = false
};
new GUICustomComponent(new RectTransform(Vector2.Zero, searchHolder.RectTransform), onUpdate:
(f, component) =>
{
searchTitle.RectTransform.NonScaledSize = searchBox.Frame.RectTransform.NonScaledSize;
});
searchBox.OnSelected += (sender, userdata) => { searchTitle.Visible = false; };
searchBox.OnDeselected += (sender, userdata) => { searchTitle.Visible = searchBox.Text.IsNullOrWhiteSpace(); };
@@ -8,7 +8,6 @@ namespace Barotrauma
private readonly int maxWidth;
private ScalableFont? cachedFont = null;
private uint cachedFontSize = 0;
public LimitLString(LocalizedString text, GUIFont font, int maxWidth)
{
@@ -27,7 +26,6 @@ namespace Barotrauma
{
cachedValue = ToolBox.LimitString(nestedStr.Value, font.Value, maxWidth);
cachedFont = font.Value;
cachedFontSize = font.Size;
UpdateLanguage();
}
}
@@ -404,7 +404,7 @@ namespace Barotrauma
public static string LimitString(string str, ScalableFont font, int maxWidth)
{
if (maxWidth <= 0 || string.IsNullOrWhiteSpace(str)) return "";
if (maxWidth <= 0 || string.IsNullOrWhiteSpace(str)) { return ""; }
float currWidth = font.MeasureString("...").X;
for (int i = 0; i < str.Length; i++)
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product>
<Version>0.17.15.0</Version>
<Version>0.18.0.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>
+1 -1
View File
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product>
<Version>0.17.15.0</Version>
<Version>0.18.0.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product>
<Version>0.17.15.0</Version>
<Version>0.18.0.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>