Unstable 0.16.0.0

This commit is contained in:
Markus Isberg
2022-01-14 01:28:24 +09:00
parent d9baeaa2e1
commit 7d6421a548
237 changed files with 6430 additions and 2205 deletions

View File

@@ -1156,9 +1156,9 @@ namespace Barotrauma
}
}
partial void OnTalentGiven(string talentIdentifier)
partial void OnTalentGiven(TalentPrefab talentPrefab)
{
AddMessage(TextManager.Get("talentname." + talentIdentifier.ToString()), GUI.Style.Yellow, playSound: this == Controlled);
AddMessage(TextManager.Get("talentname." + talentPrefab.Identifier), GUI.Style.Yellow, playSound: this == Controlled);
}
}
}

View File

@@ -521,6 +521,7 @@ namespace Barotrauma
ch.SkinColor = skinColor;
ch.HairColor = hairColor;
ch.FacialHairColor = facialHairColor;
ch.SetPersonalityTrait();
if (ch.Job != null)
{
foreach (KeyValuePair<string, float> skill in skillLevels)

View File

@@ -2,12 +2,19 @@
{
partial class AfflictionHusk : Affliction
{
private InfectionState? prevDisplayedMessage;
partial void UpdateMessages()
{
if (Prefab is AfflictionPrefabHusk { SendMessages: false }) { return; }
if (prevDisplayedMessage.HasValue && prevDisplayedMessage.Value == State) { return; }
switch (State)
{
case InfectionState.Dormant:
if (Strength < DormantThreshold * 0.5f)
{
return;
}
GUI.AddMessage(TextManager.Get("HuskDormant"), GUI.Style.Red);
break;
case InfectionState.Transition:
@@ -23,6 +30,7 @@
default:
break;
}
prevDisplayedMessage = State;
}
}
}

View File

@@ -531,6 +531,8 @@ namespace Barotrauma
bloodParticleTimer -= deltaTime * (affliction.Strength / 10.0f);
if (bloodParticleTimer <= 0.0f)
{
Limb limb = targetLimb ?? Character.AnimController.MainLimb;
bool inWater = Character.AnimController.InWater;
var drawTarget = inWater ? Particles.ParticlePrefab.DrawTargetType.Water : Particles.ParticlePrefab.DrawTargetType.Air;
var emitter = Character.BloodEmitters.FirstOrDefault(e => e.Prefab.ParticlePrefab.DrawTarget == drawTarget || e.Prefab.ParticlePrefab.DrawTarget == Particles.ParticlePrefab.DrawTargetType.Both);
@@ -543,13 +545,13 @@ namespace Barotrauma
if (!inWater)
{
bloodParticleSize *= 2.0f;
velocity = targetLimb.LinearVelocity * 100.0f;
velocity = limb.LinearVelocity * 100.0f;
}
// TODO: use the blood emitter?
var blood = GameMain.ParticleManager.CreateParticle(
inWater ? Character.Params.BleedParticleWater : Character.Params.BleedParticleAir,
targetLimb.WorldPosition, velocity, 0.0f, Character.AnimController.CurrentHull);
limb.WorldPosition, velocity, 0.0f, Character.AnimController.CurrentHull);
if (blood != null && !inWater)
{
@@ -1122,23 +1124,24 @@ namespace Barotrauma
}
public static Color GetAfflictionIconColor(AfflictionPrefab prefab, Affliction affliction)
{
return GetAfflictionIconColor(prefab, affliction.Strength);
}
public static Color GetAfflictionIconColor(AfflictionPrefab prefab, float afflictionStrength)
{
// No specific colors, use generic
if (prefab.IconColors == null)
{
if (prefab.IsBuff)
{
return ToolBox.GradientLerp(affliction.Strength / prefab.MaxStrength, GUI.Style.BuffColorLow, GUI.Style.BuffColorMedium, GUI.Style.BuffColorHigh);
}
else
{
return ToolBox.GradientLerp(affliction.Strength / prefab.MaxStrength, GUI.Style.DebuffColorLow, GUI.Style.DebuffColorMedium, GUI.Style.DebuffColorHigh);
return ToolBox.GradientLerp(afflictionStrength / prefab.MaxStrength, GUI.Style.BuffColorLow, GUI.Style.BuffColorMedium, GUI.Style.BuffColorHigh);
}
return ToolBox.GradientLerp(afflictionStrength / prefab.MaxStrength, GUI.Style.DebuffColorLow, GUI.Style.DebuffColorMedium, GUI.Style.DebuffColorHigh);
}
else
{
return ToolBox.GradientLerp(affliction.Strength / prefab.MaxStrength, prefab.IconColors);
}
return ToolBox.GradientLerp(afflictionStrength / prefab.MaxStrength, prefab.IconColors);
}
public static Color GetAfflictionIconColor(Affliction affliction) => GetAfflictionIconColor(affliction.Prefab, affliction);
@@ -1153,7 +1156,7 @@ namespace Barotrauma
return;
}
if (afflictionsDirty())
if (afflictionsDirty() || selectedLimb != currentDisplayedLimb)
{
var currentAfflictions = afflictions.Where(a => ShouldDisplayAfflictionOnLimb(a, selectedLimb)).Select(a => a.Key);
CreateAfflictionInfos(currentAfflictions);

View File

@@ -1120,7 +1120,7 @@ namespace Barotrauma
return;
}
if (Submarine.MainSub.SaveAs(Barotrauma.IO.Path.Combine(SubmarineInfo.SavePath, fileName + ".sub")))
if (Submarine.MainSub.TrySaveAs(Barotrauma.IO.Path.Combine(SubmarineInfo.SavePath, fileName + ".sub")))
{
NewMessage("Sub saved", Color.Green);
}
@@ -2392,8 +2392,9 @@ namespace Barotrauma
commands.Add(new Command("querylobbies", "Queries all SteamP2P lobbies", (args) =>
{
TaskPool.Add("DebugQueryLobbies",
SteamManager.LobbyQueryRequest(), (t) => {
var lobbies = ((Task<List<Steamworks.Data.Lobby>>)t).Result;
SteamManager.LobbyQueryRequest(), (t) =>
{
t.TryGetResult(out List<Steamworks.Data.Lobby> lobbies);
foreach (var lobby in lobbies)
{
NewMessage(lobby.GetData("name") + ", " + lobby.GetData("lobbyowner"), Color.Yellow);

View File

@@ -60,7 +60,16 @@ namespace Barotrauma
ushort id = msg.ReadUInt16();
bool scanned = msg.ReadBoolean();
Entity entity = Entity.FindEntityByID(id);
scanTargets.Add(entity as WayPoint, scanned);
if (!(entity is WayPoint wayPoint))
{
string errorMsg = $"Failed to find a waypoint in ScanMission.ClientReadScanTargetStatus. Entity {id} was {(entity?.ToString() ?? null)}";
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("ScanMission.ClientReadScanTargetStatus", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
}
else
{
scanTargets.Add(wayPoint, scanned);
}
}
}
}

View File

@@ -1,8 +1,10 @@
using Microsoft.Xna.Framework;
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using SharpFont;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
@@ -130,7 +132,7 @@ namespace Barotrauma
/// <param name="charRanges">Character ranges between each even element with their corresponding odd element. Default is 0x20 to 0xFFFF.</param>
/// <param name="texDims">Texture dimensions. Default is 512x512.</param>
/// <param name="baseChar">Base character used to shift all other characters downwards when rendering. Defaults to T.</param>
public void RenderAtlas(GraphicsDevice gd, uint[] charRanges = null, int texDims = 1024, uint baseChar = 0x54)
private void RenderAtlas(GraphicsDevice gd, uint[] charRanges = null, int texDims = 1024, uint baseChar = 0x54)
{
if (DynamicLoading) { return; }
@@ -253,13 +255,19 @@ namespace Barotrauma
}
}
public void DynamicRenderAtlas(GraphicsDevice gd, uint character, int texDims = 1024, uint baseChar = 0x54)
private void DynamicRenderAtlas(GraphicsDevice gd, uint character, int texDims = 1024, uint baseChar = 0x54)
=> DynamicRenderAtlas(gd, character.ToEnumerable(), texDims, baseChar);
private void DynamicRenderAtlas(GraphicsDevice gd, string str, int texDims = 1024, uint baseChar = 0x54)
=> DynamicRenderAtlas(gd, str.Distinct().Select(c => (uint)c), texDims, baseChar);
private void DynamicRenderAtlas(GraphicsDevice gd, IEnumerable<uint> characters, int texDims = 1024, uint baseChar = 0x54)
{
if (System.Threading.Thread.CurrentThread != GameMain.MainThread)
{
CrossThread.RequestExecutionOnMainThread(() =>
{
DynamicRenderAtlas(gd, character, texDims, baseChar);
DynamicRenderAtlas(gd, characters, texDims, baseChar);
});
return;
}
@@ -271,7 +279,6 @@ namespace Barotrauma
lock (mutex)
{
if (texCoords.ContainsKey(character)) { return; }
if (textures.Count == 0)
{
this.texDims = texDims;
@@ -282,79 +289,90 @@ namespace Barotrauma
textures.Add(new Texture2D(gd, texDims, texDims, false, SurfaceFormat.Color));
}
uint glyphIndex = face.GetCharIndex(character);
if (glyphIndex == 0) { return; }
face.SetPixelSizes(0, size);
face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal);
if (face.Glyph.Metrics.Width == 0 || face.Glyph.Metrics.Height == 0)
bool anyChanges = false;
bool firstChar = true;
foreach (var character in characters)
{
if (face.Glyph.Metrics.HorizontalAdvance > 0)
if (texCoords.ContainsKey(character)) { continue; }
uint glyphIndex = face.GetCharIndex(character);
if (glyphIndex == 0) { continue; }
face.SetPixelSizes(0, size);
face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal);
if (face.Glyph.Metrics.Width == 0 || face.Glyph.Metrics.Height == 0)
{
//glyph is empty, but char still applies advance
GlyphData blankData = new GlyphData(
advance: (float)face.Glyph.Metrics.HorizontalAdvance,
texIndex: -1); //indicates no texture because the glyph is empty
texCoords.Add(character, blankData);
if (face.Glyph.Metrics.HorizontalAdvance > 0)
{
//glyph is empty, but char still applies advance
GlyphData blankData = new GlyphData(
advance: (float)face.Glyph.Metrics.HorizontalAdvance,
texIndex: -1); //indicates no texture because the glyph is empty
texCoords.Add(character, blankData);
}
continue;
}
return;
}
//stacktrace doesn't really work that well when RenderGlyph throws an exception
face.Glyph.RenderGlyph(RenderMode.Normal);
bitmap = (byte[])face.Glyph.Bitmap.BufferData.Clone();
glyphWidth = face.Glyph.Bitmap.Width;
glyphHeight = bitmap.Length / glyphWidth;
horizontalAdvance = face.Glyph.Metrics.HorizontalAdvance;
drawOffset = new Vector2(face.Glyph.BitmapLeft, baseHeight * 14 / 10 - face.Glyph.BitmapTop);
if (glyphWidth > texDims - 1 || glyphHeight > texDims - 1)
{
throw new Exception(filename + ", " + size.ToString() + ", " + (char)character + "; Glyph dimensions exceed texture atlas dimensions");
}
currentDynamicAtlasNextY = Math.Max(currentDynamicAtlasNextY, glyphHeight + 2);
if (currentDynamicAtlasCoords.X + glyphWidth + 2 > texDims - 1)
{
currentDynamicAtlasCoords.X = 0;
currentDynamicAtlasCoords.Y += currentDynamicAtlasNextY;
currentDynamicAtlasNextY = 0;
}
//no more room in current texture atlas, create a new one
if (currentDynamicAtlasCoords.Y + glyphHeight + 2 > texDims - 1)
{
currentDynamicAtlasCoords.X = 0;
currentDynamicAtlasCoords.Y = 0;
currentDynamicAtlasNextY = 0;
textures.Add(new Texture2D(gd, texDims, texDims, false, SurfaceFormat.Color));
currentDynamicPixelBuffer = null;
}
//stacktrace doesn't really work that well when RenderGlyph throws an exception
face.Glyph.RenderGlyph(RenderMode.Normal);
bitmap = (byte[])face.Glyph.Bitmap.BufferData.Clone();
glyphWidth = face.Glyph.Bitmap.Width;
glyphHeight = bitmap.Length / glyphWidth;
horizontalAdvance = face.Glyph.Metrics.HorizontalAdvance;
drawOffset = new Vector2(face.Glyph.BitmapLeft, baseHeight * 14 / 10 - face.Glyph.BitmapTop);
GlyphData newData = new GlyphData(
advance: (float)horizontalAdvance,
texIndex: textures.Count - 1,
texCoords: new Rectangle((int)currentDynamicAtlasCoords.X, (int)currentDynamicAtlasCoords.Y, glyphWidth, glyphHeight),
drawOffset: drawOffset
);
texCoords.Add(character, newData);
if (currentDynamicPixelBuffer == null)
{
currentDynamicPixelBuffer = new uint[texDims * texDims];
textures[newData.TexIndex].GetData<uint>(currentDynamicPixelBuffer, 0, texDims * texDims);
}
for (int y = 0; y < glyphHeight; y++)
{
for (int x = 0; x < glyphWidth; x++)
if (glyphWidth > texDims - 1 || glyphHeight > texDims - 1)
{
byte byteColor = bitmap[x + y * glyphWidth];
currentDynamicPixelBuffer[((int)currentDynamicAtlasCoords.X + x) + ((int)currentDynamicAtlasCoords.Y + y) * texDims] = (uint)(byteColor << 24 | 0x00ffffff);
throw new Exception(filename + ", " + size.ToString() + ", " + (char)character + "; Glyph dimensions exceed texture atlas dimensions");
}
}
textures[newData.TexIndex].SetData<uint>(currentDynamicPixelBuffer);
currentDynamicAtlasCoords.X += glyphWidth + 2;
currentDynamicAtlasNextY = Math.Max(currentDynamicAtlasNextY, glyphHeight + 2);
if (currentDynamicAtlasCoords.X + glyphWidth + 2 > texDims - 1)
{
currentDynamicAtlasCoords.X = 0;
currentDynamicAtlasCoords.Y += currentDynamicAtlasNextY;
currentDynamicAtlasNextY = 0;
}
//no more room in current texture atlas, create a new one
if (currentDynamicAtlasCoords.Y + glyphHeight + 2 > texDims - 1)
{
if (!firstChar) { textures[^1].SetData<uint>(currentDynamicPixelBuffer); }
currentDynamicAtlasCoords.X = 0;
currentDynamicAtlasCoords.Y = 0;
currentDynamicAtlasNextY = 0;
textures.Add(new Texture2D(gd, texDims, texDims, false, SurfaceFormat.Color));
currentDynamicPixelBuffer = null;
}
GlyphData newData = new GlyphData(
advance: (float)horizontalAdvance,
texIndex: textures.Count - 1,
texCoords: new Rectangle((int)currentDynamicAtlasCoords.X, (int)currentDynamicAtlasCoords.Y, glyphWidth, glyphHeight),
drawOffset: drawOffset
);
texCoords.Add(character, newData);
if (currentDynamicPixelBuffer == null)
{
currentDynamicPixelBuffer = new uint[texDims * texDims];
textures[newData.TexIndex].GetData<uint>(currentDynamicPixelBuffer, 0, texDims * texDims);
}
for (int y = 0; y < glyphHeight; y++)
{
for (int x = 0; x < glyphWidth; x++)
{
byte byteColor = bitmap[x + y * glyphWidth];
currentDynamicPixelBuffer[((int)currentDynamicAtlasCoords.X + x) + ((int)currentDynamicAtlasCoords.Y + y) * texDims] = (uint)(byteColor << 24 | 0x00ffffff);
}
}
currentDynamicAtlasCoords.X += glyphWidth + 2;
firstChar = false;
anyChanges = true;
}
if (anyChanges) { textures[^1].SetData<uint>(currentDynamicPixelBuffer); }
}
}
@@ -374,6 +392,10 @@ namespace Barotrauma
public void DrawString(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects se, float layerDepth)
{
if (textures.Count == 0 && !DynamicLoading) { return; }
if (DynamicLoading)
{
DynamicRenderAtlas(graphicsDevice, text);
}
int lineNum = 0;
Vector2 currentPos = position;
@@ -390,10 +412,6 @@ namespace Barotrauma
}
uint charIndex = text[i];
if (DynamicLoading)
{
DynamicRenderAtlas(graphicsDevice, charIndex);
}
GlyphData gd = GetGlyphData(charIndex);
if (gd.TexIndex >= 0)
@@ -417,6 +435,10 @@ namespace Barotrauma
public void DrawString(SpriteBatch sb, string text, Vector2 position, Color color)
{
if (textures.Count == 0 && !DynamicLoading) { return; }
if (DynamicLoading)
{
DynamicRenderAtlas(graphicsDevice, text);
}
Vector2 currentPos = position;
for (int i = 0; i < text.Length; i++)
@@ -429,10 +451,6 @@ namespace Barotrauma
}
uint charIndex = text[i];
if (DynamicLoading)
{
DynamicRenderAtlas(graphicsDevice, charIndex);
}
GlyphData gd = GetGlyphData(charIndex);
if (gd.TexIndex >= 0)
@@ -452,6 +470,10 @@ namespace Barotrauma
public void DrawStringWithColors(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects se, float layerDepth, List<RichTextData> richTextData, int rtdOffset = 0)
{
if (textures.Count == 0 && !DynamicLoading) { return; }
if (DynamicLoading)
{
DynamicRenderAtlas(graphicsDevice, text);
}
int lineNum = 0;
Vector2 currentPos = position;
@@ -472,10 +494,6 @@ namespace Barotrauma
}
uint charIndex = text[i];
if (DynamicLoading && !texCoords.ContainsKey(charIndex))
{
DynamicRenderAtlas(graphicsDevice, charIndex);
}
Color currentTextColor;
@@ -626,6 +644,10 @@ namespace Barotrauma
{
retVal.Y = baseHeight;
}
if (DynamicLoading)
{
DynamicRenderAtlas(graphicsDevice, text);
}
for (int i = 0; i < text.Length; i++)
{
@@ -636,10 +658,6 @@ namespace Barotrauma
continue;
}
uint charIndex = text[i];
if (DynamicLoading && !texCoords.ContainsKey(charIndex))
{
DynamicRenderAtlas(graphicsDevice, charIndex);
}
GlyphData gd = GetGlyphData(charIndex);
currentLineX += gd.Advance;

View File

@@ -44,12 +44,12 @@ namespace Barotrauma
Waiting, // Hourglass
WaitingBackground // Cursor + Hourglass
}
public static class GUI
{
public static GUICanvas Canvas => GUICanvas.Instance;
public static CursorState MouseCursor = CursorState.Default;
public static readonly SamplerState SamplerState = new SamplerState()
{
Filter = TextureFilter.Linear,
@@ -116,14 +116,14 @@ namespace Barotrauma
public static float SlicedSpriteScale
{
get
get
{
if (Math.Abs(1.0f - Scale) < 0.1f)
{
if (Math.Abs(1.0f - Scale) < 0.1f)
{
//don't scale if very close to the "reference resolution"
return 1.0f;
return 1.0f;
}
return Scale;
return Scale;
}
}
@@ -306,7 +306,7 @@ namespace Barotrauma
t = new Texture2D(GraphicsDevice, 1, 1);
t.SetData(new Color[] { Color.White });// fill the texture with white
});
SubmarineIcon = new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(452, 385, 182, 81), new Vector2(0.5f, 0.5f));
arrow = new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(393, 393, 49, 45), new Vector2(0.5f, 0.5f));
SpeechBubbleIcon = new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(385, 449, 66, 60), new Vector2(0.5f, 0.5f));
@@ -314,7 +314,7 @@ namespace Barotrauma
}
/// <summary>
/// By default, all the gui elements are drawn automatically in the same order they appear on the update list.
/// By default, all the gui elements are drawn automatically in the same order they appear on the update list.
/// </summary>
public static void Draw(Camera cam, SpriteBatch spriteBatch)
{
@@ -706,11 +706,11 @@ namespace Barotrauma
spriteBatch.Begin(SpriteSortMode.Immediate, effect: GameMain.GameScreen.PostProcessEffect);
float scale = Math.Max(
(float)GameMain.GraphicsWidth / backgroundSprite.SourceRect.Width,
(float)GameMain.GraphicsWidth / backgroundSprite.SourceRect.Width,
(float)GameMain.GraphicsHeight / backgroundSprite.SourceRect.Height) * 1.1f;
float paddingX = backgroundSprite.SourceRect.Width * scale - GameMain.GraphicsWidth;
float paddingY = backgroundSprite.SourceRect.Height * scale - GameMain.GraphicsHeight;
double noiseT = (Timing.TotalTime * 0.02f);
Vector2 pos = new Vector2((float)PerlinNoise.CalculatePerlin(noiseT, noiseT, 0) - 0.5f, (float)PerlinNoise.CalculatePerlin(noiseT, noiseT, 0.5f) - 0.5f);
pos = new Vector2(pos.X * paddingX, pos.Y * paddingY);
@@ -719,7 +719,7 @@ namespace Barotrauma
new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight) / 2 + pos,
null, Color.White, 0.0f, backgroundSprite.size / 2,
scale, SpriteEffects.None, 0.0f);
spriteBatch.End();
}
@@ -759,8 +759,8 @@ namespace Barotrauma
else
{
additions.Enqueue(component);
}
}
}
}
}
/// <summary>
@@ -786,7 +786,7 @@ namespace Barotrauma
component.Children.ForEach(c => RemoveFromUpdateList(c));
}
}
}
}
}
public static void ClearUpdateList()
@@ -900,7 +900,7 @@ namespace Barotrauma
{
GUIMessageBox.VisibleBox.AddToGUIUpdateList();
}
}
}
}
#endregion
@@ -941,7 +941,7 @@ namespace Barotrauma
inventoryIndex = updateList.IndexOf(CharacterHUD.HUDFrame);
}
if ((!PlayerInput.PrimaryMouseButtonHeld() && !PlayerInput.PrimaryMouseButtonClicked()) ||
if ((!PlayerInput.PrimaryMouseButtonHeld() && !PlayerInput.PrimaryMouseButtonClicked()) ||
(prevMouseOn == null && !PlayerInput.SecondaryMouseButtonHeld() && !Inventory.DraggingItems.Any()))
{
for (var i = updateList.Count - 1; i > inventoryIndex; i--)
@@ -967,7 +967,7 @@ namespace Barotrauma
return MouseOn;
}
}
private static CursorState UpdateMouseCursorState(GUIComponent c)
{
lock (mutex)
@@ -994,7 +994,7 @@ namespace Barotrauma
}
if (Wire.DraggingWire != null) { return CursorState.Dragging; }
}
if (c == null || c is GUICustomComponent)
{
switch (Screen.Selected)
@@ -1027,7 +1027,7 @@ namespace Barotrauma
}
}
}
if (c != null && c.Visible)
{
if (c.AlwaysOverrideCursor) { return c.HoverCursor; }
@@ -1036,20 +1036,20 @@ namespace Barotrauma
// And this is of course picked up as clickable area.
// There has to be a better way of checking this but for now this works.
var monitorRect = new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight);
var parent = FindInteractParent(c);
if (c.Enabled)
{
// Some parent elements take priority
// but not when the child is a GUIButton or GUITickBox
if (!(parent is GUIButton) && !(parent is GUIListBox) ||
if (!(parent is GUIButton) && !(parent is GUIListBox) ||
(c is GUIButton) || (c is GUITickBox))
{
if (!c.Rect.Equals(monitorRect)) { return c.HoverCursor; }
}
}
// Children in list boxes can be interacted with despite not having
// a GUIButton inside of them so instead of hard coding we check if
// the children can be interacted with by checking their hover state
@@ -1084,7 +1084,7 @@ namespace Barotrauma
{
// Health menus
if (character.CharacterHealth.MouseOnElement) { return CursorState.Hand; }
if (character.SelectedCharacter != null)
{
if (character.SelectedCharacter.CharacterHealth.MouseOnElement)
@@ -1096,7 +1096,7 @@ namespace Barotrauma
// Character is hovering over an item placed in the world
if (character.FocusedItem != null) { return CursorState.Hand; }
}
return CursorState.Default;
static GUIComponent FindInteractParent(GUIComponent component)
@@ -1130,7 +1130,7 @@ namespace Barotrauma
}
}
}
static bool ContainsMouse(GUIComponent component)
{
// If component has a mouse rectangle then use that, if not use it's physical rect
@@ -1138,7 +1138,7 @@ namespace Barotrauma
component.MouseRect.Contains(PlayerInput.MousePosition) :
component.Rect.Contains(PlayerInput.MousePosition);
}
}
}
}
/// <summary>
@@ -1153,8 +1153,8 @@ namespace Barotrauma
{
MouseCursor = CursorState.Waiting;
var timeOut = DateTime.Now + new TimeSpan(0, 0, waitSeconds);
while (DateTime.Now < timeOut)
{
while (DateTime.Now < timeOut)
{
if (endCondition != null)
{
try
@@ -1163,13 +1163,13 @@ namespace Barotrauma
}
catch { break; }
}
yield return CoroutineStatus.Running;
yield return CoroutineStatus.Running;
}
if (MouseCursor == CursorState.Waiting) { MouseCursor = CursorState.Default; }
yield return CoroutineStatus.Success;
}
}
public static void ClearCursorWait()
{
lock (mutex)
@@ -1208,7 +1208,7 @@ namespace Barotrauma
{
debugDrawMetadataOffset--;
}
if (PlayerInput.KeyHit(Keys.Down))
{
debugDrawMetadataOffset++;
@@ -1240,17 +1240,20 @@ namespace Barotrauma
debugDrawMetadataOffset = 0;
}
}
}
HandlePersistingElements(deltaTime);
RefreshUpdateList();
UpdateMouseOn();
Debug.Assert(updateList.Count == updateListSet.Count);
updateList.ForEach(c => c.UpdateAuto(deltaTime));
foreach (var c in updateList)
{
c.UpdateAuto(deltaTime);
}
UpdateMessages(deltaTime);
UpdateSavingIndicator(deltaTime);
}
}
}
private static void UpdateMessages(float deltaTime)
@@ -1281,17 +1284,16 @@ namespace Barotrauma
//only the first message (the currently visible one) is updated at a time
break;
}
foreach (GUIMessage msg in messages)
{
if (!msg.WorldSpace) { continue; }
msg.Timer -= deltaTime;
msg.Pos += msg.Velocity * deltaTime;
msg.Timer -= deltaTime;
msg.Pos += msg.Velocity * deltaTime;
}
messages.RemoveAll(m => m.Timer <= 0.0f);
}
}
private static void UpdateSavingIndicator(float deltaTime)
@@ -1628,7 +1630,7 @@ namespace Barotrauma
private static void DrawMessages(SpriteBatch spriteBatch, Camera cam)
{
if (messages.Count == 0) return;
if (messages.Count == 0) { return; }
bool useScissorRect = messages.Any(m => !m.WorldSpace);
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
@@ -1647,7 +1649,7 @@ namespace Barotrauma
msg.Font.DrawString(spriteBatch, msg.Text, drawPos + msg.DrawPos + Vector2.One, Color.Black, 0, msg.Origin, 1.0f, SpriteEffects.None, 0);
msg.Font.DrawString(spriteBatch, msg.Text, drawPos + msg.DrawPos, msg.Color, 0, msg.Origin, 1.0f, SpriteEffects.None, 0);
break;
break;
}
if (useScissorRect)
@@ -1656,11 +1658,11 @@ namespace Barotrauma
spriteBatch.GraphicsDevice.ScissorRectangle = prevScissorRect;
spriteBatch.Begin(SpriteSortMode.Deferred);
}
foreach (GUIMessage msg in messages)
{
if (!msg.WorldSpace) { continue; }
if (cam != null)
{
float alpha = 1.0f;
@@ -1669,7 +1671,7 @@ namespace Barotrauma
Vector2 drawPos = cam.WorldToScreen(msg.DrawPos);
msg.Font.DrawString(spriteBatch, msg.Text, drawPos + Vector2.One, Color.Black * alpha, 0, msg.Origin, 1.0f, SpriteEffects.None, 0);
msg.Font.DrawString(spriteBatch, msg.Text, drawPos, msg.Color * alpha, 0, msg.Origin, 1.0f, SpriteEffects.None, 0);
}
}
}
messages.RemoveAll(m => m.Timer <= 0.0f);
@@ -1770,7 +1772,7 @@ namespace Barotrauma
{
int textureWidth = Math.Max(radius * 2, 1);
int textureHeight = Math.Max(height + radius * 2, 1);
Color[] data = new Color[textureWidth * textureHeight];
// Colour the entire texture transparent first.
@@ -1880,9 +1882,9 @@ namespace Barotrauma
/// Creates multiple elements with relative size and positions them automatically.
/// </summary>
public static List<T> CreateElements<T>(int count, Vector2 relativeSize, RectTransform parent, Func<RectTransform, T> constructor,
Anchor anchor = Anchor.TopLeft, Pivot? pivot = null, Point? minSize = null, Point? maxSize = null,
int absoluteSpacing = 0, float relativeSpacing = 0, Func<int, int> extraSpacing = null,
int startOffsetAbsolute = 0, float startOffsetRelative = 0, bool isHorizontal = false)
Anchor anchor = Anchor.TopLeft, Pivot? pivot = null, Point? minSize = null, Point? maxSize = null,
int absoluteSpacing = 0, float relativeSpacing = 0, Func<int, int> extraSpacing = null,
int startOffsetAbsolute = 0, float startOffsetRelative = 0, bool isHorizontal = false)
where T : GUIComponent
{
return CreateElements(count, parent, constructor, relativeSize, null, anchor, pivot, minSize, maxSize, absoluteSpacing, relativeSpacing, extraSpacing, startOffsetAbsolute, startOffsetRelative, isHorizontal);
@@ -1891,8 +1893,8 @@ namespace Barotrauma
/// <summary>
/// Creates multiple elements with absolute size and positions them automatically.
/// </summary>
public static List<T> CreateElements<T>(int count, Point absoluteSize, RectTransform parent, Func<RectTransform, T> constructor,
Anchor anchor = Anchor.TopLeft, Pivot? pivot = null,
public static List<T> CreateElements<T>(int count, Point absoluteSize, RectTransform parent, Func<RectTransform, T> constructor,
Anchor anchor = Anchor.TopLeft, Pivot? pivot = null,
int absoluteSpacing = 0, float relativeSpacing = 0, Func<int, int> extraSpacing = null,
int startOffsetAbsolute = 0, float startOffsetRelative = 0, bool isHorizontal = false)
where T : GUIComponent
@@ -1991,7 +1993,7 @@ namespace Barotrauma
if (i == 0)
numberInput.IntValue = value.X;
else
numberInput.IntValue = value.Y;
numberInput.IntValue = value.Y;
}
return frame;
}
@@ -2028,6 +2030,16 @@ namespace Barotrauma
return frame;
}
public static void NotifyPrompt(string header, string body)
{
GUIMessageBox msgBox = new GUIMessageBox(header, body, new[] { TextManager.Get("Ok") }, new Vector2(0.2f, 0.175f), minSize: new Point(300, 175));
msgBox.Buttons[0].OnClicked = delegate
{
msgBox.Close();
return true;
};
}
public static GUIMessageBox AskForConfirmation(string header, string body, Action onConfirm, Action onDeny = null)
{
string[] buttons = { TextManager.Get("Ok"), TextManager.Get("Cancel") };
@@ -2051,6 +2063,32 @@ namespace Barotrauma
return msgBox;
}
public static GUIMessageBox PromptTextInput(string header, string body, Action<string> onConfirm)
{
string[] buttons = { TextManager.Get("Ok"), TextManager.Get("Cancel") };
GUIMessageBox msgBox = new GUIMessageBox(header, string.Empty, buttons, new Vector2(0.2f, 0.175f), minSize: new Point(300, 175));
GUITextBox textBox = new GUITextBox(new RectTransform(Vector2.One, msgBox.Content.RectTransform), text: body)
{
OverflowClip = true
};
// Cancel button
msgBox.Buttons[1].OnClicked = delegate
{
msgBox.Close();
return true;
};
// Ok button
msgBox.Buttons[0].OnClicked = delegate
{
onConfirm.Invoke(textBox.Text);
msgBox.Close();
return true;
};
return msgBox;
}
#endregion
#region Element positioning
@@ -2210,7 +2248,7 @@ namespace Barotrauma
if (disallowedAreas == null) { continue; }
foreach (Rectangle rect2 in disallowedAreas)
{
if (!rect1.Intersects(rect2)) { continue; }
if (!rect1.Intersects(rect2)) { continue; }
intersections = true;
Point centerDiff = rect1.Center - rect2.Center;
@@ -2330,8 +2368,8 @@ namespace Barotrauma
});
}
CreateButton(GameMain.GameSession.GameMode is CampaignMode ? "ReturnToServerlobby" : "EndRound", buttonContainer,
verificationTextTag: GameMain.GameSession.GameMode is CampaignMode ? "PauseMenuReturnToServerLobbyVerification" : "EndRoundSubNotAtLevelEnd",
CreateButton(GameMain.GameSession.GameMode is CampaignMode ? "ReturnToServerlobby" : "EndRound", buttonContainer,
verificationTextTag: GameMain.GameSession.GameMode is CampaignMode ? "PauseMenuReturnToServerLobbyVerification" : "EndRoundSubNotAtLevelEnd",
action: () =>
{
GameMain.Client?.RequestRoundEnd(save: false);
@@ -2448,7 +2486,10 @@ namespace Barotrauma
public static void ClearMessages()
{
messages.Clear();
lock (mutex)
{
messages.Clear();
}
}
public static bool IsFourByThree()

View File

@@ -219,12 +219,6 @@ namespace Barotrauma
GUI.Style.ButtonPulse.Draw(spriteBatch, expandRect, ToolBox.GradientLerp(pulseExpand, Color.White, Color.White, Color.Transparent));
}
if (UserData is string s && s == "ReadyCheckButton" && ReadyCheck.lastReadyCheck > DateTime.Now)
{
float progress = (ReadyCheck.lastReadyCheck - DateTime.Now).Seconds / 60.0f;
Frame.Color = ToolBox.GradientLerp(progress, Color.White, GUI.Style.Red);
}
}
protected override void Update(float deltaTime)

View File

@@ -317,7 +317,10 @@ namespace Barotrauma
set
{
selected = value;
Children.ForEach(c => c.Selected = value);
foreach (var child in Children)
{
child.Selected = value;
}
}
}
public virtual ComponentState State
@@ -537,7 +540,10 @@ namespace Barotrauma
//would be real nice to un-jank this some day
ForceUpdate();
ForceUpdate();
foreach (var child in Children) { child.ForceLayoutRecalculation(); }
foreach (var child in Children)
{
child.ForceLayoutRecalculation();
}
}
public void ForceUpdate() => Update((float)Timing.Step);
@@ -547,7 +553,10 @@ namespace Barotrauma
/// </summary>
public void UpdateChildren(float deltaTime, bool recursive)
{
RectTransform.Children.ForEach(c => c.GUIComponent.UpdateManually(deltaTime, recursive, recursive));
foreach (var child in RectTransform.Children)
{
child.GUIComponent.UpdateManually(deltaTime, recursive, recursive);
}
}
#endregion
@@ -583,7 +592,10 @@ namespace Barotrauma
/// </summary>
public virtual void DrawChildren(SpriteBatch spriteBatch, bool recursive)
{
RectTransform.Children.ForEach(c => c.GUIComponent.DrawManually(spriteBatch, recursive, recursive));
foreach (RectTransform child in RectTransform.Children)
{
child.GUIComponent.DrawManually(spriteBatch, recursive, recursive);
}
}
protected Color _currentColor;
@@ -764,8 +776,8 @@ namespace Barotrauma
{
toolTipBlock = new GUITextBlock(new RectTransform(new Point(width, height), null), richTextData, toolTip, font: GUI.SmallFont, wrap: true, style: "GUIToolTip");
toolTipBlock.RectTransform.NonScaledSize = new Point(
(int)(GUI.SmallFont.MeasureString(toolTipBlock.WrappedText).X + padding.X + toolTipBlock.Padding.X + toolTipBlock.Padding.Z),
(int)(GUI.SmallFont.MeasureString(toolTipBlock.WrappedText).Y + padding.Y + toolTipBlock.Padding.Y + toolTipBlock.Padding.W));
(int)(toolTipBlock.Font.MeasureString(toolTipBlock.WrappedText).X + padding.X + toolTipBlock.Padding.X + toolTipBlock.Padding.Z),
(int)(toolTipBlock.Font.MeasureString(toolTipBlock.WrappedText).Y + padding.Y + toolTipBlock.Padding.Y + toolTipBlock.Padding.W));
toolTipBlock.userData = toolTip;
}

