This commit is contained in:
Evil Factory
2022-06-15 13:26:49 -03:00
410 changed files with 11140 additions and 5815 deletions

View File

@@ -1,22 +0,0 @@
---
name: Bug report (old)
about: Please use the other report form to report issues. This old template is only here until we've replaced the bug report links in the game.
title: ''
labels: ''
assignees: ''
---
Please use the other report form to report issues. This old template is only here until we've replaced the bug report links in the game.
**Description**
A clear and concise description of what the bug is.
**Steps To Reproduce**
If possible, describe how the developers can get the bug to happen (for example, "the game crashes when I try to put handcuffs on a Moloch"). Please also mention whether the bug happened in a multiplayer or single player session. It is often extremely hard to fix a bug if we don't know how to reproduce it.
**Version**
Which version of the game did the bug happen in. Also, please include the operating system you're using (Windows/Linux/Mac).
**Additional information**
Add any other context about the problem here. If the bug always occurs in a specific save file or submarine, attaching the file to the report makes it much easier for us to diagnose. Since GitHub doesn't allow attaching the .sub or .save files to reports directly, you need to .zip them first.

View File

@@ -52,9 +52,9 @@ body:
label: Version
description: Which version of the game did the bug happen in? You can see the current version number in the bottom left corner of your screen in the main menu.
options:
- 0.17.15.0
- 0.18.3.0 (Unstable)
- 0.18.2.0 (Unstable)
- 0.17.16.0
- 0.18.10.0 (Unstable)
- 0.18.9.0 (Unstable)
- Other
validations:
required: true

View File

@@ -6,7 +6,7 @@ using System;
namespace Barotrauma
{
public class Camera
public class Camera : IDisposable
{
public static bool FollowSub = true;
@@ -147,15 +147,19 @@ namespace Barotrauma
position = Vector2.Zero;
CreateMatrices();
// TODO: Needs to unregister if ever destroy cameras.
// TODO: this has the potential to cause a resource leak
// by sneakily creating a reference to cameras that we might
// fail to release.
GameMain.Instance.ResolutionChanged += CreateMatrices;
UpdateTransform(false);
}
~Camera()
private bool disposed = false;
public void Dispose()
{
GameMain.Instance.ResolutionChanged -= CreateMatrices;
if (!disposed) { GameMain.Instance.ResolutionChanged -= CreateMatrices; }
disposed = true;
}
public Vector2 TargetPos { get; set; }

View File

@@ -400,7 +400,7 @@ namespace Barotrauma
partial void UpdateControlled(float deltaTime, Camera cam)
{
if (controlled != this) return;
if (controlled != this) { return; }
ControlLocalPlayer(deltaTime, cam);

View File

@@ -3,7 +3,6 @@ using Barotrauma.Items.Components;
using FarseerPhysics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.Linq;

View File

@@ -266,7 +266,7 @@ namespace Barotrauma
disguisedSkinColor = idCard.StoredOwnerAppearance.SkinColor;
}
partial void LoadAttachmentSprites(bool omitJob)
partial void LoadAttachmentSprites()
{
if (attachmentSprites == null)
{
@@ -280,14 +280,6 @@ namespace Barotrauma
Head.BeardElement?.GetChildElements("sprite").ForEach(s => attachmentSprites.Add(new WearableSprite(s, WearableType.Beard)));
Head.MoustacheElement?.GetChildElements("sprite").ForEach(s => attachmentSprites.Add(new WearableSprite(s, WearableType.Moustache)));
Head.HairElement?.GetChildElements("sprite").ForEach(s => attachmentSprites.Add(new WearableSprite(s, WearableType.Hair)));
if (omitJob)
{
JobPrefab.NoJobElement?.GetChildElement("PortraitClothing")?.GetChildElements("sprite").ForEach(s => attachmentSprites.Add(new WearableSprite(s, WearableType.JobIndicator)));
}
else
{
Job?.Prefab.ClothingElement?.GetChildElements("sprite").ForEach(s => attachmentSprites.Add(new WearableSprite(s, WearableType.JobIndicator)));
}
}
// Doesn't work if the head's source rect does not start at 0,0.
@@ -988,11 +980,6 @@ namespace Barotrauma
HeadSelectionList = null;
}
}
~AppearanceCustomizationMenu()
{
Dispose();
}
}
}
}

View File

@@ -551,6 +551,7 @@ namespace Barotrauma
{
bool hasOwner = inc.ReadBoolean();
int ownerId = hasOwner ? inc.ReadByte() : -1;
float humanPrefabHealthMultiplier = inc.ReadSingle();
int balance = inc.ReadInt32();
int rewardDistribution = inc.ReadRangedInteger(0, 100);
byte teamID = inc.ReadByte();
@@ -573,6 +574,7 @@ namespace Barotrauma
{
character.MerchantIdentifier = inc.ReadIdentifier();
}
character.HumanPrefabHealthMultiplier = humanPrefabHealthMultiplier;
character.Wallet.Balance = balance;
character.Wallet.RewardDistribution = rewardDistribution;
if (character.CampaignInteractionType != CampaignMode.InteractionType.None)
@@ -649,6 +651,8 @@ namespace Barotrauma
GameMain.LightManager.LosEnabled = true;
GameMain.LightManager.LosAlpha = 1f;
GameMain.NetLobbyScreen.CampaignCharacterDiscarded = false;
character.memInput.Clear();
character.memState.Clear();
character.memLocalState.Clear();

View File

@@ -6,7 +6,6 @@ using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
@@ -1345,6 +1344,7 @@ namespace Barotrauma
{
UserData = item,
DisabledColor = Color.White * 0.1f,
PlaySoundOnSelect = false,
OnClicked = (btn, userdata) =>
{
if (!(userdata is ItemPrefab itemPrefab)) { return false; }
@@ -1352,6 +1352,7 @@ namespace Barotrauma
if (item == null) { return false; }
Limb targetLimb = Character.AnimController.Limbs.FirstOrDefault(l => l.HealthIndex == selectedLimbIndex);
item.ApplyTreatment(Character.Controlled, Character, targetLimb);
SoundPlayer.PlayUISound(GUISoundType.Select);
return true;
}
};
@@ -2008,8 +2009,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; }

View File

@@ -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));
}
}

View File

@@ -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)
{

View File

@@ -108,6 +108,15 @@ namespace Barotrauma
}
}
public void RemoveFile(File file)
{
if (HasFile(file))
{
files.Remove(file);
DiscardHashAndInstallTime();
}
}
public void DiscardHashAndInstallTime()
{
ExpectedHash = null;
@@ -144,7 +153,7 @@ namespace Barotrauma
=> rootElement.Add(new XAttribute(name, value.ToString() ?? ""));
addRootAttribute("name", Name);
addRootAttribute("modversion", ModVersion);
if (!ModVersion.IsNullOrEmpty()) { addRootAttribute("modversion", ModVersion); }
addRootAttribute("corepackage", IsCore);
if (SteamWorkshopId != 0) { addRootAttribute("steamworkshopid", SteamWorkshopId); }
addRootAttribute("gameversion", GameMain.Version);

View File

@@ -0,0 +1,133 @@
#nullable enable
using System;
using System.Linq;
using Barotrauma.Steam;
using Barotrauma.IO;
namespace Barotrauma
{
public static class ModMerger
{
public static void AskMerge(ContentPackage[] mods)
{
ErrorIfNonLocal(mods);
var msgBox = new GUIMessageBox(TextManager.Get("MergeModsHeader"), "", relativeSize: (0.5f, 0.8f),
buttons: new LocalizedString[] { TextManager.Get("ConfirmModMerge"), TextManager.Get("Cancel") });
msgBox.Buttons[1].OnClicked = msgBox.Close;
var desc = new GUITextBlock(new RectTransform((1.0f, 0.1f), msgBox.Content.RectTransform), TextManager.Get("MergeModsDesc"));
var modsList = new GUIListBox(new RectTransform((1.0f, 0.5f), msgBox.Content.RectTransform))
{
OnSelected = (component, o) => false,
HoverCursor = CursorState.Default
};
foreach (var mod in mods)
{
new GUITextBlock(new RectTransform((1.0f, 0.11f), modsList.Content.RectTransform), mod.Name)
{
CanBeFocused = false
};
}
var footer = new GUITextBlock(new RectTransform((1.0f, 0.1f), msgBox.Content.RectTransform), TextManager.Get("MergeModsFooter"));
var resultName = new GUITextBox(new RectTransform((1.0f, 0.1f), msgBox.Content.RectTransform))
{
Text = (mods.Count(m => m.Files.Length > 1)==1)
? mods.First(m => m.Files.Length > 1).Name
: ""
};
void flashText()
{
resultName!.Select();
resultName.Flash(GUIStyle.Red);
}
msgBox.Buttons[0].OnClicked = (button, o) =>
{
if (string.IsNullOrEmpty(resultName.Text))
{
flashText();
return false;
}
string targetDir = $"{ContentPackage.LocalModsDir}/{resultName.Text}";
bool dirMatches(ContentPackage mod)
=> mod.Dir.CleanUpPathCrossPlatform(correctFilenameCase: false)
.Equals(targetDir, StringComparison.OrdinalIgnoreCase);
if (ContentPackageManager.LocalPackages.Any(dirMatches)
&& !mods.Any(dirMatches))
{
flashText();
return false;
}
MergeMods(mods, resultName.Text);
msgBox.Close();
return false;
};
}
private static void MergeMods(ContentPackage[] mods, string resultName)
{
ModProject resultProject = new ModProject
{
Name = resultName
};
string targetDir = $"{ContentPackage.LocalModsDir}/{resultName}";
Directory.CreateDirectory(targetDir);
foreach (var mod in mods)
{
foreach (var file in Directory.GetFiles(mod.Dir, "*", System.IO.SearchOption.AllDirectories)
.Select(f => f.CleanUpPathCrossPlatform(correctFilenameCase: false)))
{
if (Path.GetFileName(file).Equals(ContentPackage.FileListFileName, StringComparison.OrdinalIgnoreCase)) { continue; }
string targetFilePath = file[mod.Dir.Length..];
if (targetFilePath.StartsWith("/") || targetFilePath.StartsWith("\\"))
{
targetFilePath = targetFilePath[1..];
}
targetFilePath = Path.Combine(targetDir, targetFilePath).CleanUpPathCrossPlatform(correctFilenameCase: false);
//DebugConsole.NewMessage(targetFilePath);
Directory.CreateDirectory(Path.GetDirectoryName(targetFilePath)!);
File.Copy(file, targetFilePath, overwrite: true);
var oldFileInProject = resultProject.Files.FirstOrDefault(f
=> f.Path.Equals(targetFilePath, StringComparison.OrdinalIgnoreCase));
if (oldFileInProject != null)
{
resultProject.RemoveFile(oldFileInProject);
}
var fileInMod = mod.Files.Find(f => f.Path == file);
if (fileInMod != null)
{
var newFileInProject = ModProject.File.FromPath(targetFilePath, fileInMod.GetType());
resultProject.AddFile(newFileInProject);
}
}
}
resultProject.Save(Path.Combine(targetDir, ContentPackage.FileListFileName));
foreach (var mod in mods)
{
Directory.Delete(mod.Dir);
}
(SettingsMenu.Instance!.WorkshopMenu as MutableWorkshopMenu)!.PopulateInstalledModLists(forceRefreshEnabled: true, refreshDisabled: true);
}
private static void ErrorIfNonLocal(ContentPackage[] mods)
{
var nonLocal = mods.Where(m => !ContentPackageManager.LocalPackages.Contains(m)).ToArray();
if (nonLocal.Any())
{
throw new Exception($"{string.Join(", ", nonLocal.Select(m => m.Name))} are not local mods");
}
}
}
}

View File

@@ -1,7 +1,6 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -9,9 +8,7 @@ using System.Xml.Linq;
using Barotrauma.Extensions;
using Barotrauma.Steam;
using Microsoft.Xna.Framework;
using Directory = Barotrauma.IO.Directory;
using File = Barotrauma.IO.File;
using Path = Barotrauma.IO.Path;
using Barotrauma.IO;
namespace Barotrauma.Transition
{
@@ -258,13 +255,13 @@ namespace Barotrauma.Transition
{
string[] getFiles(string path, string pattern)
=> Directory.Exists(path)
? Directory.GetFiles(path, pattern, SearchOption.TopDirectoryOnly)
? Directory.GetFiles(path, pattern, System.IO.SearchOption.TopDirectoryOnly)
: Array.Empty<string>();
subs = getFiles(oldSubsPath, "*.sub");
itemAssemblies = getFiles(oldItemAssembliesPath, "*.xml");
string[] allOldMods = Directory.GetDirectories(oldModsPath, "*", SearchOption.TopDirectoryOnly);
string[] allOldMods = Directory.GetDirectories(oldModsPath, "*", System.IO.SearchOption.TopDirectoryOnly);
var publishedItems = await SteamManager.Workshop.GetPublishedItems();
foreach (var modDir in allOldMods)
@@ -359,7 +356,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;
}

View File

@@ -125,7 +125,7 @@ namespace Barotrauma
{
if (isOpen)
{
frame.AddToGUIUpdateList();
frame.AddToGUIUpdateList(order: 1);
}
}
@@ -172,6 +172,7 @@ namespace Barotrauma
isOpen = false;
GUI.ForceMouseOn(null);
textBox.Deselect();
SoundPlayer.PlayUISound(GUISoundType.Select);
}
if (isOpen)
@@ -209,7 +210,7 @@ namespace Barotrauma
isOpen = !isOpen;
if (isOpen)
{
textBox.Select();
textBox.Select(ignoreSelectSound: true);
AddToGUIUpdateList();
}
else
@@ -217,6 +218,7 @@ namespace Barotrauma
GUI.ForceMouseOn(null);
textBox.Deselect();
}
SoundPlayer.PlayUISound(GUISoundType.Select);
}
private static bool IsCommandPermitted(string command, GameClient client)
@@ -1714,9 +1716,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 +2543,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 +2558,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())
{

View File

@@ -308,7 +308,7 @@ namespace Barotrauma
AlwaysOverrideCursor = true
};
LocalizedString translatedText = TextManager.Get(text);
LocalizedString translatedText = TextManager.Get(text).Fallback(text);
if (speaker?.Info != null && drawChathead)
{
@@ -335,7 +335,7 @@ namespace Barotrauma
{
foreach (string option in options)
{
var btn = new GUIButton(new RectTransform(new Vector2(0.9f, 0.01f), textContent.RectTransform), TextManager.Get(option), style: "ListBoxElement");
var btn = new GUIButton(new RectTransform(new Vector2(0.9f, 0.01f), textContent.RectTransform), TextManager.Get(option).Fallback(option), style: "ListBoxElement");
btn.TextBlock.TextAlignment = Alignment.CenterLeft;
btn.TextColor = btn.HoverTextColor = GUIStyle.Green;
btn.TextBlock.Wrap = true;

View File

@@ -20,14 +20,12 @@ namespace Barotrauma
if (targetId == Entity.NullEntityID) { continue; }
Entity target = Entity.FindEntityByID(targetId);
if (target == null) { continue; }
existingTargets.Add(target);
allTargets.Add(target);
}
ushort spawnedTargetsCount = msg.ReadUInt16();
for (int i = 0; i < spawnedTargetsCount; i++)
{
var enemy = Character.ReadSpawnData(msg);
existingTargets.Add(enemy);
allTargets.Add(enemy);
}
}

View File

@@ -81,6 +81,8 @@ namespace Barotrauma
public const int ToggleButtonWidthRaw = 30;
private int popupMessageOffset;
private GUIDropDown ChatModeDropDown { get; set; }
public ChatBox(GUIComponent parent, bool isSinglePlayer)
{
this.IsSinglePlayer = isSinglePlayer;
@@ -107,6 +109,7 @@ namespace Barotrauma
var buttonLeft = new GUIButton(new RectTransform(new Vector2(0.1f, 0.8f), channelSettingsContent.RectTransform), style: "DeviceButton")
{
PlaySoundOnSelect = false,
OnClicked = (btn, userdata) =>
{
if (Character.Controlled != null && ChatMessage.CanUseRadio(Character.Controlled, out WifiComponent radio))
@@ -150,6 +153,7 @@ namespace Barotrauma
var buttonRight = new GUIButton(new RectTransform(new Vector2(0.1f, 0.8f), channelSettingsContent.RectTransform), style: "DeviceButton")
{
PlaySoundOnSelect = false,
OnClicked = (btn, userdata) =>
{
if (Character.Controlled != null && ChatMessage.CanUseRadio(Character.Controlled, out WifiComponent radio))
@@ -178,6 +182,7 @@ namespace Barotrauma
TextColor = new Color(51, 59, 46),
SelectedTextColor = GUIStyle.Green,
UserData = i,
PlaySoundOnSelect = false,
OnClicked = (btn, userdata) =>
{
if (Character.Controlled != null && ChatMessage.CanUseRadio(Character.Controlled, out WifiComponent radio))
@@ -223,7 +228,53 @@ namespace Barotrauma
// ---------------------------------------------------------------------------------------------
InputBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.125f), hideableElements.RectTransform, Anchor.BottomLeft),
var bottomContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.125f), hideableElements.RectTransform, Anchor.BottomLeft), isHorizontal: true)
{
Stretch = true
};
var dropdownRt = new RectTransform(new Vector2(0.1f, 1.0f), bottomContainer.RectTransform)
{
// The chat mode selection dropdown will take a maximum of 45% of the horizontal space
MaxSize = new Point((int)(0.45f * bottomContainer.RectTransform.NonScaledSize.X), int.MaxValue)
};
var chatModes = new ChatMode[] { ChatMode.Local, ChatMode.Radio };
ChatModeDropDown = new GUIDropDown(dropdownRt, elementCount: chatModes.Length, dropAbove: true)
{
OnSelected = (component, userdata) =>
{
GameMain.ActiveChatMode = (ChatMode)userdata;
if (InputBox != null && InputBox.Text.StartsWith(RadioChatString) && GameMain.ActiveChatMode == ChatMode.Local)
{
string text = InputBox.Text;
InputBox.Text = text.Remove(0, RadioChatString.Length);
}
return true;
}
};
float longestDropDownOption = 0.0f;
foreach (ChatMode mode in chatModes)
{
var text = TextManager.Get($"chatmode.{mode}");
ChatModeDropDown.AddItem(text, userData: mode);
if (ChatModeDropDown.ListBox.Content.GetChildByUserData(mode) is GUITextBlock textBlock)
{
if (textBlock.TextSize.X > longestDropDownOption)
{
longestDropDownOption = textBlock.TextSize.X;
}
}
}
ChatModeDropDown.SelectItem(GameMain.ActiveChatMode);
float minDropDownWidth = longestDropDownOption + ChatModeDropDown.Padding.X +
(ChatModeDropDown.DropDownIcon?.RectTransform.NonScaledSize.X ?? 0) +
(ChatModeDropDown.DropDownIcon?.RectTransform.AbsoluteOffset.X ?? 0) * 2;
ChatModeDropDown.RectTransform.MinSize = new Point(
Math.Max((int)minDropDownWidth, ChatModeDropDown.RectTransform.MinSize.X),
ChatModeDropDown.RectTransform.MinSize.Y);
InputBox = new GUITextBox(new RectTransform(new Vector2(0.9f, 1.0f), bottomContainer.RectTransform),
style: "ChatTextBox")
{
OverflowClip = true,
@@ -236,6 +287,11 @@ namespace Barotrauma
InputBox.OnDeselected += (gui, Keys) =>
{
ChatManager.Clear();
if (GUIFrame.IsParentOf(GUI.MouseOn))
{
CloseAfterMessageSent = false;
return;
}
ChatMessage.GetChatMessageCommand(InputBox.Text, out var message);
if (string.IsNullOrEmpty(message))
{
@@ -245,8 +301,6 @@ namespace Barotrauma
CloseAfterMessageSent = false;
}
}
//gui.Text = "";
};
var chatSendButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.7f), InputBox.RectTransform, Anchor.CenterRight, scaleBasis: ScaleBasis.BothHeight), style: "GUIButtonToggleRight");
@@ -303,6 +357,10 @@ namespace Barotrauma
{
textColor = ChatMessage.MessageColor[(int)ChatMessageType.Private];
}
else if (GameMain.ActiveChatMode == ChatMode.Radio)
{
textColor = ChatMessage.MessageColor[(int)ChatMessageType.Radio];
}
else
{
textColor = ChatMessage.MessageColor[(int)ChatMessageType.Default];
@@ -357,10 +415,15 @@ namespace Barotrauma
CanBeFocused = true,
ForceUpperCase = ForceUpperCase.No,
UserData = message.SenderClient,
PlaySoundOnSelect = false,
OnClicked = (_, o) =>
{
if (!(o is Client client)) { return false; }
GameMain.NetLobbyScreen?.SelectPlayer(client);
if (GameMain.NetLobbyScreen != null)
{
GameMain.NetLobbyScreen.SelectPlayer(client);
SoundPlayer.PlayUISound(GUISoundType.Select);
}
return true;
},
OnSecondaryClicked = (_, o) =>
@@ -542,6 +605,25 @@ namespace Barotrauma
showNewMessagesButton.Visible = false;
}
if (PlayerInput.KeyHit(InputType.ToggleChatMode) && GUI.KeyboardDispatcher.Subscriber == null && Screen.Selected == GameMain.GameScreen)
{
try
{
var mode = GameMain.ActiveChatMode switch
{
ChatMode.Local => ChatMode.Radio,
ChatMode.Radio => ChatMode.Local,
_ => throw new NotImplementedException()
};
ChatModeDropDown.SelectItem(mode);
// TODO: Play a sound?
}
catch (NotImplementedException)
{
DebugConsole.ThrowError($"Error toggling chat mode: not implemented for current mode \"{GameMain.ActiveChatMode}\"");
}
}
if (ToggleButton != null)
{
ToggleButton.Selected = ToggleOpen;
@@ -692,5 +774,70 @@ namespace Barotrauma
}
}
}
public void ApplySelectionInputs() => ApplySelectionInputs(InputBox, true, ChatKeyStates.GetChatKeyStates());
public struct ChatKeyStates
{
public bool ActiveChatKeyHit { get; set; }
public bool LocalChatKeyHit { get; set; }
public bool RadioChatKeyHit { get; set; }
public bool AnyHit => ActiveChatKeyHit || LocalChatKeyHit || RadioChatKeyHit;
private ChatKeyStates(bool active, bool local, bool radio)
{
ActiveChatKeyHit = active;
LocalChatKeyHit = local;
RadioChatKeyHit = radio;
}
public static ChatKeyStates GetChatKeyStates()
{
return new ChatKeyStates(PlayerInput.KeyHit(InputType.ActiveChat),
PlayerInput.KeyHit(InputType.Chat),
PlayerInput.KeyHit(InputType.RadioChat) && (Character.Controlled == null || Character.Controlled.SpeechImpediment < 100));
}
public (bool active, bool local, bool radio) Deconstruct()
{
return (ActiveChatKeyHit, LocalChatKeyHit, RadioChatKeyHit);
}
}
public void ApplySelectionInputs(GUITextBox inputBox, bool selectInputBox, ChatKeyStates chatKeyStates)
{
inputBox ??= InputBox;
var (activeChatKeyHit, localChatKeyHit, radioChatKeyHit) = chatKeyStates.Deconstruct();
if (localChatKeyHit || (activeChatKeyHit && GameMain.ActiveChatMode == ChatMode.Local))
{
ChatModeDropDown.SelectItem(ChatMode.Local);
inputBox.AddToGUIUpdateList();
GUIFrame.Flash(Color.DarkGreen, 0.5f);
if (!ToggleOpen)
{
CloseAfterMessageSent = !ToggleOpen;
ToggleOpen = true;
}
if (selectInputBox)
{
inputBox.Select(inputBox.Text.Length);
}
}
else if (radioChatKeyHit || (activeChatKeyHit && GameMain.ActiveChatMode == ChatMode.Radio))
{
ChatModeDropDown.SelectItem(ChatMode.Radio);
inputBox.AddToGUIUpdateList();
GUIFrame.Flash(Color.YellowGreen, 0.5f);
if (!ToggleOpen)
{
CloseAfterMessageSent = !ToggleOpen;
ToggleOpen = true;
}
if (selectInputBox)
{
inputBox.Select(inputBox.Text.Length);
}
}
}
}
}

View File

