(99feac023) Unstable 0.9.704.0

This commit is contained in:
Joonas Rikkonen
2020-02-07 20:47:03 +02:00
parent 590619459b
commit 6754b9d5a2
104 changed files with 2224 additions and 1091 deletions
@@ -28,6 +28,7 @@ namespace Barotrauma
} }
ShapeExtensions.DrawCircle(spriteBatch, pos, SoundRange, 100, color, thickness: 1 / Screen.Selected.Cam.Zoom); ShapeExtensions.DrawCircle(spriteBatch, pos, SoundRange, 100, color, thickness: 1 / Screen.Selected.Cam.Zoom);
ShapeExtensions.DrawCircle(spriteBatch, pos, 3, 8, color, thickness: 2 / Screen.Selected.Cam.Zoom); ShapeExtensions.DrawCircle(spriteBatch, pos, 3, 8, color, thickness: 2 / Screen.Selected.Cam.Zoom);
GUI.DrawLine(spriteBatch, pos, pos + Vector2.UnitY * SoundRange, color, width: (int)(1 / Screen.Selected.Cam.Zoom) + 1);
} }
if (sightRange > 0.0f) if (sightRange > 0.0f)
{ {
@@ -39,8 +40,6 @@ namespace Barotrauma
else if (Entity is Item) else if (Entity is Item)
{ {
color = Color.CadetBlue; color = Color.CadetBlue;
// disable the indicators for items, because they clutter the debug view
return;
} }
else else
{ {
@@ -50,6 +49,7 @@ namespace Barotrauma
} }
ShapeExtensions.DrawCircle(spriteBatch, pos, SightRange, 100, color, thickness: 1 / Screen.Selected.Cam.Zoom); ShapeExtensions.DrawCircle(spriteBatch, pos, SightRange, 100, color, thickness: 1 / Screen.Selected.Cam.Zoom);
ShapeExtensions.DrawCircle(spriteBatch, pos, 6, 8, color, thickness: 2 / Screen.Selected.Cam.Zoom); ShapeExtensions.DrawCircle(spriteBatch, pos, 6, 8, color, thickness: 2 / Screen.Selected.Cam.Zoom);
GUI.DrawLine(spriteBatch, pos, pos + Vector2.UnitY * SightRange, color, width: (int)(1 / Screen.Selected.Cam.Zoom) + 1);
} }
} }
} }
@@ -16,9 +16,9 @@ namespace Barotrauma
}*/ }*/
} }
partial void SetOrderProjSpecific(Order order) partial void SetOrderProjSpecific(Order order, string option)
{ {
GameMain.GameSession.CrewManager.DisplayCharacterOrder(Character, order); GameMain.GameSession.CrewManager.DisplayCharacterOrder(Character, order, option);
} }
public override void DebugDraw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch) public override void DebugDraw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch)
@@ -171,10 +171,10 @@ namespace Barotrauma
if (GUI.DisableHUD) { return; } if (GUI.DisableHUD) { return; }
character.CharacterHealth.Alignment = Alignment.Right; character.CharacterHealth.Alignment = Alignment.Right;
if (Screen.Selected == GameMain.GameScreen) /*if (Screen.Selected == GameMain.GameScreen)
{ {
GUI.InfoAreaBackground.Draw(spriteBatch, Vector2.Zero, scale: GUI.Scale); GUI.InfoAreaBackground.Draw(spriteBatch, Vector2.Zero, scale: GUI.Scale);
} }*/
if (GameMain.GameSession?.CrewManager != null) if (GameMain.GameSession?.CrewManager != null)
{ {
@@ -318,7 +318,7 @@ namespace Barotrauma
if (ShouldDrawInventory(character)) if (ShouldDrawInventory(character))
{ {
character.Inventory.Locked = LockInventory(character); character.Inventory.Locked = LockInventory(character);
character.Inventory.DrawOwn(spriteBatch); character.Inventory.DrawThis(spriteBatch);
character.Inventory.CurrentLayout = CharacterHealth.OpenHealthWindow == null && character.SelectedCharacter == null ? character.Inventory.CurrentLayout = CharacterHealth.OpenHealthWindow == null && character.SelectedCharacter == null ?
CharacterInventory.Layout.Default : CharacterInventory.Layout.Default :
CharacterInventory.Layout.Right; CharacterInventory.Layout.Right;
@@ -333,7 +333,7 @@ namespace Barotrauma
{ {
///character.Inventory.CurrentLayout = Alignment.Left; ///character.Inventory.CurrentLayout = Alignment.Left;
character.SelectedCharacter.Inventory.CurrentLayout = CharacterInventory.Layout.Left; character.SelectedCharacter.Inventory.CurrentLayout = CharacterInventory.Layout.Left;
character.SelectedCharacter.Inventory.DrawOwn(spriteBatch); character.SelectedCharacter.Inventory.DrawThis(spriteBatch);
} }
else else
{ {
@@ -134,7 +134,8 @@ namespace Barotrauma
private float heartbeatTimer; private float heartbeatTimer;
private Texture2D heartrateFade; private Texture2D heartrateFade;
private HeartratePosition[] heartbeatPattern = { private readonly HeartratePosition[] heartbeatPattern =
{
new HeartratePosition() { Time = 0.0f, Height = 0.0f }, new HeartratePosition() { Time = 0.0f, Height = 0.0f },
new HeartratePosition() { Time = 0.15f, Height = 0.2f }, new HeartratePosition() { Time = 0.15f, Height = 0.2f },
new HeartratePosition() { Time = 0.2f, Height = -0.2f }, new HeartratePosition() { Time = 0.2f, Height = -0.2f },
@@ -174,7 +175,7 @@ namespace Barotrauma
// 0-1 // 0-1
private float damageIntensity; private float damageIntensity;
private float damageIntensityDropdownRate = 0.1f; private readonly float damageIntensityDropdownRate = 0.1f;
public float DamageOverlayTimer { get; private set; } public float DamageOverlayTimer { get; private set; }
@@ -259,14 +260,14 @@ namespace Barotrauma
bool horizontal = true; bool horizontal = true;
healthBar = new GUIProgressBar(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.HealthBarAreaLeft, GUI.Canvas), healthBar = new GUIProgressBar(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.HealthBarAreaLeft, GUI.Canvas),
barSize: 1.0f, color: GUIColorSettings.HealthBarColorHigh, style: horizontal ? "CharacterHealthBar" : "GUIProgressBarVertical", false) barSize: 1.0f, color: GUIColorSettings.HealthBarColorHigh, style: horizontal ? "CharacterHealthBar" : "GUIProgressBarVertical")
{ {
Enabled = true, Enabled = true,
HoverCursor = CursorState.Hand, HoverCursor = CursorState.Hand,
IsHorizontal = horizontal IsHorizontal = horizontal
}; };
healthBarShadow = new GUIProgressBar(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.HealthBarAreaLeft, GUI.Canvas), healthBarShadow = new GUIProgressBar(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.HealthBarAreaLeft, GUI.Canvas),
barSize: 1.0f, color: Color.Green, style: horizontal ? "CharacterHealthBar" : "GUIProgressBarVertical", false) barSize: 1.0f, color: Color.Green, style: horizontal ? "CharacterHealthBar" : "GUIProgressBarVertical", showFrame: false)
{ {
IsHorizontal = horizontal IsHorizontal = horizontal
}; };
@@ -281,9 +282,13 @@ namespace Barotrauma
//limb selection frame //limb selection frame
healthWindow = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.9f), healthWindowContainer.RectTransform, Anchor.CenterRight, Pivot.CenterRight), style: "GUIFrameListBox"); healthWindow = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.9f), healthWindowContainer.RectTransform, Anchor.CenterRight, Pivot.CenterRight), style: "GUIFrameListBox");
var healthWindowVerticalLayout = new GUILayoutGroup(new RectTransform(Vector2.One * 0.95f, healthWindow.RectTransform, Anchor.Center)); var healthWindowVerticalLayout = new GUILayoutGroup(new RectTransform(Vector2.One * 0.95f, healthWindow.RectTransform, Anchor.Center))
{
Stretch = true,
RelativeSpacing = 0.03f
};
var paddedHealthWindow = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.95f), healthWindowVerticalLayout.RectTransform), true) var paddedHealthWindow = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.95f), healthWindowVerticalLayout.RectTransform), isHorizontal: true)
{ {
Stretch = true, Stretch = true,
RelativeSpacing = 0.03f RelativeSpacing = 0.03f
@@ -292,7 +297,7 @@ namespace Barotrauma
var limbSelection = new GUICustomComponent(new RectTransform(new Vector2(0.6f, 1.0f), paddedHealthWindow.RectTransform), var limbSelection = new GUICustomComponent(new RectTransform(new Vector2(0.6f, 1.0f), paddedHealthWindow.RectTransform),
(spriteBatch, component) => (spriteBatch, component) =>
{ {
DrawHealthWindow(spriteBatch, component.RectTransform.Rect, true, false); DrawHealthWindow(spriteBatch, component.RectTransform.Rect, true);
}, },
(deltaTime, component) => (deltaTime, component) =>
{ {
@@ -332,11 +337,15 @@ namespace Barotrauma
GUILayoutGroup selectedLimbLayout = new GUILayoutGroup(new RectTransform(Vector2.One, rightSide.RectTransform)); GUILayoutGroup selectedLimbLayout = new GUILayoutGroup(new RectTransform(Vector2.One, rightSide.RectTransform));
selectedLimbText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.08f), selectedLimbLayout.RectTransform), "", font: GUI.LargeFont); selectedLimbText = new GUITextBlock(new RectTransform(new Vector2(0.8f, 0.08f), selectedLimbLayout.RectTransform), "", font: GUI.LargeFont, textAlignment: Alignment.Center)
selectedLimbText.AutoScaleHorizontal = true; {
AutoScaleHorizontal = true
};
afflictionIconContainer = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.92f), selectedLimbLayout.RectTransform), style: null); afflictionIconContainer = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.92f), selectedLimbLayout.RectTransform), style: null)
afflictionIconContainer.KeepSpaceForScrollBar = true; {
KeepSpaceForScrollBar = true
};
var healthBarContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.07f), healthWindowVerticalLayout.RectTransform), style: null); var healthBarContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.07f), healthWindowVerticalLayout.RectTransform), style: null);
@@ -393,7 +402,7 @@ namespace Barotrauma
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.01f), textLayout.RectTransform), style: "HorizontalLine"); new GUIFrame(new RectTransform(new Vector2(1.0f, 0.01f), textLayout.RectTransform), style: "HorizontalLine");
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), textLayout.RectTransform, Anchor.TopLeft), TextManager.Get("SuitableTreatments"), textAlignment: Alignment.TopLeft); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), textLayout.RectTransform, Anchor.TopLeft), TextManager.Get("SuitableTreatments"), font: GUI.SubHeadingFont);
treatmentLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), textLayout.RectTransform), true) treatmentLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), textLayout.RectTransform), true)
{ {
@@ -431,9 +440,11 @@ namespace Barotrauma
heartbeatTimer = 0.46f; heartbeatTimer = 0.46f;
heartratePositions = new List<HeartratePosition>(); heartratePositions = new List<HeartratePosition>
heartratePositions.Add(heartbeatPattern.First()); {
heartratePositions.Add(heartbeatPattern.Last()); heartbeatPattern.First(),
heartbeatPattern.Last()
};
cprButton = new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), cprLayout.RectTransform, scaleBasis: ScaleBasis.Smallest), text: "", style: "CPRButton") cprButton = new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), cprLayout.RectTransform, scaleBasis: ScaleBasis.Smallest), text: "", style: "CPRButton")
{ {
@@ -718,16 +729,17 @@ namespace Barotrauma
if (afflictionTooltip == null || afflictionTooltip.UserData != affliction) if (afflictionTooltip == null || afflictionTooltip.UserData != affliction)
{ {
afflictionTooltip = new GUIListBox(new RectTransform(new Vector2(0.4f, 0.2f), GUI.Canvas, scaleBasis: ScaleBasis.Smallest)); afflictionTooltip = new GUIListBox(new RectTransform(new Vector2(0.4f, 0.2f), GUI.Canvas, scaleBasis: ScaleBasis.Smallest))
afflictionTooltip.UserData = affliction; {
UserData = affliction,
afflictionTooltip.CanBeFocused = false; CanBeFocused = false
};
CreateAfflictionInfoElements(afflictionTooltip.Content, affliction); CreateAfflictionInfoElements(afflictionTooltip.Content, affliction);
int height = afflictionTooltip.Content.Children.Sum(c => c.Rect.Height) + 10; int height = afflictionTooltip.Content.Children.Sum(c => c.Rect.Height) + 10;
afflictionTooltip.RectTransform.Resize(new Point(afflictionTooltip.Rect.Width, height), true); afflictionTooltip.RectTransform.Resize(new Point(afflictionTooltip.Rect.Width, height), true);
afflictionTooltip.RectTransform.AbsoluteOffset = GUI.MouseOn.Rect.Center; afflictionTooltip.RectTransform.AbsoluteOffset = new Point(GUI.MouseOn.Rect.Right, GUI.MouseOn.Rect.Y);
afflictionTooltip.ScrollBarVisible = false; afflictionTooltip.ScrollBarVisible = false;
var labelContainer = afflictionTooltip.Content.GetChildByUserData("label"); var labelContainer = afflictionTooltip.Content.GetChildByUserData("label");
@@ -1105,6 +1117,9 @@ namespace Barotrauma
Dictionary<string, float> treatmentSuitability = new Dictionary<string, float>(); Dictionary<string, float> treatmentSuitability = new Dictionary<string, float>();
GetSuitableTreatments(treatmentSuitability, normalize: true, randomization: randomVariance); GetSuitableTreatments(treatmentSuitability, normalize: true, randomization: randomVariance);
Affliction mostSevereAffliction = afflictions.FirstOrDefault(a => !a.Prefab.IsBuff && !afflictions.Any(a2 => !a2.Prefab.IsBuff && a2.Strength > a.Strength)) ?? afflictions.FirstOrDefault();
GUIButton buttonToSelect = null;
foreach (Affliction affliction in afflictions) foreach (Affliction affliction in afflictions)
{ {
var child = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), afflictionIconContainer.Content.RectTransform, Anchor.TopCenter)) var child = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), afflictionIconContainer.Content.RectTransform, Anchor.TopCenter))
@@ -1117,16 +1132,24 @@ namespace Barotrauma
Color = Color.Gray.Multiply(0.1f).Opaque(), Color = Color.Gray.Multiply(0.1f).Opaque(),
HoverColor = Color.Gray.Multiply(0.4f).Opaque(), HoverColor = Color.Gray.Multiply(0.4f).Opaque(),
SelectedColor = Color.Gray.Multiply(0.25f).Opaque(), SelectedColor = Color.Gray.Multiply(0.25f).Opaque(),
PressedColor = Color.Black, PressedColor = Color.Gray.Multiply(0.2f).Opaque(),
UserData = "selectaffliction", UserData = "selectaffliction",
OnClicked = SelectAffliction OnClicked = SelectAffliction
}; };
new GUIImage(new RectTransform(Vector2.One * 0.9f, button.RectTransform, Anchor.Center), affliction.Prefab.Icon, scaleToFit: true) if (affliction == mostSevereAffliction)
{
buttonToSelect = button;
}
var afflictionIcon = new GUIImage(new RectTransform(Vector2.One * 0.9f, button.RectTransform, Anchor.Center), affliction.Prefab.Icon, scaleToFit: true)
{ {
Color = GetAfflictionIconColor(affliction.Prefab, affliction), Color = GetAfflictionIconColor(affliction.Prefab, affliction),
CanBeFocused = false CanBeFocused = false
}; };
afflictionIcon.PressedColor = afflictionIcon.Color;
afflictionIcon.HoverColor = Color.Lerp(afflictionIcon.Color, Color.White, 0.6f);
afflictionIcon.SelectedColor = Color.Lerp(afflictionIcon.Color, Color.White, 0.5f);
float afflictionVitalityDecrease = affliction.GetVitalityDecrease(this); float afflictionVitalityDecrease = affliction.GetVitalityDecrease(this);
@@ -1140,7 +1163,7 @@ namespace Barotrauma
afflictionEffectColor = GUI.Style.Green; afflictionEffectColor = GUI.Style.Green;
} }
new GUIProgressBar(new RectTransform(new Vector2(1.0f, 0.1f), child.RectTransform), 0.0f, afflictionEffectColor) new GUIProgressBar(new RectTransform(new Vector2(1.0f, 0.1f), child.RectTransform), 0.0f, afflictionEffectColor, style: "CharacterHealthBar")
{ {
UserData = "afflictionstrength" UserData = "afflictionstrength"
}; };
@@ -1148,6 +1171,8 @@ namespace Barotrauma
child.Recalculate(); child.Recalculate();
} }
if (buttonToSelect != null) { buttonToSelect.OnClicked(buttonToSelect, "selectaffliction"); }
afflictionIconContainer.RecalculateChildren(); afflictionIconContainer.RecalculateChildren();
List<KeyValuePair<string, float>> treatmentSuitabilities = treatmentSuitability.OrderByDescending(t => t.Value).ToList(); List<KeyValuePair<string, float>> treatmentSuitabilities = treatmentSuitability.OrderByDescending(t => t.Value).ToList();
@@ -1157,8 +1182,7 @@ namespace Barotrauma
{ {
count++; count++;
if (count > 5) { break; } if (count > 5) { break; }
ItemPrefab item = MapEntityPrefab.Find(name: null, identifier: treatment.Key, showErrorMessages: false) as ItemPrefab; if (!(MapEntityPrefab.Find(name: null, identifier: treatment.Key, showErrorMessages: false) is ItemPrefab item)) { continue; }
if (item == null) continue;
var itemSlot = new GUIFrame(new RectTransform(new Vector2(1.0f / 7.0f, 1.0f), recommendedTreatmentContainer.Content.RectTransform, Anchor.TopLeft), var itemSlot = new GUIFrame(new RectTransform(new Vector2(1.0f / 7.0f, 1.0f), recommendedTreatmentContainer.Content.RectTransform, Anchor.TopLeft),
style: null) style: null)
@@ -1299,7 +1323,9 @@ namespace Barotrauma
private void UpdateHeartrate(float deltaTime, GUICustomComponent component) private void UpdateHeartrate(float deltaTime, GUICustomComponent component)
{ {
heartbeatTimer -= deltaTime * 0.75f; if (GameMain.Instance.Paused) { return; }
heartbeatTimer -= deltaTime * 0.5f;
if (heartbeatTimer <= 0.0f) if (heartbeatTimer <= 0.0f)
{ {
@@ -1335,7 +1361,7 @@ namespace Barotrauma
} }
} }
currentHeartrateTime += deltaTime * 0.75f; currentHeartrateTime += deltaTime * 0.5f;
while (currentHeartrateTime >= 1.0f) while (currentHeartrateTime >= 1.0f)
{ {
currentHeartrateTime -= 1.0f; currentHeartrateTime -= 1.0f;
@@ -1377,9 +1403,9 @@ namespace Barotrauma
Rectangle sourceRect = heartrateFade.Bounds; Rectangle sourceRect = heartrateFade.Bounds;
Rectangle destinationRectangle = new Rectangle(); Rectangle destinationRectangle = new Rectangle(
destinationRectangle.Location = new Point((int)(currentHeartrateTime * targetRect.Width) + targetRect.Left - targetRect.Height, targetRect.Top); new Point((int)(currentHeartrateTime * targetRect.Width) + targetRect.Left - targetRect.Height, targetRect.Top),
destinationRectangle.Size = new Point((int)(targetRect.Height * ((float)sourceRect.Width / (float)sourceRect.Height)), targetRect.Height); new Point((int)(targetRect.Height * ((float)sourceRect.Width / (float)sourceRect.Height)), targetRect.Height));
if (destinationRectangle.Left < targetRect.Left) if (destinationRectangle.Left < targetRect.Left)
{ {
@@ -1557,7 +1583,7 @@ namespace Barotrauma
} }
} }
private void DrawHealthWindow(SpriteBatch spriteBatch, Rectangle drawArea, bool allowHighlight, bool highlightAll) private void DrawHealthWindow(SpriteBatch spriteBatch, Rectangle drawArea, bool allowHighlight)
{ {
if (Character.Removed) { return; } if (Character.Removed) { return; }
@@ -1586,16 +1612,16 @@ namespace Barotrauma
float midPoint = (float)limbEffectiveArea.Center.Y / (float)limbHealth.IndicatorSprite.Texture.Height; float midPoint = (float)limbEffectiveArea.Center.Y / (float)limbHealth.IndicatorSprite.Texture.Height;
float fadeDist = 0.6f * (float)limbEffectiveArea.Height / (float)limbHealth.IndicatorSprite.Texture.Height; float fadeDist = 0.6f * (float)limbEffectiveArea.Height / (float)limbHealth.IndicatorSprite.Texture.Height;
if (negativeEffect > 0.0f && negativeEffect < 5.0f) { negativeEffect = 5.0f; } if (negativeEffect > 0.0f && negativeEffect < 5.0f) { negativeEffect = 10.0f; }
if (positiveEffect > 0.0f && positiveEffect < 5.0f) { positiveEffect = 5.0f; } if (positiveEffect > 0.0f && positiveEffect < 5.0f) { positiveEffect = 10.0f; }
Color positiveColor = Color.Lerp(Color.Orange, Color.Lime, Math.Min(positiveEffect / 15.0f, 1.0f)); Color positiveColor = Color.Lerp(Color.Orange, Color.Lime, Math.Min(positiveEffect / 25.0f, 1.0f));
Color negativeColor = Color.Lerp(Color.Orange, Color.Red, Math.Min(negativeEffect / 15.0f, 1.0f)); Color negativeColor = Color.Lerp(Color.Orange, Color.Red, Math.Min(negativeEffect / 25.0f, 1.0f));
Color color1 = Color.Orange; Color color1 = Color.Orange;
Color color2 = Color.Orange; Color color2 = Color.Orange;
if (negativeEffect+positiveEffect > 0.0f) if (negativeEffect + positiveEffect > 0.0f)
{ {
if (negativeEffect >= positiveEffect) if (negativeEffect >= positiveEffect)
{ {
@@ -1614,11 +1640,6 @@ namespace Barotrauma
color1 = Color.Lerp(color1, Color.Black, 0.75f); color1 = Color.Lerp(color1, Color.Black, 0.75f);
color2 = Color.Lerp(color2, Color.Black, 0.75f); color2 = Color.Lerp(color2, Color.Black, 0.75f);
} }
if (((i == highlightedLimbIndex || i == selectedLimbIndex) && allowHighlight) || highlightAll)
{
color1 = Color.Lerp(color1, Color.White, 0.5f);
color2 = Color.Lerp(color2, Color.White, 0.5f);
}
GameMain.GameScreen.GradientEffect.Parameters["color1"].SetValue(color1.ToVector4()); GameMain.GameScreen.GradientEffect.Parameters["color1"].SetValue(color1.ToVector4());
GameMain.GameScreen.GradientEffect.Parameters["color2"].SetValue(color2.ToVector4()); GameMain.GameScreen.GradientEffect.Parameters["color2"].SetValue(color2.ToVector4());
@@ -1666,36 +1687,75 @@ namespace Barotrauma
limbIndicatorOverlay.Draw(spriteBatch, frame, drawArea.Center.ToVector2(), Color.Gray, origin: limbIndicatorOverlay.FrameSize.ToVector2() / 2, rotate: 0.0f, limbIndicatorOverlay.Draw(spriteBatch, frame, drawArea.Center.ToVector2(), Color.Gray, origin: limbIndicatorOverlay.FrameSize.ToVector2() / 2, rotate: 0.0f,
scale: Vector2.One * overlayScale); scale: Vector2.One * overlayScale);
if (allowHighlight)
{
i = 0;
foreach (LimbHealth limbHealth in limbHealths)
{
if (limbHealth.HighlightSprite == null) { continue; }
float scale = Math.Min(drawArea.Width / (float)limbHealth.HighlightSprite.SourceRect.Width, drawArea.Height / (float)limbHealth.HighlightSprite.SourceRect.Height);
int drawCount = 0;
if (i == highlightedLimbIndex) { drawCount++; }
if (i == selectedLimbIndex) { drawCount++; }
for (int j = 0; j < drawCount; j++)
{
limbHealth.HighlightSprite.Draw(spriteBatch,
drawArea.Center.ToVector2(), Color.White,
limbHealth.HighlightSprite.Origin,
0, scale);
}
i++;
}
}
spriteBatch.End(); spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.Deferred, blendState: BlendState.NonPremultiplied, rasterizerState: GameMain.ScissorTestEnable); spriteBatch.Begin(SpriteSortMode.Deferred, blendState: BlendState.NonPremultiplied, rasterizerState: GameMain.ScissorTestEnable);
i = 0; i = 0;
foreach (LimbHealth limbHealth in limbHealths) foreach (LimbHealth limbHealth in limbHealths)
{ {
if (limbHealth.IndicatorSprite == null) continue; IEnumerable<Affliction> thisAfflictions = limbHealth.Afflictions.Where(a => a.Strength >= a.Prefab.ShowIconThreshold);
thisAfflictions = thisAfflictions.Concat(afflictions.Where(a =>
{
Limb indicatorLimb = Character.AnimController.GetLimb(a.Prefab.IndicatorLimb);
return (indicatorLimb != null && indicatorLimb.HealthIndex == i && a.Strength >= a.Prefab.ShowIconThreshold);
}));
Rectangle highlightArea = GetLimbHighlightArea(limbHealth, drawArea); if (thisAfflictions.Count() <= 0) { i++; continue; }
if (limbHealth.IndicatorSprite == null) { continue; }
float scale = Math.Min(drawArea.Width / (float)limbHealth.IndicatorSprite.SourceRect.Width, drawArea.Height / (float)limbHealth.IndicatorSprite.SourceRect.Height); float scale = Math.Min(drawArea.Width / (float)limbHealth.IndicatorSprite.SourceRect.Width, drawArea.Height / (float)limbHealth.IndicatorSprite.SourceRect.Height);
float iconScale = 0.3f * scale; Rectangle highlightArea = GetLimbHighlightArea(limbHealth, drawArea);
float iconScale = 0.25f * scale;
Vector2 iconPos = highlightArea.Center.ToVector2(); Vector2 iconPos = highlightArea.Center.ToVector2();
foreach (Affliction affliction in limbHealth.Afflictions)
Affliction mostSevereAffliction = thisAfflictions.FirstOrDefault(a => !a.Prefab.IsBuff && !thisAfflictions.Any(a2 => !a2.Prefab.IsBuff && a2.Strength > a.Strength)) ?? thisAfflictions.FirstOrDefault();
if (mostSevereAffliction != null) { DrawLimbAfflictionIcon(spriteBatch, mostSevereAffliction, iconScale, ref iconPos); }
if (thisAfflictions.Count() > 1)
{ {
DrawLimbAfflictionIcon(spriteBatch, affliction, iconScale, ref iconPos); string additionalAfflictionCount = $"+{thisAfflictions.Count() - 1}";
Vector2 displace = GUI.SubHeadingFont.MeasureString(additionalAfflictionCount);
GUI.SubHeadingFont.DrawString(spriteBatch, additionalAfflictionCount, iconPos + new Vector2(displace.X * 1.1f, -displace.Y * 0.45f), Color.Black * 0.75f);
GUI.SubHeadingFont.DrawString(spriteBatch, additionalAfflictionCount, iconPos + new Vector2(displace.X, -displace.Y * 0.5f), Color.White);
} }
foreach (Affliction affliction in afflictions)
{
Limb indicatorLimb = Character.AnimController.GetLimb(affliction.Prefab.IndicatorLimb);
if (indicatorLimb != null && indicatorLimb.HealthIndex == i)
{
DrawLimbAfflictionIcon(spriteBatch, affliction, iconScale, ref iconPos);
}
}
i++; i++;
} }
if (selectedLimbIndex > -1)
{
var selectedLimbArea = GetLimbHighlightArea(limbHealths[selectedLimbIndex], drawArea);
GUI.DrawLine(spriteBatch,
new Vector2(selectedLimbText.Rect.X, selectedLimbText.Rect.Center.Y),
selectedLimbArea.Center.ToVector2(),
Color.LightGray * 0.5f, width: 4);
}
if (draggingMed != null) if (draggingMed != null)
{ {
GUIImage itemImage = draggingMed.GetChild<GUIImage>(); GUIImage itemImage = draggingMed.GetChild<GUIImage>();
@@ -32,7 +32,7 @@ namespace Barotrauma
font: GUI.SmallFont); font: GUI.SmallFont);
} }
if (!ItemNames.TryGetValue(variant, out var itemNames)) { return backFrame; } if (!ItemIdentifiers.TryGetValue(variant, out var itemIdentifiers)) { return backFrame; }
var itemContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.45f, 0.5f), paddedFrame.RectTransform, Anchor.TopRight) var itemContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.45f, 0.5f), paddedFrame.RectTransform, Anchor.TopRight)
{ RelativeOffset = new Vector2(0.0f, 0.2f + descriptionBlock.RectTransform.RelativeSize.Y) }) { RelativeOffset = new Vector2(0.0f, 0.2f + descriptionBlock.RectTransform.RelativeSize.Y) })
{ {
@@ -40,11 +40,12 @@ namespace Barotrauma
}; };
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), itemContainer.RectTransform), new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), itemContainer.RectTransform),
TextManager.Get("Items", fallBackTag: "mapentitycategory.equipment"), font: GUI.LargeFont); TextManager.Get("Items", fallBackTag: "mapentitycategory.equipment"), font: GUI.LargeFont);
foreach (string itemName in itemNames.Distinct()) foreach (string identifier in itemIdentifiers.Distinct())
{ {
int count = itemNames.Count(i => i == itemName); if (!(MapEntityPrefab.Find(name: null, identifier: identifier) is ItemPrefab itemPrefab)) { continue; }
int count = itemIdentifiers.Count(i => i == identifier);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), itemContainer.RectTransform), new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), itemContainer.RectTransform),
" - " + (count == 1 ? itemName : itemName + " x" + count), " - " + (count == 1 ? itemPrefab.Name : itemPrefab.Name + " x" + count),
font: GUI.SmallFont); font: GUI.SmallFont);
} }
@@ -0,0 +1,25 @@
using Barotrauma.Networking;
namespace Barotrauma
{
partial class CargoMission : Mission
{
public override void ClientReadInitial(IReadMessage msg)
{
ushort itemCount = msg.ReadUInt16();
for (int i = 0; i < itemCount; i++)
{
items.Add(Item.ReadSpawnData(msg));
}
if (items.Contains(null))
{
throw new System.Exception("Error in CargoMission.ClientReadInitial: item list contains null (mission: " + Prefab.Identifier + ")");
}
if (items.Count != itemCount)
{
throw new System.Exception("Error in CargoMission.ClientReadInitial: item count does not match the server count (" + itemCount + " != " + items.Count + "mission: " + Prefab.Identifier + ")");
}
if (requiredDeliveryAmount == 0) { requiredDeliveryAmount = items.Count; }
}
}
}
@@ -1,4 +1,6 @@
namespace Barotrauma using Barotrauma.Networking;
namespace Barotrauma
{ {
partial class CombatMission partial class CombatMission
{ {
@@ -18,5 +20,10 @@
return descriptions[GameMain.Client.Character.TeamID == Character.TeamType.Team1 ? 1 : 2]; return descriptions[GameMain.Client.Character.TeamID == Character.TeamType.Team1 ? 1 : 2];
} }
} }
public override void ClientReadInitial(IReadMessage msg)
{
//do nothing
}
} }
} }
@@ -23,5 +23,7 @@ namespace Barotrauma
{ {
State = msg.ReadInt16(); State = msg.ReadInt16();
} }
public abstract void ClientReadInitial(IReadMessage msg);
} }
} }
@@ -0,0 +1,25 @@
using Barotrauma.Networking;
namespace Barotrauma
{
partial class MonsterMission : Mission
{
public override void ClientReadInitial(IReadMessage msg)
{
byte monsterCount = msg.ReadByte();
for (int i = 0; i < monsterCount; i++)
{
monsters.Add(Character.ReadSpawnData(msg));
}
if (monsters.Contains(null))
{
throw new System.Exception("Error in MonsterMission.ClientReadInitial: monster list contains null (mission: " + Prefab.Identifier + ")");
}
if (monsters.Count != monsterCount)
{
throw new System.Exception("Error in MonsterMission.ClientReadInitial: monster count does not match the server count (" + monsterCount + " != " + monsters.Count + "mission: " + Prefab.Identifier + ")");
}
InitializeMonsters(monsters);
}
}
}
@@ -0,0 +1,19 @@
using Barotrauma.Networking;
using FarseerPhysics;
namespace Barotrauma
{
partial class SalvageMission : Mission
{
public override void ClientReadInitial(IReadMessage msg)
{
item = Item.ReadSpawnData(msg);
if (item == null)
{
throw new System.Exception("Error in SalvageMission.ClientReadInitial: spawned item was null (mission: " + Prefab.Identifier + ")");
}
item.body.FarseerBody.BodyType = BodyType.Kinematic;
}
}
}
@@ -37,6 +37,8 @@ namespace Barotrauma
} }
private float openState; private float openState;
public bool CloseAfterMessageSent;
private float prevUIScale; private float prevUIScale;
//individual message texts that pop up when the chatbox is hidden //individual message texts that pop up when the chatbox is hidden
@@ -286,6 +288,10 @@ namespace Barotrauma
public void SetVisibility(bool visible) public void SetVisibility(bool visible)
{ {
GUIFrame.Parent.Visible = visible; GUIFrame.Parent.Visible = visible;
if (GameMain.GameSession?.CrewManager?.ReportButtonFrame != null)
{
GameMain.GameSession.CrewManager.ReportButtonFrame.Visible = visible;
}
} }
private IEnumerable<object> UpdateMessageAnimation(GUIComponent message, float animDuration) private IEnumerable<object> UpdateMessageAnimation(GUIComponent message, float animDuration)
@@ -330,6 +336,17 @@ namespace Barotrauma
prevUIScale = GUI.Scale; prevUIScale = GUI.Scale;
} }
//hide chatbox when accessing the inventory of another character to prevent overlaps
if (Character.Controlled?.SelectedCharacter?.Inventory != null &&
Character.Controlled.SelectedCharacter.CanInventoryBeAccessed)
{
SetVisibility(false);
}
else
{
SetVisibility(true);
}
if (showNewMessagesButton.Visible && chatBox.ScrollBar.BarScroll == 1f) if (showNewMessagesButton.Visible && chatBox.ScrollBar.BarScroll == 1f)
{ {
showNewMessagesButton.Visible = false; showNewMessagesButton.Visible = false;
@@ -59,11 +59,15 @@ namespace Barotrauma
public readonly GUIStyle Style; public readonly GUIStyle Style;
public readonly string Name;
public int? Width { get; private set; } public int? Width { get; private set; }
public int? Height { get; private set; } public int? Height { get; private set; }
public GUIComponentStyle(XElement element, GUIStyle style) public GUIComponentStyle(XElement element, GUIStyle style)
{ {
Name = element.Name.LocalName;
Style = style; Style = style;
Element = element; Element = element;
@@ -133,6 +133,7 @@ namespace Barotrauma
public static ScalableFont LargeFont => Style?.LargeFont; public static ScalableFont LargeFont => Style?.LargeFont;
public static ScalableFont SubHeadingFont => Style?.SubHeadingFont; public static ScalableFont SubHeadingFont => Style?.SubHeadingFont;
public static ScalableFont DigitalFont => Style?.DigitalFont; public static ScalableFont DigitalFont => Style?.DigitalFont;
public static ScalableFont HotkeyFont => Style?.HotkeyFont;
public static ScalableFont CJKFont { get; private set; } public static ScalableFont CJKFont { get; private set; }
@@ -988,6 +989,7 @@ namespace Barotrauma
Debug.Assert(updateList.Count == updateListSet.Count); Debug.Assert(updateList.Count == updateListSet.Count);
updateList.ForEach(c => c.UpdateAuto(deltaTime)); updateList.ForEach(c => c.UpdateAuto(deltaTime));
UpdateMessages(deltaTime); UpdateMessages(deltaTime);
UpdateInput();
} }
private static void UpdateMessages(float deltaTime) private static void UpdateMessages(float deltaTime)
@@ -1027,6 +1029,32 @@ namespace Barotrauma
messages.RemoveAll(m => m.Timer <= 0.0f); messages.RemoveAll(m => m.Timer <= 0.0f);
} }
private static void UpdateInput()
{
if (PlayerInput.KeyHit(InputType.ToggleInventory))
{
if (Character.Controlled?.Inventory != null)
{
Character.Controlled.Inventory.ToggleInventory();
}
}
if (PlayerInput.KeyHit(Keys.Escape) && GameMain.WindowActive)
{
HandleEscFunctionality();
}
#if DEBUG
if (GameMain.NetworkMember == null)
{
if (PlayerInput.KeyHit(Keys.P) && !(KeyboardDispatcher.Subscriber is GUITextBox))
{
DebugConsole.Paused = !DebugConsole.Paused;
}
}
#endif
}
#region Element drawing #region Element drawing
public static void DrawIndicator(SpriteBatch spriteBatch, Vector2 worldPosition, Camera cam, float hideDist, Sprite sprite, Color color) public static void DrawIndicator(SpriteBatch spriteBatch, Vector2 worldPosition, Camera cam, float hideDist, Sprite sprite, Color color)
@@ -1760,32 +1788,30 @@ namespace Barotrauma
if (!rect1.Intersects(rect2)) continue; if (!rect1.Intersects(rect2)) continue;
intersections = true; intersections = true;
int rect1Area = rect1.Width * rect1.Height;
int rect2Area = rect2.Width * rect2.Height;
Point centerDiff = rect1.Center - rect2.Center; Point centerDiff = rect1.Center - rect2.Center;
//move the interfaces away from each other, in a random direction if they're at the same position //move the interfaces away from each other, in a random direction if they're at the same position
Vector2 moveAmount = centerDiff == Point.Zero ? Rand.Vector(1.0f) : Vector2.Normalize(centerDiff.ToVector2()); Vector2 moveAmount = centerDiff == Point.Zero ? Rand.Vector(1.0f) : Vector2.Normalize(centerDiff.ToVector2());
//if the horizontal move amount is much larger than vertical, only move horizontally //if the horizontal move amount is much larger than vertical, only move horizontally
//(= attempt to place the elements side-by-side if they're more apart horizontally than vertically) //(= attempt to place the elements side-by-side if they're more apart horizontally than vertically)
if (Math.Abs(moveAmount.X) > Math.Abs(moveAmount.Y) * 5.0f) if (Math.Abs(moveAmount.X) > Math.Abs(moveAmount.Y) * 8.0f)
{ {
moveAmount.Y = 0.0f; moveAmount.Y = 0.0f;
} }
//same for the y-axis //same for the y-axis
else if (Math.Abs(moveAmount.Y) > Math.Abs(moveAmount.X) * 5.0f) else if (Math.Abs(moveAmount.Y) > Math.Abs(moveAmount.X) * 8.0f)
{ {
moveAmount.X = 0.0f; moveAmount.X = 0.0f;
} }
//make sure we don't move the interfaces out of the screen //make sure we don't move the interfaces out of the screen
Vector2 moveAmount1 = ClampMoveAmount(rect1, area, moveAmount * 20.0f * rect1Area / (rect1Area + rect2Area)); Vector2 moveAmount1 = ClampMoveAmount(rect1, area, moveAmount * 10.0f);
Vector2 moveAmount2 = ClampMoveAmount(rect2, area, -moveAmount * 20.0f * rect1Area / (rect1Area + rect2Area)); Vector2 moveAmount2 = ClampMoveAmount(rect2, area, -moveAmount * 10.0f);
//move by 10 units in the desired direction and repeat until nothing overlaps //move by 10 units in the desired direction and repeat until nothing overlaps
//(or after 100 iterations, in which case we'll just give up and let them overlap) //(or after 100 iterations, in which case we'll just give up and let them overlap)
elements[i].RectTransform.ScreenSpaceOffset += (moveAmount1).ToPoint(); elements[i].RectTransform.ScreenSpaceOffset += moveAmount1.ToPoint();
elements[j].RectTransform.ScreenSpaceOffset += (moveAmount2).ToPoint(); elements[j].RectTransform.ScreenSpaceOffset += moveAmount2.ToPoint();
} }
if (disallowedAreas == null) continue; if (disallowedAreas == null) continue;
@@ -1834,6 +1860,50 @@ namespace Barotrauma
#endregion #endregion
#region Misc #region Misc
private static void HandleEscFunctionality()
{
// Check if a text input is selected.
if (KeyboardDispatcher.Subscriber != null)
{
if (KeyboardDispatcher.Subscriber is GUITextBox textBox)
{
textBox.Deselect();
}
KeyboardDispatcher.Subscriber = null;
}
//if a verification prompt (are you sure you want to x) is open, close it
else if (GUIMessageBox.VisibleBox as GUIMessageBox != null &&
GUIMessageBox.VisibleBox.UserData as string == "verificationprompt")
{
((GUIMessageBox)GUIMessageBox.VisibleBox).Close();
}
else if (Tutorials.Tutorial.Initialized && Tutorials.Tutorial.ContentRunning)
{
(GameMain.GameSession.GameMode as TutorialMode).Tutorial.CloseActiveContentGUI();
}
else if (PauseMenuOpen)
{
TogglePauseMenu();
}
//open the pause menu if not controlling a character OR if the character has no UIs active that can be closed with ESC
else if ((Character.Controlled == null || !itemHudActive())
//TODO: do we need to check Inventory.SelectedSlot?
&& Inventory.SelectedSlot == null && CharacterHealth.OpenHealthWindow == null
&& !CrewManager.IsCommandInterfaceOpen)
{
// Otherwise toggle pausing, unless another window/interface is open.
TogglePauseMenu();
}
bool itemHudActive()
{
if (Character.Controlled?.SelectedConstruction == null) { return false; }
return
Character.Controlled.SelectedConstruction.ActiveHUDs.Any(ic => ic.GuiFrame != null) ||
((Character.Controlled.ViewTarget as Item)?.Prefab?.FocusOnSelected ?? false);
}
}
public static void TogglePauseMenu() public static void TogglePauseMenu()
{ {
if (Screen.Selected == GameMain.MainMenuScreen) return; if (Screen.Selected == GameMain.MainMenuScreen) return;
@@ -5,22 +5,23 @@ namespace Barotrauma
public class GUIColorSettings public class GUIColorSettings
{ {
// Inventory // Inventory
public static Color InventorySlotColor = new Color(78, 114, 88); public static readonly Color InventorySlotColor = new Color(27, 140, 132);
public static Color EquipmentSlotColor = new Color(72, 58, 25); public static readonly Color InventorySlotEquippedColor = new Color(211, 227, 217);
public static Color EquipmentSlotIconColor = new Color(99, 70, 64); public static readonly Color EquipmentSlotEmptyColor = new Color(152, 148, 128);
public static readonly Color EquipmentSlotColor = new Color(225, 211, 189);
public static readonly Color EquipmentSlotIconColor = new Color(99, 70, 64);
// Health HUD // Health HUD
public static Color BuffColorLow = Color.LightGreen; public static readonly Color BuffColorLow = Color.LightGreen;
public static Color BuffColorMedium = Color.Green; public static readonly Color BuffColorMedium = Color.Green;
public static Color BuffColorHigh = Color.DarkGreen; public static readonly Color BuffColorHigh = Color.DarkGreen;
public static Color DebuffColorLow = Color.DarkSalmon; public static readonly Color DebuffColorLow = Color.DarkSalmon;
public static Color DebuffColorMedium = Color.Red; public static readonly Color DebuffColorMedium = Color.Red;
public static Color DebuffColorHigh = Color.DarkRed; public static readonly Color DebuffColorHigh = Color.DarkRed;
public static Color HealthBarColorLow = Color.Red;
public static Color HealthBarColorMedium = Color.Orange;
public static Color HealthBarColorHigh = new Color(78, 114, 88);
public static readonly Color HealthBarColorLow = Color.Red;
public static readonly Color HealthBarColorMedium = Color.Orange;
public static readonly Color HealthBarColorHigh = new Color(78, 114, 88);
} }
} }
@@ -517,6 +517,7 @@ namespace Barotrauma
return state switch return state switch
{ {
ComponentState.Hover => HoverColor, ComponentState.Hover => HoverColor,
ComponentState.HoverSelected => HoverColor,
ComponentState.Pressed => PressedColor, ComponentState.Pressed => PressedColor,
ComponentState.Selected => SelectedColor, ComponentState.Selected => SelectedColor,
_ => Color, _ => Color,
@@ -83,7 +83,7 @@ namespace Barotrauma
get { return floatValue; } get { return floatValue; }
set set
{ {
if (value == floatValue) return; if (MathUtils.NearlyEqual(value, floatValue)) return;
floatValue = value; floatValue = value;
ClampFloatValue(); ClampFloatValue();
float newValue = floatValue; float newValue = floatValue;
@@ -134,7 +134,6 @@ namespace Barotrauma
if (value == intValue) return; if (value == intValue) return;
intValue = value; intValue = value;
UpdateText(); UpdateText();
OnValueChanged?.Invoke(this);
} }
} }
@@ -24,6 +24,7 @@ namespace Barotrauma
public ScalableFont LargeFont { get; private set; } public ScalableFont LargeFont { get; private set; }
public ScalableFont SubHeadingFont { get; private set; } public ScalableFont SubHeadingFont { get; private set; }
public ScalableFont DigitalFont { get; private set; } public ScalableFont DigitalFont { get; private set; }
public ScalableFont HotkeyFont { get; private set; }
public Dictionary<ScalableFont, bool> ForceFontUpperCase public Dictionary<ScalableFont, bool> ForceFontUpperCase
{ {
@@ -63,8 +64,8 @@ namespace Barotrauma
public Color TextColorDark { get; private set; } = Color.Black * 0.9f; public Color TextColorDark { get; private set; } = Color.Black * 0.9f;
public Color TextColorDim { get; private set; } = Color.White * 0.6f; public Color TextColorDim { get; private set; } = Color.White * 0.6f;
public static Point ItemFrameMargin = new Point(50, 56).Multiply(GUI.SlicedSpriteScale); public static Point ItemFrameMargin => new Point(50, 56).Multiply(GUI.SlicedSpriteScale);
public static Point ItemFrameOffset = new Point(0, 3).Multiply(GUI.SlicedSpriteScale); public static Point ItemFrameOffset => new Point(0, 3).Multiply(GUI.SlicedSpriteScale);
public GUIStyle(XElement element, GraphicsDevice graphicsDevice) public GUIStyle(XElement element, GraphicsDevice graphicsDevice)
{ {
@@ -148,6 +149,10 @@ namespace Barotrauma
DigitalFont = LoadFont(subElement, graphicsDevice); DigitalFont = LoadFont(subElement, graphicsDevice);
ForceFontUpperCase[DigitalFont] = subElement.GetAttributeBool("forceuppercase", false); ForceFontUpperCase[DigitalFont] = subElement.GetAttributeBool("forceuppercase", false);
break; break;
case "hotkeyfont":
HotkeyFont = LoadFont(subElement, graphicsDevice);
ForceFontUpperCase[HotkeyFont] = subElement.GetAttributeBool("forceuppercase", false);
break;
case "objectivetitle": case "objectivetitle":
case "subheading": case "subheading":
SubHeadingFont = LoadFont(subElement, graphicsDevice); SubHeadingFont = LoadFont(subElement, graphicsDevice);
@@ -236,8 +241,20 @@ namespace Barotrauma
return new ScalableFont(file, size, graphicsDevice, dynamicLoading, isCJK); return new ScalableFont(file, size, graphicsDevice, dynamicLoading, isCJK);
} }
private uint GetFontSize(XElement element) private uint GetFontSize(XElement element, uint defaultSize = 14)
{ {
//check if any of the language override fonts want to override the font size as well
foreach (XElement subElement in element.Elements())
{
if (subElement.Name.ToString().ToLowerInvariant() != "override") { continue; }
string language = subElement.GetAttributeString("language", "").ToLowerInvariant();
if (GameMain.Config.Language.ToLowerInvariant() == language)
{
uint overrideFontSize = GetFontSize(subElement, 0);
if (overrideFontSize > 0) { return overrideFontSize; }
}
}
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
{ {
if (subElement.Name.ToString().ToLowerInvariant() != "size") { continue; } if (subElement.Name.ToString().ToLowerInvariant() != "size") { continue; }
@@ -247,7 +264,7 @@ namespace Barotrauma
return (uint)subElement.GetAttributeInt("size", 14); return (uint)subElement.GetAttributeInt("size", 14);
} }
} }
return 14; return defaultSize;
} }
private string GetFontFilePath(XElement element) private string GetFontFilePath(XElement element)
@@ -6,14 +6,18 @@ using System.Collections.Generic;
using System.Xml.Linq; using System.Xml.Linq;
using Barotrauma.Media; using Barotrauma.Media;
using System.Linq; using System.Linq;
using Barotrauma.Extensions;
namespace Barotrauma namespace Barotrauma
{ {
class LoadingScreen class LoadingScreen
{ {
private Texture2D backgroundTexture; private readonly Texture2D defaultBackgroundTexture, overlay;
private readonly SpriteSheet decorativeGraph, decorativeMap;
private Texture2D currentBackgroundTexture;
private Sprite noiseSprite;
private RenderTarget2D renderTarget; private string randText = "";
private Sprite languageSelectionCursor; private Sprite languageSelectionCursor;
private ScalableFont languageSelectionFont, languageSelectionFontCJK; private ScalableFont languageSelectionFont, languageSelectionFontCJK;
@@ -65,15 +69,9 @@ namespace Barotrauma
} }
} }
private float state;
private string selectedTip; private string selectedTip;
public Vector2 BackgroundPosition; private readonly object loadMutex = new object();
public Vector2 TitlePosition;
private object loadMutex = new object();
private float? loadState; private float? loadState;
public float? LoadState public float? LoadState
@@ -109,15 +107,13 @@ namespace Barotrauma
public LoadingScreen(GraphicsDevice graphics) public LoadingScreen(GraphicsDevice graphics)
{ {
backgroundTexture = TextureLoader.FromFile("Content/UI/titleBackground.png"); defaultBackgroundTexture = TextureLoader.FromFile("Content/Map/LocationPortraits/AlienRuins.png");
renderTarget = new RenderTarget2D(graphics, GameMain.GraphicsWidth, GameMain.GraphicsHeight); decorativeMap = new SpriteSheet("Content/Map/MapHUD.png", 6, 5, Vector2.Zero, sourceRect: new Rectangle(0, 0, 2048, 640));
GameMain.Instance.OnResolutionChanged += () => decorativeGraph = new SpriteSheet("Content/Map/MapHUD.png", 4, 10, Vector2.Zero, sourceRect: new Rectangle(1025, 1259, 1024, 732));
{
renderTarget?.Dispose();
renderTarget = new RenderTarget2D(graphics, GameMain.GraphicsWidth, GameMain.GraphicsHeight);
};
overlay = TextureLoader.FromFile("Content/UI/LoadingScreenOverlay.png");
noiseSprite = new Sprite("Content/UI/noise.png", Vector2.Zero);
DrawLoadingText = true; DrawLoadingText = true;
selectedTip = TextManager.Get("LoadingScreenTip", true); selectedTip = TextManager.Get("LoadingScreenTip", true);
} }
@@ -147,41 +143,35 @@ namespace Barotrauma
drawn = true; drawn = true;
graphics.SetRenderTarget(renderTarget); currentBackgroundTexture ??= defaultBackgroundTexture;
float backgroundScale = GameMain.GraphicsHeight / 1500.0f;
float titleScale = MathHelper.SmoothStep(0.8f, 1.0f, state / 10.0f) * GameMain.GraphicsHeight / 1000.0f;
state += deltaTime;
if (DrawLoadingText)
{
BackgroundPosition = new Vector2(GameMain.GraphicsWidth * 0.3f, GameMain.GraphicsHeight * 0.45f);
TitlePosition = new Vector2(GameMain.GraphicsWidth * 0.5f, GameMain.GraphicsHeight * 0.45f);
}
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, samplerState: GUI.SamplerState);
graphics.Clear(Color.Black);
spriteBatch.Draw(backgroundTexture, BackgroundPosition, null, Color.White * Math.Min(state / 5.0f, 1.0f), 0.0f,
new Vector2(backgroundTexture.Width / 2.0f, backgroundTexture.Height / 2.0f),
backgroundScale * 1.5f, SpriteEffects.None, 0.2f);
titleSprite?.Draw(spriteBatch, TitlePosition, Color.White * Math.Min((state - 1.0f) / 5.0f, 1.0f), scale: titleScale);
spriteBatch.End();
graphics.SetRenderTarget(null);
if (WaterRenderer.Instance != null)
{
WaterRenderer.Instance.ScrollWater(Vector2.One * 10.0f, deltaTime);
WaterRenderer.Instance.RenderWater(spriteBatch, renderTarget, null);
}
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, samplerState: GUI.SamplerState); spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, samplerState: GUI.SamplerState);
titleSprite?.Draw(spriteBatch, TitlePosition, Color.White * Math.Min((state - 1.0f) / 5.0f, 1.0f), scale: titleScale); float scale = (GameMain.GraphicsWidth / (float)currentBackgroundTexture.Width) * 1.2f;
float paddingX = currentBackgroundTexture.Width * scale - GameMain.GraphicsWidth;
float paddingY = currentBackgroundTexture.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);
spriteBatch.Draw(currentBackgroundTexture,
new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight) / 2 + pos,
null, Color.White, 0.0f, new Vector2(currentBackgroundTexture.Width / 2, currentBackgroundTexture.Height / 2),
scale, SpriteEffects.None, 0.0f);
spriteBatch.Draw(overlay, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), null, Color.White, 0.0f, Vector2.Zero, SpriteEffects.None, 0.0f);
float noiseStrength = (float)PerlinNoise.CalculatePerlin(noiseT, noiseT, 0);
float noiseScale = (float)PerlinNoise.CalculatePerlin(noiseT * 5.0f, noiseT * 2.0f, 0) * 4.0f;
noiseSprite.DrawTiled(spriteBatch, Vector2.Zero, new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight),
startOffset: new Point(Rand.Range(0, noiseSprite.SourceRect.Width), Rand.Range(0, noiseSprite.SourceRect.Height)),
color: Color.White * noiseStrength * 0.1f,
textureScale: Vector2.One * noiseScale);
titleSprite?.Draw(spriteBatch, new Vector2(GameMain.GraphicsWidth * 0.05f, GameMain.GraphicsHeight * 0.125f),
Color.White, origin: new Vector2(0.0f, titleSprite.SourceRect.Height / 2.0f),
scale: GameMain.GraphicsHeight / 2000.0f);
if (WaitForLanguageSelection) if (WaitForLanguageSelection)
{ {
@@ -218,7 +208,7 @@ namespace Barotrauma
if (GUI.LargeFont != null) if (GUI.LargeFont != null)
{ {
GUI.LargeFont.DrawString(spriteBatch, loadText.ToUpper(), GUI.LargeFont.DrawString(spriteBatch, loadText.ToUpper(),
new Vector2(GameMain.GraphicsWidth / 2.0f - GUI.LargeFont.MeasureString(loadText).X / 2.0f, GameMain.GraphicsHeight * 0.7f), new Vector2(GameMain.GraphicsWidth / 2.0f - GUI.LargeFont.MeasureString(loadText.ToUpper()).X / 2.0f, GameMain.GraphicsHeight * 0.75f),
Color.White); Color.White);
} }
} }
@@ -232,12 +222,49 @@ namespace Barotrauma
for (int i = 0; i < lines.Length; i++) for (int i = 0; i < lines.Length; i++)
{ {
GUI.Font.DrawString(spriteBatch, lines[i], GUI.Font.DrawString(spriteBatch, lines[i],
new Vector2((int)(GameMain.GraphicsWidth / 2.0f - GUI.Font.MeasureString(lines[i]).X / 2.0f), (int)(GameMain.GraphicsHeight * 0.78f + i * lineHeight)), Color.White); new Vector2((int)(GameMain.GraphicsWidth / 2.0f - GUI.Font.MeasureString(lines[i]).X / 2.0f), (int)(GameMain.GraphicsHeight * 0.8f + i * lineHeight)), Color.White);
} }
} }
} }
spriteBatch.End(); spriteBatch.End();
spriteBatch.Begin(blendState: BlendState.Additive);
Vector2 decorativeScale = new Vector2(GameMain.GraphicsHeight / 1080.0f);
float noiseVal = (float)PerlinNoise.CalculatePerlin(Timing.TotalTime * 0.25f, Timing.TotalTime * 0.5f, 0);
decorativeGraph.Draw(spriteBatch, (int)(decorativeGraph.FrameCount * noiseVal),
new Vector2(GameMain.GraphicsWidth * 0.001f, GameMain.GraphicsHeight * 0.24f),
Color.White, Vector2.Zero, 0.0f, decorativeScale, SpriteEffects.FlipVertically);
decorativeMap.Draw(spriteBatch, (int)(decorativeMap.FrameCount * noiseVal),
new Vector2(GameMain.GraphicsWidth * 0.99f, GameMain.GraphicsHeight * 0.66f),
Color.White, decorativeMap.FrameSize.ToVector2(), 0.0f, decorativeScale);
if (noiseVal < 0.2f)
{
//SCP-CB reference
randText = (new string[] { "NIL", "black white gray", "Sometimes we would have had time to scream", "e8m106]af", "NO" }).GetRandom();
}
else if (noiseVal < 0.3f)
{
randText = ToolBox.RandomSeed(9);
}
else if (noiseVal < 0.5f)
{
randText =
Rand.Int(100).ToString().PadLeft(2, '0') + " " +
Rand.Int(100).ToString().PadLeft(2, '0') + " " +
Rand.Int(100).ToString().PadLeft(2, '0') + " " +
Rand.Int(100).ToString().PadLeft(2, '0');
}
GUI.LargeFont?.DrawString(spriteBatch, randText,
new Vector2(GameMain.GraphicsWidth - decorativeMap.FrameSize.X * decorativeScale.X * 0.8f, GameMain.GraphicsHeight * 0.57f),
Color.White * (1.0f - noiseVal));
spriteBatch.End();
} }
private void DrawLanguageSelectionPrompt(SpriteBatch spriteBatch, GraphicsDevice graphicsDevice) private void DrawLanguageSelectionPrompt(SpriteBatch spriteBatch, GraphicsDevice graphicsDevice)
@@ -336,6 +363,7 @@ namespace Barotrauma
drawn = false; drawn = false;
LoadState = null; LoadState = null;
selectedTip = TextManager.Get("LoadingScreenTip", true); selectedTip = TextManager.Get("LoadingScreenTip", true);
currentBackgroundTexture = LocationType.List.GetRandom()?.GetPortrait(Rand.Int(int.MaxValue))?.Texture;
while (!drawn) while (!drawn)
{ {
@@ -701,8 +701,16 @@ namespace Barotrauma
public void MatchPivotToAnchor() => MatchPivotToAnchor(Anchor); public void MatchPivotToAnchor() => MatchPivotToAnchor(Anchor);
private Point? animTargetPos;
public Point AnimTargetPos
{
get { return animTargetPos ?? AbsoluteOffset; }
}
public void MoveOverTime(Point targetPos, float duration) public void MoveOverTime(Point targetPos, float duration)
{ {
animTargetPos = targetPos;
CoroutineManager.StartCoroutine(DoMoveAnimation(targetPos, duration)); CoroutineManager.StartCoroutine(DoMoveAnimation(targetPos, duration));
} }
public void ScaleOverTime(Point targetSize, float duration) public void ScaleOverTime(Point targetSize, float duration)
@@ -721,6 +729,7 @@ namespace Barotrauma
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
} }
AbsoluteOffset = targetPos; AbsoluteOffset = targetPos;
animTargetPos = null;
yield return CoroutineStatus.Success; yield return CoroutineStatus.Success;
} }
private IEnumerable<object> DoScaleAnimation(Point targetSize, float duration) private IEnumerable<object> DoScaleAnimation(Point targetSize, float duration)
@@ -37,6 +37,12 @@ namespace Barotrauma
private set; private set;
} }
/// <summary>
/// How much the borders of a sliced sprite are allowed to scale
/// You may for example want to prevent a 1-pixel border from scaling down (and disappearing) on small resolutions
/// </summary>
private float minBorderScale = 0.1f, maxBorderScale = 10.0f;
public bool CrossFadeIn { get; private set; } = true; public bool CrossFadeIn { get; private set; } = true;
public bool CrossFadeOut { get; private set; } = true; public bool CrossFadeOut { get; private set; } = true;
@@ -58,6 +64,9 @@ namespace Barotrauma
Vector4 sliceVec = element.GetAttributeVector4("slice", Vector4.Zero); Vector4 sliceVec = element.GetAttributeVector4("slice", Vector4.Zero);
if (sliceVec != Vector4.Zero) if (sliceVec != Vector4.Zero)
{ {
minBorderScale = element.GetAttributeFloat("minborderscale", 0.1f);
maxBorderScale = element.GetAttributeFloat("minborderscale", 10.0f);
Rectangle slice = new Rectangle((int)sliceVec.X, (int)sliceVec.Y, (int)(sliceVec.Z - sliceVec.X), (int)(sliceVec.W - sliceVec.Y)); Rectangle slice = new Rectangle((int)sliceVec.X, (int)sliceVec.Y, (int)(sliceVec.Z - sliceVec.X), (int)(sliceVec.W - sliceVec.Y));
Slice = true; Slice = true;
@@ -103,7 +112,8 @@ namespace Barotrauma
scale.Y = MathHelper.Clamp((float)rect.Height / (Slices[0].Height + Slices[6].Height), 0, 1); scale.Y = MathHelper.Clamp((float)rect.Height / (Slices[0].Height + Slices[6].Height), 0, 1);
scale.X = MathHelper.Clamp((float)rect.Width / (Slices[0].Width + Slices[2].Width), 0, 1); scale.X = MathHelper.Clamp((float)rect.Width / (Slices[0].Width + Slices[2].Width), 0, 1);
scale.X = scale.Y = Math.Min(Math.Min(scale.X, scale.Y), GUI.SlicedSpriteScale); scale.X = scale.Y =
MathHelper.Clamp(Math.Min(Math.Min(scale.X, scale.Y), GUI.SlicedSpriteScale), minBorderScale, maxBorderScale);
int centerHeight = rect.Height - (int)((Slices[0].Height + Slices[6].Height) * scale.Y); int centerHeight = rect.Height - (int)((Slices[0].Height + Slices[6].Height) * scale.Y);
int centerWidth = rect.Width - (int)((Slices[0].Width + Slices[2].Width) * scale.X); int centerWidth = rect.Width - (int)((Slices[0].Width + Slices[2].Width) * scale.X);
@@ -764,60 +764,6 @@ namespace Barotrauma
SoundPlayer.Update((float)Timing.Step); SoundPlayer.Update((float)Timing.Step);
if (PlayerInput.KeyHit(Keys.Escape) && WindowActive)
{
// Check if a text input is selected.
if (GUI.KeyboardDispatcher.Subscriber != null)
{
if (GUI.KeyboardDispatcher.Subscriber is GUITextBox textBox)
{
textBox.Deselect();
}
GUI.KeyboardDispatcher.Subscriber = null;
}
//if a verification prompt (are you sure you want to x) is open, close it
else if (GUIMessageBox.VisibleBox as GUIMessageBox != null &&
GUIMessageBox.VisibleBox.UserData as string == "verificationprompt")
{
((GUIMessageBox)GUIMessageBox.VisibleBox).Close();
}
else if (Tutorial.Initialized && Tutorial.ContentRunning)
{
(GameSession.GameMode as TutorialMode).Tutorial.CloseActiveContentGUI();
}
else if (GUI.PauseMenuOpen)
{
GUI.TogglePauseMenu();
}
//open the pause menu if not controlling a character OR if the character has no UIs active that can be closed with ESC
else if ((Character.Controlled == null || !itemHudActive())
//TODO: do we need to check Inventory.SelectedSlot?
&& Inventory.SelectedSlot == null && CharacterHealth.OpenHealthWindow == null
&& !CrewManager.IsCommandInterfaceOpen)
{
// Otherwise toggle pausing, unless another window/interface is open.
GUI.TogglePauseMenu();
}
bool itemHudActive()
{
if (Character.Controlled?.SelectedConstruction == null) { return false; }
return
Character.Controlled.SelectedConstruction.ActiveHUDs.Any(ic => ic.GuiFrame != null) ||
((Character.Controlled.ViewTarget as Item)?.Prefab?.FocusOnSelected ?? false);
}
}
#if DEBUG
if (GameMain.NetworkMember == null)
{
if (PlayerInput.KeyHit(Keys.P) && !(GUI.KeyboardDispatcher.Subscriber is GUITextBox))
{
DebugConsole.Paused = !DebugConsole.Paused;
}
}
#endif
GUI.ClearUpdateList(); GUI.ClearUpdateList();
Paused = (DebugConsole.IsOpen || GUI.PauseMenuOpen || GUI.SettingsMenuOpen || Tutorial.ContentRunning || DebugConsole.Paused) && Paused = (DebugConsole.IsOpen || GUI.PauseMenuOpen || GUI.SettingsMenuOpen || Tutorial.ContentRunning || DebugConsole.Paused) &&
(NetworkMember == null || !NetworkMember.GameStarted); (NetworkMember == null || !NetworkMember.GameStarted);
File diff suppressed because it is too large Load Diff
@@ -560,7 +560,7 @@ namespace Barotrauma
} }
}; };
GUITextBlock particleLimitText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("ParticleLimit"), font: GUI.SubHeadingFont); GUITextBlock particleLimitText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("ParticleLimit"), font: GUI.SubHeadingFont, wrap: true);
GUIScrollBar particleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), style: "GUISlider", GUIScrollBar particleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), style: "GUISlider",
barSize: 0.1f) barSize: 0.1f)
{ {
@@ -576,7 +576,7 @@ namespace Barotrauma
}; };
particleScrollBar.OnMoved(particleScrollBar, particleScrollBar.BarScroll); particleScrollBar.OnMoved(particleScrollBar, particleScrollBar.BarScroll);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("LosEffect"), font: GUI.SubHeadingFont); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("LosEffect"), font: GUI.SubHeadingFont, wrap: true);
var losModeDD = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform)); var losModeDD = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform));
losModeDD.AddItem(TextManager.Get("LosModeNone"), LosMode.None); losModeDD.AddItem(TextManager.Get("LosModeNone"), LosMode.None);
losModeDD.AddItem(TextManager.Get("LosModeTransparent"), LosMode.Transparent); losModeDD.AddItem(TextManager.Get("LosModeTransparent"), LosMode.Transparent);
@@ -594,7 +594,7 @@ namespace Barotrauma
return true; return true;
}; };
GUITextBlock LightText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("LightMapScale"), font: GUI.SubHeadingFont) GUITextBlock LightText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("LightMapScale"), font: GUI.SubHeadingFont, wrap: true)
{ {
ToolTip = TextManager.Get("LightMapScaleToolTip") ToolTip = TextManager.Get("LightMapScaleToolTip")
}; };
@@ -638,7 +638,7 @@ namespace Barotrauma
} }
}; };
GUITextBlock HUDScaleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("HUDScale"), font: GUI.SubHeadingFont); GUITextBlock HUDScaleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("HUDScale"), font: GUI.SubHeadingFont, wrap: true);
GUIScrollBar HUDScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), GUIScrollBar HUDScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform),
style: "GUISlider", barSize: 0.1f) style: "GUISlider", barSize: 0.1f)
{ {
@@ -35,12 +35,17 @@ namespace Barotrauma
} }
private static Dictionary<InvSlotType, Sprite> limbSlotIcons; private static Dictionary<InvSlotType, Sprite> limbSlotIcons;
private static Sprite inventoryBackgroundSprite, inventoryExtendButton, inventoryExtendUpArrow, inventoryExtendDownArrow;
public Rectangle InventoryToggleArea;
public bool InventoryToggleContains = false;
private Vector2 inventoryExtendButtonOffset, inventoryArrowOffset;
private int inventoryOpeningOffset;
public const InvSlotType PersonalSlots = InvSlotType.Card | InvSlotType.Headset | InvSlotType.InnerClothes | InvSlotType.OuterClothes | InvSlotType.Head; public const InvSlotType PersonalSlots = InvSlotType.Card | InvSlotType.Headset | InvSlotType.InnerClothes | InvSlotType.OuterClothes | InvSlotType.Head;
private Point screenResolution;
public Vector2[] SlotPositions; public Vector2[] SlotPositions;
private Vector2 bgScale;
private Layout layout; private Layout layout;
public Layout CurrentLayout public Layout CurrentLayout
@@ -50,7 +55,7 @@ namespace Barotrauma
{ {
if (layout == value) return; if (layout == value) return;
layout = value; layout = value;
SetSlotPositions(layout); SetSlotPositions();
} }
} }
public bool Hidden { get; set; } public bool Hidden { get; set; }
@@ -59,6 +64,8 @@ namespace Barotrauma
private float hidePersonalSlotsState; private float hidePersonalSlotsState;
private GUIButton hideButton; private GUIButton hideButton;
private Rectangle personalSlotArea; private Rectangle personalSlotArea;
private bool inventoryOpen = false;
private bool wasInventoryToggledAutomatically = false;
public bool HidePersonalSlots public bool HidePersonalSlots
{ {
@@ -103,10 +110,17 @@ namespace Barotrauma
limbSlotIcons.Add(InvSlotType.LeftHand, new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(634, 0, 128, 128))); limbSlotIcons.Add(InvSlotType.LeftHand, new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(634, 0, 128, 128)));
limbSlotIcons.Add(InvSlotType.RightHand, new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(762, 0, 128, 128))); limbSlotIcons.Add(InvSlotType.RightHand, new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(762, 0, 128, 128)));
limbSlotIcons.Add(InvSlotType.OuterClothes, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(256 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2))); limbSlotIcons.Add(InvSlotType.OuterClothes, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(256 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
inventoryBackgroundSprite = new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(252, 317, 263, 197));
inventoryExtendButton = new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(533, 300, 96, 19));
inventoryExtendUpArrow = new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(640, 310, 10, 10));
inventoryExtendDownArrow = new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(668, 310, 10, 10));
} }
SlotPositions = new Vector2[SlotTypes.Length]; SlotPositions = new Vector2[SlotTypes.Length];
CurrentLayout = Layout.Default; CurrentLayout = Layout.Default;
SetSlotPositions(layout); SetSlotPositions();
GameMain.Instance.OnResolutionChanged += SetSlotPositions;
} }
protected override ItemInventory GetActiveEquippedSubInventory(int slotIndex) protected override ItemInventory GetActiveEquippedSubInventory(int slotIndex)
@@ -193,31 +207,54 @@ namespace Barotrauma
} }
} }
screenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
CalculateBackgroundFrame(); CalculateBackgroundFrame();
} }
protected override void CalculateBackgroundFrame() protected override void CalculateBackgroundFrame()
{ {
Rectangle frame = Rectangle.Empty; Rectangle frame = Rectangle.Empty;
int firstSlotLocationY = 0;
int lastSlotLocationY = 0;
for (int i = 0; i < capacity; i++) for (int i = 0; i < capacity; i++)
{ {
if (HideSlot(i)) continue; if (HideSlot(i)) continue;
if (PersonalSlots.HasFlag(SlotTypes[i])) continue;
if (IsHandSlot(SlotTypes[i])) continue;
if (frame == Rectangle.Empty) if (frame == Rectangle.Empty)
{ {
firstSlotLocationY = slots[i].Rect.Location.Y;
frame = slots[i].Rect; frame = slots[i].Rect;
continue; continue;
} }
frame = Rectangle.Union(frame, slots[i].Rect); frame = Rectangle.Union(frame, slots[i].Rect);
lastSlotLocationY = slots[i].Rect.Location.Y;
} }
frame.Inflate(10, 30);
frame.Location -= new Point(0, 25); frame.Inflate(25f * UIScale, 25f * UIScale);
if (layout == Layout.Default)
{
inventoryOpeningOffset = firstSlotLocationY - lastSlotLocationY;
bgScale = new Vector2((float)frame.Width / (float)inventoryBackgroundSprite.SourceRect.Width, (float)frame.Height / (float)inventoryBackgroundSprite.SourceRect.Height);
inventoryExtendButtonOffset = new Vector2(inventoryExtendButton.size.X * UIScale * 1.5f / 2f, inventoryExtendButton.size.Y * UIScale * 1.5f / 2f + UIScale * 6);
inventoryArrowOffset = new Vector2(inventoryExtendUpArrow.size.X * UIScale * 1.25f / 2f, inventoryExtendUpArrow.size.Y * UIScale * 1.25f / 2f);
InventoryToggleArea = new Rectangle(new Point(frame.Center.X, frame.Top) - inventoryExtendButtonOffset.ToPoint(), inventoryExtendButton.size.ToPoint() + new Point(0, (int)(UIScale * 6f)));
if (!inventoryOpen)
{
frame.Offset(0, inventoryOpeningOffset);
InventoryToggleArea.Offset(0, inventoryOpeningOffset);
}
}
BackgroundFrame = frame; BackgroundFrame = frame;
} }
protected override bool HideSlot(int i) protected override bool HideSlot(int i)
{ {
if (slots[i].Disabled || (hideEmptySlot[i] && Items[i] == null)) return true; if (slots[i].Disabled) return true;
if (layout == Layout.Default) if (layout == Layout.Default)
{ {
@@ -230,19 +267,17 @@ namespace Barotrauma
return true; return true;
} }
//don't show the equip slot if the item is also in the default inventory
if (SlotTypes[i] != InvSlotType.Any && Items[i] != null)
{
for (int j = 0; j < capacity; j++)
{
if (SlotTypes[j] == InvSlotType.Any && Items[j] == Items[i]) return true;
}
}
return false; return false;
} }
private void SetSlotPositions(Layout layout)
protected override bool IsSlotHiddenDueToToggleState(int i)
{ {
return layout == Layout.Default && !inventoryOpen && slots[i].QuickUseKey == Keys.None && SlotTypes[i] == InvSlotType.Any;
}
private void SetSlotPositions()
{
Layout layout = CurrentLayout;
int spacing = (int)(10 * UIScale); int spacing = (int)(10 * UIScale);
Point slotSize = (SlotSpriteSmall.size * UIScale).ToPoint(); Point slotSize = (SlotSpriteSmall.size * UIScale).ToPoint();
int bottomOffset = slotSize.Y + spacing * 2 + ContainedIndicatorHeight; int bottomOffset = slotSize.Y + spacing * 2 + ContainedIndicatorHeight;
@@ -258,27 +293,47 @@ namespace Barotrauma
int personalSlotCount = SlotTypes.Count(s => PersonalSlots.HasFlag(s)); int personalSlotCount = SlotTypes.Count(s => PersonalSlots.HasFlag(s));
int normalSlotCount = SlotTypes.Count(s => !PersonalSlots.HasFlag(s)); int normalSlotCount = SlotTypes.Count(s => !PersonalSlots.HasFlag(s));
int x = GameMain.GraphicsWidth / 2 - normalSlotCount * (slotSize.X + spacing) / 2; int firstRowSlotCount = hotkeyCount > normalSlotCount ? normalSlotCount : hotkeyCount;
int upperX = GameMain.GraphicsWidth - slotSize.X * 2;
int startX = GameMain.GraphicsWidth / 2 - firstRowSlotCount * (slotSize.X + spacing) / 2;
int x = startX;
int equipmentX = GameMain.GraphicsWidth - slotSize.X * 2;
int buttonIndex = 0;
int startY = GameMain.GraphicsHeight - bottomOffset;
int y = startY;
int handIndex = 1;
//make sure the rightmost normal slot doesn't overlap with the personal slots //make sure the rightmost normal slot doesn't overlap with the personal slots
x -= Math.Max((x + normalSlotCount * (slotSize.X + spacing)) - (upperX - personalSlotCount * (slotSize.X + spacing)), 0); x -= Math.Max(x + firstRowSlotCount * (slotSize.X + spacing) - (equipmentX - personalSlotCount * (slotSize.X + spacing)), 0);
int hideButtonSlotIndex = -1; int hideButtonSlotIndex = -1;
for (int i = 0; i < SlotPositions.Length; i++) for (int i = 0; i < SlotPositions.Length; i++)
{ {
if (PersonalSlots.HasFlag(SlotTypes[i])) if (PersonalSlots.HasFlag(SlotTypes[i]))
{ {
SlotPositions[i] = new Vector2(upperX, GameMain.GraphicsHeight - bottomOffset); SlotPositions[i] = new Vector2(equipmentX, startY);
upperX -= slotSize.X + spacing; equipmentX -= slotSize.X + spacing;
personalSlotArea = (hideButtonSlotIndex == -1) ? personalSlotArea = (hideButtonSlotIndex == -1) ?
new Rectangle(SlotPositions[i].ToPoint(), slotSize) : new Rectangle(SlotPositions[i].ToPoint(), slotSize) :
Rectangle.Union(personalSlotArea, new Rectangle(SlotPositions[i].ToPoint(), slotSize)); Rectangle.Union(personalSlotArea, new Rectangle(SlotPositions[i].ToPoint(), slotSize));
hideButtonSlotIndex = i; hideButtonSlotIndex = i;
} }
else if (IsHandSlot(SlotTypes[i]))
{
SlotPositions[i] = new Vector2(startX - (slotSize.X + spacing * 4) - (slotSize.X + spacing) * handIndex, startY);
handIndex--;
}
else else
{ {
SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset); if (buttonIndex >= hotkeyCount)
{
y -= bottomOffset;
buttonIndex = 0;
x = startX;
}
buttonIndex++;
SlotPositions[i] = new Vector2(x, y);
x += slotSize.X + spacing; x += slotSize.X + spacing;
} }
} }
@@ -296,7 +351,6 @@ namespace Barotrauma
break; break;
case Layout.Right: case Layout.Right:
{ {
int extraOffset = 0;
int x = HUDLayoutSettings.InventoryAreaLower.Right; int x = HUDLayoutSettings.InventoryAreaLower.Right;
int personalSlotX = HUDLayoutSettings.InventoryAreaLower.Right - slotSize.X - spacing; int personalSlotX = HUDLayoutSettings.InventoryAreaLower.Right - slotSize.X - spacing;
for (int i = 0; i < slots.Length; i++) for (int i = 0; i < slots.Length; i++)
@@ -308,23 +362,34 @@ namespace Barotrauma
} }
else else
{ {
x -= slotSize.X + spacing; if (i < slots.Length - 5)
{
x -= slotSize.X + spacing;
}
} }
} }
int lowerX = x; int lowerX = x;
int y = GameMain.GraphicsHeight - bottomOffset;
for (int i = 0; i < SlotPositions.Length; i++) for (int i = 0; i < SlotPositions.Length; i++)
{ {
if (HideSlot(i)) continue; if (HideSlot(i)) continue;
if (PersonalSlots.HasFlag(SlotTypes[i])) if (PersonalSlots.HasFlag(SlotTypes[i]))
{ {
SlotPositions[i] = new Vector2(personalSlotX, GameMain.GraphicsHeight - bottomOffset * 2 - extraOffset - spacing * 2); SlotPositions[i] = new Vector2(personalSlotX, y - bottomOffset);
personalSlotX -= slots[i].Rect.Width + spacing; personalSlotX -= slots[i].Rect.Width + spacing;
} }
else else
{ {
SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset - extraOffset); SlotPositions[i] = new Vector2(x, y);
x += slots[i].Rect.Width + spacing; x += slots[i].Rect.Width + spacing;
if (i == SlotPositions.Length - 6)
{
x = lowerX + (slots[i].Rect.Width + spacing) * 2;
y -= bottomOffset;
}
} }
} }
@@ -333,28 +398,37 @@ namespace Barotrauma
{ {
if (!HideSlot(i)) continue; if (!HideSlot(i)) continue;
x -= slots[i].Rect.Width + spacing; x -= slots[i].Rect.Width + spacing;
SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset - extraOffset); SlotPositions[i] = new Vector2(x, y);
} }
} }
break; break;
case Layout.Left: case Layout.Left:
{ {
int x = HUDLayoutSettings.InventoryAreaLower.X; int x = HUDLayoutSettings.InventoryAreaLower.Left;
int y = GameMain.GraphicsHeight - bottomOffset;
int personalSlotX = x; int personalSlotX = x;
for (int i = 0; i < SlotPositions.Length; i++) for (int i = 0; i < SlotPositions.Length; i++)
{ {
if (HideSlot(i)) continue; if (HideSlot(i)) continue;
if (PersonalSlots.HasFlag(SlotTypes[i])) if (PersonalSlots.HasFlag(SlotTypes[i]))
{ {
SlotPositions[i] = new Vector2(personalSlotX, GameMain.GraphicsHeight - bottomOffset * 2 - spacing * 2); SlotPositions[i] = new Vector2(personalSlotX, y - bottomOffset);
personalSlotX += slots[i].Rect.Width + spacing; personalSlotX += slots[i].Rect.Width + spacing;
} }
else else
{ {
SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset); SlotPositions[i] = new Vector2(x, y);
x += slots[i].Rect.Width + spacing; x += slots[i].Rect.Width + spacing;
if (i == SlotPositions.Length - 7)
{
x -= (slots[i].Rect.Width + spacing) * 6;
y -= bottomOffset;
}
} }
} }
for (int i = 0; i < SlotPositions.Length; i++) for (int i = 0; i < SlotPositions.Length; i++)
{ {
if (!HideSlot(i)) continue; if (!HideSlot(i)) continue;
@@ -406,7 +480,11 @@ namespace Barotrauma
{ {
HUDLayoutSettings.InventoryTopY = slots[0].EquipButtonRect.Y - (int)(15 * GUI.Scale); HUDLayoutSettings.InventoryTopY = slots[0].EquipButtonRect.Y - (int)(15 * GUI.Scale);
} }
}
private bool IsHandSlot(InvSlotType slotType)
{
return slotType == InvSlotType.LeftHand || slotType == InvSlotType.RightHand;
} }
protected override void ControlInput(Camera cam) protected override void ControlInput(Camera cam)
@@ -419,6 +497,32 @@ namespace Barotrauma
} }
} }
public void ToggleInventory(bool automaticToggle = false)
{
if (Character.Controlled == null || !Character.Controlled.IsHuman || wasInventoryToggledAutomatically || inventoryOpen && automaticToggle) return;
inventoryOpen = !inventoryOpen;
if (inventoryOpen)
{
wasInventoryToggledAutomatically = automaticToggle;
BackgroundFrame.Offset(0, -inventoryOpeningOffset);
InventoryToggleArea.Offset(0, -inventoryOpeningOffset);
}
else
{
BackgroundFrame.Offset(0, inventoryOpeningOffset);
InventoryToggleArea.Offset(0, inventoryOpeningOffset);
}
}
private void HandleAutomaticInventoryState()
{
if (wasInventoryToggledAutomatically && inventoryOpen && Character.Controlled.SelectedConstruction == null && Character.Controlled.SelectedCharacter == null)
{
wasInventoryToggledAutomatically = false;
ToggleInventory();
}
}
public override void Update(float deltaTime, Camera cam, bool isSubInventory = false) public override void Update(float deltaTime, Camera cam, bool isSubInventory = false)
{ {
if (!AccessibleWhenAlive && !character.IsDead) if (!AccessibleWhenAlive && !character.IsDead)
@@ -427,6 +531,8 @@ namespace Barotrauma
return; return;
} }
HandleAutomaticInventoryState();
base.Update(deltaTime, cam); base.Update(deltaTime, cam);
bool hoverOnInventory = GUI.MouseOn == null && bool hoverOnInventory = GUI.MouseOn == null &&
@@ -460,7 +566,7 @@ namespace Barotrauma
for (int i = 0; i < capacity; i++) for (int i = 0; i < capacity; i++)
{ {
if (Items[i] != null && Items[i] != draggingItem && Character.Controlled?.Inventory == this && if (Items[i] != null && Items[i] != draggingItem && Character.Controlled?.Inventory == this &&
GUI.KeyboardDispatcher.Subscriber == null && GUI.KeyboardDispatcher.Subscriber == null && !CrewManager.IsCommandInterfaceOpen &&
slots[i].QuickUseKey != Keys.None && PlayerInput.KeyHit(slots[i].QuickUseKey)) slots[i].QuickUseKey != Keys.None && PlayerInput.KeyHit(slots[i].QuickUseKey))
{ {
QuickUseItem(Items[i], true, false, true); QuickUseItem(Items[i], true, false, true);
@@ -608,7 +714,10 @@ namespace Barotrauma
private void HandleButtonEquipStates(Item item, InventorySlot slot, float deltaTime) private void HandleButtonEquipStates(Item item, InventorySlot slot, float deltaTime)
{ {
slot.EquipButtonState = slot.EquipButtonRect.Contains(PlayerInput.MousePosition) ? Rectangle modifiedRect = slot.EquipButtonRect;
modifiedRect.Width = slot.InteractRect.Width;
slot.EquipButtonState = modifiedRect.Contains(PlayerInput.MousePosition) ?
GUIComponent.ComponentState.Hover : GUIComponent.ComponentState.None; GUIComponent.ComponentState.Hover : GUIComponent.ComponentState.None;
if (PlayerInput.LeftButtonHeld() && PlayerInput.RightButtonHeld()) if (PlayerInput.LeftButtonHeld() && PlayerInput.RightButtonHeld())
{ {
@@ -682,6 +791,8 @@ namespace Barotrauma
continue; continue;
} }
if (num > hotkeyCount) break;
if (SlotTypes[i] == InvSlotType.Any) if (SlotTypes[i] == InvSlotType.Any)
{ {
slots[i].QuickUseKey = Keys.D0 + num % 10; slots[i].QuickUseKey = Keys.D0 + num % 10;
@@ -827,7 +938,7 @@ namespace Barotrauma
if (character.SelectedCharacter != null && character.SelectedCharacter.Inventory != null) if (character.SelectedCharacter != null && character.SelectedCharacter.Inventory != null)
{ {
//player has selected the inventory of another character -> attempt to move the item there //player has selected the inventory of another character -> attempt to move the item there
success = character.SelectedCharacter.Inventory.TryPutItem(item, Character.Controlled, item.AllowedSlots, true); success = character.SelectedCharacter.Inventory.TryPutItem(item, Character.Controlled, item.AllowedSlots, true, true);
} }
break; break;
case QuickUseAction.PutToContainer: case QuickUseAction.PutToContainer:
@@ -843,7 +954,7 @@ namespace Barotrauma
character.SelectedBy.Inventory != null) character.SelectedBy.Inventory != null)
{ {
//item is in the inventory of another character -> attempt to get the item from there //item is in the inventory of another character -> attempt to get the item from there
success = character.SelectedBy.Inventory.TryPutItemWithAutoEquipCheck(item, Character.Controlled, item.AllowedSlots, true); success = character.SelectedBy.Inventory.TryPutItemWithAutoEquipCheck(item, Character.Controlled, item.AllowedSlots, true, true);
} }
break; break;
case QuickUseAction.TakeFromContainer: case QuickUseAction.TakeFromContainer:
@@ -862,7 +973,7 @@ namespace Barotrauma
// No subinventory found or placing unsuccessful -> attempt to put in the character's inventory // No subinventory found or placing unsuccessful -> attempt to put in the character's inventory
if (!success) if (!success)
{ {
success = TryPutItemWithAutoEquipCheck(item, Character.Controlled, item.AllowedSlots, true); success = TryPutItemWithAutoEquipCheck(item, Character.Controlled, item.AllowedSlots, true, true);
} }
break; break;
case QuickUseAction.PutToEquippedItem: case QuickUseAction.PutToEquippedItem:
@@ -894,107 +1005,87 @@ namespace Barotrauma
GUI.PlayUISound(success ? GUISoundType.PickItem : GUISoundType.PickItemFail); GUI.PlayUISound(success ? GUISoundType.PickItem : GUISoundType.PickItemFail);
} }
public void DrawOwn(SpriteBatch spriteBatch) public void DrawThis(SpriteBatch spriteBatch)
{ {
if (!AccessibleWhenAlive && !character.IsDead) return; if (!AccessibleWhenAlive && !character.IsDead) return;
if (slots == null) CreateSlots(); if (slots == null) CreateSlots();
if (GameMain.GraphicsWidth != screenResolution.X ||
GameMain.GraphicsHeight != screenResolution.Y ||
prevUIScale != UIScale ||
prevHUDScale != GUI.Scale)
{
SetSlotPositions(layout);
prevUIScale = UIScale;
prevHUDScale = GUI.Scale;
}
if (layout == Layout.Center)
{
CalculateBackgroundFrame();
GUI.DrawRectangle(spriteBatch, BackgroundFrame, Color.Black * 0.8f, true);
GUI.DrawString(spriteBatch,
new Vector2((int)(BackgroundFrame.Center.X - GUI.Font.MeasureString(character.Name).X / 2), (int)BackgroundFrame.Y + 5),
character.Name, Color.White * 0.9f);
}
for (int i = 0; i < capacity; i++)
{
if (HideSlot(i)) continue;
Rectangle interactRect = slots[i].InteractRect;
interactRect.Location += slots[i].DrawOffset.ToPoint();
//don't draw the item if it's being dragged out of the slot
bool drawItem = draggingItem == null || draggingItem != Items[i] || interactRect.Contains(PlayerInput.MousePosition);
DrawSlot(spriteBatch, this, slots[i], Items[i], i, drawItem, SlotTypes[i]);
}
if (hideButton != null && hideButton.Visible && !Locked) if (hideButton != null && hideButton.Visible && !Locked)
{ {
hideButton.DrawManually(spriteBatch, alsoChildren: true); hideButton.DrawManually(spriteBatch, alsoChildren: true);
} }
if (layout == Layout.Default)
{
inventoryBackgroundSprite.Draw(spriteBatch, BackgroundFrame.Location.ToVector2(), Color.White, Vector2.Zero, rotate: 0, scale: bgScale);
Vector2 backgroundFrameCenter = new Vector2(BackgroundFrame.Center.X, BackgroundFrame.Top);
inventoryExtendButton.Draw(spriteBatch, backgroundFrameCenter - inventoryExtendButtonOffset, Color.White, scale: UIScale * 1.5f);
Vector2 arrowPosition = backgroundFrameCenter - inventoryArrowOffset;
if (inventoryOpen)
{
inventoryExtendDownArrow.Draw(spriteBatch, arrowPosition, Color.White, scale: UIScale * 1.25f);
}
else
{
inventoryExtendUpArrow.Draw(spriteBatch, arrowPosition, Color.White, scale: UIScale * 1.25f);
}
GUI.DrawString(spriteBatch, arrowPosition + new Vector2(UIScale * 25, -3 * UIScale), GameMain.Config.KeyBindText(InputType.ToggleInventory), Color.White, font: GUI.HotkeyFont);
InventoryToggleContains = InventoryToggleArea.Contains(PlayerInput.MousePosition);
if (InventoryToggleContains && PlayerInput.PrimaryMouseButtonClicked())
{
ToggleInventory();
}
}
InventorySlot highlightedQuickUseSlot = null; InventorySlot highlightedQuickUseSlot = null;
for (int i = 0; i < capacity; i++) for (int i = 0; i < capacity; i++)
{ {
if (HideSlot(i)) continue; if (HideSlot(i)) continue;
if (IsSlotHiddenDueToToggleState(i)) continue;
if (Items[i] == null || InventorySlot slot = slots[i];
(draggingItem == Items[i] && !slots[i].InteractRect.Contains(PlayerInput.MousePosition)) ||
!Items[i].AllowedSlots.Any(a => a != InvSlotType.Any)) Item item = Items[i];
InvSlotType slotType = SlotTypes[i];
Rectangle interactRect = slot.InteractRect;
interactRect.Location += slot.DrawOffset.ToPoint();
//don't draw the item if it's being dragged out of the slot
bool drawItem = draggingItem == null || draggingItem != item || interactRect.Contains(PlayerInput.MousePosition);
DrawSlot(spriteBatch, this, slot, item, i, drawItem, slotType);
if (item == null || (draggingItem == item && !slot.InteractRect.Contains(PlayerInput.MousePosition)) || !item.AllowedSlots.Any(a => a != InvSlotType.Any))
{ {
//draw limb icons on empty slots //draw limb icons on empty slots
if (limbSlotIcons.ContainsKey(SlotTypes[i])) if (limbSlotIcons.ContainsKey(slotType))
{ {
var icon = limbSlotIcons[SlotTypes[i]]; var icon = limbSlotIcons[slotType];
icon.Draw(spriteBatch, slots[i].Rect.Center.ToVector2() + slots[i].DrawOffset, GUIColorSettings.EquipmentSlotIconColor, origin: icon.size / 2, scale: slots[i].Rect.Width / icon.size.X); icon.Draw(spriteBatch, slot.Rect.Center.ToVector2() + slot.DrawOffset, GUIColorSettings.EquipmentSlotIconColor, origin: icon.size / 2, scale: slot.Rect.Width / icon.size.X);
} }
continue;
}
if (draggingItem == Items[i] && !slots[i].IsHighlighted) continue;
//draw hand icons if the item is equipped in a hand slot // Draw always on hotkeys
if (IsInLimbSlot(Items[i], InvSlotType.LeftHand)) if (slot.QuickUseKey != Keys.None)
{ {
var icon = limbSlotIcons[InvSlotType.LeftHand]; DrawIndicatorAndHotkey(spriteBatch, slot, slot.EquipButtonState == GUIComponent.ComponentState.Hover);
icon.Draw(spriteBatch, new Vector2(slots[i].Rect.X, slots[i].Rect.Bottom) + slots[i].DrawOffset, Color.White * 0.6f, origin: new Vector2(icon.size.X * 0.35f, icon.size.Y * 0.75f), scale: slots[i].Rect.Width / icon.size.X * 0.7f); }
}
if (IsInLimbSlot(Items[i], InvSlotType.RightHand))
{
var icon = limbSlotIcons[InvSlotType.RightHand];
icon.Draw(spriteBatch, new Vector2(slots[i].Rect.Right, slots[i].Rect.Bottom) + slots[i].DrawOffset, Color.White * 0.6f, origin: new Vector2(icon.size.X * 0.65f, icon.size.Y * 0.75f), scale: slots[i].Rect.Width / icon.size.X * 0.7f);
}
Color color = slots[i].EquipButtonState == GUIComponent.ComponentState.Pressed ? Color.Gray : Color.White * 0.8f;
if (slots[i].EquipButtonState == GUIComponent.ComponentState.Hover)
{
color = Color.White;
highlightedQuickUseSlot = slots[i];
}
if (Locked) { color *= 0.3f; }
if (!Items[i].AllowedSlots.Any(a => a == InvSlotType.Any))
{
continue; continue;
} }
EquipIndicator.Draw(spriteBatch, slots[i].EquipButtonRect.Center.ToVector2(), color, EquipIndicator.Origin, 0, UIScale); if (draggingItem == item && !slot.IsHighlighted) continue;
/*slots[i].QuickUseTimer = Math.Min(slots[i].QuickUseTimer, 1.0f);
if (slots[i].QuickUseTimer > 0.0f) bool hover = slot.EquipButtonState == GUIComponent.ComponentState.Hover;
{ if (hover) highlightedQuickUseSlot = slot;
float indicatorFillAmount = character.HasEquippedItem(Items[i]) ? 1.0f - slots[i].QuickUseTimer : slots[i].QuickUseTimer;
quickUseHighlight.DrawTiled(spriteBatch, if (!item.AllowedSlots.Any(a => a == InvSlotType.Any)) continue;
slots[i].EquipButtonRect.Center.ToVector2() - quickUseHighlight.Origin * UIScale * 0.85f,
new Vector2(quickUseIndicator.SourceRect.Width * indicatorFillAmount, quickUseIndicator.SourceRect.Height) * UIScale * 0.85f, DrawIndicatorAndHotkey(spriteBatch, slot, hover);
null,
color * 0.9f,
null,
Vector2.One * UIScale * 0.85f);
}
else*/ if (character.HasEquippedItem(Items[i]))
{
EquipIndicatorHighlight.Draw(spriteBatch, slots[i].EquipButtonRect.Center.ToVector2(), color * 0.9f, EquipIndicatorHighlight.Origin, 0, UIScale * 0.85f);
}
} }
if (highlightedQuickUseSlot != null && !string.IsNullOrEmpty(highlightedQuickUseSlot.QuickUseButtonToolTip)) if (highlightedQuickUseSlot != null && !string.IsNullOrEmpty(highlightedQuickUseSlot.QuickUseButtonToolTip))
@@ -1002,5 +1093,30 @@ namespace Barotrauma
GUIComponent.DrawToolTip(spriteBatch, highlightedQuickUseSlot.QuickUseButtonToolTip, highlightedQuickUseSlot.EquipButtonRect); GUIComponent.DrawToolTip(spriteBatch, highlightedQuickUseSlot.QuickUseButtonToolTip, highlightedQuickUseSlot.EquipButtonRect);
} }
} }
private void DrawIndicatorAndHotkey(SpriteBatch spriteBatch, InventorySlot slot, bool hover)
{
Color indicatorColor = (hover) ? Color.White : Color.White * 0.8f;
/*if (character.HasEquippedItem(item))
{
indicatorColor = hover ? GUIColorSettings.InventorySlotEquippedColor : GUIColorSettings.InventorySlotEquippedColor * 0.8f;
}
else
{
indicatorColor = hover ? GUIColorSettings.InventorySlotColor : GUIColorSettings.InventorySlotColor * 0.8f;
}*/
if (Locked) indicatorColor *= 0.3f;
Rectangle equipRect = slot.EquipButtonRect;
EquipIndicator.Draw(spriteBatch, new Vector2(equipRect.Center.X, equipRect.Bottom), indicatorColor, EquipIndicator.Origin, 0, UIScale * 0.6f);
if (slot.QuickUseKey != Keys.None)
{
GUI.DrawString(spriteBatch, new Vector2(slot.Rect.Center.X - 2, slot.Rect.Top), slot.QuickUseKey.ToString().Substring(1, 1), Color.Black, font: GUI.HotkeyFont);
}
}
} }
} }
@@ -114,7 +114,7 @@ namespace Barotrauma.Items.Components
shakeTimer -= deltaTime; shakeTimer -= deltaTime;
Vector2 noisePos = new Vector2((float)PerlinNoise.CalculatePerlin(shakeTimer * 10.0f, shakeTimer * 10.0f, 0) - 0.5f, (float)PerlinNoise.CalculatePerlin(shakeTimer * 10.0f, shakeTimer * 10.0f, 0.5f) - 0.5f); Vector2 noisePos = new Vector2((float)PerlinNoise.CalculatePerlin(shakeTimer * 10.0f, shakeTimer * 10.0f, 0) - 0.5f, (float)PerlinNoise.CalculatePerlin(shakeTimer * 10.0f, shakeTimer * 10.0f, 0.5f) - 0.5f);
shakePos = noisePos * shake * 2.0f; shakePos = noisePos * shake * 2.0f;
shake = Math.Min(shake, shakeTimer); shake = Math.Min(shake, shakeTimer * 10.0f);
} }
else else
{ {
@@ -36,7 +36,7 @@ namespace Barotrauma.Items.Components
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.90f, 0.80f), GuiFrame.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter) var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.90f, 0.80f), GuiFrame.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter)
{ {
Stretch = true, Stretch = true,
RelativeSpacing = 0.02f RelativeSpacing = 0.08f
}; };
var topFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.5f), paddedFrame.RectTransform), style: null); var topFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.5f), paddedFrame.RectTransform), style: null);
@@ -51,15 +51,15 @@ namespace Barotrauma.Items.Components
inputLabel.RectTransform.Resize(new Point((int) inputLabel.Font.MeasureString(inputLabel.Text).X, inputLabel.RectTransform.Rect.Height)); inputLabel.RectTransform.Resize(new Point((int) inputLabel.Font.MeasureString(inputLabel.Text).X, inputLabel.RectTransform.Rect.Height));
new GUIFrame(new RectTransform(Vector2.One, inputLabelArea.RectTransform), style: "HorizontalLine"); new GUIFrame(new RectTransform(Vector2.One, inputLabelArea.RectTransform), style: "HorizontalLine");
var inputArea = new GUILayoutGroup(new RectTransform(new Vector2(1f, 1.2f), topFrame.RectTransform, Anchor.CenterLeft), childAnchor: Anchor.BottomLeft, isHorizontal: true) { Stretch = true, RelativeSpacing = 0.05f }; var inputArea = new GUILayoutGroup(new RectTransform(new Vector2(1f, 1f), topFrame.RectTransform, Anchor.CenterLeft), childAnchor: Anchor.BottomLeft, isHorizontal: true) { Stretch = true, RelativeSpacing = 0.05f };
// === INPUT SLOTS === // // === INPUT SLOTS === //
inputInventoryHolder = new GUIFrame(new RectTransform(new Vector2(0.7f, 1f), inputArea.RectTransform), style: null); inputInventoryHolder = new GUIFrame(new RectTransform(new Vector2(0.7f, 1f), inputArea.RectTransform), style: null);
inputInventoryOverlay = new GUICustomComponent(new RectTransform(Vector2.One, inputInventoryHolder.RectTransform), DrawOverLay, null) { CanBeFocused = false }; inputInventoryOverlay = new GUICustomComponent(new RectTransform(Vector2.One, inputInventoryHolder.RectTransform), DrawOverLay, null) { CanBeFocused = false };
// === ACTIVATE BUTTON === // // === ACTIVATE BUTTON === //
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.4f, 0.75f), inputArea.RectTransform), childAnchor: Anchor.CenterLeft); var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.4f, 0.7f), inputArea.RectTransform), childAnchor: Anchor.CenterLeft);
activateButton = new GUIButton(new RectTransform(new Vector2(0.95f, 0.65f), buttonContainer.RectTransform), TextManager.Get("DeconstructorDeconstruct"), style: "DeviceButton") activateButton = new GUIButton(new RectTransform(new Vector2(0.95f, 0.8f), buttonContainer.RectTransform), TextManager.Get("DeconstructorDeconstruct"), style: "DeviceButton")
{ {
TextBlock = { AutoScaleHorizontal = true }, TextBlock = { AutoScaleHorizontal = true },
OnClicked = ToggleActive OnClicked = ToggleActive
@@ -87,7 +87,7 @@ namespace Barotrauma.Items.Components
new GUIFrame(new RectTransform(Vector2.One, outputLabelArea.RectTransform), style: "HorizontalLine"); new GUIFrame(new RectTransform(Vector2.One, outputLabelArea.RectTransform), style: "HorizontalLine");
// === OUTPUT SLOTS === // // === OUTPUT SLOTS === //
outputInventoryHolder = new GUIFrame(new RectTransform(new Vector2(1f, 1.2f), bottomFrame.RectTransform, Anchor.CenterLeft), style: null); outputInventoryHolder = new GUIFrame(new RectTransform(new Vector2(1f, 1f), bottomFrame.RectTransform, Anchor.CenterLeft), style: null);
} }
public override bool Select(Character character) public override bool Select(Character character)
@@ -299,6 +299,11 @@ namespace Barotrauma.Items.Components
foreach (FabricationRecipe.RequiredItem requiredItem in missingItems) foreach (FabricationRecipe.RequiredItem requiredItem in missingItems)
{ {
while (slotIndex < inputContainer.Capacity && inputContainer.Inventory.Items[slotIndex] != null)
{
slotIndex++;
}
//highlight suitable ingredients in linked inventories //highlight suitable ingredients in linked inventories
foreach (Item item in availableIngredients) foreach (Item item in availableIngredients)
{ {
@@ -311,26 +316,24 @@ namespace Barotrauma.Items.Components
{ {
if (item.ParentInventory.slots[availableSlotIndex].HighlightTimer <= 0.0f) if (item.ParentInventory.slots[availableSlotIndex].HighlightTimer <= 0.0f)
{ {
item.ParentInventory.slots[availableSlotIndex].ShowBorderHighlight(GUI.Style.Green * 0.5f, 0.5f, 0.5f); item.ParentInventory.slots[availableSlotIndex].ShowBorderHighlight(GUI.Style.Green, 0.5f, 0.5f);
if (slotIndex < inputContainer.Capacity)
{
inputContainer.Inventory.slots[slotIndex].ShowBorderHighlight(GUI.Style.Green, 0.5f, 0.5f);
}
} }
} }
} }
} }
while (slotIndex < inputContainer.Capacity && inputContainer.Inventory.Items[slotIndex] != null)
{
slotIndex++;
}
if (slotIndex >= inputContainer.Capacity) { break; } if (slotIndex >= inputContainer.Capacity) { break; }
var itemIcon = requiredItem.ItemPrefab.InventoryIcon ?? requiredItem.ItemPrefab.sprite; var itemIcon = requiredItem.ItemPrefab.InventoryIcon ?? requiredItem.ItemPrefab.sprite;
Rectangle slotRect = inputContainer.Inventory.slots[slotIndex].Rect; Rectangle slotRect = inputContainer.Inventory.slots[slotIndex].Rect;
itemIcon.Draw( itemIcon.Draw(
spriteBatch, spriteBatch,
slotRect.Center.ToVector2(), slotRect.Center.ToVector2(),
color: requiredItem.ItemPrefab.InventoryIconColor * (availableIngredients.Any(i => IsItemValidIngredient(i, requiredItem)) ? 1.0f : 0.3f), color: requiredItem.ItemPrefab.InventoryIconColor * 0.3f,
scale: Math.Min(slotRect.Width / itemIcon.size.X, slotRect.Height / itemIcon.size.Y)); scale: Math.Min(slotRect.Width / itemIcon.size.X, slotRect.Height / itemIcon.size.Y));
if (slotRect.Contains(PlayerInput.MousePosition)) if (slotRect.Contains(PlayerInput.MousePosition))
@@ -59,7 +59,7 @@ namespace Barotrauma.Items.Components
public override void AddToGUIUpdateList() public override void AddToGUIUpdateList()
{ {
base.AddToGUIUpdateList(); base.AddToGUIUpdateList();
hullInfoFrame.AddToGUIUpdateList(); hullInfoFrame.AddToGUIUpdateList(order: 1);
} }
public override void OnMapLoaded() public override void OnMapLoaded()
@@ -259,6 +259,9 @@ namespace Barotrauma.Items.Components
if (mouseOnHull == hull) if (mouseOnHull == hull)
{ {
hullInfoFrame.RectTransform.ScreenSpaceOffset = hullFrame.Rect.Center; hullInfoFrame.RectTransform.ScreenSpaceOffset = hullFrame.Rect.Center;
if (hullInfoFrame.Rect.Right > GameMain.GraphicsWidth) { hullInfoFrame.RectTransform.ScreenSpaceOffset -= new Point(hullInfoFrame.Rect.Width, 0); }
if (hullInfoFrame.Rect.Bottom > GameMain.GraphicsHeight) { hullInfoFrame.RectTransform.ScreenSpaceOffset -= new Point(0, hullInfoFrame.Rect.Height); }
hullInfoFrame.Visible = true; hullInfoFrame.Visible = true;
hullNameText.Text = hull.DisplayName; hullNameText.Text = hull.DisplayName;
@@ -236,7 +236,6 @@ namespace Barotrauma.Items.Components
var directionalModeSwitchText = new GUITextBlock(new RectTransform(new Vector2(0.7f, 1), directionalModeFrame.RectTransform, Anchor.CenterRight), var directionalModeSwitchText = new GUITextBlock(new RectTransform(new Vector2(0.7f, 1), directionalModeFrame.RectTransform, Anchor.CenterRight),
TextManager.Get("SonarDirectionalPing"), GUI.Style.TextColor, GUI.SubHeadingFont, Alignment.CenterLeft); TextManager.Get("SonarDirectionalPing"), GUI.Style.TextColor, GUI.SubHeadingFont, Alignment.CenterLeft);
signalWarningText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), paddedControlContainer.RectTransform), "", warningColor, textAlignment: Alignment.Center);
GuiFrame.CanBeFocused = false; GuiFrame.CanBeFocused = false;
@@ -245,11 +244,14 @@ namespace Barotrauma.Items.Components
sonarView = new GUICustomComponent(new RectTransform(Vector2.One * 0.7f, GuiFrame.RectTransform, Anchor.BottomRight, scaleBasis: ScaleBasis.BothHeight), sonarView = new GUICustomComponent(new RectTransform(Vector2.One * 0.7f, GuiFrame.RectTransform, Anchor.BottomRight, scaleBasis: ScaleBasis.BothHeight),
(spriteBatch, guiCustomComponent) => { DrawSonar(spriteBatch, guiCustomComponent.Rect); }, null); (spriteBatch, guiCustomComponent) => { DrawSonar(spriteBatch, guiCustomComponent.Rect); }, null);
signalWarningText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.25f), sonarView.RectTransform, Anchor.Center, Pivot.BottomCenter),
"", warningColor, GUI.LargeFont, Alignment.Center);
// Setup layout for nav terminal // Setup layout for nav terminal
if (isConnectedToSteering) if (isConnectedToSteering)
{ {
controlContainer.RectTransform.SetPosition(Anchor.TopLeft);
controlContainer.RectTransform.RelativeOffset = controlBoxOffset; controlContainer.RectTransform.RelativeOffset = controlBoxOffset;
controlContainer.RectTransform.SetPosition(Anchor.TopLeft);
sonarView.RectTransform.ScaleBasis = ScaleBasis.Smallest; sonarView.RectTransform.ScaleBasis = ScaleBasis.Smallest;
sonarView.RectTransform.SetPosition(Anchor.CenterRight); sonarView.RectTransform.SetPosition(Anchor.CenterRight);
sonarView.RectTransform.Resize(Vector2.One * GUI.RelativeHorizontalAspectRatio * sonarAreaSize); sonarView.RectTransform.Resize(Vector2.One * GUI.RelativeHorizontalAspectRatio * sonarAreaSize);
@@ -108,10 +108,7 @@ namespace Barotrauma.Items.Components
private void CreateGUI() private void CreateGUI()
{ {
controlContainer = new GUIFrame(new RectTransform(new Vector2(Sonar.controlBoxSize.X, Sonar.controlBoxSize.Y + 0.02f), GuiFrame.RectTransform, Anchor.CenterLeft) controlContainer = new GUIFrame(new RectTransform(new Vector2(Sonar.controlBoxSize.X, 1 - Sonar.controlBoxSize.Y * 2), GuiFrame.RectTransform, Anchor.CenterLeft), "ItemUI");
{
RelativeOffset = new Vector2(0, 0) // The y offset should be based on the relative size difference of the steering and the status windows
}, "ItemUI");
var paddedControlContainer = new GUIFrame(new RectTransform(controlContainer.Rect.Size - GUIStyle.ItemFrameMargin, controlContainer.RectTransform, Anchor.Center) var paddedControlContainer = new GUIFrame(new RectTransform(controlContainer.Rect.Size - GUIStyle.ItemFrameMargin, controlContainer.RectTransform, Anchor.Center)
{ {
AbsoluteOffset = GUIStyle.ItemFrameOffset AbsoluteOffset = GUIStyle.ItemFrameOffset
@@ -120,7 +117,7 @@ namespace Barotrauma.Items.Components
var steeringModeArea = new GUIFrame(new RectTransform(new Vector2(1, 0.4f), paddedControlContainer.RectTransform, Anchor.TopLeft), style: null); var steeringModeArea = new GUIFrame(new RectTransform(new Vector2(1, 0.4f), paddedControlContainer.RectTransform, Anchor.TopLeft), style: null);
steeringModeSwitch = new GUIButton(new RectTransform(new Vector2(0.2f, 1), steeringModeArea.RectTransform), string.Empty, style: "SwitchVertical") steeringModeSwitch = new GUIButton(new RectTransform(new Vector2(0.2f, 1), steeringModeArea.RectTransform), string.Empty, style: "SwitchVertical")
{ {
Selected = false, Selected = autoPilot,
Enabled = true, Enabled = true,
OnClicked = (button, data) => OnClicked = (button, data) =>
{ {
@@ -141,34 +138,26 @@ namespace Barotrauma.Items.Components
manualPilotIndicator = new GUITickBox(new RectTransform(new Vector2(1, 0.45f), steeringModeRightSide.RectTransform, Anchor.TopLeft), manualPilotIndicator = new GUITickBox(new RectTransform(new Vector2(1, 0.45f), steeringModeRightSide.RectTransform, Anchor.TopLeft),
TextManager.Get("SteeringManual"), font: GUI.SubHeadingFont, style: "IndicatorLightRedSmall") TextManager.Get("SteeringManual"), font: GUI.SubHeadingFont, style: "IndicatorLightRedSmall")
{ {
Selected = true, Selected = !autoPilot,
Enabled = false Enabled = false
}; };
autopilotIndicator = new GUITickBox(new RectTransform(new Vector2(1, 0.45f), steeringModeRightSide.RectTransform, Anchor.BottomLeft), autopilotIndicator = new GUITickBox(new RectTransform(new Vector2(1, 0.45f), steeringModeRightSide.RectTransform, Anchor.BottomLeft),
TextManager.Get("SteeringAutoPilot"), font: GUI.SubHeadingFont, style: "IndicatorLightRedSmall") TextManager.Get("SteeringAutoPilot"), font: GUI.SubHeadingFont, style: "IndicatorLightRedSmall")
{ {
Selected = false, Selected = autoPilot,
Enabled = false Enabled = false
}; };
manualPilotIndicator.TextBlock.OverrideTextColor(GUI.Style.TextColor); manualPilotIndicator.TextBlock.OverrideTextColor(GUI.Style.TextColor);
autopilotIndicator.TextBlock.OverrideTextColor(GUI.Style.TextColor); autopilotIndicator.TextBlock.OverrideTextColor(GUI.Style.TextColor);
GUITextBlock.AutoScaleAndNormalize(manualPilotIndicator.TextBlock, autopilotIndicator.TextBlock); GUITextBlock.AutoScaleAndNormalize(manualPilotIndicator.TextBlock, autopilotIndicator.TextBlock);
var autoPilotControls = new GUIFrame(new RectTransform(new Vector2(0.8f, 0.6f), paddedControlContainer.RectTransform, Anchor.BottomCenter) var autoPilotControls = new GUIFrame(new RectTransform(new Vector2(0.75f, 0.62f), paddedControlContainer.RectTransform, Anchor.BottomCenter), "OutlineFrame");
{ var paddedAutoPilotControls = new GUIFrame(new RectTransform(new Vector2(0.92f, 0.88f), autoPilotControls.RectTransform, Anchor.Center), style: null);
RelativeOffset = new Vector2(0, 0.02f)
}, "OutlineFrame");
var paddedAutoPilotControls = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.85f), autoPilotControls.RectTransform, Anchor.Center))
{
Stretch = true,
RelativeSpacing = 0.03f,
ChildAnchor = Anchor.TopLeft
};
maintainPosTickBox = new GUITickBox(new RectTransform(new Vector2(1, 0.3f), paddedAutoPilotControls.RectTransform), maintainPosTickBox = new GUITickBox(new RectTransform(new Vector2(1, 0.333f), paddedAutoPilotControls.RectTransform, Anchor.TopCenter),
TextManager.Get("SteeringMaintainPos"), font: GUI.SmallFont, style: "GUIRadioButton") TextManager.Get("SteeringMaintainPos"), font: GUI.SmallFont, style: "GUIRadioButton")
{ {
Enabled = false, Enabled = autoPilot,
Selected = maintainPos, Selected = maintainPos,
OnSelected = tickBox => OnSelected = tickBox =>
{ {
@@ -200,12 +189,12 @@ namespace Barotrauma.Items.Components
return true; return true;
} }
}; };
int textLimit = (int)(MathHelper.Clamp(25 * GUI.xScale, 15, 35));
levelStartTickBox = new GUITickBox(new RectTransform(new Vector2(1, 0.3f), paddedAutoPilotControls.RectTransform), levelStartTickBox = new GUITickBox(new RectTransform(new Vector2(1, 0.333f), paddedAutoPilotControls.RectTransform, Anchor.Center),
GameMain.GameSession?.StartLocation == null ? "" : ToolBox.LimitString(GameMain.GameSession.StartLocation.Name, 30), GameMain.GameSession?.StartLocation == null ? "" : ToolBox.LimitString(GameMain.GameSession.StartLocation.Name, textLimit),
font: GUI.SmallFont, style: "GUIRadioButton") font: GUI.SmallFont, style: "GUIRadioButton")
{ {
Enabled = false, Enabled = autoPilot,
Selected = levelStartSelected, Selected = levelStartSelected,
OnSelected = tickBox => OnSelected = tickBox =>
{ {
@@ -228,11 +217,11 @@ namespace Barotrauma.Items.Components
} }
}; };
levelEndTickBox = new GUITickBox(new RectTransform(new Vector2(1, 0.3f), paddedAutoPilotControls.RectTransform), levelEndTickBox = new GUITickBox(new RectTransform(new Vector2(1, 0.333f), paddedAutoPilotControls.RectTransform, Anchor.BottomCenter),
GameMain.GameSession?.EndLocation == null ? "" : ToolBox.LimitString(GameMain.GameSession.EndLocation.Name, 30), GameMain.GameSession?.EndLocation == null ? "" : ToolBox.LimitString(GameMain.GameSession.EndLocation.Name, textLimit),
font: GUI.SmallFont, style: "GUIRadioButton") font: GUI.SmallFont, style: "GUIRadioButton")
{ {
Enabled = false, Enabled = autoPilot,
Selected = levelEndSelected, Selected = levelEndSelected,
OnSelected = tickBox => OnSelected = tickBox =>
{ {
@@ -254,14 +243,14 @@ namespace Barotrauma.Items.Components
return true; return true;
} }
}; };
GUITextBlock.AutoScaleAndNormalize(maintainPosTickBox.TextBlock, levelStartTickBox.TextBlock, levelEndTickBox.TextBlock);
maintainPosTickBox.RectTransform.IsFixedSize = levelStartTickBox.RectTransform.IsFixedSize = levelEndTickBox.RectTransform.IsFixedSize = false; maintainPosTickBox.RectTransform.IsFixedSize = levelStartTickBox.RectTransform.IsFixedSize = levelEndTickBox.RectTransform.IsFixedSize = false;
maintainPosTickBox.RectTransform.MaxSize = levelStartTickBox.RectTransform.MaxSize = levelEndTickBox.RectTransform.MaxSize = maintainPosTickBox.RectTransform.MaxSize = levelStartTickBox.RectTransform.MaxSize = levelEndTickBox.RectTransform.MaxSize =
new Point(int.MaxValue, paddedAutoPilotControls.Rect.Height / 3); new Point(int.MaxValue, paddedAutoPilotControls.Rect.Height / 3);
maintainPosTickBox.RectTransform.MinSize = levelStartTickBox.RectTransform.MinSize = levelEndTickBox.RectTransform.MinSize = maintainPosTickBox.RectTransform.MinSize = levelStartTickBox.RectTransform.MinSize = levelEndTickBox.RectTransform.MinSize =
Point.Zero; Point.Zero;
GUITextBlock.AutoScaleAndNormalize(scaleHorizontal: false, scaleVertical: true, maintainPosTickBox.TextBlock, levelStartTickBox.TextBlock, levelEndTickBox.TextBlock);
GUIRadioButtonGroup destinations = new GUIRadioButtonGroup(); GUIRadioButtonGroup destinations = new GUIRadioButtonGroup();
destinations.AddRadioButton((int)Destination.MaintainPos, maintainPosTickBox); destinations.AddRadioButton((int)Destination.MaintainPos, maintainPosTickBox);
destinations.AddRadioButton((int)Destination.LevelStart, levelStartTickBox); destinations.AddRadioButton((int)Destination.LevelStart, levelStartTickBox);
@@ -401,11 +390,12 @@ namespace Barotrauma.Items.Components
(spriteBatch, guiCustomComponent) => { DrawHUD(spriteBatch, guiCustomComponent.Rect); }, null); (spriteBatch, guiCustomComponent) => { DrawHUD(spriteBatch, guiCustomComponent.Rect); }, null);
steerRadius = steerArea.Rect.Width / 2; steerRadius = steerArea.Rect.Width / 2;
// Tooltip/helper text pressureWarningText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.25f), steerArea.RectTransform, Anchor.Center, Pivot.TopCenter),
pressureWarningText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.25f), paddedStatusContainer.RectTransform), TextManager.Get("SteeringDepthWarning"), GUI.Style.Red) TextManager.Get("SteeringDepthWarning"), Color.Red, GUI.LargeFont, Alignment.Center)
{ {
Visible = false Visible = false
}; };
// Tooltip/helper text
tipContainer = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.1f), steerArea.RectTransform, Anchor.BottomCenter, Pivot.TopCenter) tipContainer = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.1f), steerArea.RectTransform, Anchor.BottomCenter, Pivot.TopCenter)
, "", font: GUI.Font, wrap: true, style: "GUIToolTip", textAlignment: Alignment.Center) , "", font: GUI.Font, wrap: true, style: "GUIToolTip", textAlignment: Alignment.Center)
{ {
@@ -423,6 +413,7 @@ namespace Barotrauma.Items.Components
{ {
GuiFrame.ClearChildren(); GuiFrame.ClearChildren();
CreateGUI(); CreateGUI();
UpdateGUIElements();
} }
/// <summary> /// <summary>
@@ -152,7 +152,10 @@ namespace Barotrauma.Items.Components
DraggingConnected.Connections[1]?.ConnectionPanel == panel) DraggingConnected.Connections[1]?.ConnectionPanel == panel)
{ {
DraggingConnected.RemoveConnection(panel.Item); DraggingConnected.RemoveConnection(panel.Item);
panel.DisconnectedWires.Add(DraggingConnected); if (DraggingConnected.Item.ParentInventory == null)
{
panel.DisconnectedWires.Add(DraggingConnected);
}
} }
} }
@@ -134,46 +134,57 @@ namespace Barotrauma.Items.Components
} }
if (IsActive && item.ParentInventory?.Owner is Character user && user == Character.Controlled)// && Vector2.Distance(newNodePos, nodes[nodes.Count - 1]) > nodeDistance) if (IsActive && item.ParentInventory?.Owner is Character user && user == Character.Controlled)// && Vector2.Distance(newNodePos, nodes[nodes.Count - 1]) > nodeDistance)
{ {
Vector2 gridPos = Character.Controlled.Position; if (user.CanInteract)
Vector2 roundedGridPos = new Vector2(
MathUtils.RoundTowardsClosest(Character.Controlled.Position.X, Submarine.GridSize.X),
MathUtils.RoundTowardsClosest(Character.Controlled.Position.Y, Submarine.GridSize.Y));
//Vector2 attachPos = GetAttachPosition(user);
if (item.Submarine == null)
{ {
Structure attachTarget = Structure.GetAttachTarget(item.WorldPosition); Vector2 gridPos = Character.Controlled.Position;
if (attachTarget != null) Vector2 roundedGridPos = new Vector2(
MathUtils.RoundTowardsClosest(Character.Controlled.Position.X, Submarine.GridSize.X),
MathUtils.RoundTowardsClosest(Character.Controlled.Position.Y, Submarine.GridSize.Y));
//Vector2 attachPos = GetAttachPosition(user);
if (item.Submarine == null)
{ {
if (attachTarget.Submarine != null) Structure attachTarget = Structure.GetAttachTarget(item.WorldPosition);
if (attachTarget != null)
{ {
//set to submarine-relative position if (attachTarget.Submarine != null)
gridPos += attachTarget.Submarine.Position; {
roundedGridPos += attachTarget.Submarine.Position; //set to submarine-relative position
gridPos += attachTarget.Submarine.Position;
roundedGridPos += attachTarget.Submarine.Position;
}
} }
} }
else
{
gridPos += item.Submarine.Position;
roundedGridPos += item.Submarine.Position;
}
Submarine.DrawGrid(spriteBatch, 14, gridPos, roundedGridPos, alpha: 0.7f);
WireSection.Draw(
spriteBatch, this,
new Vector2(nodes[nodes.Count - 1].X, nodes[nodes.Count - 1].Y) + drawOffset,
new Vector2(newNodePos.X, newNodePos.Y) + drawOffset,
item.Color, 0.0f, 0.3f);
WireSection.Draw(
spriteBatch, this,
new Vector2(newNodePos.X, newNodePos.Y) + drawOffset,
item.DrawPosition,
item.Color, itemDepth, 0.3f);
GUI.DrawRectangle(spriteBatch, new Vector2(newNodePos.X + drawOffset.X, -(newNodePos.Y + drawOffset.Y)) - Vector2.One * 3, Vector2.One * 6, item.Color);
} }
else else
{ {
gridPos += item.Submarine.Position; WireSection.Draw(
roundedGridPos += item.Submarine.Position; spriteBatch, this,
new Vector2(nodes[nodes.Count - 1].X, nodes[nodes.Count - 1].Y) + drawOffset,
item.DrawPosition,
item.Color, 0.0f, 0.3f);
} }
Submarine.DrawGrid(spriteBatch, 14, gridPos, roundedGridPos, alpha: 0.7f);
WireSection.Draw(
spriteBatch, this,
new Vector2(nodes[nodes.Count - 1].X, nodes[nodes.Count - 1].Y) + drawOffset,
new Vector2(newNodePos.X, newNodePos.Y) + drawOffset,
item.Color, 0.0f, 0.3f);
WireSection.Draw(
spriteBatch, this,
new Vector2(newNodePos.X, newNodePos.Y) + drawOffset,
item.DrawPosition,
item.Color, itemDepth, 0.3f);
GUI.DrawRectangle(spriteBatch, new Vector2(newNodePos.X + drawOffset.X, -(newNodePos.Y + drawOffset.Y)) - Vector2.One * 3, Vector2.One * 6, item.Color);
} }
} }
@@ -53,14 +53,10 @@ namespace Barotrauma
{ {
int buttonDir = Math.Sign(SubInventoryDir); int buttonDir = Math.Sign(SubInventoryDir);
Vector2 equipIndicatorPos = new Vector2( Vector2 equipIndicatorPos = new Vector2(Rect.Left, Rect.Top);
Rect.Center.X - Inventory.EquipIndicator.size.X / 2 * Inventory.UIScale,
Rect.Center.Y + (Rect.Height / 2 + 25 * Inventory.UIScale) * buttonDir - Inventory.EquipIndicator.size.Y / 2 * Inventory.UIScale);
equipIndicatorPos += DrawOffset; equipIndicatorPos += DrawOffset;
return new Rectangle( return new Rectangle((int)equipIndicatorPos.X, (int)equipIndicatorPos.Y, Rect.Width, (int)(Inventory.EquipIndicator.size.Y * Inventory.UIScale));
(int)(equipIndicatorPos.X), (int)(equipIndicatorPos.Y),
(int)(Inventory.EquipIndicator.size.X * Inventory.UIScale), (int)(Inventory.EquipIndicator.size.Y * Inventory.UIScale));
} }
} }
@@ -132,11 +128,27 @@ namespace Barotrauma
protected Point prevScreenResolution; protected Point prevScreenResolution;
protected static Sprite slotHotkeySprite; protected static Sprite slotHotkeySprite;
public static Sprite SlotSpriteSmall;
public static Sprite EquipIndicator, EquipIndicatorHighlight; private static Sprite slotSpriteSmall;
public static Sprite SlotSpriteSmall
{
get
{
if (slotSpriteSmall == null)
{
//TODO: define these in xml
slotSpriteSmall = new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(12, 9, 115, 115), null, 0);
slotSpriteSmall.size = new Vector2(SlotSpriteSmall.SourceRect.Width * 0.682f, SlotSpriteSmall.SourceRect.Height * 0.682f);
}
return slotSpriteSmall;
}
}
public static Sprite DraggableIndicator;
public static Sprite EquipIndicator;
public static Inventory DraggingInventory; public static Inventory DraggingInventory;
public Rectangle BackgroundFrame { get; protected set; } public Rectangle BackgroundFrame;
private ushort[] receivedItemIDs; private ushort[] receivedItemIDs;
private CoroutineHandle syncItemsCoroutine; private CoroutineHandle syncItemsCoroutine;
@@ -151,6 +163,8 @@ namespace Barotrauma
private Point savedPosition, originalPos; private Point savedPosition, originalPos;
private bool canMove = false; private bool canMove = false;
private bool positionUpdateQueued = false; private bool positionUpdateQueued = false;
private Vector2 draggableIndicatorOffset;
private float draggableIndicatorScale;
public class SlotReference public class SlotReference
{ {
@@ -291,7 +305,7 @@ namespace Barotrauma
int columns = Math.Min(slotsPerRow, capacity); int columns = Math.Min(slotsPerRow, capacity);
Vector2 spacing = new Vector2(5.0f * UIScale); Vector2 spacing = new Vector2(5.0f * UIScale);
spacing.Y += (this is CharacterInventory) ? EquipIndicator.size.Y * UIScale : ContainedIndicatorHeight; spacing.Y += (this is CharacterInventory) ? EquipIndicator.size.Y : ContainedIndicatorHeight;
Vector2 rectSize = new Vector2(60.0f * UIScale); Vector2 rectSize = new Vector2(60.0f * UIScale);
padding = new Vector4(spacing.X, spacing.Y, spacing.X, spacing.X); padding = new Vector4(spacing.X, spacing.Y, spacing.X, spacing.X);
@@ -370,7 +384,12 @@ namespace Barotrauma
protected virtual bool HideSlot(int i) protected virtual bool HideSlot(int i)
{ {
return slots[i].Disabled || (hideEmptySlot[i] && Items[i] == null); return slots[i].Disabled;
}
protected virtual bool IsSlotHiddenDueToToggleState(int i)
{
return false;
} }
public virtual void Update(float deltaTime, Camera cam, bool subInventory = false) public virtual void Update(float deltaTime, Camera cam, bool subInventory = false)
@@ -386,7 +405,7 @@ namespace Barotrauma
{ {
for (int i = 0; i < capacity; i++) for (int i = 0; i < capacity; i++)
{ {
if (HideSlot(i)) { continue; } if (HideSlot(i) || IsSlotHiddenDueToToggleState(i)) { continue; }
UpdateSlot(slots[i], i, Items[i], subInventory); UpdateSlot(slots[i], i, Items[i], subInventory);
} }
if (!isSubInventory) if (!isSubInventory)
@@ -516,6 +535,9 @@ namespace Barotrauma
{ {
if (PlayerInput.PrimaryMouseButtonDown()) if (PlayerInput.PrimaryMouseButtonDown())
{ {
// Prevent us from dragging an item
draggingItem = null;
draggingSlot = null;
DraggingInventory = subInventory; DraggingInventory = subInventory;
} }
} }
@@ -535,7 +557,7 @@ namespace Barotrauma
var slot = slots[slotIndex]; var slot = slots[slotIndex];
int dir = slot.SubInventoryDir; int dir = slot.SubInventoryDir;
Rectangle subRect = slot.Rect; Rectangle subRect = slot.Rect;
Vector2 spacing = new Vector2(10 * UIScale, (10 + EquipIndicator.size.Y) * UIScale); Vector2 spacing = new Vector2(10 * UIScale, (10 * UIScale + EquipIndicator.size.Y));
int columns = (int)Math.Max(Math.Floor(Math.Sqrt(itemCapacity)), 1); int columns = (int)Math.Max(Math.Floor(Math.Sqrt(itemCapacity)), 1);
while (itemCapacity / columns * (subRect.Height + spacing.Y) > GameMain.GraphicsHeight * 0.5f) while (itemCapacity / columns * (subRect.Height + spacing.Y) > GameMain.GraphicsHeight * 0.5f)
@@ -631,18 +653,24 @@ namespace Barotrauma
/// Is the mouse on any inventory element (slot, equip button, subinventory...) /// Is the mouse on any inventory element (slot, equip button, subinventory...)
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public static bool IsMouseOnInventory() public static bool IsMouseOnInventory(bool ignoreDrag = false)
{ {
if (Character.Controlled == null) return false; if (Character.Controlled == null) return false;
if (draggingItem != null || DraggingInventory != null) return true; if (!ignoreDrag && draggingItem != null || DraggingInventory != null) return true;
if (Character.Controlled.Inventory != null) if (Character.Controlled.Inventory != null)
{ {
var inv = Character.Controlled.Inventory; var inv = Character.Controlled.Inventory;
if (inv.BackgroundFrame.Contains(PlayerInput.MousePosition) || inv.InventoryToggleContains) return true;
for (var i = 0; i < inv.slots.Length; i++) for (var i = 0; i < inv.slots.Length; i++)
{ {
var slot = inv.slots[i]; var slot = inv.slots[i];
if (inv.HideSlot(i) || inv.IsSlotHiddenDueToToggleState(i)) continue;
if (slot.InteractRect.Contains(PlayerInput.MousePosition)) if (slot.InteractRect.Contains(PlayerInput.MousePosition))
{ {
return true; return true;
@@ -715,6 +743,11 @@ namespace Barotrauma
if (inv == null) { return CursorState.Default; } if (inv == null) { return CursorState.Default; }
if (inv.InventoryToggleContains)
{
return CursorState.Hand;
}
foreach (var item in inv.Items) foreach (var item in inv.Items)
{ {
var container = item?.GetComponent<ItemContainer>(); var container = item?.GetComponent<ItemContainer>();
@@ -765,8 +798,12 @@ namespace Barotrauma
} }
} }
foreach (var slot in inv.slots) for (int i = 0; i < inv.slots.Length; i++)
{ {
InventorySlot slot = inv.slots[i];
if (inv.IsSlotHiddenDueToToggleState(i)) continue;
if (slot.EquipButtonRect.Contains(PlayerInput.MousePosition)) if (slot.EquipButtonRect.Contains(PlayerInput.MousePosition))
{ {
return CursorState.Hand; return CursorState.Hand;
@@ -783,6 +820,7 @@ namespace Barotrauma
} }
} }
} }
return CursorState.Default; return CursorState.Default;
} }
@@ -850,6 +888,12 @@ namespace Barotrauma
{ {
if (positionUpdateQueued) // Wait a frame before updating the positioning of the container after a resolution change to have everything working if (positionUpdateQueued) // Wait a frame before updating the positioning of the container after a resolution change to have everything working
{ {
int height = (int)(movableFrameRectHeight * UIScale);
CreateSlots();
container.Inventory.movableFrameRect = new Rectangle(container.Inventory.BackgroundFrame.X, container.Inventory.BackgroundFrame.Y - height, container.Inventory.BackgroundFrame.Width, height);
draggableIndicatorScale = 1.25f * UIScale;
draggableIndicatorOffset = DraggableIndicator.size * draggableIndicatorScale / 2f;
draggableIndicatorOffset += new Vector2(height / 2f - draggableIndicatorOffset.Y);
container.Inventory.originalPos = container.Inventory.savedPosition = container.Inventory.movableFrameRect.Center; container.Inventory.originalPos = container.Inventory.savedPosition = container.Inventory.movableFrameRect.Center;
positionUpdateQueued = false; positionUpdateQueued = false;
} }
@@ -862,12 +906,13 @@ namespace Barotrauma
prevScreenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight); prevScreenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
prevUIScale = UIScale; prevUIScale = UIScale;
prevHUDScale = GUI.Scale; prevHUDScale = GUI.Scale;
int height = (int)(movableFrameRectHeight * UIScale);
container.Inventory.movableFrameRect = new Rectangle(container.Inventory.BackgroundFrame.X, container.Inventory.BackgroundFrame.Y - height, container.Inventory.BackgroundFrame.Width, height);
positionUpdateQueued = true; positionUpdateQueued = true;
} }
else
GUI.DrawRectangle(spriteBatch, container.Inventory.movableFrameRect, movableFrameRectColor, true); {
GUI.DrawRectangle(spriteBatch, container.Inventory.movableFrameRect, movableFrameRectColor, true);
DraggableIndicator.Draw(spriteBatch, container.Inventory.movableFrameRect.Location.ToVector2() + draggableIndicatorOffset, 0, draggableIndicatorScale);
}
} }
} }
@@ -886,17 +931,20 @@ namespace Barotrauma
if (selectedSlot == null) if (selectedSlot == null)
{ {
if (DraggingItemToWorld && if (!IsMouseOnInventory(true))
Character.Controlled.FocusedItem?.OwnInventory != null &&
Character.Controlled.FocusedItem.OwnInventory.CanBePut(draggingItem) &&
Character.Controlled.FocusedItem.OwnInventory.TryPutItem(draggingItem, Character.Controlled))
{ {
GUI.PlayUISound(GUISoundType.PickItem); if (DraggingItemToWorld &&
} Character.Controlled.FocusedItem?.OwnInventory != null &&
else Character.Controlled.FocusedItem.OwnInventory.CanBePut(draggingItem) &&
{ Character.Controlled.FocusedItem.OwnInventory.TryPutItem(draggingItem, Character.Controlled))
GUI.PlayUISound(GUISoundType.DropItem); {
draggingItem.Drop(Character.Controlled); GUI.PlayUISound(GUISoundType.PickItem);
}
else
{
GUI.PlayUISound(GUISoundType.DropItem);
draggingItem.Drop(Character.Controlled);
}
} }
} }
else if (selectedSlot.ParentInventory.Items[selectedSlot.SlotIndex] != draggingItem) else if (selectedSlot.ParentInventory.Items[selectedSlot.SlotIndex] != draggingItem)
@@ -1028,7 +1076,7 @@ namespace Barotrauma
bool mouseOnHealthInterface = CharacterHealth.OpenHealthWindow != null && CharacterHealth.OpenHealthWindow.MouseOnElement; bool mouseOnHealthInterface = CharacterHealth.OpenHealthWindow != null && CharacterHealth.OpenHealthWindow.MouseOnElement;
if ((GUI.MouseOn == null || mouseOnHealthInterface) && selectedSlot == null) if ((GUI.MouseOn == null || mouseOnHealthInterface) && selectedSlot == null && !IsMouseOnInventory(true))
{ {
var shadowSprite = GUI.Style.GetComponentStyle("OuterGlow").Sprites[GUIComponent.ComponentState.None][0]; var shadowSprite = GUI.Style.GetComponentStyle("OuterGlow").Sprites[GUIComponent.ComponentState.None][0];
string toolTip = mouseOnHealthInterface ? TextManager.Get("QuickUseAction.UseTreatment") : string toolTip = mouseOnHealthInterface ? TextManager.Get("QuickUseAction.UseTreatment") :
@@ -1053,7 +1101,7 @@ namespace Barotrauma
} }
} }
if (selectedSlot != null && selectedSlot.Item != null) if (selectedSlot != null && selectedSlot.Item != null && selectedSlot.Slot.EquipButtonState != GUIComponent.ComponentState.Hover)
{ {
Rectangle slotRect = selectedSlot.Slot.Rect; Rectangle slotRect = selectedSlot.Slot.Rect;
slotRect.Location += selectedSlot.Slot.DrawOffset.ToPoint(); slotRect.Location += selectedSlot.Slot.DrawOffset.ToPoint();
@@ -1091,11 +1139,26 @@ namespace Barotrauma
if (inventory != null && (CharacterInventory.PersonalSlots.HasFlag(type) || (inventory.isSubInventory && (inventory.Owner as Item) != null if (inventory != null && (CharacterInventory.PersonalSlots.HasFlag(type) || (inventory.isSubInventory && (inventory.Owner as Item) != null
&& (inventory.Owner as Item).AllowedSlots.Any(a => CharacterInventory.PersonalSlots.HasFlag(a))))) && (inventory.Owner as Item).AllowedSlots.Any(a => CharacterInventory.PersonalSlots.HasFlag(a)))))
{ {
slotColor = slot.IsHighlighted ? GUIColorSettings.EquipmentSlotColor : GUIColorSettings.EquipmentSlotColor * 0.8f; if (item == null)
{
slotColor = slot.IsHighlighted ? GUIColorSettings.EquipmentSlotEmptyColor : GUIColorSettings.EquipmentSlotEmptyColor * 0.8f;
}
else
{
slotColor = slot.IsHighlighted ? GUIColorSettings.EquipmentSlotColor : GUIColorSettings.EquipmentSlotColor * 0.8f;
}
} }
else else
{ {
slotColor = slot.IsHighlighted ? GUIColorSettings.InventorySlotColor : GUIColorSettings.InventorySlotColor * 0.8f; if (item != null && Character.Controlled.HasEquippedItem(item))
{
slotColor = slot.IsHighlighted ? GUIColorSettings.InventorySlotEquippedColor : GUIColorSettings.InventorySlotEquippedColor * 0.8f;
}
else
{
slotColor = slot.IsHighlighted ? GUIColorSettings.InventorySlotColor : GUIColorSettings.InventorySlotColor * 0.8f;
}
} }
if (inventory != null && inventory.Locked) { slotColor = Color.Gray * 0.5f; } if (inventory != null && inventory.Locked) { slotColor = Color.Gray * 0.5f; }
@@ -1210,7 +1273,7 @@ namespace Barotrauma
if (GameMain.DebugDraw) if (GameMain.DebugDraw)
{ {
GUI.DrawRectangle(spriteBatch, rect, Color.White, false, 0, 1); GUI.DrawRectangle(spriteBatch, rect, Color.White, false, 0, 1);
GUI.DrawRectangle(spriteBatch, slot.EquipButtonRect, Color.White, false, 0, 1); GUI.DrawRectangle(spriteBatch, slot.EquipButtonRect, Color.Red, false, 0, 1);
} }
if (slot.HighlightColor != Color.Transparent) if (slot.HighlightColor != Color.Transparent)
@@ -1221,8 +1284,11 @@ namespace Barotrauma
if (item != null && drawItem) if (item != null && drawItem)
{ {
Sprite sprite = item.Prefab.InventoryIcon ?? item.Sprite; Sprite sprite = item.Prefab.InventoryIcon ?? item.Sprite;
float scale = Math.Min(Math.Min((rect.Width - 10) / sprite.size.X, (rect.Height - 10) / sprite.size.Y), 2.0f);
Vector2 itemPos = rect.Center.ToVector2(); float equipButtonHeightAdjustment = inventory == Character.Controlled?.Inventory ? slot.EquipButtonRect.Height : 0;
float scale = Math.Min(Math.Min((rect.Width - 10) / sprite.size.X, (rect.Height - 5 - equipButtonHeightAdjustment) / sprite.size.Y), 2.0f);
Vector2 itemPos = rect.Center.ToVector2() + new Vector2(0, equipButtonHeightAdjustment / 2f);
if (itemPos.Y > GameMain.GraphicsHeight) if (itemPos.Y > GameMain.GraphicsHeight)
{ {
itemPos.Y -= Math.Min( itemPos.Y -= Math.Min(
@@ -1248,15 +1314,6 @@ namespace Barotrauma
} }
sprite.Draw(spriteBatch, itemPos, spriteColor, rotation, scale); sprite.Draw(spriteBatch, itemPos, spriteColor, rotation, scale);
} }
if (inventory != null &&
!inventory.Locked &&
Character.Controlled?.Inventory == inventory &&
slot.QuickUseKey != Keys.None)
{
spriteBatch.Draw(slotHotkeySprite.Texture, rect.ScaleSize(1.25f), slotHotkeySprite.SourceRect, slotColor);
GUI.DrawString(spriteBatch, rect.Location.ToVector2() + new Vector2(1, -2), slot.QuickUseKey.ToString().Substring(1, 1), Color.Black, font: GUI.SmallFont);
}
} }
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
@@ -1273,7 +1330,7 @@ namespace Barotrauma
//prevents the inventory from briefly reverting to an old state if items are moved around in quick succession //prevents the inventory from briefly reverting to an old state if items are moved around in quick succession
//also delay if we're still midround syncing, some of the items in the inventory may not exist yet //also delay if we're still midround syncing, some of the items in the inventory may not exist yet
if (syncItemsDelay > 0.0f || GameMain.Client.MidRoundSyncing) if (syncItemsDelay > 0.0f || GameMain.Client.MidRoundSyncing || NetIdUtils.IdMoreRecent(lastEventID, GameMain.Client.EntityEventManager.LastReceivedID))
{ {
if (syncItemsCoroutine != null) CoroutineManager.StopCoroutines(syncItemsCoroutine); if (syncItemsCoroutine != null) CoroutineManager.StopCoroutines(syncItemsCoroutine);
syncItemsCoroutine = CoroutineManager.StartCoroutine(SyncItemsAfterDelay(lastEventID)); syncItemsCoroutine = CoroutineManager.StartCoroutine(SyncItemsAfterDelay(lastEventID));
@@ -698,16 +698,19 @@ namespace Barotrauma
//reset positions first //reset positions first
List<GUIComponent> elementsToMove = new List<GUIComponent>(); 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); elementsToMove.Add(editingHUD);
} }
debugInitialHudPositions.Clear();
foreach (ItemComponent ic in activeHUDs) foreach (ItemComponent ic in activeHUDs)
{ {
if (ic.GuiFrame == null || ic.AllowUIOverlap || ic.GetLinkUIToComponent() != null) continue; if (ic.GuiFrame == null || ic.AllowUIOverlap || ic.GetLinkUIToComponent() != null) { continue; }
ic.GuiFrame.RectTransform.ScreenSpaceOffset = Point.Zero; ic.GuiFrame.RectTransform.ScreenSpaceOffset = Point.Zero;
elementsToMove.Add(ic.GuiFrame); elementsToMove.Add(ic.GuiFrame);
debugInitialHudPositions.Add(ic.GuiFrame.Rect);
} }
List<Rectangle> disallowedAreas = new List<Rectangle>(); List<Rectangle> disallowedAreas = new List<Rectangle>();
@@ -728,18 +731,22 @@ namespace Barotrauma
foreach (ItemComponent ic in activeHUDs) foreach (ItemComponent ic in activeHUDs)
{ {
if (ic.GuiFrame == null) continue; if (ic.GuiFrame == null) { continue; }
var linkUIToComponent = ic.GetLinkUIToComponent(); var linkUIToComponent = ic.GetLinkUIToComponent();
if (linkUIToComponent == null) continue; if (linkUIToComponent == null) { continue; }
ic.GuiFrame.RectTransform.ScreenSpaceOffset = linkUIToComponent.GuiFrame.RectTransform.ScreenSpaceOffset; ic.GuiFrame.RectTransform.ScreenSpaceOffset = linkUIToComponent.GuiFrame.RectTransform.ScreenSpaceOffset;
} }
} }
private readonly List<Rectangle> debugInitialHudPositions = new List<Rectangle>();
public void UpdateHUD(Camera cam, Character character, float deltaTime) public void UpdateHUD(Camera cam, Character character, float deltaTime)
{ {
bool editingHUDCreated = false; bool editingHUDCreated = false;
if (HasInGameEditableProperties || if ((HasInGameEditableProperties && character.SelectedConstruction == this) ||
Screen.Selected == GameMain.SubEditorScreen) Screen.Selected == GameMain.SubEditorScreen)
{ {
GUIComponent prevEditingHUD = editingHUD; GUIComponent prevEditingHUD = editingHUD;
@@ -850,6 +857,23 @@ namespace Barotrauma
ic.DrawHUD(spriteBatch, character); ic.DrawHUD(spriteBatch, character);
} }
} }
if (GameMain.DebugDraw)
{
int i = 0;
foreach (ItemComponent ic in activeHUDs)
{
if (i >= debugInitialHudPositions.Count) { break; }
if (activeHUDs[i].GuiFrame == null) { continue; }
if (ic.GuiFrame == null || ic.AllowUIOverlap || ic.GetLinkUIToComponent() != null) { continue; }
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++;
}
}
} }
readonly List<ColoredText> texts = new List<ColoredText>(); readonly List<ColoredText> texts = new List<ColoredText>();
@@ -1252,7 +1276,6 @@ namespace Barotrauma
} }
} }
var item = new Item(itemPrefab, pos, sub) var item = new Item(itemPrefab, pos, sub)
{ {
ID = itemId ID = itemId
@@ -1262,8 +1285,8 @@ namespace Barotrauma
{ {
wifiComponent.TeamID = (Character.TeamType)teamID; wifiComponent.TeamID = (Character.TeamType)teamID;
} }
if (descriptionChanged) item.Description = itemDesc; if (descriptionChanged) { item.Description = itemDesc; }
if (tagsChanged) item.Tags = tags; if (tagsChanged) { item.Tags = tags; }
if (sub != null) if (sub != null)
{ {
@@ -1276,7 +1299,7 @@ namespace Barotrauma
if (inventorySlotIndex >= 0 && inventorySlotIndex < 255 && if (inventorySlotIndex >= 0 && inventorySlotIndex < 255 &&
inventory.TryPutItem(item, inventorySlotIndex, false, false, null, false)) inventory.TryPutItem(item, inventorySlotIndex, false, false, null, false))
{ {
return null; return item;
} }
inventory.TryPutItem(item, null, item.AllowedSlots, false); inventory.TryPutItem(item, null, item.AllowedSlots, false);
} }
@@ -11,6 +11,12 @@ namespace Barotrauma
{ {
base.ControlInput(cam); base.ControlInput(cam);
cam.OffsetAmount = 0; cam.OffsetAmount = 0;
if (Character.Controlled?.Inventory != null)
{
Character.Controlled.Inventory.ToggleInventory(true);
}
//if this is used, we need to implement syncing this inventory with the server //if this is used, we need to implement syncing this inventory with the server
/*Character.DisableControls = true; /*Character.DisableControls = true;
if (Character.Controlled != null) if (Character.Controlled != null)
@@ -21,7 +21,7 @@ namespace Barotrauma
public string Filename public string Filename
{ {
get { return Sound.Filename; } get { return Sound?.Filename; }
} }
public RoundSound(XElement element, Sound sound) public RoundSound(XElement element, Sound sound)
@@ -307,6 +307,8 @@ namespace Barotrauma.Networking
Status = FileTransferStatus.Finished, Status = FileTransferStatus.Finished,
FileSize = 0 FileSize = 0
}; };
Md5Hash.RemoveFromCache(directTransfer.FilePath);
OnFinished(directTransfer); OnFinished(directTransfer);
} }
break; break;
@@ -376,6 +378,7 @@ namespace Barotrauma.Networking
{ {
finishedTransfers.Add(new Pair<int, double>(transferId, Timing.TotalTime)); finishedTransfers.Add(new Pair<int, double>(transferId, Timing.TotalTime));
StopTransfer(activeTransfer); StopTransfer(activeTransfer);
Md5Hash.RemoveFromCache(activeTransfer.FilePath);
OnFinished(activeTransfer); OnFinished(activeTransfer);
} }
else else
@@ -366,14 +366,6 @@ namespace Barotrauma.Networking
CoroutineManager.StartCoroutine(WaitForStartingInfo(), "WaitForStartingInfo"); CoroutineManager.StartCoroutine(WaitForStartingInfo(), "WaitForStartingInfo");
} }
private bool RetryConnection(GUIButton button, object obj)
{
if (clientPeer != null) { clientPeer.Close(); }
clientPeer = null;
ConnectToServer(serverEndpoint, serverName);
return true;
}
private bool ReturnToPreviousMenu(GUIButton button, object obj) private bool ReturnToPreviousMenu(GUIButton button, object obj)
{ {
Disconnect(); Disconnect();
@@ -857,6 +849,7 @@ namespace Barotrauma.Networking
if (eventSyncError) if (eventSyncError)
{ {
GameMain.NetLobbyScreen.Select(); GameMain.NetLobbyScreen.Select();
GameMain.GameSession?.EndRound("");
gameStarted = false; gameStarted = false;
myCharacter = null; myCharacter = null;
} }
@@ -1244,6 +1237,8 @@ namespace Barotrauma.Networking
mirrorLevel: campaign.Map.CurrentLocation != campaign.Map.SelectedConnection.Locations[0]); mirrorLevel: campaign.Map.CurrentLocation != campaign.Map.SelectedConnection.Locations[0]);
} }
GameMain.GameSession.Mission?.ClientReadInitial(inc);
if (GameMain.GameSession.Submarine.IsFileCorrupted) if (GameMain.GameSession.Submarine.IsFileCorrupted)
{ {
DebugConsole.ThrowError($"Failed to start a round. Could not load the submarine \"{GameMain.GameSession.Submarine.Name}\"."); DebugConsole.ThrowError($"Failed to start a round. Could not load the submarine \"{GameMain.GameSession.Submarine.Name}\".");
@@ -1635,9 +1630,9 @@ namespace Barotrauma.Networking
break; break;
case ServerNetObject.ENTITY_POSITION: case ServerNetObject.ENTITY_POSITION:
UInt16 id = inc.ReadUInt16(); UInt16 id = inc.ReadUInt16();
byte msgLength = inc.ReadByte(); uint msgLength = inc.ReadVariableUInt32();
int msgEndPos = inc.BitPosition + msgLength * 8; int msgEndPos = (int)(inc.BitPosition + msgLength * 8);
var entity = Entity.FindEntityByID(id) as IServerSerializable; var entity = Entity.FindEntityByID(id) as IServerSerializable;
if (entity != null) if (entity != null)
@@ -2367,9 +2362,14 @@ namespace Barotrauma.Networking
{ {
textBox.Deselect(); textBox.Deselect();
} }
textBox.Text = ""; textBox.Text = "";
if (ChatBox.CloseAfterMessageSent)
{
ChatBox.ToggleOpen = false;
ChatBox.CloseAfterMessageSent = false;
}
return true; return true;
} }
@@ -2443,13 +2443,16 @@ namespace Barotrauma.Networking
{ {
msgBox.AddToGUIUpdateList(); msgBox.AddToGUIUpdateList();
ChatBox.GUIFrame.Flash(Color.DarkGreen, 0.5f); ChatBox.GUIFrame.Flash(Color.DarkGreen, 0.5f);
ChatBox.CloseAfterMessageSent = !ChatBox.ToggleOpen;
ChatBox.ToggleOpen = true; ChatBox.ToggleOpen = true;
ChatBox.CloseAfterMessageSent = !ChatBox.ToggleOpen;
} }
if (radioKeyHit) if (radioKeyHit)
{ {
msgBox.AddToGUIUpdateList(); msgBox.AddToGUIUpdateList();
ChatBox.GUIFrame.Flash(Color.YellowGreen, 0.5f); ChatBox.GUIFrame.Flash(Color.YellowGreen, 0.5f);
ChatBox.CloseAfterMessageSent = !ChatBox.ToggleOpen;
ChatBox.ToggleOpen = true; ChatBox.ToggleOpen = true;
if (!msgBox.Text.StartsWith(ChatBox.RadioChatString)) if (!msgBox.Text.StartsWith(ChatBox.RadioChatString))
{ {
@@ -195,7 +195,10 @@ namespace Barotrauma.Networking
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.025f), content.RectTransform), style: null); new GUIFrame(new RectTransform(new Vector2(1.0f, 0.025f), content.RectTransform), style: null);
var serverMsg = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.3f), content.RectTransform)) { ScrollBarVisible = true }; var serverMsg = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.3f), content.RectTransform)) { ScrollBarVisible = true };
var msgText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), serverMsg.Content.RectTransform), ServerMessage, font: GUI.SmallFont, wrap: true) { CanBeFocused = true }; var msgText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), serverMsg.Content.RectTransform), ServerMessage, font: GUI.SmallFont, wrap: true)
{
CanBeFocused = false
};
serverMsg.Content.RectTransform.SizeChanged += () => { msgText.CalculateHeightFromText(); }; serverMsg.Content.RectTransform.SizeChanged += () => { msgText.CalculateHeightFromText(); };
msgText.RectTransform.SizeChanged += () => { serverMsg.UpdateScrollBarSize(); }; msgText.RectTransform.SizeChanged += () => { serverMsg.UpdateScrollBarSize(); };
@@ -2,6 +2,7 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Barotrauma.Extensions;
namespace Barotrauma namespace Barotrauma
{ {
@@ -35,12 +36,13 @@ namespace Barotrauma
GameMain.NetLobbyScreen.Frame.FindChild("modevotes", true).Visible = value; GameMain.NetLobbyScreen.Frame.FindChild("modevotes", true).Visible = value;
//gray out modes that can't be voted // Disable modes that cannot be voted on
foreach (GUITextBlock comp in GameMain.NetLobbyScreen.ModeList.Content.Children) foreach (var guiComponent in GameMain.NetLobbyScreen.ModeList.Content.Children)
{ {
comp.TextColor = if (guiComponent is GUIFrame frame)
new Color(comp.TextColor.R, comp.TextColor.G, comp.TextColor.B, {
!allowModeVoting || ((GameModePreset)comp.UserData).Votable ? (byte)255 : (byte)100); frame.CanBeFocused = !allowModeVoting || ((GameModePreset) frame.UserData).Votable;
}
} }
UpdateVoteTexts(null, VoteType.Mode); UpdateVoteTexts(null, VoteType.Mode);
@@ -234,7 +234,7 @@ namespace Barotrauma
!ItemPrefab.Prefabs.Any(ep => ep.Category.HasFlag(c) && ep.CanBeBought)); !ItemPrefab.Prefabs.Any(ep => ep.Category.HasFlag(c) && ep.CanBeBought));
foreach (MapEntityCategory category in itemCategories) foreach (MapEntityCategory category in itemCategories)
{ {
var categoryButton = new GUIButton(new RectTransform(new Point(categoryButtonContainer.Rect.Width), categoryButtonContainer.RectTransform), var categoryButton = new GUIButton(new RectTransform(new Point(categoryButtonContainer.Rect.Width, categoryButtonContainer.Rect.Width), categoryButtonContainer.RectTransform),
"", style: "ItemCategory" + category.ToString()) "", style: "ItemCategory" + category.ToString())
{ {
UserData = category, UserData = category,
@@ -253,7 +253,14 @@ namespace Barotrauma
}; };
itemCategoryButtons.Add(categoryButton); itemCategoryButtons.Add(categoryButton);
new GUITextBlock(new RectTransform(new Vector2(0.9f, 0.25f), categoryButton.RectTransform, Anchor.BottomCenter) { RelativeOffset = new Vector2(0.0f, 0.02f) }, categoryButton.RectTransform.SizeChanged += () =>
{
var sprite = categoryButton.Frame.sprites[GUIComponent.ComponentState.None].First();
categoryButton.RectTransform.NonScaledSize =
new Point(categoryButton.Rect.Width, (int)(categoryButton.Rect.Width * ((float)sprite.Sprite.SourceRect.Height / sprite.Sprite.SourceRect.Width)));
};
new GUITextBlock(new RectTransform(new Vector2(0.95f, 0.256f), categoryButton.RectTransform, Anchor.BottomCenter) { RelativeOffset = new Vector2(0.0f, 0.02f) },
TextManager.Get("MapEntityCategory." + category), textAlignment: Alignment.Center, textColor: categoryButton.TextColor) TextManager.Get("MapEntityCategory." + category), textAlignment: Alignment.Center, textColor: categoryButton.TextColor)
{ {
Padding = Vector4.Zero, Padding = Vector4.Zero,
@@ -262,7 +269,7 @@ namespace Barotrauma
HoverColor = Color.Transparent, HoverColor = Color.Transparent,
PressedColor = Color.Transparent, PressedColor = Color.Transparent,
SelectedColor = Color.Transparent, SelectedColor = Color.Transparent,
CanBeFocused = false CanBeFocused = true
}; };
} }
FillStoreItemList(); FillStoreItemList();
@@ -715,11 +722,12 @@ namespace Barotrauma
for (int i = 0; i < availableMissions.Count; i++) for (int i = 0; i < availableMissions.Count; i++)
{ {
var mission = availableMissions[i]; var mission = availableMissions[i];
var tickBox = new GUITickBox(new RectTransform(new Vector2(0.1f, 0.1f), missionContent.RectTransform), var tickBox = new GUITickBox(new RectTransform(new Vector2(0.65f, 0.1f), missionContent.RectTransform),
mission?.Name ?? TextManager.Get("NoMission"), style: "GUIRadioButton") mission?.Name ?? TextManager.Get("NoMission"), style: "GUIRadioButton")
{ {
Enabled = GameMain.Client == null || GameMain.Client.HasPermission(Networking.ClientPermissions.ManageCampaign) Enabled = GameMain.Client == null || GameMain.Client.HasPermission(Networking.ClientPermissions.ManageCampaign)
}; };
tickBox.TextBlock.Wrap = true;
missionTickBoxes.Add(tickBox); missionTickBoxes.Add(tickBox);
missionRadioButtonGroup.AddRadioButton(i, tickBox); missionRadioButtonGroup.AddRadioButton(i, tickBox);
} }
@@ -833,7 +841,7 @@ namespace Barotrauma
var content = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 1.0f), frame.RectTransform, Anchor.Center), var content = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 1.0f), frame.RectTransform, Anchor.Center),
isHorizontal: true, childAnchor: Anchor.CenterLeft) isHorizontal: true, childAnchor: Anchor.CenterLeft)
{ {
RelativeSpacing = 0.02f, AbsoluteSpacing = (int)(5 * GUI.Scale),
Stretch = true Stretch = true
}; };
@@ -874,8 +882,11 @@ namespace Barotrauma
UserData = pi, UserData = pi,
IntValue = pi.Quantity IntValue = pi.Quantity
}; };
amountInput.TextBox.OnSelected += (sender, key) => { suppressBuySell = true; };
amountInput.TextBox.OnDeselected += (sender, key) => { suppressBuySell = false; amountInput.OnValueChanged?.Invoke(amountInput); };
amountInput.OnValueChanged += (numberInput) => amountInput.OnValueChanged += (numberInput) =>
{ {
if (suppressBuySell) { return; }
PurchasedItem purchasedItem = numberInput.UserData as PurchasedItem; PurchasedItem purchasedItem = numberInput.UserData as PurchasedItem;
//Attempting to buy //Attempting to buy
@@ -889,7 +900,6 @@ namespace Barotrauma
{ {
BuyItem(numberInput, purchasedItem); BuyItem(numberInput, purchasedItem);
} }
numberInput.IntValue = purchasedItem.Quantity;
} }
//Attempting to sell //Attempting to sell
else else
@@ -901,14 +911,15 @@ namespace Barotrauma
} }
} }
}; };
frame.HoverColor = frame.SelectedColor = Color.Transparent;
} }
listBox.RecalculateChildren(); listBox.RecalculateChildren();
content.Recalculate(); content.Recalculate();
content.RectTransform.RecalculateChildren(true, true); content.RectTransform.RecalculateChildren(true, true);
amountInput?.LayoutGroup.Recalculate(); amountInput?.LayoutGroup.Recalculate();
textBlock.Text = ToolBox.LimitString(textBlock.Text, textBlock.Font, textBlock.Rect.Width); textBlock.Text = ToolBox.LimitString(textBlock.Text, textBlock.Font, textBlock.Rect.Width);
/*content.RectTransform.IsFixedSize = true; //content.RectTransform.IsFixedSize = true;
content.RectTransform.Children.ForEach(c => c.IsFixedSize = true);*/ content.RectTransform.Children.ForEach(c => c.IsFixedSize = true);
return frame; return frame;
} }
@@ -946,12 +957,15 @@ namespace Barotrauma
return false; return false;
} }
private bool suppressBuySell;
private void RefreshMyItems() private void RefreshMyItems()
{ {
HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>(); HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>();
foreach (PurchasedItem pi in Campaign.CargoManager.PurchasedItems) foreach (PurchasedItem pi in Campaign.CargoManager.PurchasedItems)
{ {
var itemFrame = myItemList.Content.GetChildByUserData(pi); var itemFrame = myItemList.Content.Children.FirstOrDefault(c =>
c.UserData is PurchasedItem pi2 && pi.ItemPrefab == pi2.ItemPrefab);
if (itemFrame == null) if (itemFrame == null)
{ {
var priceInfo = pi.ItemPrefab.GetPrice(Campaign.Map.CurrentLocation); var priceInfo = pi.ItemPrefab.GetPrice(Campaign.Map.CurrentLocation);
@@ -959,8 +973,17 @@ namespace Barotrauma
itemFrame = CreateItemFrame(pi, priceInfo, myItemList); itemFrame = CreateItemFrame(pi, priceInfo, myItemList);
itemFrame.Flash(GUI.Style.Green); itemFrame.Flash(GUI.Style.Green);
} }
itemFrame.GetChild(0).GetChild<GUINumberInput>().IntValue = pi.Quantity; else
{
itemFrame.UserData = itemFrame.GetChild(0).GetChild<GUINumberInput>().UserData = pi;
}
existingItemFrames.Add(itemFrame); existingItemFrames.Add(itemFrame);
suppressBuySell = true;
var numInput = itemFrame.GetChild(0).GetChild<GUINumberInput>();
if (numInput.IntValue != pi.Quantity) { itemFrame.Flash(GUI.Style.Green); }
numInput.IntValue = (itemFrame.UserData as PurchasedItem).Quantity = pi.Quantity;
suppressBuySell = false;
} }
var removedItemFrames = myItemList.Content.Children.Except(existingItemFrames).ToList(); var removedItemFrames = myItemList.Content.Children.Except(existingItemFrames).ToList();
@@ -204,7 +204,7 @@ namespace Barotrauma
Submarine.DrawBack(spriteBatch, false, e => !(e is Structure) || e.SpriteDepth < 0.9f); Submarine.DrawBack(spriteBatch, false, e => !(e is Structure) || e.SpriteDepth < 0.9f);
foreach (Character c in Character.CharacterList) foreach (Character c in Character.CharacterList)
{ {
if (c.AnimController.Limbs.Any(l => l.DeformSprite != null) || !c.IsVisible) { continue; } if (!c.IsVisible || c.AnimController.Limbs.Any(l => l.DeformSprite != null)) { continue; }
c.Draw(spriteBatch, Cam); c.Draw(spriteBatch, Cam);
} }
spriteBatch.End(); spriteBatch.End();
@@ -1111,7 +1111,7 @@ namespace Barotrauma
Alignment textAlignment = Alignment.CenterLeft; Alignment textAlignment = Alignment.CenterLeft;
Vector2 textFieldSize = new Vector2(0.5f, 1.0f); Vector2 textFieldSize = new Vector2(0.5f, 1.0f);
Vector2 tickBoxSize = new Vector2(0.4f, 0.07f); Vector2 tickBoxSize = new Vector2(0.4f, 0.07f);
var content = new GUILayoutGroup(new RectTransform(new Vector2(0.6f, 0.9f), menuTabs[(int)Tab.HostServer].RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter) var content = new GUILayoutGroup(new RectTransform(new Vector2(0.7f, 0.9f), menuTabs[(int)Tab.HostServer].RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter)
{ {
RelativeSpacing = 0.02f, RelativeSpacing = 0.02f,
Stretch = true Stretch = true
@@ -1122,7 +1122,7 @@ namespace Barotrauma
//play style ----------------------------------------------------- //play style -----------------------------------------------------
var playstyleContainer = new GUIFrame(new RectTransform(new Vector2(1.5f, 0.1f), parent.RectTransform), style: null, color: Color.Black); var playstyleContainer = new GUIFrame(new RectTransform(new Vector2(1.35f, 0.1f), parent.RectTransform), style: null, color: Color.Black);
playstyleBanner = new GUIImage(new RectTransform(new Vector2(1.0f, 0.1f), playstyleContainer.RectTransform), playstyleBanner = new GUIImage(new RectTransform(new Vector2(1.0f, 0.1f), playstyleContainer.RectTransform),
ServerListScreen.PlayStyleBanners[0], scaleToFit: true) ServerListScreen.PlayStyleBanners[0], scaleToFit: true)
@@ -1263,7 +1263,7 @@ namespace Barotrauma
var tickboxAreaLower = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, tickBoxSize.Y), parent.RectTransform), isHorizontal: true); var tickboxAreaLower = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, tickBoxSize.Y), parent.RectTransform), isHorizontal: true);
karmaEnabledBox = new GUITickBox(new RectTransform(new Vector2(0.4f, 1.0f), tickboxAreaLower.RectTransform), TextManager.Get("ServerSettingsUseKarma")) karmaEnabledBox = new GUITickBox(new RectTransform(new Vector2(0.5f, 1.0f), tickboxAreaLower.RectTransform), TextManager.Get("ServerSettingsUseKarma"))
{ {
ToolTip = TextManager.Get("karmaexplanation"), ToolTip = TextManager.Get("karmaexplanation"),
OnSelected = (tb) => OnSelected = (tb) =>
@@ -1272,7 +1272,7 @@ namespace Barotrauma
return true; return true;
} }
}; };
karmaPresetDD = new GUIDropDown(new RectTransform(new Vector2(0.6f, 1.0f), tickboxAreaLower.RectTransform)) karmaPresetDD = new GUIDropDown(new RectTransform(new Vector2(0.5f, 1.0f), tickboxAreaLower.RectTransform))
{ {
ButtonEnabled = false, ButtonEnabled = false,
Enabled = false Enabled = false
@@ -24,6 +24,8 @@ namespace Barotrauma
private GUIListBox chatBox, playerList; private GUIListBox chatBox, playerList;
private GUIListBox serverLogBox, serverLogFilterTicks; private GUIListBox serverLogBox, serverLogFilterTicks;
private GUIComponent jobVariantTooltip;
private GUITextBox chatInput; private GUITextBox chatInput;
private GUITextBox serverLogFilter; private GUITextBox serverLogFilter;
public GUITextBox ChatInput public GUITextBox ChatInput
@@ -419,14 +421,14 @@ namespace Barotrauma
RelativeSpacing = 0.05f RelativeSpacing = 0.05f
}; };
FileTransferTitle = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), fileTransferContent.RectTransform), "", font: GUI.SmallFont); FileTransferTitle = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), fileTransferContent.RectTransform), "", font: GUI.SmallFont);
var fileTransferBottom = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), fileTransferContent.RectTransform), isHorizontal: true) var fileTransferBottom = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), fileTransferContent.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{ {
Stretch = true Stretch = true
}; };
FileTransferProgressBar = new GUIProgressBar(new RectTransform(new Vector2(0.6f, 1.0f), fileTransferBottom.RectTransform), 0.0f, Color.DarkGreen); FileTransferProgressBar = new GUIProgressBar(new RectTransform(new Vector2(0.6f, 1.0f), fileTransferBottom.RectTransform), 0.0f, Color.DarkGreen);
FileTransferProgressText = new GUITextBlock(new RectTransform(Vector2.One, FileTransferProgressBar.RectTransform), "", FileTransferProgressText = new GUITextBlock(new RectTransform(Vector2.One, FileTransferProgressBar.RectTransform), "",
font: GUI.SmallFont, textAlignment: Alignment.CenterLeft); font: GUI.SmallFont, textAlignment: Alignment.CenterLeft);
new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), fileTransferBottom.RectTransform), TextManager.Get("cancel")) new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), fileTransferBottom.RectTransform), TextManager.Get("cancel"), style: "GUIButtonSmall")
{ {
OnClicked = (btn, userdata) => OnClicked = (btn, userdata) =>
{ {
@@ -458,7 +460,7 @@ namespace Barotrauma
myCharacterFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.5f), sideBar.RectTransform)); myCharacterFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.5f), sideBar.RectTransform));
playerInfoContainer = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), myCharacterFrame.RectTransform, Anchor.Center), style: null); playerInfoContainer = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), myCharacterFrame.RectTransform, Anchor.Center), style: null);
spectateBox = new GUITickBox(new RectTransform(new Vector2(0.06f, 0.06f), myCharacterFrame.RectTransform) { RelativeOffset = new Vector2(0.05f, 0.05f) }, spectateBox = new GUITickBox(new RectTransform(new Vector2(0.4f, 0.06f), myCharacterFrame.RectTransform) { RelativeOffset = new Vector2(0.05f, 0.05f) },
TextManager.Get("spectatebutton")) TextManager.Get("spectatebutton"))
{ {
Selected = false, Selected = false,
@@ -1477,6 +1479,39 @@ namespace Barotrauma
} }
} }
private void CreateJobVariantTooltip(JobPrefab jobPrefab, int variant, GUIComponent parentSlot)
{
jobVariantTooltip = new GUIFrame(new RectTransform(new Point((int)(500 * GUI.Scale), (int)(200 * GUI.Scale)), GUI.Canvas, pivot: Pivot.TopRight),
style: "GUIToolTip")
{
UserData = new Pair<JobPrefab, int>(jobPrefab, variant)
};
jobVariantTooltip.RectTransform.AbsoluteOffset = new Point(parentSlot.Rect.Right, parentSlot.Rect.Bottom);
var content = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), jobVariantTooltip.RectTransform, Anchor.Center))
{
Stretch = true,
RelativeSpacing = 0.02f
};
string name =
TextManager.Get("jobname." + jobPrefab.Identifier + (variant + 1), returnNull: true) ??
jobPrefab.Name;
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), name, font: GUI.SubHeadingFont);
string description =
TextManager.Get("jobdescription." + jobPrefab.Identifier + (variant + 1), returnNull: true) ??
TextManager.Get("jobdescription." + jobPrefab.Identifier, returnNull: true) ??
"";
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), description, wrap: true, font: GUI.SmallFont);
new GUICustomComponent(new RectTransform(new Vector2(1.0f, 0.3f), content.RectTransform, Anchor.BottomLeft),
onDraw: (sb, component) => { DrawJobVariantItems(sb, component, new Pair<JobPrefab, int>(jobPrefab, variant)); });
jobVariantTooltip.RectTransform.MinSize = new Point(0, content.RectTransform.Children.Sum(c => c.Rect.Height));
}
public bool ToggleSpectate(GUITickBox tickBox) public bool ToggleSpectate(GUITickBox tickBox)
{ {
SetSpectate(tickBox.Selected); SetSpectate(tickBox.Selected);
@@ -2210,6 +2245,22 @@ namespace Barotrauma
JobList.Deselect(); JobList.Deselect();
JobSelectionFrame.Visible = false; JobSelectionFrame.Visible = false;
} }
if (GUI.MouseOn?.UserData is Pair<JobPrefab, int> jobPrefab && GUI.MouseOn.Style?.Name == "JobVariantButton")
{
var prevVisibleVariant = jobVariantTooltip?.UserData as Pair<JobPrefab, int>;
if (jobVariantTooltip == null || prevVisibleVariant.First != jobPrefab.First || prevVisibleVariant.Second != jobPrefab.Second)
{
CreateJobVariantTooltip(jobPrefab.First, jobPrefab.Second, GUI.MouseOn.Parent);
}
}
if (jobVariantTooltip != null)
{
jobVariantTooltip?.AddToGUIUpdateList();
Rectangle mouseRect = jobVariantTooltip.MouseRect;
mouseRect.Inflate(60 * GUI.Scale, 60 * GUI.Scale);
if (!mouseRect.Contains(PlayerInput.MousePosition)) { jobVariantTooltip = null; }
}
} }
public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch) public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
{ {
@@ -2251,6 +2302,52 @@ namespace Barotrauma
} }
} }
private void DrawJobVariantItems(SpriteBatch spriteBatch, GUICustomComponent component, Pair<JobPrefab, int> jobPrefab)
{
var itemIdentifiers = jobPrefab.First.ItemIdentifiers[jobPrefab.Second]
.Distinct()
.Where(id => jobPrefab.First.ShowItemPreview[jobPrefab.Second][id]);
Point slotSize = new Point(component.Rect.Height);
int spacing = (int)(5 * GUI.Scale);
int slotCount = itemIdentifiers.Count();
float totalWidth = slotSize.X * slotCount + spacing * (slotCount - 1);
if (totalWidth > component.Rect.Width)
{
slotSize = slotSize.Multiply(component.Rect.Width / totalWidth);
}
int i = 0;
foreach (string itemIdentifier in itemIdentifiers)
{
if (!(MapEntityPrefab.Find(null, identifier: itemIdentifier, showErrorMessages: false) is ItemPrefab itemPrefab)) { continue; }
Vector2 slotPos = new Vector2(component.Rect.X + (slotSize.X + spacing) * i, component.Rect.Center.Y - slotSize.Y / 2);
Rectangle slotRect = new Rectangle(slotPos.ToPoint(), slotSize);
Inventory.SlotSpriteSmall.Draw(spriteBatch, slotPos,
scale: slotSize.X / (float)Inventory.SlotSpriteSmall.SourceRect.Width,
color: slotRect.Contains(PlayerInput.MousePosition) ? Color.White : Color.White * 0.6f);
Sprite icon = itemPrefab.InventoryIcon ?? itemPrefab.sprite;
float iconScale = Math.Min(Math.Min(slotSize.X / icon.size.X, slotSize.Y / icon.size.Y), 2.0f) * 0.9f;
icon.Draw(spriteBatch, slotPos + slotSize.ToVector2() * 0.5f, scale: iconScale);
int count = jobPrefab.First.ItemIdentifiers[jobPrefab.Second].Count(id => id == itemIdentifier);
if (count > 1)
{
string itemCountText = "x" + count;
GUI.Font.DrawString(spriteBatch, itemCountText, slotPos + slotSize.ToVector2() - GUI.Font.MeasureString(itemCountText) - Vector2.UnitX * 5, Color.White);
}
if (slotRect.Contains(PlayerInput.MousePosition))
{
GUIComponent.DrawToolTip(spriteBatch, itemPrefab.Name+'\n'+itemPrefab.Description, slotRect);
}
i++;
}
}
public void NewChatMessage(ChatMessage message) public void NewChatMessage(ChatMessage message)
{ {
float prevSize = chatBox.BarSize; float prevSize = chatBox.BarSize;
@@ -3012,8 +3109,10 @@ namespace Barotrauma
(variantIndex + 1).ToString(), style: "JobVariantButton") (variantIndex + 1).ToString(), style: "JobVariantButton")
{ {
Selected = jobPrefab.Second == variantIndex, Selected = jobPrefab.Second == variantIndex,
//ToolTip = TextManager.Get("jobdescription." + jobPrefab.First.Identifier + (variantIndex + 1), returnNull: true) ?? "",
UserData = new Pair<JobPrefab, int>(jobPrefab.First, variantIndex), UserData = new Pair<JobPrefab, int>(jobPrefab.First, variantIndex),
}; };
return btn; return btn;
} }
@@ -1102,6 +1102,8 @@ namespace Barotrauma
return true; return true;
}; };
nameBox.Text = subNameLabel?.Text ?? "";
submarineNameCharacterCount.Text = nameBox.Text.Length + " / " + submarineNameLimit; submarineNameCharacterCount.Text = nameBox.Text.Length + " / " + submarineNameLimit;
var descriptionHeaderGroup = new GUILayoutGroup(new RectTransform(new Vector2(.975f, 0.03f), leftColumn.RectTransform), true); var descriptionHeaderGroup = new GUILayoutGroup(new RectTransform(new Vector2(.975f, 0.03f), leftColumn.RectTransform), true);
@@ -66,7 +66,7 @@ namespace Barotrauma
{ {
if (sound?.Sound == null) if (sound?.Sound == null)
{ {
string errorMsg = $"Error in StatusEffect.ApplyProjSpecific1 (sound \"{sound.Filename ?? "unknown"}\" was null)\n" + Environment.StackTrace; string errorMsg = $"Error in StatusEffect.ApplyProjSpecific1 (sound \"{sound?.Filename ?? "unknown"}\" was null)\n" + Environment.StackTrace;
GameAnalyticsManager.AddErrorEventOnce("StatusEffect.ApplyProjSpecific:SoundNull1" + Environment.StackTrace, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("StatusEffect.ApplyProjSpecific:SoundNull1" + Environment.StackTrace, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
return; return;
} }
@@ -92,7 +92,7 @@ namespace Barotrauma
var selectedSound = sounds[selectedSoundIndex]; var selectedSound = sounds[selectedSoundIndex];
if (selectedSound?.Sound == null) if (selectedSound?.Sound == null)
{ {
string errorMsg = $"Error in StatusEffect.ApplyProjSpecific2 (sound \"{selectedSound.Filename ?? "unknown"}\" was null)\n" + Environment.StackTrace; string errorMsg = $"Error in StatusEffect.ApplyProjSpecific2 (sound \"{selectedSound?.Filename ?? "unknown"}\" was null)\n" + Environment.StackTrace;
GameAnalyticsManager.AddErrorEventOnce("StatusEffect.ApplyProjSpecific:SoundNull2" + Environment.StackTrace, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("StatusEffect.ApplyProjSpecific:SoundNull2" + Environment.StackTrace, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
return; return;
} }
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.9.703.0</Version> <Version>0.9.704.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
+1 -1
View File
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.9.702.0</Version> <Version>0.9.704.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.9.703.0</Version> <Version>0.9.704.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.9.703.0</Version> <Version>0.9.704.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
+1 -1
View File
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.9.703.0</Version> <Version>0.9.704.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
@@ -394,7 +394,7 @@ namespace Barotrauma
tempBuffer.WritePadBits(); tempBuffer.WritePadBits();
msg.Write((byte)tempBuffer.LengthBytes); msg.WriteVariableUInt32((uint)tempBuffer.LengthBytes);
msg.Write(tempBuffer.Buffer, 0, tempBuffer.LengthBytes); msg.Write(tempBuffer.Buffer, 0, tempBuffer.LengthBytes);
} }
} }
@@ -439,7 +439,7 @@ namespace Barotrauma
client.GivePermission(permission); client.GivePermission(permission);
GameMain.Server.UpdateClientPermissions(client); GameMain.Server.UpdateClientPermissions(client);
NewMessage("Granted " + perm + " permissions to " + client.Name + ".", Color.White); NewMessage("Granted " + perm + " permissions to " + client.Name + ".", Color.White);
}, args, 2); }, args, 1);
}); });
AssignOnExecute("revokeperm", (string[] args) => AssignOnExecute("revokeperm", (string[] args) =>
@@ -474,7 +474,7 @@ namespace Barotrauma
client.RemovePermission(permission); client.RemovePermission(permission);
GameMain.Server.UpdateClientPermissions(client); GameMain.Server.UpdateClientPermissions(client);
NewMessage("Revoked " + perm + " permissions from " + client.Name + ".", Color.White); NewMessage("Revoked " + perm + " permissions from " + client.Name + ".", Color.White);
}, args, 2); }, args, 1);
}); });
AssignOnExecute("giverank", (string[] args) => AssignOnExecute("giverank", (string[] args) =>
@@ -511,7 +511,7 @@ namespace Barotrauma
client.SetPermissions(preset.Permissions, preset.PermittedCommands); client.SetPermissions(preset.Permissions, preset.PermittedCommands);
GameMain.Server.UpdateClientPermissions(client); GameMain.Server.UpdateClientPermissions(client);
NewMessage("Assigned the rank \"" + preset.Name + "\" to " + client.Name + ".", Color.White); NewMessage("Assigned the rank \"" + preset.Name + "\" to " + client.Name + ".", Color.White);
}, args, 2); }, args, 1);
}); });
AssignOnExecute("givecommandperm", (string[] args) => AssignOnExecute("givecommandperm", (string[] args) =>
@@ -552,7 +552,7 @@ namespace Barotrauma
client.SetPermissions(client.Permissions, client.PermittedConsoleCommands.Union(grantedCommands).Distinct().ToList()); client.SetPermissions(client.Permissions, client.PermittedConsoleCommands.Union(grantedCommands).Distinct().ToList());
GameMain.Server.UpdateClientPermissions(client); GameMain.Server.UpdateClientPermissions(client);
NewMessage("Gave the client \"" + client.Name + "\" the permission to use console commands " + string.Join(", ", grantedCommands.Select(c => c.names[0])) + ".", Color.White); NewMessage("Gave the client \"" + client.Name + "\" the permission to use console commands " + string.Join(", ", grantedCommands.Select(c => c.names[0])) + ".", Color.White);
}, args, 2); }, args, 1);
}); });
AssignOnExecute("revokecommandperm", (string[] args) => AssignOnExecute("revokecommandperm", (string[] args) =>
@@ -592,7 +592,7 @@ namespace Barotrauma
client.SetPermissions(client.Permissions, client.PermittedConsoleCommands.Except(revokedCommands).ToList()); client.SetPermissions(client.Permissions, client.PermittedConsoleCommands.Except(revokedCommands).ToList());
GameMain.Server.UpdateClientPermissions(client); GameMain.Server.UpdateClientPermissions(client);
NewMessage("Revoked \"" + client.Name + "\"'s permission to use the console commands " + string.Join(", ", revokedCommands.Select(c => c.names[0])) + ".", Color.White); NewMessage("Revoked \"" + client.Name + "\"'s permission to use the console commands " + string.Join(", ", revokedCommands.Select(c => c.names[0])) + ".", Color.White);
}, args, 2); }, args, 1);
}); });
AssignOnExecute("showperm", (string[] args) => AssignOnExecute("showperm", (string[] args) =>
@@ -0,0 +1,16 @@
using Barotrauma.Networking;
namespace Barotrauma
{
partial class CargoMission : Mission
{
public override void ServerWriteInitial(IWriteMessage msg, Client c)
{
msg.Write((ushort)items.Count);
foreach (Item item in items)
{
item.WriteSpawnData(msg, item.ID);
}
}
}
}
@@ -109,5 +109,10 @@ namespace Barotrauma
} }
} }
} }
public override void ServerWriteInitial(IWriteMessage msg, Client c)
{
//do nothing
}
} }
} }
@@ -13,5 +13,7 @@ namespace Barotrauma
GameServer.Log(TextManager.Get("MissionInfo") + ": " + header + " - " + message, ServerLog.MessageType.ServerMessage); GameServer.Log(TextManager.Get("MissionInfo") + ": " + header + " - " + message, ServerLog.MessageType.ServerMessage);
} }
public abstract void ServerWriteInitial(IWriteMessage msg, Client c);
} }
} }
@@ -0,0 +1,22 @@
using Barotrauma.Networking;
using System;
namespace Barotrauma
{
partial class MonsterMission : Mission
{
public override void ServerWriteInitial(IWriteMessage msg, Client c)
{
if (monsters.Count == 0 && monsterFiles.Count > 0)
{
throw new InvalidOperationException("Server attempted to write monster mission data when no monsters had been spawned.");
}
msg.Write((byte)monsters.Count);
foreach (Character monster in monsters)
{
monster.WriteSpawnData(msg, monster.ID);
}
}
}
}
@@ -0,0 +1,12 @@
using Barotrauma.Networking;
namespace Barotrauma
{
partial class SalvageMission : Mission
{
public override void ServerWriteInitial(IWriteMessage msg, Client c)
{
item.WriteSpawnData(msg, item.ID);
}
}
}
@@ -273,10 +273,8 @@ namespace Barotrauma
} }
Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex); Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex);
if (Map.SelectedConnection != null) if (Map.SelectedLocation == null) { Map.SelectRandomLocation(preferUndiscovered: true); }
{ if (Map.SelectedConnection != null) { Map.SelectMission(selectedMissionIndex); }
Map.SelectMission(selectedMissionIndex);
}
List<PurchasedItem> currentItems = new List<PurchasedItem>(CargoManager.PurchasedItems); List<PurchasedItem> currentItems = new List<PurchasedItem>(CargoManager.PurchasedItems);
foreach (PurchasedItem pi in currentItems) foreach (PurchasedItem pi in currentItems)
@@ -345,7 +345,7 @@ namespace Barotrauma
IWriteMessage tempBuffer = new WriteOnlyMessage(); IWriteMessage tempBuffer = new WriteOnlyMessage();
body.ServerWrite(tempBuffer, c, extraData); body.ServerWrite(tempBuffer, c, extraData);
msg.Write((byte)tempBuffer.LengthBytes); msg.WriteVariableUInt32((uint)tempBuffer.LengthBytes);
msg.Write(tempBuffer.Buffer, 0, tempBuffer.LengthBytes); msg.Write(tempBuffer.Buffer, 0, tempBuffer.LengthBytes);
msg.WritePadBits(); msg.WritePadBits();
} }
@@ -1783,6 +1783,17 @@ namespace Barotrauma.Networking
if (campaign != null) if (campaign != null)
{ {
if (campaign.Map == null)
{
throw new Exception("Campaign map was null.");
}
if (campaign.Map.SelectedConnection == null)
{
//this should not happen, there should always be some destination selected
DebugConsole.ThrowError("No connection between locations was selected when starting the round. Choosing a random location...");
campaign.Map.SelectRandomLocation(preferUndiscovered: true);
}
GameMain.GameSession.StartRound(campaign.Map.SelectedConnection.Level, GameMain.GameSession.StartRound(campaign.Map.SelectedConnection.Level,
reloadSub: true, reloadSub: true,
loadSecondSub: teamCount > 1, loadSecondSub: teamCount > 1,
@@ -2007,6 +2018,8 @@ namespace Barotrauma.Networking
serverSettings.WriteMonsterEnabled(msg); serverSettings.WriteMonsterEnabled(msg);
GameMain.GameSession.Mission?.ServerWriteInitial(msg, client);
serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable); serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
} }
@@ -20,6 +20,11 @@ namespace Barotrauma.Networking
get { return createTime; } get { return createTime; }
} }
public void ResetCreateTime()
{
createTime = Timing.TotalTime;
}
public ServerEntityEvent(IServerSerializable serializableEntity, UInt16 id) public ServerEntityEvent(IServerSerializable serializableEntity, UInt16 id)
: base(serializableEntity, id) : base(serializableEntity, id)
{ {
@@ -223,6 +228,7 @@ namespace Barotrauma.Networking
{ {
lastWarningTime = Timing.TotalTime; lastWarningTime = Timing.TotalTime;
GameServer.Log("WARNING: ServerEntityEventManager is lagging behind! Last sent id: " + lastSentToAnyone.ToString() + ", latest create id: " + ID.ToString(), ServerLog.MessageType.ServerMessage); GameServer.Log("WARNING: ServerEntityEventManager is lagging behind! Last sent id: " + lastSentToAnyone.ToString() + ", latest create id: " + ID.ToString(), ServerLog.MessageType.ServerMessage);
events.ForEach(e => e.ResetCreateTime());
//TODO: reset clients if this happens, maybe do it if a majority are behind rather than all of them? //TODO: reset clients if this happens, maybe do it if a majority are behind rather than all of them?
} }
@@ -175,6 +175,7 @@ namespace Barotrauma.Networking
GameMain.NetworkMember?.KarmaManager?.SaveCustomPreset(); GameMain.NetworkMember?.KarmaManager?.SaveCustomPreset();
GameMain.NetworkMember?.KarmaManager?.Save(); GameMain.NetworkMember?.KarmaManager?.Save();
} }
SaveSettings();
GameMain.NetLobbyScreen.LastUpdateID++; GameMain.NetLobbyScreen.LastUpdateID++;
} }
} }
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.9.703.0</Version> <Version>0.9.704.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
@@ -142,16 +142,8 @@ namespace Barotrauma
} }
} }
public AITarget(Entity e, XElement element) : this(e) public void Reset()
{ {
SightRange = element.GetAttributeFloat("sightrange", 0.0f);
SoundRange = element.GetAttributeFloat("soundrange", 0.0f);
MinSightRange = element.GetAttributeFloat("minsightrange", 0f);
MinSoundRange = element.GetAttributeFloat("minsoundrange", 0f);
MaxSightRange = element.GetAttributeFloat("maxsightrange", SightRange);
MaxSoundRange = element.GetAttributeFloat("maxsoundrange", SoundRange);
FadeOutTime = element.GetAttributeFloat("fadeouttime", FadeOutTime);
Static = element.GetAttributeBool("static", Static);
if (Static) if (Static)
{ {
SightRange = MaxSightRange; SightRange = MaxSightRange;
@@ -163,7 +155,18 @@ namespace Barotrauma
SightRange = MinSightRange; SightRange = MinSightRange;
SoundRange = MinSoundRange; SoundRange = MinSoundRange;
} }
}
public AITarget(Entity e, XElement element) : this(e)
{
SightRange = element.GetAttributeFloat("sightrange", 0.0f);
SoundRange = element.GetAttributeFloat("soundrange", 0.0f);
MinSightRange = element.GetAttributeFloat("minsightrange", 0f);
MinSoundRange = element.GetAttributeFloat("minsoundrange", 0f);
MaxSightRange = element.GetAttributeFloat("maxsightrange", SightRange);
MaxSoundRange = element.GetAttributeFloat("maxsoundrange", SoundRange);
FadeOutTime = element.GetAttributeFloat("fadeouttime", FadeOutTime);
Static = element.GetAttributeBool("static", Static);
SonarDisruption = element.GetAttributeFloat("sonardisruption", 0.0f); SonarDisruption = element.GetAttributeFloat("sonardisruption", 0.0f);
SonarLabel = element.GetAttributeString("sonarlabel", ""); SonarLabel = element.GetAttributeString("sonarlabel", "");
SonarIconIdentifier = element.GetAttributeString("sonaricon", ""); SonarIconIdentifier = element.GetAttributeString("sonaricon", "");
@@ -172,6 +175,7 @@ namespace Barotrauma
{ {
Type = t; Type = t;
} }
Reset();
} }
public AITarget(Entity e) public AITarget(Entity e)
@@ -270,6 +270,17 @@ namespace Barotrauma
} }
} }
if (isStateChanged)
{
if (State == AIState.Idle)
{
stateResetTimer -= deltaTime;
if (stateResetTimer <= 0)
{
ResetOriginalState();
}
}
}
if (targetIgnoreTimer > 0) if (targetIgnoreTimer > 0)
{ {
targetIgnoreTimer -= deltaTime; targetIgnoreTimer -= deltaTime;
@@ -662,7 +673,7 @@ namespace Barotrauma
attackSimPos = Character.GetRelativeSimPosition(SelectedAiTarget.Entity); attackSimPos = Character.GetRelativeSimPosition(SelectedAiTarget.Entity);
} }
if (CanEnterSubmarine) if (Character.AnimController.CanEnterSubmarine)
{ {
//targeting a wall section that can be passed through -> steer manually through the hole //targeting a wall section that can be passed through -> steer manually through the hole
if (wallTarget != null && wallTarget.SectionIndex > -1 && CanPassThroughHole(wallTarget.Structure, wallTarget.SectionIndex)) if (wallTarget != null && wallTarget.SectionIndex > -1 && CanPassThroughHole(wallTarget.Structure, wallTarget.SectionIndex))
@@ -674,21 +685,20 @@ namespace Barotrauma
return; return;
} }
} }
// Don't think we need this else if (SelectedAiTarget.Entity is Structure wall)
//else if (wallTarget == null && SelectedAiTarget.Entity is Structure wall) {
//{ for (int i = 0; i < wall.Sections.Length; i++)
// for (int i = 0; i < wall.Sections.Length; i++) {
// { WallSection section = wall.Sections[i];
// WallSection section = wall.Sections[i]; if (CanPassThroughHole(wall, i) && section?.gap != null)
// if (CanPassThroughHole(wall, i) && section?.gap != null) {
// { if (SteerThroughGap(wall, section, section.gap.WorldPosition, deltaTime))
// if (SteerThroughGap(wall, section, section.gap.WorldPosition, deltaTime)) {
// { return;
// return; }
// } }
// } }
// } }
//}
else if (SelectedAiTarget.Entity is Item i) else if (SelectedAiTarget.Entity is Item i)
{ {
var door = i.GetComponent<Door>(); var door = i.GetComponent<Door>();
@@ -1024,7 +1034,7 @@ namespace Barotrauma
var door = pathSteering.CurrentPath.CurrentNode?.ConnectedDoor ?? pathSteering.CurrentPath.NextNode?.ConnectedDoor; var door = pathSteering.CurrentPath.CurrentNode?.ConnectedDoor ?? pathSteering.CurrentPath.NextNode?.ConnectedDoor;
if (door != null && !door.IsOpen) if (door != null && !door.IsOpen)
{ {
if (SelectedAiTarget != door.Item.AiTarget) if (door.Item.AiTarget != null && SelectedAiTarget != door.Item.AiTarget)
{ {
SelectTarget(door.Item.AiTarget, selectedTargetMemory.Priority); SelectTarget(door.Item.AiTarget, selectedTargetMemory.Priority);
return; return;
@@ -1076,15 +1086,15 @@ namespace Barotrauma
LatchOntoAI?.DeattachFromBody(); LatchOntoAI?.DeattachFromBody();
Character.AnimController.ReleaseStuckLimbs(); Character.AnimController.ReleaseStuckLimbs();
Hull targetHull = section.gap?.FlowTargetHull; Hull targetHull = section.gap?.FlowTargetHull;
SelectedAiTarget = targetHull != null ? targetHull.AiTarget : wall.AiTarget;
float distance = Vector2.DistanceSquared(Character.WorldPosition, targetWorldPos);
float maxDistance = Math.Min(wall.Rect.Width, wall.Rect.Height); float maxDistance = Math.Min(wall.Rect.Width, wall.Rect.Height);
if (distance * distance > maxDistance * maxDistance) if (Vector2.DistanceSquared(Character.WorldPosition, targetWorldPos) > maxDistance * maxDistance)
{ {
return false; return false;
} }
if (targetHull != null) if (targetHull != null)
{ {
// If already inside, target the hull, else target the wall.
SelectedAiTarget = Character.CurrentHull != null ? targetHull.AiTarget : wall.AiTarget;
if (wall.IsHorizontal) if (wall.IsHorizontal)
{ {
targetWorldPos.Y = targetHull.WorldRect.Y - targetHull.Rect.Height / 2; targetWorldPos.Y = targetHull.WorldRect.Y - targetHull.Rect.Height / 2;
@@ -1094,6 +1104,7 @@ namespace Barotrauma
targetWorldPos.X = targetHull.WorldRect.Center.X; targetWorldPos.X = targetHull.WorldRect.Center.X;
} }
steeringManager.SteeringManual(deltaTime, Vector2.Normalize(targetWorldPos - Character.WorldPosition)); steeringManager.SteeringManual(deltaTime, Vector2.Normalize(targetWorldPos - Character.WorldPosition));
SteeringManager.SteeringAvoid(deltaTime, lookAheadDistance: avoidLookAheadDistance, weight: 15);
return true; return true;
} }
return false; return false;
@@ -1102,9 +1113,16 @@ namespace Barotrauma
private bool CanAttack(Entity target) private bool CanAttack(Entity target)
{ {
if (target == null) { return false; } if (target == null) { return false; }
if (target is Character ch) if (target is Character c)
{ {
if (Character.CurrentHull == null && ch.CurrentHull != null || Character.CurrentHull != null && ch.CurrentHull == null) if (Character.CurrentHull == null && c.CurrentHull != null || Character.CurrentHull != null && c.CurrentHull == null)
{
return false;
}
}
else if (target is Item i && i.GetComponent<Door>() == null)
{
if (Character.CurrentHull == null && i.CurrentHull != null || Character.CurrentHull != null && i.CurrentHull == null)
{ {
return false; return false;
} }
@@ -1175,7 +1193,7 @@ namespace Barotrauma
{ {
if (wall.SectionBodyDisabled(i)) if (wall.SectionBodyDisabled(i))
{ {
if (CanEnterSubmarine && CanPassThroughHole(wall, i)) if (Character.AnimController.CanEnterSubmarine && CanPassThroughHole(wall, i))
{ {
sectionIndex = i; sectionIndex = i;
break; break;
@@ -1204,12 +1222,12 @@ namespace Barotrauma
sectionPos.X += (wall.BodyWidth <= 0.0f ? wall.Rect.Width : wall.BodyWidth) / 2 * attachTargetNormal.X; sectionPos.X += (wall.BodyWidth <= 0.0f ? wall.Rect.Width : wall.BodyWidth) / 2 * attachTargetNormal.X;
} }
LatchOntoAI?.SetAttachTarget(wall.Submarine.PhysicsBody.FarseerBody, wall.Submarine, ConvertUnits.ToSimUnits(sectionPos), attachTargetNormal); LatchOntoAI?.SetAttachTarget(wall.Submarine.PhysicsBody.FarseerBody, wall.Submarine, ConvertUnits.ToSimUnits(sectionPos), attachTargetNormal);
if (CanEnterSubmarine || !wall.SectionBodyDisabled(sectionIndex) && !IsWallDisabled(wall)) if (Character.AnimController.CanEnterSubmarine || !wall.SectionBodyDisabled(sectionIndex) && !IsWallDisabled(wall))
{ {
wallTarget = new WallTarget(sectionPos, wall, sectionIndex); wallTarget = new WallTarget(sectionPos, wall, sectionIndex);
} }
} }
if (!CanEnterSubmarine && wallTarget == null) if (!Character.AnimController.CanEnterSubmarine && wallTarget == null)
{ {
if (closestBody.UserData is Structure w && w.Submarine != null || closestBody.UserData is Item i && i.Submarine != null) if (closestBody.UserData is Structure w && w.Submarine != null || closestBody.UserData is Item i && i.Submarine != null)
{ {
@@ -1251,12 +1269,18 @@ namespace Barotrauma
return; return;
} }
if (attackResult.Damage > 0.0f && Character.Params.AI.AttackWhenProvoked) if (attackResult.Damage > 0.0f)
{ {
if (attacker.Submarine == Character.Submarine && canAttackCharacters || if (Character.Params.AI.AttackWhenProvoked)
attacker.Submarine != null && canAttackSub)
{ {
ChangeTargetState(attacker, AIState.Attack, 100); if (attacker.Submarine == Character.Submarine && canAttackCharacters || attacker.Submarine != null && canAttackSub)
{
ChangeTargetState(attacker, AIState.Attack, 100);
}
}
else if (!AIParams.HasTag(attacker.SpeciesName))
{
ChangeTargetState(attacker, AIState.Flee, 100);
} }
} }
@@ -1306,7 +1330,8 @@ namespace Barotrauma
SelectTarget(aiTarget, GetTargetMemory(SelectedAiTarget).Priority); SelectTarget(aiTarget, GetTargetMemory(SelectedAiTarget).Priority);
} }
} }
if (SelectedAiTarget.Entity is IDamageable damageTarget) IDamageable damageTarget = wallTarget != null ? wallTarget.Structure : SelectedAiTarget.Entity as IDamageable;
if (damageTarget != null)
{ {
if (attackingLimb.UpdateAttack(deltaTime, attackSimPos, damageTarget, out AttackResult attackResult, distance, targetLimb)) if (attackingLimb.UpdateAttack(deltaTime, attackSimPos, damageTarget, out AttackResult attackResult, distance, targetLimb))
{ {
@@ -1480,9 +1505,9 @@ namespace Barotrauma
} }
else else
{ {
// Don't attack targets that are not in the same submarine
continue; continue;
} }
} }
if (targetCharacter.IsDead) if (targetCharacter.IsDead)
{ {
@@ -1578,7 +1603,7 @@ namespace Barotrauma
continue; continue;
} }
valueModifier = 1; valueModifier = 1;
if (!CanEnterSubmarine && IsWallDisabled(s)) if (!Character.AnimController.CanEnterSubmarine && IsWallDisabled(s))
{ {
continue; continue;
} }
@@ -1587,7 +1612,7 @@ namespace Barotrauma
var section = s.Sections[i]; var section = s.Sections[i];
if (section.gap == null) { continue; } if (section.gap == null) { continue; }
bool leadsInside = !section.gap.IsRoomToRoom && section.gap.FlowTargetHull != null; bool leadsInside = !section.gap.IsRoomToRoom && section.gap.FlowTargetHull != null;
if (CanEnterSubmarine) if (Character.AnimController.CanEnterSubmarine)
{ {
if (CanPassThroughHole(s, i)) if (CanPassThroughHole(s, i))
{ {
@@ -1766,7 +1791,7 @@ namespace Barotrauma
removals.ForEach(r => targetMemories.Remove(r)); removals.ForEach(r => targetMemories.Remove(r));
} }
private const float targetIgnoreTime = 5; private readonly float targetIgnoreTime = 5;
private float targetIgnoreTimer; private float targetIgnoreTimer;
private readonly HashSet<AITarget> ignoredTargets = new HashSet<AITarget>(); private readonly HashSet<AITarget> ignoredTargets = new HashSet<AITarget>();
public void IgnoreTarget(AITarget target) public void IgnoreTarget(AITarget target)
@@ -1775,25 +1800,16 @@ namespace Barotrauma
ignoredTargets.Add(target); ignoredTargets.Add(target);
targetIgnoreTimer = targetIgnoreTime * Rand.Range(0.75f, 1.25f); targetIgnoreTimer = targetIgnoreTime * Rand.Range(0.75f, 1.25f);
} }
protected override void OnTargetChanged(AITarget previousTarget, AITarget newTarget)
{
if (previousTarget == null || newTarget == null) { return; }
var previousCharacter = previousTarget.Entity as Character;
var newCharacter = newTarget.Entity as Character;
if (previousCharacter == null && newCharacter == null)
{
return;
}
if (previousCharacter != null && newCharacter != null && previousCharacter.SpeciesName == newCharacter.SpeciesName)
{
return;
}
modifiedParams.Keys.ForEachMod(tag => TryResetOriginalState(tag));
}
#endregion #endregion
#region State switching #region State switching
/// <summary>
/// How long do we hold on to the current state after losing a target before we reset back to the original state.
/// In other words, how long do we have to idle before the original state is restored.
/// </summary>
private readonly float stateResetCooldown = 10;
private float stateResetTimer;
private bool isStateChanged;
/// <summary> /// <summary>
/// Resets the target's state to the original value defined in the xml. /// Resets the target's state to the original value defined in the xml.
@@ -1806,6 +1822,7 @@ namespace Barotrauma
modifiedParams.Remove(tag); modifiedParams.Remove(tag);
if (tempParams.ContainsKey(tag)) if (tempParams.ContainsKey(tag))
{ {
tempParams.Values.ForEach(t => AIParams.RemoveTarget(t));
tempParams.Remove(tag); tempParams.Remove(tag);
} }
targetParams.Reset(); targetParams.Reset();
@@ -1829,23 +1846,39 @@ namespace Barotrauma
/// </summary> /// </summary>
private void ChangeTargetState(Character target, AIState state, float? priority = null) private void ChangeTargetState(Character target, AIState state, float? priority = null)
{ {
isStateChanged = true;
SetStateResetTimer();
ChangeParams(target.SpeciesName); ChangeParams(target.SpeciesName);
// If the target is shooting from the submarine, we might not perceive it because it doesn't move. // Target also items, because if we are blind and the target doesn't move, we can only perceive the target when it uses items
// --> Target the submarine too. if (state == AIState.Attack || state == AIState.Flee)
if (target.Submarine != null && state == AIState.Attack && canAttackSub)
{ {
ChangeParams("room"); ChangeParams("weapon");
ChangeParams("wall"); ChangeParams("tool");
ChangeParams("door"); }
if (state == AIState.Attack)
{
// If the target is shooting from the submarine, we might not perceive it because it doesn't move.
// --> Target the submarine too.
if (target.Submarine != null && canAttackSub)
{
ChangeParams("room");
ChangeParams("wall");
ChangeParams("door");
}
ChangeParams("provocative", onlyExisting: true);
ChangeParams("light", onlyExisting: true);
} }
void ChangeParams(string tag) void ChangeParams(string tag, bool onlyExisting = false)
{ {
if (!AIParams.TryGetTarget(tag, out CharacterParams.TargetParams targetParams)) if (!AIParams.TryGetTarget(tag, out CharacterParams.TargetParams targetParams))
{ {
if (AIParams.TryAddNewTarget(tag, state, priority ?? 100, out targetParams)) if (!onlyExisting && !tempParams.ContainsKey(tag))
{ {
tempParams.Add(tag, targetParams); if (AIParams.TryAddNewTarget(tag, state, priority ?? 100, out targetParams))
{
tempParams.Add(tag, targetParams);
}
} }
} }
if (targetParams != null) if (targetParams != null)
@@ -1862,6 +1895,12 @@ namespace Barotrauma
} }
} }
} }
private void ResetOriginalState()
{
isStateChanged = false;
modifiedParams.Keys.ForEachMod(tag => TryResetOriginalState(tag));
}
#endregion #endregion
protected override void OnStateChanged(AIState from, AIState to) protected override void OnStateChanged(AIState from, AIState to)
@@ -1873,8 +1912,14 @@ namespace Barotrauma
escapeMargin = 0; escapeMargin = 0;
allGapsSearched = false; allGapsSearched = false;
unreachableGaps.Clear(); unreachableGaps.Clear();
if (isStateChanged && to == AIState.Idle && from != to)
{
SetStateResetTimer();
}
} }
private void SetStateResetTimer() => stateResetTimer = stateResetCooldown * Rand.Range(0.75f, 1.25f);
private float GetPerceivingRange(AITarget target) => Math.Max(target.SightRange * Sight, target.SoundRange * Hearing); private float GetPerceivingRange(AITarget target) => Math.Max(target.SightRange * Sight, target.SoundRange * Hearing);
private bool CanPerceive(AITarget target, float dist = -1, float distSquared = -1) private bool CanPerceive(AITarget target, float dist = -1, float distSquared = -1)
@@ -711,6 +711,7 @@ namespace Barotrauma
public void SetOrder(Order order, string option, Character orderGiver, bool speak = true) public void SetOrder(Order order, string option, Character orderGiver, bool speak = true)
{ {
SetOrderProjSpecific(order, option);
CurrentOrderOption = option; CurrentOrderOption = option;
CurrentOrder = order; CurrentOrder = order;
objectiveManager.SetOrder(order, option, orderGiver); objectiveManager.SetOrder(order, option, orderGiver);
@@ -749,10 +750,9 @@ namespace Barotrauma
Character.Speak(TextManager.Get("DialogAffirmative"), null, 1.0f); Character.Speak(TextManager.Get("DialogAffirmative"), null, 1.0f);
} }
} }
SetOrderProjSpecific(order);
} }
partial void SetOrderProjSpecific(Order order); partial void SetOrderProjSpecific(Order order, string option);
public override void SelectTarget(AITarget target) public override void SelectTarget(AITarget target)
{ {
@@ -970,8 +970,8 @@ namespace Barotrauma
{ {
bool isValidTarget(Character e) => IsActive(e) && !IsFriendly(character, e); bool isValidTarget(Character e) => IsActive(e) && !IsFriendly(character, e);
int enemyCount = visibleHulls == null ? int enemyCount = visibleHulls == null ?
Character.CharacterList.Count(e => e.CurrentHull == hull && isValidTarget(e)) : Character.CharacterList.Count(e => isValidTarget(e) && e.CurrentHull == hull) :
Character.CharacterList.Count(e => visibleHulls.Contains(e.CurrentHull) && isValidTarget(e)); Character.CharacterList.Count(e => isValidTarget(e) && visibleHulls.Contains(e.CurrentHull));
// The hull safety decreases 90% per enemy up to 100% (TODO: test smaller percentages) // The hull safety decreases 90% per enemy up to 100% (TODO: test smaller percentages)
enemyFactor = MathHelper.Lerp(1, 0, MathHelper.Clamp(enemyCount * 0.9f, 0, 1)); enemyFactor = MathHelper.Lerp(1, 0, MathHelper.Clamp(enemyCount * 0.9f, 0, 1));
} }
@@ -988,7 +988,7 @@ namespace Barotrauma
return sameSpecies && !differentTeam; return sameSpecies && !differentTeam;
} }
public static bool IsActive(Character other) => !other.Removed && !other.IsDead && !other.IsUnconscious; public static bool IsActive(Character other) => other != null && !other.Removed && !other.IsDead && !other.IsUnconscious;
public static bool IsTrueForAllCrewMembers(Character character, Func<HumanAIController, bool> predicate) public static bool IsTrueForAllCrewMembers(Character character, Func<HumanAIController, bool> predicate)
{ {
@@ -145,9 +145,14 @@ namespace Barotrauma
}, },
onAbandon: () => onAbandon: () =>
{ {
// Don't ignore any hulls if outside, because apparently it happens that we can't find a path, in which case we just want to try again.
// If we ignore the hull, it might be the only airlock in the target sub, which ignores the whole sub.
if (currentHull != null && goToObjective != null) if (currentHull != null && goToObjective != null)
{ {
HumanAIController.UnreachableHulls.Add(goToObjective.Target as Hull); if (goToObjective.Target is Hull hull)
{
HumanAIController.UnreachableHulls.Add(hull);
}
} }
RemoveSubObjective(ref goToObjective); RemoveSubObjective(ref goToObjective);
}); });
@@ -172,7 +177,7 @@ namespace Barotrauma
} }
foreach (Character enemy in Character.CharacterList) foreach (Character enemy in Character.CharacterList)
{ {
if (HumanAIController.IsFriendly(enemy) || !HumanAIController.IsActive(enemy)) { continue; } if (!HumanAIController.IsActive(enemy) || HumanAIController.IsFriendly(enemy)) { continue; }
if (HumanAIController.VisibleHulls.Contains(enemy.CurrentHull)) if (HumanAIController.VisibleHulls.Contains(enemy.CurrentHull))
{ {
Vector2 dir = character.Position - enemy.Position; Vector2 dir = character.Position - enemy.Position;
@@ -265,6 +265,14 @@ namespace Barotrauma
{ {
get get
{ {
if (SteeringManager == PathSteering && PathSteering.CurrentPath?.CurrentNode?.Ladders != null)
{
//don't consider the character to be close enough to the target while climbing ladders,
//UNLESS the last node in the path has been reached
//otherwise characters can let go of the ladders too soon once they're close enough to the target
if (PathSteering.CurrentPath.NextNode != null) { return false; }
}
bool closeEnough = Vector2.DistanceSquared(Target.WorldPosition, character.WorldPosition) < CloseEnough * CloseEnough; bool closeEnough = Vector2.DistanceSquared(Target.WorldPosition, character.WorldPosition) < CloseEnough * CloseEnough;
if (closeEnough) if (closeEnough)
{ {
@@ -25,6 +25,7 @@ namespace Barotrauma
public static Sprite ShortcutNode { get; private set; } public static Sprite ShortcutNode { get; private set; }
public static Sprite ExpandNode { get; private set; } public static Sprite ExpandNode { get; private set; }
public static Sprite NodeContainer { get; private set; } public static Sprite NodeContainer { get; private set; }
public static Sprite HotkeyContainer { get; private set; }
public static Sprite CommandBackground { get; private set; } public static Sprite CommandBackground { get; private set; }
public static List<Order> PrefabList { get; private set; } public static List<Order> PrefabList { get; private set; }
public static Order GetPrefab(string identifier) public static Order GetPrefab(string identifier)
@@ -163,6 +164,9 @@ namespace Barotrauma
case "nodecontainer": case "nodecontainer":
NodeContainer = new Sprite(spriteElement, lazyLoad: true); NodeContainer = new Sprite(spriteElement, lazyLoad: true);
break; break;
case "hotkeycontainer":
HotkeyContainer = new Sprite(spriteElement, lazyLoad: true);
break;
case "commandbackground": case "commandbackground":
CommandBackground = new Sprite(spriteElement, lazyLoad: true); CommandBackground = new Sprite(spriteElement, lazyLoad: true);
break; break;
@@ -312,22 +312,15 @@ namespace Barotrauma
selectedCharacter.selectedBy = null; selectedCharacter.selectedBy = null;
selectedCharacter = value; selectedCharacter = value;
if (selectedCharacter != null) if (selectedCharacter != null)
{
selectedCharacter.selectedBy = this; selectedCharacter.selectedBy = this;
#if CLIENT #if CLIENT
if (GameMain.GameSession == null) return; if (Inventory != null)
// Quick & dirty hiding of the chat whenever a character with an accessible inventory is selected to prevent overlaps {
if (GameMain.GameSession.CrewManager.IsSinglePlayer) Inventory.ToggleInventory(true);
{ }
if (GameMain.GameSession.CrewManager.ChatBox == null) return;
GameMain.GameSession.CrewManager.ChatBox.SetVisibility(!(IsHumanoid && value != null && value.Inventory != null && value.CanInventoryBeAccessed));
}
else
{
if (GameMain.Client?.ChatBox == null) return;
GameMain.Client.ChatBox.SetVisibility(!(IsHumanoid && value != null && value.Inventory != null && value.CanInventoryBeAccessed));
}
#endif #endif
}
} }
} }
@@ -2315,14 +2308,21 @@ namespace Barotrauma
aiTarget.SoundRange = MathHelper.Clamp(range, 0, 10000); aiTarget.SoundRange = MathHelper.Clamp(range, 0, 10000);
} }
public bool CanHearCharacter(Character speaker)
{
if (speaker == null || speaker.SpeechImpediment > 100.0f) { return false; }
ChatMessageType messageType = ChatMessage.CanUseRadio(speaker) && ChatMessage.CanUseRadio(this) ?
ChatMessageType.Radio :
ChatMessageType.Default;
return !string.IsNullOrEmpty(ChatMessage.ApplyDistanceEffect("message", messageType, speaker, this));
}
public void SetOrder(Order order, string orderOption, Character orderGiver, bool speak = true) public void SetOrder(Order order, string orderOption, Character orderGiver, bool speak = true)
{ {
if (orderGiver != null) if (orderGiver != null)
{ {
//set the character order only if the character is close enough to hear the message //set the character order only if the character is close enough to hear the message
ChatMessageType messageType = ChatMessage.CanUseRadio(orderGiver) && ChatMessage.CanUseRadio(this) ? if (!CanHearCharacter(orderGiver)) { return; }
ChatMessageType.Radio : ChatMessageType.Default;
if (string.IsNullOrEmpty(ChatMessage.ApplyDistanceEffect("message", messageType, orderGiver, this))) return;
} }
HumanAIController humanAI = AIController as HumanAIController; HumanAIController humanAI = AIController as HumanAIController;
@@ -13,6 +13,7 @@ namespace Barotrauma
class LimbHealth class LimbHealth
{ {
public Sprite IndicatorSprite; public Sprite IndicatorSprite;
public Sprite HighlightSprite;
public Rectangle HighlightArea; public Rectangle HighlightArea;
@@ -44,6 +45,9 @@ namespace Barotrauma
IndicatorSprite = new Sprite(subElement); IndicatorSprite = new Sprite(subElement);
HighlightArea = subElement.GetAttributeRect("highlightarea", new Rectangle(0, 0, (int)IndicatorSprite.size.X, (int)IndicatorSprite.size.Y)); HighlightArea = subElement.GetAttributeRect("highlightarea", new Rectangle(0, 0, (int)IndicatorSprite.size.X, (int)IndicatorSprite.size.Y));
break; break;
case "highlightsprite":
HighlightSprite = new Sprite(subElement);
break;
case "vitalitymultiplier": case "vitalitymultiplier":
if (subElement.Attribute("name") != null) if (subElement.Attribute("name") != null)
{ {
@@ -61,7 +61,8 @@ namespace Barotrauma
} }
public readonly Dictionary<int, XElement> ItemSets = new Dictionary<int, XElement>(); public readonly Dictionary<int, XElement> ItemSets = new Dictionary<int, XElement>();
public readonly Dictionary<int, List<string>> ItemNames = new Dictionary<int, List<string>>(); public readonly Dictionary<int, List<string>> ItemIdentifiers = new Dictionary<int, List<string>>();
public readonly Dictionary<int, Dictionary<string, bool>> ShowItemPreview = new Dictionary<int, Dictionary<string, bool>>();
public readonly List<SkillPrefab> Skills = new List<SkillPrefab>(); public readonly List<SkillPrefab> Skills = new List<SkillPrefab>();
public readonly List<AutonomousObjective> AutomaticOrders = new List<AutonomousObjective>(); public readonly List<AutonomousObjective> AutomaticOrders = new List<AutonomousObjective>();
public readonly List<string> AppropriateOrders = new List<string>(); public readonly List<string> AppropriateOrders = new List<string>();
@@ -184,9 +185,10 @@ namespace Barotrauma
{ {
case "itemset": case "itemset":
ItemSets.Add(variant, subElement); ItemSets.Add(variant, subElement);
var itemNames = new List<string>(); ItemIdentifiers[variant] = new List<string>();
loadItemNames(subElement, itemNames); ShowItemPreview[variant] = new Dictionary<string, bool>();
ItemNames.Add(variant++, itemNames); loadItemIdentifiers(subElement, variant);
variant++;
break; break;
case "skills": case "skills":
foreach (XElement skillElement in subElement.Elements()) foreach (XElement skillElement in subElement.Elements())
@@ -201,20 +203,19 @@ namespace Barotrauma
case "appropriateorders": case "appropriateorders":
subElement.Elements().ForEach(order => AppropriateOrders.Add(order.GetAttributeString("identifier", "").ToLowerInvariant())); subElement.Elements().ForEach(order => AppropriateOrders.Add(order.GetAttributeString("identifier", "").ToLowerInvariant()));
break; break;
case "icon": case "jobicon":
Icon = new Sprite(subElement.FirstElement()); Icon = new Sprite(subElement.FirstElement());
break; break;
} }
} }
void loadItemNames(XElement parentElement, List<string> itemNames) void loadItemIdentifiers(XElement parentElement, int variant)
{ {
foreach (XElement itemElement in parentElement.GetChildElements("Item")) foreach (XElement itemElement in parentElement.GetChildElements("Item"))
{ {
if (itemElement.Element("name") != null) if (itemElement.Element("name") != null)
{ {
DebugConsole.ThrowError("Error in job config \"" + Name + "\" - use identifiers instead of names to configure the items."); DebugConsole.ThrowError("Error in job config \"" + Name + "\" - use identifiers instead of names to configure the items.");
itemNames.Add(itemElement.GetAttributeString("name", ""));
continue; continue;
} }
@@ -222,22 +223,13 @@ namespace Barotrauma
if (string.IsNullOrWhiteSpace(itemIdentifier)) if (string.IsNullOrWhiteSpace(itemIdentifier))
{ {
DebugConsole.ThrowError("Error in job config \"" + Name + "\" - item with no identifier."); DebugConsole.ThrowError("Error in job config \"" + Name + "\" - item with no identifier.");
itemNames.Add("");
} }
else else
{ {
var prefab = MapEntityPrefab.Find(null, itemIdentifier) as ItemPrefab; ItemIdentifiers[variant].Add(itemIdentifier);
if (prefab == null) ShowItemPreview[variant][itemIdentifier] = itemElement.GetAttributeBool("showpreview", true);
{
DebugConsole.ThrowError("Error in job config \"" + Name + "\" - item prefab \"" + itemIdentifier + "\" not found.");
itemNames.Add("");
}
else
{
itemNames.Add(prefab.Name);
}
} }
loadItemNames(itemElement, itemNames); loadItemIdentifiers(itemElement, variant);
} }
} }
@@ -536,7 +536,6 @@ namespace Barotrauma
private readonly List<Body> contactBodies = new List<Body>(); private readonly List<Body> contactBodies = new List<Body>();
private List<Body> ignoredBodies;
/// <summary> /// <summary>
/// Returns true if the attack successfully hit something. If the distance is not given, it will be calculated. /// Returns true if the attack successfully hit something. If the distance is not given, it will be calculated.
/// </summary> /// </summary>
@@ -556,20 +555,11 @@ namespace Barotrauma
case HitDetection.Distance: case HitDetection.Distance:
if (dist < attack.DamageRange) if (dist < attack.DamageRange)
{ {
if (ignoredBodies == null) structureBody = Submarine.PickBody(SimPosition, attackSimPos, collisionCategory: Physics.CollisionWall | Physics.CollisionLevel, allowInsideFixture: true);
{ if (damageTarget is Item i && i.GetComponent<Items.Components.Door>() != null)
ignoredBodies = character.AnimController.Limbs.Select(l => l.body.FarseerBody).ToList();
ignoredBodies.Add(character.AnimController.Collider.FarseerBody);
}
structureBody = Submarine.PickBody(
SimPosition, attackSimPos,
ignoredBodies, Physics.CollisionWall);
if (damageTarget is Item)
{ {
// If the attack is aimed to an item and hits an item, it's successful. // If the attack is aimed to an item and hits an item, it's successful.
// Ignore blocking on items, because it causes cases where a Mudraptor cannot hit the hatch, for example. // Ignore blocking checks on doors, because it causes cases where a Mudraptor cannot hit the hatch, for example.
wasHit = true; wasHit = true;
} }
else if (damageTarget is Structure wall && structureBody != null && else if (damageTarget is Structure wall && structureBody != null &&
@@ -464,7 +464,7 @@ namespace Barotrauma
private bool TryAddTarget(XElement targetElement, out TargetParams target) private bool TryAddTarget(XElement targetElement, out TargetParams target)
{ {
string tag = targetElement.GetAttributeString("tag", null); string tag = targetElement.GetAttributeString("tag", null);
if (!CheckTag(tag)) if (!HasTag(tag))
{ {
target = null; target = null;
DebugConsole.ThrowError($"Multiple targets with the same tag ('{tag}') defined! Only the first will be used!"); DebugConsole.ThrowError($"Multiple targets with the same tag ('{tag}') defined! Only the first will be used!");
@@ -487,15 +487,18 @@ namespace Barotrauma
if (TryAddTarget(element, out targetParams)) if (TryAddTarget(element, out targetParams))
{ {
Element.Add(element); Element.Add(element);
return true;
}
else
{
return false;
} }
return targetParams != null;
} }
private bool CheckTag(string tag) public bool HasTag(string tag)
{ {
if (tag == null) { return false; } if (tag == null) { return false; }
tag = tag.ToLowerInvariant(); return targets.None(t => t.Tag.Equals(tag, StringComparison.OrdinalIgnoreCase));
return targets.None(t => t.Tag == tag);
} }
public bool RemoveTarget(TargetParams target) => RemoveSubParam(target, targets); public bool RemoveTarget(TargetParams target) => RemoveSubParam(target, targets);
@@ -511,7 +511,7 @@ namespace Barotrauma
try try
{ {
int count = args.Length == 0 ? 10 : int.Parse(args[0]); int count = args.Length == 0 ? 10 : int.Parse(args[0]);
Entity.DumpIds(count); Entity.DumpIds(count, args.Length >= 2 ? args[1] : null);
} }
catch (Exception e) catch (Exception e)
{ {
@@ -1550,6 +1550,7 @@ namespace Barotrauma
if (args != null && args.Length > argCount) if (args != null && args.Length > argCount)
{ {
onAnswered(args[argCount]); onAnswered(args[argCount]);
return;
} }
#if CLIENT #if CLIENT
@@ -5,11 +5,11 @@ using System.Xml.Linq;
namespace Barotrauma namespace Barotrauma
{ {
class CargoMission : Mission partial class CargoMission : Mission
{ {
private XElement itemConfig; private readonly XElement itemConfig;
private List<Item> items; private readonly List<Item> items = new List<Item>();
private int requiredDeliveryAmount; private int requiredDeliveryAmount;
@@ -22,7 +22,7 @@ namespace Barotrauma
private void InitItems() private void InitItems()
{ {
items = new List<Item>(); items.Clear();
if (itemConfig == null) if (itemConfig == null)
{ {
@@ -35,7 +35,7 @@ namespace Barotrauma
LoadItemAsChild(subElement, null); LoadItemAsChild(subElement, null);
} }
if (requiredDeliveryAmount == 0) requiredDeliveryAmount = items.Count; if (requiredDeliveryAmount == 0) { requiredDeliveryAmount = items.Count; }
} }
private void LoadItemAsChild(XElement element, Item parent) private void LoadItemAsChild(XElement element, Item parent)
@@ -90,8 +90,6 @@ namespace Barotrauma
var item = new Item(itemPrefab, position, cargoRoom.Submarine); var item = new Item(itemPrefab, position, cargoRoom.Submarine);
item.FindHull(); item.FindHull();
items.Add(item); items.Add(item);
if (parent != null) parent.Combine(item, user: null); if (parent != null) parent.Combine(item, user: null);
@@ -108,7 +106,10 @@ namespace Barotrauma
public override void Start(Level level) public override void Start(Level level)
{ {
InitItems(); if (!IsClient)
{
InitItems();
}
} }
public override void End() public override void End()
@@ -127,8 +128,9 @@ namespace Barotrauma
foreach (Item item in items) foreach (Item item in items)
{ {
if (!item.Removed) item.Remove(); if (!item.Removed) { item.Remove(); }
} }
items.Clear();
} }
} }
} }
@@ -1,11 +1,12 @@
using Microsoft.Xna.Framework; using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
namespace Barotrauma namespace Barotrauma
{ {
partial class Mission abstract partial class Mission
{ {
public readonly MissionPrefab Prefab; public readonly MissionPrefab Prefab;
protected bool completed; protected bool completed;
@@ -192,9 +193,7 @@ namespace Barotrauma
public void GiveReward() public void GiveReward()
{ {
var mode = GameMain.GameSession.GameMode as CampaignMode; if (!(GameMain.GameSession.GameMode is CampaignMode mode)) { return; }
if (mode == null) return;
mode.Money += Reward; mode.Money += Reward;
} }
} }
@@ -3,14 +3,18 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System; using System;
using System.Xml.Linq; using System.Xml.Linq;
using Barotrauma.Extensions;
using Barotrauma.Networking;
namespace Barotrauma namespace Barotrauma
{ {
class MonsterMission : Mission partial class MonsterMission : Mission
{ {
private readonly string monsterFile; private readonly string monsterFile;
private readonly int monsterCount; private readonly int monsterCount;
private readonly HashSet<Tuple<string, int>> monsterFiles = new HashSet<Tuple<string, int>>();
//string = filename, point = min,max
private readonly HashSet<Tuple<string, Point>> monsterFiles = new HashSet<Tuple<string, Point>>();
private readonly List<Character> monsters = new List<Character>(); private readonly List<Character> monsters = new List<Character>();
private readonly List<Vector2> sonarPositions = new List<Vector2>(); private readonly List<Vector2> sonarPositions = new List<Vector2>();
@@ -50,7 +54,7 @@ namespace Barotrauma
maxSonarMarkerDistance = prefab.ConfigElement.GetAttributeFloat("maxsonarmarkerdistance", 10000.0f); maxSonarMarkerDistance = prefab.ConfigElement.GetAttributeFloat("maxsonarmarkerdistance", 10000.0f);
monsterCount = prefab.ConfigElement.GetAttributeInt("monstercount", 1); monsterCount = Math.Min(prefab.ConfigElement.GetAttributeInt("monstercount", 1), 255);
string monsterFileName = monsterFile; string monsterFileName = monsterFile;
foreach (var monsterElement in prefab.ConfigElement.GetChildElements("monster")) foreach (var monsterElement in prefab.ConfigElement.GetChildElements("monster"))
{ {
@@ -64,9 +68,9 @@ namespace Barotrauma
{ {
defaultCount = monsterElement.GetAttributeInt("amount", 1); defaultCount = monsterElement.GetAttributeInt("amount", 1);
} }
int min = monsterElement.GetAttributeInt("min", defaultCount); int min = Math.Min(monsterElement.GetAttributeInt("min", defaultCount), 255);
int max = Math.Max(min, monsterElement.GetAttributeInt("max", defaultCount)); int max = Math.Min(Math.Max(min, monsterElement.GetAttributeInt("max", defaultCount)), 255);
monsterFiles.Add(new Tuple<string, int>(monster, Rand.Range(min, max + 1, Rand.RandSync.Server))); monsterFiles.Add(new Tuple<string, Point>(monster, new Point(min, max)));
} }
description = description.Replace("[monster]", description = description.Replace("[monster]",
TextManager.Get("character." + System.IO.Path.GetFileNameWithoutExtension(monsterFileName))); TextManager.Get("character." + System.IO.Path.GetFileNameWithoutExtension(monsterFileName)));
@@ -74,45 +78,50 @@ namespace Barotrauma
public override void Start(Level level) public override void Start(Level level)
{ {
Level.Loaded.TryGetInterestingPosition(true, Level.PositionType.MainPath, Level.Loaded.Size.X * 0.3f, out Vector2 spawnPos);
bool isClient = IsClient;
if (monsters.Count > 0) if (monsters.Count > 0)
{ {
throw new Exception($"monsters.Count > 0 ({monsters.Count})"); throw new Exception($"monsters.Count > 0 ({monsters.Count})");
} }
if (!string.IsNullOrEmpty(monsterFile))
{
for (int i = 0; i < monsterCount; i++)
{
monsters.Add(Character.Create(monsterFile, spawnPos, ToolBox.RandomSeed(8), null, isClient, true, false));
}
}
foreach (var monster in monsterFiles)
{
for (int i = 0; i < monster.Item2; i++)
{
monsters.Add(Character.Create(monster.Item1, spawnPos, ToolBox.RandomSeed(8), null, isClient, true, false));
}
}
if (tempSonarPositions.Count > 0) if (tempSonarPositions.Count > 0)
{ {
throw new Exception($"tempSonarPositions.Count > 0 ({tempSonarPositions.Count})"); throw new Exception($"tempSonarPositions.Count > 0 ({tempSonarPositions.Count})");
} }
if (!IsClient)
{
Level.Loaded.TryGetInterestingPosition(true, Level.PositionType.MainPath, Level.Loaded.Size.X * 0.3f, out Vector2 spawnPos);
if (!string.IsNullOrEmpty(monsterFile))
{
for (int i = 0; i < monsterCount; i++)
{
monsters.Add(Character.Create(monsterFile, spawnPos, ToolBox.RandomSeed(8), createNetworkEvent: false));
}
}
foreach (var monster in monsterFiles)
{
int amount = Rand.Range(monster.Item2.X, monster.Item2.Y + 1);
for (int i = 0; i < amount; i++)
{
monsters.Add(Character.Create(monster.Item1, spawnPos, ToolBox.RandomSeed(8), createNetworkEvent: false));
}
}
InitializeMonsters(monsters);
}
}
private void InitializeMonsters(IEnumerable<Character> monsters)
{
monsters.ForEach(m => m.Enabled = false); monsters.ForEach(m => m.Enabled = false);
SwarmBehavior.CreateSwarm(monsters.Cast<AICharacter>()); SwarmBehavior.CreateSwarm(monsters.Cast<AICharacter>());
for (int i = 0; i < monsters.Count; i++) foreach (Character monster in monsters)
{ {
tempSonarPositions.Add(spawnPos + Rand.Vector(maxSonarMarkerDistance)); tempSonarPositions.Add(monster.WorldPosition + Rand.Vector(maxSonarMarkerDistance));
} }
if (monsters.Count() != tempSonarPositions.Count)
if (monsters.Count != tempSonarPositions.Count)
{ {
throw new Exception($"monsters.Count != tempSonarPositions.Count ({monsters.Count} != {tempSonarPositions.Count})"); throw new Exception($"monsters.Count != tempSonarPositions.Count ({monsters.Count()} != {tempSonarPositions.Count})");
} }
} }
@@ -183,5 +192,6 @@ namespace Barotrauma
} }
public bool IsEliminated(Character enemy) => enemy.Removed || enemy.IsDead || enemy.AIController is EnemyAIController ai && ai.State == AIState.Flee; public bool IsEliminated(Character enemy) => enemy.Removed || enemy.IsDead || enemy.AIController is EnemyAIController ai && ai.State == AIState.Flee;
} }
} }
@@ -6,13 +6,13 @@ using System.Linq;
namespace Barotrauma namespace Barotrauma
{ {
class SalvageMission : Mission partial class SalvageMission : Mission
{ {
private readonly ItemPrefab itemPrefab; private readonly ItemPrefab itemPrefab;
private Item item; private Item item;
private Level.PositionType spawnPositionType; private readonly Level.PositionType spawnPositionType;
public override IEnumerable<Vector2> SonarPositions public override IEnumerable<Vector2> SonarPositions
{ {
@@ -62,23 +62,26 @@ namespace Barotrauma
public override void Start(Level level) public override void Start(Level level)
{ {
//ruin items are allowed to spawn close to the sub if (!IsClient)
float minDistance = spawnPositionType == Level.PositionType.Ruin ? 0.0f : Level.Loaded.Size.X * 0.3f;
Vector2 position = Level.Loaded.GetRandomItemPos(spawnPositionType, 100.0f, minDistance, 30.0f);
item = new Item(itemPrefab, position, null);
item.body.FarseerBody.BodyType = BodyType.Kinematic;
if (item.HasTag("alien"))
{ {
//try to find an artifact holder and place the artifact inside it //ruin items are allowed to spawn close to the sub
foreach (Item it in Item.ItemList) float minDistance = spawnPositionType == Level.PositionType.Ruin ? 0.0f : Level.Loaded.Size.X * 0.3f;
{ Vector2 position = Level.Loaded.GetRandomItemPos(spawnPositionType, 100.0f, minDistance, 30.0f);
if (it.Submarine != null || !it.HasTag("artifactholder")) continue;
var itemContainer = it.GetComponent<Items.Components.ItemContainer>(); item = new Item(itemPrefab, position, null);
if (itemContainer == null) continue; item.body.FarseerBody.BodyType = BodyType.Kinematic;
if (itemContainer.Combine(item, user: null)) break; // Placement successful
if (item.HasTag("alien"))
{
//try to find an artifact holder and place the artifact inside it
foreach (Item it in Item.ItemList)
{
if (it.Submarine != null || !it.HasTag("artifactholder")) continue;
var itemContainer = it.GetComponent<Items.Components.ItemContainer>();
if (itemContainer == null) { continue; }
if (itemContainer.Combine(item, user: null)) { break; } // Placement successful
}
} }
} }
} }
@@ -108,7 +111,8 @@ namespace Barotrauma
{ {
if (item.CurrentHull?.Submarine == null || !item.CurrentHull.Submarine.AtEndPosition || item.Removed) { return; } if (item.CurrentHull?.Submarine == null || !item.CurrentHull.Submarine.AtEndPosition || item.Removed) { return; }
item.Remove(); item?.Remove();
item = null;
GiveReward(); GiveReward();
completed = true; completed = true;
} }
@@ -49,14 +49,13 @@ namespace Barotrauma
{ {
PurchasedItem purchasedItem = PurchasedItems.Find(pi => pi.ItemPrefab == item); PurchasedItem purchasedItem = PurchasedItems.Find(pi => pi.ItemPrefab == item);
if (purchasedItem != null && quantity == 1) campaign.Money -= item.GetPrice(campaign.Map.CurrentLocation).BuyPrice * quantity;
if (purchasedItem != null)
{ {
campaign.Money -= item.GetPrice(campaign.Map.CurrentLocation).BuyPrice; purchasedItem.Quantity += quantity;
purchasedItem.Quantity += 1;
} }
else else
{ {
campaign.Money -= item.GetPrice(campaign.Map.CurrentLocation).BuyPrice * quantity;
purchasedItem = new PurchasedItem(item, quantity); purchasedItem = new PurchasedItem(item, quantity);
purchasedItems.Add(purchasedItem); purchasedItems.Add(purchasedItem);
} }
@@ -159,6 +159,10 @@ namespace Barotrauma
public void StartRound(Level level, bool reloadSub = true, bool loadSecondSub = false, bool mirrorLevel = false) public void StartRound(Level level, bool reloadSub = true, bool loadSecondSub = false, bool mirrorLevel = false)
{ {
//make sure no status effects have been carried on from the next round
//(they should be stopped in EndRound, this is a safeguard against cases where the round is ended ungracefully)
StatusEffect.StopAll();
#if CLIENT #if CLIENT
GameMain.LightManager.LosEnabled = GameMain.Client == null || GameMain.Client.CharacterInfo != null; GameMain.LightManager.LosEnabled = GameMain.Client == null || GameMain.Client.CharacterInfo != null;
if (GameMain.Client == null) GameMain.LightManager.LosMode = GameMain.Config.LosMode; if (GameMain.Client == null) GameMain.LightManager.LosMode = GameMain.Config.LosMode;
@@ -260,7 +264,18 @@ namespace Barotrauma
if (GameMode.Mission != null) { Mission = GameMode.Mission; } if (GameMode.Mission != null) { Mission = GameMode.Mission; }
if (GameMode != null) { GameMode.Start(); } if (GameMode != null) { GameMode.Start(); }
if (GameMode.Mission != null) { Mission.Start(Level.Loaded); } if (GameMode.Mission != null)
{
int prevEntityCount = Entity.GetEntityList().Count;
Mission.Start(Level.Loaded);
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient && Entity.GetEntityList().Count != prevEntityCount)
{
DebugConsole.ThrowError(
"Entity count has changed after starting a mission as a client. " +
"The clients should not instantiate entities themselves when starting the mission," +
" but instead the server should inform the client of the spawned entities using Mission.ServerWriteInitial.");
}
}
EventManager.StartRound(level); EventManager.StartRound(level);
SteamAchievementManager.OnStartRound(); SteamAchievementManager.OnStartRound();
@@ -16,6 +16,7 @@ namespace Barotrauma
partial class CharacterInventory : Inventory partial class CharacterInventory : Inventory
{ {
private const int hotkeyCount = 5;
private Character character; private Character character;
public InvSlotType[] SlotTypes public InvSlotType[] SlotTypes
@@ -53,15 +54,6 @@ namespace Barotrauma
DebugConsole.ThrowError("Error in the inventory config of \"" + character.SpeciesName + "\" - " + slotTypeNames[i] + " is not a valid inventory slot type."); DebugConsole.ThrowError("Error in the inventory config of \"" + character.SpeciesName + "\" - " + slotTypeNames[i] + " is not a valid inventory slot type.");
} }
SlotTypes[i] = parsedSlotType; SlotTypes[i] = parsedSlotType;
switch (SlotTypes[i])
{
//case InvSlotType.Head:
//case InvSlotType.OuterClothes:
case InvSlotType.LeftHand:
case InvSlotType.RightHand:
hideEmptySlot[i] = true;
break;
}
} }
InitProjSpecific(element); InitProjSpecific(element);
@@ -130,25 +122,25 @@ namespace Barotrauma
/// <summary> /// <summary>
/// If there is no room in the generic inventory (InvSlotType.Any), check if the item can be auto-equipped into its respective limbslot /// If there is no room in the generic inventory (InvSlotType.Any), check if the item can be auto-equipped into its respective limbslot
/// </summary> /// </summary>
public bool TryPutItemWithAutoEquipCheck(Item item, Character user, List<InvSlotType> allowedSlots = null, bool createNetworkEvent = true) public bool TryPutItemWithAutoEquipCheck(Item item, Character user, List<InvSlotType> allowedSlots = null, bool createNetworkEvent = true, bool preferNonHotkeys = false)
{ {
// Does not auto-equip the item if specified and no suitable any slot found (for example handcuffs are not auto-equipped) // Does not auto-equip the item if specified and no suitable any slot found (for example handcuffs are not auto-equipped)
if (item.AllowedSlots.Contains(InvSlotType.Any)) if (item.AllowedSlots.Contains(InvSlotType.Any))
{ {
var wearable = item.GetComponent<Wearable>(); var wearable = item.GetComponent<Wearable>();
if (wearable != null && !wearable.AutoEquipWhenFull && CheckIfAnySlotAvailable(item, false) == -1) if (wearable != null && !wearable.AutoEquipWhenFull && CheckIfAnySlotAvailable(item, false, preferNonHotkeys) == -1)
{ {
return false; return false;
} }
} }
return TryPutItem(item, user, allowedSlots, createNetworkEvent); return TryPutItem(item, user, allowedSlots, createNetworkEvent, preferNonHotkeys);
} }
/// <summary> /// <summary>
/// If there is room, puts the item in the inventory and returns true, otherwise returns false /// If there is room, puts the item in the inventory and returns true, otherwise returns false
/// </summary> /// </summary>
public override bool TryPutItem(Item item, Character user, List<InvSlotType> allowedSlots = null, bool createNetworkEvent = true) public override bool TryPutItem(Item item, Character user, List<InvSlotType> allowedSlots = null, bool createNetworkEvent = true, bool preferNonHotkeys = false)
{ {
if (allowedSlots == null || !allowedSlots.Any()) return false; if (allowedSlots == null || !allowedSlots.Any()) return false;
@@ -172,7 +164,7 @@ namespace Barotrauma
//try to place the item in a LimbSlot.Any slot if that's allowed //try to place the item in a LimbSlot.Any slot if that's allowed
if (allowedSlots.Contains(InvSlotType.Any) && item.AllowedSlots.Contains(InvSlotType.Any)) if (allowedSlots.Contains(InvSlotType.Any) && item.AllowedSlots.Contains(InvSlotType.Any))
{ {
int freeIndex = CheckIfAnySlotAvailable(item, inWrongSlot); int freeIndex = CheckIfAnySlotAvailable(item, inWrongSlot, preferNonHotkeys);
if (freeIndex > -1) if (freeIndex > -1)
{ {
PutItem(item, freeIndex, user, true, createNetworkEvent); PutItem(item, freeIndex, user, true, createNetworkEvent);
@@ -215,60 +207,30 @@ namespace Barotrauma
#if CLIENT #if CLIENT
if (PersonalSlots.HasFlag(SlotTypes[i])) { hidePersonalSlots = false; } if (PersonalSlots.HasFlag(SlotTypes[i])) { hidePersonalSlots = false; }
#endif #endif
bool removeFromOtherSlots = item.ParentInventory != this; bool removeFromOtherSlots = item.ParentInventory != this || (placedInSlot == -1 && inWrongSlot);
if (placedInSlot == -1 && inWrongSlot)
{
if (!hideEmptySlot[i] || SlotTypes[currentSlot] != InvSlotType.Any) removeFromOtherSlots = true;
}
PutItem(item, i, user, removeFromOtherSlots, createNetworkEvent); PutItem(item, i, user, removeFromOtherSlots, createNetworkEvent);
item.Equip(character); item.Equip(character);
placedInSlot = i; placedInSlot = i;
} }
} }
if (placedInSlot > -1)
{
if (item.AllowedSlots.Contains(InvSlotType.Any) && hideEmptySlot[placedInSlot])
{
bool isInAnySlot = false;
for (int i = 0; i < capacity; i++)
{
if (SlotTypes[i] == InvSlotType.Any && Items[i]==item)
{
isInAnySlot = true;
break;
}
}
if (!isInAnySlot)
{
for (int i = 0; i < capacity; i++)
{
if (SlotTypes[i] == InvSlotType.Any && Items[i] == null)
{
Items[i] = item;
break;
}
}
}
}
return true;
}
} }
return placedInSlot > -1; return placedInSlot > -1;
} }
public int CheckIfAnySlotAvailable(Item item, bool inWrongSlot) public int CheckIfAnySlotAvailable(Item item, bool inWrongSlot, bool preferNonHotkeys)
{ {
for (int i = 0; i < capacity; i++) for (int i = 0; i < capacity; i++)
{
if (SlotTypes[i] != InvSlotType.Any) continue;
if (Items[i] == item)
{ {
if (SlotTypes[i] != InvSlotType.Any) continue; return i;
if (Items[i] == item)
{
return i;
}
} }
}
if (!preferNonHotkeys)
{
for (int i = 0; i < capacity; i++) for (int i = 0; i < capacity; i++)
{ {
if (SlotTypes[i] != InvSlotType.Any) continue; if (SlotTypes[i] != InvSlotType.Any) continue;
@@ -283,11 +245,45 @@ namespace Barotrauma
return i; return i;
} }
}
else
{
int hotkeysCounted = 0;
// First go through non-hotkeyed slots
for (int i = 0; i < capacity; i++)
{
if (SlotTypes[i] != InvSlotType.Any) continue;
hotkeysCounted++;
if (hotkeysCounted <= hotkeyCount) continue;
if (inWrongSlot)
{
if (Items[i] != item && Items[i] != null) continue;
}
else
{
if (Items[i] != null) continue;
}
#if CLIENT
if (!inventoryOpen)
{
ToggleInventory();
}
#endif
return i;
}
// Then redo with no preference
return CheckIfAnySlotAvailable(item, inWrongSlot, false);
}
return -1; return -1;
} }
public override bool TryPutItem(Item item, int index, bool allowSwapping, bool allowCombine, Character user, bool createNetworkEvent = true) public override bool TryPutItem(Item item, int index, bool allowSwapping, bool allowCombine, Character user, bool createNetworkEvent = true, bool avoidHotkeys = false)
{ {
if (index < 0 || index >= Items.Length) if (index < 0 || index >= Items.Length)
{ {
@@ -110,6 +110,12 @@ namespace Barotrauma.Items.Components
hitTargets.Clear(); hitTargets.Clear();
IsActive = true; IsActive = true;
if (item.AiTarget != null)
{
item.AiTarget.SoundRange = item.AiTarget.MaxSoundRange;
item.AiTarget.SightRange = item.AiTarget.MaxSightRange;
}
return false; return false;
} }
@@ -82,7 +82,7 @@ namespace Barotrauma.Items.Components
public virtual bool OnPicked(Character picker) public virtual bool OnPicked(Character picker)
{ {
if (picker.Inventory.TryPutItemWithAutoEquipCheck(item, picker, allowedSlots)) if (picker.Inventory.TryPutItemWithAutoEquipCheck(item, picker, allowedSlots, true, true))
{ {
if (!picker.HasSelectedItem(item) && item.body != null) item.body.Enabled = false; if (!picker.HasSelectedItem(item) && item.body != null) item.body.Enabled = false;
this.picker = picker; this.picker = picker;
@@ -36,7 +36,6 @@ namespace Barotrauma.Items.Components
public Propulsion(Item item, XElement element) public Propulsion(Item item, XElement element)
: base(item,element) : base(item,element)
{ {
ResetSoundRange();
} }
public override bool Use(float deltaTime, Character character = null) public override bool Use(float deltaTime, Character character = null)
@@ -106,19 +105,5 @@ namespace Barotrauma.Items.Components
item.AiTarget.SoundRange = item.AiTarget.MaxSoundRange; item.AiTarget.SoundRange = item.AiTarget.MaxSoundRange;
} }
} }
public override void Unequip(Character character)
{
base.Unequip(character);
ResetSoundRange();
}
private void ResetSoundRange()
{
if (item.AiTarget != null)
{
item.AiTarget.SoundRange = item.AiTarget.MinSoundRange;
}
}
} }
} }
@@ -85,11 +85,18 @@ namespace Barotrauma.Items.Components
public override bool Use(float deltaTime, Character character = null) public override bool Use(float deltaTime, Character character = null)
{ {
if (character == null || character.Removed) return false; if (character == null || character.Removed) { return false; }
if ((item.RequireAimToUse && !character.IsKeyDown(InputType.Aim)) || reloadTimer > 0.0f) return false; if ((item.RequireAimToUse && !character.IsKeyDown(InputType.Aim)) || reloadTimer > 0.0f) { return false; }
IsActive = true; IsActive = true;
reloadTimer = reload; reloadTimer = reload;
if (item.AiTarget != null)
{
item.AiTarget.SoundRange = item.AiTarget.MaxSoundRange;
item.AiTarget.SightRange = item.AiTarget.MaxSightRange;
}
List<Body> limbBodies = new List<Body>(); List<Body> limbBodies = new List<Body>();
foreach (Limb l in character.AnimController.Limbs) foreach (Limb l in character.AnimController.Limbs)
{ {
@@ -95,8 +95,10 @@ namespace Barotrauma.Items.Components
Force = MathHelper.Lerp(force, (Voltage < MinVoltage) ? 0.0f : targetForce, 0.1f); Force = MathHelper.Lerp(force, (Voltage < MinVoltage) ? 0.0f : targetForce, 0.1f);
if (Math.Abs(Force) > 1.0f) if (Math.Abs(Force) > 1.0f)
{ {
//arbitrary multiplier that was added to changes in submarine mass without having to readjust all engines
float forceMultiplier = 0.1f;
float voltageFactor = MinVoltage <= 0.0f ? 1.0f : Math.Min(Voltage / MinVoltage, 1.0f); float voltageFactor = MinVoltage <= 0.0f ? 1.0f : Math.Min(Voltage / MinVoltage, 1.0f);
Vector2 currForce = new Vector2((force / 10.0f) * maxForce * voltageFactor, 0.0f); Vector2 currForce = new Vector2(force * maxForce * forceMultiplier * voltageFactor, 0.0f);
//less effective when in a bad condition //less effective when in a bad condition
currForce *= MathHelper.Lerp(0.5f, 2.0f, item.Condition / item.MaxCondition); currForce *= MathHelper.Lerp(0.5f, 2.0f, item.Condition / item.MaxCondition);
@@ -107,12 +109,12 @@ namespace Barotrauma.Items.Components
if (item.AiTarget != null) if (item.AiTarget != null)
{ {
var aiTarget = item.AiTarget; var aiTarget = item.AiTarget;
aiTarget.SoundRange = MathHelper.Lerp(aiTarget.MinSoundRange, aiTarget.MaxSoundRange, currForce.Length() / maxForce); aiTarget.SoundRange = MathHelper.Lerp(aiTarget.MinSoundRange, aiTarget.MaxSoundRange, Math.Min(currForce.Length() * forceMultiplier / maxForce, 1.0f));
} }
if (item.CurrentHull != null) if (item.CurrentHull != null)
{ {
var aiTarget = item.CurrentHull.AiTarget; var aiTarget = item.CurrentHull.AiTarget;
float noise = MathHelper.Lerp(aiTarget.MinSoundRange, aiTarget.MaxSoundRange, currForce.Length() / maxForce); float noise = MathHelper.Lerp(aiTarget.MinSoundRange, aiTarget.MaxSoundRange, Math.Min(currForce.Length() * forceMultiplier / maxForce, 1.0f));
aiTarget.SoundRange = Math.Max(noise, aiTarget.SoundRange); aiTarget.SoundRange = Math.Max(noise, aiTarget.SoundRange);
} }
#if CLIENT #if CLIENT
@@ -79,7 +79,14 @@ namespace Barotrauma.Items.Components
public float Range public float Range
{ {
get { return range; } get { return range; }
set { range = MathHelper.Clamp(value, 0.0f, 100000.0f); } set
{
range = MathHelper.Clamp(value, 0.0f, 100000.0f);
if (item?.AiTarget != null && item.AiTarget.MaxSoundRange <= 0)
{
item.AiTarget.MaxSoundRange = range;
}
}
} }
[Serialize(false, false, description: "Should the sonar display the walls of the submarine it is inside.")] [Serialize(false, false, description: "Should the sonar display the walls of the submarine it is inside.")]
@@ -84,12 +84,9 @@ namespace Barotrauma.Items.Components
{ {
if (sender == null || sender.channel != channel) { return false; } if (sender == null || sender.channel != channel) { return false; }
if (sender.TeamID != Character.TeamType.None && TeamID != Character.TeamType.None) if (sender.TeamID != TeamID)
{ {
if (sender.TeamID != TeamID) return false;
{
return false;
}
} }
if (Vector2.DistanceSquared(item.WorldPosition, sender.item.WorldPosition) > sender.range * sender.range) { return false; } if (Vector2.DistanceSquared(item.WorldPosition, sender.item.WorldPosition) > sender.range * sender.range) { return false; }
@@ -264,7 +264,7 @@ namespace Barotrauma.Items.Components
Character user = item.ParentInventory?.Owner as Character; Character user = item.ParentInventory?.Owner as Character;
removeNodeDelay = (user?.SelectedConstruction == null) ? removeNodeDelay - deltaTime : 0.5f; removeNodeDelay = (user?.SelectedConstruction == null) ? removeNodeDelay - deltaTime : 0.5f;
Submarine sub = null; Submarine sub = item.Submarine;
if (connections[0] != null && connections[0].Item.Submarine != null) { sub = connections[0].Item.Submarine; } if (connections[0] != null && connections[0].Item.Submarine != null) { sub = connections[0].Item.Submarine; }
if (connections[1] != null && connections[1].Item.Submarine != null) { sub = connections[1].Item.Submarine; } if (connections[1] != null && connections[1].Item.Submarine != null) { sub = connections[1].Item.Submarine; }
@@ -14,7 +14,6 @@ namespace Barotrauma
protected readonly int capacity; protected readonly int capacity;
public Item[] Items; public Item[] Items;
protected bool[] hideEmptySlot;
public bool Locked; public bool Locked;
@@ -32,21 +31,16 @@ namespace Barotrauma
this.Owner = owner; this.Owner = owner;
Items = new Item[capacity]; Items = new Item[capacity];
hideEmptySlot = new bool[capacity];
#if CLIENT #if CLIENT
this.slotsPerRow = slotsPerRow; this.slotsPerRow = slotsPerRow;
if (SlotSpriteSmall == null) if (DraggableIndicator == null)
{ {
//TODO: define these in xml DraggableIndicator = GUI.Style.GetComponentStyle("GUIDragIndicator").Sprites[GUIComponent.ComponentState.None][0].Sprite;
SlotSpriteSmall = new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(0, 0, 128, 128), null, 0);
// Adjustment to match the old size of 75,71
SlotSpriteSmall.size = new Vector2(SlotSpriteSmall.SourceRect.Width * 0.5859375f, SlotSpriteSmall.SourceRect.Height * 0.5546875f);
slotHotkeySprite = new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(128, 0, 128, 128), null, 0); EquipIndicator = new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(137, 10, 112, 25), new Vector2(0.5f, 1f), 0);
EquipIndicator = new Sprite("Content/UI/inventoryAtlas.png", new Rectangle(673, 182, 73, 27), new Vector2(0.5f, 0.5f), 0); EquipIndicator.size = new Vector2(EquipIndicator.SourceRect.Width * 0.682f, EquipIndicator.SourceRect.Height * 0.682f);
EquipIndicatorHighlight = new Sprite("Content/UI/inventoryAtlas.png", new Rectangle(679, 108, 67, 21), new Vector2(0.5f, 0.5f), 0);
} }
#endif #endif
} }
@@ -108,7 +102,7 @@ namespace Barotrauma
/// <summary> /// <summary>
/// If there is room, puts the item in the inventory and returns true, otherwise returns false /// If there is room, puts the item in the inventory and returns true, otherwise returns false
/// </summary> /// </summary>
public virtual bool TryPutItem(Item item, Character user, List<InvSlotType> allowedSlots = null, bool createNetworkEvent = true) public virtual bool TryPutItem(Item item, Character user, List<InvSlotType> allowedSlots = null, bool createNetworkEvent = true, bool avoidHotkeys = false)
{ {
int slot = FindAllowedSlot(item); int slot = FindAllowedSlot(item);
if (slot < 0) return false; if (slot < 0) return false;
@@ -117,7 +111,7 @@ namespace Barotrauma
return true; return true;
} }
public virtual bool TryPutItem(Item item, int i, bool allowSwapping, bool allowCombine, Character user, bool createNetworkEvent = true) public virtual bool TryPutItem(Item item, int i, bool allowSwapping, bool allowCombine, Character user, bool createNetworkEvent = true, bool avoidHotkeys = false)
{ {
if (i < 0 || i >= Items.Length) if (i < 0 || i >= Items.Length)
{ {
@@ -47,9 +47,9 @@ namespace Barotrauma
return (item != null && Items[i] == null && container.CanBeContained(item)); return (item != null && Items[i] == null && container.CanBeContained(item));
} }
public override bool TryPutItem(Item item, Character user, List<InvSlotType> allowedSlots = null, bool createNetworkEvent = true) public override bool TryPutItem(Item item, Character user, List<InvSlotType> allowedSlots = null, bool createNetworkEvent = true, bool preferNonHotkeys = false)
{ {
bool wasPut = base.TryPutItem(item, user, allowedSlots, createNetworkEvent); bool wasPut = base.TryPutItem(item, user, allowedSlots, createNetworkEvent, preferNonHotkeys);
if (wasPut) if (wasPut)
{ {
@@ -68,9 +68,9 @@ namespace Barotrauma
return wasPut; return wasPut;
} }
public override bool TryPutItem(Item item, int i, bool allowSwapping, bool allowCombine, Character user, bool createNetworkEvent = true) public override bool TryPutItem(Item item, int i, bool allowSwapping, bool allowCombine, Character user, bool createNetworkEvent = true, bool preferNonHotkeys = false)
{ {
bool wasPut = base.TryPutItem(item, i, allowSwapping, allowCombine, user, createNetworkEvent); bool wasPut = base.TryPutItem(item, i, allowSwapping, allowCombine, user, createNetworkEvent, preferNonHotkeys);
if (wasPut) if (wasPut)
{ {
@@ -1,6 +1,7 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@@ -283,16 +284,23 @@ namespace Barotrauma
Removed = true; Removed = true;
} }
public static void DumpIds(int count) public static void DumpIds(int count, string filename)
{ {
List<Entity> entities = dictionary.Values.OrderByDescending(e => e.id).ToList(); List<Entity> entities = dictionary.Values.OrderByDescending(e => e.id).ToList();
count = Math.Min(entities.Count, count); count = Math.Min(entities.Count, count);
List<string> lines = new List<string>();
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
lines.Add(entities[i].id + ": " + entities[i].ToString());
DebugConsole.ThrowError(entities[i].id + ": " + entities[i].ToString()); DebugConsole.ThrowError(entities[i].id + ": " + entities[i].ToString());
} }
if (!string.IsNullOrWhiteSpace(filename))
{
File.WriteAllLines(filename, lines);
}
} }
} }
} }
@@ -70,7 +70,7 @@ namespace Barotrauma
return false; return false;
} }
public void SaveToCache(string filename, long? time=null) public void SaveToCache(string filename, long? time = null)
{ {
if (!string.IsNullOrWhiteSpace(filename)) if (!string.IsNullOrWhiteSpace(filename))
{ {
@@ -195,5 +195,22 @@ namespace Barotrauma
if (string.IsNullOrEmpty(fullHash)) { return ""; } if (string.IsNullOrEmpty(fullHash)) { return ""; }
return fullHash.Length < 7 ? fullHash : fullHash.Substring(0, 7); return fullHash.Length < 7 ? fullHash : fullHash.Substring(0, 7);
} }
public static bool RemoveFromCache(string filename)
{
if (!string.IsNullOrWhiteSpace(filename))
{
filename = filename.CleanUpPath();
lock (cache)
{
if (cache.ContainsKey(filename))
{
cache.Remove(filename);
return true;
}
}
}
return false;
}
} }
} }
@@ -173,6 +173,7 @@ namespace Barotrauma
XDocument doc = OpenFile(filePath); XDocument doc = OpenFile(filePath);
StartHashDocTask(doc); StartHashDocTask(doc);
hashTask.Wait(); hashTask.Wait();
hashTask = null;
} }
return hash; return hash;
@@ -343,7 +344,10 @@ namespace Barotrauma
public Submarine(string filePath, string hash = "", bool tryLoad = true) : base(null) public Submarine(string filePath, string hash = "", bool tryLoad = true) : base(null)
{ {
this.filePath = filePath; this.filePath = filePath;
LastModifiedTime = File.GetLastWriteTime(filePath); if (!string.IsNullOrEmpty(filePath) && File.Exists(filePath))
{
LastModifiedTime = File.GetLastWriteTime(filePath);
}
try try
{ {
name = displayName = Path.GetFileNameWithoutExtension(filePath); name = displayName = Path.GetFileNameWithoutExtension(filePath);
@@ -1631,6 +1635,10 @@ namespace Barotrauma
return false; return false;
} }
hash = null;
hashTask = null;
Md5Hash.RemoveFromCache(filePath);
return true; return true;
} }
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.IO.Pipes; using System.IO.Pipes;
using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -81,43 +82,67 @@ namespace Barotrauma.Networking
readCancellationToken = null; readCancellationToken = null;
readStream?.Dispose(); readStream = null; readStream?.Dispose(); readStream = null;
writeStream?.Dispose(); writeStream = null; writeStream?.Dispose(); writeStream = null;
msgsToRead?.Clear(); msgsToWrite?.Clear();
} }
private static int ReadIncomingMsgs()
{
Task<int> readTask = readStream?.ReadAsync(tempBytes, 0, tempBytes.Length, readCancellationToken.Token);
TimeSpan ts = TimeSpan.FromMilliseconds(100);
for (int i = 0; i < 150; i++)
{
if (shutDown)
{
readCancellationToken?.Cancel();
shutDown = true;
return -1;
}
if ((readTask?.IsCompleted ?? true) || (readTask?.Wait(ts) ?? true))
{
break;
}
}
if (readTask == null || !readTask.IsCompleted)
{
readCancellationToken?.Cancel();
shutDown = true;
return -1;
}
if (readTask.Status != TaskStatus.RanToCompletion)
{
shutDown = true;
return -1;
}
return readTask.Result;
}
private static void UpdateRead() private static void UpdateRead()
{ {
while (!shutDown) while (!shutDown)
{ {
Task<int> readTask = readStream?.ReadAsync(tempBytes, 0, tempBytes.Length, readCancellationToken.Token); #if SERVER
TimeSpan ts = TimeSpan.FromMilliseconds(100); if (!((readStream as AnonymousPipeClientStream)?.IsConnected ?? false))
for (int i=0;i<150;i++)
{
if (shutDown)
{
readCancellationToken?.Cancel();
shutDown = true;
return;
}
if ((readTask?.IsCompleted ?? true) || (readTask?.Wait(ts) ?? true))
{
break;
}
}
if (readTask == null || !readTask.IsCompleted)
{
readCancellationToken?.Cancel();
shutDown = true;
return;
}
if (readTask.Status != TaskStatus.RanToCompletion)
{ {
shutDown = true; shutDown = true;
return; return;
} }
#else
if (!((readStream as AnonymousPipeServerStream)?.IsConnected ?? false))
{
shutDown = true;
return;
}
#endif
int readLen = readTask.Result; int readLen = ReadIncomingMsgs();
if (readLen < 0) { shutDown = true; return; }
int procIndex = 0; int procIndex = 0;
@@ -125,8 +150,20 @@ namespace Barotrauma.Networking
{ {
if (readState == ReadState.WaitingForPacketStart) if (readState == ReadState.WaitingForPacketStart)
{ {
readIncTotal = tempBytes[procIndex] | (tempBytes[procIndex + 1] << 8); readIncTotal = tempBytes[procIndex];
procIndex += 2; procIndex++;
if (procIndex >= readLen)
{
readLen = ReadIncomingMsgs();
if (readLen < 0) { shutDown = true; return; }
procIndex = 0;
}
readIncTotal |= (tempBytes[procIndex] << 8);
procIndex++;
if (readIncTotal <= 0) { continue; } if (readIncTotal <= 0) { continue; }
@@ -166,6 +203,19 @@ namespace Barotrauma.Networking
{ {
while (!shutDown) while (!shutDown)
{ {
#if SERVER
if (!((writeStream as AnonymousPipeClientStream)?.IsConnected ?? false))
{
shutDown = true;
return;
}
#else
if (!((writeStream as AnonymousPipeServerStream)?.IsConnected ?? false))
{
shutDown = true;
return;
}
#endif
bool msgAvailable; byte[] msg; bool msgAvailable; byte[] msg;
lock (msgsToWrite) lock (msgsToWrite)
{ {
@@ -176,9 +226,11 @@ namespace Barotrauma.Networking
byte[] lengthBytes = new byte[2]; byte[] lengthBytes = new byte[2];
lengthBytes[0] = (byte)(msg.Length & 0xFF); lengthBytes[0] = (byte)(msg.Length & 0xFF);
lengthBytes[1] = (byte)((msg.Length >> 8) & 0xFF); lengthBytes[1] = (byte)((msg.Length >> 8) & 0xFF);
msg = lengthBytes.Concat(msg).ToArray();
try try
{ {
writeStream?.Write(lengthBytes, 0, 2);
writeStream?.Write(msg, 0, msg.Length); writeStream?.Write(msg, 0, msg.Length);
} }
catch (IOException e) catch (IOException e)
@@ -199,11 +251,23 @@ namespace Barotrauma.Networking
writeManualResetEvent.Reset(); writeManualResetEvent.Reset();
if (!writeManualResetEvent.WaitOne(1000)) if (!writeManualResetEvent.WaitOne(1000))
{ {
//heartbeat to keep the other end alive if (shutDown)
byte[] lengthBytes = new byte[2]; {
lengthBytes[0] = (byte)0; return;
lengthBytes[1] = (byte)0; }
writeStream?.Write(lengthBytes, 0, 2); try
{
//heartbeat to keep the other end alive
byte[] lengthBytes = new byte[2];
lengthBytes[0] = (byte)0;
lengthBytes[1] = (byte)0;
writeStream?.Write(lengthBytes, 0, 2);
}
catch (IOException e)
{
shutDown = true;
break;
}
} }
} }
} }
@@ -19,7 +19,7 @@ namespace Barotrauma.Networking
public OrderChatMessage(Order order, string orderOption, Entity targetEntity, Character targetCharacter, Character sender) public OrderChatMessage(Order order, string orderOption, Entity targetEntity, Character targetCharacter, Character sender)
: this(order, orderOption, : this(order, orderOption,
order.GetChatMessage(targetCharacter?.Name, sender?.CurrentHull?.DisplayName, givingOrderToSelf: targetCharacter == sender, orderOption: orderOption), order?.GetChatMessage(targetCharacter?.Name, sender?.CurrentHull?.DisplayName, givingOrderToSelf: targetCharacter == sender, orderOption: orderOption),
targetEntity, targetCharacter, sender) targetEntity, targetCharacter, sender)
{ {
} }
@@ -30,10 +30,11 @@ namespace Barotrauma
Init(columnCount, rowCount); Init(columnCount, rowCount);
} }
public SpriteSheet(string filePath, int columnCount, int rowCount, Vector2 origin) public SpriteSheet(string filePath, int columnCount, int rowCount, Vector2 origin, Rectangle? sourceRect = null)
: base(filePath, origin) : base(filePath, origin)
{ {
this.origin = origin; this.origin = origin;
if (sourceRect.HasValue) { SourceRect = sourceRect.Value; }
Init(columnCount, rowCount); Init(columnCount, rowCount);
} }
Binary file not shown.

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