View File

@@ -827,6 +827,13 @@ namespace Barotrauma
protected override void Update(float deltaTime)
{
foreach (GUIComponent child in Children)
{
if (child == ScrollBar || child == Content || child == ContentBackground) { continue; }
throw new InvalidOperationException($"Children were found in {nameof(GUIListBox)}, Add them to {nameof(GUIListBox)}.{nameof(Content)} instead.");
}
if (!Visible) { return; }
UpdateChildrenRect();
@@ -837,7 +844,6 @@ namespace Barotrauma
UpdateScrollBarSize();
}
if (FadeElements)
{
foreach (var (component, _) in childVisible)

View File

@@ -319,28 +319,29 @@ namespace Barotrauma
AbsoluteSpacing = absoluteSpacing.Y,
};
var bottomContainer = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.3f), verticalLayoutGroup.RectTransform), style: null);
var tickBoxLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.67f, 1.0f), bottomContainer.RectTransform, anchor: Anchor.CenterLeft),
isHorizontal: true, childAnchor: Anchor.CenterLeft)
var bottomContainer = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.3f), verticalLayoutGroup.RectTransform), style: null)
{
Stretch = true,
RelativeSpacing = 0.02f
CanBeFocused = true
};
var dontShowAgainTickBox = new GUITickBox(new RectTransform(new Vector2(0.5f, 1.0f), tickBoxLayoutGroup.RectTransform),
var tickBoxLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.67f, 1.0f), bottomContainer.RectTransform, anchor: Anchor.CenterLeft))
{
CanBeFocused = true,
Stretch = true
};
Vector2 tickBoxRelativeSize = new Vector2(1.0f, 0.5f);
var dontShowAgainTickBox = new GUITickBox(new RectTransform(tickBoxRelativeSize, tickBoxLayoutGroup.RectTransform),
TextManager.Get("hintmessagebox.dontshowagain"))
{
ToolTip = TextManager.Get("hintmessagebox.dontshowagaintooltip"),
UserData = "dontshowagain"
};
//var disableHintsTickBox = new GUITickBox(new RectTransform(new Vector2(0.33f, 1.0f), tickBoxLayoutGroup.RectTransform),
// TextManager.Get("hintmessagebox.disablehints"))
//{
// ToolTip = TextManager.Get("hintmessagebox.disablehintstooltip"),
// UserData = "disablehints"
//};
var disableHintsTickBox = new GUITickBox(new RectTransform(tickBoxRelativeSize, tickBoxLayoutGroup.RectTransform),
TextManager.Get("hintmessagebox.disablehints"))
{
ToolTip = TextManager.Get("hintmessagebox.disablehintstooltip"),
UserData = "disablehints"
};
Buttons = new List<GUIButton>(1)
{
@@ -379,12 +380,16 @@ namespace Barotrauma
upperContainerHeight = Math.Max(upperContainerHeight, Icon.Rect.Height);
height += upperContainerHeight;
height += absoluteSpacing.Y;
height += (int)((bottomContainer.RectTransform.RelativeSize.Y / topHorizontalLayoutGroup.RectTransform.RelativeSize.Y) * upperContainerHeight);
int bottomContainerHeight = dontShowAgainTickBox.Rect.Height + disableHintsTickBox.Rect.Height;
height += bottomContainerHeight;
height += absoluteSpacing.Y;
if (minSize.HasValue) { height = Math.Max(height, minSize.Value.Y); }
InnerFrame.RectTransform.NonScaledSize = new Point(InnerFrame.Rect.Width, height);
verticalLayoutGroup.RectTransform.NonScaledSize = GetVerticalLayoutGroupSize();
float upperContainerRelativeHeight = (float)upperContainerHeight / (upperContainerHeight + bottomContainerHeight);
topHorizontalLayoutGroup.RectTransform.RelativeSize = new Vector2(topHorizontalLayoutGroup.RectTransform.RelativeSize.X, upperContainerRelativeHeight);
bottomContainer.RectTransform.RelativeSize = new Vector2(bottomContainer.RectTransform.RelativeSize.X, 1.0f - upperContainerRelativeHeight);
verticalLayoutGroup.Recalculate();
topHorizontalLayoutGroup.Recalculate();
Content.Recalculate();

View File

@@ -29,6 +29,12 @@ namespace Barotrauma
ClampChildMouseRects(Content);
}
public override void DrawChildren(SpriteBatch spriteBatch, bool recursive)
{
//do nothing (the children have to be drawn in the Draw method after the ScissorRectangle has been set)
return;
}
protected override void Draw(SpriteBatch spriteBatch)
{
if (!Visible) { return; }

View File

@@ -204,6 +204,12 @@ namespace Barotrauma
set { textColor = value; }
}
public Color DisabledTextColor
{
get => disabledTextColor;
set => disabledTextColor = value;
}
private Color? hoverTextColor;
public Color HoverTextColor
{
@@ -303,6 +309,10 @@ namespace Barotrauma
if (parseRichText)
{
RichTextData = Barotrauma.RichTextData.GetRichTextData(text, out text);
if (RichTextData != null && RichTextData.Count == 0)
{
RichTextData = null;
}
}
//if the text is in chinese/korean/japanese and we're not using a CJK-compatible font,
@@ -457,7 +467,7 @@ namespace Barotrauma
while (size == Vector2.Zero)
{
try { size = Font.MeasureString(string.IsNullOrEmpty(text) ? " " : text); }
catch { text = text.Substring(0, text.Length - 1); }
catch { text = text.Length > 0 ? text.Substring(0, text.Length - 1) : ""; }
}
return size;

View File

@@ -70,6 +70,8 @@ namespace Barotrauma
private Vector2 selectionEndPos;
private Vector2 selectionRectSize;
private GUICustomComponent caretAndSelectionRenderer;
private bool mouseHeldInside;
private readonly Memento<string> memento = new Memento<string>();
@@ -178,8 +180,7 @@ namespace Barotrauma
}
set
{
base.ToolTip = value;
textBlock.ToolTip = value;
base.ToolTip = textBlock.ToolTip = caretAndSelectionRenderer.ToolTip = value;
}
}
@@ -268,7 +269,7 @@ namespace Barotrauma
CaretEnabled = true;
caretPosDirty = true;
new GUICustomComponent(new RectTransform(Vector2.One, frame.RectTransform), onDraw: DrawCaretAndSelection);
caretAndSelectionRenderer = new GUICustomComponent(new RectTransform(Vector2.One, frame.RectTransform), onDraw: DrawCaretAndSelection);
int clearButtonWidth = 0;
if (createClearButton)

File diff suppressed because it is too large Load Diff

View File

@@ -637,7 +637,15 @@ namespace Barotrauma
public bool IsParentOf(RectTransform rectT, bool recursive = true)
{
return children.Contains(rectT) || (recursive && children.Any(c => c.IsParentOf(rectT)));
if (children.Contains(rectT)) { return true; }
if (recursive)
{
foreach (var child in children)
{
if (child.IsParentOf(rectT)) { return true; }
}
}
return false;
}
public bool IsChildOf(RectTransform rectT, bool recursive = true)

View File

@@ -414,15 +414,8 @@ namespace Barotrauma
}
else
{
if (GameMain.Client == null)
{
subsToShow.AddRange(SubmarineInfo.SavedSubmarines.Where(s => s.IsCampaignCompatible && !GameMain.GameSession.OwnedSubmarines.Any(os => os.Name == s.Name)));
}
else
{
subsToShow.AddRange(GameMain.NetLobbyScreen.CampaignSubmarines.Where(s => !GameMain.GameSession.OwnedSubmarines.Any(os => os.Name == s.Name)));
}
subsToShow.AddRange((GameMain.Client is null ? SubmarineInfo.SavedSubmarines : MultiPlayerCampaign.GetCampaignSubs())
.Where(s => s.IsCampaignCompatible && !GameMain.GameSession.OwnedSubmarines.Any(os => os.Name == s.Name)));
subsToShow.Sort((x, y) => x.SubmarineClass.CompareTo(y.SubmarineClass));
}
@@ -446,20 +439,11 @@ namespace Barotrauma
if (preview == null)
{
SubmarineInfo potentialMatch;
if (GameMain.Client == null)
{
potentialMatch = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.EqualityCheckVal == info.EqualityCheckVal);
}
else
{
potentialMatch = GameMain.NetLobbyScreen.CampaignSubmarines.FirstOrDefault(s => s.EqualityCheckVal == info.EqualityCheckVal);
}
SubmarineInfo potentialMatch = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.EqualityCheckVal == info.EqualityCheckVal);
preview = potentialMatch?.PreviewImage;
// Try from savedsubmarines with name comparison as a backup
// Try name comparison as a backup
if (preview == null)
{
potentialMatch = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == info.Name);

View File

@@ -1543,30 +1543,12 @@ namespace Barotrauma
GUITextBlock.AutoScaleAndNormalize(skillNames);
}
private bool HasUnlockedAllTalents(Character controlledCharacter)
{
if (TalentTree.JobTalentTrees.TryGetValue(controlledCharacter.Info.Job.Prefab.Identifier, out TalentTree talentTree))
{
foreach (TalentSubTree talentSubTree in talentTree.TalentSubTrees)
{
foreach (TalentOption talentOption in talentSubTree.TalentOptionStages)
{
if (talentOption.Talents.None(t => controlledCharacter.HasTalent(t.Identifier)))
{
return false;
}
}
}
}
return true;
}
private void UpdateTalentButtons()
{
Character controlledCharacter = Character.Controlled;
if (controlledCharacter?.Info == null) { return; }
bool unlockedAllTalents = HasUnlockedAllTalents(controlledCharacter);
bool unlockedAllTalents = controlledCharacter.HasUnlockedAllTalents();
if (unlockedAllTalents)
{

View File

@@ -16,7 +16,6 @@ using Microsoft.Xna.Framework.Input;
namespace Barotrauma
{
internal class UpgradeStore
{
public readonly struct CategoryData
@@ -1688,7 +1687,7 @@ namespace Barotrauma
private bool HasPermission => campaignUI.Campaign.AllowedToManageCampaign();
private static string FormatCurrency(int money, bool format = true)
public static string FormatCurrency(int money, bool format = true)
{
return TextManager.GetWithVariable("CurrencyFormat", "[credits]", format ? string.Format(CultureInfo.InvariantCulture, "{0:N0}", money) : money.ToString());
}

View File

@@ -437,8 +437,8 @@ namespace Barotrauma
TaskPool.Add("AutoUpdateWorkshopItemsAsync",
SteamManager.AutoUpdateWorkshopItemsAsync(), (task) =>
{
bool result = ((Task<bool>)task).Result;
if (!task.TryGetResult(out bool result)) { return; }
Config.WaitingForAutoUpdate = false;
});
@@ -756,7 +756,10 @@ namespace Barotrauma
}
#if DEBUG
CancelQuickStart |= PlayerInput.KeyDown(Keys.LeftShift);
if (PlayerInput.KeyHit(Keys.LeftShift))
{
CancelQuickStart = !CancelQuickStart;
}
if (TitleScreen.LoadState >= 100.0f && !TitleScreen.PlayingSplashScreen && (Config.AutomaticQuickStartEnabled || Config.AutomaticCampaignLoadEnabled || Config.TestScreenEnabled) && FirstLoad && !CancelQuickStart)
{

View File

@@ -718,7 +718,7 @@ namespace Barotrauma
/// Sets the character's current order (if it's close enough to receive messages from orderGiver) and
/// displays the order in the crew UI
/// </summary>
public void SetCharacterOrder(Character character, Order order, string option, int priority, Character orderGiver, Hull targetHull = null)
public void SetCharacterOrder(Character character, Order order, string option, int priority, Character orderGiver, Hull targetHull = null, bool isNewOrder = true)
{
if (order != null && order.TargetAllCharacters)
{
@@ -768,7 +768,7 @@ namespace Barotrauma
if (IsSinglePlayer)
{
orderGiver.Speak(order.GetChatMessage("", hull?.DisplayName, givingOrderToSelf: character == orderGiver), ChatMessageType.Order);
orderGiver.Speak(order.GetChatMessage("", hull?.DisplayName, givingOrderToSelf: character == orderGiver, isNewOrder: isNewOrder), ChatMessageType.Order);
}
else
{
@@ -784,7 +784,7 @@ namespace Barotrauma
if (IsSinglePlayer)
{
character.SetOrder(order, option, priority, orderGiver, speak: orderGiver != character);
string message = order?.GetChatMessage(character.Name, orderGiver?.CurrentHull?.DisplayName, givingOrderToSelf: character == orderGiver, orderOption: option, priority: priority);
string message = order?.GetChatMessage(character.Name, orderGiver?.CurrentHull?.DisplayName, givingOrderToSelf: character == orderGiver, orderOption: option, isNewOrder: isNewOrder);
orderGiver?.Speak(message);
}
else if (orderGiver != null)
@@ -1071,7 +1071,7 @@ namespace Barotrauma
var priority = Math.Max(CharacterInfo.HighestManualOrderPriority - orderList.Content.GetChildIndex(orderComponent), 1);
if (orderInfo.ManualPriority == priority) { return; }
var character = (Character)orderList.UserData;
SetCharacterOrder(character, orderInfo.Order, orderInfo.OrderOption, priority, Character.Controlled);
SetCharacterOrder(character, orderInfo.Order, orderInfo.OrderOption, priority, Character.Controlled, isNewOrder: false);
}
private string CreateOrderTooltip(Order orderPrefab, string option, Entity targetEntity)
@@ -2582,15 +2582,12 @@ namespace Barotrauma
{
contextualOrders.Remove(pumpOrderInfo);
}
if (contextualOrders.None())
orderIdentifier = "cleanupitems";
if (contextualOrders.None(info => info.Order.Identifier.Equals(orderIdentifier)))
{
orderIdentifier = "cleanupitems";
if (contextualOrders.None(info => info.Order.Identifier.Equals(orderIdentifier)))
if (AIObjectiveCleanupItems.IsValidTarget(itemContext, Character.Controlled, checkInventory: false) || AIObjectiveCleanupItems.IsValidContainer(itemContext, Character.Controlled))
{
if (AIObjectiveCleanupItems.IsValidTarget(itemContext, Character.Controlled, checkInventory: false) || AIObjectiveCleanupItems.IsValidContainer(itemContext, Character.Controlled))
{
contextualOrders.Add(new OrderInfo(new Order(Order.GetPrefab(orderIdentifier), itemContext, targetItem: null, Character.Controlled), null));
}
contextualOrders.Add(new OrderInfo(new Order(Order.GetPrefab(orderIdentifier), itemContext, targetItem: null, Character.Controlled), null));
}
}
AddIgnoreOrder(itemContext);
@@ -3523,16 +3520,6 @@ namespace Barotrauma
InitRound();
}
public void EndRound()
{
//remove characterinfos whose characters have been removed or killed
characterInfos.RemoveAll(c => c.Character == null || c.Character.Removed || c.CauseOfDeath != null);
characters.Clear();
crewList.ClearChildren();
GUIContextMenu.CurrentContextMenu = null;
}
public void Reset()
{
characters.Clear();

View File

@@ -242,6 +242,11 @@ namespace Barotrauma
{
ReadyCheckButton.RectTransform.ScreenSpaceOffset = endRoundButton.RectTransform.ScreenSpaceOffset;
ReadyCheckButton.DrawManually(spriteBatch);
if (ReadyCheck.ReadyCheckCooldown > DateTime.Now)
{
float progress = (ReadyCheck.ReadyCheckCooldown - DateTime.Now).Seconds / 60.0f;
ReadyCheckButton.Color = ToolBox.GradientLerp(progress, Color.White, GUI.Style.Red);
}
}
}
@@ -290,6 +295,9 @@ namespace Barotrauma
case InteractionType.Crew when GameMain.NetworkMember != null:
CampaignUI.CrewManagement.SendCrewState(false);
goto default;
case InteractionType.MedicalClinic:
CampaignUI.MedicalClinic.RequestLatestPending();
goto default;
default:
ShowCampaignUI = true;
CampaignUI.SelectTab(npc.CampaignInteractionType);
@@ -319,6 +327,8 @@ namespace Barotrauma
{
base.Update(deltaTime);
MedicalClinic?.Update(deltaTime);
if (PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape))
{
GUIMessageBox.MessageBoxes.RemoveAll(mb => mb.UserData is RoundSummary);

View File

@@ -172,7 +172,7 @@ namespace Barotrauma
}
else
{
indicator.Visible = Character.Controlled.Info.GetAvailableTalentPoints() > 0;
indicator.Visible = Character.Controlled.Info.GetAvailableTalentPoints() > 0 && !Character.Controlled.HasUnlockedAllTalents();
}
}
}

View File

@@ -0,0 +1,386 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Barotrauma.Extensions;
using Barotrauma.Networking;
namespace Barotrauma
{
internal partial class MedicalClinic
{
public enum RequestResult
{
Undecided,
Success,
Error,
Timeout
}
public readonly struct RequestAction<T>
{
public readonly Action<T> Callback;
public readonly DateTimeOffset Timeout;
public RequestAction(Action<T> callback, DateTimeOffset timeout)
{
Callback = callback;
Timeout = timeout;
}
}
public readonly struct AfflictionRequest
{
public readonly RequestResult Result;
public readonly ImmutableArray<NetAffliction> Afflictions;
public AfflictionRequest(RequestResult result, ImmutableArray<NetAffliction> afflictions)
{
Result = result;
Afflictions = afflictions;
}
}
public readonly struct PendingRequest
{
public readonly RequestResult Result;
public readonly ImmutableArray<NetCrewMember> CrewMembers;
public PendingRequest(RequestResult result, ImmutableArray<NetCrewMember> crewMembers)
{
Result = result;
CrewMembers = crewMembers;
}
}
public readonly struct CallbackOnlyRequest
{
public readonly RequestResult Result;
public CallbackOnlyRequest(RequestResult result)
{
Result = result;
}
}
public readonly struct HealRequest
{
public readonly RequestResult Result;
public readonly HealRequestResult HealResult;
public HealRequest(RequestResult result, HealRequestResult healResult)
{
Result = result;
HealResult = healResult;
}
}
private readonly List<RequestAction<AfflictionRequest>> afflictionRequests = new List<RequestAction<AfflictionRequest>>();
private readonly List<RequestAction<PendingRequest>> pendingHealRequests = new List<RequestAction<PendingRequest>>();
private readonly List<RequestAction<CallbackOnlyRequest>> clearAllRequests = new List<RequestAction<CallbackOnlyRequest>>();
private readonly List<RequestAction<HealRequest>> healAllRequests = new List<RequestAction<HealRequest>>();
private readonly List<RequestAction<CallbackOnlyRequest>> addRequests = new List<RequestAction<CallbackOnlyRequest>>();
private readonly List<RequestAction<CallbackOnlyRequest>> removeRequests = new List<RequestAction<CallbackOnlyRequest>>();
public void RequestAfflictions(CharacterInfo info, Action<AfflictionRequest> onReceived)
{
if (GameMain.IsSingleplayer)
{
#if DEBUG && LINUX
if (Screen.Selected is TestScreen)
{
onReceived.Invoke(new AfflictionRequest(RequestResult.Success, TestAfflictions.ToImmutableArray()));
return;
}
#endif
if (!(info is { Character: { CharacterHealth: { } health } }))
{
onReceived.Invoke(new AfflictionRequest(RequestResult.Error, ImmutableArray<NetAffliction>.Empty));
return;
}
ImmutableArray<NetAffliction> pendingAfflictions = GetAllAfflictions(health).ToImmutableArray();
onReceived.Invoke(new AfflictionRequest(RequestResult.Success, pendingAfflictions));
return;
}
afflictionRequests.Add(new RequestAction<AfflictionRequest>(onReceived, GetTimeout()));
SendAfflictionRequest(info);
}
public void RequestLatestPending(Action<PendingRequest> onReceived)
{
// no need to worry about syncing when there's only one pair of eyes capable of looking at the UI
if (GameMain.IsSingleplayer) { return; }
pendingHealRequests.Add(new RequestAction<PendingRequest>(onReceived, GetTimeout()));
SendPendingRequest();
}
public void Update(float deltaTime)
{
DateTimeOffset now = DateTimeOffset.Now;
UpdateQueue(afflictionRequests, now, onTimeout: callback => { callback(new AfflictionRequest(RequestResult.Timeout, ImmutableArray<NetAffliction>.Empty)); });
UpdateQueue(pendingHealRequests, now, onTimeout: callback => { callback(new PendingRequest(RequestResult.Timeout, ImmutableArray<NetCrewMember>.Empty)); });
UpdateQueue(healAllRequests, now, onTimeout: callback => { callback(new HealRequest(RequestResult.Timeout, HealRequestResult.Unknown)); });
UpdateQueue(clearAllRequests, now, onTimeout: CallbackOnlyTimeout);
UpdateQueue(addRequests, now, onTimeout: CallbackOnlyTimeout);
UpdateQueue(removeRequests, now, onTimeout: CallbackOnlyTimeout);
void CallbackOnlyTimeout(Action<CallbackOnlyRequest> callback) { callback(new CallbackOnlyRequest(RequestResult.Timeout)); }
}
public bool IsAfflictionPending(NetCrewMember character, NetAffliction affliction)
{
foreach (NetCrewMember crewMember in PendingHeals)
{
if (!crewMember.CharacterEquals(character)) { continue; }
return crewMember.Afflictions.Any(a => a.AfflictionEquals(affliction));
}
return false;
}
private static bool TryDequeue<T>(List<RequestAction<T>> requestQueue, out Action<T> result)
{
RequestAction<T>? first = requestQueue.FirstOrNull();
if (!(first is { } action))
{
result = _ => { };
return false;
}
requestQueue.Remove(action);
result = action.Callback;
return true;
}
private static void UpdateQueue<T>(List<RequestAction<T>> requestQueue, DateTimeOffset now, Action<Action<T>> onTimeout)
{
HashSet<RequestAction<T>>? removals = null;
foreach (RequestAction<T> action in requestQueue)
{
if (action.Timeout < now)
{
onTimeout.Invoke(action.Callback);
removals ??= new HashSet<RequestAction<T>>();
removals.Add(action);
}
}
if (removals is null) { return; }
foreach (RequestAction<T> action in removals)
{
requestQueue.Remove(action);
}
}
// if you have more than 5000 ping there are probably more important things to worry about but hey just in case
private static DateTimeOffset GetTimeout() => DateTimeOffset.Now.AddSeconds(5).AddMilliseconds(GetPing());
private static int GetPing()
{
if (GameMain.IsSingleplayer || !(GameMain.Client?.Name is { } ownName) || !(GameMain.NetworkMember?.ConnectedClients is { } clients)) { return 0; }
return (from client in clients where client.Name == ownName select client.Ping).FirstOrDefault();
}
public void HealAllButtonAction(Action<HealRequest> onReceived)
{
if (GameMain.IsSingleplayer)
{
HealRequestResult result = HealAllPending();
onReceived(new HealRequest(RequestResult.Success, HealAllPending()));
if (result == HealRequestResult.Success)
{
OnUpdate?.Invoke();
}
return;
}
healAllRequests.Add(new RequestAction<HealRequest>(onReceived, GetTimeout()));
ClientSend(null, NetworkHeader.HEAL_PENDING, DeliveryMethod.Reliable);
}
public void ClearAllButtonAction(Action<CallbackOnlyRequest> onReceived)
{
if (GameMain.IsSingleplayer)
{
ClearPendingHeals();
onReceived(new CallbackOnlyRequest(RequestResult.Success));
OnUpdate?.Invoke();
return;
}
clearAllRequests.Add(new RequestAction<CallbackOnlyRequest>(onReceived, GetTimeout()));
ClientSend(null, NetworkHeader.CLEAR_PENDING, DeliveryMethod.Reliable);
}
private void ClearRequstReceived()
{
ClearPendingHeals();
if (TryDequeue(clearAllRequests, out var callback))
{
callback(new CallbackOnlyRequest(RequestResult.Success));
}
OnUpdate?.Invoke();
}
private void HealRequestReceived(IReadMessage inc)
{
NetHealRequest request = INetSerializableStruct.Read<NetHealRequest>(inc);
if (request.Result == HealRequestResult.Success)
{
HealAllPending(force: true);
}
if (TryDequeue(healAllRequests, out var callback))
{
callback(new HealRequest(RequestResult.Success, request.Result));
}
OnUpdate?.Invoke();
}
public void AddPendingButtonAction(NetCrewMember crewMember, Action<CallbackOnlyRequest> onReceived)
{
if (GameMain.IsSingleplayer)
{
InsertPendingCrewMember(crewMember);
onReceived(new CallbackOnlyRequest(RequestResult.Success));
OnUpdate?.Invoke();
return;
}
addRequests.Add(new RequestAction<CallbackOnlyRequest>(onReceived, GetTimeout()));
ClientSend(crewMember, NetworkHeader.ADD_PENDING, DeliveryMethod.Reliable);
}
public void RemovePendingButtonAction(NetCrewMember crewMember, NetAffliction affliction, Action<CallbackOnlyRequest> onReceived)
{
if (GameMain.IsSingleplayer)
{
RemovePendingAffliction(crewMember, affliction);
onReceived(new CallbackOnlyRequest(RequestResult.Success));
OnUpdate?.Invoke();
return;
}
INetSerializableStruct removedAffliction = new NetRemovedAffliction
{
CrewMember = crewMember,
Affliction = affliction
};
removeRequests.Add(new RequestAction<CallbackOnlyRequest>(onReceived, GetTimeout()));
ClientSend(removedAffliction, NetworkHeader.REMOVE_PENDING, DeliveryMethod.Reliable);
}
private void NewAdditonReceived(IReadMessage inc, MessageFlag flag)
{
NetCrewMember crewMember = INetSerializableStruct.Read<NetCrewMember>(inc);
InsertPendingCrewMember(crewMember);
if (flag == MessageFlag.Response && TryDequeue(addRequests, out var callback))
{
callback(new CallbackOnlyRequest(RequestResult.Success));
}
OnUpdate?.Invoke();
}
private void NewRemovalReceived(IReadMessage inc, MessageFlag flag)
{
NetRemovedAffliction removed = INetSerializableStruct.Read<NetRemovedAffliction>(inc);
RemovePendingAffliction(removed.CrewMember, removed.Affliction);
if (flag == MessageFlag.Response && TryDequeue(removeRequests, out var callback))
{
callback(new CallbackOnlyRequest(RequestResult.Success));
}
OnUpdate?.Invoke();
}
private static void SendAfflictionRequest(CharacterInfo info)
{
INetSerializableStruct crewMember = new NetCrewMember
{
CharacterInfo = info,
Afflictions = Array.Empty<NetAffliction>()
};
ClientSend(crewMember, NetworkHeader.REQUEST_AFFLICTIONS, DeliveryMethod.Unreliable);
}
private static void SendPendingRequest()
{
ClientSend(null, NetworkHeader.REQUEST_PENDING, DeliveryMethod.Reliable);
}
private void AfflictionRequestReceived(IReadMessage inc)
{
NetCrewMember crewMember = INetSerializableStruct.Read<NetCrewMember>(inc);
if (TryDequeue(afflictionRequests, out var callback))
{
RequestResult result = crewMember.CharacterInfoID == 0 ? RequestResult.Error : RequestResult.Success;
callback(new AfflictionRequest(result, crewMember.Afflictions.ToImmutableArray()));
}
}
private void PendingRequestReceived(IReadMessage inc)
{
NetPendingCrew pendingCrew = INetSerializableStruct.Read<NetPendingCrew>(inc);
if (TryDequeue(pendingHealRequests, out var callback))
{
callback(new PendingRequest(RequestResult.Success, pendingCrew.CrewMembers.ToImmutableArray()));
}
}
private static IWriteMessage StartSending()
{
IWriteMessage writeMessage = new WriteOnlyMessage();
writeMessage.Write((byte)ClientPacketHeader.MEDICAL);
return writeMessage;
}
private static void ClientSend(INetSerializableStruct? netStruct, NetworkHeader header, DeliveryMethod deliveryMethod)
{
IWriteMessage msg = StartSending();
msg.Write((byte)header);
netStruct?.Write(msg);
GameMain.Client.ClientPeer?.Send(msg, deliveryMethod);
}
public void ClientRead(IReadMessage inc)
{
NetworkHeader header = (NetworkHeader)inc.ReadByte();
MessageFlag flag = (MessageFlag)inc.ReadByte();
switch (header)
{
case NetworkHeader.REQUEST_AFFLICTIONS:
AfflictionRequestReceived(inc);
break;
case NetworkHeader.REQUEST_PENDING:
PendingRequestReceived(inc);
break;
case NetworkHeader.ADD_PENDING:
NewAdditonReceived(inc, flag);
break;
case NetworkHeader.REMOVE_PENDING:
NewRemovalReceived(inc, flag);
break;
case NetworkHeader.HEAL_PENDING:
HealRequestReceived(inc);
break;
case NetworkHeader.CLEAR_PENDING:
ClearRequstReceived();
break;
}
}
}
}

View File

@@ -31,7 +31,7 @@ namespace Barotrauma
private GUIMessageBox? msgBox;
private GUIMessageBox? resultsBox;
public static DateTime lastReadyCheck = DateTime.MinValue;
public static DateTime ReadyCheckCooldown = DateTime.MinValue;
public static bool IsReadyCheck(GUIComponent? msgBox) => msgBox?.UserData as string == PromptData || msgBox?.UserData as string == ResultData;
@@ -273,10 +273,10 @@ namespace Barotrauma
public static void CreateReadyCheck()
{
if (lastReadyCheck < DateTime.Now)
if (ReadyCheckCooldown < DateTime.Now)
{
#if !DEBUG
lastReadyCheck = DateTime.Now.AddMinutes(1);
ReadyCheckCooldown = DateTime.Now.AddMinutes(1);
#endif
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte) ClientPacketHeader.READY_CHECK);
@@ -285,7 +285,7 @@ namespace Barotrauma
return;
}
GUIMessageBox msgBox = new GUIMessageBox(readyCheckHeader, readyCheckPleaseWait((lastReadyCheck - DateTime.Now).Seconds), new[] { closeButton });
GUIMessageBox msgBox = new GUIMessageBox(readyCheckHeader, readyCheckPleaseWait((ReadyCheckCooldown - DateTime.Now).Seconds), new[] { closeButton });
msgBox.Buttons[0].OnClicked = delegate
{
msgBox.Close();

View File

@@ -524,6 +524,7 @@ namespace Barotrauma
return true;
};
#if !OSX
var statisticsTickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.045f), leftPanel.RectTransform), TextManager.Get("statisticsconsenttickbox"))
{
OnSelected = (GUITickBox tickBox) =>
@@ -562,6 +563,8 @@ namespace Barotrauma
statisticsTickBox.OnSelected = prevHandler;
statisticsTickBox.Enabled = GameAnalyticsManager.UserConsented != GameAnalyticsManager.Consent.Error;
});
#endif
// right panel --------------------------------------