@@ -178,7 +178,18 @@ namespace Barotrauma
return Sprites.ContainsKey(state) ? Sprites[state]?.First()?.Sprite : null;
}
public void GetSize(XElement element)
public void RefreshSize()
{
Width = null;
Height = null;
GetSize(Element);
foreach (var childStyle in ChildStyles.Values)
{
childStyle.RefreshSize();
}
}
private void GetSize(XElement element)
{
Point size = new Point(0, 0);
foreach (var subElement in element.Elements())

View File

@@ -193,12 +193,13 @@ namespace Barotrauma
};
validateHiresButton = new GUIButton(new RectTransform(new Vector2(1.0f / 3.0f, 1.0f), group.RectTransform), text: TextManager.Get("campaigncrew.validate"))
{
ClickSound = GUISoundType.HireRepairClick,
ClickSound = GUISoundType.ConfirmTransaction,
ForceUpperCase = ForceUpperCase.Yes,
OnClicked = (b, o) => ValidateHires(PendingHires, true)
};
clearAllButton = new GUIButton(new RectTransform(new Vector2(1.0f / 3.0f, 1.0f), group.RectTransform), text: TextManager.Get("campaignstore.clearall"))
{
ClickSound = GUISoundType.Cart,
ForceUpperCase = ForceUpperCase.Yes,
Enabled = HasPermission,
OnClicked = (b, o) => RemoveAllPendingHires()
@@ -403,6 +404,7 @@ namespace Barotrauma
{
var hireButton = new GUIButton(new RectTransform(new Vector2(width, 0.9f), mainGroup.RectTransform), style: "CrewManagementAddButton")
{
ClickSound = GUISoundType.Cart,
UserData = characterInfo,
Enabled = HasPermission,
OnClicked = (b, o) => AddPendingHire(o as CharacterInfo)
@@ -429,6 +431,7 @@ namespace Barotrauma
{
new GUIButton(new RectTransform(new Vector2(width, 0.9f), mainGroup.RectTransform), style: "CrewManagementRemoveButton")
{
ClickSound = GUISoundType.Cart,
UserData = characterInfo,
Enabled = HasPermission,
OnClicked = (b, o) => RemovePendingHire(o as CharacterInfo)

View File

@@ -182,7 +182,10 @@ namespace Barotrauma
window = new GUIFrame(new RectTransform(Vector2.One * 0.8f, backgroundFrame.RectTransform, Anchor.Center));
var horizontalLayout = new GUILayoutGroup(new RectTransform(Vector2.One * 0.9f, window.RectTransform, Anchor.Center), true);
sidebar = new GUIListBox(new RectTransform(new Vector2(0.29f, 1.0f), horizontalLayout.RectTransform));
sidebar = new GUIListBox(new RectTransform(new Vector2(0.29f, 1.0f), horizontalLayout.RectTransform))
{
PlaySoundOnSelect = true
};
var drives = System.IO.DriveInfo.GetDrives();
foreach (var drive in drives)
@@ -241,6 +244,7 @@ namespace Barotrauma
fileList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.85f), fileListLayout.RectTransform))
{
PlaySoundOnSelect = true,
OnSelected = (child, userdata) =>
{
if (userdata is null) { return false; }

View File

@@ -24,15 +24,17 @@ namespace Barotrauma
ChatMessage,
RadioMessage,
DeadMessage,
Click,
Select,
PickItem,
PickItemFail,
DropItem,
PopupMenu,
DecreaseQuantity,
IncreaseQuantity,
HireRepairClick,
UISwitch
Decrease,
Increase,
UISwitch,
TickBox,
ConfirmTransaction,
Cart,
}
public enum CursorState
@@ -301,6 +303,7 @@ namespace Barotrauma
}
float startY = 10.0f;
float yStep = AdjustForTextScale(18) * yScale;
if (GameMain.ShowFPS || GameMain.DebugDraw || GameMain.ShowPerf)
{
float y = startY;
@@ -309,11 +312,38 @@ namespace Barotrauma
Color.White, Color.Black * 0.5f, 0, GUIStyle.SmallFont);
if (GameMain.GameSession != null && Timing.TotalTime > GameMain.GameSession.RoundStartTime + 1.0)
{
y += AdjustForTextScale(15) * yScale;
y += yStep;
DrawString(spriteBatch, new Vector2(10, y),
$"Physics: {GameMain.CurrentUpdateRate}",
(GameMain.CurrentUpdateRate < Timing.FixedUpdateRate) ? Color.Red : Color.White, Color.Black * 0.5f, 0, GUIStyle.SmallFont);
}
if (GameMain.DebugDraw || GameMain.ShowPerf)
{
y += yStep;
DrawString(spriteBatch, new Vector2(10, y),
"Active lights: " + Lights.LightManager.ActiveLightCount,
Color.White, Color.Black * 0.5f, 0, GUIStyle.SmallFont);
y += yStep;
DrawString(spriteBatch, new Vector2(10, y),
"Physics: " + GameMain.World.UpdateTime.TotalMilliseconds + " ms",
Color.White, Color.Black * 0.5f, 0, GUIStyle.SmallFont);
y += yStep;
try
{
DrawString(spriteBatch, new Vector2(10, y),
$"Bodies: {GameMain.World.BodyList.Count} ({GameMain.World.BodyList.Count(b => b != null && b.Awake && b.Enabled)} awake, {GameMain.World.BodyList.Count(b => b != null && b.Awake && b.BodyType == BodyType.Dynamic && b.Enabled)} dynamic)",
Color.White, Color.Black * 0.5f, 0, GUIStyle.SmallFont);
}
catch (InvalidOperationException)
{
DebugConsole.AddWarning("Exception while rendering debug info. Physics bodies may have been created or removed while rendering.");
}
y += yStep;
DrawString(spriteBatch, new Vector2(10, y),
"Particle count: " + GameMain.ParticleManager.ParticleCount + "/" + GameMain.ParticleManager.MaxParticles,
Color.Lerp(GUIStyle.Green, GUIStyle.Red, (GameMain.ParticleManager.ParticleCount / (float)GameMain.ParticleManager.MaxParticles)), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
}
}
if (GameMain.ShowPerf)
@@ -324,67 +354,53 @@ namespace Barotrauma
"Draw - Avg: " + GameMain.PerformanceCounter.DrawTimeGraph.Average().ToString("0.00") + " ms" +
" Max: " + GameMain.PerformanceCounter.DrawTimeGraph.LargestValue().ToString("0.00") + " ms",
GUIStyle.Green, Color.Black * 0.8f, font: GUIStyle.SmallFont);
y += 15 * yScale;
y += yStep;
GameMain.PerformanceCounter.DrawTimeGraph.Draw(spriteBatch, new Rectangle((int)x, (int)y, 170, 50), color: GUIStyle.Green);
y += 50 * yScale;
y += yStep * 4;
DrawString(spriteBatch, new Vector2(x, y),
"Update - Avg: " + GameMain.PerformanceCounter.UpdateTimeGraph.Average().ToString("0.00") + " ms" +
" Max: " + GameMain.PerformanceCounter.UpdateTimeGraph.LargestValue().ToString("0.00") + " ms",
Color.LightBlue, Color.Black * 0.8f, font: GUIStyle.SmallFont);
y += 15 * yScale;
y += yStep;
GameMain.PerformanceCounter.UpdateTimeGraph.Draw(spriteBatch, new Rectangle((int)x, (int)y, 170, 50), color: Color.LightBlue);
y += 50 * yScale;
foreach (string key in GameMain.PerformanceCounter.GetSavedIdentifiers)
y += yStep * 4;
foreach (string key in GameMain.PerformanceCounter.GetSavedIdentifiers.OrderBy(i => i))
{
float elapsedMillisecs = GameMain.PerformanceCounter.GetAverageElapsedMillisecs(key);
DrawString(spriteBatch, new Vector2(x, y),
key + ": " + elapsedMillisecs.ToString("0.00"),
Color.Lerp(Color.LightGreen, GUIStyle.Red, elapsedMillisecs / 10.0f), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
y += 15 * yScale;
foreach (string childKey in GameMain.PerformanceCounter.GetSavedPartialIdentifiers(key))
{
elapsedMillisecs = GameMain.PerformanceCounter.GetPartialAverageElapsedMillisecs(key, childKey);
DrawString(spriteBatch, new Vector2(x + 15, y),
childKey + ": " + elapsedMillisecs.ToString("0.00"),
Color.Lerp(Color.LightGreen, GUIStyle.Red, elapsedMillisecs / 10.0f), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
y += 15 * yScale;
}
}
int categoryDepth = key.Count(c => c == ':');
//color the more fine-grained counters red more easily (ok for the whole Update to take a longer time than specific part of the update)
float runningSlowThreshold = 10.0f / categoryDepth;
DrawString(spriteBatch, new Vector2(x + categoryDepth * 15, y),
key.Split(':').Last() + ": " + elapsedMillisecs.ToString("0.00"),
ToolBox.GradientLerp(elapsedMillisecs / runningSlowThreshold, Color.LightGreen, GUIStyle.Yellow, GUIStyle.Orange, GUIStyle.Red, Color.Magenta), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
y += yStep;
}
if (Powered.Grids != null)
{
DrawString(spriteBatch, new Vector2(x, y), "Grids: " + Powered.Grids.Count, Color.LightGreen, Color.Black * 0.5f, 0, GUIStyle.SmallFont);
y += 15 * yScale;
y += yStep;
}
if (Settings.EnableDiagnostics)
{
x += 20 * xScale;
DrawString(spriteBatch, new Vector2(x, y), "ContinuousPhysicsTime: " + GameMain.World.ContinuousPhysicsTime.TotalMilliseconds, Color.Lerp(Color.LightGreen, GUIStyle.Red, (float)GameMain.World.ContinuousPhysicsTime.TotalMilliseconds / 10.0f), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
DrawString(spriteBatch, new Vector2(x, y + 15 * yScale), "ControllersUpdateTime: " + GameMain.World.ControllersUpdateTime.TotalMilliseconds, Color.Lerp(Color.LightGreen, GUIStyle.Red, (float)GameMain.World.ControllersUpdateTime.TotalMilliseconds / 10.0f), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
DrawString(spriteBatch, new Vector2(x, y + 30 * yScale), "AddRemoveTime: " + GameMain.World.AddRemoveTime.TotalMilliseconds, Color.Lerp(Color.LightGreen, GUIStyle.Red, (float)GameMain.World.AddRemoveTime.TotalMilliseconds / 10.0f), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
DrawString(spriteBatch, new Vector2(x, y + 45 * yScale), "NewContactsTime: " + GameMain.World.NewContactsTime.TotalMilliseconds, Color.Lerp(Color.LightGreen, GUIStyle.Red, (float)GameMain.World.NewContactsTime.TotalMilliseconds / 10.0f), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
DrawString(spriteBatch, new Vector2(x, y + 60 * yScale), "ContactsUpdateTime: " + GameMain.World.ContactsUpdateTime.TotalMilliseconds, Color.Lerp(Color.LightGreen, GUIStyle.Red, (float)GameMain.World.ContactsUpdateTime.TotalMilliseconds / 10.0f), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
DrawString(spriteBatch, new Vector2(x, y + 75 * yScale), "SolveUpdateTime: " + GameMain.World.SolveUpdateTime.TotalMilliseconds, Color.Lerp(Color.LightGreen, GUIStyle.Red, (float)GameMain.World.SolveUpdateTime.TotalMilliseconds / 10.0f), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
x += yStep * 2;
DrawString(spriteBatch, new Vector2(x, y), "ContinuousPhysicsTime: " + GameMain.World.ContinuousPhysicsTime.TotalMilliseconds.ToString("0.00"), Color.Lerp(Color.LightGreen, GUIStyle.Red, (float)GameMain.World.ContinuousPhysicsTime.TotalMilliseconds / 10.0f), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
DrawString(spriteBatch, new Vector2(x, y + yStep), "ControllersUpdateTime: " + GameMain.World.ControllersUpdateTime.TotalMilliseconds.ToString("0.00"), Color.Lerp(Color.LightGreen, GUIStyle.Red, (float)GameMain.World.ControllersUpdateTime.TotalMilliseconds / 10.0f), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
DrawString(spriteBatch, new Vector2(x, y + yStep * 2), "AddRemoveTime: " + GameMain.World.AddRemoveTime.TotalMilliseconds.ToString("0.00"), Color.Lerp(Color.LightGreen, GUIStyle.Red, (float)GameMain.World.AddRemoveTime.TotalMilliseconds / 10.0f), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
DrawString(spriteBatch, new Vector2(x, y + yStep * 3), "NewContactsTime: " + GameMain.World.NewContactsTime.TotalMilliseconds.ToString("0.00"), Color.Lerp(Color.LightGreen, GUIStyle.Red, (float)GameMain.World.NewContactsTime.TotalMilliseconds / 10.0f), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
DrawString(spriteBatch, new Vector2(x, y + yStep * 4), "ContactsUpdateTime: " + GameMain.World.ContactsUpdateTime.TotalMilliseconds.ToString("0.00"), Color.Lerp(Color.LightGreen, GUIStyle.Red, (float)GameMain.World.ContactsUpdateTime.TotalMilliseconds / 10.0f), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
DrawString(spriteBatch, new Vector2(x, y + yStep * 5), "SolveUpdateTime: " + GameMain.World.SolveUpdateTime.TotalMilliseconds.ToString("0.00"), Color.Lerp(Color.LightGreen, GUIStyle.Red, (float)GameMain.World.SolveUpdateTime.TotalMilliseconds / 10.0f), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
}
}
if (GameMain.DebugDraw && !Submarine.Unloading && !(Screen.Selected is RoundSummaryScreen))
{
float y = startY + 15 * yScale;
DrawString(spriteBatch, new Vector2(10, y),
"Physics: " + GameMain.World.UpdateTime,
Color.White, Color.Black * 0.5f, 0, GUIStyle.SmallFont);
y += 15 * yScale;
DrawString(spriteBatch, new Vector2(10, y),
$"Bodies: {GameMain.World.BodyList.Count} ({GameMain.World.BodyList.Count(b => b != null && b.Awake && b.Enabled)} awake, {GameMain.World.BodyList.Count(b => b != null && b.Awake && b.BodyType == BodyType.Dynamic && b.Enabled)} dynamic)",
Color.White, Color.Black * 0.5f, 0, GUIStyle.SmallFont);
float y = startY + yStep * 6;
if (Screen.Selected.Cam != null)
{
y += 15 * yScale;
y += yStep;
DrawString(spriteBatch, new Vector2(10, y),
"Camera pos: " + Screen.Selected.Cam.Position.ToPoint() + ", zoom: " + Screen.Selected.Cam.Zoom,
Color.White, Color.Black * 0.5f, 0, GUIStyle.SmallFont);
@@ -392,23 +408,18 @@ namespace Barotrauma
if (Submarine.MainSub != null)
{
y += 15 * yScale;
y += yStep;
DrawString(spriteBatch, new Vector2(10, y),
"Sub pos: " + Submarine.MainSub.Position.ToPoint(),
Color.White, Color.Black * 0.5f, 0, GUIStyle.SmallFont);
}
y += 20 * yScale;
DrawString(spriteBatch, new Vector2(10, y),
"Particle count: " + GameMain.ParticleManager.ParticleCount + "/" + GameMain.ParticleManager.MaxParticles,
Color.Lerp(GUIStyle.Green, GUIStyle.Red, (GameMain.ParticleManager.ParticleCount / (float)GameMain.ParticleManager.MaxParticles)), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
if (loadedSpritesText == null || DateTime.Now > loadedSpritesUpdateTime)
{
loadedSpritesText = "Loaded sprites: " + Sprite.LoadedSprites.Count() + "\n(" + Sprite.LoadedSprites.Select(s => s.FilePath).Distinct().Count() + " unique textures)";
loadedSpritesUpdateTime = DateTime.Now + new TimeSpan(0, 0, seconds: 5);
}
y += 25 * yScale;
y += yStep * 2;
DrawString(spriteBatch, new Vector2(10, y), loadedSpritesText, Color.White, Color.Black * 0.5f, 0, GUIStyle.SmallFont);
if (debugDrawSounds)
@@ -416,21 +427,21 @@ namespace Barotrauma
float soundTextY = 0;
DrawString(spriteBatch, new Vector2(500, soundTextY),
"Sounds (Ctrl+S to hide): ", Color.White, Color.Black * 0.5f, 0, GUIStyle.SmallFont);
soundTextY += 15 * yScale;
soundTextY += yStep;
DrawString(spriteBatch, new Vector2(500, soundTextY),
"Current playback amplitude: " + GameMain.SoundManager.PlaybackAmplitude.ToString(), Color.White, Color.Black * 0.5f, 0, GUIStyle.SmallFont);
soundTextY += 15 * yScale;
soundTextY += yStep;
DrawString(spriteBatch, new Vector2(500, soundTextY),
"Compressed dynamic range gain: " + GameMain.SoundManager.CompressionDynamicRangeGain.ToString(), Color.White, Color.Black * 0.5f, 0, GUIStyle.SmallFont);
soundTextY += 15 * yScale;
soundTextY += yStep;
DrawString(spriteBatch, new Vector2(500, soundTextY),
"Loaded sounds: " + GameMain.SoundManager.LoadedSoundCount + " (" + GameMain.SoundManager.UniqueLoadedSoundCount + " unique)", Color.White, Color.Black * 0.5f, 0, GUIStyle.SmallFont);
soundTextY += 15 * yScale;
soundTextY += yStep;
for (int i = 0; i < SoundManager.SOURCE_COUNT; i++)
{
@@ -479,7 +490,7 @@ namespace Barotrauma
}
DrawString(spriteBatch, new Vector2(500, soundTextY), soundStr, clr, Color.Black * 0.5f, 0, GUIStyle.SmallFont);
soundTextY += 15 * yScale;
soundTextY += yStep;
}
}
else
@@ -1981,7 +1992,7 @@ namespace Barotrauma
var element = new GUIFrame(new RectTransform(new Vector2(0.22f, 1), inputArea.RectTransform) { MinSize = new Point(50, 0), MaxSize = new Point(150, 50) }, style: null);
new GUITextBlock(new RectTransform(new Vector2(0.3f, 1), element.RectTransform, Anchor.CenterLeft), RectComponentLabels[i], font: font, textAlignment: Alignment.CenterLeft);
GUINumberInput numberInput = new GUINumberInput(new RectTransform(new Vector2(0.7f, 1), element.RectTransform, Anchor.CenterRight),
GUINumberInput.NumberType.Int)
NumberType.Int)
{
Font = font
};
@@ -2025,7 +2036,7 @@ namespace Barotrauma
var element = new GUIFrame(new RectTransform(new Vector2(0.45f, 1), inputArea.RectTransform), style: null);
new GUITextBlock(new RectTransform(new Vector2(0.3f, 1), element.RectTransform, Anchor.CenterLeft), VectorComponentLabels[i], font: GUIStyle.SmallFont, textAlignment: Alignment.CenterLeft);
GUINumberInput numberInput = new GUINumberInput(new RectTransform(new Vector2(0.7f, 1), element.RectTransform, Anchor.CenterRight),
GUINumberInput.NumberType.Int)
NumberType.Int)
{
Font = GUIStyle.SmallFont
};
@@ -2055,7 +2066,7 @@ namespace Barotrauma
{
var element = new GUIFrame(new RectTransform(new Vector2(0.45f, 1), inputArea.RectTransform), style: null);
new GUITextBlock(new RectTransform(new Vector2(0.3f, 1), element.RectTransform, Anchor.CenterLeft), VectorComponentLabels[i], font: font, textAlignment: Alignment.CenterLeft);
GUINumberInput numberInput = new GUINumberInput(new RectTransform(new Vector2(0.7f, 1), element.RectTransform, Anchor.CenterRight), GUINumberInput.NumberType.Float) { Font = font };
GUINumberInput numberInput = new GUINumberInput(new RectTransform(new Vector2(0.7f, 1), element.RectTransform, Anchor.CenterRight), NumberType.Float) { Font = font };
switch (i)
{
case 0:
@@ -2369,7 +2380,7 @@ namespace Barotrauma
CreateButton("PauseMenuResume", buttonContainer, null);
CreateButton("PauseMenuSettings", buttonContainer, () => SettingsMenuOpen = true);
bool IsOutpostLevel() => GameMain.GameSession != null && Level.IsLoadedOutpost;
bool IsFriendlyOutpostLevel() => GameMain.GameSession != null && Level.IsLoadedFriendlyOutpost;
if (Screen.Selected == GameMain.GameScreen && GameMain.GameSession != null)
{
if (GameMain.GameSession.GameMode is SinglePlayerCampaign spMode)
@@ -2384,11 +2395,11 @@ namespace Barotrauma
GameMain.GameSession.LoadPreviousSave();
});
if (IsOutpostLevel())
if (IsFriendlyOutpostLevel())
{
CreateButton("PauseMenuSaveQuit", buttonContainer, verificationTextTag: "PauseMenuSaveAndReturnToMainMenuVerification", action: () =>
{
if (IsOutpostLevel()) { GameMain.QuitToMainMenu(save: true); }
if (IsFriendlyOutpostLevel()) { GameMain.QuitToMainMenu(save: true); }
});
}
}
@@ -2401,7 +2412,7 @@ namespace Barotrauma
}
else if (!GameMain.GameSession.GameMode.IsSinglePlayer && GameMain.Client != null && GameMain.Client.HasPermission(ClientPermissions.ManageRound))
{
bool canSave = GameMain.GameSession.GameMode is CampaignMode && IsOutpostLevel();
bool canSave = GameMain.GameSession.GameMode is CampaignMode && IsFriendlyOutpostLevel();
if (canSave)
{
CreateButton("PauseMenuSaveQuit", buttonContainer, verificationTextTag: "PauseMenuSaveAndReturnToServerLobbyVerification", action: () =>

View File

@@ -111,6 +111,11 @@ namespace Barotrauma
set { textBlock.SelectedTextColor = value; }
}
public Color DisabledTextColor
{
get { return textBlock.DisabledTextColor; }
}
public override float FlashTimer
{
get { return Frame.FlashTimer; }
@@ -159,7 +164,9 @@ namespace Barotrauma
private float pulseExpand;
private bool flashed;
public GUISoundType ClickSound { get; set; } = GUISoundType.Click;
public GUISoundType ClickSound { get; set; } = GUISoundType.Select;
public override bool PlaySoundOnSelect { get; set; } = true;
public GUIButton(RectTransform rectT, Alignment textAlignment = Alignment.Center, string style = "", Color? color = null) : this(rectT, new RawLString(""), textAlignment, style, color) { }
@@ -247,7 +254,10 @@ namespace Barotrauma
}
else if (PlayerInput.PrimaryMouseButtonClicked())
{
SoundPlayer.PlayUISound(ClickSound);
if (PlaySoundOnSelect)
{
SoundPlayer.PlayUISound(ClickSound);
}
if (OnClicked != null)
{
if (OnClicked(this, UserData))

View File

@@ -8,6 +8,8 @@ namespace Barotrauma
{
public class GUICanvas : RectTransform
{
private static readonly object mutex = new object();
protected GUICanvas() : base(size, parent: null) { }
private static GUICanvas _instance;
@@ -39,22 +41,25 @@ namespace Barotrauma
private static void OnChildrenChanged(RectTransform _)
{
//add weak reference if we don't have one yet
foreach (var child in _instance.Children)
lock (mutex)
{
if (!_instance.childrenWeakRef.Any(c => c.TryGetTarget(out var existingChild) && existingChild == child))
//add weak reference if we don't have one yet
foreach (var child in _instance.Children)
{
_instance.childrenWeakRef.Add(new WeakReference<RectTransform>(child));
if (!_instance.childrenWeakRef.Any(c => c.TryGetTarget(out var existingChild) && existingChild == child))
{
_instance.childrenWeakRef.Add(new WeakReference<RectTransform>(child));
}
}
}
//get rid of strong references
_instance.children.Clear();
//remove dead children
for (int i = _instance.childrenWeakRef.Count - 2; i >= 0; i--)
{
if (!_instance.childrenWeakRef[i].TryGetTarget(out var child) || child.Parent != _instance)
//get rid of strong references
_instance.children.Clear();
//remove dead children
for (int i = _instance.childrenWeakRef.Count - 2; i >= 0; i--)
{
_instance.childrenWeakRef.RemoveAt(i);
if (!_instance.childrenWeakRef[i].TryGetTarget(out var child) || child.Parent != _instance)
{
_instance.childrenWeakRef.RemoveAt(i);
}
}
}
}

View File

@@ -383,6 +383,8 @@ namespace Barotrauma
public bool ExternalHighlight = false;
public virtual bool PlaySoundOnSelect { get; set; } = false;
private RectTransform rectTransform;
public RectTransform RectTransform
{

View File

@@ -113,7 +113,8 @@ namespace Barotrauma
{
AutoHideScrollBar = false,
ScrollBarVisible = false,
Padding = hasHeader ? new Vector4(4, 0, 4, 4) : padding
Padding = hasHeader ? new Vector4(4, 0, 4, 4) : padding,
PlaySoundOnSelect = true
};
foreach (var (option, size) in optionsAndSizes)
@@ -290,7 +291,7 @@ namespace Barotrauma
public override void AddToGUIUpdateList(bool ignoreChildren = false, int order = 0)
{
base.AddToGUIUpdateList(ignoreChildren, order);
SubMenu?.AddToGUIUpdateList();
SubMenu?.AddToGUIUpdateList(order: 2);
}
public static void AddActiveToGUIUpdateList()
@@ -300,7 +301,7 @@ namespace Barotrauma
CurrentContextMenu = null;
}
CurrentContextMenu?.AddToGUIUpdateList();
CurrentContextMenu?.AddToGUIUpdateList(order: 2);
}
}
}

View File

@@ -160,6 +160,10 @@ namespace Barotrauma
listBox.ToolTip = value;
}
}
public GUIImage DropDownIcon => icon;
public Vector4 Padding => button.TextBlock.Padding;
public GUIDropDown(RectTransform rectT, LocalizedString text = null, int elementCount = 4, string style = "", bool selectMultiple = false, bool dropAbove = false, Alignment textAlignment = Alignment.CenterLeft) : base(style, rectT)
{
@@ -183,7 +187,8 @@ namespace Barotrauma
listBox = new GUIListBox(new RectTransform(new Point(Rect.Width, Rect.Height * MathHelper.Clamp(elementCount, 2, 10)), rectT, listAnchor, listPivot)
{ IsFixedSize = false }, style: null)
{
Enabled = !selectMultiple
Enabled = !selectMultiple,
PlaySoundOnSelect = true,
};
if (!selectMultiple) { listBox.OnSelected = SelectItem; }
GUIStyle.Apply(listBox, "GUIListBox", this);

View File

@@ -309,6 +309,45 @@ namespace Barotrauma
}
}
public override bool PlaySoundOnSelect { get; set; } = false;
public bool PlaySoundOnDragStop { get; set; } = false;
public GUISoundType? SoundOnDragStart { get; set; } = null;
public GUISoundType? SoundOnDragStop { get; set; } = null;
#region enums
public enum Force
{
Yes,
No
}
public enum AutoScroll
{
Enabled,
Disabled
}
public enum TakeKeyBoardFocus
{
Yes,
No
}
public enum PlaySelectSound
{
Yes,
No
}
private AutoScroll GetAutoScroll(bool b)
{
return b ? AutoScroll.Enabled : AutoScroll.Disabled;
}
#endregion
/// <param name="isScrollBarOnDefaultSide">For horizontal listbox, default side is on the bottom. For vertical, it's on the right.</param>
public GUIListBox(RectTransform rectT, bool isHorizontal = false, Color? color = null, string style = "", bool isScrollBarOnDefaultSide = true, bool useMouseDownToSelect = false) : base(style, rectT)
{
@@ -396,7 +435,7 @@ namespace Barotrauma
UpdateScrollBarSize();
}
public void Select(object userData, bool force = false, bool autoScroll = true)
public void Select(object userData, Force force = Force.No, AutoScroll autoScroll = AutoScroll.Enabled)
{
var children = Content.Children;
int i = 0;
@@ -515,9 +554,12 @@ namespace Barotrauma
/// Scrolls the list to the specific element.
/// </summary>
/// <param name="component"></param>
public void ScrollToElement(GUIComponent component, bool playSound = true)
public void ScrollToElement(GUIComponent component, PlaySelectSound playSelectSound = PlaySelectSound.No)
{
if (playSound) { SoundPlayer.PlayUISound(GUISoundType.Click); }
if (playSelectSound == PlaySelectSound.Yes)
{
SoundPlayer.PlayUISound(GUISoundType.Select);
}
List<GUIComponent> children = Content.Children.ToList();
int index = children.IndexOf(component);
if (index < 0) { return; }
@@ -573,9 +615,16 @@ namespace Barotrauma
}
}
private double lastDragStartTime;
private void StartDraggingElement(GUIComponent child)
{
DraggedElement = child;
if (Timing.TotalTime > lastDragStartTime + 0.2f)
{
lastDragStartTime = Timing.TotalTime;
SoundPlayer.PlayUISound(SoundOnDragStart);
}
}
private bool UpdateDragging()
@@ -586,6 +635,10 @@ namespace Barotrauma
var draggedElem = draggedElement;
OnRearranged?.Invoke(this, draggedElem.UserData);
DraggedElement = null;
if (PlaySoundOnDragStop)
{
SoundPlayer.PlayUISound(SoundOnDragStop);
}
RepositionChildren();
if (AllSelected.Contains(draggedElem)) { return true; }
}
@@ -710,7 +763,7 @@ namespace Barotrauma
int index = Content.Children.ToList().IndexOf(component);
if (index >= 0)
{
Select(index, false, false, takeKeyBoardFocus: true);
Select(index, autoScroll: AutoScroll.Disabled, takeKeyBoardFocus: TakeKeyBoardFocus.Yes);
}
}
}
@@ -733,7 +786,7 @@ namespace Barotrauma
{
ScrollToElement(child);
}
Select(i, autoScroll: false, takeKeyBoardFocus: true);
Select(i, autoScroll: AutoScroll.Disabled, takeKeyBoardFocus: TakeKeyBoardFocus.Yes, playSelectSound: PlaySelectSound.Yes);
}
if (CurrentDragMode != DragMode.NoDragging
@@ -929,14 +982,13 @@ namespace Barotrauma
if (ClampScrollToElements)
{
bool scrollDown = Math.Clamp(PlayerInput.ScrollWheelSpeed, 0, 1) > 0;
if (scrollDown)
{
SelectPrevious(takeKeyBoardFocus: true);
SelectPrevious(takeKeyBoardFocus: TakeKeyBoardFocus.Yes, playSelectSound: PlaySelectSound.Yes);
}
else
{
SelectNext(takeKeyBoardFocus: true);
SelectNext(takeKeyBoardFocus: TakeKeyBoardFocus.Yes, playSelectSound: PlaySelectSound.Yes);
}
}
}
@@ -964,7 +1016,7 @@ namespace Barotrauma
return FindScrollableParentListBox(target.Parent);
}
public void SelectNext(bool force = false, bool autoScroll = true, bool takeKeyBoardFocus = false)
public void SelectNext(Force force = Force.No, AutoScroll autoScroll = AutoScroll.Enabled, TakeKeyBoardFocus takeKeyBoardFocus = TakeKeyBoardFocus.No, PlaySelectSound playSelectSound = PlaySelectSound.No)
{
int index = SelectedIndex + 1;
while (index < Content.CountChildren)
@@ -972,10 +1024,10 @@ namespace Barotrauma
GUIComponent child = Content.GetChild(index);
if (child.Visible)
{
Select(index, force, !SmoothScroll && autoScroll, takeKeyBoardFocus: takeKeyBoardFocus);
Select(index, force, GetAutoScroll(!SmoothScroll && autoScroll == AutoScroll.Enabled), takeKeyBoardFocus, playSelectSound);
if (SmoothScroll)
{
ScrollToElement(child);
ScrollToElement(child, playSelectSound);
}
break;
}
@@ -983,7 +1035,7 @@ namespace Barotrauma
}
}
public void SelectPrevious(bool force = false, bool autoScroll = true, bool takeKeyBoardFocus = false)
public void SelectPrevious(Force force = Force.No, AutoScroll autoScroll = AutoScroll.Enabled, TakeKeyBoardFocus takeKeyBoardFocus = TakeKeyBoardFocus.No, PlaySelectSound playSelectSound = PlaySelectSound.No)
{
int index = SelectedIndex - 1;
while (index >= 0)
@@ -991,10 +1043,10 @@ namespace Barotrauma
GUIComponent child = Content.GetChild(index);
if (child.Visible)
{
Select(index, force, !SmoothScroll && autoScroll, takeKeyBoardFocus: takeKeyBoardFocus);
Select(index, force, GetAutoScroll(!SmoothScroll && autoScroll == AutoScroll.Enabled), takeKeyBoardFocus, playSelectSound);
if (SmoothScroll)
{
ScrollToElement(child);
ScrollToElement(child, playSelectSound);
}
break;
}
@@ -1002,7 +1054,7 @@ namespace Barotrauma
}
}
public void Select(int childIndex, bool force = false, bool autoScroll = true, bool takeKeyBoardFocus = false)
public void Select(int childIndex, Force force = Force.No, AutoScroll autoScroll = AutoScroll.Enabled, TakeKeyBoardFocus takeKeyBoardFocus = TakeKeyBoardFocus.No, PlaySelectSound playSelectSound = PlaySelectSound.No)
{
if (childIndex >= Content.CountChildren || childIndex < 0) { return; }
@@ -1013,7 +1065,7 @@ namespace Barotrauma
if (OnSelected != null)
{
// TODO: The callback is called twice, fix this!
wasSelected = force || OnSelected(child, child.UserData);
wasSelected = force == Force.Yes || OnSelected(child, child.UserData);
}
if (!wasSelected) { return; }
@@ -1055,7 +1107,7 @@ namespace Barotrauma
// Ensure that the selected element is visible. This may not be the case, if the selection is run from code. (e.g. if we have two list boxes that are synced)
// TODO: This method only works when moving one item up/down (e.g. when using the up and down arrows)
if (autoScroll)
if (autoScroll == AutoScroll.Enabled)
{
if (ScrollBar.IsHorizontal)
{
@@ -1086,11 +1138,19 @@ namespace Barotrauma
}
// If one of the children is the subscriber, we don't want to register, because it will unregister the child.
if (takeKeyBoardFocus && CanTakeKeyBoardFocus && RectTransform.GetAllChildren().None(rt => rt.GUIComponent == GUI.KeyboardDispatcher.Subscriber))
if (takeKeyBoardFocus == TakeKeyBoardFocus.Yes && CanTakeKeyBoardFocus && RectTransform.GetAllChildren().None(rt => rt.GUIComponent == GUI.KeyboardDispatcher.Subscriber))
{
Selected = true;
GUI.KeyboardDispatcher.Subscriber = this;
}
// List box child components can be parents to other components that can play sounds when selected (e.g. store elements)
// so the list box shouldn't play the Select sound if the GUI.MouseOn component has a sound to play
if (playSelectSound == PlaySelectSound.Yes && PlaySoundOnSelect && !child.PlaySoundOnSelect &&
(GUI.MouseOn == null || GUI.MouseOn.Parent == Content || !GUI.MouseOn.PlaySoundOnSelect))
{
SoundPlayer.PlayUISound(GUISoundType.Select);
}
}
public void Select(IEnumerable<GUIComponent> children)
@@ -1293,16 +1353,16 @@ namespace Barotrauma
switch (key)
{
case Keys.Down:
if (!isHorizontal && AllowArrowKeyScroll) { SelectNext(); }
if (!isHorizontal && AllowArrowKeyScroll) { SelectNext(playSelectSound: PlaySelectSound.Yes); }
break;
case Keys.Up:
if (!isHorizontal && AllowArrowKeyScroll) { SelectPrevious(); }
if (!isHorizontal && AllowArrowKeyScroll) { SelectPrevious(playSelectSound: PlaySelectSound.Yes); }
break;
case Keys.Left:
if (isHorizontal && AllowArrowKeyScroll) { SelectPrevious(); }
if (isHorizontal && AllowArrowKeyScroll) { SelectPrevious(playSelectSound: PlaySelectSound.Yes); }
break;
case Keys.Right:
if (isHorizontal && AllowArrowKeyScroll) { SelectNext(); }
if (isHorizontal && AllowArrowKeyScroll) { SelectNext(playSelectSound: PlaySelectSound.Yes); }
break;
case Keys.Enter:
case Keys.Space:

View File

@@ -7,11 +7,6 @@ namespace Barotrauma
{
class GUINumberInput : GUIComponent
{
public enum NumberType
{
Int, Float
}
public delegate void OnValueEnteredHandler(GUINumberInput numberInput);
public OnValueEnteredHandler OnValueEntered;
@@ -187,7 +182,7 @@ namespace Barotrauma
public float valueStep;
private float pressedTimer;
private float pressedDelay = 0.5f;
private readonly float pressedDelay = 0.5f;
private bool IsPressedTimerRunning { get { return pressedTimer > 0; } }
public GUINumberInput(RectTransform rectT, NumberType inputType, string style = "", Alignment textAlignment = Alignment.Center, float? relativeButtonAreaWidth = null, bool hidePlusMinusButtons = false) : base(style, rectT)
@@ -233,6 +228,7 @@ namespace Barotrauma
var buttonArea = new GUIFrame(new RectTransform(new Vector2(_relativeButtonAreaWidth, 1.0f), LayoutGroup.RectTransform, Anchor.CenterRight), style: null);
PlusButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.5f), buttonArea.RectTransform), style: null);
GUIStyle.Apply(PlusButton, "PlusButton", this);
PlusButton.ClickSound = GUISoundType.Increase;
PlusButton.OnButtonDown += () =>
{
pressedTimer = pressedDelay;
@@ -254,6 +250,7 @@ namespace Barotrauma
MinusButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.5f), buttonArea.RectTransform, Anchor.BottomRight), style: null);
GUIStyle.Apply(MinusButton, "MinusButton", this);
MinusButton.ClickSound = GUISoundType.Decrease;
MinusButton.OnButtonDown += () =>
{
pressedTimer = pressedDelay;
@@ -428,8 +425,8 @@ namespace Barotrauma
intValue = Math.Min(intValue, MaxValueInt.Value);
UpdateText();
}
PlusButton.Enabled = intValue < MaxValueInt;
MinusButton.Enabled = intValue > MinValueInt;
PlusButton.Enabled = MaxValueInt == null || intValue < MaxValueInt;
MinusButton.Enabled = MinValueInt == null || intValue > MinValueInt;
}
private void UpdateText()

View File

@@ -98,7 +98,6 @@ namespace Barotrauma
foreach (var subElement in element.Elements().Reverse())
{
if (subElement.NameAsIdentifier() != "override") { continue; }
if (subElement.GetAttributeBool("iscjk", false))
{
return new ScalableFont(subElement, GameMain.Instance.GraphicsDevice);
@@ -111,8 +110,7 @@ namespace Barotrauma
{
foreach (var subElement in element.Elements())
{
if (!subElement.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase)) { continue; }
if (GameSettings.CurrentConfig.Language == subElement.GetAttributeIdentifier("language", "").ToLanguageIdentifier())
if (IsValidOverride(subElement))
{
return subElement.GetAttributeContentPath("file")?.Value;
}
@@ -125,8 +123,7 @@ namespace Barotrauma
//check if any of the language override fonts want to override the font size as well
foreach (var subElement in element.Elements())
{
if (!subElement.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase)) { continue; }
if (GameSettings.CurrentConfig.Language == subElement.GetAttributeIdentifier("language", "").ToLanguageIdentifier())
if (IsValidOverride(subElement))
{
uint overrideFontSize = GetFontSize(subElement, 0);
if (overrideFontSize > 0) { return (uint)Math.Round(overrideFontSize * GameSettings.CurrentConfig.Graphics.TextScale); }
@@ -149,8 +146,7 @@ namespace Barotrauma
{
foreach (var subElement in element.Elements())
{
if (!subElement.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase)) { continue; }
if (GameSettings.CurrentConfig.Language == subElement.GetAttributeIdentifier("language", "").ToLanguageIdentifier())
if (IsValidOverride(subElement))
{
return subElement.GetAttributeBool("dynamicloading", false);
}
@@ -162,14 +158,20 @@ namespace Barotrauma
{
foreach (var subElement in element.Elements())
{
if (!subElement.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase)) { continue; }
if (GameSettings.CurrentConfig.Language == subElement.GetAttributeIdentifier("language", "").ToLanguageIdentifier())
if (IsValidOverride(subElement))
{
return subElement.GetAttributeBool("iscjk", false);
}
}
return element.GetAttributeBool("iscjk", false);
}
private bool IsValidOverride(XElement element)
{
if (!element.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase)) { return false; }
var languages = element.GetAttributeIdentifierArray("language", Array.Empty<Identifier>());
return languages.Any(l => l.ToLanguageIdentifier() == GameSettings.CurrentConfig.Language);
}
}
public class GUIFont : GUISelector<GUIFontPrefab>

View File

@@ -322,9 +322,8 @@ namespace Barotrauma
{
if (!enabled || !PlayerInput.PrimaryMouseButtonDown()) { return false; }
if (barSize >= 1.0f) { return false; }
DraggingBar = this;
SoundPlayer.PlayUISound(GUISoundType.Select);
return true;
}

View File

@@ -34,7 +34,6 @@ namespace Barotrauma
public readonly static PrefabCollection<GUIComponentStyle> ComponentStyles = new PrefabCollection<GUIComponentStyle>();
public readonly static GUIFont Font = new GUIFont("Font");
public readonly static GUIFont GlobalFont = new GUIFont("GlobalFont");
public readonly static GUIFont UnscaledSmallFont = new GUIFont("UnscaledSmallFont");
public readonly static GUIFont SmallFont = new GUIFont("SmallFont");
public readonly static GUIFont LargeFont = new GUIFont("LargeFont");
@@ -142,10 +141,6 @@ namespace Barotrauma
public readonly static GUIColor HealthBarColorMedium = new GUIColor("HealthBarColorMedium");
public readonly static GUIColor HealthBarColorHigh = new GUIColor("HealthBarColorHigh");
public readonly static GUIColor EquipmentIndicatorNotEquipped = new GUIColor("EquipmentIndicatorNotEquipped");
public readonly static GUIColor EquipmentIndicatorEquipped = new GUIColor("EquipmentIndicatorEquipped");
public readonly static GUIColor EquipmentIndicatorRunningOut = new GUIColor("EquipmentIndicatorRunningOut");
public static Point ItemFrameMargin => new Point(50, 56).Multiply(GUI.SlicedSpriteScale);
public static Point ItemFrameOffset => new Point(0, 3).Multiply(GUI.SlicedSpriteScale);
@@ -159,7 +154,7 @@ namespace Barotrauma
public static void Apply(GUIComponent targetComponent, Identifier styleName, GUIComponent parent = null)
{
GUIComponentStyle componentStyle = null;
GUIComponentStyle componentStyle;
if (parent != null)
{
GUIComponentStyle parentStyle = parent.Style;
@@ -212,5 +207,21 @@ namespace Barotrauma
return ItemQualityColorNormal;
}
}
public static void RecalculateFonts()
{
foreach (var font in Fonts.Values)
{
font.Prefabs.ForEach(p => p.LoadFont());
}
}
public static void RecalculateSizeRestrictions()
{
foreach (var componentStyle in ComponentStyles)
{
componentStyle.RefreshSize();
}
}
}
}

View File

@@ -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();

View File

@@ -261,6 +261,8 @@ namespace Barotrauma
public bool Readonly { get; set; }
public override bool PlaySoundOnSelect { get; set; } = true;
public GUITextBox(RectTransform rectT, string text = "", Color? textColor = null, GUIFont font = null,
Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null, bool createClearButton = false, bool createPenIcon = true)
: base(style, rectT)
@@ -271,7 +273,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;
@@ -360,7 +362,7 @@ namespace Barotrauma
caretPosDirty = false;
}
public void Select(int forcedCaretIndex = -1)
public void Select(int forcedCaretIndex = -1, bool ignoreSelectSound = false)
{
skipUpdate = true;
if (memento.Current == null)
@@ -370,9 +372,14 @@ namespace Barotrauma
CaretIndex = forcedCaretIndex == - 1 ? textBlock.GetCaretIndexFromScreenPos(PlayerInput.MousePosition) : forcedCaretIndex;
CalculateCaretPos();
ClearSelection();
bool wasSelected = selected;
selected = true;
GUI.KeyboardDispatcher.Subscriber = this;
OnSelected?.Invoke(this, Keys.None);
if (!wasSelected && PlaySoundOnSelect && !ignoreSelectSound)
{
SoundPlayer.PlayUISound(GUISoundType.Select);
}
}
public void Deselect()

View File

@@ -1,15 +1,13 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
namespace Barotrauma
{
public class GUITickBox : GUIComponent
{
private GUILayoutGroup layoutGroup;
private GUIFrame box;
private GUITextBlock text;
private readonly GUILayoutGroup layoutGroup;
private readonly GUIFrame box;
private readonly GUITextBlock text;
public delegate bool OnSelectedHandler(GUITickBox obj);
public OnSelectedHandler OnSelected;
@@ -129,6 +127,12 @@ namespace Barotrauma
set { text.Text = value; }
}
public float ContentWidth { get; private set; }
public GUISoundType SoundType { private get; set; } = GUISoundType.TickBox;
public override bool PlaySoundOnSelect { get; set; } = true;
public GUITickBox(RectTransform rectT, LocalizedString label, GUIFont font = null, string style = "") : base(null, rectT)
{
CanBeFocused = true;
@@ -180,6 +184,7 @@ namespace Barotrauma
box.RectTransform.MinSize = new Point(Rect.Height);
box.RectTransform.Resize(box.RectTransform.MinSize);
text.SetTextPos();
ContentWidth = box.Rect.Width + text.Padding.X + text.TextSize.X + text.Padding.Z;
}
protected override void Update(float deltaTime)
@@ -209,6 +214,10 @@ namespace Barotrauma
{
Selected = true;
}
if (PlaySoundOnSelect)
{
SoundPlayer.PlayUISound(SoundType);
}
}
}
else if (isSelected)

View File

@@ -1,6 +1,7 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Linq;
namespace Barotrauma
{
@@ -14,7 +15,7 @@ namespace Barotrauma
get { return inventoryTopY; }
set
{
if (value == inventoryTopY) return;
if (value == inventoryTopY) { return; }
inventoryTopY = value;
CreateAreas();
}
@@ -90,8 +91,6 @@ namespace Barotrauma
if (GameMain.Instance != null)
{
GameMain.Instance.ResolutionChanged += CreateAreas;
#warning TODO: reimplement
//GameSettings.CurrentConfig.OnHUDScaleChanged += CreateAreas;
CreateAreas();
CharacterInfo.Init();
}
@@ -122,7 +121,17 @@ namespace Barotrauma
//horizontal slices at the corners of the screen for health bar and affliction icons
int afflictionAreaHeight = (int)(50 * GUI.Scale);
int healthBarWidth = (int)(BottomRightInfoArea.Width * 1.58f);
int healthBarWidth = BottomRightInfoArea.Width;
var healthBarChildStyles = GUIStyle.GetComponentStyle("CharacterHealthBar")?.ChildStyles;
if (healthBarChildStyles!= null && healthBarChildStyles.TryGetValue("GUIFrame".ToIdentifier(), out var style))
{
if (style.Sprites.TryGetValue(GUIComponent.ComponentState.None, out var uiSprites) && uiSprites.FirstOrDefault() is { } uiSprite)
{
// The default health bar uses a sliced sprite so let's make sure the health bar area is calculated accordingly
healthBarWidth += (int)(uiSprite.NonSliceSize.X * Math.Min(GUI.Scale, 1f));
}
}
int healthBarHeight = (int)(50f * GUI.Scale);
HealthBarArea = new Rectangle(BottomRightInfoArea.Right - healthBarWidth + (int)Math.Floor(1 / GUI.Scale), BottomRightInfoArea.Y - healthBarHeight + GUI.IntScale(10), healthBarWidth, healthBarHeight);
AfflictionAreaLeft = new Rectangle(HealthBarArea.X, HealthBarArea.Y - Padding - afflictionAreaHeight, HealthBarArea.Width, afflictionAreaHeight);

View File

@@ -569,6 +569,7 @@ namespace Barotrauma
GUILayoutGroup buttonLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.5f), footerLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterRight);
GUIButton healButton = new GUIButton(new RectTransform(new Vector2(0.33f, 1f), buttonLayout.RectTransform), TextManager.Get("medicalclinic.heal"))
{
ClickSound = GUISoundType.ConfirmTransaction,
Enabled = medicalClinic.PendingHeals.Any() && medicalClinic.GetBalance() >= medicalClinic.GetTotalCost(),
OnClicked = (button, _) =>
{
@@ -595,6 +596,7 @@ namespace Barotrauma
GUIButton clearButton = new GUIButton(new RectTransform(new Vector2(0.33f, 1f), buttonLayout.RectTransform), TextManager.Get("campaignstore.clearall"))
{
ClickSound = GUISoundType.Cart,
OnClicked = (button, _) =>
{
button.Enabled = false;
@@ -657,6 +659,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,13 +679,14 @@ 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
};
GUIButton healButton = new GUIButton(new RectTransform(new Vector2(0.2f, 1f), textLayout.RectTransform), style: "CrewManagementRemoveButton")
{
ClickSound = GUISoundType.Cart,
OnClicked = (button, _) =>
{
button.Enabled = false;
@@ -746,12 +750,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)
{
@@ -765,6 +769,7 @@ namespace Barotrauma
GUIButton treatAllButton = new GUIButton(new RectTransform(new Vector2(1f, 0.2f), mainLayout.RectTransform), TextManager.Get("medicalclinic.treatall"))
{
ClickSound = GUISoundType.Cart,
Font = GUIStyle.SubHeadingFont,
Visible = false
};
@@ -819,7 +824,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,15 +869,32 @@ 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");
GUIButton buyButton = new GUIButton(new RectTransform(new Vector2(0.2f, 0.75f), bottomLayout.RectTransform), style: "CrewManagementAddButton")
{
ClickSound = GUISoundType.Cart
};
ImmutableArray<GUIComponent> elementsToDisable = ImmutableArray.Create<GUIComponent>(prefabBlock, backgroundFrame, icon, vitalityBlock, severityBlock, buyButton, descriptionBlock, priceBlock);
@@ -923,6 +947,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; }

View File

@@ -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("reputationmodifier"), 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);
@@ -707,7 +656,7 @@ namespace Barotrauma
SetConfirmButtonBehavior();
clearAllButton = new GUIButton(new RectTransform(new Vector2(0.35f, 1.0f), buttonContainer.RectTransform), TextManager.Get("campaignstore.clearall"))
{
ClickSound = GUISoundType.DecreaseQuantity,
ClickSound = GUISoundType.Cart,
Enabled = HasActiveTabPermissions(),
ForceUpperCase = ForceUpperCase.Yes,
OnClicked = (button, userData) =>
@@ -1597,7 +1546,7 @@ namespace Barotrauma
{
RelativeSpacing = 0.02f
};
amountInput = new GUINumberInput(new RectTransform(new Vector2(0.4f, 1.0f), shoppingCrateAmountGroup.RectTransform), GUINumberInput.NumberType.Int)
amountInput = new GUINumberInput(new RectTransform(new Vector2(0.4f, 1.0f), shoppingCrateAmountGroup.RectTransform), NumberType.Int)
{
MinValueInt = 0,
MaxValueInt = GetMaxAvailable(pi.ItemPrefab, containingTab),
@@ -1618,8 +1567,6 @@ namespace Barotrauma
}
AddToShoppingCrate(purchasedItem, quantity: numberInput.IntValue - purchasedItem.Quantity);
};
amountInput.PlusButton.ClickSound = GUISoundType.IncreaseQuantity;
amountInput.MinusButton.ClickSound = GUISoundType.DecreaseQuantity;
frame.HoverColor = frame.SelectedColor = Color.Transparent;
}
@@ -1673,7 +1620,7 @@ namespace Barotrauma
{
new GUIButton(new RectTransform(new Vector2(buttonRelativeWidth, 0.9f), mainGroup.RectTransform), style: "StoreAddToCrateButton")
{
ClickSound = GUISoundType.IncreaseQuantity,
ClickSound = GUISoundType.Cart,
Enabled = !forceDisable && pi.Quantity > 0,
ForceUpperCase = ForceUpperCase.Yes,
UserData = "addbutton",
@@ -1684,7 +1631,7 @@ namespace Barotrauma
{
new GUIButton(new RectTransform(new Vector2(buttonRelativeWidth, 0.9f), mainGroup.RectTransform), style: "StoreRemoveFromCrateButton")
{
ClickSound = GUISoundType.DecreaseQuantity,
ClickSound = GUISoundType.Cart,
Enabled = !forceDisable,
ForceUpperCase = ForceUpperCase.Yes,
UserData = "removebutton",
@@ -2127,11 +2074,13 @@ namespace Barotrauma
{
if (IsBuying)
{
confirmButton.ClickSound = GUISoundType.ConfirmTransaction;
confirmButton.Text = TextManager.Get("CampaignStore.Purchase");
confirmButton.OnClicked = (b, o) => BuyItems();
}
else
{
confirmButton.ClickSound = GUISoundType.Select;
confirmButton.Text = TextManager.Get("CampaignStoreTab.Sell");
confirmButton.OnClicked = (b, o) =>
{
@@ -2139,6 +2088,7 @@ namespace Barotrauma
TextManager.Get("FireWarningHeader"),
TextManager.Get("CampaignStore.SellWarningText"),
new LocalizedString[] { TextManager.Get("Yes"), TextManager.Get("No") });
confirmDialog.Buttons[0].ClickSound = GUISoundType.ConfirmTransaction;
confirmDialog.Buttons[0].OnClicked = (b, o) => SellItems();
confirmDialog.Buttons[0].OnClicked += confirmDialog.Close;
confirmDialog.Buttons[1].OnClicked = confirmDialog.Close;
@@ -2258,7 +2208,7 @@ namespace Barotrauma
}
updateStopwatch.Stop();
GameMain.PerformanceCounter.AddPartialElapsedTicks("GameSessionUpdate", "StoreUpdate", updateStopwatch.ElapsedTicks);
GameMain.PerformanceCounter.AddElapsedTicks("Update:GameSession:Store", updateStopwatch.ElapsedTicks);
}
}
}

View File

@@ -29,6 +29,8 @@ namespace Barotrauma
private GUITextBlock descriptionTextBlock;
private int selectionIndicatorThickness;
private GUIImage listBackground;
private GUITickBox transferItemsTickBox;
private GUITextBlock itemTransferReminderBlock;
private readonly List<SubmarineInfo> subsToShow;
private readonly SubmarineDisplayContent[] submarineDisplays = new SubmarineDisplayContent[submarinesPerPage];
@@ -61,6 +63,23 @@ namespace Barotrauma
public GUIButton previewButton;
}
private bool TransferItemsOnSwitch
{
get
{
return transferItemsOnSwitch;
}
set
{
transferItemsOnSwitch = value;
if (transferItemsTickBox != null)
{
transferItemsTickBox.Selected = value;
}
}
}
private bool transferItemsOnSwitch = true;
public SubmarineSelection(bool transfer, Action closeAction, RectTransform parent)
{
if (GameMain.GameSession.Campaign == null) { return; }
@@ -149,11 +168,12 @@ namespace Barotrauma
GUIListBox descriptionFrame = new GUIListBox(new RectTransform(new Vector2(0.59f, 1f), infoFrame.RectTransform), style: null) { Padding = new Vector4(HUDLayoutSettings.Padding / 2f, HUDLayoutSettings.Padding * 1.5f, HUDLayoutSettings.Padding * 1.5f, HUDLayoutSettings.Padding / 2f) };
descriptionTextBlock = new GUITextBlock(new RectTransform(new Vector2(1, 0), descriptionFrame.Content.RectTransform), string.Empty, font: GUIStyle.Font, wrap: true) { CanBeFocused = false };
GUILayoutGroup buttonFrame = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.075f), content.RectTransform), childAnchor: Anchor.CenterRight) { IsHorizontal = true, AbsoluteSpacing = HUDLayoutSettings.Padding };
GUILayoutGroup bottomContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.075f), content.RectTransform, Anchor.CenterRight), childAnchor: Anchor.CenterRight) { IsHorizontal = true, AbsoluteSpacing = HUDLayoutSettings.Padding };
float transferInfoFrameWidth = 1.0f;
if (closeAction != null)
{
GUIButton closeButton = new GUIButton(new RectTransform(new Vector2(0.2f, 1f), buttonFrame.RectTransform), TextManager.Get("Close"), style: "GUIButtonFreeScale")
GUIButton closeButton = new GUIButton(new RectTransform(new Vector2(0.2f, 1f), bottomContainer.RectTransform), TextManager.Get("Close"), style: "GUIButtonFreeScale")
{
OnClicked = (button, userData) =>
{
@@ -161,11 +181,33 @@ namespace Barotrauma
return true;
}
};
transferInfoFrameWidth -= closeButton.RectTransform.RelativeSize.X;
}
if (purchaseService) confirmButtonAlt = new GUIButton(new RectTransform(new Vector2(0.2f, 1f), buttonFrame.RectTransform), purchaseOnlyText, style: "GUIButtonFreeScale");
confirmButton = new GUIButton(new RectTransform(new Vector2(0.2f, 1f), buttonFrame.RectTransform), purchaseService ? purchaseAndSwitchText : deliveryFee > 0 ? deliveryText : switchText, style: "GUIButtonFreeScale");
if (purchaseService)
{
confirmButtonAlt = new GUIButton(new RectTransform(new Vector2(0.2f, 1f), bottomContainer.RectTransform), purchaseOnlyText, style: "GUIButtonFreeScale");
transferInfoFrameWidth -= confirmButtonAlt.RectTransform.RelativeSize.X;
}
confirmButton = new GUIButton(new RectTransform(new Vector2(0.2f, 1f), bottomContainer.RectTransform), purchaseService ? purchaseAndSwitchText : deliveryFee > 0 ? deliveryText : switchText, style: "GUIButtonFreeScale");
SetConfirmButtonState(false);
transferInfoFrameWidth -= confirmButton.RectTransform.RelativeSize.X;
GUIFrame transferInfoFrame = new GUIFrame(new RectTransform(new Vector2(transferInfoFrameWidth, 1.0f), bottomContainer.RectTransform), style: null)
{
CanBeFocused = false
};
transferItemsTickBox = new GUITickBox(new RectTransform(new Vector2(0.2f, 1.0f), transferInfoFrame.RectTransform, Anchor.CenterRight), TextManager.Get("transferitems"), font: GUIStyle.SubHeadingFont)
{
Selected = TransferItemsOnSwitch,
Visible = false,
OnSelected = (tb) => transferItemsOnSwitch = tb.Selected
};
transferItemsTickBox.RectTransform.Resize(new Point(Math.Min((int)transferItemsTickBox.ContentWidth, transferInfoFrame.Rect.Width), transferItemsTickBox.Rect.Height));
itemTransferReminderBlock = new GUITextBlock(new RectTransform(Vector2.One, transferInfoFrame.RectTransform, Anchor.CenterRight), null)
{
TextAlignment = Alignment.CenterRight,
Visible = false
};
pageIndicatorHolder = new GUIFrame(new RectTransform(new Vector2(1f, 1.5f), submarineControlsGroup.RectTransform), style: null);
pageIndicator = GUIStyle.GetComponentStyle("GUIPageIndicator").GetDefaultSprite();
@@ -272,7 +314,7 @@ namespace Barotrauma
}
}
public void RefreshSubmarineDisplay(bool updateSubs)
public void RefreshSubmarineDisplay(bool updateSubs, bool setTransferOptionToTrue = false)
{
if (!initialized)
{
@@ -286,6 +328,10 @@ namespace Barotrauma
{
playerBalanceElement = CampaignUI.UpdateBalanceElement(playerBalanceElement);
}
if (setTransferOptionToTrue)
{
TransferItemsOnSwitch = true;
}
if (updateSubs)
{
UpdateSubmarines();
@@ -401,6 +447,10 @@ namespace Barotrauma
{
SelectSubmarine(null, Rectangle.Empty);
}
else
{
UpdateItemTransferInfoFrame();
}
}
private void UpdateSubmarines()
@@ -553,6 +603,40 @@ namespace Barotrauma
selectedSubmarineIndicator.RectTransform.NonScaledSize = Point.Zero;
SetConfirmButtonState(false);
}
UpdateItemTransferInfoFrame();
}
private void UpdateItemTransferInfoFrame()
{
if (selectedSubmarine != null)
{
var pendingSub = GameMain.GameSession?.Campaign?.PendingSubmarineSwitch;
if (Submarine.MainSub?.Info?.Name == selectedSubmarine.Name && pendingSub == null)
{
transferItemsTickBox.Visible = false;
itemTransferReminderBlock.Visible = false;
}
else if (pendingSub?.Name == selectedSubmarine.Name)
{
transferItemsTickBox.Visible = false;
itemTransferReminderBlock.Text = GameMain.GameSession.Campaign.TransferItemsOnSubSwitch ?
TextManager.Get("itemtransferenabledreminder") :
TextManager.Get("itemtransferdisabledreminder");
itemTransferReminderBlock.Visible = true;
}
else
{
transferItemsTickBox.Selected = TransferItemsOnSwitch;
transferItemsTickBox.Visible = true;
itemTransferReminderBlock.Visible = false;
}
}
else
{
transferItemsTickBox.Visible = false;
itemTransferReminderBlock.Visible = false;
}
}
private void SetConfirmButtonState(bool state)
@@ -614,24 +698,27 @@ namespace Barotrauma
("[submarinename2]", CurrentOrPendingSubmarine().DisplayName),
("[amount]", deliveryFee.ToString()),
("[currencyname]", currencyName)), messageBoxOptions);
msgBox.Buttons[0].ClickSound = GUISoundType.ConfirmTransaction;
}
else
{
msgBox = new GUIMessageBox(TextManager.Get("switchsubmarineheader"), TextManager.GetWithVariables("switchsubmarinetext",
var text = TextManager.GetWithVariables("switchsubmarinetext",
("[submarinename1]", CurrentOrPendingSubmarine().DisplayName),
("[submarinename2]", selectedSubmarine.DisplayName)), messageBoxOptions);
("[submarinename2]", selectedSubmarine.DisplayName));
text += GetItemTransferText();
msgBox = new GUIMessageBox(TextManager.Get("switchsubmarineheader"), text, messageBoxOptions);
}
msgBox.Buttons[0].OnClicked = (applyButton, obj) =>
{
if (GameMain.Client == null)
{
SubmarineInfo newSub = GameMain.GameSession.SwitchSubmarine(selectedSubmarine, deliveryFee);
GameMain.GameSession.SwitchSubmarine(selectedSubmarine, TransferItemsOnSwitch, deliveryFee);
RefreshSubmarineDisplay(true);
}
else
{
GameMain.Client.InitiateSubmarineChange(selectedSubmarine, Networking.VoteType.SwitchSub);
GameMain.Client.InitiateSubmarineChange(selectedSubmarine, TransferItemsOnSwitch, Networking.VoteType.SwitchSub);
}
return true;
};
@@ -653,23 +740,25 @@ namespace Barotrauma
if (!purchaseOnly)
{
msgBox = new GUIMessageBox(TextManager.Get("purchaseandswitchsubmarineheader"), TextManager.GetWithVariables("purchaseandswitchsubmarinetext",
var text = TextManager.GetWithVariables("purchaseandswitchsubmarinetext",
("[submarinename1]", selectedSubmarine.DisplayName),
("[amount]", selectedSubmarine.Price.ToString()),
("[currencyname]", currencyName),
("[submarinename2]", CurrentOrPendingSubmarine().DisplayName)), messageBoxOptions);
("[submarinename2]", CurrentOrPendingSubmarine().DisplayName));
text += GetItemTransferText();
msgBox = new GUIMessageBox(TextManager.Get("purchaseandswitchsubmarineheader"), text, messageBoxOptions);
msgBox.Buttons[0].OnClicked = (applyButton, obj) =>
{
if (GameMain.Client == null)
{
GameMain.GameSession.PurchaseSubmarine(selectedSubmarine);
SubmarineInfo newSub = GameMain.GameSession.SwitchSubmarine(selectedSubmarine, 0);
GameMain.GameSession.SwitchSubmarine(selectedSubmarine, TransferItemsOnSwitch, 0);
RefreshSubmarineDisplay(true);
}
else
{
GameMain.Client.InitiateSubmarineChange(selectedSubmarine, Networking.VoteType.PurchaseAndSwitchSub);
GameMain.Client.InitiateSubmarineChange(selectedSubmarine, TransferItemsOnSwitch, Networking.VoteType.PurchaseAndSwitchSub);
}
return true;
};
@@ -690,14 +779,20 @@ namespace Barotrauma
}
else
{
GameMain.Client.InitiateSubmarineChange(selectedSubmarine, Networking.VoteType.PurchaseSub);
GameMain.Client.InitiateSubmarineChange(selectedSubmarine, false, Networking.VoteType.PurchaseSub);
}
return true;
};
}
msgBox.Buttons[0].ClickSound = GUISoundType.ConfirmTransaction;
msgBox.Buttons[0].OnClicked += msgBox.Close;
msgBox.Buttons[1].OnClicked = msgBox.Close;
}
}
private LocalizedString GetItemTransferText()
{
return "\n\n" + TextManager.Get(TransferItemsOnSwitch ? "itemswillbetransferred" : "itemswontbetransferred");
}
}
}

View File

@@ -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;
if (!talentsButton.Enabled && selectedTab == InfoFrameTab.Talents)
{
SelectInfoFrameTab(InfoFrameTab.Crew);
@@ -430,7 +453,8 @@ namespace Barotrauma
GUIListBox crewList = new GUIListBox(new RectTransform(crewListSize, content.RectTransform))
{
Padding = new Vector4(2, 5, 0, 0),
AutoHideScrollBar = false
AutoHideScrollBar = false,
PlaySoundOnSelect = true
};
crewList.UpdateDimensions();
@@ -560,7 +584,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 +681,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 +692,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 +742,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 +799,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
};
@@ -897,8 +929,8 @@ namespace Barotrauma
}
else
{
Vector2 stringOffset = GUIStyle.GlobalFont.MeasureString(inLobbyString) / 2f;
GUIStyle.GlobalFont.DrawString(spriteBatch, inLobbyString, area.Center.ToVector2() - stringOffset, Color.White);
Vector2 stringOffset = GUIStyle.Font.MeasureString(inLobbyString) / 2f;
GUIStyle.Font.DrawString(spriteBatch, inLobbyString, area.Center.ToVector2() - stringOffset, Color.White);
}
}
@@ -971,16 +1003,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,
@@ -1024,7 +1065,7 @@ namespace Barotrauma
GUIButton centerButton = new GUIButton(new RectTransform(new Vector2(1f), centerLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight, anchor: Anchor.Center), style: "GUIButtonTransferArrow");
GUILayoutGroup inputLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.25f), paddedTransferMenuLayout.RectTransform), childAnchor: Anchor.Center);
GUINumberInput transferAmountInput = new GUINumberInput(new RectTransform(new Vector2(0.5f, 1f), inputLayout.RectTransform), GUINumberInput.NumberType.Int, hidePlusMinusButtons: true)
GUINumberInput transferAmountInput = new GUINumberInput(new RectTransform(new Vector2(0.5f, 1f), inputLayout.RectTransform), NumberType.Int, hidePlusMinusButtons: true)
{
MinValueInt = 0
};
@@ -1050,149 +1091,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.MaximumMoneyTransferRequest);
if (serverSettings.MaximumMoneyTransferRequest <= 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 +1322,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)
@@ -1490,10 +1559,10 @@ namespace Barotrauma
RichString missionReputationString = RichString.Rich(reputationText, wrapMissionText(GUIStyle.Font));
RichString missionDescriptionString = RichString.Rich(descriptionText, wrapMissionText(GUIStyle.Font));
Vector2 missionNameSize = GUIStyle.LargeFont.MeasureString(missionNameString);
Vector2 missionDescriptionSize = GUIStyle.Font.MeasureString(missionDescriptionString);
Vector2 missionRewardSize = GUIStyle.Font.MeasureString(missionRewardString);
Vector2 missionReputationSize = GUIStyle.Font.MeasureString(missionReputationString);
Vector2 missionNameSize = GUIStyle.LargeFont.MeasureString(missionNameString.SanitizedValue);
Vector2 missionDescriptionSize = GUIStyle.Font.MeasureString(missionDescriptionString.SanitizedValue);
Vector2 missionRewardSize = GUIStyle.Font.MeasureString(missionRewardString.SanitizedValue);
Vector2 missionReputationSize = GUIStyle.Font.MeasureString(missionReputationString.SanitizedValue);
float ySize = missionNameSize.Y + missionDescriptionSize.Y + missionRewardSize.Y + missionReputationSize.Y + missionTextGroup.AbsoluteSpacing * 4;
bool displayDifficulty = mission.Difficulty.HasValue;
@@ -1740,9 +1809,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 +1828,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 +1849,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) =>
{
@@ -1787,25 +1858,30 @@ namespace Barotrauma
});
GUILayoutGroup nameLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.3f, 1f), talentInfoLayoutGroup.RectTransform)) { RelativeSpacing = 0.05f };
Vector2 nameSize = GUIStyle.SubHeadingFont.MeasureString(info.Name);
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(Vector2.One, nameLayout.RectTransform), info.Name, font: GUIStyle.SubHeadingFont) { TextColor = job.Prefab.UIColor };
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(Vector2.One, nameLayout.RectTransform), info.Name, font: GUIStyle.SubHeadingFont);
nameBlock.RectTransform.NonScaledSize = nameSize.Pad(nameBlock.Padding).ToPoint();
Vector2 jobSize = GUIStyle.SmallFont.MeasureString(job.Name);
GUITextBlock jobBlock = new GUITextBlock(new RectTransform(Vector2.One, nameLayout.RectTransform), job.Name, font: GUIStyle.SmallFont) { TextColor = job.Prefab.UIColor };
jobBlock.RectTransform.NonScaledSize = jobSize.Pad(jobBlock.Padding).ToPoint();
if (!info.OmitJobInMenus)
{
nameBlock.TextColor = job.Prefab.UIColor;
Vector2 jobSize = GUIStyle.SmallFont.MeasureString(job.Name);
GUITextBlock jobBlock = new GUITextBlock(new RectTransform(Vector2.One, nameLayout.RectTransform), job.Name, font: GUIStyle.SmallFont) { TextColor = job.Prefab.UIColor };
jobBlock.RectTransform.NonScaledSize = jobSize.Pad(jobBlock.Padding).ToPoint();
}
LocalizedString traitString = TextManager.AddPunctuation(':', TextManager.Get("PersonalityTrait"), TextManager.Get("personalitytrait." + info.PersonalityTrait.Name.Replace(" ", "")));
Vector2 traitSize = GUIStyle.SmallFont.MeasureString(traitString);
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
};
@@ -1844,6 +1920,7 @@ namespace Barotrauma
{
OnClicked = (button, o) =>
{
GameMain.Client?.SendCharacterInfo(GameMain.Client.PendingName);
characterSettingsFrame!.Visible = false;
talentFrameMain.Visible = true;
return true;
@@ -1852,13 +1929,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 +1948,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 +2013,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 +2046,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 +2056,7 @@ namespace Barotrauma
OutlineColor = GUIStyle.Red,
TextColor = GUIStyle.Red,
PressedColor = unselectableColor,
DisabledColor = unselectableColor,
CanBeFocused = false,
};
}
@@ -1979,63 +2065,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 +2215,7 @@ namespace Barotrauma
talentButton.icon.HoverColor = hoverColor;
}
CreateTalentSkillList(controlledCharacter, skillListBox);
CreateTalentSkillList(controlledCharacter, controlledCharacter.Info, skillListBox);
}
private void ApplyTalents(Character controlledCharacter)
@@ -2157,6 +2243,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;