View File

@@ -504,6 +504,13 @@ namespace Barotrauma
{
HUDLayoutSettings.InventoryTopY = visualSlots[0].EquipButtonRect.Y - (int)(15 * GUI.Scale);
}
else
{
for (int i = 0; i < capacity; i++)
{
visualSlots[i].DrawOffset = Vector2.Zero;
}
}
}
protected override void ControlInput(Camera cam)

View File

@@ -28,8 +28,8 @@ namespace Barotrauma.Items.Components
}
case AreaShape.Circle:
Vector2 center = item.WorldPosition;
center.Y = -center.Y;
center += SpawnAreaOffset;
center.Y = -center.Y;
spriteBatch.DrawCircle(center, SpawnAreaRadius, 32, GUI.Style.Red, thickness: 4f);
if (MaximumAmountRangePadding > 0f)
@@ -51,8 +51,8 @@ namespace Barotrauma.Items.Components
}
case AreaShape.Circle:
Vector2 center = item.WorldPosition;
center.Y = -center.Y;
center += CrewAreaOffset;
center.Y = -center.Y;
spriteBatch.DrawCircle(center, CrewAreaRadius, 32, GUI.Style.Green);
break;
}

View File

@@ -390,10 +390,10 @@ namespace Barotrauma.Items.Components
if (SerializableProperties.TryGetValue(sound.VolumeProperty, out SerializableProperty property))
{
float newVolume = 0.0f;
float newVolume;
try
{
newVolume = (float)property.GetValue(this);
newVolume = property.GetFloatValue(this);
}
catch
{

View File

@@ -40,7 +40,7 @@ namespace Barotrauma.Items.Components
}
}
partial void SetLightSourceTransform()
partial void SetLightSourceTransformProjSpecific()
{
if (ParentBody != null)
{

View File

@@ -6,21 +6,23 @@ namespace Barotrauma.Items.Components
{
partial class Controller : ItemComponent
{
private bool chatBoxOriginalState;
private bool isHUDsHidden;
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
{
if (focusTarget != null && character.ViewTarget == focusTarget)
{
foreach (ItemComponent ic in focusTarget.Components)
{
ic.DrawHUD(spriteBatch, character);
if (ic.ShouldDrawHUD(character))
{
ic.DrawHUD(spriteBatch, character);
}
}
}
}
private bool crewAreaOriginalState;
private bool chatBoxOriginalState;
private bool isHUDsHidden;
partial void HideHUDs(bool value)
{
if (isHUDsHidden == value) { return; }

View File

@@ -302,6 +302,12 @@ namespace Barotrauma.Items.Components
}
HideEmptyItemListCategories();
if (selectedItem != null)
{
//reselect to recreate the info based on the new user's skills
SelectItem(character, selectedItem);
}
}
private void DrawInputOverLay(SpriteBatch spriteBatch, GUICustomComponent overlayComponent)
@@ -343,13 +349,14 @@ namespace Barotrauma.Items.Components
foreach (Item it in availableItems)
{
if (it.ParentInventory == inputContainer.Inventory) { continue; }
var rootContainer = it.GetRootContainer();
if (rootContainer?.OwnInventory?.visualSlots == null) { continue; }
int availableSlotIndex = rootContainer.OwnInventory.FindIndex(it.Container == rootContainer ? it : it.Container);
var rootInventoryOwner = it.GetRootInventoryOwner();
Inventory rootInventory = (rootInventoryOwner as Item)?.OwnInventory as Inventory ?? (rootInventoryOwner as Character)?.Inventory;
if (rootInventory?.visualSlots == null) { continue; }
int availableSlotIndex = rootInventory.FindIndex((it.Container != rootInventoryOwner ? it.Container : it) ?? it);
if (availableSlotIndex < 0) { continue; }
if (rootContainer.OwnInventory.visualSlots[availableSlotIndex].HighlightTimer <= 0.0f)
if (rootInventory.visualSlots[availableSlotIndex].HighlightTimer <= 0.0f)
{
rootContainer.OwnInventory.visualSlots[availableSlotIndex].ShowBorderHighlight(GUI.Style.Green, 0.5f, 0.5f, 0.2f);
rootInventory.visualSlots[availableSlotIndex].ShowBorderHighlight(GUI.Style.Green, 0.5f, 0.5f, 0.2f);
if (slotIndex < inputContainer.Capacity)
{
inputContainer.Inventory.visualSlots[slotIndex].ShowBorderHighlight(GUI.Style.Green, 0.5f, 0.5f, 0.2f);
@@ -406,9 +413,16 @@ namespace Barotrauma.Items.Components
{
toolTipText += " " + (int)Math.Round(requiredItem.MinCondition * 100) + "%";
}
else if(requiredItem.MaxCondition < 1.0f)
else if (requiredItem.MaxCondition < 1.0f)
{
toolTipText += " 0-" + (int)Math.Round(requiredItem.MaxCondition * 100) + "%";
if (requiredItem.MaxCondition <= 0.0f)
{
toolTipText += " " + (int)Math.Round(requiredItem.MaxCondition * 100) + "%";
}
else
{
toolTipText += " 0-" + (int)Math.Round(requiredItem.MaxCondition * 100) + "%";
}
}
else if (requiredItem.MaxCondition <= 0.0f)
{
@@ -524,16 +538,6 @@ namespace Barotrauma.Items.Components
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.9f), selectedItemFrame.RectTransform, Anchor.Center)) { RelativeSpacing = 0.03f };
var paddedReqFrame = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.9f), selectedItemReqsFrame.RectTransform, Anchor.Center)) { RelativeSpacing = 0.03f };
/*var itemIcon = selectedItem.TargetItem.InventoryIcon ?? selectedItem.TargetItem.sprite;
if (itemIcon != null)
{
GUIImage img = new GUIImage(new RectTransform(new Point(40, 40), paddedFrame.RectTransform),
itemIcon, scaleToFit: true)
{
Color = selectedItem.TargetItem.InventoryIconColor
};
}*/
string itemName = GetRecipeNameAndAmount(selectedItem);
string name = itemName;
@@ -732,8 +736,6 @@ namespace Barotrauma.Items.Components
Character user = Entity.FindEntityByID(userID) as Character;
State = newState;
timeUntilReady = newTimeUntilReady;
if (newState == FabricatorState.Stopped || itemIndex == -1)
{
CancelFabricating();
@@ -747,6 +749,7 @@ namespace Barotrauma.Items.Components
SelectItem(user, fabricationRecipes[itemIndex]);
StartFabricating(fabricationRecipes[itemIndex], user);
}
timeUntilReady = newTimeUntilReady;
}
}
}

View File

@@ -537,9 +537,9 @@ namespace Barotrauma.Items.Components
if (item.Submarine == null && displayedSubs.Count > 0 || // item not inside a sub anymore, but display is still showing subs
item.Submarine is { } itemSub &&
(
!displayedSubs.Contains(itemSub) || // current sub not displayed
itemSub.DockedTo.Any(s => !displayedSubs.Contains(s)) || // some of the docked subs not displayed
displayedSubs.Any(s => s != itemSub && !itemSub.DockedTo.Contains(s)) // displaying a sub that shouldn't be displayed
!displayedSubs.Contains(itemSub) || // current sub not displayed
itemSub.DockedTo.Any(s => !displayedSubs.Contains(s) && itemSub.ConnectedDockingPorts[s].IsLocked) || // some of the docked subs not displayed
displayedSubs.Any(s => s != itemSub && !itemSub.DockedTo.Contains(s)) // displaying a sub that shouldn't be displayed
) ||
prevResolution.X != GameMain.GraphicsWidth || prevResolution.Y != GameMain.GraphicsHeight || // resolution changed
!submarineContainer.Children.Any()) // We lack a GUI
@@ -1092,6 +1092,12 @@ namespace Barotrauma.Items.Components
if (!(entity is Item it)) { continue; }
if (!electricalChildren.TryGetValue(miniMapGuiComponent, out GUIComponent component)) { continue; }
if (entity.Removed)
{
component.Visible = false;
continue;
}
if (item.Submarine == null || !hasPower)
{
component.Color = component.OutlineColor = NoPowerElectricalColor;
@@ -1117,7 +1123,7 @@ namespace Barotrauma.Items.Components
int current = (int)-powerTransfer.CurrPowerConsumption, load = (int)powerTransfer.PowerLoad;
line1 = TextManager.GetWithVariable("statusmonitor.junctionpower.tooltip", "[amount]", current.ToString(), fallBackTag: "statusmonitor.junctioncurrent.tooltip");
line2 = TextManager.GetWithVariable("statusmonitor.junctionload.tooltip", "[amount]", load.ToString());
line2 = TextManager.GetWithVariables("statusmonitor.junctionload.tooltip", new string[] { "[amount]", "[load]" }, new string[] { load.ToString(), load.ToString() });
}
string line3 = TextManager.GetWithVariable("statusmonitor.durability.tooltip", "[amount]", durability.ToString());

View File

@@ -19,15 +19,6 @@ namespace Barotrauma.Items.Components
private readonly List<(Vector2 position, ParticleEmitter emitter)> pumpOutEmitters = new List<(Vector2 position, ParticleEmitter emitter)>();
private readonly List<(Vector2 position, ParticleEmitter emitter)> pumpInEmitters = new List<(Vector2 position, ParticleEmitter emitter)>();
public float CurrentBrokenVolume
{
get
{
if (item.ConditionPercentage > 10.0f || !IsActive) { return 0.0f; }
return (1.0f - item.ConditionPercentage / 10.0f) * 100.0f;
}
}
partial void InitProjSpecific(XElement element)
{
foreach (XElement subElement in element.Elements())
@@ -193,8 +184,6 @@ namespace Barotrauma.Items.Components
private readonly float flickerFrequency = 1;
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{
pumpSpeedLockTimer -= deltaTime;
isActiveLockTimer -= deltaTime;
autoControlIndicator.Selected = IsAutoControlled;
PowerButton.Enabled = isActiveLockTimer <= 0.0f;
if (HasPower)

View File

@@ -83,9 +83,7 @@ namespace Barotrauma.Items.Components
private const float ConnectedSubUpdateInterval = 1.0f;
float connectedSubUpdateTimer;
//Vector2 = vector from the ping source to the position of the disruption
//float = strength of the disruption, between 0-1
private readonly List<Pair<Vector2, float>> disruptedDirections = new List<Pair<Vector2, float>>();
private readonly List<(Vector2 pos, float strength)> disruptedDirections = new List<(Vector2 pos, float strength)>();
private readonly Dictionary<object, CachedDistance> markerDistances = new Dictionary<object, CachedDistance>();
@@ -455,7 +453,20 @@ namespace Barotrauma.Items.Components
zoomSlider.OnMoved(zoomSlider, zoomSlider.BarScroll);
}
}
Vector2 transducerCenter = GetTransducerPos();
if (steering != null && steering.DockingModeEnabled && steering.ActiveDockingSource != null)
{
Vector2 worldFocusPos = (steering.ActiveDockingSource.Item.WorldPosition + steering.DockingTarget.Item.WorldPosition) / 2.0f;
DisplayOffset = Vector2.Lerp(DisplayOffset, worldFocusPos - transducerCenter, 0.1f);
}
else
{
DisplayOffset = Vector2.Lerp(DisplayOffset, Vector2.Zero, 0.1f);
}
transducerCenter += DisplayOffset;
float distort = MathHelper.Clamp(1.0f - item.Condition / item.MaxCondition, 0.0f, 1.0f);
for (int i = sonarBlips.Count - 1; i >= 0; i--)
{
@@ -502,8 +513,6 @@ namespace Barotrauma.Items.Components
return;
}
Vector2 transducerCenter = GetTransducerPos() + DisplayOffset;
if (Level.Loaded != null)
{
nearbyObjectUpdateTimer -= deltaTime;
@@ -829,8 +838,7 @@ namespace Barotrauma.Items.Components
}
}
Vector2 transducerCenter = GetTransducerPos();
Vector2 transducerCenter = GetTransducerPos();// + DisplayOffset;
if (sonarBlips.Count > 0)
{
@@ -840,7 +848,7 @@ namespace Barotrauma.Items.Components
foreach (SonarBlip sonarBlip in sonarBlips)
{
DrawBlip(spriteBatch, sonarBlip, transducerCenter, center, sonarBlip.FadeTimer / 2.0f * signalStrength, blipScale);
DrawBlip(spriteBatch, sonarBlip, transducerCenter + DisplayOffset, center, sonarBlip.FadeTimer / 2.0f * signalStrength, blipScale);
}
spriteBatch.End();
@@ -849,8 +857,8 @@ namespace Barotrauma.Items.Components
if (item.Submarine != null && !DetectSubmarineWalls)
{
DrawDockingPorts(spriteBatch, transducerCenter, signalStrength);
transducerCenter += DisplayOffset;
DrawDockingPorts(spriteBatch, transducerCenter, signalStrength);
DrawOwnSubmarineBorders(spriteBatch, transducerCenter, signalStrength);
}
else
@@ -1083,10 +1091,6 @@ namespace Barotrauma.Items.Components
{
DrawDockingIndicator(spriteBatch, steering, ref transducerCenter);
}
else
{
DisplayOffset = Vector2.Lerp(DisplayOffset, Vector2.Zero, 0.1f);
}
foreach (DockingPort dockingPort in DockingPort.List)
{
@@ -1131,9 +1135,6 @@ namespace Barotrauma.Items.Components
Vector2 worldFocusPos = (steering.ActiveDockingSource.Item.WorldPosition + steering.DockingTarget.Item.WorldPosition) / 2.0f;
worldFocusPos.X = steering.DockingTarget.Item.WorldPosition.X;
DisplayOffset = Vector2.Lerp(DisplayOffset, worldFocusPos - transducerCenter, 0.1f);
transducerCenter += DisplayOffset;
Vector2 sourcePortDiff = (steering.ActiveDockingSource.Item.WorldPosition - transducerCenter) * scale;
Vector2 sourcePortPos = new Vector2(sourcePortDiff.X, -sourcePortDiff.Y);
Vector2 targetPortDiff = (steering.DockingTarget.Item.WorldPosition - transducerCenter) * scale;
@@ -1234,7 +1235,7 @@ namespace Barotrauma.Items.Components
Vector2 disruptionPos = new Vector2(levelObject.Position.X, levelObject.Position.Y);
float disruptionDist = Vector2.Distance(pingSource, disruptionPos);
disruptedDirections.Add(new Pair<Vector2, float>((disruptionPos - pingSource) / disruptionDist, disruptionStrength));
disruptedDirections.Add(((disruptionPos - pingSource) / disruptionDist, disruptionStrength));
CreateBlipsForDisruption(disruptionPos, disruptionStrength);
@@ -1246,7 +1247,7 @@ namespace Barotrauma.Items.Components
float distSqr = Vector2.DistanceSquared(aiTarget.WorldPosition, pingSource);
if (distSqr > worldPingRadiusSqr) { continue; }
float disruptionDist = (float)Math.Sqrt(distSqr);
disruptedDirections.Add(new Pair<Vector2, float>((aiTarget.WorldPosition - pingSource) / disruptionDist, aiTarget.SonarDisruption));
disruptedDirections.Add(((aiTarget.WorldPosition - pingSource) / disruptionDist, aiTarget.SonarDisruption));
CreateBlipsForDisruption(aiTarget.WorldPosition, disruption);
}
}
@@ -1461,10 +1462,10 @@ namespace Barotrauma.Items.Components
float transducerDist = transducerDiff.Length();
Vector2 pingDirection = transducerDiff / transducerDist;
bool disrupted = false;
foreach (Pair<Vector2, float> disruptDir in disruptedDirections)
foreach ((Vector2 disruptPos, float disruptStrength) in disruptedDirections)
{
float dot = Vector2.Dot(pingDirection, disruptDir.First);
if (dot > 1.0f - disruptDir.Second)
float dot = Vector2.Dot(pingDirection, disruptPos);
if (dot > 1.0f - disruptStrength)
{
disrupted = true;
break;

View File

@@ -403,7 +403,7 @@ namespace Barotrauma.Items.Components
{
if (GameMain.Client == null)
{
item.SendSignal("1", "toggle_docking");
item.SendSignal(new Signal("1", sender: Character.Controlled), "toggle_docking");
}
else
{

View File

@@ -434,6 +434,7 @@ namespace Barotrauma.Items.Components
DeteriorateAlways = msg.ReadBoolean();
tinkeringDuration = msg.ReadSingle();
tinkeringStrength = msg.ReadSingle();
tinkeringPowersDevices = msg.ReadBoolean();
ushort currentFixerID = msg.ReadUInt16();
currentFixerAction = (FixActions)msg.ReadRangedInteger(0, 2);
CurrentFixer = currentFixerID != 0 ? Entity.FindEntityByID(currentFixerID) as Character : null;

View File

@@ -46,6 +46,7 @@ namespace Barotrauma.Items.Components
child.Enabled = buttonsEnabled;
child.Children.ForEach(c => c.Enabled = buttonsEnabled);
}
if (Container == null) { return; }
bool itemsContained = Container.Inventory.AllItems.Any();
if (itemsContained)
{
@@ -77,7 +78,7 @@ namespace Barotrauma.Items.Components
{
if (GameMain.IsSingleplayer)
{
SendSignal((int)userData);
SendSignal((int)userData, Character.Controlled);
}
else
{
@@ -98,6 +99,7 @@ namespace Barotrauma.Items.Components
partial void OnItemLoadedProjSpecific()
{
if (Container == null) { return; }
Container.AllowUIOverlap = true;
Container.Inventory.RectTransform = containerHolder.RectTransform;
}
@@ -109,7 +111,7 @@ namespace Barotrauma.Items.Components
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
SendSignal(msg.ReadRangedInteger(0, Signals.Length - 1), isServerMessage: true);
SendSignal(msg.ReadRangedInteger(0, Signals.Length - 1), sender: null, isServerMessage: true);
}
}
}

View File

@@ -197,7 +197,7 @@ namespace Barotrauma.Items.Components
}
}
partial void UpdateProjSpecific()
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{
bool elementVisibilityChanged = false;
int visibleElementCount = 0;
@@ -209,6 +209,7 @@ namespace Barotrauma.Items.Components
if (uiElement.Visible != visible)
{
uiElement.Visible = visible;
uiElement.IgnoreLayoutGroups = !uiElement.Visible;
elementVisibilityChanged = true;
}
}
@@ -223,6 +224,7 @@ namespace Barotrauma.Items.Components
uiElement.RectTransform.RelativeSize = new Vector2(1.0f, elementSize);
}
GuiFrame.Visible = visibleElementCount > 0;
uiElementContainer.Recalculate();
}
}

View File

@@ -12,9 +12,9 @@ namespace Barotrauma.Items.Components
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
{
if (!editing || !MapEntity.SelectedList.Contains(item)) return;
if (!editing || !MapEntity.SelectedList.Contains(item)) { return; }
Vector2 pos = item.WorldPosition + detectOffset;
Vector2 pos = item.WorldPosition + TransformedDetectOffset;
pos.Y = -pos.Y;
GUI.DrawRectangle(spriteBatch, pos - new Vector2(rangeX, rangeY), new Vector2(rangeX, rangeY) * 2.0f, Color.Cyan * 0.5f, isFilled: false, thickness: 2);
}

View File