View File

@@ -27,6 +27,11 @@ namespace Barotrauma
set;
}
/// <summary>
/// The size of fixed area around the slice area
/// </summary>
public Point NonSliceSize { get; set; }
public bool MaintainAspectRatio
{
get;
@@ -72,6 +77,7 @@ namespace Barotrauma
maxBorderScale = element.GetAttributeFloat("minborderscale", 10.0f);
Rectangle slice = new Rectangle((int)sliceVec.X, (int)sliceVec.Y, (int)(sliceVec.Z - sliceVec.X), (int)(sliceVec.W - sliceVec.Y));
NonSliceSize = new Point(Sprite.SourceRect.Width - slice.Width, Sprite.SourceRect.Height - slice.Height);
Slices = new Rectangle[9];

View File

@@ -462,7 +462,7 @@ namespace Barotrauma
button.Enabled = false;
}
return true;
});
}, overrideConfirmButtonSound: GUISoundType.ConfirmTransaction);
}
else
{
@@ -497,7 +497,7 @@ namespace Barotrauma
button.Enabled = false;
}
return true;
});
}, overrideConfirmButtonSound: GUISoundType.ConfirmTransaction);
}
else
{
@@ -539,7 +539,7 @@ namespace Barotrauma
GameMain.Client?.SendCampaignState();
}
return true;
});
}, overrideConfirmButtonSound: GUISoundType.ConfirmTransaction);
}
else
{
@@ -589,7 +589,7 @@ namespace Barotrauma
new GUITextBlock(rectT(1, 0, textLayout), title, font: GUIStyle.SubHeadingFont) { CanBeFocused = false, AutoScaleHorizontal = true };
new GUITextBlock(rectT(1, 0, textLayout), TextManager.FormatCurrency(price));
GUILayoutGroup buyButtonLayout = new GUILayoutGroup(rectT(0.2f, 1, contentLayout), childAnchor: Anchor.Center) { UserData = "buybutton" };
new GUIButton(rectT(0.7f, 0.5f, buyButtonLayout), string.Empty, style: "RepairBuyButton") { ClickSound = GUISoundType.HireRepairClick, Enabled = PlayerBalance >= price && !isDisabled, OnClicked = onPressed };
new GUIButton(rectT(0.7f, 0.5f, buyButtonLayout), string.Empty, style: "RepairBuyButton") { Enabled = PlayerBalance >= price && !isDisabled, OnClicked = onPressed };
contentLayout.Recalculate();
buyButtonLayout.Recalculate();
@@ -622,7 +622,8 @@ namespace Barotrauma
PadBottom = true,
SelectTop = true,
ClampScrollToElements = true,
Spacing = 8
Spacing = 8,
PlaySoundOnSelect = true
};
Dictionary<UpgradeCategory, List<UpgradePrefab>> upgrades = new Dictionary<UpgradeCategory, List<UpgradePrefab>>();
@@ -1123,7 +1124,10 @@ namespace Barotrauma
{
priceText.Text = string.Empty;
}
new GUIButton(rectT(0.7f, 0.5f, buyButtonLayout), string.Empty, style: buttonStyle) { Enabled = false };
new GUIButton(rectT(0.7f, 0.5f, buyButtonLayout), string.Empty, style: buttonStyle)
{
Enabled = false
};
if (upgradePrefab != null)
{
var increaseText = new GUITextBlock(rectT(1, 0.2f, buyButtonLayout), "", textAlignment: Alignment.Center);
@@ -1212,7 +1216,7 @@ namespace Barotrauma
Campaign.UpgradeManager.PurchaseUpgrade(prefab, category);
GameMain.Client?.SendCampaignState();
return true;
});
}, overrideConfirmButtonSound: GUISoundType.ConfirmTransaction);
return true;
};
@@ -1400,7 +1404,7 @@ namespace Barotrauma
if (PlayerInput.PrimaryMouseButtonClicked() && selectedUpgradeTab == UpgradeTab.Upgrade && currentStoreLayout != null)
{
ScrollToCategory(data => data.Category.IsWallUpgrade);
ScrollToCategory(data => data.Category.IsWallUpgrade, GUIListBox.PlaySelectSound.Yes);
}
}
}
@@ -1682,7 +1686,7 @@ namespace Barotrauma
}
}
private void ScrollToCategory(Predicate<CategoryData> predicate)
private void ScrollToCategory(Predicate<CategoryData> predicate, GUIListBox.PlaySelectSound playSelectSound = GUIListBox.PlaySelectSound.No)
{
if (currentStoreLayout == null) { return; }
@@ -1690,7 +1694,7 @@ namespace Barotrauma
{
if (child.UserData is CategoryData data && predicate(data))
{
currentStoreLayout.ScrollToElement(child);
currentStoreLayout.ScrollToElement(child, playSelectSound);
break;
}
}

View File

@@ -26,7 +26,7 @@ namespace Barotrauma
private Color SubmarineColor => GUIStyle.Orange;
private Point createdForResolution;
public static VotingInterface CreateSubmarineVotingInterface(Client starter, SubmarineInfo info, VoteType type, float votingTime)
public static VotingInterface CreateSubmarineVotingInterface(Client starter, SubmarineInfo info, VoteType type, bool transferItems, float votingTime)
{
if (starter == null || info == null) { return null; }
@@ -38,7 +38,7 @@ namespace Barotrauma
getMaxVotes = () => GameMain.NetworkMember?.Voting?.GetVoteCountMax(type) ?? 0,
};
subVoting.onVoteEnd = () => subVoting.SendSubmarineVoteEndMessage(info, type);
subVoting.SetSubmarineVotingText(starter, info, type);
subVoting.SetSubmarineVotingText(starter, info, transferItems, type);
subVoting.Initialize(starter, type);
return subVoting;
}
@@ -160,19 +160,21 @@ namespace Barotrauma
}
#region Submarine Voting
private void SetSubmarineVotingText(Client starter, SubmarineInfo info, VoteType type)
private void SetSubmarineVotingText(Client starter, SubmarineInfo info, bool transferItems, VoteType type)
{
string name = starter.Name;
JobPrefab prefab = starter?.Character?.Info?.Job?.Prefab;
Color nameColor = prefab != null ? prefab.UIColor : Color.White;
string characterRichString = $"‖color:{nameColor.R},{nameColor.G},{nameColor.B}‖{name}‖color:end‖";
string submarineRichString = $"‖color:{SubmarineColor.R},{SubmarineColor.G},{SubmarineColor.B}‖{info.DisplayName}‖color:end‖";
string tag = string.Empty;
LocalizedString text = string.Empty;
switch (type)
{
case VoteType.PurchaseAndSwitchSub:
text = TextManager.GetWithVariables("submarinepurchaseandswitchvote",
tag = transferItems ? "submarinepurchaseandswitchwithitemsvote" : "submarinepurchaseandswitchvote";
text = TextManager.GetWithVariables(tag,
("[playername]", characterRichString),
("[submarinename]", submarineRichString),
("[amount]", info.Price.ToString()),
@@ -189,7 +191,8 @@ namespace Barotrauma
int deliveryFee = SubmarineSelection.DeliveryFeePerDistanceTravelled * GameMain.GameSession.Map.DistanceToClosestLocationWithOutpost(GameMain.GameSession.Map.CurrentLocation, out Location endLocation);
if (deliveryFee > 0)
{
text = TextManager.GetWithVariables("submarineswitchfeevote",
tag = transferItems ? "submarineswitchwithitemsfeevote" : "submarineswitchfeevote";
text = TextManager.GetWithVariables(tag,
("[playername]", characterRichString),
("[submarinename]", submarineRichString),
("[locationname]", endLocation.Name),
@@ -198,13 +201,13 @@ namespace Barotrauma
}
else
{
text = TextManager.GetWithVariables("submarineswitchnofeevote",
tag = transferItems ? "submarineswitchwithitemsnofeevote" : "submarineswitchnofeevote";
text = TextManager.GetWithVariables(tag,
("[playername]", characterRichString),
("[submarinename]", submarineRichString));
}
break;
}
votingOnText = RichString.Rich(text);
}

View File

@@ -204,6 +204,8 @@ namespace Barotrauma
public static bool CancelQuickStart;
#endif
public static ChatMode ActiveChatMode { get; set; } = ChatMode.Radio;
public GameMain(string[] args)
{
Content.RootDirectory = "Content";
@@ -284,9 +286,9 @@ namespace Barotrauma
screen.OnFileDropped(filePath, extension);
}
public void ApplyGraphicsSettings()
public void ApplyGraphicsSettings(bool recalculateFontsAndStyles = false)
{
void updateConfig()
static void updateConfig()
{
var config = GameSettings.CurrentConfig;
config.Graphics.Width = GraphicsWidth;
@@ -325,6 +327,12 @@ namespace Barotrauma
defaultViewport = GraphicsDevice.Viewport;
if (recalculateFontsAndStyles)
{
GUIStyle.RecalculateFonts();
GUIStyle.RecalculateSizeRestrictions();
}
ResolutionChanged?.Invoke();
}
@@ -550,6 +558,10 @@ namespace Barotrauma
yield return CoroutineStatus.Running;
#if DEBUG
LevelGenerationParams.CheckValidity();
#endif
MainMenuScreen.Select();
foreach (Identifier steamError in SteamManager.InitializationErrors)
@@ -940,7 +952,7 @@ namespace Barotrauma
updateCount++;
sw.Stop();
PerformanceCounter.AddElapsedTicks("Update total", sw.ElapsedTicks);
PerformanceCounter.AddElapsedTicks("Update", sw.ElapsedTicks);
PerformanceCounter.UpdateTimeGraph.Update(sw.ElapsedTicks * 1000.0f / (float)Stopwatch.Frequency);
}
@@ -959,6 +971,23 @@ namespace Barotrauma
Timing.Accumulator = 0.0f;
}
private void FixRazerCortex()
{
#if WINDOWS
//Razer Cortex's overlay is broken.
//For whatever reason, it messes up the blendstate and,
//because MonoGame reasonably assumes that you don't need
//to touch it if you're setting it to the exact same one
//you were already using, it doesn't fix Razer's mess.
//Therefore, we need to change the blendstate TWICE:
//once to force MonoGame to change it, and then again to
//use the blendstate we actually want.
var oldBlendState = GraphicsDevice.BlendState;
GraphicsDevice.BlendState = oldBlendState == BlendState.Opaque ? BlendState.NonPremultiplied : BlendState.Opaque;
GraphicsDevice.BlendState = oldBlendState;
#endif
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
@@ -966,7 +995,9 @@ namespace Barotrauma
{
Stopwatch sw = new Stopwatch();
sw.Start();
FixRazerCortex();
double deltaTime = gameTime.ElapsedGameTime.TotalSeconds;
if (Timing.FrameLimit > 0)
@@ -1023,7 +1054,7 @@ namespace Barotrauma
}
sw.Stop();
PerformanceCounter.AddElapsedTicks("Draw total", sw.ElapsedTicks);
PerformanceCounter.AddElapsedTicks("Draw", sw.ElapsedTicks);
PerformanceCounter.DrawTimeGraph.Update(sw.ElapsedTicks * 1000.0f / (float)Stopwatch.Frequency);
}
@@ -1059,7 +1090,7 @@ namespace Barotrauma
}
// 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)
if (GameSession?.Campaign is SinglePlayerCampaign spCampaign && Level.IsLoadedFriendlyOutpost && spCampaign.Map?.CurrentLocation != null && spCampaign.CargoManager != null)
{
spCampaign.Map.CurrentLocation.AddStock(spCampaign.CargoManager.SoldItems);
spCampaign.CargoManager.ClearSoldItemsProjSpecific();
@@ -1185,7 +1216,7 @@ namespace Barotrauma
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), linkHolder.RectTransform), TextManager.Get("bugreportgithubform"), style: "MainMenuGUIButton", textAlignment: Alignment.Left)
{
UserData = "https://github.com/Regalis11/Barotrauma/issues/new?template=bug_report.md",
UserData = "https://github.com/Regalis11/Barotrauma/issues/new/choose",
OnClicked = (btn, userdata) =>
{
ShowOpenUrlInWebBrowserPrompt(userdata as string);

View File

@@ -81,7 +81,6 @@ namespace Barotrauma
: this(isSinglePlayer)
{
AddCharacterElements(element);
ActiveOrdersElement = element.GetChildElement("activeorders");
}
partial void InitProjectSpecific()
@@ -148,9 +147,10 @@ namespace Barotrauma
string msgCommand = ChatMessage.GetChatMessageCommand(text, out string msg);
// add to local history
ChatBox.ChatManager.Store(text);
WifiComponent headset = null;
ChatMessageType messageType =
((msgCommand == "r" || msgCommand == "radio") && ChatMessage.CanUseRadio(Character.Controlled, out headset)) ? ChatMessageType.Radio : ChatMessageType.Default;
bool isUsingRadioMode = GameMain.ActiveChatMode == ChatMode.Radio;
bool containsRadioCommand = msgCommand == "r" || msgCommand == "radio";
bool canUseRadio = ChatMessage.CanUseRadio(Character.Controlled, out WifiComponent headset);
ChatMessageType messageType = ((isUsingRadioMode && msgCommand == "") || containsRadioCommand) && canUseRadio ? ChatMessageType.Radio : ChatMessageType.Default;
AddSinglePlayerChatMessage(
Character.Controlled.Info.Name,
msg, messageType,
@@ -1554,40 +1554,9 @@ namespace Barotrauma
{
ChatBox.Update(deltaTime);
ChatBox.InputBox.Visible = Character.Controlled != null;
if (!DebugConsole.IsOpen && ChatBox.InputBox.Visible && GUI.KeyboardDispatcher.Subscriber == null)
if (!DebugConsole.IsOpen && ChatBox.InputBox.Visible && GUI.KeyboardDispatcher.Subscriber == null && !ChatBox.InputBox.Selected)
{
if (PlayerInput.KeyHit(InputType.Chat) && !ChatBox.InputBox.Selected)
{
ChatBox.InputBox.AddToGUIUpdateList();
ChatBox.GUIFrame.Flash(Color.DarkGreen, 0.5f);
if (!ChatBox.ToggleOpen)
{
ChatBox.CloseAfterMessageSent = !ChatBox.ToggleOpen;
ChatBox.ToggleOpen = true;
}
ChatBox.InputBox.Select(ChatBox.InputBox.Text.Length);
}
if (PlayerInput.KeyHit(InputType.RadioChat) && !ChatBox.InputBox.Selected)
{
if (Character.Controlled == null || Character.Controlled.SpeechImpediment < 100)
{
ChatBox.InputBox.AddToGUIUpdateList();
ChatBox.GUIFrame.Flash(Color.YellowGreen, 0.5f);
if (!ChatBox.ToggleOpen)
{
ChatBox.CloseAfterMessageSent = !ChatBox.ToggleOpen;
ChatBox.ToggleOpen = true;
}
if (!ChatBox.InputBox.Text.StartsWith(ChatBox.RadioChatString))
{
ChatBox.InputBox.Text = ChatBox.RadioChatString;
}
ChatBox.InputBox.Select(ChatBox.InputBox.Text.Length);
}
}
ChatBox.ApplySelectionInputs();
}
}
@@ -1609,7 +1578,7 @@ namespace Barotrauma
{
if (character == Character.Controlled && crewList.SelectedComponent != characterComponent)
{
crewList.Select(character, force: true);
crewList.Select(character, GUIListBox.Force.Yes);
}
// Icon colors might change based on the target so we check if they need to be updated
if (GetCurrentOrderIconList(characterComponent) is GUIListBox currentOrderIconList)
@@ -3661,9 +3630,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 +3643,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)

View File

@@ -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
{

View File

@@ -587,196 +587,78 @@ namespace Barotrauma
//static because we may need to instantiate the campaign if it hasn't been done yet
public static void ClientRead(IReadMessage msg)
{
NetFlags requiredFlags = (NetFlags)msg.ReadUInt16();
bool isFirstRound = msg.ReadBoolean();
byte campaignID = msg.ReadByte();
UInt16 updateID = msg.ReadUInt16();
UInt16 saveID = msg.ReadUInt16();
string mapSeed = msg.ReadString();
UInt16 currentLocIndex = msg.ReadUInt16();
UInt16 selectedLocIndex = msg.ReadUInt16();
byte selectedMissionCount = msg.ReadByte();
List<int> selectedMissionIndices = new List<int>();
for (int i = 0; i < selectedMissionCount; i++)
{
selectedMissionIndices.Add(msg.ReadByte());
}
ushort ownedSubCount = msg.ReadUInt16();
List<ushort> ownedSubIndices = new List<ushort>();
for (int i = 0; i < ownedSubCount; i++)
{
ownedSubIndices.Add(msg.ReadUInt16());
}
bool allowDebugTeleport = msg.ReadBoolean();
float? reputation = null;
if (msg.ReadBoolean()) { reputation = msg.ReadSingle(); }
Dictionary<Identifier, float> factionReps = new Dictionary<Identifier, float>();
byte factionsCount = msg.ReadByte();
for (int i = 0; i < factionsCount; i++)
{
factionReps.Add(msg.ReadIdentifier(), msg.ReadSingle());
}
bool forceMapUI = msg.ReadBoolean();
bool purchasedHullRepairs = msg.ReadBoolean();
bool purchasedItemRepairs = msg.ReadBoolean();
bool purchasedLostShuttles = msg.ReadBoolean();
byte missionCount = msg.ReadByte();
var availableMissions = new List<(Identifier Identifier, byte ConnectionIndex)>();
for (int i = 0; i < missionCount; i++)
{
Identifier missionIdentifier = msg.ReadIdentifier();
byte connectionIndex = msg.ReadByte();
availableMissions.Add((missionIdentifier, connectionIndex));
}
var storeBalances = new Dictionary<Identifier, UInt16>();
if (msg.ReadBoolean())
{
byte storeCount = msg.ReadByte();
for (int i = 0; i < storeCount; i++)
{
Identifier identifier = msg.ReadIdentifier();
UInt16 storeBalance = msg.ReadUInt16();
storeBalances.Add(identifier, storeBalance);
}
}
var buyCrateItems = ReadPurchasedItems(msg, sender: null);
var subSellCrateItems = ReadPurchasedItems(msg, sender: null);
var purchasedItems = ReadPurchasedItems(msg, sender: null);
var soldItems = ReadSoldItems(msg);
ushort pendingUpgradeCount = msg.ReadUInt16();
List<PurchasedUpgrade> pendingUpgrades = new List<PurchasedUpgrade>();
for (int i = 0; i < pendingUpgradeCount; i++)
{
Identifier upgradeIdentifier = msg.ReadIdentifier();
UpgradePrefab prefab = UpgradePrefab.Find(upgradeIdentifier);
Identifier categoryIdentifier = msg.ReadIdentifier();
UpgradeCategory category = UpgradeCategory.Find(categoryIdentifier);
int upgradeLevel = msg.ReadByte();
if (prefab == null || category == null) { continue; }
pendingUpgrades.Add(new PurchasedUpgrade(prefab, category, upgradeLevel));
}
ushort purchasedItemSwapCount = msg.ReadUInt16();
List<PurchasedItemSwap> purchasedItemSwaps = new List<PurchasedItemSwap>();
for (int i = 0; i < purchasedItemSwapCount; i++)
{
UInt16 itemToRemoveID = msg.ReadUInt16();
Identifier itemToInstallIdentifier = msg.ReadIdentifier();
ItemPrefab itemToInstall = itemToInstallIdentifier.IsEmpty ? null : ItemPrefab.Find(string.Empty, itemToInstallIdentifier);
if (!(Entity.FindEntityByID(itemToRemoveID) is Item itemToRemove)) { continue; }
purchasedItemSwaps.Add(new PurchasedItemSwap(itemToRemove, itemToInstall));
}
bool hasCharacterData = msg.ReadBoolean();
CharacterInfo myCharacterInfo = null;
if (hasCharacterData)
{
myCharacterInfo = CharacterInfo.ClientRead(CharacterPrefab.HumanSpeciesName, msg);
}
bool refreshCampaignUI = false;
if (!(GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign) || campaignID != campaign.CampaignID)
{
string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer);
GameMain.GameSession = new GameSession(null, savePath, GameModePreset.MultiPlayerCampaign, CampaignSettings.Unsure, mapSeed);
GameMain.GameSession = new GameSession(null, savePath, GameModePreset.MultiPlayerCampaign, CampaignSettings.Empty, mapSeed);
campaign = (MultiPlayerCampaign)GameMain.GameSession.GameMode;
campaign.CampaignID = campaignID;
GameMain.NetLobbyScreen.ToggleCampaignMode(true);
}
//server has a newer save file
if (NetIdUtils.IdMoreRecent(saveID, campaign.PendingSaveID))
{
campaign.PendingSaveID = saveID;
}
if (NetIdUtils.IdMoreRecent(updateID, campaign.lastUpdateID))
{
campaign.SuppressStateSending = true;
campaign.IsFirstRound = isFirstRound;
if (NetIdUtils.IdMoreRecent(saveID, campaign.PendingSaveID)) { campaign.PendingSaveID = saveID; }
campaign.IsFirstRound = isFirstRound;
//we need to have the latest save file to display location/mission/store
if (campaign.LastSaveID == saveID)
if (requiredFlags.HasFlag(NetFlags.Misc))
{
DebugConsole.Log("Received campaign update (Misc)");
UInt16 id = msg.ReadUInt16();
bool purchasedHullRepairs = msg.ReadBoolean();
bool purchasedItemRepairs = msg.ReadBoolean();
bool purchasedLostShuttles = msg.ReadBoolean();
if (ShouldApply(NetFlags.Misc, id, requireUpToDateSave: false))
{
refreshCampaignUI = campaign.PurchasedHullRepairs != purchasedHullRepairs ||
campaign.PurchasedItemRepairs != purchasedItemRepairs ||
campaign.PurchasedLostShuttles != purchasedLostShuttles;
campaign.PurchasedHullRepairs = purchasedHullRepairs;
campaign.PurchasedItemRepairs = purchasedItemRepairs;
campaign.PurchasedLostShuttles = purchasedLostShuttles;
}
}
if (requiredFlags.HasFlag(NetFlags.MapAndMissions))
{
DebugConsole.Log("Received campaign update (MapAndMissions)");
UInt16 id = msg.ReadUInt16();
bool forceMapUI = msg.ReadBoolean();
bool allowDebugTeleport = msg.ReadBoolean();
UInt16 currentLocIndex = msg.ReadUInt16();
UInt16 selectedLocIndex = msg.ReadUInt16();
byte missionCount = msg.ReadByte();
var availableMissions = new List<(Identifier Identifier, byte ConnectionIndex)>();
for (int i = 0; i < missionCount; i++)
{
Identifier missionIdentifier = msg.ReadIdentifier();
byte connectionIndex = msg.ReadByte();
availableMissions.Add((missionIdentifier, connectionIndex));
}
byte selectedMissionCount = msg.ReadByte();
List<int> selectedMissionIndices = new List<int>();
for (int i = 0; i < selectedMissionCount; i++)
{
selectedMissionIndices.Add(msg.ReadByte());
}
if (ShouldApply(NetFlags.MapAndMissions, id, requireUpToDateSave: true))
{
campaign.ForceMapUI = forceMapUI;
UpgradeStore.WaitForServerUpdate = false;
campaign.Map.AllowDebugTeleport = allowDebugTeleport;
campaign.Map.SetLocation(currentLocIndex == UInt16.MaxValue ? -1 : currentLocIndex);
campaign.Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex);
campaign.Map.SelectMission(selectedMissionIndices);
GameMain.GameSession.OwnedSubmarines.Clear();
foreach (int ownedSubIndex in ownedSubIndices)
{
SubmarineInfo sub = GameMain.Client.ServerSubmarines[ownedSubIndex];
if (GameMain.NetLobbyScreen.CheckIfCampaignSubMatches(sub, NetLobbyScreen.SubmarineDeliveryData.Owned))
{
GameMain.GameSession.OwnedSubmarines.Add(sub);
}
}
campaign.Map.AllowDebugTeleport = allowDebugTeleport;
campaign.CargoManager.SetItemsInBuyCrate(buyCrateItems);
campaign.CargoManager.SetItemsInSubSellCrate(subSellCrateItems);
campaign.CargoManager.SetPurchasedItems(purchasedItems);
campaign.CargoManager.SetSoldItems(soldItems);
foreach (var balance in storeBalances)
{
if (campaign.Map.CurrentLocation.GetStore(balance.Key) is { } store)
{
store.Balance = balance.Value;
}
}
campaign.UpgradeManager.SetPendingUpgrades(pendingUpgrades);
campaign.UpgradeManager.PurchasedUpgrades.Clear();
foreach (var purchasedItemSwap in purchasedItemSwaps)
{
if (purchasedItemSwap.ItemToInstall == null)
{
campaign.UpgradeManager.CancelItemSwap(purchasedItemSwap.ItemToRemove, force: true);
}
else
{
campaign.UpgradeManager.PurchaseItemSwap(purchasedItemSwap.ItemToRemove, purchasedItemSwap.ItemToInstall, force: true);
}
}
foreach (Item item in Item.ItemList)
{
if (item.PendingItemSwap != null && !purchasedItemSwaps.Any(it => it.ItemToRemove == item))
{
item.PendingItemSwap = null;
}
}
foreach (var (identifier, rep) in factionReps)
{
Faction faction = campaign.Factions.FirstOrDefault(f => f.Prefab.Identifier == identifier);
if (faction?.Reputation != null)
{
faction.Reputation.SetReputation(rep);
}
else
{
DebugConsole.ThrowError($"Received an update for a faction that doesn't exist \"{identifier}\".");
}
}
if (reputation.HasValue)
{
campaign.Map.CurrentLocation.Reputation.SetReputation(reputation.Value);
campaign?.CampaignUI?.UpgradeStore?.RequestRefresh();
}
foreach (var availableMission in availableMissions)
{
MissionPrefab missionPrefab = MissionPrefab.Prefabs.Find(mp => mp.Identifier == availableMission.Identifier);
@@ -800,36 +682,269 @@ namespace Barotrauma
campaign.Map.CurrentLocation.UnlockMission(missionPrefab, connection);
}
}
GameMain.NetLobbyScreen.ToggleCampaignMode(true);
}
bool shouldRefresh = campaign.PurchasedHullRepairs != purchasedHullRepairs ||
campaign.PurchasedItemRepairs != purchasedItemRepairs ||
campaign.PurchasedLostShuttles != purchasedLostShuttles;
campaign.PurchasedHullRepairs = purchasedHullRepairs;
campaign.PurchasedItemRepairs = purchasedItemRepairs;
campaign.PurchasedLostShuttles = purchasedLostShuttles;
if (shouldRefresh)
{
campaign?.CampaignUI?.UpgradeStore?.RequestRefresh();
}
if (myCharacterInfo != null)
{
GameMain.Client.CharacterInfo = myCharacterInfo;
GameMain.NetLobbyScreen.SetCampaignCharacterInfo(myCharacterInfo);
campaign.Map.SelectMission(selectedMissionIndices);
ReadStores(msg, apply: true);
}
else
{
GameMain.NetLobbyScreen.SetCampaignCharacterInfo(null);
ReadStores(msg, apply: false);
}
}
if (requiredFlags.HasFlag(NetFlags.SubList))
{
DebugConsole.Log("Received campaign update (SubList)");
UInt16 id = msg.ReadUInt16();
ushort ownedSubCount = msg.ReadUInt16();
List<ushort> ownedSubIndices = new List<ushort>();
for (int i = 0; i < ownedSubCount; i++)
{
ownedSubIndices.Add(msg.ReadUInt16());
}
campaign.lastUpdateID = updateID;
campaign.SuppressStateSending = false;
if (ShouldApply(NetFlags.SubList, id, requireUpToDateSave: false))
{
GameMain.GameSession.OwnedSubmarines.Clear();
foreach (int ownedSubIndex in ownedSubIndices)
{
SubmarineInfo sub = GameMain.Client.ServerSubmarines[ownedSubIndex];
if (GameMain.NetLobbyScreen.CheckIfCampaignSubMatches(sub, NetLobbyScreen.SubmarineDeliveryData.Owned))
{
GameMain.GameSession.OwnedSubmarines.Add(sub);
}
}
}
}
if (requiredFlags.HasFlag(NetFlags.UpgradeManager))
{
DebugConsole.Log("Received campaign update (UpgradeManager)");
UInt16 id = msg.ReadUInt16();
ushort pendingUpgradeCount = msg.ReadUInt16();
List<PurchasedUpgrade> pendingUpgrades = new List<PurchasedUpgrade>();
for (int i = 0; i < pendingUpgradeCount; i++)
{
Identifier upgradeIdentifier = msg.ReadIdentifier();
UpgradePrefab prefab = UpgradePrefab.Find(upgradeIdentifier);
Identifier categoryIdentifier = msg.ReadIdentifier();
UpgradeCategory category = UpgradeCategory.Find(categoryIdentifier);
int upgradeLevel = msg.ReadByte();
if (prefab == null || category == null) { continue; }
pendingUpgrades.Add(new PurchasedUpgrade(prefab, category, upgradeLevel));
}
ushort purchasedItemSwapCount = msg.ReadUInt16();
List<PurchasedItemSwap> purchasedItemSwaps = new List<PurchasedItemSwap>();
for (int i = 0; i < purchasedItemSwapCount; i++)
{
UInt16 itemToRemoveID = msg.ReadUInt16();
Identifier itemToInstallIdentifier = msg.ReadIdentifier();
ItemPrefab itemToInstall = itemToInstallIdentifier.IsEmpty ? null : ItemPrefab.Find(string.Empty, itemToInstallIdentifier);
if (!(Entity.FindEntityByID(itemToRemoveID) is Item itemToRemove)) { continue; }
purchasedItemSwaps.Add(new PurchasedItemSwap(itemToRemove, itemToInstall));
}
if (!Submarine.Unloading && !(Submarine.MainSub is { Loading: true }) &&
ShouldApply(NetFlags.UpgradeManager, id, requireUpToDateSave: true))
{
UpgradeStore.WaitForServerUpdate = false;
campaign.UpgradeManager.SetPendingUpgrades(pendingUpgrades);
campaign.UpgradeManager.PurchasedUpgrades.Clear();
foreach (var purchasedItemSwap in purchasedItemSwaps)
{
if (purchasedItemSwap.ItemToInstall == null)
{
campaign.UpgradeManager.CancelItemSwap(purchasedItemSwap.ItemToRemove, force: true);
}
else
{
campaign.UpgradeManager.PurchaseItemSwap(purchasedItemSwap.ItemToRemove, purchasedItemSwap.ItemToInstall, force: true);
}
}
foreach (Item item in Item.ItemList.ToList())
{
if (item.PendingItemSwap != null && !purchasedItemSwaps.Any(it => it.ItemToRemove == item))
{
item.PendingItemSwap = null;
}
}
}
}
if (requiredFlags.HasFlag(NetFlags.ItemsInBuyCrate))
{
DebugConsole.Log("Received campaign update (ItemsInBuyCrate)");
UInt16 id = msg.ReadUInt16();
var buyCrateItems = ReadPurchasedItems(msg, sender: null);
if (ShouldApply(NetFlags.ItemsInBuyCrate, id, requireUpToDateSave: true))
{
campaign.CargoManager.SetItemsInBuyCrate(buyCrateItems);
campaign.SetLastUpdateIdForFlag(NetFlags.ItemsInBuyCrate, id);
ReadStores(msg, apply: true);
}
else
{
ReadStores(msg, apply: false);
}
}
if (requiredFlags.HasFlag(NetFlags.ItemsInSellFromSubCrate))
{
DebugConsole.Log("Received campaign update (ItemsInSellFromSubCrate)");
UInt16 id = msg.ReadUInt16();
var subSellCrateItems = ReadPurchasedItems(msg, sender: null);
if (ShouldApply(NetFlags.ItemsInSellFromSubCrate, id, requireUpToDateSave: true))
{
campaign.CargoManager.SetItemsInSubSellCrate(subSellCrateItems);
campaign.SetLastUpdateIdForFlag(NetFlags.ItemsInSellFromSubCrate, id);
ReadStores(msg, apply: true);
}
else
{
ReadStores(msg, apply: false);
}
}
if (requiredFlags.HasFlag(NetFlags.PurchasedItems))
{
DebugConsole.Log("Received campaign update (PuchasedItems)");
UInt16 id = msg.ReadUInt16();
var purchasedItems = ReadPurchasedItems(msg, sender: null);
if (ShouldApply(NetFlags.PurchasedItems, id, requireUpToDateSave: true))
{
campaign.CargoManager.SetPurchasedItems(purchasedItems);
campaign.SetLastUpdateIdForFlag(NetFlags.PurchasedItems, id);
ReadStores(msg, apply: true);
}
else
{
ReadStores(msg, apply: false);
}
}
if (requiredFlags.HasFlag(NetFlags.SoldItems))
{
DebugConsole.Log("Received campaign update (SoldItems)");
UInt16 id = msg.ReadUInt16();
var soldItems = ReadSoldItems(msg);
if (ShouldApply(NetFlags.SoldItems, id, requireUpToDateSave: true))
{
campaign.CargoManager.SetSoldItems(soldItems);
campaign.SetLastUpdateIdForFlag(NetFlags.SoldItems, id);
ReadStores(msg, apply: true);
}
else
{
ReadStores(msg, apply: false);
}
}
if (requiredFlags.HasFlag(NetFlags.Reputation))
{
DebugConsole.Log("Received campaign update (Reputation)");
UInt16 id = msg.ReadUInt16();
float? reputation = null;
if (msg.ReadBoolean()) { reputation = msg.ReadSingle(); }
Dictionary<Identifier, float> factionReps = new Dictionary<Identifier, float>();
byte factionsCount = msg.ReadByte();
for (int i = 0; i < factionsCount; i++)
{
factionReps.Add(msg.ReadIdentifier(), msg.ReadSingle());
}
if (ShouldApply(NetFlags.Reputation, id, requireUpToDateSave: true))
{
if (reputation.HasValue)
{
campaign.Map.CurrentLocation.Reputation.SetReputation(reputation.Value);
campaign?.CampaignUI?.UpgradeStore?.RequestRefresh();
}
foreach (var (identifier, rep) in factionReps)
{
Faction faction = campaign.Factions.FirstOrDefault(f => f.Prefab.Identifier == identifier);
if (faction?.Reputation != null)
{
faction.Reputation.SetReputation(rep);
}
else
{
DebugConsole.ThrowError($"Received an update for a faction that doesn't exist \"{identifier}\".");
}
}
}
}
if (requiredFlags.HasFlag(NetFlags.CharacterInfo))
{
DebugConsole.Log("Received campaign update (CharacterInfo)");
UInt16 id = msg.ReadUInt16();
bool hasCharacterData = msg.ReadBoolean();
CharacterInfo myCharacterInfo = null;
if (hasCharacterData)
{
myCharacterInfo = CharacterInfo.ClientRead(CharacterPrefab.HumanSpeciesName, msg);
}
if (ShouldApply(NetFlags.CharacterInfo, id, requireUpToDateSave: true))
{
if (myCharacterInfo != null)
{
GameMain.Client.CharacterInfo = myCharacterInfo;
GameMain.NetLobbyScreen.SetCampaignCharacterInfo(myCharacterInfo);
}
else
{
GameMain.NetLobbyScreen.SetCampaignCharacterInfo(null);
}
}
}
campaign.SuppressStateSending = true;
//we need to have the latest save file to display location/mission/store
if (campaign.LastSaveID == saveID)
{
GameMain.NetLobbyScreen.ToggleCampaignMode(true);
}
if (refreshCampaignUI)
{
campaign?.CampaignUI?.UpgradeStore?.RequestRefresh();
}
campaign.SuppressStateSending = false;
bool ShouldApply(NetFlags flag, UInt16 id, bool requireUpToDateSave)
{
if (NetIdUtils.IdMoreRecent(id, campaign.GetLastUpdateIdForFlag(flag)) &&
(!requireUpToDateSave || saveID == campaign.LastSaveID))
{
campaign.SetLastUpdateIdForFlag(flag, id);
return true;
}
else
{
return false;
}
}
void ReadStores(IReadMessage msg, bool apply)
{
var storeBalances = new Dictionary<Identifier, UInt16>();
if (msg.ReadBoolean())
{
byte storeCount = msg.ReadByte();
for (int i = 0; i < storeCount; i++)
{
Identifier identifier = msg.ReadIdentifier();
UInt16 storeBalance = msg.ReadUInt16();
storeBalances.Add(identifier, storeBalance);
}
}
if (apply)
{
foreach (var balance in storeBalances)
{
if (campaign.Map?.CurrentLocation?.GetStore(balance.Key) is { } store)
{
store.Balance = balance.Value;
}
}
}
}
}
public void ClientReadCrew(IReadMessage msg)

View File

@@ -35,11 +35,7 @@ namespace Barotrauma
}
}
if (CrewManager.ChatBox != null)
{
CrewManager.ChatBox.Update(deltaTime);
}
CrewManager.ChatBox?.Update(deltaTime);
CrewManager.UpdateReports();
}
@@ -58,12 +54,12 @@ namespace Barotrauma
/// <summary>
/// Instantiates a new single player campaign
/// </summary>
private SinglePlayerCampaign(string mapSeed, CampaignSettings settings) : base(GameModePreset.SinglePlayerCampaign)
private SinglePlayerCampaign(string mapSeed, CampaignSettings settings) : base(GameModePreset.SinglePlayerCampaign, settings)
{
CampaignMetadata = new CampaignMetadata(this);
UpgradeManager = new UpgradeManager(this);
map = new Map(this, mapSeed, settings);
Settings = settings;
map = new Map(this, mapSeed);
foreach (JobPrefab jobPrefab in JobPrefab.Prefabs)
{
for (int i = 0; i < jobPrefab.InitialCount; i++)
@@ -79,7 +75,7 @@ namespace Barotrauma
/// <summary>
/// Loads a previously saved single player campaign from XML
/// </summary>
private SinglePlayerCampaign(XElement element) : base(GameModePreset.SinglePlayerCampaign)
private SinglePlayerCampaign(XElement element) : base(GameModePreset.SinglePlayerCampaign, CampaignSettings.Empty)
{
IsFirstRound = false;
@@ -87,14 +83,15 @@ namespace Barotrauma
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "campaignsettings":
case CampaignSettings.LowerCaseSaveElementName:
Settings = new CampaignSettings(subElement);
break;
case "crew":
GameMain.GameSession.CrewManager = new CrewManager(subElement, true);
ActiveOrdersElement = subElement.GetChildElement("activeorders");
break;
case "map":
map = Map.Load(this, subElement, Settings);
map = Map.Load(this, subElement);
break;
case "metadata":
CampaignMetadata = new CampaignMetadata(this, subElement);
@@ -162,21 +159,14 @@ namespace Barotrauma
/// <summary>
/// Start a completely new single player campaign
/// </summary>
public static SinglePlayerCampaign StartNew(string mapSeed, SubmarineInfo selectedSub, CampaignSettings settings)
{
var campaign = new SinglePlayerCampaign(mapSeed, settings);
return campaign;
}
public static SinglePlayerCampaign StartNew(string mapSeed, CampaignSettings startingSettings) => new SinglePlayerCampaign(mapSeed, startingSettings);
/// <summary>
/// Load a previously saved single player campaign from xml
/// </summary>
/// <param name="element"></param>
/// <returns></returns>
public static SinglePlayerCampaign Load(XElement element)
{
return new SinglePlayerCampaign(element);
}
public static SinglePlayerCampaign Load(XElement element) => new SinglePlayerCampaign(element);
private void InitUI()
{
@@ -242,11 +232,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 +450,8 @@ 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 +722,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);

View File

@@ -102,29 +102,11 @@ namespace Barotrauma.Tutorials
radioSpeakerName = TextManager.Get("Tutorial.Radio.Watchman");
GameMain.GameSession.CrewManager.AllowCharacterSwitch = false;
var revolver = FindOrGiveItem(captain, "revolver".ToIdentifier());
revolver.Unequip(captain);
captain.Inventory.RemoveItem(revolver);
var captainscap =
captain.Inventory.FindItemByIdentifier("captainscap1".ToIdentifier()) ??
captain.Inventory.FindItemByIdentifier("captainscap2".ToIdentifier()) ??
captain.Inventory.FindItemByIdentifier("captainscap3".ToIdentifier());
if (captainscap != null)
foreach (Item item in captain.Inventory.AllItemsMod)
{
captainscap.Unequip(captain);
captain.Inventory.RemoveItem(captainscap);
}
var captainsuniform =
captain.Inventory.FindItemByIdentifier("captainsuniform1".ToIdentifier()) ??
captain.Inventory.FindItemByIdentifier("captainsuniform2".ToIdentifier()) ??
captain.Inventory.FindItemByIdentifier("captainsuniform3".ToIdentifier());
if (captainsuniform != null)
{
captainsuniform.Unequip(captain);
captain.Inventory.RemoveItem(captainsuniform);
if (item.HasTag("identitycard") || item.HasTag("mobileradio")) { continue; }
item.Unequip(captain);
captain.Inventory.RemoveItem(item);
}
var steerOrder = OrderPrefab.Prefabs["steer"];

View File

@@ -105,21 +105,12 @@ namespace Barotrauma.Tutorials
radioSpeakerName = TextManager.Get("Tutorial.Radio.Speaker");
doctor = Character.Controlled;
var bandages = FindOrGiveItem(doctor, "antibleeding1".ToIdentifier());
bandages.Unequip(doctor);
doctor.Inventory.RemoveItem(bandages);
var syringegun = FindOrGiveItem(doctor, "syringegun".ToIdentifier());
syringegun.Unequip(doctor);
doctor.Inventory.RemoveItem(syringegun);
var antibiotics = FindOrGiveItem(doctor, "antibiotics".ToIdentifier());
antibiotics.Unequip(doctor);
doctor.Inventory.RemoveItem(antibiotics);
var morphine = FindOrGiveItem(doctor, "antidama1".ToIdentifier());
morphine.Unequip(doctor);
doctor.Inventory.RemoveItem(morphine);
foreach (Item item in doctor.Inventory.AllItemsMod)
{
if (item.HasTag("clothing") || item.HasTag("identitycard") || item.HasTag("mobileradio")) { continue; }
item.Unequip(doctor);
doctor.Inventory.RemoveItem(item);
}
doctor_suppliesCabinet = Item.ItemList.Find(i => i.HasTag("doctor_suppliescabinet"))?.GetComponent<ItemContainer>();
doctor_medBayCabinet = Item.ItemList.Find(i => i.HasTag("doctor_medbaycabinet"))?.GetComponent<ItemContainer>();
@@ -260,7 +251,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
{

View File

@@ -131,9 +131,12 @@ namespace Barotrauma.Tutorials
radioSpeakerName = TextManager.Get("Tutorial.Radio.Speaker");
engineer = Character.Controlled;
var toolbelt = FindOrGiveItem(engineer, "toolbelt".ToIdentifier());
toolbelt.Unequip(engineer);
engineer.Inventory.RemoveItem(toolbelt);
foreach (Item item in engineer.Inventory.AllItemsMod)
{
if (item.HasTag("clothing") || item.HasTag("identitycard") || item.HasTag("mobileradio")) { continue; }
item.Unequip(engineer);
engineer.Inventory.RemoveItem(item);
}
var repairOrder = OrderPrefab.Prefabs["repairsystems"];
engineer_repairIcon = repairOrder.SymbolSprite;
@@ -278,7 +281,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;

View File

@@ -160,13 +160,12 @@ namespace Barotrauma.Tutorials
radioSpeakerName = TextManager.Get("Tutorial.Radio.Speaker");
mechanic = Character.Controlled;
var toolbelt = FindOrGiveItem(mechanic, "toolbelt".ToIdentifier());
toolbelt.Unequip(mechanic);
mechanic.Inventory.RemoveItem(toolbelt);
var crowbar = FindOrGiveItem(mechanic, "crowbar".ToIdentifier());
crowbar.Unequip(mechanic);
mechanic.Inventory.RemoveItem(crowbar);
foreach (Item item in mechanic.Inventory.AllItemsMod)
{
if (item.HasTag("clothing") || item.HasTag("identitycard") || item.HasTag("mobileradio")) { continue; }
item.Unequip(mechanic);
mechanic.Inventory.RemoveItem(item);
}
var repairOrder = OrderPrefab.Prefabs["repairsystems"];
mechanic_repairIcon = repairOrder.SymbolSprite;
@@ -297,7 +296,10 @@ namespace Barotrauma.Tutorials
public override void Update(float deltaTime)
{
mechanic_brokenhull_1.WaterVolume = MathHelper.Clamp(mechanic_brokenhull_1.WaterVolume, 0, mechanic_brokenhull_1.Volume * 0.85f);
if (mechanic_brokenhull_1 != null)
{
mechanic_brokenhull_1.WaterVolume = MathHelper.Clamp(mechanic_brokenhull_1.WaterVolume, 0, mechanic_brokenhull_1.Volume * 0.85f);
}
base.Update(deltaTime);
}
@@ -334,7 +336,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;
@@ -377,7 +379,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()))
@@ -413,7 +415,7 @@ namespace Barotrauma.Tutorials
}
} while (mechanic_workingPump.FlowPercentage >= 0 || !mechanic_workingPump.IsActive); // Highlight until draining
SetHighlight(mechanic_workingPump.Item, false);
do { yield return null; } while (mechanic_brokenhull_1.WaterPercentage > waterVolumeBeforeOpening); // Unlock door once drained
do { yield return null; } while (mechanic_brokenhull_1 != null && mechanic_brokenhull_1.WaterPercentage > waterVolumeBeforeOpening); // Unlock door once drained
RemoveCompletedObjective(3);
GameAnalyticsManager.AddDesignEvent("Tutorial:MechanicTutorial:Objective3");

View File

@@ -141,36 +141,12 @@ namespace Barotrauma.Tutorials
radioSpeakerName = TextManager.Get("Tutorial.Radio.Speaker");
officer = Character.Controlled;
var handcuffs = FindOrGiveItem(officer, "handcuffs".ToIdentifier());
handcuffs.Unequip(officer);
officer.Inventory.RemoveItem(handcuffs);
var stunbaton = FindOrGiveItem(officer, "stunbaton".ToIdentifier());
stunbaton.Unequip(officer);
officer.Inventory.RemoveItem(stunbaton);
var smg = FindOrGiveItem(officer, "smg".ToIdentifier());
smg.Unequip(officer);
officer.Inventory.RemoveItem(smg);
var divingknife = FindOrGiveItem(officer, "divingknife".ToIdentifier());
divingknife.Unequip(officer);
officer.Inventory.RemoveItem(divingknife);
var steroids = FindOrGiveItem(officer, "steroids".ToIdentifier());
steroids.Unequip(officer);
officer.Inventory.RemoveItem(steroids);
var ballistichelmet =
officer.Inventory.FindItemByIdentifier("ballistichelmet1".ToIdentifier()) ??
officer.Inventory.FindItemByIdentifier("ballistichelmet2".ToIdentifier()) ??
FindOrGiveItem(officer, "ballistichelmet3".ToIdentifier());
ballistichelmet.Unequip(officer);
officer.Inventory.RemoveItem(ballistichelmet);
var bodyarmor = FindOrGiveItem(officer, "bodyarmor".ToIdentifier());
bodyarmor.Unequip(officer);
officer.Inventory.RemoveItem(bodyarmor);
foreach (Item item in officer.Inventory.AllItemsMod)
{
if (item.HasTag("clothing") || item.HasTag("identitycard") || item.HasTag("mobileradio")) { continue; }
item.Unequip(officer);
officer.Inventory.RemoveItem(item);
}
var gunOrder = OrderPrefab.Prefabs["operateweapons"];
officer_gunIcon = gunOrder.SymbolSprite;

View File

@@ -1,10 +1,7 @@
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using Barotrauma.IO;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Tutorials
{
@@ -106,7 +103,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!");
@@ -228,6 +225,8 @@ namespace Barotrauma.Tutorials
{
CoroutineManager.StopCoroutines(tutorialCoroutine);
}
GUI.PreventPauseMenuToggle = false;
ContentRunning = false;
infoBox = null;
}
else if (Character.Controlled.IsDead)

View File