@@ -286,7 +286,6 @@ namespace Barotrauma.Items.Components
item.Color, depth, 0.3f);
}
public static void UpdateEditing(List<Wire> wires)
{
var doubleClicked = PlayerInput.DoubleClicked();
@@ -509,6 +508,31 @@ namespace Barotrauma.Items.Components
}
}
public override void Move(Vector2 amount)
{
//only used in the sub editor, hence only in the client project
if (!item.IsSelected) { return; }
Vector2 wireNodeOffset = item.Submarine == null ? Vector2.Zero : item.Submarine.HiddenSubPosition + amount;
for (int i = 0; i < nodes.Count; i++)
{
if (i == 0 || i == nodes.Count - 1)
{
if (connections[0]?.Item != null && !connections[0].Item.IsSelected &&
(Submarine.RectContains(connections[0].Item.Rect, nodes[i] + wireNodeOffset) || Submarine.RectContains(connections[0].Item.Rect, nodes[i] + wireNodeOffset - amount)))
{
continue;
}
else if (connections[1]?.Item != null && !connections[1].Item.IsSelected &&
(Submarine.RectContains(connections[1].Item.Rect, nodes[i] + wireNodeOffset) || Submarine.RectContains(connections[1].Item.Rect, nodes[i] + wireNodeOffset - amount)))
{
continue;
}
}
nodes[i] += amount;
}
UpdateSections();
}
public bool IsMouseOn()
{
if (GUI.MouseOn == null)

View File

@@ -301,7 +301,7 @@ namespace Barotrauma.Items.Components
Dictionary<AfflictionPrefab, float> combinedAfflictionStrengths = new Dictionary<AfflictionPrefab, float>();
foreach (Affliction affliction in allAfflictions)
{
if (affliction.Strength < affliction.Prefab.ShowInHealthScannerThreshold || affliction.Strength <= 0.0f) continue;
if (affliction.Strength < affliction.Prefab.ShowInHealthScannerThreshold || affliction.Strength <= 0.0f) { continue; }
if (combinedAfflictionStrengths.ContainsKey(affliction.Prefab))
{
combinedAfflictionStrengths[affliction.Prefab] += affliction.Strength;
@@ -314,7 +314,7 @@ namespace Barotrauma.Items.Components
foreach (AfflictionPrefab affliction in combinedAfflictionStrengths.Keys)
{
texts.Add(TextManager.AddPunctuation(':', affliction.Name, Math.Max(((int)combinedAfflictionStrengths[affliction]), 1).ToString() + " %"));
texts.Add(TextManager.AddPunctuation(':', affliction.Name, Math.Max((int)combinedAfflictionStrengths[affliction], 1).ToString() + " %"));
textColors.Add(Color.Lerp(GUI.Style.Orange, GUI.Style.Red, combinedAfflictionStrengths[affliction] / affliction.MaxStrength));
}
}

View File

@@ -17,7 +17,7 @@ namespace Barotrauma
partial class Item : MapEntity, IDamageable, ISerializableEntity, IServerSerializable, IClientSerializable
{
public static bool ShowItems = true, ShowWires = true;
private readonly List<PosInfo> positionBuffer = new List<PosInfo>();
private readonly List<ItemComponent> activeHUDs = new List<ItemComponent>();
@@ -89,8 +89,8 @@ namespace Barotrauma
{
if (itemInUseWarning == null)
{
itemInUseWarning = new GUITextBlock(new RectTransform(new Point(10), GUI.Canvas), "",
textColor: GUI.Style.Orange, color: Color.Black,
itemInUseWarning = new GUITextBlock(new RectTransform(new Point(10), GUI.Canvas), "",
textColor: GUI.Style.Orange, color: Color.Black,
textAlignment: Alignment.Center, style: "OuterGlow");
}
return itemInUseWarning;
@@ -105,6 +105,9 @@ namespace Barotrauma
{
return false;
}
if (!SubEditorScreen.IsLayerVisible(this)) { return false;}
return parentInventory == null && (body == null || body.Enabled) && ShowItems;
}
}
@@ -154,7 +157,7 @@ namespace Barotrauma
if (containedSprite.UseWhenAttached)
{
activeContainedSprite = containedSprite;
activeSprite = containedSprite.Sprite;
activeSprite = containedSprite.Sprite;
UpdateSpriteStates(0.0f);
return;
}
@@ -196,7 +199,7 @@ namespace Barotrauma
{
brokenSprite.Sprite.EnsureLazyLoaded();
}
foreach (var decorativeSprite in ((ItemPrefab)prefab).DecorativeSprites)
{
decorativeSprite.Sprite.EnsureLazyLoaded();
@@ -255,7 +258,7 @@ namespace Barotrauma
public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true)
{
if (!Visible || (!editing && HiddenInGame)) { return; }
if (!Visible || (!editing && HiddenInGame) || !SubEditorScreen.IsLayerVisible(this)) { return; }
if (editing)
{
@@ -265,7 +268,7 @@ namespace Barotrauma
}
else if (!ShowItems) { return; }
}
Color color = IsIncludedInSelection && editing ? GUI.Style.Blue : IsHighlighted && !GUI.DisableItemHighlights && Screen.Selected != GameMain.GameScreen ? GUI.Style.Orange * Math.Max(GetSpriteColor().A / (float) byte.MaxValue, 0.1f) : GetSpriteColor();
//if (IsSelected && editing) color = Color.Lerp(color, Color.Gold, 0.5f);
@@ -273,7 +276,7 @@ namespace Barotrauma
bool isWiringMode = editing && SubEditorScreen.TransparentWiringMode && SubEditorScreen.IsWiringMode() && !isWire && parentInventory == null;
bool renderTransparent = isWiringMode && GetComponent<ConnectionPanel>() == null;
if (renderTransparent) { color *= 0.15f; }
BrokenItemSprite fadeInBrokenSprite = null;
float fadeInBrokenSpriteAlpha = 0.0f;
float displayCondition = FakeBroken ? 0.0f : ConditionPercentage;
@@ -322,7 +325,7 @@ namespace Barotrauma
Vector2 size = new Vector2(rect.Width, rect.Height);
if (color.A > 0)
{
activeSprite.DrawTiled(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2)) + drawOffset,
activeSprite.DrawTiled(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2)) + drawOffset,
size, color: color,
textureScale: Vector2.One * Scale,
depth: depth);
@@ -336,11 +339,11 @@ namespace Barotrauma
}
foreach (var decorativeSprite in Prefab.DecorativeSprites)
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
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,
decorativeSprite.Sprite.DrawTiled(spriteBatch,
new Vector2(DrawPosition.X + offset.X - rect.Width / 2, -(DrawPosition.Y + offset.Y + rect.Height / 2)),
size, color: color,
textureScale: Vector2.One * Scale,
@@ -380,7 +383,7 @@ namespace Barotrauma
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.Draw(spriteBatch, new Vector2(DrawPosition.X + offset.X, -(DrawPosition.Y + offset.Y)), color,
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,
depth: Math.Min(depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth), 0.999f));
}
@@ -440,11 +443,11 @@ namespace Barotrauma
depth: depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth));
}
}
foreach (var upgrade in Upgrades)
{
var upgradeSprites = GetUpgradeSprites(upgrade);
foreach (var decorativeSprite in upgradeSprites)
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
@@ -456,7 +459,7 @@ namespace Barotrauma
rotation, decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, activeSprite.effects,
depth: depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth));
}
}
activeSprite.effects = oldEffects;
@@ -466,7 +469,7 @@ namespace Barotrauma
}
}
//use a backwards for loop because the drawable components may disable drawing,
//use a backwards for loop because the drawable components may disable drawing,
//causing them to be removed from the list
for (int i = drawableComponents.Count - 1; i >= 0; i--)
{
@@ -501,7 +504,7 @@ namespace Barotrauma
Vector2 drawPos = new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2));
Vector2 drawSize = new Vector2(MathF.Ceiling(rect.Width + Math.Abs(drawPos.X - (int)drawPos.X)), MathF.Ceiling(rect.Height + Math.Abs(drawPos.Y - (int)drawPos.Y)));
drawPos = new Vector2(MathF.Floor(drawPos.X), MathF.Floor(drawPos.Y));
GUI.DrawRectangle(spriteBatch, drawPos, drawSize,
GUI.DrawRectangle(spriteBatch, drawPos, drawSize,
Color.White, false, 0, thickness: Math.Max(1, (int)(2 / Screen.Selected.Cam.Zoom)));
foreach (Rectangle t in Prefab.Triggers)
@@ -582,19 +585,25 @@ namespace Barotrauma
}
}
DecorativeSprite.UpdateSpriteStates(Prefab.DecorativeSpriteGroups, spriteAnimState, ID, deltaTime, ConditionalMatches);
if (Prefab.DecorativeSpriteGroups.Count > 0)
{
DecorativeSprite.UpdateSpriteStates(Prefab.DecorativeSpriteGroups, spriteAnimState, ID, deltaTime, ConditionalMatches);
}
foreach (var upgrade in Upgrades)
{
var upgradeSprites = GetUpgradeSprites(upgrade);
var upgradeSprites = GetUpgradeSprites(upgrade);
foreach (var decorativeSprite in upgradeSprites)
{
var spriteState = spriteAnimState[decorativeSprite];
spriteState.IsActive = true;
foreach (var _ in decorativeSprite.IsActiveConditionals.Where(conditional => !ConditionalMatches(conditional)))
foreach (var conditional in decorativeSprite.IsActiveConditionals)
{
spriteState.IsActive = false;
break;
if (!ConditionalMatches(conditional))
{
spriteState.IsActive = false;
break;
}
}
}
}
@@ -696,8 +705,8 @@ namespace Barotrauma
foreach (string tag in ip.PreferredContainers.SelectMany(pc => pc.Primary)) { availableTags.Add(tag); }
foreach (string tag in ip.PreferredContainers.SelectMany(pc => pc.Secondary)) { availableTags.Add(tag); }
}
//remove identifiers from the available container tags
//(otherwise the list will include many irrelevant options,
//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)
availableTags.RemoveWhere(t => MapEntityPrefab.List.Any(me => me.Identifier == t));
new GUIButton(new RectTransform(new Vector2(0.1f, 1), tagsField.RectTransform, Anchor.TopRight), "...")
@@ -749,7 +758,7 @@ namespace Barotrauma
{
me.FlipY(relativeToSub: false);
}
if (!SelectedList.Contains(this)) { FlipY(relativeToSub: false); }
if (!SelectedList.Contains(this)) { FlipY(relativeToSub: false); }
return true;
}
};
@@ -805,9 +814,9 @@ namespace Barotrauma
{
if (!ic.AllowInGameEditing) { continue; }
if (SerializableProperty.GetProperties<InGameEditable>(ic).Count == 0 &&
!SerializableProperty.GetProperties<ConditionallyEditable>(ic).Any(p => p.GetAttribute<ConditionallyEditable>().IsEditable(ic)))
!SerializableProperty.GetProperties<ConditionallyEditable>(ic).Any(p => p.GetAttribute<ConditionallyEditable>().IsEditable(ic)))
{
continue;
continue;
}
}
else
@@ -869,7 +878,7 @@ namespace Barotrauma
textBox.Text = relatedItem.JoinedIdentifiers;
return true;
};
}
}
ic.CreateEditingHUD(componentEditor);
componentEditor.Recalculate();
@@ -892,7 +901,7 @@ namespace Barotrauma
return upgradeSprites;
}
public override bool AddUpgrade(Upgrade upgrade, bool createNetworkEvent = false)
{
if (upgrade.Prefab.IsWallUpgrade) { return false; }
@@ -949,7 +958,7 @@ namespace Barotrauma
//reset positions first
List<GUIComponent> elementsToMove = new List<GUIComponent>();
if (editingHUD != null && editingHUD.UserData == this &&
if (editingHUD != null && editingHUD.UserData == this &&
((HasInGameEditableProperties && Character.Controlled?.SelectedConstruction == this) || Screen.Selected == GameMain.SubEditorScreen))
{
elementsToMove.Add(editingHUD);
@@ -972,8 +981,8 @@ namespace Barotrauma
int disallowedPadding = (int)(50 * GUI.Scale);
disallowedAreas.Add(GameMain.GameSession.CrewManager.GetActiveCrewArea());
disallowedAreas.Add(new Rectangle(
HUDLayoutSettings.ChatBoxArea.X - disallowedPadding, HUDLayoutSettings.ChatBoxArea.Y,
HUDLayoutSettings.ChatBoxArea.Width + disallowedPadding, HUDLayoutSettings.ChatBoxArea.Height));
HUDLayoutSettings.ChatBoxArea.X - disallowedPadding, HUDLayoutSettings.ChatBoxArea.Y,
HUDLayoutSettings.ChatBoxArea.Width + disallowedPadding, HUDLayoutSettings.ChatBoxArea.Height));
}
if (Screen.Selected is SubEditorScreen editor)
@@ -985,8 +994,8 @@ namespace Barotrauma
GUI.PreventElementOverlap(elementsToMove, disallowedAreas,
new Rectangle(
0, 20,
GameMain.GraphicsWidth,
0, 20,
GameMain.GraphicsWidth,
HUDLayoutSettings.InventoryTopY > 0 ? HUDLayoutSettings.InventoryTopY - 40 : GameMain.GraphicsHeight - 80));
foreach (ItemComponent ic in activeHUDs)
@@ -995,7 +1004,7 @@ namespace Barotrauma
var linkUIToComponent = ic.GetLinkUIToComponent();
if (linkUIToComponent == null) { continue; }
if (linkUIToComponent == null) { continue; }
ic.GuiFrame.RectTransform.ScreenSpaceOffset = linkUIToComponent.GuiFrame.RectTransform.ScreenSpaceOffset;
}
@@ -1110,14 +1119,14 @@ namespace Barotrauma
}
}
}
public void DrawHUD(SpriteBatch spriteBatch, Camera cam, Character character)
{
if (HasInGameEditableProperties && (character.SelectedConstruction == this || EditableWhenEquipped))
{
DrawEditing(spriteBatch, cam);
}
foreach (ItemComponent ic in activeHUDs)
{
if (ic.CanBeSelected)
@@ -1138,9 +1147,9 @@ namespace Barotrauma
GUI.DrawRectangle(spriteBatch, debugInitialHudPositions[i], Color.Orange);
GUI.DrawRectangle(spriteBatch, ic.GuiFrame.Rect, Color.LightGreen);
GUI.DrawLine(spriteBatch, debugInitialHudPositions[i].Location.ToVector2(), ic.GuiFrame.Rect.Location.ToVector2(), Color.Orange);
i++;
}
}
}
}
@@ -1262,7 +1271,7 @@ namespace Barotrauma
NetEntityEvent.Type eventType =
(NetEntityEvent.Type)msg.ReadRangedInteger(0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1);
switch (eventType)
{
case NetEntityEvent.Type.ComponentState:
@@ -1323,7 +1332,7 @@ namespace Barotrauma
ItemComponent targetComponent = componentIndex < components.Count ? components[componentIndex] : null;
Character targetCharacter = FindEntityByID(targetCharacterID) as Character;
Limb targetLimb = targetCharacter != null && targetLimbID < targetCharacter.AnimController.Limbs.Length ?
Limb targetLimb = targetCharacter != null && targetLimbID < targetCharacter.AnimController.Limbs.Length ?
targetCharacter.AnimController.Limbs[targetLimbID] : null;
Entity useTarget = FindEntityByID(useTargetID);
@@ -1334,7 +1343,7 @@ namespace Barotrauma
else
{
targetComponent.ApplyStatusEffects(actionType, 1.0f, targetCharacter, targetLimb, useTarget, worldPosition: worldPosition);
}
}
}
break;
case NetEntityEvent.Type.ChangeProperty:
@@ -1346,7 +1355,7 @@ namespace Barotrauma
if (UpgradePrefab.Find(identifier) is { } upgradePrefab)
{
Upgrade upgrade = new Upgrade(this, upgradePrefab, level);
byte targetCount = msg.ReadByte();
for (int i = 0; i < targetCount; i++)
{
@@ -1360,7 +1369,7 @@ namespace Barotrauma
AddUpgrade(upgrade, false);
}
break;
break;
case NetEntityEvent.Type.Invalid:
break;
}
@@ -1394,7 +1403,7 @@ namespace Barotrauma
Character targetCharacter = FindEntityByID(characterID) as Character;
msg.Write(characterID);
msg.Write(targetCharacter == null ? (byte)255 : (byte)Array.IndexOf(targetCharacter.AnimController.Limbs, targetLimb));
msg.Write(targetCharacter == null ? (byte)255 : (byte)Array.IndexOf(targetCharacter.AnimController.Limbs, targetLimb));
break;
case NetEntityEvent.Type.ChangeProperty:
WritePropertyChange(msg, extraData, true);

View File

@@ -14,7 +14,7 @@ namespace Barotrauma
{
get
{
return ShowGaps;
return ShowGaps && SubEditorScreen.IsLayerVisible(this);
}
}
@@ -42,7 +42,7 @@ namespace Barotrauma
}
}
if (!editing || !ShowGaps) { return; }
if (!editing || !ShowGaps || !SubEditorScreen.IsLayerVisible(this)) { return; }
Color clr = (open == 0.0f) ? GUI.Style.Red : Color.Cyan;
if (IsHighlighted) clr = Color.Gold;
@@ -128,8 +128,14 @@ namespace Barotrauma
{
//no flow particles between linked hulls (= rooms consisting of multiple hulls)
if (hull1.linkedTo.Contains(hull2)) { return; }
if (hull1.linkedTo.Any(h => h.linkedTo.Contains(hull1) && h.linkedTo.Contains(hull2))) { return; }
if (hull2.linkedTo.Any(h => h.linkedTo.Contains(hull1) && h.linkedTo.Contains(hull2))) { return; }
foreach (Hull h in hull1.linkedTo)
{
if (h.linkedTo.Contains(hull1) && h.linkedTo.Contains(hull2)) { return; }
}
foreach (Hull h in hull2.linkedTo)
{
if (h.linkedTo.Contains(hull1) && h.linkedTo.Contains(hull2)) { return; }
}
}
Vector2 pos = Position;
@@ -177,7 +183,7 @@ namespace Barotrauma
"bubbles",
(Submarine == null ? pos : pos + Submarine.Position),
velocity, 0, flowTargetHull);
particleTimer -= emitInterval;
}
}

View File