@@ -28,7 +28,7 @@ namespace Barotrauma
UserListData = "ReadyUserList",
ReadySpriteData = "ReadySprite";
private int lastSecond;
private int lastSecond = 1;
private GUIMessageBox? msgBox;
private GUIMessageBox? resultsBox;
@@ -44,7 +44,7 @@ namespace Barotrauma
msgBox = new GUIMessageBox(readyCheckHeader, readyCheckBody(author), new[] { yesButton, noButton }, relativeSize, minSize, type: GUIMessageBox.Type.Vote) { UserData = PromptData, Draggable = true };
GUILayoutGroup contentLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.125f), msgBox.Content.RectTransform), childAnchor: Anchor.Center);
new GUIProgressBar(new RectTransform(new Vector2(0.8f, 1f), contentLayout.RectTransform), time / endTime, GUIStyle.Orange) { UserData = TimerData };
new GUIProgressBar(new RectTransform(new Vector2(0.8f, 1f), contentLayout.RectTransform), 0.0f, GUIStyle.Orange) { UserData = TimerData };
// Yes
msgBox.Buttons[0].OnClicked = delegate
@@ -116,17 +116,18 @@ namespace Barotrauma
private void UpdateBar()
{
double elapsedTime = (DateTime.Now - startTime).TotalSeconds;
if (msgBox != null && !msgBox.Closed && GUIMessageBox.MessageBoxes.Contains(msgBox))
{
if (msgBox.FindChild(TimerData, true) is GUIProgressBar bar)
{
bar.BarSize = time / endTime;
bar.BarSize = (float)(elapsedTime / (endTime - startTime).TotalSeconds);
}
}
// play click sound after a second has passed
int second = (int) Math.Ceiling(time);
if (second < lastSecond)
int second = (int)Math.Ceiling(elapsedTime);
if (second > lastSecond)
{
if (msgBox != null && !msgBox.Closed)
{
@@ -156,7 +157,8 @@ namespace Barotrauma
bool isOwn = false;
byte authorId = 0;
float duration = inc.ReadSingle();
long startTime = inc.ReadInt64();
long endTime = inc.ReadInt64();
string author = inc.ReadString();
bool hasAuthor = inc.ReadBoolean();
@@ -173,7 +175,9 @@ namespace Barotrauma
clients.Add(inc.ReadByte());
}
ReadyCheck rCheck = new ReadyCheck(clients, duration);
ReadyCheck rCheck = new ReadyCheck(clients,
DateTimeOffset.FromUnixTimeSeconds(startTime).LocalDateTime,
DateTimeOffset.FromUnixTimeSeconds(endTime).LocalDateTime);
crewManager.ActiveReadyCheck = rCheck;
if (isOwn)
@@ -192,12 +196,10 @@ namespace Barotrauma
}
break;
case ReadyCheckState.Update:
float time = inc.ReadSingle();
ReadyStatus newState = (ReadyStatus) inc.ReadByte();
byte targetId = inc.ReadByte();
if (crewManager.ActiveReadyCheck != null)
{
crewManager.ActiveReadyCheck.time = time;
crewManager.ActiveReadyCheck?.UpdateState(targetId, newState);
}
break;

View File

@@ -64,7 +64,6 @@ namespace Barotrauma
public Vector2[] SlotPositions;
public static Point SlotSize;
public static int Spacing;
public static int HideButtonWidth;
private Layout layout;
public Layout CurrentLayout
@@ -77,64 +76,11 @@ namespace Barotrauma
SetSlotPositions(layout);
}
}
public bool Hidden { get; set; }
private bool hidePersonalSlots;
private float hidePersonalSlotsState;
private GUIButton hideButton;
private Rectangle personalSlotArea;
public bool HidePersonalSlots
{
get { return hidePersonalSlots; }
}
public Rectangle PersonalSlotArea
{
get { return personalSlotArea; }
}
private readonly GUIImage[] indicators = new GUIImage[5];
private readonly int[] indicatorIndices = new int[5];
private Vector2 indicatorSpriteSize;
private GUILayoutGroup indicatorGroup;
partial void InitProjSpecific(XElement element)
{
Hidden = true;
hideButton = new GUIButton(new RectTransform(new Point((int)(31f * (HUDLayoutSettings.BottomRightInfoArea.Height / 100f)), HUDLayoutSettings.BottomRightInfoArea.Height), GUI.Canvas)
{ AbsoluteOffset = HUDLayoutSettings.CrewArea.Location },
"", style: "EquipmentToggleButton");
indicatorGroup = new GUILayoutGroup(new RectTransform(Point.Zero, hideButton.RectTransform)) { IsHorizontal = false };
indicatorGroup.ChildAnchor = Anchor.TopCenter;
indicatorSpriteSize = GUIStyle.GetComponentStyle("EquipmentIndicatorDivingSuit").GetDefaultSprite().size;
indicators[0] = new GUIImage(new RectTransform(Point.Zero, indicatorGroup.RectTransform), "EquipmentIndicatorDivingSuit");
indicators[1] = new GUIImage(new RectTransform(Point.Zero, indicatorGroup.RectTransform), "EquipmentIndicatorID");
indicators[2] = new GUIImage(new RectTransform(Point.Zero, indicatorGroup.RectTransform), "EquipmentIndicatorOutfit");
indicators[3] = new GUIImage(new RectTransform(Point.Zero, indicatorGroup.RectTransform), "EquipmentIndicatorHeadwear");
indicators[4] = new GUIImage(new RectTransform(Point.Zero, indicatorGroup.RectTransform), "EquipmentIndicatorHeadphones");
indicatorIndices[0] = FindLimbSlot(InvSlotType.OuterClothes);
indicatorIndices[1] = FindLimbSlot(InvSlotType.Card);
indicatorIndices[2] = FindLimbSlot(InvSlotType.InnerClothes);
indicatorIndices[3] = FindLimbSlot(InvSlotType.Head);
indicatorIndices[4] = FindLimbSlot(InvSlotType.Headset);
for (int i = 0; i < indicators.Length; i++)
{
indicators[i].CanBeFocused = false;
}
hideButton.OnClicked += (GUIButton btn, object userdata) =>
{
hidePersonalSlots = !hidePersonalSlots;
return true;
};
hidePersonalSlots = false;
SlotPositions = new Vector2[SlotTypes.Length];
CurrentLayout = Layout.Default;
SetSlotPositions(layout);
@@ -271,25 +217,6 @@ namespace Barotrauma
return false;
}
private void SetIndicatorSizes()
{
indicatorGroup.RectTransform.AbsoluteOffset = new Point((int)Math.Round(4 * GUI.Scale), (int)Math.Round(7 * GUI.Scale));
indicatorGroup.RectTransform.NonScaledSize = new Point(hideButton.Rect.Width - indicatorGroup.RectTransform.AbsoluteOffset.X * 2, hideButton.Rect.Height - indicatorGroup.RectTransform.AbsoluteOffset.Y * 2);
indicatorGroup.AbsoluteSpacing = (int)Math.Ceiling(2 * GUI.Scale);
int indicatorHeight = (indicatorGroup.RectTransform.NonScaledSize.Y - indicatorGroup.AbsoluteSpacing * (indicators.Length - 1)) / indicators.Length;
int indicatorWidth = (int)(indicatorSpriteSize.X / (indicatorSpriteSize.Y / indicatorHeight));
if (HideButtonWidth % 2 != indicatorWidth % 2) indicatorWidth++;
Point indicatorSize = new Point(indicatorWidth, indicatorHeight);
for (int i = 0; i < indicators.Length; i++)
{
indicators[i].RectTransform.NonScaledSize = indicatorSize;
}
}
private void SetSlotPositions(Layout layout)
{
bool isFourByThree = GUI.IsFourByThree();
@@ -302,13 +229,9 @@ namespace Barotrauma
Spacing = (int)(8 * UIScale);
}
HideButtonWidth = (int)(31f * (HUDLayoutSettings.BottomRightInfoArea.Height / 100f));
SlotSize = !isFourByThree ? (SlotSpriteSmall.size * UIScale).ToPoint() : (SlotSpriteSmall.size * UIScale * .925f).ToPoint();
int bottomOffset = SlotSize.Y + Spacing * 2 + ContainedIndicatorHeight;
hideButton.Visible = false;
if (visualSlots == null) { CreateSlots(); }
if (visualSlots.None()) { return; }
@@ -320,7 +243,7 @@ namespace Barotrauma
int normalSlotCount = SlotTypes.Count(s => !PersonalSlots.HasFlag(s) && s != InvSlotType.HealthInterface);
int x = GameMain.GraphicsWidth / 2 - normalSlotCount * (SlotSize.X + Spacing) / 2;
int upperX = HUDLayoutSettings.BottomRightInfoArea.X - SlotSize.X - Spacing * 4 - HideButtonWidth;
int upperX = HUDLayoutSettings.BottomRightInfoArea.X - SlotSize.X - Spacing;
//make sure the rightmost normal slot doesn't overlap with the personal slots
x -= Math.Max((x + normalSlotCount * (SlotSize.X + Spacing)) - (upperX - personalSlotCount * (SlotSize.X + Spacing)), 0);
@@ -343,16 +266,6 @@ namespace Barotrauma
x += SlotSize.X + Spacing;
}
}
if (hideButtonSlotIndex > -1)
{
hideButton.RectTransform.SetPosition(Anchor.TopLeft, Pivot.TopLeft);
hideButton.RectTransform.NonScaledSize = new Point(HideButtonWidth, HUDLayoutSettings.BottomRightInfoArea.Height);
hideButton.RectTransform.AbsoluteOffset = new Point(HUDLayoutSettings.BottomRightInfoArea.Left - HideButtonWidth + GUI.IntScaleCeiling(2f), HUDLayoutSettings.BottomRightInfoArea.Y + GUI.IntScaleCeiling(1f));
hideButton.Visible = Screen.Selected != GameMain.SubEditorScreen || !GameMain.SubEditorScreen.WiringMode;
SetIndicatorSizes();
}
}
break;
case Layout.Right:
@@ -525,6 +438,7 @@ namespace Barotrauma
if (!AccessibleWhenAlive && !character.IsDead && !AccessibleByOwner)
{
syncItemsDelay = Math.Max(syncItemsDelay - deltaTime, 0.0f);
doubleClickedItems.Clear();
return;
}
@@ -532,58 +446,13 @@ namespace Barotrauma
bool hoverOnInventory = GUI.MouseOn == null &&
((selectedSlot != null && selectedSlot.IsSubSlot) || (DraggingItems.Any() && (DraggingSlot == null || !DraggingSlot.MouseOn())));
if (CharacterHealth.OpenHealthWindow != null) hoverOnInventory = true;
if (layout == Layout.Default && (Screen.Selected != GameMain.SubEditorScreen || Screen.Selected is SubEditorScreen editor && editor.WiringMode))
{
if (hideButton.Visible)
{
hideButton.AddToGUIUpdateList();
hideButton.UpdateManually(deltaTime, alsoChildren: true);
hidePersonalSlotsState = hidePersonalSlots ?
Math.Min(hidePersonalSlotsState + deltaTime * 5.0f, 1.0f) :
Math.Max(hidePersonalSlotsState - deltaTime * 5.0f, 0.0f);
bool personalSlotsMoving = hidePersonalSlotsState > 0 && hidePersonalSlotsState < 1f;
for (int i = 0; i < visualSlots.Length; i++)
{
if (!PersonalSlots.HasFlag(SlotTypes[i])) { continue; }
if (HidePersonalSlots)
{
if (selectedSlot?.Slot == visualSlots[i]) { selectedSlot = null; }
highlightedSubInventorySlots.RemoveWhere(s => s.Slot == visualSlots[i]);
}
visualSlots[i].IsMoving = personalSlotsMoving;
visualSlots[i].DrawOffset = Vector2.Lerp(Vector2.Zero, new Vector2(personalSlotArea.Width, 0.0f), hidePersonalSlotsState);
}
}
}
if (CharacterHealth.OpenHealthWindow != null) { hoverOnInventory = true; }
if (hoverOnInventory) { HideTimer = 0.5f; }
if (HideTimer > 0.0f) { HideTimer -= deltaTime; }
UpdateSlotInput();
//force personal slots open if an item is running out of battery/fuel/oxygen/etc
if (hidePersonalSlots)
{
for (int i = 0; i < visualSlots.Length; i++)
{
var item = slots[i].FirstOrDefault();
if (item?.OwnInventory != null && item.OwnInventory.Capacity == 1 && PersonalSlots.HasFlag(SlotTypes[i]))
{
var containedItem = item.OwnInventory.AllItems.FirstOrDefault();
if (containedItem != null &&
containedItem.Condition > 0.0f &&
containedItem.Condition / containedItem.MaxCondition < 0.15f)
{
hidePersonalSlots = false;
}
}
}
}
hideSubInventories.Clear();
//remove highlighted subinventory slots that can no longer be accessed
highlightedSubInventorySlots.RemoveWhere(s =>
@@ -652,8 +521,6 @@ namespace Barotrauma
if (character == Character.Controlled && character.SelectedCharacter == null) // Permanently open subinventories only available when the default UI layout is in use -> not when grabbing characters
{
UpdateEquipmentIndicators();
//remove the highlighted slots of other characters' inventories when not grabbing anyone
highlightedSubInventorySlots.RemoveWhere(s => s.ParentInventory != this && s.ParentInventory?.Owner is Character);
@@ -798,40 +665,6 @@ namespace Barotrauma
}
}
}
private void UpdateEquipmentIndicators()
{
for (int i = 0; i < indicators.Length; i++)
{
if (indicatorIndices[i] < 0) { continue; }
Item item = slots[indicatorIndices[i]].FirstOrDefault();
if (item != null)
{
Wearable wearable = item.GetComponent<Wearable>();
if (wearable != null && wearable.DisplayContainedStatus)
{
float conditionPercentage = item.GetContainedItemConditionPercentage();
if (conditionPercentage != -1)
{
indicators[i].Color = ToolBox.GradientLerp(conditionPercentage, GUIStyle.EquipmentIndicatorRunningOut, GUIStyle.EquipmentIndicatorEquipped);
}
else
{
indicators[i].Color = GUIStyle.EquipmentIndicatorRunningOut;
}
}
else
{
indicators[i].Color = GUIStyle.EquipmentIndicatorEquipped;
}
}
else
{
indicators[i].Color = GUIStyle.EquipmentIndicatorNotEquipped;
}
}
}
private void ShowSubInventory(SlotReference slotRef, float deltaTime, Camera cam, List<SlotReference> hideSubInventories, bool isEquippedSubInventory)
{
@@ -931,7 +764,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;
@@ -941,6 +774,7 @@ namespace Barotrauma
}
else
{
bool isEquippable = item.AllowedSlots.Any(s => s != InvSlotType.Any);
var selectedContainer = character.SelectedConstruction?.GetComponent<ItemContainer>();
if (selectedContainer != null &&
selectedContainer.Inventory != null &&
@@ -959,14 +793,16 @@ 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;
}
else if (character.HeldItems.Any(i =>
i.OwnInventory != null &&
((i.OwnInventory.CanBePut(item) && allowInventorySwap) || (i.OwnInventory.Capacity == 1 && i.OwnInventory.AllowSwappingContainedItems && i.OwnInventory.Container.CanBeContained(item)))))
i.OwnInventory != null &&
/*disallow putting into equipped item if the item is equippable (equip as the quick action instead)*/
((i.OwnInventory.CanBePut(item) && (allowInventorySwap || !isEquippable)) || (i.OwnInventory.Capacity == 1 && i.OwnInventory.AllowSwappingContainedItems && i.OwnInventory.Container.CanBeContained(item)))))
{
return QuickUseAction.PutToEquippedItem;
}
@@ -1129,11 +965,18 @@ namespace Barotrauma
}
break;
case QuickUseAction.PutToEquippedItem:
foreach (Item heldItem in character.HeldItems)
{
if (heldItem.OwnInventory == null) { continue; }
//don't allow swapping if we're moving items into an item with 1 slot holding a stack of items
//(in that case, the quick action should just fill up the stack)
bool disallowSwapping =
heldItem.OwnInventory.Capacity == 1 &&
heldItem.OwnInventory.GetItemAt(0)?.Prefab == item.Prefab &&
heldItem.OwnInventory.GetItemsAt(0).Count() > 1;
if (heldItem.OwnInventory.TryPutItem(item, Character.Controlled) ||
(heldItem.OwnInventory.Capacity == 1 && heldItem.OwnInventory.TryPutItem(item, 0, allowSwapping: true, allowCombine: false, user: Character.Controlled)))
(heldItem.OwnInventory.Capacity == 1 && heldItem.OwnInventory.TryPutItem(item, 0, allowSwapping: !disallowSwapping, allowCombine: false, user: Character.Controlled)))
{
success = true;
for (int j = 0; j < capacity; j++)
@@ -1195,11 +1038,6 @@ namespace Barotrauma
DrawSlot(spriteBatch, this, visualSlots[i], slots[i].FirstOrDefault(), i, drawItem, SlotTypes[i]);
}
if (hideButton != null && hideButton.Visible && !Locked)
{
hideButton.DrawManually(spriteBatch, alsoChildren: true);
}
VisualSlot highlightedQuickUseSlot = null;
Rectangle inventoryArea = Rectangle.Empty;

View File

@@ -43,6 +43,31 @@ namespace Barotrauma.Items.Components
corners[2] = center + new Vector2(shadowSize.X, shadowSize.Y) / 2;
corners[3] = center + new Vector2(shadowSize.X, -shadowSize.Y) / 2;
if (IsHorizontal)
{
if (item.FlippedX)
{
Vector2 itemCenter = new Vector2(item.Rect.Center.X, item.Rect.Y - item.Rect.Height / 2);
for (int i = 0; i < corners.Length; i++)
{
corners[i].X = itemCenter.X * 2 - corners[i].X;
}
Array.Reverse(corners);
}
}
else
{
if (item.FlippedY)
{
Vector2 itemCenter = new Vector2(item.Rect.Center.X, item.Rect.Y - item.Rect.Height / 2);
for (int i = 0; i < corners.Length; i++)
{
corners[i].Y = itemCenter.Y * 2 - corners[i].Y;
}
Array.Reverse(corners);
}
}
return corners;
}
@@ -66,6 +91,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 +116,7 @@ namespace Barotrauma.Items.Components
else
{
convexHull2.Enabled = true;
convexHull2.SetVertices(GetConvexHullCorners(rect2));
convexHull2.SetVertices(GetConvexHullCorners(rect2), mergeOverlappingSegments);
}
}
}
@@ -112,7 +140,7 @@ namespace Barotrauma.Items.Components
else
{
convexHull2.Enabled = true;
convexHull2.SetVertices(GetConvexHullCorners(rect2));
convexHull2.SetVertices(GetConvexHullCorners(rect2), mergeOverlappingSegments);
}
}
}
@@ -127,7 +155,7 @@ namespace Barotrauma.Items.Components
else
{
convexHull.Enabled = true;
convexHull.SetVertices(GetConvexHullCorners(rect));
convexHull.SetVertices(GetConvexHullCorners(rect), mergeOverlappingSegments);
}
}
@@ -160,69 +188,68 @@ namespace Barotrauma.Items.Components
if (stuck > 0.0f && weldedSprite != null)
{
Vector2 weldSpritePos = new Vector2(item.Rect.Center.X, item.Rect.Y - item.Rect.Height / 2.0f) + shakePos;
if (item.Submarine != null) weldSpritePos += item.Submarine.DrawPosition;
if (item.Submarine != null) { weldSpritePos += item.Submarine.DrawPosition; }
weldSpritePos.Y = -weldSpritePos.Y;
weldedSprite.Draw(spriteBatch,
weldSpritePos, item.SpriteColor * (stuck / 100.0f), scale: item.Scale);
}
if (openState >= 1.0f)
{
return;
}
if (openState >= 1.0f) { return; }
Vector2 pos;
if (IsHorizontal)
{
Vector2 pos = new Vector2(item.Rect.X, item.Rect.Y - item.Rect.Height / 2) + shakePos;
if (item.Submarine != null) pos += item.Submarine.DrawPosition;
pos.Y = -pos.Y;
if (brokenSprite == null || !IsBroken)
{
spriteBatch.Draw(doorSprite.Texture, pos,
new Rectangle((int) (doorSprite.SourceRect.X + doorSprite.size.X * openState),
(int) doorSprite.SourceRect.Y,
(int) (doorSprite.size.X * (1.0f - openState)), (int) doorSprite.size.Y),
color, 0.0f, doorSprite.Origin, item.Scale, SpriteEffects.None, doorSprite.Depth);
}
if (brokenSprite != null && item.Health < item.MaxCondition)
{
Vector2 scale = scaleBrokenSprite ? new Vector2(1.0f, 1.0f - item.Health / item.MaxCondition) : Vector2.One;
float alpha = fadeBrokenSprite ? 1.0f - item.Health / item.MaxCondition : 1.0f;
spriteBatch.Draw(brokenSprite.Texture, pos,
new Rectangle((int)(brokenSprite.SourceRect.X + brokenSprite.size.X * openState), brokenSprite.SourceRect.Y,
(int)(brokenSprite.size.X * (1.0f - openState)), (int)brokenSprite.size.Y),
color * alpha, 0.0f, brokenSprite.Origin, scale * item.Scale, SpriteEffects.None,
brokenSprite.Depth);
}
pos = new Vector2(item.Rect.X, item.Rect.Y - item.Rect.Height / 2);
if (item.FlippedX) { pos.X += (int)(doorSprite.size.X * item.Scale * openState); }
}
else
{
Vector2 pos = new Vector2(item.Rect.Center.X, item.Rect.Y) + shakePos;
if (item.Submarine != null) pos += item.Submarine.DrawPosition;
pos.Y = -pos.Y;
if (brokenSprite == null || !IsBroken)
{
spriteBatch.Draw(doorSprite.Texture, pos,
new Rectangle(doorSprite.SourceRect.X,
(int) (doorSprite.SourceRect.Y + doorSprite.size.Y * openState),
(int) doorSprite.size.X, (int) (doorSprite.size.Y * (1.0f - openState))),
color, 0.0f, doorSprite.Origin, item.Scale, SpriteEffects.None, doorSprite.Depth);
}
if (brokenSprite != null && item.Health < item.MaxCondition)
{
Vector2 scale = scaleBrokenSprite ? new Vector2(1.0f - item.Health / item.MaxCondition, 1.0f) : Vector2.One;
float alpha = fadeBrokenSprite ? 1.0f - item.Health / item.MaxCondition : 1.0f;
spriteBatch.Draw(brokenSprite.Texture, pos,
new Rectangle(brokenSprite.SourceRect.X, (int)(brokenSprite.SourceRect.Y + brokenSprite.size.Y * openState),
(int)brokenSprite.size.X, (int)(brokenSprite.size.Y * (1.0f - openState))),
color * alpha, 0.0f, brokenSprite.Origin, scale * item.Scale, SpriteEffects.None, brokenSprite.Depth);
}
pos = new Vector2(item.Rect.Center.X, item.Rect.Y);
if (item.FlippedY) { pos.Y -= (int)(doorSprite.size.Y * item.Scale * openState); }
}
pos += shakePos;
if (item.Submarine != null) { pos += item.Submarine.DrawPosition; }
pos.Y = -pos.Y;
if (brokenSprite == null || !IsBroken)
{
spriteBatch.Draw(doorSprite.Texture, pos,
getSourceRect(doorSprite, openState, IsHorizontal),
color, 0.0f, doorSprite.Origin, item.Scale, item.SpriteEffects, doorSprite.Depth);
}
if (brokenSprite != null && item.Health < item.MaxCondition)
{
Vector2 scale = scaleBrokenSprite ? new Vector2(1.0f - item.Health / item.MaxCondition) : Vector2.One;
if (IsHorizontal) { scale.X = 1; } else { scale.Y = 1; }
float alpha = fadeBrokenSprite ? 1.0f - item.Health / item.MaxCondition : 1.0f;
spriteBatch.Draw(brokenSprite.Texture, pos,
getSourceRect(brokenSprite, openState, IsHorizontal),
color * alpha, 0.0f, brokenSprite.Origin, scale * item.Scale, item.SpriteEffects,
brokenSprite.Depth);
}
static Rectangle getSourceRect(Sprite sprite, float openState, bool horizontal)
{
if (horizontal)
{
return new Rectangle(
(int)(sprite.SourceRect.X + sprite.size.X * openState),
sprite.SourceRect.Y,
(int)(sprite.size.X * (1.0f - openState)),
(int)sprite.size.Y);
}
else
{
return new Rectangle(
sprite.SourceRect.X,
(int)(sprite.SourceRect.Y + sprite.size.Y * openState),
(int)sprite.size.X,
(int)(sprite.size.Y * (1.0f - openState)));
}
}
}
partial void OnFailedToOpen()

View File

@@ -321,7 +321,7 @@ namespace Barotrauma.Items.Components
{
GUILayoutGroup layout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.08f), parent), isHorizontal: true);
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), layout.RectTransform), label);
GUINumberInput input = new GUINumberInput(new RectTransform(new Vector2(0.5f, 1f), layout.RectTransform), GUINumberInput.NumberType.Int) { IntValue = defaultValue };
GUINumberInput input = new GUINumberInput(new RectTransform(new Vector2(0.5f, 1f), layout.RectTransform), NumberType.Int) { IntValue = defaultValue };
return input;
}
@@ -329,7 +329,7 @@ namespace Barotrauma.Items.Components
{
GUILayoutGroup layout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.08f), parent), isHorizontal: true);
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), layout.RectTransform), label);
GUINumberInput input = new GUINumberInput(new RectTransform(new Vector2(0.5f, 1f), layout.RectTransform), GUINumberInput.NumberType.Float) { FloatValue = defaultValue, DecimalsToDisplay = 2 };
GUINumberInput input = new GUINumberInput(new RectTransform(new Vector2(0.5f, 1f), layout.RectTransform), NumberType.Float) { FloatValue = defaultValue, DecimalsToDisplay = 2 };
return input;
}
@@ -341,7 +341,7 @@ namespace Barotrauma.Items.Components
for (var i = 0; i < values.Length; i++)
{
float value = values[i];
GUINumberInput input = new GUINumberInput(new RectTransform(new Vector2(0.5f / values.Length, 1f), layout.RectTransform), GUINumberInput.NumberType.Float)
GUINumberInput input = new GUINumberInput(new RectTransform(new Vector2(0.5f / values.Length, 1f), layout.RectTransform), NumberType.Float)
{
FloatValue = value, DecimalsToDisplay = 2,
MinValueFloat = min,

View File

@@ -116,12 +116,6 @@ namespace Barotrauma.Items.Components
loadAttachments(Attachments, disguisedBeardElement, WearableType.Beard);
loadAttachments(Attachments, disguisedMoustacheElement, WearableType.Moustache);
loadAttachments(Attachments, disguisedHairElement, WearableType.Hair);
loadAttachments(Attachments,
characterInfo.OmitJobInPortraitClothing
? JobPrefab.NoJobElement?.GetChildElement("PortraitClothing")
: JobPrefab?.ClothingElement,
WearableType.JobIndicator);
}
HairColor = hairColor;

View File

@@ -203,7 +203,7 @@ namespace Barotrauma.Items.Components
private float lastMuffleCheckTime;
private ItemSound loopingSound;
private SoundChannel loopingSoundChannel;
private List<SoundChannel> playingOneshotSoundChannels = new List<SoundChannel>();
private readonly List<SoundChannel> playingOneshotSoundChannels = new List<SoundChannel>();
public ItemComponent ReplacedBy;
public ItemComponent GetReplacementOrThis()
@@ -211,13 +211,16 @@ namespace Barotrauma.Items.Components
return ReplacedBy?.GetReplacementOrThis() ?? this;
}
public bool NeedsSoundUpdate()
{
if (hasSoundsOfType[(int)ActionType.Always]) { return true; }
if (loopingSoundChannel != null && loopingSoundChannel.IsPlaying) { return true; }
if (playingOneshotSoundChannels.Count > 0) { return true; }
return false;
}
public void UpdateSounds()
{
if (!isActive || item.Condition <= 0.0f)
{
StopSounds(ActionType.OnActive);
}
if (loopingSound != null && loopingSoundChannel != null && loopingSoundChannel.IsPlaying)
{
if (Timing.TotalTime > lastMuffleCheckTime + 0.2f)
@@ -280,6 +283,7 @@ namespace Barotrauma.Items.Components
loopingSound.RoundSound.GetRandomFrequencyMultiplier(),
SoundPlayer.ShouldMuffleSound(Character.Controlled, item.WorldPosition, loopingSound.Range, Character.Controlled?.CurrentHull));
loopingSoundChannel.Looping = true;
item.CheckNeedsSoundUpdate(this);
//TODO: tweak
loopingSoundChannel.Near = loopingSound.Range * 0.4f;
loopingSoundChannel.Far = loopingSound.Range;
@@ -298,7 +302,6 @@ namespace Barotrauma.Items.Components
loopingSound = null;
}
}
return;
}
@@ -333,6 +336,7 @@ namespace Barotrauma.Items.Components
}
PlaySound(matchingSounds[index], item.WorldPosition);
item.CheckNeedsSoundUpdate(this);
}
}
private void PlaySound(ItemSound itemSound, Vector2 position)
@@ -401,7 +405,7 @@ namespace Barotrauma.Items.Components
float newVolume;
try
{
newVolume = property.GetFloatValue(this);
newVolume = Math.Min(property.GetFloatValue(this), 1.0f);
}
catch
{

View File

@@ -280,9 +280,9 @@ namespace Barotrauma.Items.Components
transformedItemPos += new Vector2(item.Rect.X, item.Rect.Y);
if (item.Submarine != null) { transformedItemPos += item.Submarine.DrawPosition; }
if (Math.Abs(item.Rotation) > 0.01f)
if (Math.Abs(item.RotationRad) > 0.01f)
{
Matrix transform = Matrix.CreateRotationZ(MathHelper.ToRadians(-item.Rotation));
Matrix transform = Matrix.CreateRotationZ(-item.RotationRad);
transformedItemPos = Vector2.Transform(transformedItemPos - item.DrawPosition, transform) + item.DrawPosition;
transformedItemInterval = Vector2.Transform(transformedItemInterval, transform);
transformedItemIntervalHorizontal = Vector2.Transform(transformedItemIntervalHorizontal, transform);

View File

@@ -56,9 +56,7 @@ namespace Barotrauma.Items.Components
}
else
{
Vector2 pos = item.DrawPosition;
if (item.Submarine != null) { pos -= item.Submarine.DrawPosition; }
Light.Position = pos;
Light.Position = item.Position;
}
PhysicsBody body = Light.ParentBody;
if (body != null)
@@ -68,7 +66,7 @@ namespace Barotrauma.Items.Components
}
else
{
Light.Rotation = -Rotation - MathHelper.ToRadians(item.Rotation);
Light.Rotation = -Rotation - item.RotationRad;
Light.LightSpriteEffect = item.SpriteEffects;
}
}

View File

@@ -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 && !DeconstructItemsSimultaneously)
{
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;
}

View File