@@ -34,14 +34,14 @@ namespace Barotrauma
private readonly List<RemoteDecal> remoteDecals = new List<RemoteDecal>();
private readonly HashSet<Decal> pendingDecalUpdates = new HashSet<Decal>();
private double lastAmbientLightEditTime;
public override bool SelectableInEditor
{
get
{
return ShowHulls;
return ShowHulls && SubEditorScreen.IsLayerVisible(this);
}
}
@@ -133,39 +133,41 @@ namespace Barotrauma
else
{
if (!entity.linkedTo.Contains(this)) { entity.linkedTo.Add(this); }
if (!linkedTo.Contains(this)) { linkedTo.Add(entity); }
if (!linkedTo.Contains(this)) { linkedTo.Add(entity); }
}
}
}
partial void UpdateProjSpecific(float deltaTime, Camera cam)
{
serverUpdateDelay -= deltaTime;
if (serverUpdateDelay <= 0.0f)
if (GameMain.Client != null)
{
ApplyRemoteState();
}
if (networkUpdatePending)
{
networkUpdateTimer += deltaTime;
if (networkUpdateTimer > 0.2f)
serverUpdateDelay -= deltaTime;
if (serverUpdateDelay <= 0.0f)
{
if (!pendingSectionUpdates.Any() && !pendingDecalUpdates.Any())
ApplyRemoteState();
}
if (networkUpdatePending)
{
networkUpdateTimer += deltaTime;
if (networkUpdateTimer > 0.2f)
{
GameMain.NetworkMember?.CreateEntityEvent(this);
if (!pendingSectionUpdates.Any() && !pendingDecalUpdates.Any())
{
GameMain.NetworkMember?.CreateEntityEvent(this);
}
foreach (Decal decal in pendingDecalUpdates)
{
GameMain.NetworkMember?.CreateEntityEvent(this, new object[] { decal });
}
foreach (int pendingSectionUpdate in pendingSectionUpdates)
{
GameMain.NetworkMember?.CreateEntityEvent(this, new object[] { pendingSectionUpdate });
}
pendingSectionUpdates.Clear();
networkUpdatePending = false;
networkUpdateTimer = 0.0f;
}
foreach (Decal decal in pendingDecalUpdates)
{
GameMain.NetworkMember?.CreateEntityEvent(this, new object[] { decal });
}
foreach (int pendingSectionUpdate in pendingSectionUpdates)
{
GameMain.NetworkMember?.CreateEntityEvent(this, new object[] { pendingSectionUpdate });
}
pendingSectionUpdates.Clear();
networkUpdatePending = false;
networkUpdateTimer = 0.0f;
}
}
@@ -243,7 +245,7 @@ namespace Barotrauma
return;
}
if (!ShowHulls && !GameMain.DebugDraw) { return; }
if ((!ShowHulls || !SubEditorScreen.IsLayerVisible(this)) && !GameMain.DebugDraw) { return; }
if (!editing && (!GameMain.DebugDraw || Screen.Selected.Cam.Zoom < 0.1f)) { return; }
@@ -385,45 +387,42 @@ namespace Barotrauma
}
}
private static readonly Vector3[] corners = new Vector3[6];
private static readonly Vector2[] uvCoords = new Vector2[4];
private static readonly Vector3[] prevCorners = new Vector3[2];
private static readonly Vector2[] prevUVs = new Vector2[2];
private void UpdateVertices(Camera cam, EntityGrid entityGrid, WaterRenderer renderer)
{
Vector2 submarinePos = Submarine == null ? Vector2.Zero : Submarine.DrawPosition;
//if there's no more space in the buffer, don't render the water in the hull
//not an ideal solution, but this seems to only happen in cases where the missing
//not an ideal solution, but this seems to only happen in cases where the missing
//water is not very noticeable (e.g. zoomed very far out so that multiple subs and ruins are visible)
if (renderer.PositionInBuffer > renderer.vertices.Length - 6)
{
return;
}
if (!renderer.IndoorsVertices.ContainsKey(entityGrid))
{
renderer.IndoorsVertices[entityGrid] = new VertexPositionColorTexture[WaterRenderer.DefaultIndoorsBufferSize];
renderer.PositionInIndoorsBuffer[entityGrid] = 0;
}
//calculate where the surface should be based on the water volume
float top = rect.Y + submarinePos.Y;
float bottom = top - rect.Height;
float renderSurface = drawSurface + submarinePos.Y;
if (bottom > cam.WorldView.Y || top < cam.WorldView.Y - cam.WorldView.Height) return;
if (bottom > cam.WorldView.Y || top < cam.WorldView.Y - cam.WorldView.Height) { return; }
if (rect.X + submarinePos.X > cam.WorldView.Right || rect.Right + submarinePos.X < cam.WorldView.X) { return; }
Matrix transform = cam.Transform * Matrix.CreateOrthographic(GameMain.GraphicsWidth, GameMain.GraphicsHeight, -1, 1) * 0.5f;
Matrix transform = cam.Transform * Matrix.CreateOrthographic(GameMain.GraphicsWidth, GameMain.GraphicsHeight, -1, 1) * 0.5f;
if (!update)
{
// create the four corners of our triangle.
Vector3[] corners = new Vector3[4];
corners[0] = new Vector3(rect.X, rect.Y, 0.0f);
corners[1] = new Vector3(rect.X + rect.Width, rect.Y, 0.0f);
corners[2] = new Vector3(corners[1].X, rect.Y - rect.Height, 0.0f);
corners[3] = new Vector3(corners[0].X, corners[2].Y, 0.0f);
Vector2[] uvCoords = new Vector2[4];
for (int i = 0; i < 4; i++)
{
corners[i] += new Vector3(submarinePos, 0.0f);
@@ -443,6 +442,15 @@ namespace Barotrauma
return;
}
if (!renderer.IndoorsVertices.ContainsKey(entityGrid))
{
renderer.IndoorsVertices[entityGrid] = new VertexPositionColorTexture[WaterRenderer.DefaultIndoorsBufferSize];
}
if (!renderer.PositionInIndoorsBuffer.ContainsKey(entityGrid))
{
renderer.PositionInIndoorsBuffer[entityGrid] = 0;
}
float x = rect.X;
if (Submarine != null) { x += Submarine.DrawPosition.X; }
@@ -454,20 +462,15 @@ namespace Barotrauma
x += start * WaveWidth;
Vector3[] prevCorners = new Vector3[2];
Vector2[] prevUVs = new Vector2[2];
int width = WaveWidth;
for (int i = start; i < end; i++)
{
Vector3[] corners = new Vector3[6];
//top left
corners[0] = new Vector3(x, top, 0.0f);
//watersurface left
corners[3] = new Vector3(corners[0].X, renderSurface + waveY[i], 0.0f);
//top right
corners[1] = new Vector3(x + width, top, 0.0f);
//watersurface right
@@ -477,7 +480,7 @@ namespace Barotrauma
corners[4] = new Vector3(x, bottom, 0.0f);
//bottom right
corners[5] = new Vector3(x + width, bottom, 0.0f);
Vector2[] uvCoords = new Vector2[4];
for (int n = 0; n < 4; n++)
{
@@ -714,7 +717,7 @@ namespace Barotrauma
}
remoteBackgroundSections.Clear();
if (remoteDecals.Any())
if (remoteDecals.Count > 0)
{
decals.Clear();
foreach (RemoteDecal remoteDecal in remoteDecals)

View File

@@ -51,7 +51,7 @@ namespace Barotrauma
public readonly WaterVertexData IndoorsSurfaceBottomColor = new WaterVertexData(0.2f, 0.1f, 0.9f, 1.0f);
public VertexPositionTexture[] vertices = new VertexPositionTexture[DefaultBufferSize];
public Dictionary<EntityGrid, VertexPositionColorTexture[]> IndoorsVertices = new Dictionary<EntityGrid, VertexPositionColorTexture[]>();// VertexPositionColorTexture[DefaultBufferSize * 2];
public Dictionary<EntityGrid, VertexPositionColorTexture[]> IndoorsVertices = new Dictionary<EntityGrid, VertexPositionColorTexture[]>();
public Effect WaterEffect
{
@@ -81,13 +81,17 @@ namespace Barotrauma
if (basicEffect == null)
{
basicEffect = new BasicEffect(GameMain.Instance.GraphicsDevice);
basicEffect.VertexColorEnabled = false;
basicEffect.TextureEnabled = true;
basicEffect = new BasicEffect(GameMain.Instance.GraphicsDevice)
{
VertexColorEnabled = false,
TextureEnabled = true
};
}
}
private readonly VertexPositionColorTexture[] tempVertices = new VertexPositionColorTexture[6];
private readonly Vector3[] tempCorners = new Vector3[4];
public void RenderWater(SpriteBatch spriteBatch, RenderTarget2D texture, Camera cam)
{
spriteBatch.GraphicsDevice.BlendState = BlendState.NonPremultiplied;
@@ -139,29 +143,26 @@ namespace Barotrauma
WaterEffect.CurrentTechnique.Passes[0].Apply();
VertexPositionColorTexture[] verts = new VertexPositionColorTexture[6];
Rectangle view = cam != null ? cam.WorldView : spriteBatch.GraphicsDevice.Viewport.Bounds;
var corners = new Vector3[4];
corners[0] = new Vector3(view.X, view.Y, 0.1f);
corners[1] = new Vector3(view.Right, view.Y, 0.1f);
corners[2] = new Vector3(view.Right, view.Y - view.Height, 0.1f);
corners[3] = new Vector3(view.X, view.Y - view.Height, 0.1f);
tempCorners[0] = new Vector3(view.X, view.Y, 0.1f);
tempCorners[1] = new Vector3(view.Right, view.Y, 0.1f);
tempCorners[2] = new Vector3(view.Right, view.Y - view.Height, 0.1f);
tempCorners[3] = new Vector3(view.X, view.Y - view.Height, 0.1f);
WaterVertexData backGroundColor = new WaterVertexData(0.1f, 0.1f, 0.5f, 1.0f);
verts[0] = new VertexPositionColorTexture(corners[0], backGroundColor, Vector2.Zero);
verts[1] = new VertexPositionColorTexture(corners[1], backGroundColor, Vector2.Zero);
verts[2] = new VertexPositionColorTexture(corners[2], backGroundColor, Vector2.Zero);
verts[3] = new VertexPositionColorTexture(corners[0], backGroundColor, Vector2.Zero);
verts[4] = new VertexPositionColorTexture(corners[2], backGroundColor, Vector2.Zero);
verts[5] = new VertexPositionColorTexture(corners[3], backGroundColor, Vector2.Zero);
tempVertices[0] = new VertexPositionColorTexture(tempCorners[0], backGroundColor, Vector2.Zero);
tempVertices[1] = new VertexPositionColorTexture(tempCorners[1], backGroundColor, Vector2.Zero);
tempVertices[2] = new VertexPositionColorTexture(tempCorners[2], backGroundColor, Vector2.Zero);
tempVertices[3] = new VertexPositionColorTexture(tempCorners[0], backGroundColor, Vector2.Zero);
tempVertices[4] = new VertexPositionColorTexture(tempCorners[2], backGroundColor, Vector2.Zero);
tempVertices[5] = new VertexPositionColorTexture(tempCorners[3], backGroundColor, Vector2.Zero);
spriteBatch.GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, verts, 0, 2);
spriteBatch.GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, tempVertices, 0, 2);
foreach (KeyValuePair<EntityGrid, VertexPositionColorTexture[]> subVerts in IndoorsVertices)
{
if (!PositionInIndoorsBuffer.ContainsKey(subVerts.Key) || PositionInIndoorsBuffer[subVerts.Key] == 0) continue;
if (!PositionInIndoorsBuffer.ContainsKey(subVerts.Key) || PositionInIndoorsBuffer[subVerts.Key] == 0) { continue; }
offset = WavePos;
if (subVerts.Key.Submarine != null) { offset -= subVerts.Key.Submarine.WorldPosition; }
@@ -207,11 +208,23 @@ namespace Barotrauma
basicEffect.CurrentTechnique.Passes[0].Apply();
}
private readonly List<EntityGrid> buffersToRemove = new List<EntityGrid>();
public void ResetBuffers()
{
PositionInBuffer = 0;
PositionInIndoorsBuffer.Clear();
IndoorsVertices.Clear();
buffersToRemove.Clear();
foreach (var buffer in IndoorsVertices.Keys)
{
if (buffer.Submarine?.Removed ?? false)
{
buffersToRemove.Add(buffer);
}
}
foreach (var bufferToRemove in buffersToRemove)
{
IndoorsVertices.Remove(bufferToRemove);
}
}
public void Dispose()

View File

@@ -517,7 +517,7 @@ namespace Barotrauma.Lights
private void RefreshConvexHullList(ConvexHullList chList, Vector2 lightPos, Submarine sub)
{
var fullChList = ConvexHull.HullLists.Find(x => x.Submarine == sub);
if (fullChList == null) return;
if (fullChList == null) { return; }
chList.List = fullChList.List.FindAll(ch => ch.Enabled && MathUtils.CircleIntersectsRectangle(lightPos, TextureRange, ch.BoundingBox));
@@ -530,105 +530,121 @@ namespace Barotrauma.Lights
/// </summary>
private void CheckHullsInRange()
{
List<Submarine> subs = new List<Submarine>(Submarine.Loaded);
subs.Add(null);
foreach (Submarine sub in subs)
foreach (Submarine sub in Submarine.Loaded)
{
//find the list of convexhulls that belong to the sub
var chList = hullsInRange.Find(x => x.Submarine == sub);
CheckHullsInRange(sub);
}
//check convex hulls that aren't in any sub
CheckHullsInRange(null);
}
//not found -> create one
if (chList == null)
private void CheckHullsInRange(Submarine sub)
{
//find the list of convexhulls that belong to the sub
ConvexHullList chList = null;
foreach (var ch in hullsInRange)
{
if (ch.Submarine == sub)
{
chList = new ConvexHullList(sub);
hullsInRange.Add(chList);
NeedsRecalculation = true;
}
if (chList.List.Any(ch => ch.LastVertexChangeTime > lastRecalculationTime && !chList.IsHidden.Contains(ch)))
{
NeedsRecalculation = true;
}
Vector2 lightPos = position;
if (ParentSub == null)
{
//light and the convexhulls are both outside
if (sub == null)
{
if (NeedsHullCheck)
{
RefreshConvexHullList(chList, lightPos, null);
}
}
//light is outside, convexhulls inside a sub
else
{
lightPos -= sub.Position;
Rectangle subBorders = sub.Borders;
subBorders.Location += sub.HiddenSubPosition.ToPoint() - new Point(0, sub.Borders.Height);
//only draw if the light overlaps with the sub
if (!MathUtils.CircleIntersectsRectangle(lightPos, TextureRange, subBorders))
{
if (chList.List.Count > 0) NeedsRecalculation = true;
chList.List.Clear();
continue;
}
RefreshConvexHullList(chList, lightPos, sub);
}
}
else
{
//light is inside, convexhull outside
if (sub == null) continue;
//light and convexhull are both inside the same sub
if (sub == ParentSub)
{
if (NeedsHullCheck)
{
RefreshConvexHullList(chList, lightPos, sub);
}
}
//light and convexhull are inside different subs
else
{
if (sub.DockedTo.Contains(ParentSub) && !NeedsHullCheck) continue;
lightPos -= (sub.Position - ParentSub.Position);
Rectangle subBorders = sub.Borders;
subBorders.Location += sub.HiddenSubPosition.ToPoint() - new Point(0, sub.Borders.Height);
//don't draw any shadows if the light doesn't overlap with the borders of the sub
if (!MathUtils.CircleIntersectsRectangle(lightPos, TextureRange, subBorders))
{
if (chList.List.Count > 0) NeedsRecalculation = true;
chList.List.Clear();
continue;
}
//recalculate vertices if the subs have moved > 5 px relative to each other
Vector2 diff = ParentSub.WorldPosition - sub.WorldPosition;
if (!diffToSub.TryGetValue(sub, out Vector2 prevDiff))
{
diffToSub.Add(sub, diff);
NeedsRecalculation = true;
}
else if (Vector2.DistanceSquared(diff, prevDiff) > 5.0f * 5.0f)
{
diffToSub[sub] = diff;
NeedsRecalculation = true;
}
RefreshConvexHullList(chList, lightPos, sub);
}
chList = ch;
break;
}
}
//not found -> create one
if (chList == null)
{
chList = new ConvexHullList(sub);
hullsInRange.Add(chList);
NeedsRecalculation = true;
}
foreach (var ch in chList.List)
{
if (ch.LastVertexChangeTime > lastRecalculationTime && !chList.IsHidden.Contains(ch))
{
NeedsRecalculation = true;
break;
}
}
Vector2 lightPos = position;
if (ParentSub == null)
{
//light and the convexhulls are both outside
if (sub == null)
{
if (NeedsHullCheck)
{
RefreshConvexHullList(chList, lightPos, null);
}
}
//light is outside, convexhulls inside a sub
else
{
lightPos -= sub.Position;
Rectangle subBorders = sub.Borders;
subBorders.Location += sub.HiddenSubPosition.ToPoint() - new Point(0, sub.Borders.Height);
//only draw if the light overlaps with the sub
if (!MathUtils.CircleIntersectsRectangle(lightPos, TextureRange, subBorders))
{
if (chList.List.Count > 0) { NeedsRecalculation = true; }
chList.List.Clear();
return;
}
RefreshConvexHullList(chList, lightPos, sub);
}
}
else
{
//light is inside, convexhull outside
if (sub == null) { return; }
//light and convexhull are both inside the same sub
if (sub == ParentSub)
{
if (NeedsHullCheck)
{
RefreshConvexHullList(chList, lightPos, sub);
}
}
//light and convexhull are inside different subs
else
{
if (sub.DockedTo.Contains(ParentSub) && !NeedsHullCheck) { return; }
lightPos -= (sub.Position - ParentSub.Position);
Rectangle subBorders = sub.Borders;
subBorders.Location += sub.HiddenSubPosition.ToPoint() - new Point(0, sub.Borders.Height);
//don't draw any shadows if the light doesn't overlap with the borders of the sub
if (!MathUtils.CircleIntersectsRectangle(lightPos, TextureRange, subBorders))
{
if (chList.List.Count > 0) { NeedsRecalculation = true; }
chList.List.Clear();
return;
}
//recalculate vertices if the subs have moved > 5 px relative to each other
Vector2 diff = ParentSub.WorldPosition - sub.WorldPosition;
if (!diffToSub.TryGetValue(sub, out Vector2 prevDiff))
{
diffToSub.Add(sub, diff);
NeedsRecalculation = true;
}
else if (Vector2.DistanceSquared(diff, prevDiff) > 5.0f * 5.0f)
{
diffToSub[sub] = diff;
NeedsRecalculation = true;
}
RefreshConvexHullList(chList, lightPos, sub);
}
}
}
private List<Vector2> FindRaycastHits()

View File

@@ -75,10 +75,12 @@ namespace Barotrauma
if (linkedTo.Contains(entity))
{
linkedTo.Remove(entity);
entity.linkedTo.Remove(this);
}
else
{
linkedTo.Add(entity);
if (!entity.linkedTo.Contains(this)) { entity.linkedTo.Add(this); }
}
}
}

View File

@@ -30,6 +30,9 @@ namespace Barotrauma
{
return false;
}
if (!SubEditorScreen.IsLayerVisible(this)) { return false; }
return HasBody ? ShowWalls : ShowStructures;
}
}
@@ -244,8 +247,10 @@ namespace Barotrauma
public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true)
{
if (prefab.sprite == null) { return; }
if (editing)
{
if (!SubEditorScreen.IsLayerVisible(this)) { return; }
if (!HasBody && !ShowStructures) { return; }
if (HasBody && !ShowWalls) { return; }
}
@@ -273,6 +278,7 @@ namespace Barotrauma
if (prefab.sprite == null) { return; }
if (editing)
{
if (!SubEditorScreen.IsLayerVisible(this)) { return; }
if (!HasBody && !ShowStructures) { return; }
if (HasBody && !ShowWalls) { return; }
}
@@ -285,13 +291,11 @@ namespace Barotrauma
//color = Color.Lerp(color, Color.Gold, 0.5f);
color = spriteColor;
Vector2 rectSize = rect.Size.ToVector2();
if (BodyWidth > 0.0f) { rectSize.X = BodyWidth; }
if (BodyHeight > 0.0f) { rectSize.Y = BodyHeight; }
Vector2 bodyPos = WorldPosition + BodyOffset;
Vector2 bodyPos = WorldPosition + BodyOffset * Scale;
GUI.DrawRectangle(spriteBatch, new Vector2(bodyPos.X, -bodyPos.Y), rectSize.X, rectSize.Y, BodyRotation, Color.White,
thickness: Math.Max(1, (int)(2 / Screen.Selected.Cam.Zoom)));
@@ -465,7 +469,8 @@ namespace Barotrauma
public void UpdateSpriteStates(float deltaTime)
{
DecorativeSprite.UpdateSpriteStates(Prefab.DecorativeSpriteGroups, spriteAnimState, ID, deltaTime, ConditionalMatches);
if (Prefab.DecorativeSpriteGroups.Count == 0) { return; }
DecorativeSprite.UpdateSpriteStates(Prefab.DecorativeSpriteGroups, spriteAnimState, ID, deltaTime, ConditionalMatches);
foreach (int spriteGroup in Prefab.DecorativeSpriteGroups.Keys)
{
for (int i = 0; i < Prefab.DecorativeSpriteGroups[spriteGroup].Count; i++)

View File

@@ -25,14 +25,11 @@ namespace Barotrauma
public readonly bool Stream;
public readonly bool IgnoreMuffling;
public string Filename
{
get { return Sound?.Filename; }
}
public readonly string Filename;
public RoundSound(XElement element, Sound sound)
{
Filename = sound?.Filename;
Sound = sound;
Stream = sound.Stream;
Range = element.GetAttributeFloat("range", 1000.0f);

View File

@@ -146,6 +146,7 @@ namespace Barotrauma
private bool IsHidden()
{
if (!SubEditorScreen.IsLayerVisible(this)) { return false; }
if (spawnType == SpawnType.Path)
{
return (!GameMain.DebugDraw && !ShowWayPoints);
@@ -294,7 +295,7 @@ namespace Barotrauma
else
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), paddedFrame.RectTransform), TextManager.Get("Spawnpoint"), font: GUI.LargeFont);
var spawnTypeContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.2f), paddedFrame.RectTransform), isHorizontal: true)
{
Stretch = true,
@@ -318,7 +319,10 @@ namespace Barotrauma
};
var descText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), paddedFrame.RectTransform),
TextManager.Get("IDCardDescription"), font: GUI.SmallFont);
TextManager.Get("IDCardDescription"), font: GUI.SmallFont)
{
ToolTip = TextManager.Get("IDCardDescriptionTooltip")
};
GUITextBox propertyBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), descText.RectTransform, Anchor.CenterRight), IdCardDesc)
{
MaxTextLength = 150,
@@ -342,7 +346,10 @@ namespace Barotrauma
};
var idCardTagsText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), paddedFrame.RectTransform),
TextManager.Get("IDCardTags"), font: GUI.SmallFont);
TextManager.Get("IDCardTags"), font: GUI.SmallFont)
{
ToolTip = TextManager.Get("IDCardTagsTooltip")
};
propertyBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), idCardTagsText.RectTransform, Anchor.CenterRight), string.Join(", ", idCardTags))
{
MaxTextLength = 60,
@@ -414,6 +421,6 @@ namespace Barotrauma
PositionEditingHUD();
return editingHUD;
}
}
}
}

View File

@@ -78,7 +78,7 @@ namespace Barotrauma.Networking
txt = orderPrefab.GetChatMessage(orderMessageInfo.TargetCharacter?.Name, targetRoom,
givingOrderToSelf: orderMessageInfo.TargetCharacter == senderCharacter,
orderOption: orderOption,
priority: orderMessageInfo.Priority);
isNewOrder: orderMessageInfo.IsNewOrder);
if (GameMain.Client.GameStarted && Screen.Selected == GameMain.GameScreen)
{

View File

@@ -948,6 +948,9 @@ namespace Barotrauma.Networking
case ServerPacketHeader.CREW:
campaign?.ClientReadCrew(inc);
break;
case ServerPacketHeader.MEDICAL:
campaign?.MedicalClinic?.ClientRead(inc);
break;
case ServerPacketHeader.READY_CHECK:
ReadyCheck.ClientRead(inc);
break;
@@ -1116,9 +1119,9 @@ namespace Barotrauma.Networking
disconnectReason != DisconnectReason.InvalidVersion)
{
GameAnalyticsManager.AddErrorEventOnce(
"GameClient.HandleDisconnectMessage",
GameAnalyticsManager.ErrorSeverity.Debug,
"Client received a disconnect message. Reason: " + disconnectReason.ToString());
"GameClient.HandleDisconnectMessage",
GameAnalyticsManager.ErrorSeverity.Debug,
"Client received a disconnect message. Reason: " + disconnectReason.ToString());
}
if (disconnectReason == DisconnectReason.ServerFull)
@@ -1271,7 +1274,15 @@ namespace Barotrauma.Networking
private void ReadAchievement(IReadMessage inc)
{
string achievementIdentifier = inc.ReadString();
SteamAchievementManager.UnlockAchievement(achievementIdentifier);
int amount = inc.ReadInt32();
if (amount == 0)
{
SteamAchievementManager.UnlockAchievement(achievementIdentifier);
}
else
{
SteamAchievementManager.IncrementStat(achievementIdentifier, amount);
}
}
private void ReadTraitorMessage(IReadMessage inc)
@@ -1471,7 +1482,7 @@ namespace Barotrauma.Networking
serverSettings.LockAllDefaultWires = inc.ReadBoolean();
serverSettings.AllowRagdollButton = inc.ReadBoolean();
serverSettings.AllowLinkingWifiToChat = inc.ReadBoolean();
GameMain.NetLobbyScreen.UsingShuttle = inc.ReadBoolean();
bool usingShuttle = GameMain.NetLobbyScreen.UsingShuttle = inc.ReadBoolean();
GameMain.LightManager.LosMode = (LosMode)inc.ReadByte();
bool includesFinalize = inc.ReadBoolean(); inc.ReadPadBits();
GameMain.LightManager.LightingEnabled = true;
@@ -1483,6 +1494,8 @@ namespace Barotrauma.Networking
Task loadTask = null;
var roundSummary = (GUIMessageBox.MessageBoxes.Find(c => c?.UserData is RoundSummary)?.UserData) as RoundSummary;
bool isOutpost = false;
if (gameMode != GameModePreset.MultiPlayerCampaign)
{
string levelSeed = inc.ReadString();
@@ -1621,6 +1634,7 @@ namespace Barotrauma.Networking
{
GameMain.GameSession.StartRound(levelData, mirrorLevel);
}
isOutpost = levelData.Type == LevelData.LevelType.Outpost;
}
if (GameMain.Client?.ServerSettings?.Voting != null)
@@ -1740,8 +1754,7 @@ namespace Barotrauma.Networking
if (respawnAllowed)
{
bool isOutpost = GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign && Level.Loaded?.Type == LevelData.LevelType.Outpost;
respawnManager = new RespawnManager(this, GameMain.NetLobbyScreen.UsingShuttle && !isOutpost ? GameMain.NetLobbyScreen.SelectedShuttle : null);
respawnManager = new RespawnManager(this, usingShuttle && !isOutpost ? GameMain.NetLobbyScreen.SelectedShuttle : null);
}
gameStarted = true;
@@ -1872,7 +1885,7 @@ namespace Barotrauma.Networking
if (int.TryParse(ownedIndexes[i], out int index))
{
SubmarineInfo sub = GameMain.Client.ServerSubmarines[index];
if (GameMain.NetLobbyScreen.CheckIfCampaignSubMatches(sub, "owned"))
if (GameMain.NetLobbyScreen.CheckIfCampaignSubMatches(sub, NetLobbyScreen.SubmarineDeliveryData.Owned))
{
GameMain.GameSession.OwnedSubmarines.Add(sub);
}
@@ -1888,7 +1901,7 @@ namespace Barotrauma.Networking
if (int.TryParse(ownedIndexes[i], out index))
{
SubmarineInfo sub = GameMain.Client.ServerSubmarines[index];
if (GameMain.NetLobbyScreen.CheckIfCampaignSubMatches(sub, "owned"))
if (GameMain.NetLobbyScreen.CheckIfCampaignSubMatches(sub, NetLobbyScreen.SubmarineDeliveryData.Owned))
{
GameMain.NetLobbyScreen.ServerOwnedSubmarines.Add(sub);
}
@@ -2090,13 +2103,6 @@ namespace Barotrauma.Networking
string selectShuttleName = inc.ReadString();
string selectShuttleHash = inc.ReadString();
UInt16 campaignSubmarineIndexCount = inc.ReadUInt16();
List<int> campaignSubIndices = new List<int>();
for (int i = 0; i< campaignSubmarineIndexCount; i++)
{
campaignSubIndices.Add(inc.ReadUInt16());
}
bool allowSubVoting = inc.ReadBoolean();
bool allowModeVoting = inc.ReadBoolean();
@@ -2157,16 +2163,11 @@ namespace Barotrauma.Networking
if (GameMain.Client.IsServerOwner) RequestSelectMode(modeIndex);
}
if (campaignSubIndices != null)
if (GameMain.NetLobbyScreen.SelectedMode == GameModePreset.MultiPlayerCampaign)
{
GameMain.NetLobbyScreen.CampaignSubmarines = new List<SubmarineInfo>();
foreach (UInt16 campaignSubIndex in campaignSubIndices)
foreach (SubmarineInfo sub in ServerSubmarines.Where(s => !ServerSettings.HiddenSubs.Contains(s.Name)))
{
SubmarineInfo sub = GameMain.Client.ServerSubmarines[campaignSubIndex];
if (GameMain.NetLobbyScreen.CheckIfCampaignSubMatches(sub, "campaign"))
{
GameMain.NetLobbyScreen.CampaignSubmarines.Add(sub);
}
GameMain.NetLobbyScreen.CheckIfCampaignSubMatches(sub, NetLobbyScreen.SubmarineDeliveryData.Campaign);
}
}
@@ -2599,7 +2600,6 @@ namespace Barotrauma.Networking
NetLobbyScreen.FailedSubInfo failedCampaignSub = GameMain.NetLobbyScreen.FailedCampaignSubs.Find(s => s.Name == newSub.Name && s.Hash == newSub.MD5Hash.Hash);
if (failedCampaignSub != default)
{
GameMain.NetLobbyScreen.CampaignSubmarines.Add(newSub);
GameMain.NetLobbyScreen.FailedCampaignSubs.Remove(failedCampaignSub);
}

View File

@@ -100,7 +100,7 @@ namespace Barotrauma.Networking
if (!isActive) { return; }
if (steamId != hostSteamId) { return; }
Close($"SteamP2P connection failed: {error}");
OnDisconnectMessageReceived?.Invoke($"SteamP2P connection failed: {error}");
OnDisconnectMessageReceived?.Invoke($"{DisconnectReason.SteamP2PError}/SteamP2P connection failed: {error}");
}
private void OnP2PData(ulong steamId, byte[] data, int dataLength)
@@ -167,14 +167,14 @@ namespace Barotrauma.Networking
if (state == null)
{
Close("SteamP2P connection could not be established");
OnDisconnectMessageReceived?.Invoke("SteamP2P connection could not be established");
OnDisconnectMessageReceived?.Invoke(DisconnectReason.SteamP2PError.ToString());
}
else
{
if (state?.P2PSessionError != Steamworks.P2PSessionError.None)
{
Close($"SteamP2P error code: {state?.P2PSessionError}");
OnDisconnectMessageReceived?.Invoke($"SteamP2P error code: {state?.P2PSessionError}");
OnDisconnectMessageReceived?.Invoke($"{DisconnectReason.SteamP2PError}/SteamP2P error code: {state?.P2PSessionError}");
}
}
connectionStatusTimer = 1.0f;
@@ -210,7 +210,7 @@ namespace Barotrauma.Networking
if (timeout < 0.0)
{
Close("Timed out");
OnDisconnectMessageReceived?.Invoke("");
OnDisconnectMessageReceived?.Invoke(DisconnectReason.SteamP2PTimeOut.ToString());
return;
}
@@ -349,13 +349,19 @@ namespace Barotrauma.Networking
outMsg.Write((byte)PacketHeader.IsDisconnectMessage);
outMsg.Write(msg ?? "Disconnected");
Steamworks.SteamNetworking.SendP2PPacket(hostSteamId, outMsg.Buffer, outMsg.LengthBytes, 0, Steamworks.P2PSend.Reliable);
sentBytes += outMsg.LengthBytes;
try
{
Steamworks.SteamNetworking.SendP2PPacket(hostSteamId, outMsg.Buffer, outMsg.LengthBytes, 0, Steamworks.P2PSend.Reliable);
sentBytes += outMsg.LengthBytes;
}
catch (Exception e)
{
DebugConsole.ThrowError("Failed to send a disconnect message to the server using SteamP2P.", e);
}
Thread.Sleep(100);
Steamworks.SteamNetworking.ResetActions();
Steamworks.SteamNetworking.CloseP2PSessionWithUser(hostSteamId);
steamAuthTicket?.Cancel(); steamAuthTicket = null;

View File

@@ -81,16 +81,15 @@ namespace Barotrauma.Networking
public bool ContentPackagesMatch()
{
var myContentPackages = ContentPackage.AllPackages;
//make sure we have all the packages the server requires
if (ContentPackageHashes.Count != ContentPackageWorkshopIds.Count) { return false; }
for (int i = 0; i < ContentPackageWorkshopIds.Count; i++)
{
string hash = ContentPackageHashes[i];
UInt64 id = ContentPackageWorkshopIds[i];
if (!myContentPackages.Any(myPackage => myPackage.MD5hash.Hash == hash))
if (!GameMain.ServerListScreen.ContentPackagesByHash.ContainsKey(hash))
{
if (myContentPackages.Any(p => p.SteamWorkshopId == id)) { return false; }
if (GameMain.ServerListScreen.ContentPackagesByWorkshopId.ContainsKey(id)) { return false; }
if (id == 0) { return false; }
}
}
@@ -98,12 +97,6 @@ namespace Barotrauma.Networking
return true;
}
public bool ContentPackagesMatch(IEnumerable<string> myContentPackageHashes)
{
HashSet<string> contentPackageHashes = new HashSet<string>(ContentPackageHashes);
return contentPackageHashes.SetEquals(myContentPackageHashes);
}
public void CreatePreviewWindow(GUIFrame frame)
{
if (frame == null) { return; }
@@ -428,7 +421,7 @@ namespace Barotrauma.Networking
return;
}
var rules = ((Task<Dictionary<string, string>>)t).Result;
t.TryGetResult(out Dictionary<string, string> rules);
SteamManager.AssignServerRulesToServerInfo(rules, this);
onServerRulesReceived(this);

View File