@@ -108,6 +108,7 @@ namespace Barotrauma.Items.Components
itemList = new GUIListBox(new RectTransform(new Vector2(1f, 0.9f), paddedItemFrame.RectTransform), style: null)
{
PlaySoundOnSelect = true,
OnSelected = (component, userdata) =>
{
selectedItem = userdata as FabricationRecipe;

View File

@@ -333,6 +333,7 @@ namespace Barotrauma.Items.Components
GUIListBox listBox = new GUIListBox(new RectTransform(Vector2.One, searchAutoComplete.RectTransform))
{
PlaySoundOnSelect = true,
OnSelected = (component, o) =>
{
if (o is ItemPrefab prefab)
@@ -613,7 +614,7 @@ namespace Barotrauma.Items.Components
if (hullData.Distort)
{
hullData.ReceivedOxygenAmount = Rand.Range(0.0f, 100.0f);
hullData.ReceivedWaterAmount = Rand.Range(0.0f, 1.0f);
hullData.ReceivedWaterAmount = Rand.Range(0.0f, 100.0f);
}
hullData.DistortionTimer = Rand.Range(1.0f, 10.0f);
}
@@ -681,7 +682,7 @@ namespace Barotrauma.Items.Components
var sprite = GUIStyle.UIGlowSolidCircular.Value?.Sprite;
float alpha = (MathF.Sin(blipState / maxBlipState * MathHelper.TwoPi) + 1.5f) * 0.5f;
if (sprite != null)
if (sprite != null && ShowHullIntegrity)
{
Vector2 spriteSize = sprite.size;
Rectangle worldBorders = item.Submarine.GetDockedBorders();
@@ -744,11 +745,11 @@ namespace Barotrauma.Items.Components
if (key == Keys.Down)
{
listBox.SelectNext(true, autoScroll: true);
listBox.SelectNext(force: GUIListBox.Force.Yes, playSelectSound: GUIListBox.PlaySelectSound.Yes);
}
else if (key == Keys.Up)
{
listBox.SelectPrevious(true, autoScroll: true);
listBox.SelectPrevious(force: GUIListBox.Force.Yes, playSelectSound: GUIListBox.PlaySelectSound.Yes);
}
else if (key == Keys.Enter)
{
@@ -782,7 +783,7 @@ namespace Barotrauma.Items.Components
if (component.Visible && first)
{
listBox.Select(i, force: true, autoScroll: false);
listBox.Select(i, GUIListBox.Force.Yes, GUIListBox.AutoScroll.Disabled);
first = false;
}
}
@@ -1014,13 +1015,13 @@ namespace Barotrauma.Items.Components
hullData.HullWaterAmount = 0.0f;
foreach (Hull linkedHull in hullData.LinkedHulls)
{
hullData.HullWaterAmount += Math.Min(linkedHull.WaterVolume / linkedHull.Volume, 1.0f);
hullData.HullWaterAmount += WaterDetector.GetWaterPercentage(linkedHull);
}
hullData.HullWaterAmount /= hullData.LinkedHulls.Count;
}
else
{
hullData.HullWaterAmount = Math.Min(hull.WaterVolume / hull.Volume, 1.0f);
hullData.HullWaterAmount = WaterDetector.GetWaterPercentage(hull);
}
float gapOpenSum = 0.0f;
@@ -1052,8 +1053,8 @@ namespace Barotrauma.Items.Components
LocalizedString line3 = waterAmount == null ?
TextManager.Get("MiniMapWaterLevelUnavailable") :
TextManager.AddPunctuation(':', TextManager.Get("MiniMapWaterLevel"), (int)Math.Round(waterAmount.Value * 100.0f) + "%");
Color line3Color = waterAmount == null ? GUIStyle.Red : Color.Lerp(Color.LightGreen, GUIStyle.Red, (float)waterAmount);
TextManager.AddPunctuation(':', TextManager.Get("MiniMapWaterLevel"), (int)Math.Round(waterAmount.Value) + "%");
Color line3Color = waterAmount == null ? GUIStyle.Red : Color.Lerp(Color.LightGreen, GUIStyle.Red, (float)waterAmount / 100.0f);
SetTooltip(borderComponent.Rect.Center, header, line1, line2, line3, line1Color, line2Color, line3Color);
}
@@ -1188,7 +1189,8 @@ namespace Barotrauma.Items.Components
if (hullsVisible && hullData.HullWaterAmount is { } waterAmount)
{
if (!RequireWaterDetectors) { waterAmount = hull.WaterPercentage / 100.0f; }
if (!RequireWaterDetectors) { waterAmount = WaterDetector.GetWaterPercentage(hull); }
waterAmount /= 100.0f;
if (hullFrame.Rect.Height * waterAmount > 1.0f)
{
RectangleF waterRect = new RectangleF(hullFrame.Rect.X, hullFrame.Rect.Y + hullFrame.Rect.Height * (1.0f - waterAmount), hullFrame.Rect.Width, hullFrame.Rect.Height * waterAmount);
@@ -1327,7 +1329,7 @@ namespace Barotrauma.Items.Components
pos.X += inflate;
pos.Y += inflate;
sprite.Draw(spriteBatch, pos, item.SpriteColor, sprite.Origin, MathHelper.ToRadians(item.Rotation), spriteScale, item.SpriteEffects);
sprite.Draw(spriteBatch, pos, item.SpriteColor, sprite.Origin, item.RotationRad, spriteScale, item.SpriteEffects);
void DrawAdditionalSprite(Vector2 basePos, Sprite addSprite, float rotation)
{

View File

@@ -18,7 +18,7 @@ namespace Barotrauma.Items.Components
}
GuiFrame = selectionUI.GuiFrame;
selectionUI.RefreshSubmarineDisplay(true);
selectionUI.RefreshSubmarineDisplay(true, setTransferOptionToTrue: true);
IsActive = true;
return base.Select(character);
}

View File

@@ -133,7 +133,6 @@ namespace Barotrauma.Items.Components
partial void UpdateProjSpecific(float deltaTime)
{
float rotationRad = MathHelper.ToRadians(item.Rotation);
if (FlowPercentage < 0.0f)
{
foreach (var (position, emitter) in pumpOutEmitters)
@@ -142,8 +141,8 @@ namespace Barotrauma.Items.Components
//only emit "pump out" particles when underwater
Vector2 relativeParticlePos = (item.WorldRect.Location.ToVector2() + position * item.Scale) - item.WorldPosition;
relativeParticlePos = MathUtils.RotatePoint(relativeParticlePos, item.FlippedX ? rotationRad : -rotationRad);
float angle = -rotationRad;
relativeParticlePos = MathUtils.RotatePoint(relativeParticlePos, item.FlippedX ? item.RotationRad : -item.RotationRad);
float angle = -item.RotationRad;
if (item.FlippedX)
{
relativeParticlePos.X = -relativeParticlePos.X;
@@ -163,8 +162,8 @@ namespace Barotrauma.Items.Components
foreach (var (position, emitter) in pumpInEmitters)
{
Vector2 relativeParticlePos = (item.WorldRect.Location.ToVector2() + position * item.Scale) - item.WorldPosition;
relativeParticlePos = MathUtils.RotatePoint(relativeParticlePos, item.FlippedX ? rotationRad : -rotationRad);
float angle = -rotationRad;
relativeParticlePos = MathUtils.RotatePoint(relativeParticlePos, item.FlippedX ? item.RotationRad : -item.RotationRad);
float angle = -item.RotationRad;
if (item.FlippedX)
{
relativeParticlePos.X = -relativeParticlePos.X;

View File

@@ -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)

View File

@@ -927,6 +927,8 @@ namespace Barotrauma.Items.Components
bool autoPilot = msg.ReadBoolean();
bool dockingButtonClicked = msg.ReadBoolean();
ushort userID = msg.ReadUInt16();
Vector2 newSteeringInput = steeringInput;
Vector2 newTargetVelocity = targetVelocity;
float newSteeringAdjustSpeed = steeringAdjustSpeed;
@@ -935,7 +937,7 @@ namespace Barotrauma.Items.Components
if (dockingButtonClicked)
{
item.SendSignal("1", "toggle_docking");
item.SendSignal(new Signal("1", sender: Entity.FindEntityByID(userID) as Character), "toggle_docking");
}
if (autoPilot)

View File

@@ -40,8 +40,6 @@ namespace Barotrauma.Items.Components
}
}
private LightComponent lightComponent;
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
{
for (var i = 0; i < GrowableSeeds.Length; i++)

View File

@@ -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

View File

@@ -1,11 +1,10 @@
using System;
using Barotrauma.Networking;
using Barotrauma.Networking;
using Barotrauma.Particles;
using Barotrauma.Sounds;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
@@ -419,7 +418,7 @@ namespace Barotrauma.Items.Components
if (!GameMain.IsMultiplayer) { RepairBoost(qteSuccess); }
SoundPlayer.PlayUISound(qteSuccess ? GUISoundType.IncreaseQuantity : GUISoundType.DecreaseQuantity);
SoundPlayer.PlayUISound(qteSuccess ? GUISoundType.Increase : GUISoundType.Decrease);
//on failure during cooldown reset cursor to beginning
if (!qteSuccess && qteCooldown > 0.0f) { qteTimer = QteDuration; }

View File

@@ -92,6 +92,8 @@ namespace Barotrauma.Items.Components
{
if (target == null || target.Removed) { return; }
if (target.ParentInventory != null) { return; }
if (source is Limb limb && limb.Removed) { return; }
if (source is Entity e && e.Removed) { return; }
Vector2 startPos = GetSourcePos();
startPos.Y = -startPos.Y;

View File

@@ -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);

View File

@@ -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);
@@ -147,9 +147,10 @@ namespace Barotrauma.Items.Components
//because some of the wires connected to the panel may not exist yet
long msgStartPos = msg.BitPosition;
msg.ReadUInt16(); //user ID
foreach (Connection connection in Connections)
foreach (Connection _ in Connections)
{
for (int i = 0; i < connection.MaxWires; i++)
uint wireCount = msg.ReadVariableUInt32();
for (int i = 0; i < wireCount; i++)
{
msg.ReadUInt16();
}
@@ -173,9 +174,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 +195,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 +206,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);
}
}

View File

@@ -45,7 +45,7 @@ namespace Barotrauma.Items.Components
};
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), layoutGroup.RectTransform),
TextManager.Get(ciElement.Label).Fallback(ciElement.Label));
if (!ciElement.IsIntegerInput)
if (!ciElement.IsNumberInput)
{
var textBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), layoutGroup.RectTransform), ciElement.Signal, style: "GUITextBoxNoIcon")
{
@@ -77,29 +77,71 @@ namespace Barotrauma.Items.Components
}
else
{
int.TryParse(ciElement.Signal, out int signal);
var numberInput = new GUINumberInput(new RectTransform(new Vector2(0.5f, 1.0f), layoutGroup.RectTransform), GUINumberInput.NumberType.Int)
GUINumberInput numberInput = null;
if (ciElement.NumberType == NumberType.Float)
{
UserData = ciElement,
MinValueInt = ciElement.NumberInputMin,
MaxValueInt = ciElement.NumberInputMax,
IntValue = Math.Clamp(signal, ciElement.NumberInputMin, ciElement.NumberInputMax)
};
//reset size restrictions set by the Style to make sure the elements can fit the interface
numberInput.RectTransform.MinSize = numberInput.LayoutGroup.RectTransform.MinSize = new Point(0, 0);
numberInput.RectTransform.MaxSize = numberInput.LayoutGroup.RectTransform.MaxSize = new Point(int.MaxValue, int.MaxValue);
numberInput.OnValueChanged += (ni) =>
TryParseFloatInvariantCulture(ciElement.Signal, out float floatSignal);
TryParseFloatInvariantCulture(ciElement.NumberInputMin, out float numberInputMin);
TryParseFloatInvariantCulture(ciElement.NumberInputMax, out float numberInputMax);
TryParseFloatInvariantCulture(ciElement.NumberInputStep, out float numberInputStep);
numberInput = new GUINumberInput(new RectTransform(new Vector2(0.5f, 1.0f), layoutGroup.RectTransform), NumberType.Float)
{
UserData = ciElement,
MinValueFloat = numberInputMin,
MaxValueFloat = numberInputMax,
FloatValue = Math.Clamp(floatSignal, numberInputMin, numberInputMax),
DecimalsToDisplay = ciElement.NumberInputDecimalPlaces,
valueStep = numberInputStep,
OnValueChanged = (ni) =>
{
if (GameMain.Client == null)
{
ValueChanged(ni.UserData as CustomInterfaceElement, ni.FloatValue);
}
else
{
item.CreateClientEvent(this);
}
}
};
}
else if (ciElement.NumberType == NumberType.Int)
{
if (GameMain.Client == null)
int.TryParse(ciElement.Signal, out int intSignal);
int.TryParse(ciElement.NumberInputMin, out int numberInputMin);
int.TryParse(ciElement.NumberInputMax, out int numberInputMax);
TryParseFloatInvariantCulture(ciElement.NumberInputStep, out float numberInputStep);
numberInput = new GUINumberInput(new RectTransform(new Vector2(0.5f, 1.0f), layoutGroup.RectTransform), NumberType.Int)
{
ValueChanged(ni.UserData as CustomInterfaceElement, ni.IntValue);
}
else
{
item.CreateClientEvent(this);
}
};
uiElements.Add(numberInput);
UserData = ciElement,
MinValueInt = numberInputMin,
MaxValueInt = numberInputMax,
IntValue = Math.Clamp(intSignal, numberInputMin, numberInputMax),
valueStep = numberInputStep,
OnValueChanged = (ni) =>
{
if (GameMain.Client == null)
{
ValueChanged(ni.UserData as CustomInterfaceElement, ni.IntValue);
}
else
{
item.CreateClientEvent(this);
}
}
};
}
else
{
DebugConsole.ShowError($"Error creating a CustomInterface component: unexpected NumberType \"{(ciElement.NumberType.HasValue ? ciElement.NumberType.Value.ToString() : "none")}\"");
}
if (numberInput != null)
{
//reset size restrictions set by the Style to make sure the elements can fit the interface
numberInput.RectTransform.MinSize = numberInput.LayoutGroup.RectTransform.MinSize = new Point(0, 0);
numberInput.RectTransform.MaxSize = numberInput.LayoutGroup.RectTransform.MaxSize = new Point(int.MaxValue, int.MaxValue);
uiElements.Add(numberInput);
}
}
}
else if (ciElement.ContinuousSignal)
@@ -205,7 +247,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)
{
@@ -293,7 +335,7 @@ namespace Barotrauma.Items.Components
}
else if (uiElements[i] is GUINumberInput ni)
{
if (ni.InputType == GUINumberInput.NumberType.Int)
if (ni.InputType == NumberType.Int)
{
int.TryParse(customInterfaceElementList[i].Signal, out int value);
ni.IntValue = value;
@@ -307,18 +349,28 @@ namespace Barotrauma.Items.Components
//extradata contains an array of buttons clicked by the player (or nothing if the player didn't click anything)
for (int i = 0; i < customInterfaceElementList.Count; i++)
{
if (customInterfaceElementList[i].HasPropertyName)
var element = customInterfaceElementList[i];
if (element.HasPropertyName)
{
if (!customInterfaceElementList[i].IsIntegerInput)
if (!element.IsNumberInput)
{
msg.Write(((GUITextBox)uiElements[i]).Text);
}
else
{
msg.Write(((GUINumberInput)uiElements[i]).IntValue.ToString());
switch (element.NumberType)
{
case NumberType.Float:
msg.Write(((GUINumberInput)uiElements[i]).FloatValue.ToString());
break;
case NumberType.Int:
default:
msg.Write(((GUINumberInput)uiElements[i]).IntValue.ToString());
break;
}
}
}
else if (customInterfaceElementList[i].ContinuousSignal)
else if (element.ContinuousSignal)
{
msg.Write(((GUITickBox)uiElements[i]).Selected);
}
@@ -333,29 +385,38 @@ namespace Barotrauma.Items.Components
{
for (int i = 0; i < customInterfaceElementList.Count; i++)
{
if (customInterfaceElementList[i].HasPropertyName)
var element = customInterfaceElementList[i];
if (element.HasPropertyName)
{
if (!customInterfaceElementList[i].IsIntegerInput)
string newValue = msg.ReadString();
if (!element.IsNumberInput)
{
TextChanged(customInterfaceElementList[i], msg.ReadString());
TextChanged(element, newValue);
}
else
{
int.TryParse(msg.ReadString(), out int value);
ValueChanged(customInterfaceElementList[i], value);
switch (element.NumberType)
{
case NumberType.Int when int.TryParse(newValue, out int value):
ValueChanged(element, value);
break;
case NumberType.Float when TryParseFloatInvariantCulture(newValue, out float value):
ValueChanged(element, value);
break;
}
}
}
else
{
bool elementState = msg.ReadBoolean();
if (customInterfaceElementList[i].ContinuousSignal)
if (element.ContinuousSignal)
{
((GUITickBox)uiElements[i]).Selected = elementState;
TickBoxToggled(customInterfaceElementList[i], elementState);
TickBoxToggled(element, elementState);
}
else if (elementState)
{
ButtonClicked(customInterfaceElementList[i]);
ButtonClicked(element);
}
}
}

View File

@@ -58,6 +58,8 @@ namespace Barotrauma.Items.Components
return true;
}
};
layoutGroup.Recalculate();
}
// Create fillerBlock to cover historyBox so new values appear at the bottom of historyBox
@@ -100,7 +102,7 @@ namespace Barotrauma.Items.Components
GUITextBlock newBlock = new GUITextBlock(
new RectTransform(new Vector2(1, 0), historyBox.Content.RectTransform, anchor: Anchor.TopCenter),
"> " + input,
textColor: color, wrap: true, font: UseMonospaceFont ? GUIStyle.MonospacedFont : GUIStyle.GlobalFont)
textColor: color, wrap: true, font: UseMonospaceFont ? GUIStyle.MonospacedFont : GUIStyle.Font)
{
CanBeFocused = false
};

View File

@@ -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; }

View File

@@ -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();
}

View File

@@ -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);
}
}
}

View File

@@ -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;

View File

@@ -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
{
@@ -355,7 +352,7 @@ namespace Barotrauma
foreach (var decorativeSprite in Prefab.DecorativeSprites)
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier, flippedX && Prefab.CanSpriteFlipX ? rotationRad : -rotationRad) * Scale;
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier, flippedX && Prefab.CanSpriteFlipX ? RotationRad : -RotationRad) * Scale;
if (flippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; }
if (flippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; }
decorativeSprite.Sprite.DrawTiled(spriteBatch,
@@ -379,17 +376,17 @@ namespace Barotrauma
}
if (color.A > 0)
{
activeSprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, color, origin, rotationRad, Scale, activeSprite.effects, depth);
activeSprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, color, origin, RotationRad, Scale, activeSprite.effects, depth);
if (fadeInBrokenSprite != null)
{
float d = Math.Min(depth + (fadeInBrokenSprite.Sprite.Depth - activeSprite.Depth - 0.000001f), 0.999f);
fadeInBrokenSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + fadeInBrokenSprite.Offset.ToVector2() * Scale, color * fadeInBrokenSpriteAlpha, origin, rotationRad, Scale, activeSprite.effects, d);
fadeInBrokenSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + fadeInBrokenSprite.Offset.ToVector2() * Scale, color * fadeInBrokenSpriteAlpha, origin, RotationRad, Scale, activeSprite.effects, d);
}
}
if (Infector != null && (Infector.ParentBallastFlora.HasBrokenThrough || BallastFloraBehavior.AlwaysShowBallastFloraSprite))
{
Prefab.InfectedSprite?.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, color, Prefab.InfectedSprite.Origin, rotationRad, Scale, activeSprite.effects, depth - 0.001f);
Prefab.DamagedInfectedSprite?.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, Infector.HealthColor, Prefab.DamagedInfectedSprite.Origin, rotationRad, Scale, activeSprite.effects, depth - 0.002f);
Prefab.InfectedSprite?.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, color, Prefab.InfectedSprite.Origin, RotationRad, Scale, activeSprite.effects, depth - 0.001f);
Prefab.DamagedInfectedSprite?.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, Infector.HealthColor, Prefab.DamagedInfectedSprite.Origin, RotationRad, Scale, activeSprite.effects, depth - 0.002f);
}
foreach (var decorativeSprite in Prefab.DecorativeSprites)
{
@@ -397,11 +394,11 @@ namespace Barotrauma
float rot = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState, spriteAnimState[decorativeSprite].RandomRotationFactor);
bool flipX = flippedX && Prefab.CanSpriteFlipX;
bool flipY = flippedY && Prefab.CanSpriteFlipY;
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier, flipX ^ flipY ? rotationRad : -rotationRad) * Scale;
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier, flipX ^ flipY ? RotationRad : -RotationRad) * Scale;
if (flipX) { offset.X = -offset.X; }
if (flipY) { offset.Y = -offset.Y; }
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + offset.X, -(DrawPosition.Y + offset.Y)), color,
rotationRad + rot, decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, activeSprite.effects,
RotationRad + rot, decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, activeSprite.effects,
depth: Math.Min(depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth), 0.999f));
}
}
@@ -448,7 +445,7 @@ namespace Barotrauma
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState, spriteAnimState[decorativeSprite].RandomRotationFactor);
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier, -rotationRad) * Scale;
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier, -RotationRad) * Scale;
if (flippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; }
if (flippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; }
var ca = (float)Math.Cos(-body.Rotation);
@@ -469,7 +466,7 @@ namespace Barotrauma
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState, spriteAnimState[decorativeSprite].RandomRotationFactor);
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier, -rotationRad) * Scale;
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier, -RotationRad) * Scale;
if (flippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; }
if (flippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; }
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + offset.X, -(DrawPosition.Y + offset.Y)), color,
@@ -572,6 +569,18 @@ namespace Barotrauma
}
}
public void CheckNeedsSoundUpdate(ItemComponent ic)
{
if (ic.NeedsSoundUpdate())
{
if (!updateableComponents.Contains(ic))
{
updateableComponents.Add(ic);
}
isActive = true;
}
}
public void UpdateSpriteStates(float deltaTime)
{
if (activeContainedSprite != null)
@@ -720,7 +729,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), "...")
{
@@ -943,6 +952,7 @@ namespace Barotrauma
var textList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.8f), msgBox.Content.RectTransform, Anchor.TopCenter))
{
PlaySoundOnSelect = true,
OnSelected = (component, userData) =>
{
if (!(userData is Identifier)) { return true; }
@@ -1174,7 +1184,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;

View File

@@ -183,8 +183,21 @@ namespace Barotrauma.MapCreatures.Behavior
Color branchColor = (branch.IsRoot || branch.IsRootGrowth) ? RootColor : Color.White;
if (GameMain.DebugDraw)
{
if (branch.DisconnectedFromRoot && branch.ParentBranch == null)
{
branchColor = Color.Yellow;
}
else if (branch.DisconnectedFromRoot)
{
branchColor = Color.Cyan;
}
else if (branch.ParentBranch == null)
{
branchColor = Color.Magenta;
}
#if DEBUG
Vector2 basePos = Parent.WorldPosition;
foreach (var (from, to) in debugSearchLines)

View File

@@ -33,7 +33,7 @@ namespace Barotrauma
DrawArrow(FlowTargetHull, IsHorizontal ? rect.Height: rect.Width, Math.Abs(lerpedFlowForce.Length()), Color.Red * 0.3f);
}
if (outsideCollisionBlocker.Enabled && Submarine != null)
if (Submarine != null && outsideCollisionBlocker != null && outsideCollisionBlocker.Enabled)
{
var edgeShape = outsideCollisionBlocker.FixtureList[0].Shape as FarseerPhysics.Collision.Shapes.EdgeShape;
Vector2 startPos = ConvertUnits.ToDisplayUnits(outsideCollisionBlocker.GetWorldPoint(edgeShape.Vertex1)) + Submarine.Position;

View File

@@ -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))

View File

@@ -60,12 +60,6 @@ namespace Barotrauma
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
IsDisposed = true;
WallEdgeBuffer?.Dispose();
@@ -482,12 +476,6 @@ namespace Barotrauma
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
foreach (var vertexBuffer in vertexBuffers)
{

View File

@@ -230,14 +230,6 @@ namespace Barotrauma
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposing) return;
if (WaterEffect != null)
{
WaterEffect.Dispose();
@@ -250,6 +242,5 @@ namespace Barotrauma
basicEffect = null;
}
}
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -11,6 +11,18 @@ namespace Barotrauma.Lights
{
class LightManager
{
/// <summary>
/// How many light sources are allowed to recalculate their light volumes per frame.
/// Pending calculations will be done on subsequent frames, starting from the light sources that have been waiting for a recalculation the longest.
/// </summary>
const int MaxLightVolumeRecalculationsPerFrame = 5;
/// <summary>
/// If zoomed further out than this, characters no longer obstruct lights behind them.
/// Improves performance, and isn't very noticeable if we do it after zoomed far out enough.
/// </summary>
const float ObstructLightsBehindCharactersZoomThreshold = 0.5f;
public static Entity ViewTarget { get; set; }
private float currLightMapScale;
@@ -59,6 +71,8 @@ namespace Barotrauma.Lights
private Vector2 losOffset;
private int recalculationCount;
public IEnumerable<LightSource> Lights
{
get { return lights; }
@@ -151,6 +165,9 @@ namespace Barotrauma.Lights
}
private readonly List<LightSource> activeLights = new List<LightSource>(capacity: 100);
private readonly List<LightSource> activeLightsWithLightVolume = new List<LightSource>(capacity: 100);
public static int ActiveLightCount { get; private set; }
public void Update(float deltaTime)
{
@@ -180,11 +197,13 @@ namespace Barotrauma.Lights
Rectangle viewRect = cam.WorldView;
viewRect.Y -= cam.WorldView.Height;
//check which lights need to be drawn
recalculationCount = 0;
activeLights.Clear();
foreach (LightSource light in lights)
{
if (!light.Enabled) { continue; }
if ((light.Color.A < 1 || light.Range < 1.0f) && !light.LightSourceParams.OverrideLightSpriteAlpha.HasValue) { continue; }
if (light.ParentBody != null)
{
light.ParentBody.UpdateDrawPosition();
@@ -205,8 +224,44 @@ namespace Barotrauma.Lights
range = Math.Max(Math.Max(spriteRange, targetSize), range);
}
if (!MathUtils.CircleIntersectsRectangle(light.WorldPosition, range, viewRect)) { continue; }
activeLights.Add(light);
light.Priority = lightPriority(range, light);
int i = 0;
while (i < activeLights.Count && light.Priority < activeLights[i].Priority)
{
i++;
}
activeLights.Insert(i, light);
}
ActiveLightCount = activeLights.Count;
float lightPriority(float range, LightSource light)
{
return
range *
((Character.Controlled?.Submarine != null && light.ParentSub == Character.Controlled?.Submarine) ? 2.0f : 1.0f) *
(light.CastShadows ? 10.0f : 1.0f) *
(light.LightSourceParams.OverrideLightSpriteAlpha ?? (light.Color.A / 255.0f));
}
//find the lights with an active light volume
activeLightsWithLightVolume.Clear();
foreach (var activeLight in activeLights)
{
if (activeLight.Range < 1.0f || activeLight.Color.A < 1 || activeLight.CurrentBrightness <= 0.0f) { continue; }
activeLightsWithLightVolume.Add(activeLight);
}
//remove some lights with a light volume if there's too many of them
if (activeLightsWithLightVolume.Count > GameSettings.CurrentConfig.Graphics.VisibleLightLimit)
{
for (int i = GameSettings.CurrentConfig.Graphics.VisibleLightLimit; i < activeLightsWithLightVolume.Count; i++)
{
activeLights.Remove(activeLightsWithLightVolume[i]);
}
}
activeLights.Sort((l1, l2) => l1.LastRecalculationTime.CompareTo(l2.LastRecalculationTime));
//draw light sprites attached to characters
//render into a separate rendertarget using alpha blending (instead of on top of everything else with alpha blending)
@@ -235,7 +290,7 @@ namespace Barotrauma.Lights
{
if (!light.IsBackground || light.CurrentBrightness <= 0.0f) { continue; }
light.DrawSprite(spriteBatch, cam);
light.DrawLightVolume(spriteBatch, lightEffect, transform);
light.DrawLightVolume(spriteBatch, lightEffect, transform, recalculationCount < MaxLightVolumeRecalculationsPerFrame, ref recalculationCount);
}
GameMain.ParticleManager.Draw(spriteBatch, true, null, Particles.ParticleBlendState.Additive);
spriteBatch.End();
@@ -243,14 +298,6 @@ namespace Barotrauma.Lights
//draw a black rectangle on hulls to hide background lights behind subs
//---------------------------------------------------------------------------------------------------
/*if (backgroundObstructor != null)
{
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied);
spriteBatch.Draw(backgroundObstructor, new Rectangle(0, 0,
(int)(GameMain.GraphicsWidth * currLightMapScale), (int)(GameMain.GraphicsHeight * currLightMapScale)), Color.Black);
spriteBatch.End();
}*/
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, transformMatrix: spriteBatchTransform);
Dictionary<Hull, Rectangle> visibleHulls = GetVisibleHulls(cam);
foreach (KeyValuePair<Hull, Rectangle> hull in visibleHulls)
@@ -292,41 +339,44 @@ namespace Barotrauma.Lights
//draw characters to obstruct the highlighted items/characters and light sprites
//---------------------------------------------------------------------------------------------------
SolidColorEffect.CurrentTechnique = SolidColorEffect.Techniques["SolidVertexColor"];
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, effect: SolidColorEffect, transformMatrix: spriteBatchTransform);
foreach (Character character in Character.CharacterList)
if (cam.Zoom > ObstructLightsBehindCharactersZoomThreshold)
{
if (character.CurrentHull == null || !character.Enabled || !character.IsVisible) { continue; }
if (Character.Controlled?.FocusedCharacter == character) { continue; }
Color lightColor = character.CurrentHull.AmbientLight == Color.TransparentBlack ?
Color.Black :
character.CurrentHull.AmbientLight.Multiply(character.CurrentHull.AmbientLight.A / 255.0f).Opaque();
foreach (Limb limb in character.AnimController.Limbs)
SolidColorEffect.CurrentTechnique = SolidColorEffect.Techniques["SolidVertexColor"];
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, effect: SolidColorEffect, transformMatrix: spriteBatchTransform);
foreach (Character character in Character.CharacterList)
{
if (limb.DeformSprite != null) { continue; }
limb.Draw(spriteBatch, cam, lightColor);
if (character.CurrentHull == null || !character.Enabled || !character.IsVisible) { continue; }
if (Character.Controlled?.FocusedCharacter == character) { continue; }
Color lightColor = character.CurrentHull.AmbientLight == Color.TransparentBlack ?
Color.Black :
character.CurrentHull.AmbientLight.Multiply(character.CurrentHull.AmbientLight.A / 255.0f).Opaque();
foreach (Limb limb in character.AnimController.Limbs)
{
if (limb.DeformSprite != null) { continue; }
limb.Draw(spriteBatch, cam, lightColor);
}
}
}
spriteBatch.End();
spriteBatch.End();
DeformableSprite.Effect.CurrentTechnique = DeformableSprite.Effect.Techniques["DeformShaderSolidVertexColor"];
DeformableSprite.Effect.CurrentTechnique.Passes[0].Apply();
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, transformMatrix: spriteBatchTransform);
foreach (Character character in Character.CharacterList)
{
if (character.CurrentHull == null || !character.Enabled || !character.IsVisible) { continue; }
if (Character.Controlled?.FocusedCharacter == character) { continue; }
Color lightColor = character.CurrentHull.AmbientLight == Color.TransparentBlack ?
Color.Black :
character.CurrentHull.AmbientLight.Multiply(character.CurrentHull.AmbientLight.A / 255.0f).Opaque();
foreach (Limb limb in character.AnimController.Limbs)
DeformableSprite.Effect.CurrentTechnique = DeformableSprite.Effect.Techniques["DeformShaderSolidVertexColor"];
DeformableSprite.Effect.CurrentTechnique.Passes[0].Apply();
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, transformMatrix: spriteBatchTransform);
foreach (Character character in Character.CharacterList)
{
if (limb.DeformSprite == null) { continue; }
limb.Draw(spriteBatch, cam, lightColor);
if (character.CurrentHull == null || !character.Enabled || !character.IsVisible) { continue; }
if (Character.Controlled?.FocusedCharacter == character) { continue; }
Color lightColor = character.CurrentHull.AmbientLight == Color.TransparentBlack ?
Color.Black :
character.CurrentHull.AmbientLight.Multiply(character.CurrentHull.AmbientLight.A / 255.0f).Opaque();
foreach (Limb limb in character.AnimController.Limbs)
{
if (limb.DeformSprite == null) { continue; }
limb.Draw(spriteBatch, cam, lightColor);
}
}
spriteBatch.End();
}
spriteBatch.End();
DeformableSprite.Effect.CurrentTechnique = DeformableSprite.Effect.Techniques["DeformShader"];
graphics.BlendState = BlendState.Additive;
@@ -344,7 +394,7 @@ namespace Barotrauma.Lights
foreach (LightSource light in activeLights)
{
if (light.IsBackground || light.CurrentBrightness <= 0.0f) { continue; }
light.DrawLightVolume(spriteBatch, lightEffect, transform);
light.DrawLightVolume(spriteBatch, lightEffect, transform, recalculationCount < MaxLightVolumeRecalculationsPerFrame, ref recalculationCount);
}
lightEffect.World = transform;

View File