@@ -94,10 +94,10 @@ namespace Barotrauma.Networking
public void ClientAdminRead(IReadMessage incMsg)
{
int count = incMsg.ReadUInt16();
for (int i = 0; i < count; i++)
while (true)
{
UInt32 key = incMsg.ReadUInt32();
if (key == 0) { break; }
if (netProperties.ContainsKey(key))
{
bool changedLocally = netProperties[key].ChangedLocally;
@@ -153,8 +153,11 @@ namespace Barotrauma.Networking
{
ReadExtraCargo(incMsg);
}
ReadHiddenSubs(incMsg);
if (requiredFlags.HasFlag(NetFlags.HiddenSubs))
{
ReadHiddenSubs(incMsg);
}
GameMain.NetLobbyScreen.UpdateSubVisibility();
bool isAdmin = incMsg.ReadBoolean();

View File

@@ -143,8 +143,7 @@ namespace Barotrauma.Steam
return;
}
currentLobby = ((Task<Steamworks.Data.Lobby?>)lobby).Result;
lobby.TryGetResult(out currentLobby);
if (currentLobby == null)
{
DebugConsole.ThrowError("Failed to create Steam lobby: returned lobby was null");
@@ -250,7 +249,7 @@ namespace Barotrauma.Steam
TaskPool.Add("JoinLobbyAsync", Steamworks.SteamMatchmaking.JoinLobbyAsync(lobbyID),
(lobby) =>
{
currentLobby = ((Task<Steamworks.Data.Lobby?>)lobby).Result;
lobby.TryGetResult(out currentLobby);
lobbyState = LobbyState.Joined;
lobbyID = (currentLobby?.Id).Value;
if (joinServer)
@@ -293,10 +292,11 @@ namespace Barotrauma.Steam
taskDone();
return;
}
var lobbies = ((Task<List<Steamworks.Data.Lobby>>)t).Result;
if (lobbies != null)
t.TryGetResult(out List<Steamworks.Data.Lobby> lobbies);
IEnumerable<CoroutineStatus> lobbyAddCoroutine()
{
foreach (var lobby in lobbies)
int i = 0;
foreach (var lobby in lobbies ?? Enumerable.Empty<Steamworks.Data.Lobby>())
{
if (string.IsNullOrEmpty(lobby.GetData("name"))) { continue; }
@@ -312,9 +312,13 @@ namespace Barotrauma.Steam
AssignLobbyDataToServerInfo(lobby, serverInfo);
addToServerList(serverInfo);
i++;
if (i >= 16) { yield return CoroutineStatus.Running; i = 0; }
}
taskDone();
yield return CoroutineStatus.Success;
}
taskDone();
CoroutineManager.StartCoroutine(lobbyAddCoroutine());
});
Steamworks.ServerList.Internet serverQuery = new Steamworks.ServerList.Internet();
@@ -344,13 +348,10 @@ namespace Barotrauma.Steam
return;
}
var rules = ((Task<Dictionary<string, string>>)t).Result;
t.TryGetResult(out Dictionary<string, string> rules);
AssignServerRulesToServerInfo(rules, serverInfo);
CrossThread.RequestExecutionOnMainThread(() =>
{
addToServerList(serverInfo);
});
addToServerList(serverInfo);
});
}
else
@@ -618,7 +619,10 @@ namespace Barotrauma.Steam
.WithLongDescription();
if (requireTags != null) { query = query.WithTags(requireTags); }
TaskPool.Add("GetSubscribedWorkshopItems", GetWorkshopItemsAsync(query), (task) => { onItemsFound?.Invoke(((Task<List<Steamworks.Ugc.Item>>)task).Result); });
TaskPool.Add("GetSubscribedWorkshopItems", GetWorkshopItemsAsync(query), (task) =>
{
task.TryGetResult(out List<Steamworks.Ugc.Item> result); onItemsFound?.Invoke(result);
});
}
public static void GetPopularWorkshopItems(Action<IList<Steamworks.Ugc.Item>> onItemsFound, int amount, List<string> requireTags = null)
@@ -632,7 +636,7 @@ namespace Barotrauma.Steam
TaskPool.Add("GetPopularWorkshopItems", GetWorkshopItemsAsync(query, amount, (item) => !item.IsSubscribed), (task) =>
{
var entries = ((Task<List<Steamworks.Ugc.Item>>)task).Result;
task.TryGetResult(out List<Steamworks.Ugc.Item> entries);
//count the number of each unique tag
foreach (var item in entries)
@@ -677,7 +681,10 @@ namespace Barotrauma.Steam
.WithLongDescription();
if (requireTags != null) query.WithTags(requireTags);
TaskPool.Add("GetPublishedWorkshopItems", GetWorkshopItemsAsync(query), (task) => { onItemsFound?.Invoke(((Task<List<Steamworks.Ugc.Item>>)task).Result); });
TaskPool.Add("GetPublishedWorkshopItems", GetWorkshopItemsAsync(query), (task) =>
{
task.TryGetResult(out List<Steamworks.Ugc.Item> result); onItemsFound?.Invoke(result);
});
}
private static readonly HashSet<ulong> pendingWorkshopSubscriptions = new HashSet<ulong>();
@@ -724,7 +731,7 @@ namespace Barotrauma.Steam
}
else
{
var item = ((Task<Steamworks.Ugc.Item?>)t).Result;
t.TryGetResult(out Steamworks.Ugc.Item? item);
if (item != null)
{
if (item?.IsInstalled ?? false)
@@ -1077,7 +1084,7 @@ namespace Barotrauma.Steam
GameMain.SteamWorkshopScreen?.SetReinstallButtonStatus(item, true, GUI.Style.Red);
return;
}
string errorMsg = ((Task<string>)task).Result;
task.TryGetResult(out string errorMsg);
if (!string.IsNullOrWhiteSpace(errorMsg))
{
DebugConsole.ThrowError($"Failed to copy \"{item.Title}\": {errorMsg}");

View File

@@ -246,6 +246,8 @@ namespace Barotrauma
foreach (string saveFile in saveFiles)
{
if (string.IsNullOrEmpty(saveFile)) { continue; }
string fileName = saveFile;
string subName = "";
string saveTime = "";

View File

@@ -45,6 +45,8 @@ namespace Barotrauma
public UpgradeStore UpgradeStore { get; set; }
public MedicalClinicUI MedicalClinic { get; set; }
public CampaignUI(CampaignMode campaign, GUIComponent container)
{
Campaign = campaign;
@@ -270,6 +272,9 @@ namespace Barotrauma
// Submarine buying tab
tabs[(int)CampaignMode.InteractionType.PurchaseSub] = new GUIFrame(new RectTransform(Vector2.One, container.RectTransform, Anchor.TopLeft), color: Color.Black * 0.9f);
tabs[(int)CampaignMode.InteractionType.MedicalClinic] = new GUIFrame(new RectTransform(Vector2.One, container.RectTransform), color: Color.Black * 0.9f);
MedicalClinic = new MedicalClinicUI(Campaign.MedicalClinic, GetTabContainer(CampaignMode.InteractionType.MedicalClinic));
// mission info -------------------------------------------------------------------------
locationInfoPanel = new GUIFrame(new RectTransform(new Vector2(0.35f, 0.75f), GetTabContainer(CampaignMode.InteractionType.Map).RectTransform, Anchor.CenterRight)
@@ -355,6 +360,10 @@ namespace Barotrauma
case CampaignMode.InteractionType.Store:
Store?.Update(deltaTime);
break;
case CampaignMode.InteractionType.MedicalClinic:
MedicalClinic?.Update(deltaTime);
break;
}
}

View File

@@ -247,16 +247,6 @@ namespace Barotrauma
return msgBox;
}
private void NotifyPrompt(string header, string body)
{
GUIMessageBox msgBox = new GUIMessageBox(header, body, new[] { TextManager.Get("Ok") }, new Vector2(0.2f, 0.175f), minSize: new Point(300, 175));
msgBox.Buttons[0].OnClicked = delegate
{
msgBox.Close();
return true;
};
}
private bool SaveProjectToFile(GUIButton button, object o)
{
string directory = Path.GetFullPath("EventProjects");
@@ -315,7 +305,7 @@ namespace Barotrauma
CreateNodes(prefab.ConfigElement, ref hadNodes);
if (!hadNodes)
{
NotifyPrompt(TextManager.Get("EventEditor.RandomGenerationHeader"), TextManager.Get("EventEditor.RandomGenerationBody"));
GUI.NotifyPrompt(TextManager.Get("EventEditor.RandomGenerationHeader"), TextManager.Get("EventEditor.RandomGenerationBody"));
}
return true;
});

View File

@@ -221,7 +221,6 @@ namespace Barotrauma
public SubmarineInfo SelectedShuttle => ShuttleList.SelectedData as SubmarineInfo;
public MultiPlayerCampaignSetupUI CampaignSetupUI;
public List<SubmarineInfo> CampaignSubmarines = new List<SubmarineInfo>();
// Passed onto the gamesession when created
public List<SubmarineInfo> ServerOwnedSubmarines = new List<SubmarineInfo>();
@@ -611,6 +610,7 @@ namespace Barotrauma
{
OnClicked = (btn, obj) =>
{
if (GameMain.Client == null) { return true; }
GameMain.Client.RequestStartRound();
CoroutineManager.StartCoroutine(WaitForStartRound(StartButton), "WaitForStartRound");
return true;
@@ -628,7 +628,7 @@ namespace Barotrauma
{
OnSelected = (tickBox) =>
{
GameMain.Client.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, autoRestart: tickBox.Selected);
GameMain.Client?.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, autoRestart: tickBox.Selected);
return true;
}
};
@@ -655,6 +655,7 @@ namespace Barotrauma
};
ServerName.OnDeselected += (textBox, key) =>
{
if (GameMain.Client == null) { return; }
if (!textBox.Readonly)
{
GameMain.Client.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Name);
@@ -669,6 +670,7 @@ namespace Barotrauma
ToolTip = TextManager.Get("addtofavorites"),
OnSelected = (tickbox) =>
{
if (GameMain.Client == null) { return true; }
ServerInfo info = GameMain.Client.ServerSettings.GetServerListInfo();
if (tickbox.Selected)
{
@@ -766,6 +768,7 @@ namespace Barotrauma
};
ServerMessage.OnDeselected += (textBox, key) =>
{
if (GameMain.Client == null) { return; }
if (!textBox.Readonly)
{
GameMain.Client?.ServerSettings?.ClientAdminWrite(ServerSettings.NetFlags.Message);
@@ -849,7 +852,7 @@ namespace Barotrauma
Selected = true,
OnSelected = (GUITickBox box) =>
{
GameMain.Client.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, useRespawnShuttle: box.Selected);
GameMain.Client?.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, useRespawnShuttle: box.Selected);
return true;
}
};
@@ -868,7 +871,7 @@ namespace Barotrauma
{
OnSelected = (component, obj) =>
{
GameMain.Client.RequestSelectSub(component.Parent.GetChildIndex(component), isShuttle: true);
GameMain.Client?.RequestSelectSub(component.Parent.GetChildIndex(component), isShuttle: true);
return true;
}
};
@@ -970,7 +973,7 @@ namespace Barotrauma
{
OnClicked = (_, __) =>
{
GameMain.Client.RequestSelectMode(ModeList.Content.GetChildIndex(ModeList.Content.GetChildByUserData(GameModePreset.Sandbox)));
GameMain.Client?.RequestSelectMode(ModeList.Content.GetChildIndex(ModeList.Content.GetChildByUserData(GameModePreset.Sandbox)));
return true;
}
};
@@ -1026,7 +1029,7 @@ namespace Barotrauma
{
int missionTypeOr = tickbox.Selected ? (int)tickbox.UserData : (int)MissionType.None;
int missionTypeAnd = (int)MissionType.All & (!tickbox.Selected ? (~(int)tickbox.UserData) : (int)MissionType.All);
GameMain.Client.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, (int)missionTypeOr, (int)missionTypeAnd);
GameMain.Client?.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, (int)missionTypeOr, (int)missionTypeAnd);
return true;
}
};
@@ -1059,7 +1062,7 @@ namespace Barotrauma
SeedBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), seedLabel.RectTransform, Anchor.CenterRight));
SeedBox.OnDeselected += (textBox, key) =>
{
GameMain.Client.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.LevelSeed);
GameMain.Client?.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.LevelSeed);
};
clientDisabledElements.Add(SeedBox);
LevelSeed = ToolBox.RandomSeed(8);
@@ -1080,7 +1083,7 @@ namespace Barotrauma
ToolTip = TextManager.Get("leveldifficultyexplanation"),
OnReleased = (scrollbar, value) =>
{
GameMain.Client.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, levelDifficulty: scrollbar.BarScrollValue);
GameMain.Client?.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, levelDifficulty: scrollbar.BarScrollValue);
return true;
}
};
@@ -1112,8 +1115,7 @@ namespace Barotrauma
{
OnClicked = (button, obj) =>
{
GameMain.Client.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, traitorSetting: -1);
GameMain.Client?.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, traitorSetting: -1);
return true;
}
};
@@ -1124,8 +1126,7 @@ namespace Barotrauma
{
OnClicked = (button, obj) =>
{
GameMain.Client.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, traitorSetting: 1);
GameMain.Client?.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, traitorSetting: 1);
return true;
}
};
@@ -1143,7 +1144,7 @@ namespace Barotrauma
{
OnClicked = (button, obj) =>
{
GameMain.Client.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, botCount: -1);
GameMain.Client?.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, botCount: -1);
return true;
}
};
@@ -1153,7 +1154,7 @@ namespace Barotrauma
{
OnClicked = (button, obj) =>
{
GameMain.Client.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, botCount: 1);
GameMain.Client?.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, botCount: 1);
return true;
}
};
@@ -1169,7 +1170,7 @@ namespace Barotrauma
{
OnClicked = (button, obj) =>
{
GameMain.Client.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, botSpawnMode: -1);
GameMain.Client?.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, botSpawnMode: -1);
return true;
}
};
@@ -1179,7 +1180,7 @@ namespace Barotrauma
{
OnClicked = (button, obj) =>
{
GameMain.Client.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, botSpawnMode: 1);
GameMain.Client?.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, botSpawnMode: 1);
return true;
}
};
@@ -3576,7 +3577,13 @@ namespace Barotrauma
return false;
}
public bool CheckIfCampaignSubMatches(SubmarineInfo serverSubmarine, string deliveryData)
public enum SubmarineDeliveryData
{
Owned,
Campaign
}
public bool CheckIfCampaignSubMatches(SubmarineInfo serverSubmarine, SubmarineDeliveryData deliveryData)
{
if (GameMain.Client == null) return false;
@@ -3630,11 +3637,11 @@ namespace Barotrauma
{
FailedSubInfo fileInfo = (FailedSubInfo)userdata;
if (deliveryData == "owned") //owned!!!!
if (deliveryData == SubmarineDeliveryData.Owned)
{
FailedOwnedSubs.Add(fileInfo);
}
else if (deliveryData == "campaign")
else if (deliveryData == SubmarineDeliveryData.Campaign)
{
FailedCampaignSubs.Add(fileInfo);
}

View File

@@ -7,6 +7,7 @@ using Microsoft.Xna.Framework.Graphics;
using RestSharp;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
@@ -21,6 +22,11 @@ namespace Barotrauma
//how often the client is allowed to refresh servers
private readonly TimeSpan AllowedRefreshInterval = new TimeSpan(0, 0, 3);
public ImmutableDictionary<UInt64, ContentPackage> ContentPackagesByWorkshopId { get; private set; }
= ImmutableDictionary<UInt64, ContentPackage>.Empty;
public ImmutableDictionary<string, ContentPackage> ContentPackagesByHash { get; private set; }
= ImmutableDictionary<string, ContentPackage>.Empty;
private GUIFrame menu;
private GUIListBox serverList;
@@ -1011,6 +1017,17 @@ namespace Barotrauma
public override void Select()
{
base.Select();
ContentPackagesByWorkshopId = ContentPackage.AllPackages
.Select(p => new KeyValuePair<UInt64, ContentPackage>(p.SteamWorkshopId, p))
.Where(p => p.Key != 0)
.GroupBy(x => x.Key).Select(g => g.First())
.ToImmutableDictionary();
ContentPackagesByHash = ContentPackage.AllPackages
.Select(p => new KeyValuePair<string, ContentPackage>(p.MD5hash.Hash, p))
.GroupBy(x => x.Key).Select(g => g.First())
.ToImmutableDictionary();
SelectedTab = ServerListTab.All;
LoadServerFilters(GameMain.Config.ServerFilterElement);
if (GameSettings.ShowOffensiveServerPrompt)
@@ -1039,6 +1056,8 @@ namespace Barotrauma
public override void Deselect()
{
ContentPackagesByWorkshopId = ImmutableDictionary<UInt64, ContentPackage>.Empty;
ContentPackagesByHash = ImmutableDictionary<string, ContentPackage>.Empty;
base.Deselect();
GameMain.Config.SaveNewPlayerConfig();
@@ -1491,7 +1510,7 @@ namespace Barotrauma
}
TaskPool.Add($"Get{avatarSize}AvatarAsync", avatarFunc(friend.Id), (task) =>
{
Steamworks.Data.Image? img = ((Task<Steamworks.Data.Image?>)task).Result;
if (!task.TryGetResult(out Steamworks.Data.Image? img)) { return; }
if (!img.HasValue) { return; }
var avatarImage = img.Value;
@@ -2203,7 +2222,7 @@ namespace Barotrauma
TaskPool.PrintTaskExceptions(t, $"Failed to retrieve Workshop item info (ID {entry.Id})");
return;
}
Steamworks.Ugc.Item? item = ((Task<Steamworks.Ugc.Item?>)t).Result;
t.TryGetResult(out Steamworks.Ugc.Item? item);
if (!item.HasValue)
{
@@ -2313,7 +2332,7 @@ namespace Barotrauma
{
var info = obj.Item1;
var text = obj.Item2;
info.Ping = ((Task<int>)rtt).Result; info.PingChecked = true;
rtt.TryGetResult(out info.Ping); info.PingChecked = true;
text.TextColor = GetPingTextColor(info.Ping);
text.Text = info.Ping > -1 ? info.Ping.ToString() : "?";
lock (activePings)

View File

@@ -854,7 +854,7 @@ namespace Barotrauma
(var it, var lb) = tuple;
if (lb.Content.FindChild(item)?.GetChildByUserData("previewimage") is GUIImage previewImage)
{
previewImage.Sprite = ((Task<Sprite>)task).Result;
if (task.TryGetResult(out Sprite sprite)) { previewImage.Sprite = sprite; }
}
else
{

View File

@@ -99,6 +99,10 @@ namespace Barotrauma
private GUIFrame previouslyUsedPanel;
private GUIListBox previouslyUsedList;
private GUIButton visibilityButton;
private GUIFrame layerPanel;
private GUIListBox layerList;
private GUIFrame undoBufferPanel;
private GUIFrame undoBufferDisclaimer;
private GUIListBox undoBufferList;
@@ -234,6 +238,8 @@ namespace Barotrauma
public bool WiringMode => mode == Mode.Wiring;
public static readonly Dictionary<string, bool> Layers = new Dictionary<string, bool>();
public SubEditorScreen()
{
cam = new Camera
@@ -320,19 +326,34 @@ namespace Barotrauma
new GUIFrame(new RectTransform(new Vector2(0.01f, 0.9f), paddedTopPanel.RectTransform), style: "VerticalLine");
var visibilityButton = new GUIButton(new RectTransform(new Vector2(0.9f, 0.9f), paddedTopPanel.RectTransform, scaleBasis: ScaleBasis.BothHeight), "", style: "SetupVisibilityButton")
visibilityButton = new GUIButton(new RectTransform(new Vector2(0.9f, 0.9f), paddedTopPanel.RectTransform, scaleBasis: ScaleBasis.BothHeight), "", style: "SetupVisibilityButton")
{
ToolTip = TextManager.Get("SubEditorVisibilityButton") + '\n' + TextManager.Get("SubEditorVisibilityToolTip"),
OnClicked = (btn, userData) =>
{
previouslyUsedPanel.Visible = false;
undoBufferPanel.Visible = false;
layerPanel.Visible = false;
showEntitiesPanel.Visible = !showEntitiesPanel.Visible;
showEntitiesPanel.RectTransform.AbsoluteOffset = new Point(Math.Max(Math.Max(btn.Rect.X, entityCountPanel.Rect.Right), saveAssemblyFrame.Rect.Right), TopPanel.Rect.Height);
return true;
}
};
new GUIButton(new RectTransform(new Vector2(0.9f, 0.9f), paddedTopPanel.RectTransform, scaleBasis: ScaleBasis.BothHeight), "", style: "EditorLayerButton")
{
ToolTip = TextManager.Get("editor.layer.button") + '\n' + TextManager.Get("editor.layer.tooltip"),
OnClicked = (btn, userData) =>
{
previouslyUsedPanel.Visible = false;
showEntitiesPanel.Visible = false;
undoBufferPanel.Visible = false;
layerPanel.Visible = !layerPanel.Visible;
layerPanel.RectTransform.AbsoluteOffset = new Point(Math.Max(Math.Max(btn.Rect.X, entityCountPanel.Rect.Right), saveAssemblyFrame.Rect.Right), TopPanel.Rect.Height);
return true;
}
};
var previouslyUsedButton = new GUIButton(new RectTransform(new Vector2(0.9f, 0.9f), paddedTopPanel.RectTransform, scaleBasis: ScaleBasis.BothHeight), "", style: "RecentlyUsedButton")
{
ToolTip = TextManager.Get("PreviouslyUsedLabel"),
@@ -340,6 +361,7 @@ namespace Barotrauma
{
showEntitiesPanel.Visible = false;
undoBufferPanel.Visible = false;
layerPanel.Visible = false;
previouslyUsedPanel.Visible = !previouslyUsedPanel.Visible;
previouslyUsedPanel.RectTransform.AbsoluteOffset = new Point(Math.Max(Math.Max(btn.Rect.X, entityCountPanel.Rect.Right), saveAssemblyFrame.Rect.Right), TopPanel.Rect.Height);
return true;
@@ -353,6 +375,7 @@ namespace Barotrauma
{
showEntitiesPanel.Visible = false;
previouslyUsedPanel.Visible = false;
layerPanel.Visible = false;
undoBufferPanel.Visible = !undoBufferPanel.Visible;
undoBufferPanel.RectTransform.AbsoluteOffset = new Point(Math.Max(Math.Max(btn.Rect.X, entityCountPanel.Rect.Right), saveAssemblyFrame.Rect.Right), TopPanel.Rect.Height);
return true;
@@ -484,14 +507,81 @@ namespace Barotrauma
//-----------------------------------------------
layerPanel = new GUIFrame(new RectTransform(new Vector2(0.175f, 0.4f), GUI.Canvas))
{
Visible = false
};
GUILayoutGroup layerGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.9f), layerPanel.RectTransform, anchor: Anchor.Center));
layerList = new GUIListBox(new RectTransform(new Vector2(1f, 0.8f), layerGroup.RectTransform))
{
ScrollBarVisible = true,
AutoHideScrollBar = false,
OnSelected = (component, o) =>
{
if (!(o is string layer)) { return false; }
MapEntity.SelectedList.Clear();
foreach (MapEntity entity in MapEntity.mapEntityList.Where(me => !me.Removed && me.Layer == layer))
{
if (entity.IsSelected) { continue; }
MapEntity.SelectedList.Add(entity);
}
return true;
}
};
GUILayoutGroup layerButtonGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.2f), layerGroup.RectTransform));
GUILayoutGroup layerButtonTopGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.5f), layerButtonGroup.RectTransform), isHorizontal: true);
GUIButton layerAddButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1f), layerButtonTopGroup.RectTransform), text: TextManager.Get("editor.layer.newlayer"), style: "GUIButtonFreeScale")
{
OnClicked = (button, o) =>
{
CreateNewLayer(null, MapEntity.SelectedList.ToList());
return true;
}
};
GUIButton layerDeleteButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1f), layerButtonTopGroup.RectTransform), text: TextManager.Get("editor.layer.deletelayer"), style: "GUIButtonFreeScale")
{
OnClicked = (button, o) =>
{
if (layerList.SelectedData is string layer)
{
RenameLayer(layer, null);
}
return true;
}
};
GUIButton layerRenameButton = new GUIButton(new RectTransform(new Vector2(1f, 0.5f), layerButtonGroup.RectTransform), text: TextManager.Get("editor.layer.renamelayer"), style: "GUIButtonFreeScale")
{
OnClicked = (button, o) =>
{
if (layerList.SelectedData is string layer)
{
GUI.PromptTextInput(TextManager.Get("editor.layer.renamelayer"), layer, newName =>
{
RenameLayer(layer, newName);
});
}
return true;
}
};
Vector2 subPanelSize = new Vector2(0.925f, 0.9f);
undoBufferPanel = new GUIFrame(new RectTransform(new Vector2(0.15f, 0.2f), GUI.Canvas) { MinSize = new Point(200, 200) })
{
Visible = false
};
Vector2 undoSize = new Vector2(0.925f, 0.9f);
undoBufferList = new GUIListBox(new RectTransform(undoSize, undoBufferPanel.RectTransform, Anchor.Center))
undoBufferList = new GUIListBox(new RectTransform(subPanelSize, undoBufferPanel.RectTransform, Anchor.Center))
{
ScrollBarVisible = true,
OnSelected = (_, userData) =>
@@ -522,7 +612,7 @@ namespace Barotrauma
}
};
undoBufferDisclaimer = new GUIFrame(new RectTransform(undoSize, undoBufferPanel.RectTransform, Anchor.Center), style: null)
undoBufferDisclaimer = new GUIFrame(new RectTransform(subPanelSize, undoBufferPanel.RectTransform, Anchor.Center), style: null)
{
Color = Color.Black,
Visible = false
@@ -1354,7 +1444,7 @@ namespace Barotrauma
TimeSpan timeInEditor = DateTime.Now - editorSelectedTime;
#if USE_STEAM
Steam.SteamManager.IncrementStat("hoursineditor", (float)timeInEditor.TotalHours);
SteamAchievementManager.IncrementStat("hoursineditor", (float)timeInEditor.TotalHours);
#endif
GUI.ForceMouseOn(null);
@@ -1400,6 +1490,7 @@ namespace Barotrauma
});
ClearFilter();
ClearLayers();
}
private void CreateDummyCharacter()
@@ -1752,11 +1843,11 @@ namespace Barotrauma
DebugConsole.ThrowError($"Saving the preview image of the submarine \"{Submarine.MainSub.Info.Name}\" failed.", e);
savePreviewImage = false;
}
Submarine.MainSub.SaveAs(savePath, savePreviewImage ? imgStream : null);
Submarine.MainSub.TrySaveAs(savePath, savePreviewImage ? imgStream : null);
}
else
{
Submarine.MainSub.SaveAs(savePath);
Submarine.MainSub.TrySaveAs(savePath);
}
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
@@ -2890,6 +2981,8 @@ namespace Barotrauma
};
adjustLightsPrompt.Buttons[1].OnClicked += adjustLightsPrompt.Close;
}
ReconstructLayers();
}
private void TryDeleteSub(SubmarineInfo sub)
@@ -3075,6 +3168,8 @@ namespace Barotrauma
if (container == null || container.DrawInventory) { target = item; }
}
bool hasTargets = targets.Count > 0;
// Holding shift brings up special context menu options
if (PlayerInput.IsShiftDown())
{
@@ -3083,7 +3178,7 @@ namespace Barotrauma
new ContextMenuOption("SubEditor.ToggleTransparency", isEnabled: true, onSelected: () => TransparentWiringMode = !TransparentWiringMode),
new ContextMenuOption("SubEditor.ToggleGrid", isEnabled: true, onSelected: () => ShouldDrawGrid = !ShouldDrawGrid),
new ContextMenuOption("SubEditor.PasteAssembly", isEnabled: true, () => PasteAssembly()),
new ContextMenuOption("Editor.SelectSame", isEnabled: targets.Count > 0, onSelected: delegate
new ContextMenuOption("Editor.SelectSame", isEnabled: hasTargets, onSelected: delegate
{
bool doorGapSelected = targets.Any(t => t is Gap gap && gap.ConnectedDoor != null);
foreach (MapEntity match in MapEntity.mapEntityList.Where(e => e.prefab != null && targets.Any(t => t.prefab?.Identifier == e.prefab.Identifier) && !MapEntity.SelectedList.Contains(e)))
@@ -3115,12 +3210,44 @@ namespace Barotrauma
}
else
{
List<ContextMenuOption> availableLayerOptions = new List<ContextMenuOption>
{
new ContextMenuOption("editor.layer.nolayer", true, onSelected: () => { MoveToLayer(null, targets); })
};
availableLayerOptions.AddRange(Layers.Select(layer => new ContextMenuOption(layer.Key, true, onSelected: () => { MoveToLayer(layer.Key, targets); })));
ContextMenuOption[] layerOptions =
{
new ContextMenuOption("editor.layer.movetolayer", isEnabled: hasTargets, availableLayerOptions.ToArray()),
new ContextMenuOption("editor.layer.createlayer", isEnabled: hasTargets, onSelected: () => { CreateNewLayer(null, targets); }),
new ContextMenuOption("editor.layer.selectall", isEnabled: hasTargets, onSelected: () =>
{
foreach (MapEntity match in MapEntity.mapEntityList.Where(e => targets.Any(t => !string.IsNullOrWhiteSpace(t.Layer) && t.Layer == e.Layer && !MapEntity.SelectedList.Contains(e))))
{
if (MapEntity.SelectedList.Contains(match)) { continue; }
MapEntity.SelectedList.Add(match);
}
}),
new ContextMenuOption("editor.layer.openlayermenu", isEnabled: true, onSelected: () =>
{
if (visibilityButton is null) { return; }
previouslyUsedPanel.Visible = false;
undoBufferPanel.Visible = false;
showEntitiesPanel.Visible = false;
layerPanel.Visible = !layerPanel.Visible;
layerPanel.RectTransform.AbsoluteOffset = new Point(Math.Max(Math.Max(visibilityButton.Rect.X, entityCountPanel.Rect.Right), saveAssemblyFrame.Rect.Right), TopPanel.Rect.Height);
})
};
GUIContextMenu.CreateContextMenu(
new ContextMenuOption("label.openlabel", isEnabled: target != null, onSelected: () => OpenItem(target)),
new ContextMenuOption("editor.cut", isEnabled: targets.Count > 0, onSelected: () => MapEntity.Cut(targets)),
new ContextMenuOption("editor.copytoclipboard", isEnabled: targets.Count > 0, onSelected: () => MapEntity.Copy(targets)),
new ContextMenuOption("editor.paste", isEnabled: MapEntity.CopiedList.Any(), onSelected: () => MapEntity.Paste(cam.ScreenToWorld(PlayerInput.MousePosition))),
new ContextMenuOption("delete", isEnabled: targets.Count > 0, onSelected: delegate
new ContextMenuOption("label.openlabel", isEnabled: target != null, onSelected: () => OpenItem(target)),
new ContextMenuOption("editor.layer", isEnabled: hasTargets, layerOptions),
new ContextMenuOption("editor.cut", isEnabled: hasTargets, onSelected: () => MapEntity.Cut(targets)),
new ContextMenuOption("editor.copytoclipboard", isEnabled: hasTargets, onSelected: () => MapEntity.Copy(targets)),
new ContextMenuOption("editor.paste", isEnabled: MapEntity.CopiedList.Any(), onSelected: () => MapEntity.Paste(cam.ScreenToWorld(PlayerInput.MousePosition))),
new ContextMenuOption("delete", isEnabled: hasTargets, onSelected: delegate
{
StoreCommand(new AddOrDeleteCommand(targets, true));
foreach (var me in targets)
@@ -3131,6 +3258,76 @@ namespace Barotrauma
}
}
private void MoveToLayer(string layer, List<MapEntity> content)
{
layer ??= string.Empty;
foreach (MapEntity entity in content)
{
entity.Layer = layer;
}
}
private void CreateNewLayer(string name, List<MapEntity> content)
{
if (string.IsNullOrWhiteSpace(name))
{
name = TextManager.Get("editor.layer.newlayer");
}
string incrementedName = name;
for (int i = 1; Layers.ContainsKey(incrementedName); i++)
{
incrementedName = $"{name} ({i})";
}
name = incrementedName;
if (content != null)
{
MoveToLayer(name, content);
}
Layers.Add(name, true);
UpdateLayerPanel();
}
private void RenameLayer(string original, string newName)
{
Layers.Remove(original);
foreach (MapEntity entity in MapEntity.mapEntityList.Where(entity => entity.Layer == original))
{
entity.Layer = newName ?? string.Empty;
}
if (!string.IsNullOrWhiteSpace(newName))
{
Layers.TryAdd(newName, true);
}
UpdateLayerPanel();
}
private void ReconstructLayers()
{
ClearLayers();
foreach (MapEntity entity in MapEntity.mapEntityList)
{
if (!string.IsNullOrWhiteSpace(entity.Layer))
{
Layers.TryAdd(entity.Layer, true);
}
}
UpdateLayerPanel();
}
private void ClearLayers()
{
Layers.Clear();
UpdateLayerPanel();
}
private void PasteAssembly(string text = null, Vector2? pos = null)
{
pos ??= cam.ScreenToWorld(PlayerInput.MousePosition);
@@ -4044,6 +4241,7 @@ namespace Barotrauma
previouslyUsedPanel.AddToGUIUpdateList();
undoBufferPanel.AddToGUIUpdateList();
entityCountPanel.AddToGUIUpdateList();
layerPanel.AddToGUIUpdateList();
TopPanel.AddToGUIUpdateList();
if (WiringMode)
@@ -4147,9 +4345,55 @@ namespace Barotrauma
GameMain.SubEditorScreen.UpdateUndoHistoryPanel();
}
private void UpdateLayerPanel()
{
if (layerPanel is null || layerList is null) { return; }
layerList.Content.ClearChildren();
layerList.Deselect();
foreach (var (layer, isVisible) in Layers)
{
GUIFrame parent = new GUIFrame(new RectTransform(new Vector2(1f, 0.1f), layerList.Content.RectTransform), style: "ListBoxElement")
{
UserData = layer
};
GUILayoutGroup layerGroup = new GUILayoutGroup(new RectTransform(Vector2.One, parent.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
GUITickBox layerVisibleButton = new GUITickBox(new RectTransform(Vector2.One, layerGroup.RectTransform, scaleBasis: ScaleBasis.BothHeight), string.Empty)
{
Selected = isVisible,
OnSelected = box =>
{
if (!Layers.TryGetValue(layer, out bool _))
{
UpdateLayerPanel();
return false;
}
Layers[layer] = box.Selected;
return true;
}
};
layerGroup.Recalculate();
new GUITextBlock(new RectTransform(new Vector2(1.0f - layerVisibleButton.RectTransform.RelativeSize.X, 1f), layerGroup.RectTransform), layer, textAlignment: Alignment.CenterLeft)
{
CanBeFocused = false
};
layerGroup.Recalculate();
}
layerList.RecalculateChildren();
}
public void UpdateUndoHistoryPanel()
{
if (undoBufferPanel == null) { return; }
if (undoBufferPanel is null) { return; }
undoBufferDisclaimer.Visible = mode == Mode.Wiring;
@@ -4203,7 +4447,7 @@ namespace Barotrauma
public override void Update(double deltaTime)
{
SkipInventorySlotUpdate = false;
ImageManager.Update((float) deltaTime);
ImageManager.Update((float)deltaTime);
if (GameMain.GraphicsWidth != screenResolution.X || GameMain.GraphicsHeight != screenResolution.Y)
{
@@ -4720,6 +4964,11 @@ namespace Barotrauma
if (!saveAssemblyFrame.Rect.Contains(PlayerInput.MousePosition) && dummyCharacter?.SelectedConstruction == null && !WiringMode && GUI.MouseOn == null)
{
if (layerList is { Visible: true } && GUI.KeyboardDispatcher.Subscriber == layerList)
{
GUI.KeyboardDispatcher.Subscriber = null;
}
MapEntity.UpdateSelecting(cam);
}
@@ -4998,7 +5247,7 @@ namespace Barotrauma
var prevScissorRect = GameMain.Instance.GraphicsDevice.ScissorRectangle;
Rectangle subDimensions = Submarine.MainSub.CalculateDimensions(false);
Rectangle subDimensions = Submarine.MainSub.CalculateDimensions(onlyHulls: false);
Vector2 viewPos = subDimensions.Center.ToVector2();
float scale = Math.Min(width / (float)subDimensions.Width, height / (float)subDimensions.Height);
@@ -5087,5 +5336,19 @@ namespace Barotrauma
public static bool IsSubEditor() => Screen.Selected is SubEditorScreen && !Submarine.Unloading;
public static bool IsWiringMode() => Screen.Selected == GameMain.SubEditorScreen && GameMain.SubEditorScreen.WiringMode && !Submarine.Unloading;
public static bool IsLayerVisible(MapEntity entity)
{
if (!IsSubEditor()) { return true; }
if (string.IsNullOrWhiteSpace(entity.Layer)) { return true; }
if (!Layers.TryGetValue(entity.Layer, out bool isVisible))
{
Layers.TryAdd(entity.Layer, true);
return true;
}
return isVisible;
}
}
}

View File

@@ -23,6 +23,7 @@ namespace Barotrauma
private Submarine? submarine;
private Character? dummyCharacter;
public static Effect BlueprintEffect;
private GUIFrame container;
private TabMenu tabMenu;
@@ -42,21 +43,25 @@ namespace Barotrauma
return true;
}
};
}
public override void Select()
{
base.Select();
container = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), style: "InnerGlow", color: Color.Black);
var tab = new GUIFrame(new RectTransform(Vector2.One, container.RectTransform), color: Color.Black * 0.9f);
MedicalClinicUI clinic = new MedicalClinicUI(new MedicalClinic(null!), tab);
clinic.RequestLatestPending();
if (dummyCharacter is { Removed: false })
{
dummyCharacter?.Remove();
}
dummyCharacter = Character.Create(CharacterPrefab.HumanSpeciesName, Vector2.Zero, "", id: Entity.DummyID, hasAi: false);
dummyCharacter.Info.Job = new Job(JobPrefab.Prefabs.Where(jp => TalentTree.JobTalentTrees.ContainsKey(jp.Identifier)).GetRandom());
dummyCharacter.Info.Name = "Galldren";
dummyCharacter.Inventory.CreateSlots();
// dummyCharacter = Character.Create(CharacterPrefab.HumanSpeciesName, Vector2.Zero, "", id: Entity.DummyID, hasAi: false);
// dummyCharacter.Info.Job = new Job(JobPrefab.Prefabs.Where(jp => TalentTree.JobTalentTrees.ContainsKey(jp.Identifier)).GetRandom());
// dummyCharacter.Info.Name = "Galldren";
// dummyCharacter.Inventory.CreateSlots();
Character.Controlled = dummyCharacter;
GameMain.World.ProcessChanges();
@@ -67,9 +72,9 @@ namespace Barotrauma
public override void AddToGUIUpdateList()
{
Frame.AddToGUIUpdateList();
CharacterHUD.AddToGUIUpdateList(dummyCharacter);
dummyCharacter?.SelectedConstruction?.AddToGUIUpdateList();
tabMenu.AddToGUIUpdateList();
container.AddToGUIUpdateList();
// CharacterHUD.AddToGUIUpdateList(dummyCharacter);
// dummyCharacter?.SelectedConstruction?.AddToGUIUpdateList();
}
public override void Update(double deltaTime)
@@ -92,12 +97,12 @@ namespace Barotrauma
graphics.Clear(BackgroundColor);
spriteBatch.Begin(SpriteSortMode.BackToFront, transformMatrix: Cam.Transform);
miniMapItem?.Draw(spriteBatch, false);
if (dummyCharacter is { } dummy)
{
dummyCharacter.DrawFront(spriteBatch, Cam);
dummyCharacter.Draw(spriteBatch, Cam);
}
// miniMapItem?.Draw(spriteBatch, false);
// if (dummyCharacter is { } dummy)
// {
// dummyCharacter.DrawFront(spriteBatch, Cam);
// dummyCharacter.Draw(spriteBatch, Cam);
// }
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState);

View File

@@ -567,8 +567,6 @@ namespace Barotrauma
{
if (SetPropertyValue(property, entity, numInput.FloatValue))
{
// This causes stack overflow. What's the purpose of it?
//numInput.FloatValue = (float)property.GetValue(entity);
TrySendNetworkUpdate(entity, property);
}
};
@@ -674,7 +672,16 @@ namespace Barotrauma
Text = value,
OverflowClip = true
};
HashSet<MapEntity> editedEntities = new HashSet<MapEntity>();
propertyBox.OnTextChanged += (textBox, text) =>
{
foreach (var entity in MapEntity.SelectedList)
{
editedEntities.Add(entity);
}
return true;
};
propertyBox.OnDeselected += (textBox, keys) => OnApply(textBox);
propertyBox.OnEnterPressed += (box, text) => OnApply(box);
refresh += () =>
@@ -684,12 +691,25 @@ namespace Barotrauma
bool OnApply(GUITextBox textBox)
{
List<MapEntity> prevSelected = MapEntity.SelectedList.ToList();
//reselect the entities that were selected during editing
//otherwise multi-editing won't work when we deselect the entities with unapplied changes in the textbox
foreach (var entity in editedEntities)
{
MapEntity.SelectedList.Add(entity);
}
if (SetPropertyValue(property, entity, textBox.Text))
{
TrySendNetworkUpdate(entity, property);
textBox.Text = (string) property.GetValue(entity);
textBox.Flash(GUI.Style.Green, flashDuration: 1f);
}
//restore the entities that were selected before applying
MapEntity.SelectedList.Clear();
foreach (var entity in prevSelected)
{
MapEntity.SelectedList.Add(entity);
}
return true;
}

View File

@@ -14,9 +14,10 @@ namespace Barotrauma
{
private List<ParticleEmitter> particleEmitters;
private static HashSet<StatusEffect> ActiveLoopingSounds = new HashSet<StatusEffect>();
private readonly static HashSet<StatusEffect> ActiveLoopingSounds = new HashSet<StatusEffect>();
private static double LastMuffleCheckTime;
private readonly List<RoundSound> sounds = new List<RoundSound>();
public IEnumerable<RoundSound> Sounds { get { return sounds; } }
private SoundSelectionMode soundSelectionMode;
private SoundChannel soundChannel;
private Entity soundEmitter;
@@ -53,7 +54,7 @@ namespace Barotrauma
}
}
partial void ApplyProjSpecific(float deltaTime, Entity entity, IEnumerable<ISerializableEntity> targets, Hull hull, Vector2 worldPosition, bool playSound)
partial void ApplyProjSpecific(float deltaTime, Entity entity, IReadOnlyList<ISerializableEntity> targets, Hull hull, Vector2 worldPosition, bool playSound)
{
if (playSound)
{
@@ -84,7 +85,14 @@ namespace Barotrauma
}
else
{
targetLimb = targets.FirstOrDefault(t => t is Limb) as Limb;
for (int i = 0; i < targets.Count; i++)
{
if (targets[i] is Limb limb)
{
targetLimb = limb;
break;
}
}
}
if (targetLimb != null && !targetLimb.Removed)
{
@@ -147,10 +155,6 @@ namespace Barotrauma
GameAnalyticsManager.AddErrorEventOnce("StatusEffect.ApplyProjSpecific:SoundNull2" + Environment.StackTrace.CleanupStackTrace(), GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
return;
}
if (selectedSound.Sound.Disposed)
{
Submarine.ReloadRoundSound(selectedSound);
}
soundChannel = SoundPlayer.PlaySound(selectedSound.Sound, worldPosition, selectedSound.Volume, selectedSound.Range, hullGuess: hull, ignoreMuffling: selectedSound.IgnoreMuffling);
ignoreMuffling = selectedSound.IgnoreMuffling;
if (soundChannel != null) { soundChannel.Looping = loopSound; }

View File

@@ -122,7 +122,7 @@ namespace Barotrauma
{
int width = 4096; int height = 4096;
Rectangle subDimensions = sub.CalculateDimensions(false);
Rectangle subDimensions = sub.Borders;
Vector2 viewPos = subDimensions.Center.ToVector2();
float scale = Math.Min(width / (float)subDimensions.Width, height / (float)subDimensions.Height);

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product>
<Version>0.15.23.0</Version>
<Version>0.16.0.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product>
<Version>0.15.23.0</Version>
<Version>0.16.0.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product>
<Version>0.15.23.0</Version>
<Version>0.16.0.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product>
<Version>0.15.23.0</Version>
<Version>0.16.0.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName>

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product>
<Version>0.15.23.0</Version>
<Version>0.16.0.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName>

View File

@@ -61,9 +61,9 @@ namespace Barotrauma
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.UpdateMoney });
}
partial void OnTalentGiven(string talentIdentifier)
partial void OnTalentGiven(TalentPrefab talentPrefab)
{
GameServer.Log($"{GameServer.CharacterLogName(this)} has gained the talent '{talentIdentifier}'", ServerLog.MessageType.Talent);
GameServer.Log($"{GameServer.CharacterLogName(this)} has gained the talent '{talentPrefab.DisplayName}'", ServerLog.MessageType.Talent);
}
}
}

View File

@@ -288,15 +288,18 @@ namespace Barotrauma
{
UInt32 talentIdentifier = msg.ReadUInt32();
var prefab = TalentPrefab.TalentPrefabs.Find(p => p.UIntIdentifier == talentIdentifier);
if (prefab != null) { talentSelection.Add(prefab.Identifier); }
if (prefab == null) { continue; }
if (TalentTree.IsViableTalentForCharacter(this, prefab.Identifier, talentSelection))
{
GiveTalent(prefab.Identifier);
talentSelection.Add(prefab.Identifier);
}
}
talentSelection = TalentTree.CheckTalentSelection(this, talentSelection);
foreach (string talent in talentSelection)
if (talentSelection.Count != talentCount)
{
GiveTalent(talent);
DebugConsole.AddWarning($"Failed to unlock talents: the amount of unlocked talents doesn't match (client: {talentCount}, server: {talentSelection.Count})");
}
break;
}
break;

View File

@@ -17,6 +17,9 @@ namespace Barotrauma
{
public static readonly Version Version = Assembly.GetEntryAssembly().GetName().Version;
public static bool IsSingleplayer => NetworkMember == null;
public static bool IsMultiplayer => NetworkMember != null;
private static World world;
public static World World

View File

@@ -53,7 +53,7 @@ namespace Barotrauma
foreach (var activeOrder in ActiveOrders)
{
if (!(activeOrder?.First is Order order) || activeOrder.Second.HasValue) { continue; }
OrderChatMessage.WriteOrder(msg, order, null, order.TargetSpatialEntity, null, 0, order.WallSectionIndex);
OrderChatMessage.WriteOrder(msg, order, targetCharacter: null, order.TargetSpatialEntity, orderOption: null, orderPriority: 0, order.WallSectionIndex, isNewOrder: true);
bool hasOrderGiver = order.OrderGiver != null;
msg.Write(hasOrderGiver);
if (hasOrderGiver)

View File

@@ -348,7 +348,6 @@ namespace Barotrauma
}
}
}
UpdateCampaignSubs();
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
PendingSubmarineSwitch = null;
@@ -399,44 +398,12 @@ namespace Barotrauma
Map.OnMissionsSelected += (loc, mission) => { LastUpdateID++; };
Reputation.OnAnyReputationValueChanged += () => { LastUpdateID++; };
UpdateCampaignSubs();
//increment save ID so clients know they're lacking the most up-to-date save file
LastSaveID++;
}
public static void UpdateCampaignSubs()
{
bool isSubmarineVisible(SubmarineInfo s)
=> !GameMain.Server.ServerSettings.HiddenSubs.Any(h
=> s.Name.Equals(h, StringComparison.OrdinalIgnoreCase));
List<SubmarineInfo> availableSubs =
SubmarineInfo.SavedSubmarines
.Where(s =>
s.IsCampaignCompatible
&& isSubmarineVisible(s))
.ToList();
if (!availableSubs.Any())
{
//None of the available subs were marked as campaign-compatible, just include all visible subs
availableSubs.AddRange(
SubmarineInfo.SavedSubmarines
.Where(isSubmarineVisible));
}
if (!availableSubs.Any())
{
//No subs are visible at all! Just make the selected one available
availableSubs.Add(GameMain.NetLobbyScreen.SelectedSub);
}
GameMain.NetLobbyScreen.CampaignSubmarines = availableSubs;
}
public bool CanPurchaseSub(SubmarineInfo info)
=> info.Price <= Money && GameMain.NetLobbyScreen.CampaignSubmarines.Contains(info);
=> info.Price <= Money && GetCampaignSubs().Contains(info);
public void DiscardClientCharacterData(Client client)
{

View File

@@ -0,0 +1,191 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using Barotrauma.Networking;
namespace Barotrauma
{
internal partial class MedicalClinic
{
private enum RateLimitResult
{
OK,
LimitReached
}
private struct RateLimitInfo
{
public int Requests;
public const int MaxRequests = 5;
public DateTimeOffset Expiry;
}
private readonly Dictionary<Client, RateLimitInfo> rateLimits = new Dictionary<Client, RateLimitInfo>();
public void ServerRead(IReadMessage inc, Client sender)
{
NetworkHeader header = (NetworkHeader)inc.ReadByte();
switch (header)
{
case NetworkHeader.REQUEST_AFFLICTIONS:
ProcessRequestedAfflictions(inc, sender);
break;
case NetworkHeader.REQUEST_PENDING:
ProcessRequestedPending(sender);
break;
case NetworkHeader.ADD_PENDING:
ProcessNewAddition(inc, sender);
break;
case NetworkHeader.REMOVE_PENDING:
ProcessNewRemoval(inc, sender);
break;
case NetworkHeader.HEAL_PENDING:
ProcessHealing(sender);
break;
case NetworkHeader.CLEAR_PENDING:
ProcessClearing(sender);
break;
}
}
private void ProcessNewAddition(IReadMessage inc, Client client)
{
if (CheckRateLimit(client) == RateLimitResult.LimitReached) { return; }
NetCrewMember newCrewMember = INetSerializableStruct.Read<NetCrewMember>(inc);
InsertPendingCrewMember(newCrewMember);
ServerSend(newCrewMember, NetworkHeader.ADD_PENDING, DeliveryMethod.Reliable, reponseClient: client);
}
private void ProcessNewRemoval(IReadMessage inc, Client client)
{
if (CheckRateLimit(client) == RateLimitResult.LimitReached) { return; }
NetRemovedAffliction removed = INetSerializableStruct.Read<NetRemovedAffliction>(inc);
RemovePendingAffliction(removed.CrewMember, removed.Affliction);
ServerSend(removed, NetworkHeader.REMOVE_PENDING, DeliveryMethod.Reliable, reponseClient: client);
}
private void ProcessRequestedPending(Client client)
{
if (CheckRateLimit(client) == RateLimitResult.LimitReached) { return; }
INetSerializableStruct writeCrewMember = new NetPendingCrew
{
CrewMembers = PendingHeals.ToArray()
};
ServerSend(writeCrewMember, NetworkHeader.REQUEST_PENDING, DeliveryMethod.Reliable, targetClient: client);
}
private void ProcessHealing(Client client)
{
if (CheckRateLimit(client) == RateLimitResult.LimitReached) { return; }
HealRequestResult result = HealAllPending();
ServerSend(new NetHealRequest { Result = result }, NetworkHeader.HEAL_PENDING, DeliveryMethod.Reliable, reponseClient: client);
}
private void ProcessClearing(Client client)
{
if (CheckRateLimit(client) == RateLimitResult.LimitReached) { return; }
if (!PendingHeals.Any()) { return; }
ClearPendingHeals();
ServerSend(null, NetworkHeader.CLEAR_PENDING, DeliveryMethod.Reliable, reponseClient: client);
}
private void ProcessRequestedAfflictions(IReadMessage inc, Client client)
{
if (CheckRateLimit(client) == RateLimitResult.LimitReached) { return; }
NetCrewMember crewMember = INetSerializableStruct.Read<NetCrewMember>(inc);
CharacterInfo? foundInfo = crewMember.FindCharacterInfo(GetCrewCharacters());
NetAffliction[] pendingAfflictions = Array.Empty<NetAffliction>();
int infoId = 0;
if (foundInfo is { Character: { CharacterHealth: { } health } })
{
pendingAfflictions = GetAllAfflictions(health);
infoId = foundInfo.GetIdentifierUsingOriginalName();
}
INetSerializableStruct writeCrewMember = new NetCrewMember
{
CharacterInfoID = infoId,
Afflictions = pendingAfflictions
};
ServerSend(writeCrewMember, NetworkHeader.REQUEST_AFFLICTIONS, DeliveryMethod.Unreliable, client);
}
private RateLimitResult CheckRateLimit(Client client)
{
if (rateLimits.TryGetValue(client, out RateLimitInfo rateLimitInfo))
{
if (rateLimitInfo.Expiry < DateTimeOffset.Now)
{
rateLimitInfo.Expiry = DateTimeOffset.Now.AddSeconds(5);
rateLimitInfo.Requests = 1;
}
else
{
if (rateLimitInfo.Requests > RateLimitInfo.MaxRequests) { return RateLimitResult.LimitReached; }
rateLimitInfo.Requests++;
}
rateLimits[client] = rateLimitInfo;
}
else
{
rateLimits.Add(client, new RateLimitInfo { Requests = 1, Expiry = DateTimeOffset.Now.AddSeconds(5) });
}
return RateLimitResult.OK;
}
private IWriteMessage StartSending()
{
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ServerPacketHeader.MEDICAL);
return msg;
}
private void ServerSend(INetSerializableStruct? netStruct, NetworkHeader header, DeliveryMethod deliveryMethod, Client? targetClient = null, Client? reponseClient = null)
{
if (targetClient is null)
{
foreach (Client c in GameMain.Server.ConnectedClients)
{
SendToClient(c);
}
return;
}
SendToClient(targetClient);
void SendToClient(Client c)
{
MessageFlag flag = MessageFlag.Announce;
if (reponseClient != null && reponseClient == c)
{
flag = MessageFlag.Response;
}
IWriteMessage msg = StartSending();
msg.Write((byte)header);
msg.Write((byte)flag);
netStruct?.Write(msg);
GameMain.Server.ServerPeer.Send(msg, c.Connection, deliveryMethod);
}
}
}
}

View File

@@ -50,14 +50,14 @@ namespace Barotrauma.Items.Components
newSteeringInput = new Vector2(msg.ReadSingle(), msg.ReadSingle());
}
if (!item.CanClientAccess(c)) return;
if (!item.CanClientAccess(c)) { return; }
user = c.Character;
AutoPilot = autoPilot;
if (dockingButtonClicked)
{
item.SendSignal("1", "toggle_docking");
item.SendSignal(new Signal("1", sender: c.Character), "toggle_docking");
GameMain.Server.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ComponentState, item.GetComponentIndex(this), true });
}

View File

@@ -46,6 +46,7 @@ namespace Barotrauma.Items.Components
msg.Write(DeteriorateAlways);
msg.Write(tinkeringDuration);
msg.Write(tinkeringStrength);
msg.Write(tinkeringPowersDevices);
msg.Write(CurrentFixer == null ? (ushort)0 : CurrentFixer.ID);
msg.WriteRangedInteger((int)currentFixerAction, 0, 2);
}

View File

@@ -8,7 +8,7 @@ namespace Barotrauma.Items.Components
{
int signalIndex = msg.ReadRangedInteger(0, Signals.Length - 1);
if (!item.CanClientAccess(c)) { return; }
if (!SendSignal(signalIndex)) { return; }
if (!SendSignal(signalIndex, c.Character)) { return; }
GameServer.Log($"{GameServer.CharacterLogName(c.Character)} sent a signal \"{Signals[signalIndex]}\" from {item.Name}", ServerLog.MessageType.ItemInteraction);
item.CreateServerEvent(this, new object[] { signalIndex });
}

View File

@@ -218,33 +218,6 @@ namespace Barotrauma.Networking
if (shuttle != null) { GameMain.NetLobbyScreen.SelectedShuttle = shuttle; }
}
List<SubmarineInfo> campaignSubs = new List<SubmarineInfo>();
if (serverSettings.CampaignSubmarines != null && serverSettings.CampaignSubmarines.Length > 0)
{
string[] submarines = serverSettings.CampaignSubmarines.Split(ServerSettings.SubmarineSeparatorChar);
for (int i = 0; i < submarines.Length; i++)
{
SubmarineInfo subInfo = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == submarines[i]);
if (subInfo != null && subInfo.IsCampaignCompatible)
{
campaignSubs.Add(subInfo);
}
}
}
else
{
// Add vanilla submarines by default
for (int i = 0; i < SubmarineInfo.SavedSubmarines.Count(); i++)
{
SubmarineInfo subInfo = SubmarineInfo.SavedSubmarines.ElementAt(i);
if (subInfo.IsVanillaSubmarine() && subInfo.IsCampaignCompatible)
{
campaignSubs.Add(SubmarineInfo.SavedSubmarines.ElementAt(i));
}
}
}
GameMain.NetLobbyScreen.CampaignSubmarines = campaignSubs;
started = true;
GameAnalyticsManager.AddDesignEvent("GameServer:Start");
@@ -835,6 +808,9 @@ namespace Barotrauma.Networking
case ClientPacketHeader.CREW:
ReadCrewMessage(inc, connectedClient);
break;
case ClientPacketHeader.MEDICAL:
ReadMedicalMessage(inc, connectedClient);
break;
case ClientPacketHeader.READY_CHECK:
ReadyCheck.ServerRead(inc, connectedClient);
break;
@@ -879,16 +855,16 @@ namespace Barotrauma.Networking
}
else if (entity is Character character)
{
errorStr = "Missing character " + character.Name + " (event id " + eventID.ToString() + ", entity id " + entityID.ToString() + ").";
errorStrNoName = "Missing character " + character.SpeciesName + "(event id " + eventID.ToString() + ", entity id " + entityID.ToString() + ").";
errorStr = $"Missing character {character.Name} (event id {eventID}, entity id {entityID}).";
errorStrNoName = $"Missing character {character.SpeciesName} (event id {eventID}, entity id {entityID}).";
}
else if (entity is Item item)
{
errorStr = errorStrNoName = "Missing item " + item.Name + " (event id " + eventID.ToString() + ", entity id " + entityID.ToString() + ").";
errorStr = errorStrNoName = $"Missing item {item.Name}, sub: {item.Submarine?.Info?.Name ?? "none"} (event id {eventID}, entity id {entityID}).";
}
else
{
errorStr = errorStrNoName = "Missing entity " + entity.ToString() + " (event id " + eventID.ToString() + ", entity id " + entityID.ToString() + ").";
errorStr = errorStrNoName = $"Missing entity {entity}, sub: {entity.Submarine?.Info?.Name ?? "none"} (event id {eventID}, entity id {entityID}).";
}
break;
}
@@ -1234,6 +1210,14 @@ namespace Barotrauma.Networking
}
}
private void ReadMedicalMessage(IReadMessage inc, Client sender)
{
if (GameMain.GameSession?.Campaign is MultiPlayerCampaign mpCampaign)
{
mpCampaign.MedicalClinic.ServerRead(inc, sender);
}
}
private void ReadReadyToSpawnMessage(IReadMessage inc, Client sender)
{
sender.SpectateOnly = inc.ReadBoolean() && (serverSettings.AllowSpectating || sender.Connection == OwnerConnection);
@@ -1861,24 +1845,6 @@ namespace Barotrauma.Networking
outmsg.Write(GameMain.NetLobbyScreen.SelectedShuttle.Name);
outmsg.Write(GameMain.NetLobbyScreen.SelectedShuttle.MD5Hash.ToString());
List<int> campaignSubIndices = new List<int>();
if (GameMain.NetLobbyScreen.SelectedMode == GameModePreset.MultiPlayerCampaign)
{
IReadOnlyList<SubmarineInfo> subList = GameMain.NetLobbyScreen.GetSubList();
for (int i = 0; i < subList.Count; i++)
{
if (GameMain.NetLobbyScreen.CampaignSubmarines.Contains(subList[i]))
{
campaignSubIndices.Add(i);
}
}
}
outmsg.Write((UInt16)campaignSubIndices.Count);
foreach (int campaignSubIndex in campaignSubIndices)
{
outmsg.Write((UInt16)campaignSubIndex);
}
outmsg.Write(serverSettings.Voting.AllowSubVoting);
outmsg.Write(serverSettings.Voting.AllowModeVoting);
@@ -2166,7 +2132,6 @@ namespace Barotrauma.Networking
else
{
SendStartMessage(roundStartSeed, GameMain.NetLobbyScreen.LevelSeed, GameMain.GameSession, connectedClients, false);
GameMain.GameSession.StartRound(GameMain.NetLobbyScreen.LevelSeed, serverSettings.SelectedLevelDifficulty);
Log("Game mode: " + selectedMode.Name, ServerLog.MessageType.ServerMessage);
Log("Submarine: " + selectedSub.Name, ServerLog.MessageType.ServerMessage);
@@ -2186,8 +2151,7 @@ namespace Barotrauma.Networking
yield return CoroutineStatus.Failure;
}
MissionMode missionMode = GameMain.GameSession.GameMode as MissionMode;
bool missionAllowRespawn = missionMode == null || !missionMode.Missions.Any(m => !m.AllowRespawn);
bool missionAllowRespawn = !(GameMain.GameSession.GameMode is MissionMode missionMode) || !missionMode.Missions.Any(m => !m.AllowRespawn);
bool isOutpost = campaign != null && campaign.NextLevel?.Type == LevelData.LevelType.Outpost;
if (serverSettings.AllowRespawn && missionAllowRespawn)
@@ -2263,7 +2227,7 @@ namespace Barotrauma.Networking
characterInfos.Add(client.CharacterInfo);
if (client.CharacterInfo.Job == null || client.CharacterInfo.Job.Prefab != client.AssignedJob.First)
{
client.CharacterInfo.Job = new Job(client.AssignedJob.First, client.AssignedJob.Second);
client.CharacterInfo.Job = new Job(client.AssignedJob.First, Rand.RandSync.Unsynced, client.AssignedJob.Second);
}
}
@@ -2475,7 +2439,7 @@ namespace Barotrauma.Networking
msg.Write(serverSettings.LockAllDefaultWires);
msg.Write(serverSettings.AllowRagdollButton);
msg.Write(serverSettings.AllowLinkingWifiToChat);
msg.Write(serverSettings.UseRespawnShuttle);
msg.Write(serverSettings.UseRespawnShuttle || (gameStarted && respawnManager.UsingShuttle));
msg.Write((byte)serverSettings.LosMode);
msg.Write(includesFinalize); msg.WritePadBits();
@@ -3372,14 +3336,40 @@ namespace Barotrauma.Networking
}
}
public void IncrementStat(Character character, string achievementIdentifier, int amount)
{
achievementIdentifier = achievementIdentifier.ToLowerInvariant();
foreach (Client client in connectedClients)
{
if (client.Character == character)
{
IncrementStat(client, achievementIdentifier, amount);
return;
}
}
}
public void GiveAchievement(Client client, string achievementIdentifier)
{
if (client.GivenAchievements.Contains(achievementIdentifier)) return;
if (client.GivenAchievements.Contains(achievementIdentifier)) { return; }
client.GivenAchievements.Add(achievementIdentifier);
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ServerPacketHeader.ACHIEVEMENT);
msg.Write(achievementIdentifier);
msg.Write(0);
serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
}
public void IncrementStat(Client client, string achievementIdentifier, int amount)
{
if (client.GivenAchievements.Contains(achievementIdentifier)) { return; }
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ServerPacketHeader.ACHIEVEMENT);
msg.Write(achievementIdentifier);
msg.Write(amount);
serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
}
@@ -3733,7 +3723,7 @@ namespace Barotrauma.Networking
if (assignedPlayerCount[jobPrefab] >= jobPrefab.MaxNumber) { continue; }
var variant = Rand.Range(0, jobPrefab.Variants, Rand.RandSync.Server);
unassignedBots[0].Job = new Job(jobPrefab, variant);
unassignedBots[0].Job = new Job(jobPrefab, Rand.RandSync.Server, variant);
assignedPlayerCount[jobPrefab]++;
unassignedBots.Remove(unassignedBots[0]);
canAssign = true;
@@ -3756,7 +3746,7 @@ namespace Barotrauma.Networking
{
var job = remainingJobs.GetRandom();
var variant = Rand.Range(0, job.Variants);
c.Job = new Job(job, variant);
c.Job = new Job(job, Rand.RandSync.Unsynced, variant);
assignedPlayerCount[c.Job.Prefab]++;
}
}
@@ -3842,16 +3832,6 @@ namespace Barotrauma.Networking
if (GameMain.NetLobbyScreen.SelectedSub != null) { serverSettings.SelectedSubmarine = GameMain.NetLobbyScreen.SelectedSub.Name; }
if (GameMain.NetLobbyScreen.SelectedShuttle != null) { serverSettings.SelectedShuttle = GameMain.NetLobbyScreen.SelectedShuttle.Name; }
if (GameMain.NetLobbyScreen.CampaignSubmarines != null)
{
string submarinesString = string.Empty;
for (int i = 0; i < GameMain.NetLobbyScreen.CampaignSubmarines.Count; i++)
{
submarinesString += GameMain.NetLobbyScreen.CampaignSubmarines[i].Name + ServerSettings.SubmarineSeparatorChar;
}
submarinesString.Trim(ServerSettings.SubmarineSeparatorChar);
serverSettings.CampaignSubmarines = submarinesString;
}
serverSettings.SaveSettings();