@@ -205,7 +205,7 @@ namespace Barotrauma.Lights
private VertexPositionColorTexture[] vertices;
private short[] indices;
private List<ConvexHullList> hullsInRange;
private readonly List<ConvexHullList> hullsInRange;
public Texture2D texture;
@@ -246,9 +246,9 @@ namespace Barotrauma.Lights
}
//when were the vertices of the light volume last calculated
private float lastRecalculationTime;
public float LastRecalculationTime { get; private set; }
private Dictionary<Submarine, Vector2> diffToSub;
private readonly Dictionary<Submarine, Vector2> diffToSub;
private DynamicVertexBuffer lightVolumeBuffer;
private DynamicIndexBuffer lightVolumeIndexBuffer;
@@ -376,6 +376,8 @@ namespace Barotrauma.Lights
}
}
public float Priority;
private Vector2 lightTextureTargetSize;
public Vector2 LightTextureTargetSize
@@ -423,7 +425,7 @@ namespace Barotrauma.Lights
public bool Enabled = true;
private ISerializableEntity conditionalTarget;
private readonly ISerializableEntity conditionalTarget;
private readonly PropertyConditional.Comparison comparison;
private readonly List<PropertyConditional> conditionals = new List<PropertyConditional>();
@@ -561,7 +563,7 @@ namespace Barotrauma.Lights
foreach (var ch in chList.List)
{
if (ch.LastVertexChangeTime > lastRecalculationTime && !chList.IsHidden.Contains(ch))
if (ch.LastVertexChangeTime > LastRecalculationTime && !chList.IsHidden.Contains(ch))
{
NeedsRecalculation = true;
break;
@@ -1289,7 +1291,7 @@ namespace Barotrauma.Lights
}
//visualize light recalculations
float timeSinceRecalculation = (float)Timing.TotalTime - lastRecalculationTime;
float timeSinceRecalculation = (float)Timing.TotalTime - LastRecalculationTime;
if (timeSinceRecalculation < 0.1f)
{
GUI.DrawRectangle(spriteBatch, drawPos - Vector2.One * 10, Vector2.One * 20, GUIStyle.Red * (1.0f - timeSinceRecalculation * 10.0f), isFilled: true);
@@ -1313,7 +1315,7 @@ namespace Barotrauma.Lights
}
}
public void DrawLightVolume(SpriteBatch spriteBatch, BasicEffect lightEffect, Matrix transform)
public void DrawLightVolume(SpriteBatch spriteBatch, BasicEffect lightEffect, Matrix transform, bool allowRecalculation, ref int recalculationCount)
{
if (Range < 1.0f || Color.A < 1 || CurrentBrightness <= 0.0f) { return; }
@@ -1338,8 +1340,9 @@ namespace Barotrauma.Lights
CheckHullsInRange();
if (NeedsRecalculation)
if (NeedsRecalculation && allowRecalculation)
{
recalculationCount++;
var verts = FindRaycastHits();
if (verts == null)
{
@@ -1352,7 +1355,7 @@ namespace Barotrauma.Lights
CalculateLightVertices(verts);
lastRecalculationTime = (float)Timing.TotalTime;
LastRecalculationTime = (float)Timing.TotalTime;
NeedsRecalculation = false;
}

View File

@@ -98,7 +98,7 @@ namespace Barotrauma
OnClicked = (btn, userData) =>
{
Rand.SetSyncedSeed(ToolBox.StringToInt(this.Seed));
Generate();
Generate(GameMain.GameSession.GameMode is CampaignMode campaign ? campaign.Settings : CampaignSettings.Empty);
InitProjectSpecific();
return true;
}
@@ -642,11 +642,11 @@ namespace Barotrauma
}
}
if (GameMain.DebugDraw && location == HighlightedLocation && (!location.Discovered || !location.HasOutpost()))
if (GameMain.DebugDraw)
{
if (location.Reputation != null)
Vector2 dPos = pos;
if (location == HighlightedLocation && (!location.Discovered || !location.HasOutpost()) && location.Reputation != null)
{
Vector2 dPos = pos;
dPos.Y += 48;
string name = $"Reputation: {location.Name}";
Vector2 nameSize = GUIStyle.SmallFont.MeasureString(name);
@@ -663,6 +663,8 @@ namespace Barotrauma
GUI.DrawString(spriteBatch, dPos + (new Vector2(256, 32) / 2) - (repValueSize / 2), reputationValue, Color.White, Color.Black, font: GUIStyle.SubHeadingFont);
GUI.DrawRectangle(spriteBatch, new Rectangle((int)dPos.X, (int)dPos.Y, 256, 32), Color.White);
}
dPos.Y += 48;
GUI.DrawString(spriteBatch, dPos, $"Difficulty: {location.LevelData.Difficulty.FormatZeroDecimal()}", Color.White, Color.Black * 0.8f, 4, font: GUIStyle.SmallFont);
}
}
}
@@ -977,7 +979,7 @@ namespace Barotrauma
Vector2 center = rectCenter + (connection.CenterPos + viewOffset) * zoom;
if (viewArea.Contains(center) && connection.Biome != null)
{
GUI.DrawString(spriteBatch, center, connection.Biome.Identifier + " (" + connection.Difficulty + ")", Color.White);
GUI.DrawString(spriteBatch, center, (connection.LevelData?.GenerationParams?.Identifier ?? connection.Biome.Identifier) + " (" + (int)connection.Difficulty + ")", Color.White);
}
}

View File

@@ -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);

View File

@@ -1,55 +1,61 @@
using Barotrauma.Networking;
using Barotrauma.RuinGeneration;
using Barotrauma.Sounds;
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using FarseerPhysics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using Barotrauma.IO;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Items.Components;
namespace Barotrauma
{
partial class Submarine : Entity, IServerPositionSync
{
public static Vector2 MouseToWorldGrid(Camera cam, Submarine sub)
{
Vector2 position = PlayerInput.MousePosition;
position = cam.ScreenToWorld(position);
Vector2 worldGridPos = VectorToWorldGrid(position);
if (sub != null)
{
worldGridPos.X += sub.Position.X % GridSize.X;
worldGridPos.Y += sub.Position.Y % GridSize.Y;
}
return worldGridPos;
}
//drawing ----------------------------------------------------
private static readonly HashSet<Submarine> visibleSubs = new HashSet<Submarine>();
private static double prevCullTime;
private static Rectangle prevCullArea;
/// <summary>
/// Interval at which we force culled entites to be updated, regardless if the camera has moved
/// </summary>
private const float CullInterval = 0.25f;
/// <summary>
/// Margin applied around the view area when culling entities (i.e. entities that are this far outside the view are still considered visible)
/// </summary>
private const int CullMargin = 500;
/// <summary>
/// Update entity culling when any corner of the view has moved more than this
/// </summary>
private const int CullMoveThreshold = 50;
public static void CullEntities(Camera cam)
{
Rectangle camView = cam.WorldView;
camView = new Rectangle(camView.X - CullMargin, camView.Y + CullMargin, camView.Width + CullMargin * 2, camView.Height + CullMargin * 2);
if (Math.Abs(camView.X - prevCullArea.X) < CullMoveThreshold &&
Math.Abs(camView.Y - prevCullArea.Y) < CullMoveThreshold &&
Math.Abs(camView.Right - prevCullArea.Right) < CullMoveThreshold &&
Math.Abs(camView.Bottom - prevCullArea.Bottom) < CullMoveThreshold &&
prevCullTime > Timing.TotalTime - CullInterval)
{
return;
}
visibleSubs.Clear();
foreach (Submarine sub in Loaded)
{
if (Level.Loaded != null && sub.WorldPosition.Y < Level.MaxEntityDepth) { continue; }
int margin = 500;
Rectangle worldBorders = new Rectangle(
sub.VisibleBorders.X + (int)sub.WorldPosition.X - margin,
sub.VisibleBorders.Y + (int)sub.WorldPosition.Y + margin,
sub.VisibleBorders.Width + margin * 2,
sub.VisibleBorders.Height + margin * 2);
sub.VisibleBorders.X + (int)sub.WorldPosition.X,
sub.VisibleBorders.Y + (int)sub.WorldPosition.Y,
sub.VisibleBorders.Width,
sub.VisibleBorders.Height);
if (RectsOverlap(worldBorders, cam.WorldView))
if (RectsOverlap(worldBorders, camView))
{
visibleSubs.Add(sub);
}
@@ -64,16 +70,22 @@ namespace Barotrauma
visibleEntities.Clear();
}
Rectangle worldView = cam.WorldView;
foreach (MapEntity entity in MapEntity.mapEntityList)
{
if (entity.Submarine != null)
{
if (!visibleSubs.Contains(entity.Submarine)) { continue; }
}
if (entity.IsVisible(worldView)) { visibleEntities.Add(entity); }
if (entity.IsVisible(camView)) { visibleEntities.Add(entity); }
}
prevCullArea = camView;
prevCullTime = Timing.TotalTime;
}
public static void ForceVisibilityRecheck()
{
prevCullTime = 0;
}
public static void Draw(SpriteBatch spriteBatch, bool editing = false)
@@ -148,7 +160,7 @@ namespace Barotrauma
{
if (predicate != null)
{
if (!predicate(e)) continue;
if (!predicate(e)) { continue; }
}
float drawDepth = structure.GetDrawDepth();
int i = 0;
@@ -525,7 +537,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",
@@ -679,6 +691,22 @@ namespace Barotrauma
return GameMain.LightManager.Lights.Count(l => l.CastShadows && !l.IsBackground) - disabledItemLightCount;
}
public static Vector2 MouseToWorldGrid(Camera cam, Submarine sub)
{
Vector2 position = PlayerInput.MousePosition;
position = cam.ScreenToWorld(position);
Vector2 worldGridPos = VectorToWorldGrid(position);
if (sub != null)
{
worldGridPos.X += sub.Position.X % GridSize.X;
worldGridPos.Y += sub.Position.Y % GridSize.Y;
}
return worldGridPos;
}
public void ClientReadPosition(IReadMessage msg, float sendingTime)
{
var posInfo = PhysicsBody.ClientRead(msg, sendingTime, parentDebugName: Info.Name);

View File

@@ -8,7 +8,7 @@ namespace Barotrauma
partial class SubmarineInfo : IDisposable
{
public Sprite PreviewImage;
partial void InitProjectSpecific()
{
string previewImageData = SubmarineElement.GetAttributeString("previewimage", "");
@@ -154,13 +154,13 @@ namespace Barotrauma
crewSizeText.RectTransform.MinSize = new Point(0, crewSizeText.Children.First().Rect.Height);
}
if (!string.IsNullOrEmpty(RecommendedCrewExperience))
if (RecommendedCrewExperience != CrewExperienceLevel.Unknown)
{
var crewExperienceText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), parent.Content.RectTransform),
TextManager.Get("RecommendedCrewExperience"), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), crewExperienceText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
TextManager.Get(RecommendedCrewExperience), textAlignment: Alignment.TopLeft, font: font, wrap: true)
TextManager.Get(RecommendedCrewExperience.ToIdentifier()), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
crewExperienceText.RectTransform.MinSize = new Point(0, crewExperienceText.Children.First().Rect.Height);
}

View File

@@ -16,7 +16,7 @@ namespace Barotrauma
class SubmarinePreview : IDisposable
{
private SpriteRecorder spriteRecorder;
private SubmarineInfo submarineInfo;
private readonly SubmarineInfo submarineInfo;
private Camera camera;
private Task loadTask;
private volatile bool isDisposed;
@@ -66,7 +66,7 @@ namespace Barotrauma
public static void Close()
{
instance?.Dispose();
instance?.Dispose(); instance = null;
}
private SubmarinePreview(SubmarineInfo subInfo)
@@ -100,12 +100,16 @@ namespace Barotrauma
GUIListBox specsContainer = null;
new GUICustomComponent(new RectTransform(Vector2.One, innerPadded.RectTransform, Anchor.Center),
(spriteBatch, component) => {
(spriteBatch, component) =>
{
if (isDisposed) { return; }
camera.UpdateTransform(interpolate: true, updateListener: false);
Rectangle drawRect = new Rectangle(component.Rect.X + 1, component.Rect.Y + 1, component.Rect.Width - 2, component.Rect.Height - 2);
RenderSubmarine(spriteBatch, drawRect, component);
},
(deltaTime, component) => {
(deltaTime, component) =>
{
if (isDisposed) { return; }
bool isMouseOnComponent = GUI.MouseOn == component;
camera.MoveCamera(deltaTime, allowZoom: isMouseOnComponent, followSub: false);
if (isMouseOnComponent &&
@@ -294,8 +298,8 @@ namespace Barotrauma
private void BakeMapEntity(XElement element)
{
string identifier = element.GetAttributeString("identifier", "");
if (string.IsNullOrEmpty(identifier)) { return; }
Identifier identifier = element.GetAttributeIdentifier("identifier", Identifier.Empty);
if (identifier.IsEmpty) { return; }
Rectangle rect = element.GetAttributeRect("rect", Rectangle.Empty);
if (rect.Equals(Rectangle.Empty)) { return; }
@@ -308,7 +312,16 @@ namespace Barotrauma
float rotation = element.GetAttributeFloat("rotation", 0f);
MapEntityPrefab prefab = MapEntityPrefab.List.FirstOrDefault(p => p.Identifier == identifier);
MapEntityPrefab prefab = null;
if (element.Name.ToString().Equals("item", StringComparison.OrdinalIgnoreCase) &&
ItemPrefab.Prefabs.TryGet(identifier, out ItemPrefab ip))
{
prefab = ip;
}
else
{
prefab = MapEntityPrefab.List.FirstOrDefault(p => p.Identifier == identifier);
}
if (prefab == null) { return; }
var texture = prefab.Sprite.Texture;
@@ -329,7 +342,6 @@ namespace Barotrauma
bool overrideSprite = false;
ItemPrefab itemPrefab = prefab as ItemPrefab;
StructurePrefab structurePrefab = prefab as StructurePrefab;
if (itemPrefab != null)
{
BakeItemComponents(itemPrefab, rect, color, scale, rotation, depth, out overrideSprite);
@@ -337,7 +349,7 @@ namespace Barotrauma
if (!overrideSprite)
{
if (structurePrefab != null)
if (prefab is StructurePrefab structurePrefab)
{
ParseUpgrades(structurePrefab.ConfigElement, ref scale);
@@ -655,7 +667,8 @@ namespace Barotrauma
previewFrame.RectTransform.Parent = null;
previewFrame = null;
}
spriteRecorder?.Dispose();
spriteRecorder?.Dispose(); spriteRecorder = null;
camera?.Dispose(); camera = null;
isDisposed = true;
}
}

View File

@@ -1,4 +1,5 @@
using Microsoft.Xna.Framework;
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using System;
namespace Barotrauma.Networking
@@ -9,14 +10,15 @@ namespace Barotrauma.Networking
{
msg.Write((byte)ClientNetObject.CHAT_MESSAGE);
msg.Write(NetStateID);
msg.Write((byte)Type);
msg.WriteRangedInteger((int)Type, 0, Enum.GetValues(typeof(ChatMessageType)).Length - 1);
msg.WriteRangedInteger((int)ChatMode, 0, Enum.GetValues(typeof(ChatMode)).Length - 1);
msg.Write(Text);
}
public static void ClientRead(IReadMessage msg)
{
UInt16 id = msg.ReadUInt16();
ChatMessageType type = (ChatMessageType)msg.ReadByte();
ChatMessageType type = (ChatMessageType)msg.ReadRangedInteger(0, Enum.GetValues(typeof(ChatMessageType)).Length - 1);
PlayerConnectionChangeType changeType = PlayerConnectionChangeType.None;
string txt = "";
string styleSetting = string.Empty;
@@ -183,6 +185,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;

View File

@@ -146,26 +146,19 @@ namespace Barotrauma.Networking
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (disposed) return;
if (disposing)
{
if (WriteStream != null)
{
WriteStream.Flush();
WriteStream.Close();
WriteStream.Dispose();
WriteStream = null;
}
}
disposed = true;
}
public void Dispose()
{
Dispose(true);
if (disposed) { return; }
if (WriteStream != null)
{
WriteStream.Flush();
WriteStream.Close();
WriteStream.Dispose();
WriteStream = null;
}
disposed = true;
}
}

View File

@@ -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)
@@ -668,7 +689,7 @@ namespace Barotrauma.Networking
if (ChildServerRelay.Process?.HasExited ?? true)
{
Disconnect();
if (!GUIMessageBox.MessageBoxes.Any(mb => (mb as GUIMessageBox)?.Text.Text == ChildServerRelay.CrashMessage))
if (!GUIMessageBox.MessageBoxes.Any(mb => (mb as GUIMessageBox)?.Text?.Text == ChildServerRelay.CrashMessage))
{
var msgBox = new GUIMessageBox(TextManager.Get("ConnectionLost"), ChildServerRelay.CrashMessage);
msgBox.Buttons[0].OnClicked += ReturnToPreviousMenu;
@@ -805,7 +826,11 @@ namespace Barotrauma.Networking
byte campaignID = inc.ReadByte();
UInt16 campaignSaveID = inc.ReadUInt16();
UInt16 campaignUpdateID = inc.ReadUInt16();
Dictionary<MultiPlayerCampaign.NetFlags, UInt16> campaignUpdateIDs = new Dictionary<MultiPlayerCampaign.NetFlags, ushort>();
foreach (MultiPlayerCampaign.NetFlags flag in Enum.GetValues(typeof(MultiPlayerCampaign.NetFlags)))
{
campaignUpdateIDs[flag] = inc.ReadUInt16();
}
IWriteMessage readyToStartMsg = new WriteOnlyMessage();
readyToStartMsg.Write((byte)ClientPacketHeader.RESPONSE_STARTGAME);
@@ -824,7 +849,7 @@ namespace Barotrauma.Networking
campaign != null &&
campaign.CampaignID == campaignID &&
campaign.LastSaveID == campaignSaveID &&
campaign.LastUpdateID == campaignUpdateID;
campaignUpdateIDs.All(kvp => campaign.GetLastUpdateIdForFlag(kvp.Key) == kvp.Value);
}
readyToStartMsg.Write(readyToStart);
@@ -1021,40 +1046,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);
}
}
@@ -1078,6 +1088,7 @@ namespace Barotrauma.Networking
reconnectBox?.Close();
reconnectBox = null;
GameMain.ModDownloadScreen.Reset();
ContentPackageManager.EnabledPackages.Restore();
GUI.ClearCursorWait();
@@ -1191,7 +1202,14 @@ namespace Barotrauma.Networking
new LocalizedString[] { TextManager.Get("Cancel") });
reconnectBox.Buttons[0].OnClicked += (btn, userdata) => { CancelConnect(); return true; };
connected = false;
var prevContentPackages = clientPeer.ServerContentPackages;
ConnectToServer(serverEndpoint, serverName);
if (clientPeer != null)
{
//restore the previous list of content packages so we can reconnect immediately without having to recheck that the packages match
clientPeer.ServerContentPackages = prevContentPackages;
}
}
else
{
@@ -1382,18 +1400,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);
@@ -1432,7 +1445,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)
{
@@ -1471,6 +1484,7 @@ namespace Barotrauma.Networking
}
GameMain.NetLobbyScreen.RefreshEnabledElements();
OnPermissionChanged.Invoke(new PermissionChangedEvent(permissions, this.permittedConsoleCommands));
}
private IEnumerable<CoroutineStatus> StartGame(IReadMessage inc)
@@ -1518,6 +1532,7 @@ namespace Barotrauma.Networking
serverSettings.LockAllDefaultWires = inc.ReadBoolean();
serverSettings.AllowRagdollButton = inc.ReadBoolean();
serverSettings.AllowLinkingWifiToChat = inc.ReadBoolean();
serverSettings.MaximumMoneyTransferRequest = inc.ReadInt32();
bool usingShuttle = GameMain.NetLobbyScreen.UsingShuttle = inc.ReadBoolean();
GameMain.LightManager.LosMode = (LosMode)inc.ReadByte();
bool includesFinalize = inc.ReadBoolean(); inc.ReadPadBits();
@@ -1683,6 +1698,13 @@ namespace Barotrauma.Networking
}
}
if (clientPeer == null)
{
DebugConsole.ThrowError("There was an error initializing the round (disconnected during the StartGame coroutine.)");
roundInitStatus = RoundInitStatus.Error;
yield return CoroutineStatus.Failure;
}
roundInitStatus = RoundInitStatus.WaitingForStartGameFinalize;
DateTime? timeOut = null;
@@ -1825,7 +1847,8 @@ namespace Barotrauma.Networking
GameMain.GameScreen.Select();
AddChatMessage($"ServerMessage.HowToCommunicate~[chatbutton]={GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Chat)}~[radiobutton]={GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.RadioChat)}", ChatMessageType.Server);
// TODO: Re-enable the server message once it's been edited and translated
//AddChatMessage($"ServerMessage.HowToCommunicate~[chatbutton]={GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Chat)}~[radiobutton]={GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.RadioChat)}", ChatMessageType.Server);
yield return CoroutineStatus.Success;
}
@@ -2399,7 +2422,10 @@ namespace Barotrauma.Networking
{
outmsg.Write(campaign.LastSaveID);
outmsg.Write(campaign.CampaignID);
outmsg.Write(campaign.LastUpdateID);
foreach (MultiPlayerCampaign.NetFlags netFlag in Enum.GetValues(typeof(MultiPlayerCampaign.NetFlags)))
{
outmsg.Write(campaign.GetLastUpdateIdForFlag(netFlag));
}
outmsg.Write(GameMain.NetLobbyScreen.CampaignCharacterDiscarded);
}
@@ -2444,7 +2470,10 @@ namespace Barotrauma.Networking
{
outmsg.Write(campaign.LastSaveID);
outmsg.Write(campaign.CampaignID);
outmsg.Write(campaign.LastUpdateID);
foreach (MultiPlayerCampaign.NetFlags flag in Enum.GetValues(typeof(MultiPlayerCampaign.NetFlags)))
{
outmsg.Write(campaign.GetLastUpdateIdForFlag(flag));
}
outmsg.Write(GameMain.NetLobbyScreen.CampaignCharacterDiscarded);
}
@@ -2491,6 +2520,7 @@ namespace Barotrauma.Networking
message,
type,
gameStarted && myCharacter != null ? myCharacter : null);
chatMessage.ChatMode = GameMain.ActiveChatMode;
lastQueueChatMsgID++;
chatMessage.NetStateID = lastQueueChatMsgID;
@@ -2642,7 +2672,7 @@ namespace Barotrauma.Networking
if (!(GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign) || campaign.CampaignID != campaignID)
{
string savePath = transfer.FilePath;
GameMain.GameSession = new GameSession(null, savePath, GameModePreset.MultiPlayerCampaign, CampaignSettings.Unsure);
GameMain.GameSession = new GameSession(null, savePath, GameModePreset.MultiPlayerCampaign, CampaignSettings.Empty);
campaign = (MultiPlayerCampaign)GameMain.GameSession.GameMode;
campaign.CampaignID = campaignID;
GameMain.NetLobbyScreen.ToggleCampaignMode(true);
@@ -2672,9 +2702,12 @@ namespace Barotrauma.Networking
}
DebugConsole.Log("Campaign save received (" + GameMain.GameSession.SavePath + "), save ID " + campaign.LastSaveID);
//decrement campaign update ID so the server will send us the latest data
//decrement campaign update IDs so the server will send us the latest data
//(as there may have been campaign updates after the save file was created)
campaign.LastUpdateID--;
foreach (MultiPlayerCampaign.NetFlags flag in Enum.GetValues(typeof(MultiPlayerCampaign.NetFlags)))
{
campaign.SetLastUpdateIdForFlag(flag, (ushort)(campaign.GetLastUpdateIdForFlag(flag) - 1));
}
break;
case FileTransferType.Mod:
if (!(Screen.Selected is ModDownloadScreen)) { return; }
@@ -2775,10 +2808,21 @@ namespace Barotrauma.Networking
GameMain.GameSession = null;
}
public void WriteCharacterInfo(IWriteMessage msg)
public void SendCharacterInfo(string newName = null)
{
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ClientPacketHeader.UPDATE_CHARACTERINFO);
WriteCharacterInfo(msg, newName);
msg.Write((byte)ServerNetObject.END_OF_MESSAGE);
clientPeer?.Send(msg, DeliveryMethod.Reliable);
}
public void WriteCharacterInfo(IWriteMessage msg, string newName = null)
{
msg.Write(characterInfo == null);
if (characterInfo == null) return;
if (characterInfo == null) { return; }
msg.Write(newName ?? string.Empty);
msg.Write((byte)characterInfo.Head.Preset.TagSet.Count);
foreach (Identifier tag in characterInfo.Head.Preset.TagSet)
@@ -2824,18 +2868,18 @@ namespace Barotrauma.Networking
}
#region Submarine Change Voting
public void InitiateSubmarineChange(SubmarineInfo sub, VoteType voteType)
public void InitiateSubmarineChange(SubmarineInfo sub, bool transferItems, VoteType voteType)
{
if (sub == null) { return; }
Vote(voteType, sub);
Vote(voteType, (sub, transferItems));
}
public void ShowSubmarineChangeVoteInterface(Client starter, SubmarineInfo info, VoteType type, float timeOut)
public void ShowSubmarineChangeVoteInterface(Client starter, SubmarineInfo info, VoteType type, bool transferItems, float timeOut)
{
if (info == null) { return; }
if (votingInterface != null && votingInterface.VoteRunning) { return; }
votingInterface?.Remove();
votingInterface = VotingInterface.CreateSubmarineVotingInterface(starter, info, type, timeOut);
votingInterface = VotingInterface.CreateSubmarineVotingInterface(starter, info, type, transferItems, timeOut);
}
#endregion
@@ -3017,7 +3061,7 @@ namespace Barotrauma.Networking
msg.Write(mapSeed);
msg.Write(sub.Name);
msg.Write(sub.MD5Hash.StringRepresentation);
settings.Serialize(msg);
msg.Write(settings);
clientPeer.Send(msg, DeliveryMethod.Reliable);
}
@@ -3265,10 +3309,8 @@ namespace Barotrauma.Networking
{
if (GUI.KeyboardDispatcher.Subscriber == null)
{
bool chatKeyHit = PlayerInput.KeyHit(InputType.Chat);
bool radioKeyHit = PlayerInput.KeyHit(InputType.RadioChat) && (Character.Controlled == null || Character.Controlled.SpeechImpediment < 100);
if (chatKeyHit || radioKeyHit)
var chatKeyStates = ChatBox.ChatKeyStates.GetChatKeyStates();
if (chatKeyStates.AnyHit)
{
if (msgBox.Selected)
{
@@ -3279,34 +3321,8 @@ namespace Barotrauma.Networking
{
if (Screen.Selected == GameMain.GameScreen)
{
if (chatKeyHit)
{
msgBox.AddToGUIUpdateList();
ChatBox.GUIFrame.Flash(Color.DarkGreen, 0.5f);
if (!chatBox.ToggleOpen)
{
ChatBox.CloseAfterMessageSent = !ChatBox.ToggleOpen;
ChatBox.ToggleOpen = true;
}
}
if (radioKeyHit)
{
msgBox.AddToGUIUpdateList();
ChatBox.GUIFrame.Flash(Color.YellowGreen, 0.5f);
if (!chatBox.ToggleOpen)
{
ChatBox.CloseAfterMessageSent = !ChatBox.ToggleOpen;
ChatBox.ToggleOpen = true;
}
if (!msgBox.Text.StartsWith(ChatBox.RadioChatString))
{
msgBox.Text = ChatBox.RadioChatString;
}
}
ChatBox.ApplySelectionInputs(msgBox, false, chatKeyStates);
}
msgBox.Select(msgBox.Text.Length);
}
}
@@ -3574,13 +3590,13 @@ namespace Barotrauma.Networking
return true;
};
durationInputDays = new GUINumberInput(new RectTransform(new Vector2(0.2f, 1.0f), durationContainer.RectTransform), GUINumberInput.NumberType.Int)
durationInputDays = new GUINumberInput(new RectTransform(new Vector2(0.2f, 1.0f), durationContainer.RectTransform), NumberType.Int)
{
MinValueInt = 0,
MaxValueFloat = 1000
};
new GUITextBlock(new RectTransform(new Vector2(0.2f, 1.0f), durationContainer.RectTransform), TextManager.Get("Days"));
durationInputHours = new GUINumberInput(new RectTransform(new Vector2(0.2f, 1.0f), durationContainer.RectTransform), GUINumberInput.NumberType.Int)
durationInputHours = new GUINumberInput(new RectTransform(new Vector2(0.2f, 1.0f), durationContainer.RectTransform), NumberType.Int)
{
MinValueInt = 0,
MaxValueFloat = 24
@@ -3687,7 +3703,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))

View File

@@ -126,7 +126,7 @@ namespace Barotrauma
ToolTip = TextManager.Get("Karma." + propertyName + "ToolTip")
};
var numInput = new GUINumberInput(new RectTransform(new Vector2(0.3f, 1.0f), container.RectTransform), GUINumberInput.NumberType.Int)
var numInput = new GUINumberInput(new RectTransform(new Vector2(0.3f, 1.0f), container.RectTransform), NumberType.Int)
{
MinValueInt = min,
MaxValueInt = max

View File

@@ -1,4 +1,6 @@
namespace Barotrauma.Networking
using System;
namespace Barotrauma.Networking
{
partial class OrderChatMessage : ChatMessage
{
@@ -6,7 +8,8 @@
{
msg.Write((byte)ClientNetObject.CHAT_MESSAGE);
msg.Write(NetStateID);
msg.Write((byte)ChatMessageType.Order);
msg.WriteRangedInteger((int)ChatMessageType.Order, 0, Enum.GetValues(typeof(ChatMessageType)).Length - 1);
msg.WriteRangedInteger((int)ChatMode.None, 0, Enum.GetValues(typeof(ChatMode)).Length - 1);
WriteOrder(msg);
}
}

View File

@@ -50,7 +50,7 @@ namespace Barotrauma.Networking
}
}
public ImmutableArray<ServerContentPackage> ServerContentPackages { get; private set; } =
public ImmutableArray<ServerContentPackage> ServerContentPackages { get; set; } =
ImmutableArray<ServerContentPackage>.Empty;
public delegate void MessageCallback(IReadMessage message);

Some files were not shown because too many files have changed in this diff Show More