View File

@@ -351,7 +351,7 @@ namespace Barotrauma.Networking
{
if (campaign?.GetClientCharacterData(c) == null || c.CharacterInfo.Job == null)
{
c.CharacterInfo.Job = new Job(c.AssignedJob.First, c.AssignedJob.Second);
c.CharacterInfo.Job = new Job(c.AssignedJob.First, Rand.RandSync.Unsynced, c.AssignedJob.Second);
}
}

View File

@@ -11,6 +11,21 @@ namespace Barotrauma.Networking
{
partial class ServerSettings
{
partial class NetPropertyData
{
private object lastSyncedValue;
public UInt16 LastUpdateID { get; private set; }
public void SyncValue()
{
if (!PropEquals(lastSyncedValue, Value))
{
LastUpdateID = (UInt16)(GameMain.NetLobbyScreen.LastUpdateID);
lastSyncedValue = Value;
}
}
}
public static readonly string ClientPermissionsFile = "Data" + Path.DirectorySeparatorChar + "clientpermissions.xml";
public static readonly char SubmarineSeparatorChar = '|';
@@ -35,20 +50,25 @@ namespace Barotrauma.Networking
LoadClientPermissions();
}
private void WriteNetProperties(IWriteMessage outMsg)
private void WriteNetProperties(IWriteMessage outMsg, Client c)
{
outMsg.Write((UInt16)netProperties.Keys.Count);
foreach (UInt32 key in netProperties.Keys)
{
outMsg.Write(key);
netProperties[key].Write(outMsg);
var property = netProperties[key];
property.SyncValue();
if (property.LastUpdateID > c.LastRecvLobbyUpdate)
{
outMsg.Write(key);
netProperties[key].Write(outMsg);
}
}
outMsg.Write((UInt32)0);
}
public void ServerAdminWrite(IWriteMessage outMsg, Client c)
{
c.LastSentServerSettingsUpdate = LastPropertyUpdateId;
WriteNetProperties(outMsg);
WriteNetProperties(outMsg, c);
WriteMonsterEnabled(outMsg);
BanList.ServerAdminWrite(outMsg, c);
Whitelist.ServerAdminWrite(outMsg, c);
@@ -79,8 +99,11 @@ namespace Barotrauma.Networking
{
WriteExtraCargo(outMsg);
}
WriteHiddenSubs(outMsg);
if (requiredFlags.HasFlag(NetFlags.HiddenSubs))
{
WriteHiddenSubs(outMsg);
}
if (c.HasPermission(Networking.ClientPermissions.ManageSettings)
&& !NetIdUtils.IdMoreRecentOrMatches(c.LastRecvServerSettingsUpdate, LastPropertyUpdateId))
@@ -164,6 +187,7 @@ namespace Barotrauma.Networking
{
ReadHiddenSubs(incMsg);
changed |= true;
UpdateFlag(NetFlags.HiddenSubs);
}
if (flags.HasFlag(NetFlags.Misc))

View File

@@ -32,26 +32,6 @@ namespace Barotrauma
set { selectedShuttle = value; lastUpdateID++; }
}
[Obsolete("TODO: this list shouldn't exist, the client should just use the visible subs list instead")]
public List<SubmarineInfo> CampaignSubmarines
{
get
{
return campaignSubmarines;
}
set
{
campaignSubmarines = value;
lastUpdateID++;
if (GameMain.NetworkMember?.ServerSettings != null)
{
GameMain.NetworkMember.ServerSettings.ServerDetailsChanged = true;
}
}
}
private List<SubmarineInfo> campaignSubmarines;
public GameModePreset[] GameModes { get; }
private int selectedModeIndex;

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product>
<Version>0.15.23.0</Version>
<Version>0.16.0.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName>

View File

@@ -144,6 +144,7 @@
<Submarine file="Submarines/Venture.sub" />
<Submarine file="Submarines/Azimuth.sub" />
<Submarine file="Submarines/R-29.sub" />
<Submarine file="Submarines/Barsuk.sub" />
<Text file="Content/Texts/English/EnglishVanilla.xml" />
<Text file="Content/Texts/German/GermanVanilla.xml" />
<Text file="Content/Texts/French/FrenchVanilla.xml" />

View File

@@ -2,8 +2,8 @@
http://www.barotraumagame.com
© 2018-2020 FakeFish Ltd. All rights reserved.
© 2019-2020 Daedalic Entertainment GmbH. The Daedalic logo is a trademark of Daedalic Entertainment GmbH, Germany. All rights reserved.
© 2018-2022 FakeFish Ltd. All rights reserved.
© 2019-2022 Daedalic Entertainment GmbH. The Daedalic logo is a trademark of Daedalic Entertainment GmbH, Germany. All rights reserved.
Privacy policy: http://privacypolicy.daedalic.com
See the wiki for more detailed info and instructions:

View File

@@ -150,7 +150,7 @@ namespace Barotrauma
private CoroutineHandle disableTailCoroutine;
private readonly IEnumerable<Body> myBodies;
private readonly List<Body> myBodies;
public LatchOntoAI LatchOntoAI { get; private set; }
public SwarmBehavior SwarmBehavior { get; private set; }
@@ -306,7 +306,8 @@ namespace Barotrauma
requiredHoleCount = (int)Math.Ceiling(ConvertUnits.ToDisplayUnits(colliderWidth) / Structure.WallSectionSize);
myBodies = Character.AnimController.Limbs.Select(l => l.body.FarseerBody);
myBodies = Character.AnimController.Limbs.Select(l => l.body.FarseerBody).ToList();
myBodies.Add(Character.AnimController.Collider.FarseerBody);
}
private CharacterParams.AIParams _aiParams;
@@ -1837,7 +1838,7 @@ namespace Barotrauma
if (!attack.IsValidTarget(target)) { return false; }
if (target is ISerializableEntity se && target is Character)
{
if (attack.Conditionals.Any(c => !c.Matches(se))) { return false; }
if (attack.Conditionals.Any(c => !c.TargetSelf && !c.Matches(se))) { return false; }
}
if (attack.Conditionals.Any(c => c.TargetSelf && !c.Matches(Character))) { return false; }
if (attack.Ranged)
@@ -2182,10 +2183,22 @@ namespace Barotrauma
float margin = MathHelper.PiOver4 * distanceFactor;
if (angle < margin)
{
var collisionCategories = Physics.CollisionCharacter | Physics.CollisionWall | Physics.CollisionLevel;
var pickedBody = Submarine.PickBody(weapon.SimPosition, target.SimPosition, myBodies, collisionCategories, allowInsideFixture: true);
var collisionCategories = Physics.CollisionCharacter | Physics.CollisionWall | Physics.CollisionLevel;
var pickedBody = Submarine.PickBody(weapon.SimPosition, Character.GetRelativeSimPosition(target), myBodies, collisionCategories, allowInsideFixture: true);
if (pickedBody != null)
{
if (target is MapEntity)
{
if (pickedBody.UserData is Submarine sub && sub == target.Submarine)
{
return true;
}
else if (target == pickedBody.UserData)
{
return true;
}
}
Character t = null;
if (pickedBody.UserData is Character c)
{

View File

@@ -832,6 +832,8 @@ namespace Barotrauma
if (container == null) { return 0; }
if (!container.HasAccess(character)) { return 0; }
if (!container.Inventory.CanBePut(containableItem)) { return 0; }
var rootContainer = container.Item.GetRootContainer();
if (rootContainer?.GetComponent<Fabricator>() != null || rootContainer?.GetComponent<Fabricator>() != null) { return 0; }
if (container.ShouldBeContained(containableItem, out bool isRestrictionsDefined))
{
if (isRestrictionsDefined)
@@ -1088,6 +1090,7 @@ namespace Barotrauma
private void RespondToAttack(Character attacker, AttackResult attackResult)
{
float minorDamageThreshold = 10;
float healAmount = 0.0f;
if (attacker != null)
{
@@ -1135,6 +1138,7 @@ namespace Barotrauma
// Don't react to attackers that are outside of the sub (e.g. AoE attacks)
return;
}
bool isAttackerInfected = false;
bool isAttackerFightingEnemy = false;
if (IsFriendly(attacker))
{
@@ -1155,10 +1159,14 @@ namespace Barotrauma
}
else
{
isAttackerInfected = attacker.CharacterHealth.GetAfflictionStrength("alieninfection") > 0;
// Inform other NPCs
if (cumulativeDamage > 1 || totalDamage >= 10)
if (isAttackerInfected || cumulativeDamage > 1 || totalDamage >= minorDamageThreshold)
{
InformOtherNPCs(cumulativeDamage);
if (GameMain.IsMultiplayer || !attacker.IsPlayer || Character.TeamID != attacker.TeamID)
{
InformOtherNPCs(cumulativeDamage);
}
}
if (Character.IsBot)
{
@@ -1167,7 +1175,7 @@ namespace Barotrauma
{
if (Character.IsSecurity)
{
if (attacker.TeamID != Character.TeamID && cumulativeDamage > 1 || cumulativeDamage > 10)
if (attacker.TeamID != Character.TeamID && cumulativeDamage > 1 || cumulativeDamage > minorDamageThreshold)
{
Character.Speak(TextManager.Get("dialogattackedbyfriendlysecurityarrest"), null, 0.50f, "attackedbyfriendlysecurityarrest", minDurationBetweenSimilar: 30.0f);
}
@@ -1181,26 +1189,8 @@ namespace Barotrauma
Character.Speak(TextManager.Get("DialogAttackedByFriendly"), null, 0.50f, "attackedbyfriendly", minDurationBetweenSimilar: 30.0f);
}
}
if (cumulativeDamage > 1 && attacker.TeamID != Character.TeamID)
{
// If the attacker is using a low damage and high frequency weapon like a repair tool, we shouldn't use any delay.
AddCombatObjective(DetermineCombatMode(Character, cumulativeDamage), attacker, delay: realDamage > 1 ? GetReactionTime() : 0);
}
else
{
// Don't react to minor (accidental) dmg done by characters that are in the same team
if (cumulativeDamage < 10)
{
if (!Character.IsSecurity && cumulativeDamage > 1)
{
AddCombatObjective(AIObjectiveCombat.CombatMode.Retreat, attacker);
}
}
else
{
AddCombatObjective(DetermineCombatMode(Character, cumulativeDamage, dmgThreshold: 50), attacker, GetReactionTime() * 2);
}
}
// If the attacker is using a low damage and high frequency weapon like a repair tool, we shouldn't use any delay.
AddCombatObjective(DetermineCombatMode(Character, cumulativeDamage), attacker, delay: realDamage > 1 ? GetReactionTime() : 0);
}
if (!isAttackerFightingEnemy)
{
@@ -1242,13 +1232,13 @@ namespace Barotrauma
continue;
}
}
var combatMode = DetermineCombatMode(otherCharacter, cumulativeDamage, isWitnessing, dmgThreshold: attacker.TeamID == Character.TeamID ? 50 : 10);
var combatMode = DetermineCombatMode(otherCharacter, cumulativeDamage, isWitnessing);
float delay = isWitnessing ? GetReactionTime() : Rand.Range(2.0f, 5.0f, Rand.RandSync.Unsynced);
otherHumanAI.AddCombatObjective(combatMode, attacker, delay);
}
}
AIObjectiveCombat.CombatMode DetermineCombatMode(Character c, float cumulativeDamage, bool isWitnessing = false, float dmgThreshold = 10, bool allowOffensive = true)
AIObjectiveCombat.CombatMode DetermineCombatMode(Character c, float cumulativeDamage, bool isWitnessing = false)
{
if (!IsFriendly(attacker))
{
@@ -1268,6 +1258,17 @@ namespace Barotrauma
}
else
{
float dmgThreshold = attacker.TeamID == Character.TeamID ? 50 : minorDamageThreshold;
if (isAttackerInfected)
{
cumulativeDamage = 100;
}
if (GameMain.IsSingleplayer && attacker.IsPlayer && Character.TeamID == attacker.TeamID)
{
// Bots in the player team never act aggressively in single player when attacked by the player
dmgThreshold = minorDamageThreshold;
return cumulativeDamage > dmgThreshold ? AIObjectiveCombat.CombatMode.Retreat : AIObjectiveCombat.CombatMode.None;
}
if (Character.Submarine == null || !Character.Submarine.GetConnectedSubs().Contains(attacker.Submarine))
{
// Outside or attacked from an unconnected submarine -> don't react.
@@ -1279,17 +1280,17 @@ namespace Barotrauma
isAttackerFightingEnemy = true;
return AIObjectiveCombat.CombatMode.None;
}
else if (isWitnessing && Character.CombatAction != null && !c.IsSecurity)
if (isWitnessing && Character.CombatAction != null && !c.IsSecurity)
{
return Character.CombatAction.WitnessReaction;
}
else if (attacker.IsPlayer && FindInstigator() is Character instigator)
if (attacker.IsPlayer && FindInstigator() is Character instigator)
{
// The guards don't react when the player there's an instigator around
// The guards don't react to player's aggressions when there's an instigator around
isAttackerFightingEnemy = true;
return c.IsSecurity ? AIObjectiveCombat.CombatMode.None : (instigator.CombatAction != null ? instigator.CombatAction.WitnessReaction : AIObjectiveCombat.CombatMode.Retreat);
}
else if (attacker.TeamID == CharacterTeamType.FriendlyNPC && !(attacker.AIController.IsMentallyUnstable || attacker.AIController.IsMentallyUnstable))
if (attacker.TeamID == CharacterTeamType.FriendlyNPC && !(attacker.AIController.IsMentallyUnstable || attacker.AIController.IsMentallyUnstable))
{
if (c.IsSecurity)
{
@@ -1307,15 +1308,11 @@ namespace Barotrauma
// Already targeting the attacker -> treat as a more serious threat.
cumulativeDamage *= 2;
}
if (attackResult.Afflictions != null && attackResult.Afflictions.Any(a => a is AfflictionHusk))
{
cumulativeDamage = 100;
}
if (cumulativeDamage > dmgThreshold)
{
if (c.IsSecurity)
{
return c.IsSecurity && allowOffensive ? AIObjectiveCombat.CombatMode.Offensive : AIObjectiveCombat.CombatMode.Arrest;
return c.IsSecurity ? AIObjectiveCombat.CombatMode.Offensive : AIObjectiveCombat.CombatMode.Arrest;
}
else
{
@@ -1838,7 +1835,7 @@ namespace Barotrauma
bool ignoreFire = objectiveManager.CurrentOrder is AIObjectiveExtinguishFires extinguishOrder && extinguishOrder.Priority > 0 || objectiveManager.HasActiveObjective<AIObjectiveExtinguishFire>();
bool ignoreWater = HasDivingSuit(character);
bool ignoreOxygen = ignoreWater || HasDivingMask(character);
bool ignoreEnemies = ObjectiveManager.IsCurrentOrder<AIObjectiveFightIntruders>() || ObjectiveManager.Objectives.Any(o => o is AIObjectiveFightIntruders);
bool ignoreEnemies = ObjectiveManager.IsCurrentOrder<AIObjectiveFightIntruders>() || ObjectiveManager.GetActiveObjectives<AIObjectiveFightIntruders>().Any();
float safety = CalculateHullSafety(hull, visibleHulls, character, ignoreWater, ignoreOxygen, ignoreFire, ignoreEnemies);
if (isCurrentHull)
{

View File

@@ -51,19 +51,13 @@ namespace Barotrauma
private set;
}
/// <summary>
/// Returns true if the current or the next node is in ladders.
/// </summary>
public bool InLadders =>
currentPath != null && currentPath.CurrentNode != null &&
(currentPath.CurrentNode.Ladders != null && currentPath.CurrentNode.Ladders.Item.IsInteractable(character) ||
(currentPath.NextNode != null && currentPath.NextNode.Ladders != null && currentPath.NextNode.Ladders.Item.IsInteractable(character)));
/// <summary>
/// Returns true if any node in the path is in stairs
/// </summary>
public bool InStairs => currentPath != null && currentPath.Nodes.Any(n => n.Stairs != null);
public bool IsCurrentNodeLadder => currentPath?.CurrentNode?.Ladders != null && currentPath.CurrentNode.Ladders.Item.IsInteractable(character);
public bool IsNextNodeLadder => GetNextLadder() != null;
public bool IsNextLadderSameAsCurrent
@@ -99,14 +93,24 @@ namespace Barotrauma
base.Update(speed);
float step = 1.0f / 60.0f;
checkDoorsTimer -= step;
buttonPressTimer -= step;
if (lastDoor.door == null || !lastDoor.shouldBeOpen || lastDoor.door.IsOpen)
{
buttonPressTimer = 0;
}
else
{
buttonPressTimer -= step;
}
findPathTimer -= step;
}
public void SetPath(SteeringPath path)
{
currentPath = path;
if (path.Nodes.Any()) currentTarget = path.Nodes[path.Nodes.Count - 1].SimPosition;
if (path.Nodes.Any())
{
currentTarget = path.Nodes[path.Nodes.Count - 1].SimPosition;
}
findPathTimer = Math.Min(findPathTimer, 1.0f);
IsPathDirty = false;
}
@@ -124,15 +128,9 @@ namespace Barotrauma
public void SteeringSeek(Vector2 target, float weight, float minGapWidth = 0, Func<PathNode, bool> startNodeFilter = null, Func<PathNode, bool> endNodeFilter = null, Func<PathNode, bool> nodeFilter = null, bool checkVisiblity = true)
{
if (buttonPressTimer > 0 && lastDoor.door != null && lastDoor.state && !lastDoor.door.IsOpen)
{
// We have pressed the button and are waiting for the door to open -> Hold still until we can press the button again.
Reset();
}
else
{
steering += CalculateSteeringSeek(target, weight, minGapWidth, startNodeFilter, endNodeFilter, nodeFilter, checkVisiblity);
}
// Have to use a variable here or resetting doesn't work.
Vector2 addition = CalculateSteeringSeek(target, weight, minGapWidth, startNodeFilter, endNodeFilter, nodeFilter, checkVisiblity);
steering += addition;
}
/// <summary>
@@ -328,7 +326,13 @@ namespace Barotrauma
{
CheckDoorsInPath();
doorsChecked = true;
}
}
if (buttonPressTimer > 0 && lastDoor.door != null && lastDoor.shouldBeOpen && !lastDoor.door.IsOpen)
{
// We have pressed the button and are waiting for the door to open -> Hold still until we can press the button again.
Reset();
return Vector2.Zero;
}
Vector2 pos = host.WorldPosition;
bool isDiving = character.AnimController.InWater && character.AnimController.HeadInWater;
// Only humanoids can climb ladders
@@ -378,7 +382,7 @@ namespace Barotrauma
//at the same height as the waypoint
if (Math.Abs(collider.SimPosition.Y - currentPath.CurrentNode.SimPosition.Y) < (collider.height / 2 + collider.radius) * 1.25f)
{
float heightFromFloor = character.AnimController.GetColliderBottom().Y - character.AnimController.FloorY;
float heightFromFloor = character.AnimController.GetHeightFromFloor();
if (heightFromFloor <= 0.0f)
{
diff.Y = Math.Max(diff.Y, 100);
@@ -516,7 +520,7 @@ namespace Barotrauma
return ConvertUnits.ToDisplayUnits(Math.Max(colliderSize.X, colliderSize.Y));
}
private (Door door, bool state) lastDoor;
private (Door door, bool shouldBeOpen) lastDoor;
private float GetDoorCheckTime()
{
if (steering.LengthSquared() > 0)
@@ -539,7 +543,6 @@ namespace Barotrauma
WayPoint nextWaypoint = null;
Door door = null;
bool shouldBeOpen = false;
if (currentPath.Nodes.Count == 1)
{
door = currentPath.Nodes.First().ConnectedDoor;
@@ -645,7 +648,7 @@ namespace Barotrauma
});
if (canAccess)
{
bool pressButton = buttonPressTimer <= 0 || lastDoor.door != door || lastDoor.state != shouldBeOpen;
bool pressButton = buttonPressTimer <= 0 || lastDoor.door != door || lastDoor.shouldBeOpen != shouldBeOpen;
if (door.HasIntegratedButtons)
{
if (pressButton && character.CanSeeTarget(door.Item))
@@ -653,7 +656,7 @@ namespace Barotrauma
if (door.Item.TryInteract(character, forceSelectKey: true))
{
lastDoor = (door, shouldBeOpen);
buttonPressTimer = buttonPressCooldown;
buttonPressTimer = shouldBeOpen ? buttonPressCooldown : 0;
}
else
{
@@ -671,7 +674,7 @@ namespace Barotrauma
if (closestButton.Item.TryInteract(character, forceSelectKey: true))
{
lastDoor = (door, shouldBeOpen);
buttonPressTimer = buttonPressCooldown;
buttonPressTimer = shouldBeOpen ? buttonPressCooldown : 0;
}
else
{
@@ -697,7 +700,6 @@ namespace Barotrauma
// The button is on the wrong side of the door or a wall
currentPath.Unreachable = true;
}
lastDoor = (null, false);
return;
}
}

View File

@@ -16,7 +16,6 @@ namespace Barotrauma
public virtual bool IgnoreUnsafeHulls => false;
public virtual bool AbandonWhenCannotCompleteSubjectives => true;
public virtual bool AllowSubObjectiveSorting => false;
public virtual bool ForceOrderPriority => true;
public virtual bool PrioritizeIfSubObjectivesActive => false;
/// <summary>

View File

@@ -85,11 +85,12 @@ namespace Barotrauma
bool equip = item.GetComponent<Holdable>() != null ||
item.AllowedSlots.Any(s => s != InvSlotType.Any) &&
item.AllowedSlots.None(s =>
s == InvSlotType.Card ||
s == InvSlotType.Head ||
s == InvSlotType.Headset ||
s == InvSlotType.InnerClothes ||
s == InvSlotType.OuterClothes);
s == InvSlotType.Card ||
s == InvSlotType.Head ||
s == InvSlotType.Headset ||
s == InvSlotType.InnerClothes ||
s == InvSlotType.OuterClothes ||
s == InvSlotType.HealthInterface);
TryAddSubObjective(ref decontainObjective, () => new AIObjectiveDecontainItem(character, item, objectiveManager, targetContainer: suitableContainer.GetComponent<ItemContainer>())
{

View File

@@ -11,7 +11,7 @@ namespace Barotrauma
public override string Identifier { get; set; } = "cleanup items";
public override bool KeepDivingGearOn => true;
public override bool AllowAutomaticItemUnequipping => false;
public override bool ForceOrderPriority => false;
protected override bool ForceOrderPriority => false;
public readonly List<Item> prioritizedItems = new List<Item>();

View File

@@ -117,7 +117,7 @@ namespace Barotrauma
private float AimSpeed => HumanAIController.AimSpeed;
private float AimAccuracy => HumanAIController.AimAccuracy;
private bool EnemyIsClose() => Enemy != null && character.CurrentHull != null && character.CurrentHull == Enemy.CurrentHull || Vector2.DistanceSquared(character.Position, Enemy.Position) < 500;
private bool EnemyIsClose() => Enemy != null && Enemy.CurrentHull != null && HumanAIController.VisibleHulls.Contains(Enemy.CurrentHull) && Math.Abs(character.WorldPosition.X - Enemy.WorldPosition.X) < 300;
public AIObjectiveCombat(Character character, Character enemy, CombatMode mode, AIObjectiveManager objectiveManager, float priorityModifier = 1, float coolDown = 10.0f)
: base(character, objectiveManager, priorityModifier)
@@ -366,7 +366,7 @@ namespace Barotrauma
}
}
}
bool isAllowedToSeekWeapons = !EnemyIsClose() && character.TeamID != CharacterTeamType.FriendlyNPC && IsOffensiveOrArrest;
bool isAllowedToSeekWeapons = character.CurrentHull != null && !EnemyIsClose() && character.TeamID != CharacterTeamType.FriendlyNPC && IsOffensiveOrArrest;
if (!isAllowedToSeekWeapons)
{
if (WeaponComponent == null)
@@ -1190,19 +1190,5 @@ namespace Barotrauma
}
}
}
//private float CalculateEnemyStrength()
//{
// float enemyStrength = 0;
// AttackContext currentContext = character.GetAttackContext();
// foreach (Limb limb in Enemy.AnimController.Limbs)
// {
// if (limb.attack == null) continue;
// if (!limb.attack.IsValidContext(currentContext)) { continue; }
// if (!limb.attack.IsValidTarget(AttackTarget.Character)) { continue; }
// enemyStrength += limb.attack.GetTotalDamage(false);
// }
// return enemyStrength;
//}
}
}

View File

@@ -159,7 +159,8 @@ namespace Barotrauma
{
TargetName = container.Item.Name,
AbortCondition = obj =>
container?.Item == null || container.Item.Removed || container.Item.IsThisOrAnyContainerIgnoredByAI(character) ||
container?.Item == null || container.Item.Removed || container.Item.IsThisOrAnyContainerIgnoredByAI(character) ||
(container.Item.GetRootContainer()?.OwnInventory?.Locked ?? false) ||
ItemToContain == null || ItemToContain.Removed ||
!ItemToContain.IsOwnedBy(character) || container.Item.GetRootInventoryOwner() is Character c && c != character,
SpeakIfFails = !objectiveManager.IsCurrentOrder<AIObjectiveCleanupItems>()

View File

@@ -40,6 +40,7 @@ namespace Barotrauma
public Func<Item, bool> RemoveExistingPredicate { get; set; }
public int? RemoveExistingMax { get; set; }
public string AbandonGetItemDialogueIdentifier { get; set; }
public Func<bool> AbandonGetItemDialogueCondition { get; set; }
public AIObjectiveDecontainItem(Character character, Item targetItem, AIObjectiveManager objectiveManager, ItemContainer sourceContainer = null, ItemContainer targetContainer = null, float priorityModifier = 1)
: base(character, objectiveManager, priorityModifier)
@@ -106,6 +107,7 @@ namespace Barotrauma
TryAddSubObjective(ref getItemObjective,
constructor: () => new AIObjectiveGetItem(character, targetItem, objectiveManager, Equip)
{
CannotFindDialogueCondition = AbandonGetItemDialogueCondition,
CannotFindDialogueIdentifierOverride = AbandonGetItemDialogueIdentifier,
SpeakIfFails = AbandonGetItemDialogueIdentifier != null,
TakeWholeStack = this.TakeWholeStack

View File

@@ -26,6 +26,7 @@ namespace Barotrauma
if (totalEnemies == 0) { return 0; }
if (character.IsSecurity) { return 100; }
if (objectiveManager.IsOrder(this)) { return 100; }
// If there's any security officers onboard, leave fighting for them.
return HumanAIController.IsTrueForAnyCrewMember(c => c.Character.IsSecurity && !c.Character.IsIncapacitated && c.Character.Submarine == character.Submarine) ? 0 : 100;
}
@@ -64,6 +65,7 @@ namespace Barotrauma
if (target.CurrentHull == null) { return false; }
if (HumanAIController.IsFriendly(character, target)) { return false; }
if (!character.Submarine.IsConnectedTo(target.Submarine)) { return false; }
if (character.Submarine.TeamID != target.Submarine.TeamID) { return false; }
if (target.HasAbilityFlag(AbilityFlags.IgnoredByEnemyAI)) { return false; }
if (target.IsArrested) { return false; }
return true;

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