(bcb06cc5c) Unstable v0.9.9.0

This commit is contained in:
Juan Pablo Arce
2020-03-27 15:22:59 -03:00
parent c81486a993
commit b143329701
326 changed files with 9692 additions and 4364 deletions
+3
View File
@@ -1,6 +1,9 @@
--- ---
name: Bug report name: Bug report
about: Found a bug? Help us squash it by making a bug report! about: Found a bug? Help us squash it by making a bug report!
title: ''
labels: ''
assignees: ''
--- ---
@@ -0,0 +1,33 @@
---
name: Release checklist
about: A checklist that should be gone through when releasing a hotfix/patch/update
title: v0.0.0.0
labels: Code, CM
assignees: ''
---
**Code:**
- [ ] Build and upload dedicated server
- [ ] Verify that Vanilla content package hashes match between Windows/Mac/Linux
- [ ] Run "checkmissingloca" command to make sure localization files are up-to-date.
- [ ] Install Trick or Trauma and check you can start a round with no obvious issues/errors (to make sure we didn't unintentionally break compatibility with older mods).
- [ ] Prepare new main menu content (changelog)
- [ ] Prepare public github repo for pushing the new changes
**CM:**
- [ ] Prepare Steam announcement
- [ ] Prepare Discord announcement (if needed)
- [ ] Prepare blog post (if needed)
**Code (when everything above is ready):**
- [ ] Publish
- [ ] Upload new main menu content
- [ ] Update public github repo
- [ ] Merge release to master and active branches
**CM (after the update is live):**
- [ ] Post Steam announcement
- [ ] Post Discord announcement (if needed)
- [ ] Blog post (if needed)
- [ ] Clean up Trello
@@ -335,7 +335,8 @@ namespace Barotrauma
//an ad-hoc way of allowing the players to have roughly the same maximum view distance regardless of the resolution, //an ad-hoc way of allowing the players to have roughly the same maximum view distance regardless of the resolution,
//while still keeping the zoom around 1.0 when not looking further away (because otherwise we'd always be downsampling //while still keeping the zoom around 1.0 when not looking further away (because otherwise we'd always be downsampling
//on lower resolutions, which doesn't look that good) //on lower resolutions, which doesn't look that good)
float newZoom = MathHelper.Lerp(unscaledZoom, scaledZoom, (float)Math.Sqrt(zoomOutAmount)); float newZoom = MathHelper.Lerp(unscaledZoom, scaledZoom,
(GameMain.Config == null || GameMain.Config.EnableMouseLook) ? (float)Math.Sqrt(zoomOutAmount) : 0.3f);
Zoom += (newZoom - zoom) / ZoomSmoothness; Zoom += (newZoom - zoom) / ZoomSmoothness;
@@ -9,7 +9,7 @@ namespace Barotrauma
public void Draw(SpriteBatch spriteBatch) public void Draw(SpriteBatch spriteBatch)
{ {
if (!ShowAITargets) return; if (!ShowAITargets) { return; }
var pos = new Vector2(WorldPosition.X, -WorldPosition.Y); var pos = new Vector2(WorldPosition.X, -WorldPosition.Y);
if (soundRange > 0.0f) if (soundRange > 0.0f)
{ {
@@ -43,8 +43,8 @@ namespace Barotrauma
} }
else else
{ {
color = Color.WhiteSmoke; //color = Color.WhiteSmoke;
// disable the indicators for structures, because they clutter the debug view // disable the indicators for structures and hulls, because they clutter the debug view
return; return;
} }
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);
@@ -17,7 +17,7 @@ namespace Barotrauma
if (State == AIState.Idle && PreviousState == AIState.Attack) if (State == AIState.Idle && PreviousState == AIState.Attack)
{ {
var target = _selectedAiTarget ?? _lastAiTarget; var target = _selectedAiTarget ?? _lastAiTarget;
if (target != null) if (target != null && target.Entity != null)
{ {
var memory = GetTargetMemory(target); var memory = GetTargetMemory(target);
Vector2 targetPos = memory.Location; Vector2 targetPos = memory.Location;
@@ -16,11 +16,6 @@ namespace Barotrauma
}*/ }*/
} }
partial void SetOrderProjSpecific(Order order, string option)
{
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)
{ {
Vector2 pos = Character.WorldPosition; Vector2 pos = Character.WorldPosition;
@@ -40,7 +35,7 @@ namespace Barotrauma
var currentOrder = ObjectiveManager.CurrentOrder; var currentOrder = ObjectiveManager.CurrentOrder;
if (currentOrder != null) if (currentOrder != null)
{ {
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 20), $"ORDER: {currentOrder.DebugTag} ({currentOrder.GetPriority().FormatZeroDecimal()})", Color.White, Color.Black); GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 20), $"ORDER: {currentOrder.DebugTag} ({currentOrder.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
} }
else if (ObjectiveManager.WaitTimer > 0) else if (ObjectiveManager.WaitTimer > 0)
{ {
@@ -51,17 +46,17 @@ namespace Barotrauma
{ {
if (currentOrder == null) if (currentOrder == null)
{ {
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 20), $"MAIN OBJECTIVE: {currentObjective.DebugTag} ({currentObjective.GetPriority().FormatZeroDecimal()})", Color.White, Color.Black); GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 20), $"MAIN OBJECTIVE: {currentObjective.DebugTag} ({currentObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
} }
var subObjective = currentObjective.SubObjectives.FirstOrDefault(); var subObjective = currentObjective.CurrentSubObjective;
if (subObjective != null) if (subObjective != null)
{ {
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 40), $"SUBOBJECTIVE: {subObjective.DebugTag} ({subObjective.GetPriority().FormatZeroDecimal()})", Color.White, Color.Black); GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 40), $"SUBOBJECTIVE: {subObjective.DebugTag} ({subObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
} }
var activeObjective = ObjectiveManager.GetActiveObjective(); var activeObjective = ObjectiveManager.GetActiveObjective();
if (activeObjective != null) if (activeObjective != null)
{ {
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 60), $"ACTIVE OBJECTIVE: {activeObjective.DebugTag} ({activeObjective.GetPriority().FormatZeroDecimal()})", Color.White, Color.Black); GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 60), $"ACTIVE OBJECTIVE: {activeObjective.DebugTag} ({activeObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
} }
} }
} }
@@ -286,7 +286,7 @@ namespace Barotrauma
{ {
bool inWater = limb.inWater; bool inWater = limb.inWater;
if (character.CurrentHull != null && if (character.CurrentHull != null &&
character.CurrentHull.Surface > character.CurrentHull.Rect.Y - character.CurrentHull.Rect.Height && character.CurrentHull.Surface > character.CurrentHull.Rect.Y - character.CurrentHull.Rect.Height + 5.0f &&
limb.SimPosition.Y < ConvertUnits.ToSimUnits(character.CurrentHull.Rect.Y - character.CurrentHull.Rect.Height) + limb.body.GetMaxExtent()) limb.SimPosition.Y < ConvertUnits.ToSimUnits(character.CurrentHull.Rect.Y - character.CurrentHull.Rect.Height) + limb.body.GetMaxExtent())
{ {
inWater = true; inWater = true;
@@ -370,6 +370,7 @@ namespace Barotrauma
foreach (var deformation in SpriteDeformations) foreach (var deformation in SpriteDeformations)
{ {
if (character.IsDead && deformation.Params.StopWhenHostIsDead) { continue; } if (character.IsDead && deformation.Params.StopWhenHostIsDead) { continue; }
if (!character.AnimController.InWater && deformation.Params.OnlyInWater) { continue; }
if (deformation.Params.UseMovementSine) if (deformation.Params.UseMovementSine)
{ {
if (this is AnimController animator) if (this is AnimController animator)
@@ -365,7 +365,7 @@ namespace Barotrauma
} }
} }
partial void KillProjSpecific(CauseOfDeathType causeOfDeath, Affliction causeOfDeathAffliction) partial void KillProjSpecific(CauseOfDeathType causeOfDeath, Affliction causeOfDeathAffliction, bool log)
{ {
if (GameMain.NetworkMember != null && controlled == this) if (GameMain.NetworkMember != null && controlled == this)
{ {
@@ -466,7 +466,7 @@ namespace Barotrauma
//modify the distance based on the size of the trigger (preferring smaller items) //modify the distance based on the size of the trigger (preferring smaller items)
distanceToItem *= MathHelper.Lerp(0.05f, 2.0f, (transformedTrigger.Width + transformedTrigger.Height) / 250.0f); distanceToItem *= MathHelper.Lerp(0.05f, 2.0f, (transformedTrigger.Width + transformedTrigger.Height) / 250.0f);
} }
else else if (!item.Prefab.RequireCursorInsideTrigger)
{ {
Rectangle itemDisplayRect = new Rectangle(item.InteractionRect.X, item.InteractionRect.Y - item.InteractionRect.Height, item.InteractionRect.Width, item.InteractionRect.Height); Rectangle itemDisplayRect = new Rectangle(item.InteractionRect.X, item.InteractionRect.Y - item.InteractionRect.Height, item.InteractionRect.Width, item.InteractionRect.Height);
@@ -551,7 +551,7 @@ namespace Barotrauma
{ {
if (!enabled) { return; } if (!enabled) { return; }
if (!IsDead && !IsUnconscious) if (!IsDead && !IsIncapacitated)
{ {
if (soundTimer > 0) if (soundTimer > 0)
{ {
@@ -603,6 +603,11 @@ namespace Barotrauma
} }
} }
partial void SetOrderProjSpecific(Order order, string orderOption)
{
GameMain.GameSession?.CrewManager?.DisplayCharacterOrder(this, order, orderOption);
}
public static void AddAllToGUIUpdateList() public static void AddAllToGUIUpdateList()
{ {
for (int i = 0; i < CharacterList.Count; i++) for (int i = 0; i < CharacterList.Count; i++)
@@ -638,8 +643,7 @@ namespace Barotrauma
public void Draw(SpriteBatch spriteBatch, Camera cam) public void Draw(SpriteBatch spriteBatch, Camera cam)
{ {
if (!Enabled) return; if (!Enabled) { return; }
AnimController.Draw(spriteBatch, cam); AnimController.Draw(spriteBatch, cam);
} }
@@ -656,8 +660,6 @@ namespace Barotrauma
if (GameMain.DebugDraw) if (GameMain.DebugDraw)
{ {
AnimController.DebugDraw(spriteBatch); AnimController.DebugDraw(spriteBatch);
if (aiTarget != null) aiTarget.Draw(spriteBatch);
} }
if (GUI.DisableHUD) return; if (GUI.DisableHUD) return;
@@ -61,7 +61,7 @@ namespace Barotrauma
{ {
if (GUI.DisableHUD) return; if (GUI.DisableHUD) return;
if (!character.IsUnconscious && character.Stun <= 0.0f) if (!character.IsIncapacitated && character.Stun <= 0.0f)
{ {
if (character.Inventory != null) if (character.Inventory != null)
{ {
@@ -90,9 +90,9 @@ namespace Barotrauma
{ {
if (GUI.DisableHUD) { return; } if (GUI.DisableHUD) { return; }
if (!character.IsUnconscious && character.Stun <= 0.0f) if (!character.IsIncapacitated && character.Stun <= 0.0f)
{ {
if (character.Info != null && !character.ShouldLockHud()) if (character.Info != null && !character.ShouldLockHud() && character.SelectedCharacter == null)
{ {
bool mouseOnPortrait = HUDLayoutSettings.BottomRightInfoArea.Contains(PlayerInput.MousePosition) && GUI.MouseOn == null; bool mouseOnPortrait = HUDLayoutSettings.BottomRightInfoArea.Contains(PlayerInput.MousePosition) && GUI.MouseOn == null;
if (mouseOnPortrait && PlayerInput.PrimaryMouseButtonClicked()) if (mouseOnPortrait && PlayerInput.PrimaryMouseButtonClicked())
@@ -154,6 +154,7 @@ namespace Barotrauma
brokenItemsCheckTimer = 1.0f; brokenItemsCheckTimer = 1.0f;
foreach (Item item in Item.ItemList) foreach (Item item in Item.ItemList)
{ {
if (item.Submarine == null || item.Submarine.TeamID != character.TeamID || item.Submarine.Info.IsWreck) { continue; }
if (!item.Repairables.Any(r => item.ConditionPercentage <= r.AIRepairThreshold)) { continue; } if (!item.Repairables.Any(r => item.ConditionPercentage <= r.AIRepairThreshold)) { continue; }
if (Submarine.VisibleEntities != null && !Submarine.VisibleEntities.Contains(item)) { continue; } if (Submarine.VisibleEntities != null && !Submarine.VisibleEntities.Contains(item)) { continue; }
@@ -201,7 +202,7 @@ namespace Barotrauma
Color.Lerp(GUI.Style.Red, GUI.Style.Orange * 0.5f, brokenItem.Condition / brokenItem.MaxCondition) * alpha); Color.Lerp(GUI.Style.Red, GUI.Style.Orange * 0.5f, brokenItem.Condition / brokenItem.MaxCondition) * alpha);
} }
if (!character.IsUnconscious && character.Stun <= 0.0f) if (!character.IsIncapacitated && character.Stun <= 0.0f)
{ {
if (character.FocusedCharacter != null && character.FocusedCharacter.CanBeSelected) if (character.FocusedCharacter != null && character.FocusedCharacter.CanBeSelected)
{ {
@@ -309,7 +310,7 @@ namespace Barotrauma
(int)(HUDLayoutSettings.BottomRightInfoArea.Y + HUDLayoutSettings.BottomRightInfoArea.Height * 0.1f), (int)(HUDLayoutSettings.BottomRightInfoArea.Y + HUDLayoutSettings.BottomRightInfoArea.Height * 0.1f),
(int)(HUDLayoutSettings.BottomRightInfoArea.Width / 2), (int)(HUDLayoutSettings.BottomRightInfoArea.Width / 2),
(int)(HUDLayoutSettings.BottomRightInfoArea.Height * 0.7f))); (int)(HUDLayoutSettings.BottomRightInfoArea.Height * 0.7f)));
character.Info.DrawPortrait(spriteBatch, HUDLayoutSettings.PortraitArea.Location.ToVector2(), new Vector2((int)(-4 * GUI.Scale), (int)(2 * GUI.Scale)), targetWidth: HUDLayoutSettings.PortraitArea.Width, true); character.Info.DrawPortrait(spriteBatch, HUDLayoutSettings.PortraitArea.Location.ToVector2(), new Vector2(-12 * GUI.Scale, 4 * GUI.Scale), targetWidth: HUDLayoutSettings.PortraitArea.Width, true);
} }
mouseOnPortrait = HUDLayoutSettings.BottomRightInfoArea.Contains(PlayerInput.MousePosition) && !character.ShouldLockHud(); mouseOnPortrait = HUDLayoutSettings.BottomRightInfoArea.Contains(PlayerInput.MousePosition) && !character.ShouldLockHud();
if (mouseOnPortrait) if (mouseOnPortrait)
@@ -327,7 +328,7 @@ namespace Barotrauma
} }
} }
if (!character.IsUnconscious && character.Stun <= 0.0f) if (!character.IsIncapacitated && character.Stun <= 0.0f)
{ {
if (character.IsHumanoid && character.SelectedCharacter != null && character.SelectedCharacter.Inventory != null) if (character.IsHumanoid && character.SelectedCharacter != null && character.SelectedCharacter.Inventory != null)
{ {
@@ -10,12 +10,12 @@ namespace Barotrauma
{ {
partial class CharacterInfo partial class CharacterInfo
{ {
public const float BgScale = 1.2f;
private static Sprite infoAreaPortraitBG; private static Sprite infoAreaPortraitBG;
public static void Init() public static void Init()
{ {
infoAreaPortraitBG = new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(833, 298, 142, 98), null, 0); infoAreaPortraitBG = GUI.Style.GetComponentStyle("InfoAreaPortraitBG")?.Sprites[GUIComponent.ComponentState.None][0].Sprite;
new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(833, 298, 142, 98), null, 0);
} }
@@ -171,6 +171,7 @@ namespace Barotrauma
public void DrawBackground(SpriteBatch spriteBatch) public void DrawBackground(SpriteBatch spriteBatch)
{ {
if (infoAreaPortraitBG == null) { return; }
infoAreaPortraitBG.Draw(spriteBatch, HUDLayoutSettings.BottomRightInfoArea.Location.ToVector2(), Color.White, Vector2.Zero, 0.0f, infoAreaPortraitBG.Draw(spriteBatch, HUDLayoutSettings.BottomRightInfoArea.Location.ToVector2(), Color.White, Vector2.Zero, 0.0f,
scale: new Vector2( scale: new Vector2(
HUDLayoutSettings.BottomRightInfoArea.Width / (float)infoAreaPortraitBG.SourceRect.Width, HUDLayoutSettings.BottomRightInfoArea.Width / (float)infoAreaPortraitBG.SourceRect.Width,
@@ -1,4 +1,5 @@
using Barotrauma.Networking; using Barotrauma.Extensions;
using Barotrauma.Networking;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Linq; using System.Linq;
@@ -384,6 +385,37 @@ namespace Barotrauma
character = Create(speciesName, position, seed, info, GameMain.Client.ID != ownerId, hasAi); character = Create(speciesName, position, seed, info, GameMain.Client.ID != ownerId, hasAi);
character.ID = id; character.ID = id;
character.TeamID = (TeamType)teamID; character.TeamID = (TeamType)teamID;
// Check if the character has a current order
if (inc.ReadBoolean())
{
int orderPrefabIndex = inc.ReadByte();
Entity targetEntity = FindEntityByID(inc.ReadUInt16());
Character orderGiver = inc.ReadBoolean() ? FindEntityByID(inc.ReadUInt16()) as Character : null;
int orderOptionIndex = inc.ReadByte();
if (orderPrefabIndex >= 0 && orderPrefabIndex < Order.PrefabList.Count)
{
var orderPrefab = Order.PrefabList[orderPrefabIndex];
if ((orderPrefab.ItemComponentType == null && orderPrefab.ItemIdentifiers.None()) ||
(targetEntity != null && (targetEntity as Item).Components.Any(c => c?.GetType() == orderPrefab.ItemComponentType)))
{
character.SetOrder(
new Order(orderPrefab, targetEntity, (targetEntity as Item)?.Components.FirstOrDefault(c => c?.GetType() == orderPrefab.ItemComponentType), orderGiver: orderGiver),
orderOptionIndex >= 0 && orderOptionIndex < orderPrefab.Options.Length ? orderPrefab.Options[orderOptionIndex] : null,
orderGiver, speak: false);
}
else
{
DebugConsole.ThrowError("Could not set order \"" + orderPrefab.Identifier + "\" for character \"" + character.Name + "\" because required target entity was not found.");
}
}
else
{
DebugConsole.ThrowError("Invalid order prefab index - index (" + orderPrefabIndex + ") out of bounds.");
}
}
bool containsStatusData = inc.ReadBoolean(); bool containsStatusData = inc.ReadBoolean();
if (containsStatusData) if (containsStatusData)
{ {
@@ -14,8 +14,8 @@ namespace Barotrauma
public SoundType Type => Params.State; public SoundType Type => Params.State;
public Gender Gender => Params.Gender; public Gender Gender => Params.Gender;
public float Volume => roundSound.Volume; public float Volume => roundSound == null ? 0.0f : roundSound.Volume;
public float Range => roundSound.Range; public float Range => roundSound == null ? 0.0f : roundSound.Range;
public Sound Sound => roundSound?.Sound; public Sound Sound => roundSound?.Sound;
public CharacterSound(CharacterParams.SoundParams soundParams) public CharacterSound(CharacterParams.SoundParams soundParams)
@@ -8,26 +8,25 @@ namespace Barotrauma
{ {
partial class AfflictionHusk : Affliction partial class AfflictionHusk : Affliction
{ {
partial void UpdateMessages(float prevStrength, Character character) partial void UpdateMessages()
{ {
if (Strength < Prefab.MaxStrength * 0.5f) switch (State)
{ {
if (prevStrength % 10.0f > 0.05f && Strength % 10.0f < 0.05f) case InfectionState.Dormant:
{
GUI.AddMessage(TextManager.Get("HuskDormant"), GUI.Style.Red); GUI.AddMessage(TextManager.Get("HuskDormant"), GUI.Style.Red);
} break;
} case InfectionState.Transition:
else if (Strength < Prefab.MaxStrength)
{
if (state == InfectionState.Dormant && Character.Controlled == character)
{
GUI.AddMessage(TextManager.Get("HuskCantSpeak"), GUI.Style.Red); GUI.AddMessage(TextManager.Get("HuskCantSpeak"), GUI.Style.Red);
} break;
} case InfectionState.Active:
else if (state != InfectionState.Active && Character.Controlled == character) if (character.Params.UseHuskAppendage)
{ {
GUI.AddMessage(TextManager.GetWithVariable("HuskActivate", "[Attack]", GameMain.Config.KeyBindText(InputType.Attack)), GUI.AddMessage(TextManager.GetWithVariable("HuskActivate", "[Attack]", GameMain.Config.KeyBindText(InputType.Attack)), GUI.Style.Red);
GUI.Style.Red); }
break;
case InfectionState.Final:
default:
break;
} }
} }
} }
@@ -132,7 +132,7 @@ namespace Barotrauma
private List<HeartratePosition> heartratePositions; private List<HeartratePosition> heartratePositions;
private float currentHeartrateTime; private float currentHeartrateTime;
private float heartbeatTimer; private float heartbeatTimer;
private Texture2D heartrateFade; private static Texture2D heartrateFade;
private readonly HeartratePosition[] heartbeatPattern = private readonly HeartratePosition[] heartbeatPattern =
{ {
@@ -246,11 +246,13 @@ namespace Barotrauma
} }
private GUIFrame healthBarHolder; private GUIFrame healthBarHolder;
private Point healthBarOffset private Point healthBarOffset
{ {
get get
{ {
return new Point(5 - (int)Math.Ceiling(1 - 1 * GUI.Scale), (int)Math.Min(Math.Ceiling(17 * GUI.Scale), 20)); // 0.38775510204f = percentage of offset before reaching the healthbar portion of the graphic going from bottom upwards
return new Point(2, (int)(HUDLayoutSettings.HealthBarArea.Size.Y * 0.38775510204f));
} }
} }
@@ -258,7 +260,7 @@ namespace Barotrauma
{ {
get get
{ {
return new Point(healthBarHolder.Rect.Width - (int)Math.Ceiling(Math.Min(46 * GUI.Scale, 53)), (int)(healthBarHolder.Rect.Height - Math.Min(23 * GUI.Scale, 25)) / 2); return new Point((int)Math.Ceiling(HUDLayoutSettings.HealthBarArea.Size.X - 45 * GUI.Scale), (int)(healthBarHolder.Rect.Height - Math.Min(23 * GUI.Scale, 25)) / 2);
} }
} }
@@ -303,7 +305,7 @@ namespace Barotrauma
healthShadowSize = 1.0f; healthShadowSize = 1.0f;
healthBar = new GUIProgressBar(new RectTransform(healthBarSize, healthBarHolder.RectTransform, Anchor.BottomRight), healthBar = new GUIProgressBar(new RectTransform(healthBarSize, healthBarHolder.RectTransform, Anchor.BottomRight),
barSize: 1.0f, color: GUIColorSettings.HealthBarColorHigh, style: horizontal ? "CharacterHealthBarSlider" : "GUIProgressBarVertical", showFrame: false) barSize: 1.0f, color: GUI.Style.HealthBarColorHigh, style: horizontal ? "CharacterHealthBarSlider" : "GUIProgressBarVertical", showFrame: false)
{ {
HoverCursor = CursorState.Hand, HoverCursor = CursorState.Hand,
Enabled = true, Enabled = true,
@@ -478,7 +480,7 @@ namespace Barotrauma
cprFrame = new GUIFrame(new RectTransform(new Vector2(0.7f, 1.0f), cprLayout.RectTransform), style: "GUIFrameListBox"); cprFrame = new GUIFrame(new RectTransform(new Vector2(0.7f, 1.0f), cprLayout.RectTransform), style: "GUIFrameListBox");
heartrateFade = TextureLoader.FromFile("Content/UI/Health/HeartrateFade.png"); heartrateFade ??= TextureLoader.FromFile("Content/UI/Health/HeartrateFade.png");
new GUICustomComponent(new RectTransform(Vector2.One * 0.95f, cprFrame.RectTransform, Anchor.Center), DrawHeartrate, UpdateHeartrate); new GUICustomComponent(new RectTransform(Vector2.One * 0.95f, cprFrame.RectTransform, Anchor.Center), DrawHeartrate, UpdateHeartrate);
@@ -520,8 +522,10 @@ namespace Barotrauma
UpdateAlignment(); UpdateAlignment();
suicideButton = new GUIButton(new RectTransform(new Vector2(0.06f, 0.02f), GUI.Canvas, Anchor.TopCenter) suicideButton = new GUIButton(new RectTransform(new Vector2(0.1f, 0.02f), GUI.Canvas, Anchor.TopCenter)
{ MinSize = new Point(150, 20), RelativeOffset = new Vector2(0.0f, 0.01f) }, {
MinSize = new Point(150, 20), RelativeOffset = new Vector2(0.0f, 0.01f)
},
TextManager.Get("GiveInButton"), style: "GUIButtonLarge") TextManager.Get("GiveInButton"), style: "GUIButtonLarge")
{ {
ToolTip = TextManager.Get(GameMain.NetworkMember == null ? "GiveInHelpSingleplayer" : "GiveInHelpMultiplayer"), ToolTip = TextManager.Get(GameMain.NetworkMember == null ? "GiveInHelpSingleplayer" : "GiveInHelpMultiplayer"),
@@ -816,9 +820,7 @@ namespace Barotrauma
if (highlightedLimbIndex < 0 && selectedLimbIndex < 0) if (highlightedLimbIndex < 0 && selectedLimbIndex < 0)
{ {
// If no limb is selected or highlighted, select the one with the most critical afflictions. // If no limb is selected or highlighted, select the one with the most critical afflictions.
var affliction = GetAllAfflictions(a => a.Prefab.IndicatorLimb != LimbType.None) var affliction = SortAfflictionsBySeverity(GetAllAfflictions(a => a.Prefab.IndicatorLimb != LimbType.None)).FirstOrDefault();
.OrderByDescending(a => a.DamagePerSecond)
.ThenByDescending(a => a.Strength).FirstOrDefault();
if (affliction.DamagePerSecond > 0 || affliction.Strength > 0) if (affliction.DamagePerSecond > 0 || affliction.Strength > 0)
{ {
var limbHealth = GetMatchingLimbHealth(affliction); var limbHealth = GetMatchingLimbHealth(affliction);
@@ -849,7 +851,7 @@ namespace Barotrauma
} }
else else
{ {
healthBar.Color = healthWindowHealthBar.Color = ToolBox.GradientLerp(DisplayedVitality / MaxVitality, GUIColorSettings.HealthBarColorLow, GUIColorSettings.HealthBarColorMedium, GUIColorSettings.HealthBarColorHigh); healthBar.Color = healthWindowHealthBar.Color = ToolBox.GradientLerp(DisplayedVitality / MaxVitality, GUI.Style.HealthBarColorLow, GUI.Style.HealthBarColorMedium, GUI.Style.HealthBarColorHigh);
healthBar.HoverColor = healthWindowHealthBar.HoverColor = healthBar.Color * 2.0f; healthBar.HoverColor = healthWindowHealthBar.HoverColor = healthBar.Color * 2.0f;
healthBar.BarSize = healthWindowHealthBar.BarSize = healthBar.BarSize = healthWindowHealthBar.BarSize =
(DisplayedVitality > 0.0f) ? (DisplayedVitality > 0.0f) ?
@@ -936,7 +938,7 @@ namespace Barotrauma
healthBar.State = GUIComponent.ComponentState.None; healthBar.State = GUIComponent.ComponentState.None;
} }
suicideButton.Visible = Character == Character.Controlled && Character.IsUnconscious && !Character.IsDead; suicideButton.Visible = Character == Character.Controlled && !Character.IsDead && Character.IsIncapacitated;
cprButton.Visible = cprButton.Visible =
Character == Character.Controlled?.SelectedCharacter Character == Character.Controlled?.SelectedCharacter
@@ -1134,11 +1136,11 @@ namespace Barotrauma
{ {
if (prefab.IsBuff) if (prefab.IsBuff)
{ {
return ToolBox.GradientLerp(affliction.Strength / prefab.MaxStrength, GUIColorSettings.BuffColorLow, GUIColorSettings.BuffColorMedium, GUIColorSettings.BuffColorHigh); return ToolBox.GradientLerp(affliction.Strength / prefab.MaxStrength, GUI.Style.BuffColorLow, GUI.Style.BuffColorMedium, GUI.Style.BuffColorHigh);
} }
else else
{ {
return ToolBox.GradientLerp(affliction.Strength / prefab.MaxStrength, GUIColorSettings.DebuffColorLow, GUIColorSettings.DebuffColorMedium, GUIColorSettings.DebuffColorHigh); return ToolBox.GradientLerp(affliction.Strength / prefab.MaxStrength, GUI.Style.DebuffColorLow, GUI.Style.DebuffColorMedium, GUI.Style.DebuffColorHigh);
} }
} }
else else
@@ -1185,7 +1187,8 @@ 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(); //Affliction mostSevereAffliction = afflictions.FirstOrDefault(a => !a.Prefab.IsBuff && !afflictions.Any(a2 => !a2.Prefab.IsBuff && a2.Strength > a.Strength)) ?? afflictions.FirstOrDefault();
Affliction mostSevereAffliction = SortAfflictionsBySeverity(afflictions).FirstOrDefault();
GUIButton buttonToSelect = null; GUIButton buttonToSelect = null;
foreach (Affliction affliction in afflictions) foreach (Affliction affliction in afflictions)
@@ -1811,8 +1814,8 @@ namespace Barotrauma
float iconScale = 0.25f * scale; float iconScale = 0.25f * scale;
Vector2 iconPos = highlightArea.Center.ToVector2(); Vector2 iconPos = highlightArea.Center.ToVector2();
Affliction mostSevereAffliction = thisAfflictions.FirstOrDefault(a => !a.Prefab.IsBuff && !thisAfflictions.Any(a2 => !a2.Prefab.IsBuff && a2.Strength > a.Strength)) ?? thisAfflictions.FirstOrDefault(); //Affliction mostSevereAffliction = thisAfflictions.FirstOrDefault(a => !a.Prefab.IsBuff && !thisAfflictions.Any(a2 => !a2.Prefab.IsBuff && a2.Strength > a.Strength)) ?? thisAfflictions.FirstOrDefault();
Affliction mostSevereAffliction = SortAfflictionsBySeverity(thisAfflictions).FirstOrDefault();
if (mostSevereAffliction != null) { DrawLimbAfflictionIcon(spriteBatch, mostSevereAffliction, iconScale, ref iconPos); } if (mostSevereAffliction != null) { DrawLimbAfflictionIcon(spriteBatch, mostSevereAffliction, iconScale, ref iconPos); }
if (thisAfflictions.Count() > 1) if (thisAfflictions.Count() > 1)
@@ -1871,10 +1874,11 @@ namespace Barotrauma
healthBarHolder.Visible = value; healthBarHolder.Visible = value;
} }
private readonly List<Pair<AfflictionPrefab, float>> newAfflictions = new List<Pair<AfflictionPrefab, float>>();
private readonly List<Triplet<LimbHealth, AfflictionPrefab, float>> newLimbAfflictions = new List<Triplet<LimbHealth, AfflictionPrefab, float>>();
public void ClientRead(IReadMessage inc) public void ClientRead(IReadMessage inc)
{ {
List<Pair<AfflictionPrefab, float>> newAfflictions = new List<Pair<AfflictionPrefab, float>>(); newAfflictions.Clear();
byte afflictionCount = inc.ReadByte(); byte afflictionCount = inc.ReadByte();
for (int i = 0; i < afflictionCount; i++) for (int i = 0; i < afflictionCount; i++)
{ {
@@ -1914,7 +1918,7 @@ namespace Barotrauma
} }
} }
List<Triplet<LimbHealth, AfflictionPrefab, float>> newLimbAfflictions = new List<Triplet<LimbHealth, AfflictionPrefab, float>>(); newLimbAfflictions.Clear();
byte limbAfflictionCount = inc.ReadByte(); byte limbAfflictionCount = inc.ReadByte();
for (int i = 0; i < limbAfflictionCount; i++) for (int i = 0; i < limbAfflictionCount; i++)
{ {
@@ -1992,6 +1996,9 @@ namespace Barotrauma
} }
} }
medUIExtra?.Remove();
medUIExtra = null;
limbIndicatorOverlay?.Remove(); limbIndicatorOverlay?.Remove();
limbIndicatorOverlay = null; limbIndicatorOverlay = null;
} }
@@ -83,22 +83,22 @@ namespace Barotrauma
// pos.Y = -pos.Y; // pos.Y = -pos.Y;
// ShapeExtensions.DrawPoint(spriteBatch, pos, GUI.Style.Red, size: 5); // ShapeExtensions.DrawPoint(spriteBatch, pos, GUI.Style.Red, size: 5);
//} //}
return;
// A debug visualisation on the bezier curve between limbs. // A debug visualisation on the bezier curve between limbs.
var start = LimbA.WorldPosition; /*var start = LimbA.WorldPosition;
var end = LimbB.WorldPosition; var end = LimbB.WorldPosition;
var jointAPos = ConvertUnits.ToDisplayUnits(LocalAnchorA); var jointAPos = ConvertUnits.ToDisplayUnits(LocalAnchorA);
var control = start + Vector2.Transform(jointAPos, Matrix.CreateRotationZ(LimbA.Rotation)); var control = start + Vector2.Transform(jointAPos, Matrix.CreateRotationZ(LimbA.Rotation));
start.Y = -start.Y; start.Y = -start.Y;
end.Y = -end.Y; end.Y = -end.Y;
control.Y = -control.Y; control.Y = -control.Y;
//GUI.DrawRectangle(spriteBatch, start, Vector2.One * 5, Color.White, true); GUI.DrawRectangle(spriteBatch, start, Vector2.One * 5, Color.White, true);
//GUI.DrawRectangle(spriteBatch, end, Vector2.One * 5, Color.Black, true); GUI.DrawRectangle(spriteBatch, end, Vector2.One * 5, Color.Black, true);
//GUI.DrawRectangle(spriteBatch, control, Vector2.One * 5, Color.Black, true); GUI.DrawRectangle(spriteBatch, control, Vector2.One * 5, Color.Black, true);
//GUI.DrawLine(spriteBatch, start, end, Color.White); GUI.DrawLine(spriteBatch, start, end, Color.White);
//GUI.DrawLine(spriteBatch, start, control, Color.Black); GUI.DrawLine(spriteBatch, start, control, Color.Black);
//GUI.DrawLine(spriteBatch, control, end, Color.Black); GUI.DrawLine(spriteBatch, control, end, Color.Black);
GUI.DrawBezierWithDots(spriteBatch, start, end, control, 1000, GUI.Style.Red); GUI.DrawBezierWithDots(spriteBatch, start, end, control, 1000, GUI.Style.Red);*/
} }
} }
@@ -220,9 +220,18 @@ namespace Barotrauma
} }
set set
{ {
if (HuskSprite != null && value != enableHuskSprite) if (enableHuskSprite == value) { return; }
enableHuskSprite = value;
if (enableHuskSprite)
{ {
if (value) if (HuskSprite == null)
{
LoadHuskSprite();
}
}
if (HuskSprite != null)
{
if (enableHuskSprite)
{ {
List<WearableSprite> otherWearablesWithHusk = new List<WearableSprite>() { HuskSprite }; List<WearableSprite> otherWearablesWithHusk = new List<WearableSprite>() { HuskSprite };
otherWearablesWithHusk.AddRange(OtherWearables); otherWearablesWithHusk.AddRange(OtherWearables);
@@ -235,7 +244,6 @@ namespace Barotrauma
UpdateWearableTypesToHide(); UpdateWearableTypesToHide();
} }
} }
enableHuskSprite = value;
} }
} }
@@ -195,6 +195,34 @@ namespace Barotrauma
} }
} }
private static bool IsCommandPermitted(string command, GameClient client)
{
switch (command)
{
case "kick":
return client.HasPermission(ClientPermissions.Kick);
case "ban":
case "banip":
case "banendpoint":
return client.HasPermission(ClientPermissions.Ban);
case "unban":
case "unbanip":
return client.HasPermission(ClientPermissions.Unban);
case "netstats":
case "help":
case "dumpids":
case "admin":
case "entitylist":
case "togglehud":
case "toggleupperhud":
case "togglecharacternames":
case "fpscounter":
return true;
default:
return client.HasConsoleCommandPermission(command);
}
}
public static void DequeueMessages() public static void DequeueMessages()
{ {
while (queuedMessages.Count > 0) while (queuedMessages.Count > 0)
@@ -367,6 +395,7 @@ namespace Barotrauma
NewMessage("Steam achievements have been disabled during this play session.", Color.Red); NewMessage("Steam achievements have been disabled during this play session.", Color.Red);
#endif #endif
})); }));
AssignRelayToServer("enablecheats", true);
commands.Add(new Command("mainmenu|menu", "mainmenu/menu: Go to the main menu.", (string[] args) => commands.Add(new Command("mainmenu|menu", "mainmenu/menu: Go to the main menu.", (string[] args) =>
{ {
@@ -394,7 +423,8 @@ namespace Barotrauma
{ {
if (args.Length > 0) if (args.Length > 0)
{ {
Submarine.Load(string.Join(" ", args), true); var subInfo = new SubmarineInfo(string.Join(" ", args));
Submarine.MainSub = Submarine.Load(subInfo, true);
} }
GameMain.SubEditorScreen.Select(); GameMain.SubEditorScreen.Select();
}, isCheat: true)); }, isCheat: true));
@@ -440,6 +470,7 @@ namespace Barotrauma
AssignRelayToServer("ban", false); AssignRelayToServer("ban", false);
AssignRelayToServer("banid", false); AssignRelayToServer("banid", false);
AssignRelayToServer("dumpids", false); AssignRelayToServer("dumpids", false);
AssignRelayToServer("dumptofile", false);
AssignRelayToServer("findentityids", false); AssignRelayToServer("findentityids", false);
AssignRelayToServer("campaigninfo", false); AssignRelayToServer("campaigninfo", false);
AssignRelayToServer("help", false); AssignRelayToServer("help", false);
@@ -805,7 +836,7 @@ namespace Barotrauma
return; return;
} }
if (Submarine.SaveCurrent(System.IO.Path.Combine(Submarine.SavePath, fileName + ".sub"))) if (Submarine.MainSub.SaveAs(System.IO.Path.Combine(SubmarineInfo.SavePath, fileName + ".sub")))
{ {
NewMessage("Sub saved", Color.Green); NewMessage("Sub saved", Color.Green);
} }
@@ -814,7 +845,8 @@ namespace Barotrauma
commands.Add(new Command("load|loadsub", "load [submarine name]: Load a submarine.", (string[] args) => commands.Add(new Command("load|loadsub", "load [submarine name]: Load a submarine.", (string[] args) =>
{ {
if (args.Length == 0) return; if (args.Length == 0) return;
Submarine.Load(string.Join(" ", args), true); SubmarineInfo subInfo = new SubmarineInfo(string.Join(" ", args));
Submarine.Load(subInfo, true);
})); }));
commands.Add(new Command("cleansub", "", (string[] args) => commands.Add(new Command("cleansub", "", (string[] args) =>
@@ -1057,23 +1089,69 @@ namespace Barotrauma
if (args.Length != 2 || Screen.Selected != GameMain.SubEditorScreen) { return; } if (args.Length != 2 || Screen.Selected != GameMain.SubEditorScreen) { return; }
foreach (MapEntity me in MapEntity.SelectedList) foreach (MapEntity me in MapEntity.SelectedList)
{ {
if (me is ISerializableEntity serializableEntity) bool propertyFound = false;
if (!(me is ISerializableEntity serializableEntity)) { continue; }
if (serializableEntity.SerializableProperties == null) { continue; }
if (serializableEntity.SerializableProperties.TryGetValue(args[0].ToLowerInvariant(), out SerializableProperty property))
{ {
if (serializableEntity.SerializableProperties == null) propertyFound = true;
object prevValue = property.GetValue(me);
if (property.TrySetValue(me, args[1]))
{ {
continue; NewMessage($"Changed the value \"{args[0]}\" from {(prevValue?.ToString() ?? null)} to {args[1]} on entity \"{me.ToString()}\".", Color.LightGreen);
} }
if (!serializableEntity.SerializableProperties.TryGetValue(args[0].ToLowerInvariant(), out SerializableProperty property)) else
{ {
NewMessage("Property \"" + args[0] + "\" not found in the entity \"" + me.ToString() + "\".", Color.Orange); NewMessage($"Failed to set the value of \"{args[0]}\" to \"{args[1]}\" on the entity \"{me.ToString()}\".", Color.Orange);
continue;
} }
if (!property.TrySetValue(me, args[1])) }
if (me is Item item)
{
foreach (ItemComponent ic in item.Components)
{ {
NewMessage("Failed to set the value of \"" + args[0] + "\" to \"" + args[1] + "\" on the entity \"" + me.ToString() + "\".", Color.Orange); ic.SerializableProperties.TryGetValue(args[0].ToLowerInvariant(), out SerializableProperty componentProperty);
if (componentProperty == null) { continue; }
propertyFound = true;
object prevValue = componentProperty.GetValue(ic);
if (componentProperty.TrySetValue(ic, args[1]))
{
NewMessage($"Changed the value \"{args[0]}\" from {prevValue} to {args[1]} on item \"{me.ToString()}\", component \"{ic.GetType().Name}\".", Color.LightGreen);
}
else
{
NewMessage($"Failed to set the value of \"{args[0]}\" to \"{args[1]}\" on the item \"{me.ToString()}\", component \"{ic.GetType().Name}\".", Color.Orange);
}
}
}
if (!propertyFound)
{
NewMessage($"Property \"{args[0]}\" not found in the entity \"{me.ToString()}\".", Color.Orange);
}
}
},
() =>
{
List<string> propertyList = new List<string>();
foreach (MapEntity me in MapEntity.SelectedList)
{
if (!(me is ISerializableEntity serializableEntity)) { continue; }
if (serializableEntity.SerializableProperties == null) { continue; }
propertyList.AddRange(serializableEntity.SerializableProperties.Select(p => p.Key));
if (me is Item item)
{
foreach (ItemComponent ic in item.Components)
{
propertyList.AddRange(ic.SerializableProperties.Select(p => p.Key));
} }
} }
} }
return new string[][]
{
propertyList.Distinct().ToArray(),
new string[0]
};
})); }));
commands.Add(new Command("checkmissingloca", "", (string[] args) => commands.Add(new Command("checkmissingloca", "", (string[] args) =>
@@ -1106,7 +1184,7 @@ namespace Barotrauma
} }
} }
foreach (Submarine sub in Submarine.SavedSubmarines) foreach (SubmarineInfo sub in SubmarineInfo.SavedSubmarines)
{ {
string nameIdentifier = "submarine.name." + sub.Name.ToLowerInvariant(); string nameIdentifier = "submarine.name." + sub.Name.ToLowerInvariant();
if (!tags[language].Contains(nameIdentifier)) if (!tags[language].Contains(nameIdentifier))
@@ -2113,6 +2191,11 @@ namespace Barotrauma
commands.Add(new Command("flipx", "flipx: mirror the main submarine horizontally", (string[] args) => commands.Add(new Command("flipx", "flipx: mirror the main submarine horizontally", (string[] args) =>
{ {
if (GameMain.NetworkMember != null)
{
ThrowError("Cannot use the flipx command while playing online.");
return;
}
Submarine.MainSub?.FlipX(); Submarine.MainSub?.FlipX();
}, isCheat: true)); }, isCheat: true));
@@ -2235,7 +2318,8 @@ namespace Barotrauma
} }
try try
{ {
Submarine spawnedSub = Submarine.Load(args[0], false); SubmarineInfo subInfo = new SubmarineInfo(args[0]);
Submarine spawnedSub = Submarine.Load(subInfo, false);
spawnedSub.SetPosition(GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition)); spawnedSub.SetPosition(GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition));
} }
catch (Exception e) catch (Exception e)
@@ -2313,7 +2397,7 @@ namespace Barotrauma
switch (firstArg) switch (firstArg)
{ {
case "name": case "name":
var sprites = Sprite.LoadedSprites.Where(s => s.Name?.ToLowerInvariant() == secondArg.ToLowerInvariant()); var sprites = Sprite.LoadedSprites.Where(s => s.Name != null && s.Name.Equals(secondArg, StringComparison.OrdinalIgnoreCase));
if (sprites.Any()) if (sprites.Any())
{ {
foreach (var s in sprites) foreach (var s in sprites)
@@ -2329,7 +2413,7 @@ namespace Barotrauma
} }
case "identifier": case "identifier":
case "id": case "id":
sprites = Sprite.LoadedSprites.Where(s => s.EntityID?.ToLowerInvariant() == secondArg.ToLowerInvariant()); sprites = Sprite.LoadedSprites.Where(s => s.EntityID != null && s.EntityID.Equals(secondArg, StringComparison.OrdinalIgnoreCase));
if (sprites.Any()) if (sprites.Any())
{ {
foreach (var s in sprites) foreach (var s in sprites)
@@ -24,7 +24,7 @@ namespace Barotrauma
{ {
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
{ {
if (subElement.Name.ToString().ToLowerInvariant() != "icon") { continue; } if (!subElement.Name.ToString().Equals("icon", StringComparison.OrdinalIgnoreCase)) { continue; }
Icon = new Sprite(subElement); Icon = new Sprite(subElement);
IconColor = subElement.GetAttributeColor("color", Color.White); IconColor = subElement.GetAttributeColor("color", Color.White);
} }
@@ -7,10 +7,23 @@ namespace Barotrauma
{ {
public override void ClientReadInitial(IReadMessage msg) public override void ClientReadInitial(IReadMessage msg)
{ {
item = Item.ReadSpawnData(msg); bool usedExistingItem = msg.ReadBoolean();
if (item == null) if (usedExistingItem)
{ {
throw new System.Exception("Error in SalvageMission.ClientReadInitial: spawned item was null (mission: " + Prefab.Identifier + ")"); ushort id = msg.ReadUInt16();
item = Entity.FindEntityByID(id) as Item;
if (item == null)
{
throw new System.Exception("Error in SalvageMission.ClientReadInitial: failed to find item " + id + " (mission: " + Prefab.Identifier + ")");
}
}
else
{
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; item.body.FarseerBody.BodyType = BodyType.Kinematic;
@@ -135,31 +135,33 @@ namespace Barotrauma
} }
} }
Color textColor = textBox.Color;
switch (command) switch (command)
{ {
case "r": case "r":
case "radio": case "radio":
textBox.TextColor = ChatMessage.MessageColor[(int)ChatMessageType.Radio]; textColor = ChatMessage.MessageColor[(int)ChatMessageType.Radio];
break; break;
case "d": case "d":
case "dead": case "dead":
textBox.TextColor = ChatMessage.MessageColor[(int)ChatMessageType.Dead]; textColor = ChatMessage.MessageColor[(int)ChatMessageType.Dead];
break; break;
default: default:
if (Character.Controlled != null && (Character.Controlled.IsDead || Character.Controlled.SpeechImpediment >= 100.0f)) if (Character.Controlled != null && (Character.Controlled.IsDead || Character.Controlled.SpeechImpediment >= 100.0f))
{ {
textBox.TextColor = ChatMessage.MessageColor[(int)ChatMessageType.Dead]; textColor = ChatMessage.MessageColor[(int)ChatMessageType.Dead];
} }
else if (command != "") //PMing else if (command != "") //PMing
{ {
textBox.TextColor = ChatMessage.MessageColor[(int)ChatMessageType.Private]; textColor = ChatMessage.MessageColor[(int)ChatMessageType.Private];
} }
else else
{ {
textBox.TextColor = ChatMessage.MessageColor[(int)ChatMessageType.Default]; textColor = ChatMessage.MessageColor[(int)ChatMessageType.Default];
} }
break; break;
} }
textBox.TextColor = textBox.TextBlock.SelectedTextColor = textColor;
return true; return true;
} }
@@ -252,21 +254,29 @@ namespace Barotrauma
Visible = false, Visible = false,
CanBeFocused = false CanBeFocused = false
}; };
var senderText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), popupMsg.RectTransform, Anchor.TopRight), var content = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), popupMsg.RectTransform, Anchor.Center));
senderName, textColor: senderColor, font: GUI.SmallFont, textAlignment: Alignment.TopRight) Vector2 senderTextSize = Vector2.Zero;
if (!string.IsNullOrEmpty(senderName))
{
var senderText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform),
senderName, textColor: senderColor, style: null, font: GUI.SmallFont)
{
CanBeFocused = false
};
senderTextSize = senderText.Font.MeasureString(senderText.WrappedText);
senderText.RectTransform.MinSize = new Point(0, senderText.Rect.Height);
}
var msgPopupText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform),
displayedText, textColor: message.Color, font: GUI.SmallFont, textAlignment: Alignment.BottomLeft, style: null, wrap: true)
{ {
CanBeFocused = false CanBeFocused = false
}; };
var msgPopupText = new GUITextBlock(new RectTransform(new Vector2(0.8f, 0.0f), popupMsg.RectTransform, Anchor.TopRight) msgPopupText.RectTransform.MinSize = new Point(0, msgPopupText.Rect.Height);
{ AbsoluteOffset = new Point(0, senderText.Rect.Height) }, Vector2 msgSize = msgPopupText.Font.MeasureString(msgPopupText.WrappedText);
displayedText, textColor: message.Color, font: GUI.SmallFont, textAlignment: Alignment.TopRight, style: null, wrap: true) int textWidth = (int)Math.Max(msgSize.X + msgPopupText.Padding.X + msgPopupText.Padding.Z, senderTextSize.X) + 10;
{ popupMsg.RectTransform.Resize(new Point((int)(textWidth / content.RectTransform.RelativeSize.X) , (int)((senderTextSize.Y + msgSize.Y) / content.RectTransform.RelativeSize.Y)), resizeChildren: true);
CanBeFocused = false popupMsg.RectTransform.IsFixedSize = true;
}; content.Recalculate();
int textWidth = (int)Math.Max(
msgPopupText.Font.MeasureString(msgPopupText.WrappedText).X,
senderText.Font.MeasureString(senderText.WrappedText).X);
popupMsg.RectTransform.Resize(new Point(textWidth + 20, msgPopupText.Rect.Bottom - senderText.Rect.Y), resizeChildren: false);
popupMessages.Enqueue(popupMsg); popupMessages.Enqueue(popupMsg);
} }
@@ -5,16 +5,6 @@ using System.Xml.Linq;
namespace Barotrauma namespace Barotrauma
{ {
public enum TransitionMode
{
Linear,
Smooth,
Smoother,
EaseIn,
EaseOut,
Exponential
}
public enum SpriteFallBackState public enum SpriteFallBackState
{ {
None, None,
@@ -159,7 +149,7 @@ namespace Barotrauma
Point size = new Point(0, 0); Point size = new Point(0, 0);
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
{ {
if (subElement.Name.ToString().ToLowerInvariant() != "size") { continue; } if (!subElement.Name.ToString().Equals("size", StringComparison.OrdinalIgnoreCase)) { continue; }
Point maxResolution = subElement.GetAttributePoint("maxresolution", new Point(int.MaxValue, int.MaxValue)); Point maxResolution = subElement.GetAttributePoint("maxresolution", new Point(int.MaxValue, int.MaxValue));
if (GameMain.GraphicsWidth <= maxResolution.X && GameMain.GraphicsHeight <= maxResolution.Y) if (GameMain.GraphicsWidth <= maxResolution.X && GameMain.GraphicsHeight <= maxResolution.Y)
{ {
@@ -26,7 +26,8 @@ namespace Barotrauma
Click, Click,
PickItem, PickItem,
PickItemFail, PickItemFail,
DropItem DropItem,
PopupMenu
} }
public enum CursorState public enum CursorState
@@ -242,6 +243,7 @@ namespace Barotrauma
sounds[(int)GUISoundType.RadioMessage] = GameMain.SoundManager.LoadSound("Content/Sounds/UI/RadioMsg.ogg", false); sounds[(int)GUISoundType.RadioMessage] = GameMain.SoundManager.LoadSound("Content/Sounds/UI/RadioMsg.ogg", false);
sounds[(int)GUISoundType.DeadMessage] = GameMain.SoundManager.LoadSound("Content/Sounds/UI/DeadMsg.ogg", false); sounds[(int)GUISoundType.DeadMessage] = GameMain.SoundManager.LoadSound("Content/Sounds/UI/DeadMsg.ogg", false);
sounds[(int)GUISoundType.Click] = GameMain.SoundManager.LoadSound("Content/Sounds/UI/Click.ogg", false); sounds[(int)GUISoundType.Click] = GameMain.SoundManager.LoadSound("Content/Sounds/UI/Click.ogg", false);
sounds[(int)GUISoundType.PopupMenu] = GameMain.SoundManager.LoadSound("Content/Sounds/UI/PopupMenu.ogg", false);
sounds[(int)GUISoundType.PickItem] = GameMain.SoundManager.LoadSound("Content/Sounds/PickItem.ogg", false); sounds[(int)GUISoundType.PickItem] = GameMain.SoundManager.LoadSound("Content/Sounds/PickItem.ogg", false);
sounds[(int)GUISoundType.PickItemFail] = GameMain.SoundManager.LoadSound("Content/Sounds/PickItemFail.ogg", false); sounds[(int)GUISoundType.PickItemFail] = GameMain.SoundManager.LoadSound("Content/Sounds/PickItemFail.ogg", false);
@@ -1922,6 +1924,19 @@ namespace Barotrauma
return true; return true;
}; };
} }
else if (GameMain.GameSession.GameMode is SubTestMode)
{
button = new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), buttonContainer.RectTransform), text: TextManager.Get("PauseMenuReturnToEditor"))
{
OnClicked = (btn, userdata) =>
{
GameMain.GameSession.GameMode.End("");
return true;
}
};
button.OnClicked += TogglePauseMenu;
}
else if (!GameMain.GameSession.GameMode.IsSinglePlayer && GameMain.Client != null && GameMain.Client.HasPermission(ClientPermissions.ManageRound)) else if (!GameMain.GameSession.GameMode.IsSinglePlayer && GameMain.Client != null && GameMain.Client.HasPermission(ClientPermissions.ManageRound))
{ {
new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), buttonContainer.RectTransform), text: TextManager.Get("EndRound")) new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), buttonContainer.RectTransform), text: TextManager.Get("EndRound"))
@@ -524,23 +524,9 @@ namespace Barotrauma
}; };
} }
private float GetEasing(TransitionMode easing, float t)
{
return easing switch
{
TransitionMode.Smooth => MathUtils.SmoothStep(t),
TransitionMode.Smoother => MathUtils.SmootherStep(t),
TransitionMode.EaseIn => MathUtils.EaseIn(t),
TransitionMode.EaseOut => MathUtils.EaseOut(t),
TransitionMode.Exponential => t * t,
TransitionMode.Linear => t,
_ => t,
};
}
protected Color GetBlendedColor(Color targetColor, ref Color blendedColor) protected Color GetBlendedColor(Color targetColor, ref Color blendedColor)
{ {
blendedColor = ColorCrossFadeTime > 0 ? Color.Lerp(blendedColor, targetColor, MathUtils.InverseLerp(ColorCrossFadeTime, 0, GetEasing(ColorTransition, colorFadeTimer))) : targetColor; blendedColor = ColorCrossFadeTime > 0 ? Color.Lerp(blendedColor, targetColor, MathUtils.InverseLerp(ColorCrossFadeTime, 0, ToolBox.GetEasing(ColorTransition, colorFadeTimer))) : targetColor;
return blendedColor; return blendedColor;
} }
@@ -598,7 +584,7 @@ namespace Barotrauma
foreach (UISprite uiSprite in previousSprites) foreach (UISprite uiSprite in previousSprites)
{ {
float alphaMultiplier = SpriteCrossFadeTime > 0 && (uiSprite.CrossFadeOut || currentSprites != null && currentSprites.Any(s => s.CrossFadeIn)) float alphaMultiplier = SpriteCrossFadeTime > 0 && (uiSprite.CrossFadeOut || currentSprites != null && currentSprites.Any(s => s.CrossFadeIn))
? MathUtils.InverseLerp(0, SpriteCrossFadeTime, GetEasing(uiSprite.TransitionMode, spriteFadeTimer)) : 0; ? MathUtils.InverseLerp(0, SpriteCrossFadeTime, ToolBox.GetEasing(uiSprite.TransitionMode, spriteFadeTimer)) : 0;
if (alphaMultiplier > 0) if (alphaMultiplier > 0)
{ {
uiSprite.Draw(spriteBatch, rect, previousColor * alphaMultiplier, SpriteEffects); uiSprite.Draw(spriteBatch, rect, previousColor * alphaMultiplier, SpriteEffects);
@@ -612,7 +598,7 @@ namespace Barotrauma
foreach (UISprite uiSprite in currentSprites) foreach (UISprite uiSprite in currentSprites)
{ {
float alphaMultiplier = SpriteCrossFadeTime > 0 && (uiSprite.CrossFadeIn || previousSprites != null && previousSprites.Any(s => s.CrossFadeOut)) float alphaMultiplier = SpriteCrossFadeTime > 0 && (uiSprite.CrossFadeIn || previousSprites != null && previousSprites.Any(s => s.CrossFadeOut))
? MathUtils.InverseLerp(SpriteCrossFadeTime, 0, GetEasing(uiSprite.TransitionMode, spriteFadeTimer)) : (_currentColor.A / 255.0f); ? MathUtils.InverseLerp(SpriteCrossFadeTime, 0, ToolBox.GetEasing(uiSprite.TransitionMode, spriteFadeTimer)) : (_currentColor.A / 255.0f);
if (alphaMultiplier > 0) if (alphaMultiplier > 0)
{ {
uiSprite.Draw(spriteBatch, rect, _currentColor * alphaMultiplier, SpriteEffects); uiSprite.Draw(spriteBatch, rect, _currentColor * alphaMultiplier, SpriteEffects);
@@ -801,8 +787,7 @@ namespace Barotrauma
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
{ {
if (subElement.Name.ToString().ToLowerInvariant() == "conditional" && if (subElement.Name.ToString().Equals("conditional", StringComparison.OrdinalIgnoreCase) && !CheckConditional(subElement))
!CheckConditional(subElement))
{ {
return null; return null;
} }
@@ -851,7 +836,7 @@ namespace Barotrauma
{ {
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
{ {
if (subElement.Name.ToString().ToLowerInvariant() == "conditional") { continue; } if (subElement.Name.ToString().Equals("conditional", StringComparison.OrdinalIgnoreCase)) { continue; }
FromXML(subElement, component is GUIListBox listBox ? listBox.Content.RectTransform : component.RectTransform); FromXML(subElement, component is GUIListBox listBox ? listBox.Content.RectTransform : component.RectTransform);
} }
@@ -1019,7 +1004,7 @@ namespace Barotrauma
private static GUIFrame LoadGUIFrame(XElement element, RectTransform parent) private static GUIFrame LoadGUIFrame(XElement element, RectTransform parent)
{ {
string style = element.GetAttributeString("style", element.Name.ToString().ToLowerInvariant() == "spacing" ? null : ""); string style = element.GetAttributeString("style", element.Name.ToString().Equals("spacing", StringComparison.OrdinalIgnoreCase) ? null : "");
if (style == "null") { style = null; } if (style == "null") { style = null; }
return new GUIFrame(RectTransform.Load(element, parent), style: style); return new GUIFrame(RectTransform.Load(element, parent), style: style);
} }
@@ -95,6 +95,15 @@ namespace Barotrauma
set { button.TextColor = value; } set { button.TextColor = value; }
} }
public override ScalableFont Font
{
get { return button?.Font ?? base.Font; }
set
{
if (button != null) { button.Font = value; }
}
}
public void ReceiveTextInput(char inputChar) public void ReceiveTextInput(char inputChar)
{ {
GUI.KeyboardDispatcher.Subscriber = null; GUI.KeyboardDispatcher.Subscriber = null;
@@ -168,12 +177,11 @@ namespace Barotrauma
Anchor listAnchor = dropAbove ? Anchor.TopCenter : Anchor.BottomCenter; Anchor listAnchor = dropAbove ? Anchor.TopCenter : Anchor.BottomCenter;
Pivot listPivot = dropAbove ? Pivot.BottomCenter : Pivot.TopCenter; Pivot listPivot = dropAbove ? Pivot.BottomCenter : Pivot.TopCenter;
listBox = new GUIListBox(new RectTransform(new Point(Rect.Width, Rect.Height * MathHelper.Clamp(elementCount, 2, 10)), rectT, listAnchor, listPivot) listBox = new GUIListBox(new RectTransform(new Point(Rect.Width, Rect.Height * MathHelper.Clamp(elementCount, 2, 10)), rectT, listAnchor, listPivot)
{ IsFixedSize = false }, style: null) { IsFixedSize = false }, style: null)
{ {
Enabled = !selectMultiple, Enabled = !selectMultiple
OnSelected = SelectItem
}; };
if (!selectMultiple) { listBox.OnSelected = SelectItem; }
GUI.Style.Apply(listBox, "GUIListBox", this); GUI.Style.Apply(listBox, "GUIListBox", this);
GUI.Style.Apply(listBox.ContentBackground, "GUIListBox", this); GUI.Style.Apply(listBox.ContentBackground, "GUIListBox", this);
@@ -245,7 +253,7 @@ namespace Barotrauma
ToolTip = toolTip ToolTip = toolTip
}; };
new GUITickBox(new RectTransform(new Point((int)(button.Rect.Height * 0.8f)), frame.RectTransform, anchor: Anchor.CenterLeft), text) new GUITickBox(new RectTransform(new Vector2(1.0f, 0.8f), frame.RectTransform, anchor: Anchor.CenterLeft) { MaxSize = new Point(int.MaxValue, (int)(button.Rect.Height * 0.8f)) }, text)
{ {
UserData = userData, UserData = userData,
ToolTip = toolTip, ToolTip = toolTip,
@@ -59,11 +59,36 @@ namespace Barotrauma
/// </summary> /// </summary>
public Color Blue { get; private set; } = Color.Blue; public Color Blue { get; private set; } = Color.Blue;
public Color ColorInventoryEmpty { get; private set; } = Color.Red;
public Color ColorInventoryHalf { get; private set; } = Color.Orange;
public Color ColorInventoryFull { get; private set; } = Color.LightGreen;
public Color ColorInventoryBackground { get; private set; } = Color.Gray;
public Color TextColor { get; private set; } = Color.White * 0.8f; public Color TextColor { get; private set; } = Color.White * 0.8f;
public Color TextColorBright { get; private set; } = Color.White * 0.9f; public Color TextColorBright { get; private set; } = Color.White * 0.9f;
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;
// Inventory
public Color EquipmentSlotIconColor { get; private set; } = new Color(99, 70, 64);
// Health HUD
public Color BuffColorLow { get; private set; } = Color.LightGreen;
public Color BuffColorMedium { get; private set; } = Color.Green;
public Color BuffColorHigh { get; private set; } = Color.DarkGreen;
public Color DebuffColorLow { get; private set; } = Color.DarkSalmon;
public Color DebuffColorMedium { get; private set; } = Color.Red;
public Color DebuffColorHigh { get; private set; } = Color.DarkRed;
public Color HealthBarColorLow { get; private set; } = Color.Red;
public Color HealthBarColorMedium { get; private set; } = Color.Orange;
public Color HealthBarColorHigh { get; private set; } = new Color(78, 114, 88);
public Color EquipmentIndicatorNotEquipped { get; private set; } = Color.Gray;
public Color EquipmentIndicatorEquipped { get; private set; } = new Color(105, 202, 125);
public Color EquipmentIndicatorRunningOut { get; private set; } = new Color(202, 105, 105);
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);
@@ -103,10 +128,22 @@ namespace Barotrauma
case "blue": case "blue":
Blue = subElement.GetAttributeColor("color", Blue); Blue = subElement.GetAttributeColor("color", Blue);
break; break;
case "colorinventoryempty":
ColorInventoryEmpty = subElement.GetAttributeColor("color", ColorInventoryEmpty);
break;
case "colorinventoryhalf":
ColorInventoryHalf = subElement.GetAttributeColor("color", ColorInventoryHalf);
break;
case "colorinventoryfull":
ColorInventoryFull = subElement.GetAttributeColor("color", ColorInventoryFull);
break;
case "colorinventorybackground":
ColorInventoryBackground = subElement.GetAttributeColor("color", ColorInventoryBackground);
break;
case "textcolordark": case "textcolordark":
TextColorDark = subElement.GetAttributeColor("color", TextColorDark); TextColorDark = subElement.GetAttributeColor("color", TextColorDark);
break; break;
case "TextColorBright": case "textcolorbright":
TextColorBright = subElement.GetAttributeColor("color", TextColorBright); TextColorBright = subElement.GetAttributeColor("color", TextColorBright);
break; break;
case "textcolordim": case "textcolordim":
@@ -116,6 +153,45 @@ namespace Barotrauma
case "textcolor": case "textcolor":
TextColor = subElement.GetAttributeColor("color", TextColor); TextColor = subElement.GetAttributeColor("color", TextColor);
break; break;
case "equipmentsloticoncolor":
EquipmentSlotIconColor = subElement.GetAttributeColor("color", EquipmentSlotIconColor);
break;
case "buffcolorlow":
BuffColorLow = subElement.GetAttributeColor("color", BuffColorLow);
break;
case "buffcolormedium":
BuffColorMedium = subElement.GetAttributeColor("color", BuffColorMedium);
break;
case "buffcolorhigh":
BuffColorHigh = subElement.GetAttributeColor("color", BuffColorHigh);
break;
case "debuffcolorlow":
DebuffColorLow = subElement.GetAttributeColor("color", DebuffColorLow);
break;
case "debuffcolormedium":
DebuffColorMedium = subElement.GetAttributeColor("color", DebuffColorMedium);
break;
case "debuffcolorhigh":
DebuffColorHigh = subElement.GetAttributeColor("color", DebuffColorHigh);
break;
case "healthbarcolorlow":
HealthBarColorLow = subElement.GetAttributeColor("color", HealthBarColorLow);
break;
case "healthbarcolormedium":
HealthBarColorMedium = subElement.GetAttributeColor("color", HealthBarColorMedium);
break;
case "healthbarcolorhigh":
HealthBarColorHigh = subElement.GetAttributeColor("color", HealthBarColorHigh);
break;
case "equipmentindicatornotequipped":
EquipmentIndicatorNotEquipped = subElement.GetAttributeColor("color", EquipmentIndicatorNotEquipped);
break;
case "equipmentindicatorequipped":
EquipmentIndicatorEquipped = subElement.GetAttributeColor("color", EquipmentIndicatorEquipped);
break;
case "equipmentindicatorrunningout":
EquipmentIndicatorRunningOut = subElement.GetAttributeColor("color", EquipmentIndicatorRunningOut);
break;
case "uiglow": case "uiglow":
UIGlow = new UISprite(subElement); UIGlow = new UISprite(subElement);
break; break;
@@ -250,9 +326,8 @@ namespace Barotrauma
//check if any of the language override fonts want to override the font size as well //check if any of the language override fonts want to override the font size as well
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
{ {
if (subElement.Name.ToString().ToLowerInvariant() != "override") { continue; } if (!subElement.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase)) { continue; }
string language = subElement.GetAttributeString("language", "").ToLowerInvariant(); if (GameMain.Config.Language.Equals(subElement.GetAttributeString("language", ""), StringComparison.OrdinalIgnoreCase))
if (GameMain.Config.Language.ToLowerInvariant() == language)
{ {
uint overrideFontSize = GetFontSize(subElement, 0); uint overrideFontSize = GetFontSize(subElement, 0);
if (overrideFontSize > 0) { return overrideFontSize; } if (overrideFontSize > 0) { return overrideFontSize; }
@@ -261,7 +336,7 @@ namespace Barotrauma
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
{ {
if (subElement.Name.ToString().ToLowerInvariant() != "size") { continue; } if (!subElement.Name.ToString().Equals("size", StringComparison.OrdinalIgnoreCase)) { continue; }
Point maxResolution = subElement.GetAttributePoint("maxresolution", new Point(int.MaxValue, int.MaxValue)); Point maxResolution = subElement.GetAttributePoint("maxresolution", new Point(int.MaxValue, int.MaxValue));
if (GameMain.GraphicsWidth <= maxResolution.X && GameMain.GraphicsHeight <= maxResolution.Y) if (GameMain.GraphicsWidth <= maxResolution.X && GameMain.GraphicsHeight <= maxResolution.Y)
{ {
@@ -275,9 +350,8 @@ namespace Barotrauma
{ {
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
{ {
if (subElement.Name.ToString().ToLowerInvariant() != "override") { continue; } if (!subElement.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase)) { continue; }
string language = subElement.GetAttributeString("language", "").ToLowerInvariant(); if (GameMain.Config.Language.Equals(subElement.GetAttributeString("language", ""), StringComparison.OrdinalIgnoreCase))
if (GameMain.Config.Language.ToLowerInvariant() == language)
{ {
return subElement.GetAttributeString("file", ""); return subElement.GetAttributeString("file", "");
} }
@@ -289,9 +363,8 @@ namespace Barotrauma
{ {
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
{ {
if (subElement.Name.ToString().ToLowerInvariant() != "override") { continue; } if (!subElement.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase)) { continue; }
string language = subElement.GetAttributeString("language", "").ToLowerInvariant(); if (GameMain.Config.Language.Equals(subElement.GetAttributeString("language", ""), StringComparison.OrdinalIgnoreCase))
if (GameMain.Config.Language.ToLowerInvariant() == language)
{ {
return subElement.GetAttributeBool("dynamicloading", false); return subElement.GetAttributeBool("dynamicloading", false);
} }
@@ -303,9 +376,8 @@ namespace Barotrauma
{ {
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
{ {
if (subElement.Name.ToString().ToLowerInvariant() != "override") { continue; } if (!subElement.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase)) { continue; }
string language = subElement.GetAttributeString("language", "").ToLowerInvariant(); if (GameMain.Config.Language.Equals(subElement.GetAttributeString("language", ""), StringComparison.OrdinalIgnoreCase))
if (GameMain.Config.Language.ToLowerInvariant() == language)
{ {
return subElement.GetAttributeBool("iscjk", false); return subElement.GetAttributeBool("iscjk", false);
} }
@@ -328,11 +328,7 @@ namespace Barotrauma
{ {
if (text == null) { return; } if (text == null) { return; }
censoredText = ""; censoredText = string.IsNullOrEmpty(text) ? "" : new string('\u2022', text.Length);
for (int i = 0; i < text.Length; i++)
{
censoredText += "\u2022";
}
var rect = Rect; var rect = Rect;
@@ -446,9 +442,10 @@ namespace Barotrauma
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle; Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
if (overflowClipActive) if (overflowClipActive)
{ {
spriteBatch.End();
Rectangle scissorRect = new Rectangle(rect.X + (int)padding.X, rect.Y, rect.Width - (int)padding.X - (int)padding.Z, rect.Height); Rectangle scissorRect = new Rectangle(rect.X + (int)padding.X, rect.Y, rect.Width - (int)padding.X - (int)padding.Z, rect.Height);
spriteBatch.GraphicsDevice.ScissorRectangle = scissorRect; if (!scissorRect.Intersects(prevScissorRect)) { return; }
spriteBatch.End();
spriteBatch.GraphicsDevice.ScissorRectangle = Rectangle.Intersect(prevScissorRect, scissorRect);
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable); spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
} }
@@ -69,6 +69,11 @@ namespace Barotrauma
private readonly Memento<string> memento = new Memento<string>(); private readonly Memento<string> memento = new Memento<string>();
public GUIFrame Frame
{
get { return frame; }
}
public GUITextBlock.TextGetterHandler TextGetter public GUITextBlock.TextGetterHandler TextGetter
{ {
get { return textBlock.TextGetter; } get { return textBlock.TextGetter; }
@@ -112,16 +112,16 @@ namespace Barotrauma
//slice from the top of the screen for misc buttons (info, end round, server controls) //slice from the top of the screen for misc buttons (info, end round, server controls)
ButtonAreaTop = new Rectangle(Padding, Padding, GameMain.GraphicsWidth - Padding * 2, (int)(50 * GUI.Scale)); ButtonAreaTop = new Rectangle(Padding, Padding, GameMain.GraphicsWidth - Padding * 2, (int)(50 * GUI.Scale));
int infoAreaWidth = (int)(142 * GUI.Scale * CharacterInfo.BgScale); int infoAreaWidth = (int)(142 * GUI.Scale);
int infoAreaHeight = (int)(98 * GUI.Scale * CharacterInfo.BgScale); int infoAreaHeight = (int)(98 * GUI.Scale);
int portraitSize = (int)(125 * GUI.Scale); int portraitSize = (int)(infoAreaHeight * 0.95f);
BottomRightInfoArea = new Rectangle(GameMain.GraphicsWidth - Padding * 2 - infoAreaWidth, GameMain.GraphicsHeight - Padding * 2 - infoAreaHeight, infoAreaWidth, infoAreaHeight); BottomRightInfoArea = new Rectangle(GameMain.GraphicsWidth - Padding * 2 - infoAreaWidth, GameMain.GraphicsHeight - Padding * 2 - infoAreaHeight, infoAreaWidth, infoAreaHeight);
PortraitArea = new Rectangle(GameMain.GraphicsWidth - Padding - portraitSize, GameMain.GraphicsHeight - Padding - portraitSize, portraitSize, portraitSize); PortraitArea = new Rectangle(GameMain.GraphicsWidth - portraitSize, BottomRightInfoArea.Bottom - portraitSize + Padding / 2, portraitSize, portraitSize);
//horizontal slices at the corners of the screen for health bar and affliction icons //horizontal slices at the corners of the screen for health bar and affliction icons
int afflictionAreaHeight = (int)(50 * GUI.Scale); int afflictionAreaHeight = (int)(50 * GUI.Scale);
int healthBarWidth = (int)((BottomRightInfoArea.Width + CharacterInventory.SlotSize.X + CharacterInventory.Spacing) * 1.1f); int healthBarWidth = BottomRightInfoArea.Width + CharacterInventory.SlotSize.X + CharacterInventory.Spacing * 2 + CharacterInventory.HideButtonWidth;
int healthBarHeight = (int)Math.Max(50f * GUI.Scale, 25f); int healthBarHeight = (int)(50f * GUI.Scale);
HealthBarArea = new Rectangle(BottomRightInfoArea.X - (healthBarWidth - BottomRightInfoArea.Width) + (int)(2 * GUI.Scale), BottomRightInfoArea.Y - healthBarHeight + (int)(10 * GUI.Scale), healthBarWidth, healthBarHeight); HealthBarArea = new Rectangle(BottomRightInfoArea.X - (healthBarWidth - BottomRightInfoArea.Width) + (int)(2 * GUI.Scale), BottomRightInfoArea.Y - healthBarHeight + (int)(10 * GUI.Scale), healthBarWidth, healthBarHeight);
AfflictionAreaLeft = new Rectangle(HealthBarArea.X, HealthBarArea.Y - Padding - afflictionAreaHeight, HealthBarArea.Width, afflictionAreaHeight); AfflictionAreaLeft = new Rectangle(HealthBarArea.X, HealthBarArea.Y - Padding - afflictionAreaHeight, HealthBarArea.Width, afflictionAreaHeight);
@@ -66,7 +66,7 @@ namespace Barotrauma
if (vanillaContent == null) if (vanillaContent == null)
{ {
// TODO: Dynamic method for defining and finding the vanilla content package. // TODO: Dynamic method for defining and finding the vanilla content package.
vanillaContent = ContentPackage.List.SingleOrDefault(cp => Path.GetFileName(cp.Path).ToLowerInvariant() == "vanilla 0.9.xml"); vanillaContent = ContentPackage.List.SingleOrDefault(cp => Path.GetFileName(cp.Path).Equals("vanilla 0.9.xml", StringComparison.OrdinalIgnoreCase));
} }
return vanillaContent; return vanillaContent;
} }
@@ -189,6 +189,11 @@ namespace Barotrauma
Instance = this; Instance = this;
if (!Directory.Exists(Content.RootDirectory))
{
throw new Exception("Content folder not found. If you are trying to compile the game from the source code and own a legal copy of the game, you can copy the Content folder from the game's files to BarotraumaShared/Content.");
}
Config = new GameSettings(); Config = new GameSettings();
Md5Hash.LoadCache(); Md5Hash.LoadCache();
@@ -380,6 +385,13 @@ namespace Barotrauma
} }
} }
public class LoadingException : Exception
{
public LoadingException(Exception e) : base("Loading was interrupted due to an error.", innerException: e)
{
}
}
private IEnumerable<object> Load(bool isSeparateThread) private IEnumerable<object> Load(bool isSeparateThread)
{ {
if (GameSettings.VerboseLogging) if (GameSettings.VerboseLogging)
@@ -397,7 +409,7 @@ namespace Barotrauma
SoundManager.SetCategoryGainMultiplier("ui", Config.SoundVolume, 0); SoundManager.SetCategoryGainMultiplier("ui", Config.SoundVolume, 0);
SoundManager.SetCategoryGainMultiplier("waterambience", Config.SoundVolume, 0); SoundManager.SetCategoryGainMultiplier("waterambience", Config.SoundVolume, 0);
SoundManager.SetCategoryGainMultiplier("music", Config.MusicVolume, 0); SoundManager.SetCategoryGainMultiplier("music", Config.MusicVolume, 0);
SoundManager.SetCategoryGainMultiplier("voip", Config.VoiceChatVolume * 20.0f, 0); SoundManager.SetCategoryGainMultiplier("voip", Config.VoiceChatVolume, 0);
if (Config.EnableSplashScreen && !ConsoleArguments.Contains("-skipintro")) if (Config.EnableSplashScreen && !ConsoleArguments.Contains("-skipintro"))
{ {
@@ -421,17 +433,27 @@ namespace Barotrauma
GUI.Init(Window, Config.SelectedContentPackages, GraphicsDevice); GUI.Init(Window, Config.SelectedContentPackages, GraphicsDevice);
DebugConsole.Init(); DebugConsole.Init();
CrossThread.RequestExecutionOnMainThread(() => if (Config.AutoUpdateWorkshopItems)
{ {
if (Config.AutoUpdateWorkshopItems) bool waitingForWorkshopUpdates = true;
bool result = false;
TaskPool.Add(SteamManager.AutoUpdateWorkshopItemsAsync(), (task) =>
{ {
if (SteamManager.AutoUpdateWorkshopItems()) result = task.Result;
waitingForWorkshopUpdates = false;
});
while (waitingForWorkshopUpdates) { yield return CoroutineStatus.Running; }
if (result)
{
CrossThread.RequestExecutionOnMainThread(() =>
{ {
ContentPackage.LoadAll(); ContentPackage.LoadAll();
Config.ReloadContentPackages(); Config.ReloadContentPackages();
} });
} }
}); }
if (SelectedPackages.None()) if (SelectedPackages.None())
@@ -488,6 +510,7 @@ namespace Barotrauma
Tutorials.Tutorial.Init(); Tutorials.Tutorial.Init();
MapGenerationParams.Init(); MapGenerationParams.Init();
LevelGenerationParams.LoadPresets(); LevelGenerationParams.LoadPresets();
WreckAIConfig.LoadAll();
ScriptedEventSet.LoadPrefabs(); ScriptedEventSet.LoadPrefabs();
AfflictionPrefab.LoadAll(GetFilesOfType(ContentType.Afflictions)); AfflictionPrefab.LoadAll(GetFilesOfType(ContentType.Afflictions));
SkillSettings.Load(GetFilesOfType(ContentType.SkillSettings)); SkillSettings.Load(GetFilesOfType(ContentType.SkillSettings));
@@ -503,6 +526,7 @@ namespace Barotrauma
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
JobPrefab.LoadAll(GetFilesOfType(ContentType.Jobs)); JobPrefab.LoadAll(GetFilesOfType(ContentType.Jobs));
CorpsePrefab.LoadAll(GetFilesOfType(ContentType.Corpses));
NPCConversation.LoadAll(GetFilesOfType(ContentType.NPCConversations)); NPCConversation.LoadAll(GetFilesOfType(ContentType.NPCConversations));
@@ -512,7 +536,7 @@ namespace Barotrauma
GameModePreset.Init(); GameModePreset.Init();
Submarine.RefreshSavedSubs(); SubmarineInfo.RefreshSavedSubs();
TitleScreen.LoadState = 65.0f; TitleScreen.LoadState = 65.0f;
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
@@ -649,6 +673,8 @@ namespace Barotrauma
{ {
#if DEBUG #if DEBUG
DebugConsole.ThrowError($"Failed to parse a Steam friend's connect invitation command ({connectCommand})", e); DebugConsole.ThrowError($"Failed to parse a Steam friend's connect invitation command ({connectCommand})", e);
#else
DebugConsole.Log($"Failed to parse a Steam friend's connect invitation command ({connectCommand})\n" + e.StackTrace);
#endif #endif
ConnectName = null; ConnectName = null;
ConnectEndpoint = null; ConnectEndpoint = null;
@@ -741,12 +767,7 @@ namespace Barotrauma
if (!hasLoaded && !CoroutineManager.IsCoroutineRunning(loadingCoroutine)) if (!hasLoaded && !CoroutineManager.IsCoroutineRunning(loadingCoroutine))
{ {
string errMsg = "Loading was interrupted due to an error"; throw new LoadingException(loadingCoroutine.Exception);
if (loadingCoroutine.Exception != null)
{
errMsg += ": " + loadingCoroutine.Exception.Message + "\n" + loadingCoroutine.Exception.StackTrace;
}
throw new Exception(errMsg);
} }
} }
else if (hasLoaded) else if (hasLoaded)
@@ -1086,7 +1107,10 @@ namespace Barotrauma
UserData = "https://steamcommunity.com/app/602960/discussions/1/", UserData = "https://steamcommunity.com/app/602960/discussions/1/",
OnClicked = (btn, userdata) => OnClicked = (btn, userdata) =>
{ {
SteamManager.OverlayCustomURL(userdata as string); if (!SteamManager.OverlayCustomURL(userdata as string))
{
ShowOpenUrlInWebBrowserPrompt(userdata as string);
}
msgBox.Close(); msgBox.Close();
return true; return true;
} }
@@ -34,6 +34,7 @@ namespace Barotrauma
private GUIButton commandButton, toggleCrewButton; private GUIButton commandButton, toggleCrewButton;
private float crewListOpenState; private float crewListOpenState;
private bool toggleCrewListOpen = true; private bool toggleCrewListOpen = true;
private Point crewListEntrySize;
/// <summary> /// <summary>
/// Present only in single player games. In multiplayer. The chatbox is found from GameSession.Client. /// Present only in single player games. In multiplayer. The chatbox is found from GameSession.Client.
@@ -51,7 +52,6 @@ namespace Barotrauma
{ {
if (toggleCrewListOpen == value) { return; } if (toggleCrewListOpen == value) { return; }
toggleCrewListOpen = GameMain.Config.CrewMenuOpen = value; toggleCrewListOpen = GameMain.Config.CrewMenuOpen = value;
toggleCrewButton.Children.ForEach(c => c.SpriteEffects = toggleCrewListOpen ? SpriteEffects.None : SpriteEffects.FlipHorizontally);
} }
} }
@@ -70,13 +70,13 @@ namespace Barotrauma
{ {
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
{ {
if (subElement.Name.ToString().ToLowerInvariant() != "character") continue; if (!subElement.Name.ToString().Equals("character", StringComparison.OrdinalIgnoreCase)) { continue; }
var characterInfo = new CharacterInfo(subElement); var characterInfo = new CharacterInfo(subElement);
characterInfos.Add(characterInfo); characterInfos.Add(characterInfo);
foreach (XElement invElement in subElement.Elements()) foreach (XElement invElement in subElement.Elements())
{ {
if (invElement.Name.ToString().ToLowerInvariant() != "inventory") continue; if (!invElement.Name.ToString().Equals("inventory", StringComparison.OrdinalIgnoreCase)) { continue; }
characterInfo.InventoryData = invElement; characterInfo.InventoryData = invElement;
break; break;
} }
@@ -90,6 +90,8 @@ namespace Barotrauma
CanBeFocused = false CanBeFocused = false
}; };
#region Crew Area
var crewAreaWithButtons = new GUIFrame( var crewAreaWithButtons = new GUIFrame(
HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.CrewArea, guiFrame.RectTransform), HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.CrewArea, guiFrame.RectTransform),
style: null, style: null,
@@ -98,11 +100,13 @@ namespace Barotrauma
CanBeFocused = false CanBeFocused = false
}; };
var buttonHeight = (int)(GUI.Scale * 40); var commandButtonHeight = (int)(GUI.Scale * 40);
var buttonSize = new Point((int)(182f / 99f * commandButtonHeight), commandButtonHeight);
var crewListToggleButtonHeight = (int)(64f * buttonSize.X / 175f);
crewArea = new GUIFrame( crewArea = new GUIFrame(
new RectTransform( new RectTransform(
new Point(crewAreaWithButtons.Rect.Width, crewAreaWithButtons.Rect.Height - (int)(1.5f * buttonHeight) - 2 * HUDLayoutSettings.Padding), new Point(crewAreaWithButtons.Rect.Width, crewAreaWithButtons.Rect.Height - commandButtonHeight - crewListToggleButtonHeight - 2 * HUDLayoutSettings.Padding),
crewAreaWithButtons.RectTransform, crewAreaWithButtons.RectTransform,
Anchor.BottomLeft), Anchor.BottomLeft),
style: null, style: null,
@@ -111,7 +115,6 @@ namespace Barotrauma
CanBeFocused = false CanBeFocused = false
}; };
var buttonSize = new Point((int)(182.0f / 99.0f * buttonHeight), buttonHeight);
commandButton = new GUIButton( commandButton = new GUIButton(
new RectTransform(buttonSize, parent: crewAreaWithButtons.RectTransform), new RectTransform(buttonSize, parent: crewAreaWithButtons.RectTransform),
style: "CommandButton") style: "CommandButton")
@@ -139,14 +142,13 @@ namespace Barotrauma
Spacing = (int)(GUI.Scale * 10) Spacing = (int)(GUI.Scale * 10)
}; };
buttonSize.Y = crewListToggleButtonHeight;
toggleCrewButton = new GUIButton( toggleCrewButton = new GUIButton(
new RectTransform( new RectTransform(buttonSize, parent: crewAreaWithButtons.RectTransform)
new Point(buttonSize.X, (int)(0.5f * buttonHeight)),
parent: crewAreaWithButtons.RectTransform)
{ {
AbsoluteOffset = new Point(0, buttonHeight + HUDLayoutSettings.Padding) AbsoluteOffset = new Point(0, commandButtonHeight + HUDLayoutSettings.Padding)
}, },
style: "UIToggleButton") style: "CrewListToggleButton")
{ {
OnClicked = (GUIButton btn, object userdata) => OnClicked = (GUIButton btn, object userdata) =>
{ {
@@ -159,6 +161,17 @@ namespace Barotrauma
previousOrderArrow = new Sprite("Content/UI/CommandUIAtlas.png", new Rectangle(128, 512, 128, 128)); previousOrderArrow = new Sprite("Content/UI/CommandUIAtlas.png", new Rectangle(128, 512, 128, 128));
cancelIcon = new Sprite("Content/UI/CommandUIAtlas.png", new Rectangle(512, 384, 128, 128)); cancelIcon = new Sprite("Content/UI/CommandUIAtlas.png", new Rectangle(512, 384, 128, 128));
// Calculate and store crew list entry size so it doesn't have to be calculated for every entry
crewListEntrySize = new Point(crewList.Content.Rect.Width - HUDLayoutSettings.Padding, 0);
int crewListEntryMinHeight = 32;
crewListEntrySize.Y = Math.Max(crewListEntryMinHeight, (int)(crewListEntrySize.X / 8f));
float charactersPerView = crewList.Content.Rect.Height / (float)(crewListEntrySize.Y + crewList.Spacing);
int adjustedHeight = (int)Math.Ceiling(crewList.Content.Rect.Height / Math.Round(charactersPerView)) - crewList.Spacing;
if (adjustedHeight < crewListEntryMinHeight) { adjustedHeight = (int)Math.Ceiling(crewList.Content.Rect.Height / Math.Floor(charactersPerView)) - crewList.Spacing; }
crewListEntrySize.Y = adjustedHeight;
#endregion
#region Chatbox #region Chatbox
if (IsSinglePlayer) if (IsSinglePlayer)
@@ -212,7 +225,7 @@ namespace Barotrauma
var chatBox = ChatBox ?? GameMain.Client?.ChatBox; var chatBox = ChatBox ?? GameMain.Client?.ChatBox;
if (chatBox != null) if (chatBox != null)
{ {
chatBox.ToggleButton = new GUIButton(new RectTransform(new Point((int)(182f * GUI.Scale * 0.4f), (int)(99f * GUI.Scale * 0.4f)), guiFrame.RectTransform), style: "ChatToggleButton"); chatBox.ToggleButton = new GUIButton(new RectTransform(new Point((int)(182f * GUI.Scale * 0.4f), (int)(99f * GUI.Scale * 0.4f)), chatBox.GUIFrame.Parent.RectTransform), style: "ChatToggleButton");
chatBox.ToggleButton.RectTransform.AbsoluteOffset = new Point(0, HUDLayoutSettings.ChatBoxArea.Height - chatBox.ToggleButton.Rect.Height); chatBox.ToggleButton.RectTransform.AbsoluteOffset = new Point(0, HUDLayoutSettings.ChatBoxArea.Height - chatBox.ToggleButton.Rect.Height);
chatBox.ToggleButton.OnClicked += (GUIButton btn, object userdata) => chatBox.ToggleButton.OnClicked += (GUIButton btn, object userdata) =>
{ {
@@ -248,6 +261,8 @@ namespace Barotrauma
OnClicked = (GUIButton button, object userData) => OnClicked = (GUIButton button, object userData) =>
{ {
if (!CanIssueOrders) { return false; } if (!CanIssueOrders) { return false; }
var sub = Character.Controlled.Submarine;
if (sub == null || sub.TeamID != Character.Controlled.TeamID || sub.Info.IsWreck) { return false; }
SetCharacterOrder(null, order, null, Character.Controlled); SetCharacterOrder(null, order, null, Character.Controlled);
var visibleHulls = new List<Hull>(Character.Controlled.GetVisibleHulls()); var visibleHulls = new List<Hull>(Character.Controlled.GetVisibleHulls());
foreach (var hull in visibleHulls) foreach (var hull in visibleHulls)
@@ -330,17 +345,7 @@ namespace Barotrauma
} }
AddCharacterToCrewList(character); AddCharacterToCrewList(character);
DisplayCharacterOrder(character, character.CurrentOrder, character.CurrentOrderOption);
if (character is AICharacter)
{
var ai = character.AIController as HumanAIController;
if (ai == null)
{
DebugConsole.ThrowError("Error in crewmanager - attempted to give orders to a character with no HumanAIController");
return;
}
character.SetOrder(ai.CurrentOrder, "", null, false);
}
} }
public void AddCharacterInfo(CharacterInfo characterInfo) public void AddCharacterInfo(CharacterInfo characterInfo)
@@ -383,16 +388,14 @@ namespace Barotrauma
{ {
if (character == null) { return; } if (character == null) { return; }
int width = crewList.Content.Rect.Width - HUDLayoutSettings.Padding;
int height = Math.Max(32, (int)((1.0f / 8.0f) * width));
var background = new GUIFrame( var background = new GUIFrame(
new RectTransform(new Point(width, height), parent: crewList.Content.RectTransform, anchor: Anchor.TopRight), new RectTransform(crewListEntrySize, parent: crewList.Content.RectTransform, anchor: Anchor.TopRight),
style: "CrewListBackground") style: "CrewListBackground")
{ {
UserData = character UserData = character
}; };
var iconRelativeWidth = (float)height / background.Rect.Width; var iconRelativeWidth = (float)crewListEntrySize.Y / background.Rect.Width;
var layoutGroup = new GUILayoutGroup( var layoutGroup = new GUILayoutGroup(
new RectTransform(Vector2.One, parent: background.RectTransform), new RectTransform(Vector2.One, parent: background.RectTransform),
@@ -788,11 +791,11 @@ namespace Barotrauma
characterFrame.SetAsFirstChild(); characterFrame.SetAsFirstChild();
} }
private void DisplayPreviousCharacterOrder(Character character, GUILayoutGroup characterComponent, OrderInfo currentOrderInfo) private void DisplayPreviousCharacterOrder(Character character, GUILayoutGroup characterComponent, OrderInfo orderInfo)
{ {
if (currentOrderInfo.Order == null || currentOrderInfo.Order.Identifier == dismissedOrderPrefab.Identifier) { return; } if (orderInfo.Order == null || orderInfo.Order.Identifier == dismissedOrderPrefab.Identifier) { return; }
var previousOrderInfo = new OrderInfo(currentOrderInfo); var previousOrderInfo = new OrderInfo(orderInfo);
var prevOrderFrame = new GUIButton( var prevOrderFrame = new GUIButton(
new RectTransform( new RectTransform(
characterComponent.GetChildByUserData("job").RectTransform.RelativeSize, characterComponent.GetChildByUserData("job").RectTransform.RelativeSize,
@@ -835,12 +838,12 @@ namespace Barotrauma
private GUIComponent GetCurrentOrderComponent(GUILayoutGroup characterComponent) private GUIComponent GetCurrentOrderComponent(GUILayoutGroup characterComponent)
{ {
return characterComponent.FindChild(c => c.UserData is OrderInfo orderInfo && orderInfo.ComponentIdentifier == "currentorder"); return characterComponent?.FindChild(c => c?.UserData is OrderInfo orderInfo && orderInfo.ComponentIdentifier == "currentorder");
} }
private GUIComponent GetPreviousOrderComponent(GUILayoutGroup characterComponent) private GUIComponent GetPreviousOrderComponent(GUILayoutGroup characterComponent)
{ {
return characterComponent.FindChild(c => c.UserData is OrderInfo orderInfo && orderInfo.ComponentIdentifier == "previousorder"); return characterComponent?.FindChild(c => c?.UserData is OrderInfo orderInfo && orderInfo.ComponentIdentifier == "previousorder");
} }
private struct OrderInfo private struct OrderInfo
@@ -910,7 +913,11 @@ namespace Barotrauma
{ {
if (!(c.UserData is Character character) || character.IsDead || character.Removed) { continue; } if (!(c.UserData is Character character) || character.IsDead || character.Removed) { continue; }
AddCharacter(character); AddCharacter(character);
DisplayCharacterOrder(character, character.CurrentOrder, (character.AIController as HumanAIController)?.CurrentOrderOption); if (GetPreviousOrderComponent(c.GetChild<GUILayoutGroup>())?.UserData is OrderInfo prevInfo &&
crewList.Content.Children.FirstOrDefault(c => c?.UserData == character)?.GetChild<GUILayoutGroup>() is GUILayoutGroup newLayoutGroup)
{
DisplayPreviousCharacterOrder(character, newLayoutGroup, prevInfo);
}
} }
} }
@@ -944,6 +951,7 @@ namespace Barotrauma
{ {
Character.Controlled.AIController.ObjectiveManager.WaitTimer = CharacterWaitOnSwitch; Character.Controlled.AIController.ObjectiveManager.WaitTimer = CharacterWaitOnSwitch;
} }
DisableCommandUI();
Character.Controlled = character; Character.Controlled = character;
} }
@@ -988,6 +996,7 @@ namespace Barotrauma
commandFrame == null && !clicklessSelectionActive && CanIssueOrders) commandFrame == null && !clicklessSelectionActive && CanIssueOrders)
{ {
CreateCommandUI(HUDLayoutSettings.PortraitArea.Contains(PlayerInput.MousePosition) ? Character.Controlled : GUI.MouseOn?.UserData as Character); CreateCommandUI(HUDLayoutSettings.PortraitArea.Contains(PlayerInput.MousePosition) ? Character.Controlled : GUI.MouseOn?.UserData as Character);
GUI.PlayUISound(GUISoundType.PopupMenu);
clicklessSelectionActive = isOpeningClick = true; clicklessSelectionActive = isOpeningClick = true;
} }
@@ -1176,19 +1185,22 @@ namespace Barotrauma
if (PlayerInput.KeyHit(InputType.RadioChat) && !ChatBox.InputBox.Selected) if (PlayerInput.KeyHit(InputType.RadioChat) && !ChatBox.InputBox.Selected)
{ {
ChatBox.InputBox.AddToGUIUpdateList(); if (Character.Controlled == null || Character.Controlled.SpeechImpediment < 100)
ChatBox.GUIFrame.Flash(Color.YellowGreen, 0.5f);
if (!ChatBox.ToggleOpen)
{ {
ChatBox.CloseAfterMessageSent = !ChatBox.ToggleOpen; ChatBox.InputBox.AddToGUIUpdateList();
ChatBox.ToggleOpen = true; ChatBox.GUIFrame.Flash(Color.YellowGreen, 0.5f);
} if (!ChatBox.ToggleOpen)
{
ChatBox.CloseAfterMessageSent = !ChatBox.ToggleOpen;
ChatBox.ToggleOpen = true;
}
if (!ChatBox.InputBox.Text.StartsWith(ChatBox.RadioChatString)) if (!ChatBox.InputBox.Text.StartsWith(ChatBox.RadioChatString))
{ {
ChatBox.InputBox.Text = ChatBox.RadioChatString; ChatBox.InputBox.Text = ChatBox.RadioChatString;
}
ChatBox.InputBox.Select(ChatBox.InputBox.Text.Length);
} }
ChatBox.InputBox.Select(ChatBox.InputBox.Text.Length);
} }
} }
} }
@@ -1234,6 +1246,7 @@ namespace Barotrauma
if (GUI.KeyboardDispatcher.Subscriber == null && PlayerInput.KeyHit(InputType.CrewOrders)) if (GUI.KeyboardDispatcher.Subscriber == null && PlayerInput.KeyHit(InputType.CrewOrders))
{ {
GUI.PlayUISound(GUISoundType.PopupMenu);
ToggleCrewListOpen = !ToggleCrewListOpen; ToggleCrewListOpen = !ToggleCrewListOpen;
} }
@@ -1293,13 +1306,21 @@ namespace Barotrauma
{ {
get get
{ {
#if DEBUG
return Character.Controlled == null || Character.Controlled.Info != null && Character.Controlled.SpeechImpediment < 100.0f;
#else
return Character.Controlled != null && Character.Controlled.SpeechImpediment < 100.0f; return Character.Controlled != null && Character.Controlled.SpeechImpediment < 100.0f;
#endif
} }
} }
private bool CanSomeoneHearCharacter() private bool CanSomeoneHearCharacter()
{ {
#if DEBUG
return true;
#else
return Character.Controlled != null && characters.Any(c => c != Character.Controlled && c.CanHearCharacter(Character.Controlled)); return Character.Controlled != null && characters.Any(c => c != Character.Controlled && c.CanHearCharacter(Character.Controlled));
#endif
} }
private void CreateCommandUI(Character characterContext = null) private void CreateCommandUI(Character characterContext = null)
@@ -1505,17 +1526,7 @@ namespace Barotrauma
shortcutCenterNode = null; shortcutCenterNode = null;
} }
CreateNodes(userData); CreateNodes(userData);
if (returnNode != null && returnNode.Visible) CreateReturnNodeHotkey();
{
var hotkey = optionNodes.Count + 1;
if (expandNode != null && expandNode.Visible) { hotkey += 1; }
CreateHotkeyIcon(returnNode.RectTransform, hotkey % 10, true);
returnNodeHotkey = Keys.D0 + hotkey % 10;
}
else
{
returnNodeHotkey = Keys.None;
}
return true; return true;
} }
@@ -1539,10 +1550,20 @@ namespace Barotrauma
returnNode = null; returnNode = null;
} }
CreateNodes(userData); CreateNodes(userData);
CreateReturnNodeHotkey();
return true;
}
private void CreateReturnNodeHotkey()
{
if (returnNode != null && returnNode.Visible) if (returnNode != null && returnNode.Visible)
{ {
var hotkey = optionNodes.Count + 1; var hotkey = 1;
if (expandNode != null && expandNode.Visible) { hotkey += 1; } if (targetFrame == null || !targetFrame.Visible)
{
hotkey = optionNodes.Count + 1;
if (expandNode != null && expandNode.Visible) { hotkey += 1; }
}
CreateHotkeyIcon(returnNode.RectTransform, hotkey % 10, true); CreateHotkeyIcon(returnNode.RectTransform, hotkey % 10, true);
returnNodeHotkey = Keys.D0 + hotkey % 10; returnNodeHotkey = Keys.D0 + hotkey % 10;
} }
@@ -1550,7 +1571,6 @@ namespace Barotrauma
{ {
returnNodeHotkey = Keys.None; returnNodeHotkey = Keys.None;
} }
return true;
} }
private void SetCenterNode(GUIButton node) private void SetCenterNode(GUIButton node)
@@ -1822,7 +1842,7 @@ namespace Barotrauma
Item.ItemList.FindAll(it => it.Components.Any(ic => ic.GetType() == order.ItemComponentType)); Item.ItemList.FindAll(it => it.Components.Any(ic => ic.GetType() == order.ItemComponentType));
matchingItems.RemoveAll(it => it.Submarine != submarine && !submarine.DockedTo.Contains(it.Submarine)); matchingItems.RemoveAll(it => it.Submarine != submarine && !submarine.DockedTo.Contains(it.Submarine));
matchingItems.RemoveAll(it => it.Submarine != null && it.Submarine.IsOutpost); matchingItems.RemoveAll(it => it.Submarine != null && it.Submarine.Info.Type != SubmarineInfo.SubmarineType.Player);
} }
//more than one target item -> create a minimap-like selection with a pic of the sub //more than one target item -> create a minimap-like selection with a pic of the sub
@@ -2148,7 +2168,11 @@ namespace Barotrauma
ToolTip = character.Info.DisplayName + " (" + character.Info.Job.Name + ")" ToolTip = character.Info.DisplayName + " (" + character.Info.Job.Name + ")"
}; };
#if DEBUG
bool canHear = true;
#else
bool canHear = character.CanHearCharacter(Character.Controlled); bool canHear = character.CanHearCharacter(Character.Controlled);
#endif
if (!canHear) if (!canHear)
{ {
node.CanBeFocused = icon.CanBeFocused = false; node.CanBeFocused = icon.CanBeFocused = false;
@@ -2271,8 +2295,14 @@ namespace Barotrauma
private Character GetBestCharacterForOrder(Order order) private Character GetBestCharacterForOrder(Order order)
{ {
#if !DEBUG
if (Character.Controlled == null) { return null; } if (Character.Controlled == null) { return null; }
return characters.FindAll(c => c != Character.Controlled && c.TeamID == Character.Controlled.TeamID) #endif
if (order.Category == OrderCategory.Operate && HumanAIController.IsItemOperatedByAnother(null, order.TargetItemComponent, out Character operatingCharacter))
{
return operatingCharacter;
}
return characters.FindAll(c => Character.Controlled == null || (c != Character.Controlled && c.TeamID == Character.Controlled.TeamID))
.OrderByDescending(c => c.CurrentOrder == null || c.CurrentOrder.Identifier == dismissedOrderPrefab.Identifier) .OrderByDescending(c => c.CurrentOrder == null || c.CurrentOrder.Identifier == dismissedOrderPrefab.Identifier)
.ThenByDescending(c => order.HasAppropriateJob(c)) .ThenByDescending(c => order.HasAppropriateJob(c))
.ThenBy(c => c.CurrentOrder?.Weight) .ThenBy(c => c.CurrentOrder?.Weight)
@@ -2281,16 +2311,18 @@ namespace Barotrauma
private List<Character> GetCharactersSortedForOrder(Order order) private List<Character> GetCharactersSortedForOrder(Order order)
{ {
#if !DEBUG
if (Character.Controlled == null) { return new List<Character>(); } if (Character.Controlled == null) { return new List<Character>(); }
#endif
if (order.Identifier == "follow") if (order.Identifier == "follow")
{ {
return characters.FindAll(c => c != Character.Controlled && c.TeamID == Character.Controlled.TeamID) return characters.FindAll(c => Character.Controlled == null || (c != Character.Controlled && c.TeamID == Character.Controlled.TeamID))
.OrderByDescending(c => c.CurrentOrder == null || c.CurrentOrder.Identifier == dismissedOrderPrefab.Identifier) .OrderByDescending(c => c.CurrentOrder == null || c.CurrentOrder.Identifier == dismissedOrderPrefab.Identifier)
.ToList(); .ToList();
} }
else else
{ {
return characters.FindAll(c => c.TeamID == Character.Controlled.TeamID) return characters.FindAll(c => Character.Controlled == null || c.TeamID == Character.Controlled.TeamID)
.OrderByDescending(c => c.CurrentOrder == null || c.CurrentOrder.Identifier == dismissedOrderPrefab.Identifier) .OrderByDescending(c => c.CurrentOrder == null || c.CurrentOrder.Identifier == dismissedOrderPrefab.Identifier)
.ThenByDescending(c => order.HasAppropriateJob(c)) .ThenByDescending(c => order.HasAppropriateJob(c))
.ThenBy(c => c.CurrentOrder?.Weight) .ThenBy(c => c.CurrentOrder?.Weight)
@@ -2411,12 +2443,15 @@ namespace Barotrauma
var reportButtonParent = ChatBox ?? GameMain.Client?.ChatBox; var reportButtonParent = ChatBox ?? GameMain.Client?.ChatBox;
if (reportButtonParent == null) { return; } if (reportButtonParent == null) { return; }
var sub = Character.Controlled.Submarine;
if (sub == null || sub.TeamID != Character.Controlled.TeamID || sub.Info.IsWreck) { return; }
ReportButtonFrame.RectTransform.AbsoluteOffset = new Point(reportButtonParent.GUIFrame.Rect.Right + (int)(10 * GUI.Scale), reportButtonParent.GUIFrame.Rect.Y); ReportButtonFrame.RectTransform.AbsoluteOffset = new Point(reportButtonParent.GUIFrame.Rect.Right + (int)(10 * GUI.Scale), reportButtonParent.GUIFrame.Rect.Y);
bool hasFires = Character.Controlled.CurrentHull.FireSources.Count > 0; bool hasFires = Character.Controlled.CurrentHull.FireSources.Count > 0;
ToggleReportButton("reportfire", hasFires); ToggleReportButton("reportfire", hasFires);
bool hasLeaks = Character.Controlled.CurrentHull.Submarine != null && Character.Controlled.CurrentHull.ConnectedGaps.Any(g => !g.IsRoomToRoom && g.Open > 0.0f); bool hasLeaks = Character.Controlled.CurrentHull.ConnectedGaps.Any(g => !g.IsRoomToRoom && g.Open > 0.0f);
ToggleReportButton("reportbreach", hasLeaks); ToggleReportButton("reportbreach", hasLeaks);
bool hasIntruders = Character.CharacterList.Any(c => c.CurrentHull == Character.Controlled.CurrentHull && AIObjectiveFightIntruders.IsValidTarget(c, Character.Controlled)); bool hasIntruders = Character.CharacterList.Any(c => c.CurrentHull == Character.Controlled.CurrentHull && AIObjectiveFightIntruders.IsValidTarget(c, Character.Controlled));
@@ -91,7 +91,7 @@ namespace Barotrauma
if (Character.Controlled.Submarine != outpost) { return null; } if (Character.Controlled.Submarine != outpost) { return null; }
//if there's a sub docked to the outpost, we can leave the level //if there's a sub docked to the outpost, we can leave the level
if (outpost.DockedTo.Count > 0) if (outpost.DockedTo.Any())
{ {
var dockedSub = outpost.DockedTo.FirstOrDefault(); var dockedSub = outpost.DockedTo.FirstOrDefault();
return dockedSub.DockedTo.Contains(Submarine.MainSub) ? Submarine.MainSub : dockedSub; return dockedSub.DockedTo.Contains(Submarine.MainSub) ? Submarine.MainSub : dockedSub;
@@ -277,6 +277,9 @@ namespace Barotrauma
c.Info.InventoryData = inventoryElement; c.Info.InventoryData = inventoryElement;
c.Inventory?.DeleteAllItems(); c.Inventory?.DeleteAllItems();
} }
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
SaveUtil.SaveGame(GameMain.GameSession.SavePath); SaveUtil.SaveGame(GameMain.GameSession.SavePath);
} }
@@ -0,0 +1,80 @@
using Barotrauma.Tutorials;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
class SubTestMode : GameMode
{
public SubTestMode(GameModePreset preset, object param)
: base(preset, param)
{
foreach (JobPrefab jobPrefab in JobPrefab.Prefabs)
{
for (int i = 0; i < jobPrefab.InitialCount; i++)
{
var variant = Rand.Range(0, jobPrefab.Variants);
CrewManager.AddCharacterInfo(new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: jobPrefab, variant: variant));
}
}
}
public override void Start()
{
base.Start();
isRunning = true;
CrewManager.InitSinglePlayerRound();
Submarine.MainSub.SetPosition(Vector2.Zero);
}
public override void Draw(SpriteBatch spriteBatch)
{
if (!isRunning|| GUI.DisableHUD || GUI.DisableUpperHUD) return;
if (Submarine.MainSub == null) return;
}
public override void AddToGUIUpdateList()
{
if (!isRunning) return;
base.AddToGUIUpdateList();
CrewManager.AddToGUIUpdateList();
}
public override void Update(float deltaTime)
{
if (!isRunning) { return; }
base.Update(deltaTime);
}
public override void End(string endMessage = "")
{
isRunning = false;
GameMain.GameSession.EndRound("");
CrewManager.EndRound();
Submarine.Unload();
GameMain.SubEditorScreen.Select();
}
private bool EndRound(Submarine leavingSub)
{
isRunning = false;
End("");
return true;
}
}
}
@@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
using Barotrauma.Items.Components; using Barotrauma.Items.Components;
using Barotrauma.Networking; using Barotrauma.Networking;
@@ -221,7 +222,7 @@ namespace Barotrauma.Tutorials
{ {
//captain_navConsoleCustomInterface.HighlightElement(0, uiHighlightColor, duration: 1.0f, pulsateAmount: 0.0f); //captain_navConsoleCustomInterface.HighlightElement(0, uiHighlightColor, duration: 1.0f, pulsateAmount: 0.0f);
yield return new WaitForSeconds(1.0f, false); yield return new WaitForSeconds(1.0f, false);
} while (Submarine.MainSub.DockedTo.Count > 0); } while (Submarine.MainSub.DockedTo.Any());
RemoveCompletedObjective(segments[4]); RemoveCompletedObjective(segments[4]);
yield return new WaitForSeconds(2f, false); yield return new WaitForSeconds(2f, false);
TriggerTutorialSegment(5); // Navigate to destination TriggerTutorialSegment(5); // Navigate to destination
@@ -245,7 +246,7 @@ namespace Barotrauma.Tutorials
{ {
//captain_navConsoleCustomInterface.HighlightElement(0, uiHighlightColor, duration: 1.0f, pulsateAmount: 0.0f); //captain_navConsoleCustomInterface.HighlightElement(0, uiHighlightColor, duration: 1.0f, pulsateAmount: 0.0f);
yield return new WaitForSeconds(1.0f, false); yield return new WaitForSeconds(1.0f, false);
} while (!Submarine.MainSub.AtEndPosition || Submarine.MainSub.DockedTo.Count == 0); } while (!Submarine.MainSub.AtEndPosition || Submarine.MainSub.DockedTo.Any());
RemoveCompletedObjective(segments[6]); RemoveCompletedObjective(segments[6]);
yield return new WaitForSeconds(3f, false); yield return new WaitForSeconds(3f, false);
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.GetWithVariable("Captain.Radio.Complete", "[OUTPOSTNAME]", GameMain.GameSession.EndLocation.Name), ChatMessageType.Radio, null); GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.GetWithVariable("Captain.Radio.Complete", "[OUTPOSTNAME]", GameMain.GameSession.EndLocation.Name), ChatMessageType.Radio, null);
@@ -21,8 +21,8 @@ namespace Barotrauma.Tutorials
private string levelSeed; private string levelSeed;
private string levelParams; private string levelParams;
private Submarine startOutpost = null; private SubmarineInfo startOutpost = null;
private Submarine endOutpost = null; private SubmarineInfo endOutpost = null;
private bool currentTutorialCompleted = false; private bool currentTutorialCompleted = false;
private float fadeOutTime = 3f; private float fadeOutTime = 3f;
protected float waitBeforeFade = 4f; protected float waitBeforeFade = 4f;
@@ -56,13 +56,13 @@ namespace Barotrauma.Tutorials
private IEnumerable<object> Loading() private IEnumerable<object> Loading()
{ {
Submarine.MainSub = Submarine.Load(submarinePath, "", true); SubmarineInfo subInfo = new SubmarineInfo(submarinePath);
LevelGenerationParams generationParams = LevelGenerationParams.LevelParams.Find(p => p.Name == levelParams); LevelGenerationParams generationParams = LevelGenerationParams.LevelParams.Find(p => p.Name == levelParams);
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
GameMain.GameSession = new GameSession(Submarine.MainSub, "", GameMain.GameSession = new GameSession(subInfo, "",
GameModePreset.List.Find(g => g.Identifier == "tutorial")); GameModePreset.List.Find(g => g.Identifier == "tutorial"));
(GameMain.GameSession.GameMode as TutorialMode).Tutorial = this; (GameMain.GameSession.GameMode as TutorialMode).Tutorial = this;
@@ -72,12 +72,12 @@ namespace Barotrauma.Tutorials
if (startOutpostPath != string.Empty) if (startOutpostPath != string.Empty)
{ {
startOutpost = Submarine.Load(startOutpostPath, "", false); startOutpost = new SubmarineInfo(startOutpostPath);
} }
if (endOutpostPath != string.Empty) if (endOutpostPath != string.Empty)
{ {
endOutpost = Submarine.Load(endOutpostPath, "", false); endOutpost = new SubmarineInfo(endOutpostPath);
} }
Level tutorialLevel = new Level(levelSeed, 0, 0, generationParams, biome, startOutpost, endOutpost); Level tutorialLevel = new Level(levelSeed, 0, 0, generationParams, biome, startOutpost, endOutpost);
@@ -160,11 +160,11 @@ namespace Barotrauma.Tutorials
switch (this.spawnSub) switch (this.spawnSub)
{ {
case "startoutpost": case "startoutpost":
spawnSub = startOutpost; spawnSub = Level.Loaded.StartOutpost;
break; break;
case "endoutpost": case "endoutpost":
spawnSub = endOutpost; spawnSub = Level.Loaded.EndOutpost;
break; break;
default: default:
@@ -13,6 +13,7 @@ namespace Barotrauma
private GUIFrame infoFrameContent; private GUIFrame infoFrameContent;
public RoundSummary RoundSummary { get; private set; } public RoundSummary RoundSummary { get; private set; }
public static bool IsInfoFrameOpen => GameMain.GameSession?.infoFrame != null;
private bool ToggleInfoFrame() private bool ToggleInfoFrame()
{ {
@@ -24,7 +24,7 @@ namespace Barotrauma
public GUIFrame CreateSummaryFrame(string endMessage) public GUIFrame CreateSummaryFrame(string endMessage)
{ {
bool singleplayer = GameMain.NetworkMember == null; bool singleplayer = GameMain.NetworkMember == null;
bool gameOver = gameSession.CrewManager.GetCharacters().All(c => c.IsDead || c.IsUnconscious); bool gameOver = gameSession.CrewManager.GetCharacters().All(c => c.IsDead || c.IsIncapacitated);
bool progress = Submarine.MainSub.AtEndPosition; bool progress = Submarine.MainSub.AtEndPosition;
if (!singleplayer) if (!singleplayer)
{ {
@@ -55,7 +55,7 @@ namespace Barotrauma
string summaryText = TextManager.GetWithVariables(gameOver ? "RoundSummaryGameOver" : string summaryText = TextManager.GetWithVariables(gameOver ? "RoundSummaryGameOver" :
(progress ? "RoundSummaryProgress" : "RoundSummaryReturn"), new string[2] { "[sub]", "[location]" }, (progress ? "RoundSummaryProgress" : "RoundSummaryReturn"), new string[2] { "[sub]", "[location]" },
new string[2] { Submarine.MainSub.Name, progress ? GameMain.GameSession.EndLocation.Name : GameMain.GameSession.StartLocation.Name }); new string[2] { Submarine.MainSub.Info.Name, progress ? GameMain.GameSession.EndLocation.Name : GameMain.GameSession.StartLocation.Name });
var infoText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoTextBox.Content.RectTransform), var infoText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoTextBox.Content.RectTransform),
summaryText, wrap: true); summaryText, wrap: true);
@@ -47,7 +47,7 @@ namespace Barotrauma
{ {
keyMapping = new KeyOrMouse[Enum.GetNames(typeof(InputType)).Length]; keyMapping = new KeyOrMouse[Enum.GetNames(typeof(InputType)).Length];
keyMapping[(int)InputType.Run] = new KeyOrMouse(Keys.LeftShift); keyMapping[(int)InputType.Run] = new KeyOrMouse(Keys.LeftShift);
keyMapping[(int)InputType.Attack] = new KeyOrMouse(MouseButton.MiddleMouse); keyMapping[(int)InputType.Attack] = new KeyOrMouse(Keys.R);
keyMapping[(int)InputType.Crouch] = new KeyOrMouse(Keys.LeftControl); keyMapping[(int)InputType.Crouch] = new KeyOrMouse(Keys.LeftControl);
keyMapping[(int)InputType.Grab] = new KeyOrMouse(Keys.G); keyMapping[(int)InputType.Grab] = new KeyOrMouse(Keys.G);
keyMapping[(int)InputType.Health] = new KeyOrMouse(Keys.H); keyMapping[(int)InputType.Health] = new KeyOrMouse(Keys.H);
@@ -1182,7 +1182,7 @@ namespace Barotrauma
{ RelativeOffset = new Vector2(0.02f, 0.02f) }) { RelativeOffset = new Vector2(0.02f, 0.02f) })
{ RelativeSpacing = 0.01f }; { RelativeSpacing = 0.01f };
var automaticQuickStartTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), "Enable automatic quickstart", style: "GUITickBox"); var automaticQuickStartTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), "Automatic quickstart enabled", style: "GUITickBox");
automaticQuickStartTickBox.Selected = AutomaticQuickStartEnabled; automaticQuickStartTickBox.Selected = AutomaticQuickStartEnabled;
automaticQuickStartTickBox.ToolTip = "Will the game automatically move on to Quickstart when the game is launched"; automaticQuickStartTickBox.ToolTip = "Will the game automatically move on to Quickstart when the game is launched";
automaticQuickStartTickBox.OnSelected = (tickBox) => automaticQuickStartTickBox.OnSelected = (tickBox) =>
@@ -1192,7 +1192,7 @@ namespace Barotrauma
return true; return true;
}; };
var showSplashScreenTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), "Show splash screen", style: "GUITickBox"); var showSplashScreenTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), "Splash screen enabled", style: "GUITickBox");
showSplashScreenTickBox.Selected = EnableSplashScreen; showSplashScreenTickBox.Selected = EnableSplashScreen;
showSplashScreenTickBox.ToolTip = "Are the splash screens shown when the game is launched"; showSplashScreenTickBox.ToolTip = "Are the splash screens shown when the game is launched";
showSplashScreenTickBox.OnSelected = (tickBox) => showSplashScreenTickBox.OnSelected = (tickBox) =>
@@ -1202,7 +1202,7 @@ namespace Barotrauma
return true; return true;
}; };
var verboseLoggingTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), "Enable verbose logging", style: "GUITickBox"); var verboseLoggingTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), "Verbose logging enabled", style: "GUITickBox");
verboseLoggingTickBox.Selected = VerboseLogging; verboseLoggingTickBox.Selected = VerboseLogging;
verboseLoggingTickBox.ToolTip = "Should verbose logging be used"; verboseLoggingTickBox.ToolTip = "Should verbose logging be used";
verboseLoggingTickBox.OnSelected = (tickBox) => verboseLoggingTickBox.OnSelected = (tickBox) =>
@@ -1211,6 +1211,16 @@ namespace Barotrauma
UnsavedSettings = true; UnsavedSettings = true;
return true; return true;
}; };
var textManagerDebugModeTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), "TextManager debug mode enabled", style: "GUITickBox");
textManagerDebugModeTickBox.Selected = TextManagerDebugModeEnabled;
textManagerDebugModeTickBox.ToolTip = "Does the TextManager return the text tags for debug purposes?";
textManagerDebugModeTickBox.OnSelected = (tickBox) =>
{
TextManagerDebugModeEnabled = tickBox.Selected;
UnsavedSettings = true;
return true;
};
#endif #endif
UnsavedSettings = false; // Reset unsaved settings to false once the UI has been created UnsavedSettings = false; // Reset unsaved settings to false once the UI has been created
@@ -1279,7 +1289,7 @@ namespace Barotrauma
string[] prefixes = { "OpenAL Soft on " }; string[] prefixes = { "OpenAL Soft on " };
foreach (string prefix in prefixes) foreach (string prefix in prefixes)
{ {
if (name.StartsWith(prefix, StringComparison.InvariantCulture)) if (name.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
{ {
return name.Remove(0, prefix.Length); return name.Remove(0, prefix.Length);
} }
@@ -43,6 +43,7 @@ namespace Barotrauma
public Vector2[] SlotPositions; public Vector2[] SlotPositions;
public static Point SlotSize; public static Point SlotSize;
public static int Spacing; public static int Spacing;
public static int HideButtonWidth;
private Layout layout; private Layout layout;
public Layout CurrentLayout public Layout CurrentLayout
@@ -71,22 +72,44 @@ namespace Barotrauma
{ {
get { return personalSlotArea; } get { return personalSlotArea; }
} }
private GUIImage[] indicators = new GUIImage[5];
private int[] indicatorIndexes = new int[5];
private Vector2 indicatorSpriteSize;
private GUILayoutGroup indicatorGroup;
partial void InitProjSpecific(XElement element) partial void InitProjSpecific(XElement element)
{ {
Hidden = true; Hidden = true;
hideButton = new GUIButton(new RectTransform(new Point((int)(30 * GUI.Scale), (int)(60 * GUI.Scale)), GUI.Canvas) hideButton = new GUIButton(new RectTransform(new Point((int)(31f * (HUDLayoutSettings.BottomRightInfoArea.Height / 100f)), HUDLayoutSettings.BottomRightInfoArea.Height), GUI.Canvas)
{ AbsoluteOffset = HUDLayoutSettings.CrewArea.Location }, { AbsoluteOffset = HUDLayoutSettings.CrewArea.Location },
"", style: "UIToggleButton"); "", style: "EquipmentToggleButton");
hideButton.Children.ForEach(c => c.SpriteEffects = SpriteEffects.FlipHorizontally);
indicatorGroup = new GUILayoutGroup(new RectTransform(Point.Zero, hideButton.RectTransform)) { IsHorizontal = false };
indicatorGroup.ChildAnchor = Anchor.TopCenter;
indicatorSpriteSize = GUI.Style.GetComponentStyle("EquipmentIndicatorDivingSuit").Sprites[GUIComponent.ComponentState.None][0].Sprite.size;
indicators[0] = new GUIImage(new RectTransform(Point.Zero, indicatorGroup.RectTransform), "EquipmentIndicatorDivingSuit");
indicators[1] = new GUIImage(new RectTransform(Point.Zero, indicatorGroup.RectTransform), "EquipmentIndicatorID");
indicators[2] = new GUIImage(new RectTransform(Point.Zero, indicatorGroup.RectTransform), "EquipmentIndicatorOutfit");
indicators[3] = new GUIImage(new RectTransform(Point.Zero, indicatorGroup.RectTransform), "EquipmentIndicatorHeadwear");
indicators[4] = new GUIImage(new RectTransform(Point.Zero, indicatorGroup.RectTransform), "EquipmentIndicatorHeadphones");
indicatorIndexes[0] = FindLimbSlot(InvSlotType.OuterClothes);
indicatorIndexes[1] = FindLimbSlot(InvSlotType.Card);
indicatorIndexes[2] = FindLimbSlot(InvSlotType.InnerClothes);
indicatorIndexes[3] = FindLimbSlot(InvSlotType.Head);
indicatorIndexes[4] = FindLimbSlot(InvSlotType.Headset);
for (int i = 0; i < indicators.Length; i++)
{
indicators[i].CanBeFocused = false;
}
hideButton.OnClicked += (GUIButton btn, object userdata) => hideButton.OnClicked += (GUIButton btn, object userdata) =>
{ {
hidePersonalSlots = !hidePersonalSlots; hidePersonalSlots = !hidePersonalSlots;
foreach (GUIComponent child in btn.Children)
{
child.SpriteEffects = hidePersonalSlots ? SpriteEffects.None : SpriteEffects.FlipHorizontally;
}
return true; return true;
}; };
@@ -245,6 +268,26 @@ namespace Barotrauma
return false; return false;
} }
private void SetIndicatorSizes()
{
indicatorGroup.RectTransform.AbsoluteOffset = new Point((int)Math.Round(4 * GUI.Scale), (int)Math.Round(7 * GUI.Scale));
indicatorGroup.RectTransform.NonScaledSize = new Point(hideButton.Rect.Width - indicatorGroup.RectTransform.AbsoluteOffset.X * 2, hideButton.Rect.Height - indicatorGroup.RectTransform.AbsoluteOffset.Y * 2);
indicatorGroup.AbsoluteSpacing = (int)Math.Ceiling(2 * GUI.Scale);
int indicatorHeight = (indicatorGroup.RectTransform.NonScaledSize.Y - indicatorGroup.AbsoluteSpacing * (indicators.Length - 1)) / indicators.Length;
int indicatorWidth = (int)(indicatorSpriteSize.X / (indicatorSpriteSize.Y / indicatorHeight));
if (HideButtonWidth % 2 != indicatorWidth % 2) indicatorWidth++;
Point indicatorSize = new Point(indicatorWidth, indicatorHeight);
for (int i = 0; i < indicators.Length; i++)
{
indicators[i].RectTransform.NonScaledSize = indicatorSize;
}
}
private void SetSlotPositions(Layout layout) private void SetSlotPositions(Layout layout)
{ {
bool isFourByThree = GUI.IsFourByThree(); bool isFourByThree = GUI.IsFourByThree();
@@ -257,6 +300,8 @@ namespace Barotrauma
Spacing = (int)(8 * UIScale); Spacing = (int)(8 * UIScale);
} }
HideButtonWidth = (int)(31f * (HUDLayoutSettings.BottomRightInfoArea.Height / 100f));
SlotSize = !isFourByThree ? (SlotSpriteSmall.size * UIScale).ToPoint() : (SlotSpriteSmall.size * UIScale * .925f).ToPoint(); SlotSize = !isFourByThree ? (SlotSpriteSmall.size * UIScale).ToPoint() : (SlotSpriteSmall.size * UIScale * .925f).ToPoint();
int bottomOffset = SlotSize.Y + Spacing * 2 + ContainedIndicatorHeight; int bottomOffset = SlotSize.Y + Spacing * 2 + ContainedIndicatorHeight;
@@ -272,8 +317,7 @@ namespace Barotrauma
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 x = GameMain.GraphicsWidth / 2 - normalSlotCount * (SlotSize.X + Spacing) / 2;
int upperX = HUDLayoutSettings.BottomRightInfoArea.X - Spacing * 2 - SlotSize.X - SlotSize.X / 2; int upperX = HUDLayoutSettings.BottomRightInfoArea.X - SlotSize.X - Spacing * 4 - HideButtonWidth;
//int upperX = GameMain.GraphicsWidth - personalSlotCount * (slotSize.X + spacing) + (int)(11 * GUI.Scale) + spacing;
//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 + normalSlotCount * (SlotSize.X + Spacing)) - (upperX - personalSlotCount * (SlotSize.X + Spacing)), 0);
@@ -300,11 +344,13 @@ namespace Barotrauma
if (hideButtonSlotIndex > -1) if (hideButtonSlotIndex > -1)
{ {
hideButton.RectTransform.SetPosition(Anchor.TopLeft, Pivot.TopLeft); hideButton.RectTransform.SetPosition(Anchor.TopLeft, Pivot.TopLeft);
hideButton.RectTransform.NonScaledSize = new Point(SlotSize.X / 2, HUDLayoutSettings.BottomRightInfoArea.Height); hideButton.RectTransform.NonScaledSize = new Point(HideButtonWidth, HUDLayoutSettings.BottomRightInfoArea.Height);
hideButton.RectTransform.AbsoluteOffset = new Point( hideButton.RectTransform.AbsoluteOffset = new Point(
personalSlotArea.Right + Spacing, personalSlotArea.Right + Spacing * 2,
HUDLayoutSettings.BottomRightInfoArea.Y); HUDLayoutSettings.BottomRightInfoArea.Y);
hideButton.Visible = true; hideButton.Visible = true;
SetIndicatorSizes();
} }
} }
break; break;
@@ -457,7 +503,8 @@ namespace Barotrauma
hidePersonalSlotsState = hidePersonalSlots ? hidePersonalSlotsState = hidePersonalSlots ?
Math.Min(hidePersonalSlotsState + deltaTime * 5.0f, 1.0f) : Math.Min(hidePersonalSlotsState + deltaTime * 5.0f, 1.0f) :
Math.Max(hidePersonalSlotsState - deltaTime * 5.0f, 0.0f); Math.Max(hidePersonalSlotsState - deltaTime * 5.0f, 0.0f);
bool personalSlotsMoving = hidePersonalSlotsState > 0 && hidePersonalSlotsState < 1f;
for (int i = 0; i < slots.Length; i++) for (int i = 0; i < slots.Length; i++)
{ {
if (!PersonalSlots.HasFlag(SlotTypes[i])) { continue; } if (!PersonalSlots.HasFlag(SlotTypes[i])) { continue; }
@@ -466,6 +513,7 @@ namespace Barotrauma
if (selectedSlot?.Slot == slots[i]) { selectedSlot = null; } if (selectedSlot?.Slot == slots[i]) { selectedSlot = null; }
highlightedSubInventorySlots.RemoveWhere(s => s.Slot == slots[i]); highlightedSubInventorySlots.RemoveWhere(s => s.Slot == slots[i]);
} }
slots[i].IsMoving = personalSlotsMoving;
slots[i].DrawOffset = Vector2.Lerp(Vector2.Zero, new Vector2(personalSlotArea.Width, 0.0f), hidePersonalSlotsState); slots[i].DrawOffset = Vector2.Lerp(Vector2.Zero, new Vector2(personalSlotArea.Width, 0.0f), hidePersonalSlotsState);
} }
} }
@@ -551,6 +599,8 @@ namespace Barotrauma
if (character == Character.Controlled && character.SelectedCharacter == null) // Permanently open subinventories only available when the default UI layout is in use -> not when grabbing characters if (character == Character.Controlled && character.SelectedCharacter == null) // Permanently open subinventories only available when the default UI layout is in use -> not when grabbing characters
{ {
UpdateEquipmentIndicators();
//remove the highlighted slots of other characters' inventories when not grabbing anyone //remove the highlighted slots of other characters' inventories when not grabbing anyone
highlightedSubInventorySlots.RemoveWhere(s => s.ParentInventory != this && s.ParentInventory?.Owner is Character); highlightedSubInventorySlots.RemoveWhere(s => s.ParentInventory != this && s.ParentInventory?.Owner is Character);
@@ -651,6 +701,39 @@ namespace Barotrauma
} }
} }
} }
private void UpdateEquipmentIndicators()
{
for (int i = 0; i < indicators.Length; i++)
{
Item item = Items[indicatorIndexes[i]];
if (item != null)
{
Wearable wearable = item.GetComponent<Wearable>();
if (wearable != null && wearable.DisplayContainedStatus)
{
float conditionPercentage = item.GetContainedItemConditionPercentage();
if (conditionPercentage != -1)
{
indicators[i].Color = ToolBox.GradientLerp(conditionPercentage, GUI.Style.EquipmentIndicatorRunningOut, GUI.Style.EquipmentIndicatorEquipped);
}
else
{
indicators[i].Color = GUI.Style.EquipmentIndicatorRunningOut;
}
}
else
{
indicators[i].Color = GUI.Style.EquipmentIndicatorEquipped;
}
}
else
{
indicators[i].Color = GUI.Style.EquipmentIndicatorNotEquipped;
}
}
}
private void ShowSubInventory(SlotReference slotRef, float deltaTime, Camera cam, List<SlotReference> hideSubInventories, bool isEquippedSubInventory) private void ShowSubInventory(SlotReference slotRef, float deltaTime, Camera cam, List<SlotReference> hideSubInventories, bool isEquippedSubInventory)
{ {
@@ -964,7 +1047,7 @@ namespace Barotrauma
if (limbSlotIcons.ContainsKey(SlotTypes[i])) if (limbSlotIcons.ContainsKey(SlotTypes[i]))
{ {
var icon = limbSlotIcons[SlotTypes[i]]; var icon = limbSlotIcons[SlotTypes[i]];
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, slots[i].Rect.Center.ToVector2() + slots[i].DrawOffset, GUI.Style.EquipmentSlotIconColor, origin: icon.size / 2, scale: slots[i].Rect.Width / icon.size.X);
} }
continue; continue;
} }
@@ -52,10 +52,14 @@ namespace Barotrauma.Items.Components
get { return sounds.Count > 0; } get { return sounds.Count > 0; }
} }
private bool[] hasSoundsOfType; private readonly bool[] hasSoundsOfType;
private Dictionary<ActionType, List<ItemSound>> sounds; private readonly Dictionary<ActionType, List<ItemSound>> sounds;
private Dictionary<ActionType, SoundSelectionMode> soundSelectionModes; private Dictionary<ActionType, SoundSelectionMode> soundSelectionModes;
protected float correctionTimer;
public float IsActiveTimer;
public GUILayoutSettings DefaultLayout { get; protected set; } public GUILayoutSettings DefaultLayout { get; protected set; }
public GUILayoutSettings AlternativeLayout { get; protected set; } public GUILayoutSettings AlternativeLayout { get; protected set; }
@@ -230,20 +234,23 @@ namespace Barotrauma.Items.Components
if (loopingSound != null) if (loopingSound != null)
{ {
float targetGain = 0.0f;
if (Vector3.DistanceSquared(GameMain.SoundManager.ListenerPosition, new Vector3(item.WorldPosition, 0.0f)) > loopingSound.Range * loopingSound.Range || if (Vector3.DistanceSquared(GameMain.SoundManager.ListenerPosition, new Vector3(item.WorldPosition, 0.0f)) > loopingSound.Range * loopingSound.Range ||
(targetGain = GetSoundVolume(loopingSound)) <= 0.0001f) (GetSoundVolume(loopingSound)) <= 0.0001f)
{ {
if (loopingSoundChannel != null) if (loopingSoundChannel != null)
{ {
loopingSoundChannel.FadeOutAndDispose(); loopingSoundChannel = null; loopingSoundChannel.FadeOutAndDispose();
loopingSoundChannel = null;
loopingSound = null;
} }
return; return;
} }
if (loopingSoundChannel != null && loopingSoundChannel.Sound != loopingSound.RoundSound.Sound) if (loopingSoundChannel != null && loopingSoundChannel.Sound != loopingSound.RoundSound.Sound)
{ {
loopingSoundChannel.FadeOutAndDispose(); loopingSoundChannel = null; loopingSoundChannel.FadeOutAndDispose();
loopingSoundChannel = null;
loopingSound = null;
} }
if (loopingSoundChannel == null || !loopingSoundChannel.IsPlaying) if (loopingSoundChannel == null || !loopingSoundChannel.IsPlaying)
{ {
@@ -258,8 +265,7 @@ namespace Barotrauma.Items.Components
} }
return; return;
} }
ItemSound itemSound = null;
var matchingSounds = sounds[type]; var matchingSounds = sounds[type];
if (loopingSoundChannel == null || !loopingSoundChannel.IsPlaying) if (loopingSoundChannel == null || !loopingSoundChannel.IsPlaying)
{ {
@@ -277,7 +283,7 @@ namespace Barotrauma.Items.Components
{ {
foreach (ItemSound sound in matchingSounds) foreach (ItemSound sound in matchingSounds)
{ {
PlaySound(sound, item.WorldPosition, user); PlaySound(sound, item.WorldPosition);
} }
return; return;
} }
@@ -286,13 +292,12 @@ namespace Barotrauma.Items.Components
index = Rand.Int(matchingSounds.Count); index = Rand.Int(matchingSounds.Count);
} }
itemSound = matchingSounds[index]; PlaySound(matchingSounds[index], item.WorldPosition);
PlaySound(matchingSounds[index], item.WorldPosition, user);
} }
} }
private void PlaySound(ItemSound itemSound, Vector2 position, Character user = null) private void PlaySound(ItemSound itemSound, Vector2 position)
{ {
if (Vector2.DistanceSquared(new Vector2(GameMain.SoundManager.ListenerPosition.X, GameMain.SoundManager.ListenerPosition.Y), position) > itemSound.Range * itemSound.Range) if (Vector2.DistanceSquared(new Vector2(GameMain.SoundManager.ListenerPosition.X, GameMain.SoundManager.ListenerPosition.Y), position) > itemSound.Range * itemSound.Range)
{ {
@@ -301,8 +306,7 @@ namespace Barotrauma.Items.Components
if (itemSound.Loop) if (itemSound.Loop)
{ {
loopingSound = itemSound; if (loopingSoundChannel != null && loopingSoundChannel.Sound != itemSound.RoundSound.Sound)
if (loopingSoundChannel != null && loopingSoundChannel.Sound != loopingSound.RoundSound.Sound)
{ {
loopingSoundChannel.FadeOutAndDispose(); loopingSoundChannel = null; loopingSoundChannel.FadeOutAndDispose(); loopingSoundChannel = null;
} }
@@ -310,6 +314,7 @@ namespace Barotrauma.Items.Components
{ {
float volume = GetSoundVolume(itemSound); float volume = GetSoundVolume(itemSound);
if (volume <= 0.0001f) { return; } if (volume <= 0.0001f) { return; }
loopingSound = itemSound;
loopingSoundChannel = loopingSound.RoundSound.Sound.Play( loopingSoundChannel = loopingSound.RoundSound.Sound.Play(
new Vector3(position.X, position.Y, 0.0f), new Vector3(position.X, position.Y, 0.0f),
0.01f, 0.01f,
@@ -3,11 +3,17 @@ using Barotrauma.Lights;
using Barotrauma.Networking; using Barotrauma.Networking;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic;
namespace Barotrauma.Items.Components namespace Barotrauma.Items.Components
{ {
partial class LightComponent : Powered, IServerSerializable, IDrawableComponent partial class LightComponent : Powered, IServerSerializable, IDrawableComponent
{ {
private bool? lastReceivedState;
private CoroutineHandle resetPredictionCoroutine;
private float resetPredictionTimer;
public Vector2 DrawSize public Vector2 DrawSize
{ {
get { return new Vector2(light.Range * 2, light.Range * 2); } get { return new Vector2(light.Range * 2, light.Range * 2); }
@@ -32,6 +38,12 @@ namespace Barotrauma.Items.Components
light.Color = LightColor.Multiply(brightness); light.Color = LightColor.Multiply(brightness);
} }
public override void OnItemLoaded()
{
base.OnItemLoaded();
SetLightSourceState(IsActive, lightBrightness);
}
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1) public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
{ {
if (light.LightSprite != null && (item.body == null || item.body.Enabled) && lightBrightness > 0.0f && IsOn) if (light.LightSprite != null && (item.body == null || item.body.Enabled) && lightBrightness > 0.0f && IsOn)
@@ -49,9 +61,36 @@ namespace Barotrauma.Items.Components
} }
} }
partial void OnStateChanged()
{
if (GameMain.Client == null || !lastReceivedState.HasValue) { return; }
//reset to last known server state after the state hasn't changed in 1.0 seconds client-side
resetPredictionTimer = 1.0f;
if (resetPredictionCoroutine == null || !CoroutineManager.IsCoroutineRunning(resetPredictionCoroutine))
{
resetPredictionCoroutine = CoroutineManager.StartCoroutine(ResetPredictionAfterDelay());
}
}
/// <summary>
/// Reset client-side prediction of the light's state to the last known state sent by the server after resetPredictionTimer runs out
/// </summary>
private IEnumerable<object> ResetPredictionAfterDelay()
{
while (resetPredictionTimer > 0.0f)
{
resetPredictionTimer -= CoroutineManager.DeltaTime;
yield return CoroutineStatus.Running;
}
if (lastReceivedState.HasValue) { IsActive = lastReceivedState.Value; }
resetPredictionCoroutine = null;
yield return CoroutineStatus.Success;
}
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{ {
IsOn = msg.ReadBoolean(); IsActive = msg.ReadBoolean();
lastReceivedState = IsActive;
} }
protected override void RemoveComponentSpecific() protected override void RemoveComponentSpecific()
@@ -140,11 +140,11 @@ namespace Barotrauma.Items.Components
var inputArea = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 1f), bottomFrame.RectTransform, Anchor.BottomCenter), isHorizontal: true, childAnchor: Anchor.BottomLeft); var inputArea = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 1f), bottomFrame.RectTransform, Anchor.BottomCenter), isHorizontal: true, childAnchor: Anchor.BottomLeft);
// === INPUT SLOTS === // // === INPUT SLOTS === //
inputInventoryHolder = new GUIFrame(new RectTransform(new Vector2(0.8f, 1f), inputArea.RectTransform), style: null); inputInventoryHolder = new GUIFrame(new RectTransform(new Vector2(0.7f, 1f), inputArea.RectTransform), style: null);
new GUICustomComponent(new RectTransform(Vector2.One, inputInventoryHolder.RectTransform), DrawInputOverLay) { CanBeFocused = false }; new GUICustomComponent(new RectTransform(Vector2.One, inputInventoryHolder.RectTransform), DrawInputOverLay) { CanBeFocused = false };
// === ACTIVATE BUTTON === // // === ACTIVATE BUTTON === //
var buttonFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.2f, 0.8f), inputArea.RectTransform), childAnchor: Anchor.CenterRight); var buttonFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.3f, 0.8f), inputArea.RectTransform), childAnchor: Anchor.CenterRight);
activateButton = new GUIButton(new RectTransform(new Vector2(1f, 0.6f), buttonFrame.RectTransform), activateButton = new GUIButton(new RectTransform(new Vector2(1f, 0.6f), buttonFrame.RectTransform),
TextManager.Get("FabricatorCreate"), style: "DeviceButton") TextManager.Get("FabricatorCreate"), style: "DeviceButton")
{ {
@@ -154,7 +154,7 @@ namespace Barotrauma.Items.Components
}; };
// === POWER WARNING === // // === POWER WARNING === //
inSufficientPowerWarning = new GUITextBlock(new RectTransform(Vector2.One, activateButton.RectTransform), inSufficientPowerWarning = new GUITextBlock(new RectTransform(Vector2.One, activateButton.RectTransform),
TextManager.Get("FabricatorNoPower"), textColor: GUI.Style.Orange, textAlignment: Alignment.Center, color: Color.Black, style: "OuterGlow") TextManager.Get("FabricatorNoPower"), textColor: GUI.Style.Orange, textAlignment: Alignment.Center, color: Color.Black, style: "OuterGlow", wrap: true)
{ {
HoverColor = Color.Black, HoverColor = Color.Black,
IgnoreLayoutGroups = true, IgnoreLayoutGroups = true,
@@ -356,6 +356,8 @@ namespace Barotrauma.Items.Components
private void DrawOutputOverLay(SpriteBatch spriteBatch, GUICustomComponent overlayComponent) private void DrawOutputOverLay(SpriteBatch spriteBatch, GUICustomComponent overlayComponent)
{ {
overlayComponent.RectTransform.SetAsLastChild(); overlayComponent.RectTransform.SetAsLastChild();
if (outputContainer.Inventory.Items.First() != null) { return; }
FabricationRecipe targetItem = fabricatedItem ?? selectedItem; FabricationRecipe targetItem = fabricatedItem ?? selectedItem;
if (targetItem != null) if (targetItem != null)
@@ -584,15 +586,20 @@ namespace Barotrauma.Items.Components
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{ {
FabricatorState newState = (FabricatorState)msg.ReadByte();
float newTimeUntilReady = msg.ReadSingle();
int itemIndex = msg.ReadRangedInteger(-1, fabricationRecipes.Count - 1); int itemIndex = msg.ReadRangedInteger(-1, fabricationRecipes.Count - 1);
UInt16 userID = msg.ReadUInt16(); UInt16 userID = msg.ReadUInt16();
Character user = Entity.FindEntityByID(userID) as Character; Character user = Entity.FindEntityByID(userID) as Character;
if (itemIndex == -1 || user == null) State = newState;
timeUntilReady = newTimeUntilReady;
if (newState == FabricatorState.Stopped || itemIndex == -1 || user == null)
{ {
CancelFabricating(); CancelFabricating();
} }
else else if (newState == FabricatorState.Active || newState == FabricatorState.Paused)
{ {
//if already fabricating the selected item, return //if already fabricating the selected item, return
if (fabricatedItem != null && fabricationRecipes.IndexOf(fabricatedItem) == itemIndex) { return; } if (fabricatedItem != null && fabricationRecipes.IndexOf(fabricatedItem) == itemIndex) { return; }
@@ -129,8 +129,8 @@ namespace Barotrauma.Items.Components
{ {
if (child.UserData is Hull hull) if (child.UserData is Hull hull)
{ {
if (hull.Submarine == null || !hull.Submarine.IsOutpost) { continue; } if (hull.Submarine == null || !hull.Submarine.Info.IsOutpost) { continue; }
string text = TextManager.GetWithVariable("MiniMapOutpostDockingInfo", "[outpost]", hull.Submarine.Name); string text = TextManager.GetWithVariable("MiniMapOutpostDockingInfo", "[outpost]", hull.Submarine.Info.Name);
Vector2 textSize = GUI.Font.MeasureString(text); Vector2 textSize = GUI.Font.MeasureString(text);
Vector2 textPos = child.Center; Vector2 textPos = child.Center;
if (textPos.X + textSize.X / 2 > submarineContainer.Rect.Right) if (textPos.X + textSize.X / 2 > submarineContainer.Rect.Right)
@@ -99,6 +99,10 @@ namespace Barotrauma.Items.Components
Step = 0.05f, Step = 0.05f,
OnMoved = (GUIScrollBar scrollBar, float barScroll) => OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
{ {
if (pumpSpeedLockTimer <= 0.0f)
{
targetLevel = null;
}
float newValue = barScroll * 200.0f - 100.0f; float newValue = barScroll * 200.0f - 100.0f;
if (Math.Abs(newValue - FlowPercentage) < 0.1f) { return false; } if (Math.Abs(newValue - FlowPercentage) < 0.1f) { return false; }
@@ -490,7 +490,7 @@ namespace Barotrauma.Items.Components
disruptedDirections.Clear(); disruptedDirections.Clear();
foreach (AITarget t in AITarget.List) foreach (AITarget t in AITarget.List)
{ {
if (t.SoundRange <= 0.0f || !t.Enabled || float.IsNaN(t.SoundRange) || float.IsInfinity(t.SoundRange)) { continue; } if (t.SoundRange <= 0.0f || float.IsNaN(t.SoundRange) || float.IsInfinity(t.SoundRange)) { continue; }
float distSqr = Vector2.DistanceSquared(t.WorldPosition, transducerCenter); float distSqr = Vector2.DistanceSquared(t.WorldPosition, transducerCenter);
if (distSqr > t.SoundRange * t.SoundRange * 2) { continue; } if (distSqr > t.SoundRange * t.SoundRange * 2) { continue; }
@@ -647,6 +647,8 @@ namespace Barotrauma.Items.Components
if (GameMain.GameSession == null) { return; } if (GameMain.GameSession == null) { return; }
if (Level.Loaded == null) { return; }
DrawMarker(spriteBatch, DrawMarker(spriteBatch,
GameMain.GameSession.StartLocation.Name, GameMain.GameSession.StartLocation.Name,
"outpost", "outpost",
@@ -699,8 +701,8 @@ namespace Barotrauma.Items.Components
if (sub.WorldPosition.Y > Level.Loaded.Size.Y) { continue; } if (sub.WorldPosition.Y > Level.Loaded.Size.Y) { continue; }
DrawMarker(spriteBatch, DrawMarker(spriteBatch,
sub.Name, sub.Info.Name,
sub.HasTag(SubmarineTag.Shuttle) ? "shuttle" : "submarine", sub.Info.HasTag(SubmarineTag.Shuttle) ? "shuttle" : "submarine",
sub.WorldPosition - transducerCenter, sub.WorldPosition - transducerCenter,
displayScale, center, DisplayRadius * 0.95f); displayScale, center, DisplayRadius * 0.95f);
} }
@@ -46,6 +46,8 @@ namespace Barotrauma.Items.Components
private float checkConnectedPortsTimer; private float checkConnectedPortsTimer;
private const float CheckConnectedPortsInterval = 1.0f; private const float CheckConnectedPortsInterval = 1.0f;
public DockingPort ActiveDockingSource, DockingTarget;
private Vector2 keyboardInput = Vector2.Zero; private Vector2 keyboardInput = Vector2.Zero;
private float inputCumulation; private float inputCumulation;
@@ -665,7 +667,7 @@ namespace Barotrauma.Items.Components
if (Vector2.DistanceSquared(PlayerInput.MousePosition, steerArea.Rect.Center.ToVector2()) < steerRadius * steerRadius) if (Vector2.DistanceSquared(PlayerInput.MousePosition, steerArea.Rect.Center.ToVector2()) < steerRadius * steerRadius)
{ {
if (PlayerInput.PrimaryMouseButtonHeld() && !CrewManager.IsCommandInterfaceOpen) if (PlayerInput.PrimaryMouseButtonHeld() && !CrewManager.IsCommandInterfaceOpen && !GameSession.IsInfoFrameOpen)
{ {
Vector2 displaySubPos = (-sonar.DisplayOffset * sonar.Zoom) / sonar.Range * sonar.DisplayRadius * sonar.Zoom; Vector2 displaySubPos = (-sonar.DisplayOffset * sonar.Zoom) / sonar.Range * sonar.DisplayRadius * sonar.Zoom;
displaySubPos.Y = -displaySubPos.Y; displaySubPos.Y = -displaySubPos.Y;
@@ -106,11 +106,10 @@ namespace Barotrauma.Items.Components
{ {
if (indicatorSize.X <= 1.0f || indicatorSize.Y <= 1.0f) { return; } if (indicatorSize.X <= 1.0f || indicatorSize.Y <= 1.0f) { return; }
GUI.DrawRectangle(spriteBatch, Vector2 itemSize = new Vector2(item.Sprite.SourceRect.Width, item.Sprite.SourceRect.Height) * item.Scale;
new Vector2( Vector2 indicatorPos = -itemSize / 2 + indicatorPosition * item.Scale;
item.DrawPosition.X - item.Sprite.SourceRect.Width / 2 * item.Scale + indicatorPosition.X * item.Scale, if (item.FlippedX && item.Prefab.CanSpriteFlipX) { indicatorPos.X = -indicatorPos.X - indicatorSize.X * item.Scale; }
-item.DrawPosition.Y - item.Sprite.SourceRect.Height / 2 * item.Scale + indicatorPosition.Y * item.Scale), if (item.FlippedY && item.Prefab.CanSpriteFlipY) { indicatorPos.Y = -indicatorPos.Y - indicatorSize.Y * item.Scale; }
indicatorSize * item.Scale, Color.Black, depth: item.SpriteDepth - 0.00001f);
if (charge > 0) if (charge > 0)
{ {
@@ -118,25 +117,23 @@ namespace Barotrauma.Items.Components
if (!isHorizontal) if (!isHorizontal)
{ {
GUI.DrawRectangle(spriteBatch, GUI.DrawRectangle(spriteBatch,
new Vector2( new Vector2(item.DrawPosition.X, -item.DrawPosition.Y + ((indicatorSize.Y * item.Scale) * (1.0f - charge / capacity))) + indicatorPos,
item.DrawPosition.X - item.Sprite.SourceRect.Width / 2 * item.Scale + indicatorPosition.X * item.Scale + 1, new Vector2(indicatorSize.X * item.Scale, (indicatorSize.Y * item.Scale) * (charge / capacity)), indicatorColor, true,
-item.DrawPosition.Y - item.Sprite.SourceRect.Height / 2 * item.Scale + indicatorPosition.Y * item.Scale + 1 + ((indicatorSize.Y * item.Scale) * (1.0f - charge / capacity))), depth: item.SpriteDepth - 0.00001f);
new Vector2(indicatorSize.X * item.Scale - 2, (indicatorSize.Y * item.Scale - 2) * (charge / capacity)), indicatorColor, true,
depth: item.SpriteDepth - 0.00001f);
} }
else else
{ {
GUI.DrawRectangle(spriteBatch, GUI.DrawRectangle(spriteBatch,
new Vector2( new Vector2(item.DrawPosition.X, -item.DrawPosition.Y) + indicatorPos,
item.DrawPosition.X - item.Sprite.SourceRect.Width / 2 * item.Scale + indicatorPosition.X * item.Scale + 1 , new Vector2((indicatorSize.X * item.Scale) * (charge / capacity), indicatorSize.Y * item.Scale), indicatorColor, true,
-item.DrawPosition.Y - item.Sprite.SourceRect.Height / 2 * item.Scale + indicatorPosition.Y * item.Scale + 1), depth: item.SpriteDepth - 0.00001f);
new Vector2((indicatorSize.X * item.Scale - 2) * (charge / capacity), indicatorSize.Y * item.Scale - 2), indicatorColor, true,
depth: item.SpriteDepth - 0.00001f);
} }
} }
GUI.DrawRectangle(spriteBatch,
new Vector2(item.DrawPosition.X, -item.DrawPosition.Y) + indicatorPos,
indicatorSize * item.Scale, Color.Black, depth: item.SpriteDepth - 0.00001f);
} }
public void ClientWrite(IWriteMessage msg, object[] extraData) public void ClientWrite(IWriteMessage msg, object[] extraData)
{ {
msg.WriteRangedInteger((int)(rechargeSpeed / MaxRechargeSpeed * 10), 0, 10); msg.WriteRangedInteger((int)(rechargeSpeed / MaxRechargeSpeed * 10), 0, 10);
@@ -0,0 +1,66 @@
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
partial class Projectile : ItemComponent
{
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
bool isStuck = msg.ReadBoolean();
if (isStuck)
{
Vector2 simPosition = new Vector2(
msg.ReadSingle(),
msg.ReadSingle());
Vector2 axis = new Vector2(
msg.ReadSingle(),
msg.ReadSingle());
UInt16 entityID = msg.ReadUInt16();
Entity entity = Entity.FindEntityByID(entityID);
item.body.SetTransform(simPosition, item.body.Rotation);
if (entity is Character character)
{
byte limbIndex = msg.ReadByte();
if (limbIndex >= character.AnimController.Limbs.Length)
{
DebugConsole.ThrowError($"Failed to read a projectile update from the server. Limb index out of bounds ({limbIndex}, character: {character.ToString()})");
return;
}
var limb = character.AnimController.Limbs[limbIndex];
StickToTarget(limb.body.FarseerBody, axis);
}
else if (entity is Structure structure)
{
byte bodyIndex = msg.ReadByte();
if (bodyIndex >= structure.Bodies.Count)
{
DebugConsole.ThrowError($"Failed to read a projectile update from the server. Structure body index out of bounds ({bodyIndex}, structure: {structure.ToString()})");
return;
}
var body = structure.Bodies[bodyIndex];
StickToTarget(body, axis);
}
else if (entity is Item item)
{
StickToTarget(item.body.FarseerBody, axis);
}
else if (entity is Submarine sub)
{
StickToTarget(sub.PhysicsBody.FarseerBody, axis);
}
else
{
DebugConsole.ThrowError($"Failed to read a projectile update from the server. Invalid stick target ({entity?.ToString() ?? "null"}, {entityID})");
}
}
else
{
Unstick();
}
}
}
}
@@ -236,15 +236,7 @@ namespace Barotrauma.Items.Components
DeteriorateAlways = msg.ReadBoolean(); DeteriorateAlways = msg.ReadBoolean();
ushort currentFixerID = msg.ReadUInt16(); ushort currentFixerID = msg.ReadUInt16();
currentFixerAction = (FixActions)msg.ReadRangedInteger(0, 2); currentFixerAction = (FixActions)msg.ReadRangedInteger(0, 2);
CurrentFixer = currentFixerID != 0 ? Entity.FindEntityByID(currentFixerID) as Character : null;
if (currentFixerID == 0)
{
CurrentFixer = null;
}
else
{
CurrentFixer = Entity.FindEntityByID(currentFixerID) as Character;
}
} }
public void ClientWrite(IWriteMessage msg, object[] extraData = null) public void ClientWrite(IWriteMessage msg, object[] extraData = null)
@@ -0,0 +1,163 @@
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
partial class Rope : ItemComponent, IDrawableComponent
{
private Sprite sprite, startSprite, endSprite;
[Serialize(5, false)]
public int SpriteWidth
{
get;
set;
}
[Serialize("255,255,255,255", false)]
public Color SpriteColor
{
get;
set;
}
[Serialize(false, false)]
public bool Tile
{
get;
set;
}
public Vector2 DrawSize
{
get
{
if (target == null || source == null) { return Vector2.Zero; }
return new Vector2(
Math.Abs(target.DrawPosition.X - source.DrawPosition.X),
Math.Abs(target.DrawPosition.Y - source.DrawPosition.Y)) * 1.5f;
}
}
partial void InitProjSpecific(XElement element)
{
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "sprite":
sprite = new Sprite(subElement);
break;
case "startsprite":
startSprite = new Sprite(subElement);
break;
case "endsprite":
endSprite = new Sprite(subElement);
break;
}
}
}
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
{
if (target == null) { return; }
Vector2 startPos = new Vector2(source.DrawPosition.X, -source.DrawPosition.Y);
Vector2 endPos = new Vector2(target.DrawPosition.X, -target.DrawPosition.Y);
if (Snapped)
{
float snapState = 1.0f - snapTimer / SnapAnimDuration;
Vector2 diff = target.DrawPosition - source.DrawPosition;
diff.Y = -diff.Y;
int width = (int)(SpriteWidth * snapState);
if (width > 0.0f)
{
DrawRope(spriteBatch, endPos - diff * snapState * 0.5f, endPos, width);
DrawRope(spriteBatch, startPos, startPos + diff * snapState * 0.5f, width);
}
}
else
{
DrawRope(spriteBatch, startPos, endPos, SpriteWidth);
}
if (startSprite != null || endSprite != null)
{
Vector2 dir = endPos - startPos;
float angle = (float)Math.Atan2(dir.Y, dir.X);
if (startSprite != null)
{
float depth = Math.Min(item.GetDrawDepth() + (startSprite.Depth - item.Sprite.Depth), 0.999f);
startSprite?.Draw(spriteBatch, startPos, SpriteColor, angle, depth: depth);
}
if (endSprite != null)
{
float depth = Math.Min(item.GetDrawDepth() + (endSprite.Depth - item.Sprite.Depth), 0.999f);
endSprite?.Draw(spriteBatch, endPos, SpriteColor, angle, depth: depth);
}
}
}
private void DrawRope(SpriteBatch spriteBatch, Vector2 startPos, Vector2 endPos, int width)
{
float depth = sprite == null ?
item.Sprite.Depth + 0.001f :
Math.Min(item.GetDrawDepth() + (sprite.Depth - item.Sprite.Depth), 0.999f);
if (sprite?.Texture == null)
{
GUI.DrawLine(spriteBatch,
startPos,
endPos,
SpriteColor, depth: depth, width: width);
return;
}
if (Tile)
{
float length = Vector2.Distance(startPos, endPos);
Vector2 dir = (endPos - startPos) / length;
float x;
for (x = 0.0f; x <= length - sprite.size.X; x += sprite.size.X)
{
GUI.DrawLine(spriteBatch, sprite,
startPos + dir * (x - 5.0f),
startPos + dir * (x + sprite.size.X),
SpriteColor, depth: depth, width: width);
}
float leftOver = length - x;
if (leftOver > 0.0f)
{
GUI.DrawLine(spriteBatch, sprite,
startPos + dir * (x - 5.0f),
endPos,
SpriteColor, depth: depth, width: width);
}
}
else
{
GUI.DrawLine(spriteBatch, sprite,
startPos,
endPos,
SpriteColor, depth: depth, width: width);
}
}
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
snapped = msg.ReadBoolean();
}
protected override void RemoveComponentSpecific()
{
sprite?.Remove(); sprite = null;
startSprite?.Remove(); startSprite = null;
endSprite?.Remove(); endSprite = null;
}
}
}
@@ -45,13 +45,48 @@ namespace Barotrauma.Items.Components
float elementSize = Math.Min(1.0f / visibleElements.Count(), 1); float elementSize = Math.Min(1.0f / visibleElements.Count(), 1);
foreach (CustomInterfaceElement ciElement in visibleElements) foreach (CustomInterfaceElement ciElement in visibleElements)
{ {
if (ciElement.ContinuousSignal) if (!string.IsNullOrEmpty(ciElement.PropertyName))
{
var layoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, elementSize), uiElementContainer.RectTransform), isHorizontal: true)
{
RelativeSpacing = 0.02f,
UserData = ciElement
};
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), layoutGroup.RectTransform),
TextManager.Get(ciElement.Label, returnNull: true) ?? ciElement.Label);
var textBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), layoutGroup.RectTransform), "", style: "GUITextBoxNoIcon")
{
OverflowClip = true,
UserData = ciElement
};
//reset size restrictions set by the Style to make sure the elements can fit the interface
textBox.RectTransform.MinSize = textBox.Frame.RectTransform.MinSize = new Point(0, 0);
textBox.RectTransform.MaxSize = textBox.Frame.RectTransform.MaxSize = new Point(int.MaxValue, int.MaxValue);
textBox.OnDeselected += (tb, key) =>
{
if (GameMain.Client == null)
{
TextChanged(tb.UserData as CustomInterfaceElement, textBox.Text);
}
else
{
item.CreateClientEvent(this);
}
};
textBox.OnEnterPressed += (tb, text) =>
{
tb.Deselect();
return true;
};
uiElements.Add(textBox);
}
else if (ciElement.ContinuousSignal)
{ {
var tickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, elementSize), uiElementContainer.RectTransform) var tickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, elementSize), uiElementContainer.RectTransform)
{ {
MaxSize = ElementMaxSize MaxSize = ElementMaxSize
}, }, TextManager.Get(ciElement.Label, returnNull: true) ?? ciElement.Label)
TextManager.Get(ciElement.Label, returnNull: true) ?? ciElement.Label)
{ {
UserData = ciElement UserData = ciElement
}; };
@@ -148,7 +183,7 @@ namespace Barotrauma.Items.Components
foreach (var uiElement in uiElements) foreach (var uiElement in uiElements)
{ {
if (!(uiElement.UserData is CustomInterfaceElement element)) { continue; } if (!(uiElement.UserData is CustomInterfaceElement element)) { continue; }
bool visible = Screen.Selected == GameMain.SubEditorScreen || element.StatusEffects.Any() || (element.Connection != null && element.Connection.Wires.Any(w => w != null)); bool visible = Screen.Selected == GameMain.SubEditorScreen || element.StatusEffects.Any() || !string.IsNullOrEmpty(element.PropertyName) || (element.Connection != null && element.Connection.Wires.Any(w => w != null));
if (visible) { visibleElementCount++; } if (visible) { visibleElementCount++; }
if (uiElement.Visible != visible) if (uiElement.Visible != visible)
{ {
@@ -188,6 +223,22 @@ namespace Barotrauma.Items.Components
customInterfaceElementList[i].Label; customInterfaceElementList[i].Label;
tickBox.TextBlock.Wrap = tickBox.Text.Contains(' '); tickBox.TextBlock.Wrap = tickBox.Text.Contains(' ');
} }
if (uiElements[i] is GUITextBox textBox)
{
var textBlock = textBox.Parent.GetChild<GUITextBlock>();
textBlock.Text = string.IsNullOrWhiteSpace(customInterfaceElementList[i].Label) ?
TextManager.GetWithVariable("connection.signaloutx", "[num]", (i + 1).ToString()) :
customInterfaceElementList[i].Label;
textBlock.Wrap = textBlock.Text.Contains(' ');
foreach (ISerializableEntity e in item.AllPropertyObjects)
{
if (e.SerializableProperties.ContainsKey(customInterfaceElementList[i].PropertyName))
{
textBox.Text = e.SerializableProperties[customInterfaceElementList[i].PropertyName].GetValue(e) as string;
}
}
}
} }
uiElementContainer.Recalculate(); uiElementContainer.Recalculate();
@@ -206,6 +257,10 @@ namespace Barotrauma.Items.Components
{ {
textBlocks.Add(tickBox.TextBlock); textBlocks.Add(tickBox.TextBlock);
} }
else if (element is GUILayoutGroup)
{
textBlocks.Add(element.GetChild<GUITextBlock>());
}
} }
uiElementContainer.Recalculate(); uiElementContainer.Recalculate();
GUITextBlock.AutoScaleAndNormalize(textBlocks); GUITextBlock.AutoScaleAndNormalize(textBlocks);
@@ -216,7 +271,11 @@ namespace Barotrauma.Items.Components
//extradata contains an array of buttons clicked by the player (or nothing if the player didn't click anything) //extradata contains an array of buttons clicked by the player (or nothing if the player didn't click anything)
for (int i = 0; i < customInterfaceElementList.Count; i++) for (int i = 0; i < customInterfaceElementList.Count; i++)
{ {
if (customInterfaceElementList[i].ContinuousSignal) if (!string.IsNullOrEmpty(customInterfaceElementList[i].PropertyName))
{
msg.Write(((GUITextBox)uiElements[i]).Text);
}
else if (customInterfaceElementList[i].ContinuousSignal)
{ {
msg.Write(((GUITickBox)uiElements[i]).Selected); msg.Write(((GUITickBox)uiElements[i]).Selected);
} }
@@ -231,15 +290,22 @@ namespace Barotrauma.Items.Components
{ {
for (int i = 0; i < customInterfaceElementList.Count; i++) for (int i = 0; i < customInterfaceElementList.Count; i++)
{ {
bool elementState = msg.ReadBoolean(); if (!string.IsNullOrEmpty(customInterfaceElementList[i].PropertyName))
if (customInterfaceElementList[i].ContinuousSignal)
{ {
((GUITickBox)uiElements[i]).Selected = elementState; TextChanged(customInterfaceElementList[i], msg.ReadString());
TickBoxToggled(customInterfaceElementList[i], elementState);
} }
else if (elementState) else
{ {
ButtonClicked(customInterfaceElementList[i]); bool elementState = msg.ReadBoolean();
if (customInterfaceElementList[i].ContinuousSignal)
{
((GUITickBox)uiElements[i]).Selected = elementState;
TickBoxToggled(customInterfaceElementList[i], elementState);
}
else if (elementState)
{
ButtonClicked(customInterfaceElementList[i]);
}
} }
} }
} }
@@ -20,10 +20,10 @@ namespace Barotrauma.Items.Components
public void Draw(SpriteBatch spriteBatch, Wire wire, Color color, Vector2 offset, float depth, float width = 0.3f) public void Draw(SpriteBatch spriteBatch, Wire wire, Color color, Vector2 offset, float depth, float width = 0.3f)
{ {
spriteBatch.Draw(wire.wireSprite.Texture, spriteBatch.Draw(wire.wireSprite.Texture,
new Vector2(start.X + offset.X, -(start.Y + offset.Y)), null, color, new Vector2(start.X + offset.X, -(start.Y + offset.Y)), wire.wireSprite.SourceRect, color,
-angle, -angle,
new Vector2(0.0f, wire.wireSprite.size.Y / 2.0f), new Vector2(0.0f, wire.wireSprite.size.Y / 2.0f),
new Vector2(length / wire.wireSprite.Texture.Width, width), new Vector2(length / wire.wireSprite.size.X, width),
SpriteEffects.None, SpriteEffects.None,
depth); depth);
} }
@@ -34,10 +34,10 @@ namespace Barotrauma.Items.Components
end.Y = -end.Y; end.Y = -end.Y;
spriteBatch.Draw(wire.wireSprite.Texture, spriteBatch.Draw(wire.wireSprite.Texture,
start, null, color, start, wire.wireSprite.SourceRect, color,
MathUtils.VectorToAngle(end - start), MathUtils.VectorToAngle(end - start),
new Vector2(0.0f, wire.wireSprite.size.Y / 2.0f), new Vector2(0.0f, wire.wireSprite.size.Y / 2.0f),
new Vector2((Vector2.Distance(start, end)) / wire.wireSprite.Texture.Width, width), new Vector2((Vector2.Distance(start, end)) / wire.wireSprite.size.X, width),
SpriteEffects.None, SpriteEffects.None,
depth); depth);
} }
@@ -50,6 +50,13 @@ namespace Barotrauma.Items.Components
private static int? selectedNodeIndex; private static int? selectedNodeIndex;
private static int? highlightedNodeIndex; private static int? highlightedNodeIndex;
[Serialize(0.3f, false)]
public float Width
{
get;
set;
}
public Vector2 DrawSize public Vector2 DrawSize
{ {
get { return sectionExtents; } get { return sectionExtents; }
@@ -72,7 +79,7 @@ namespace Barotrauma.Items.Components
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
{ {
if (subElement.Name.ToString().ToLowerInvariant() == "wiresprite") if (subElement.Name.ToString().Equals("wiresprite", StringComparison.OrdinalIgnoreCase))
{ {
overrideSprite = new Sprite(subElement); overrideSprite = new Sprite(subElement);
break; break;
@@ -109,20 +116,20 @@ namespace Barotrauma.Items.Components
{ {
foreach (WireSection section in sections) foreach (WireSection section in sections)
{ {
section.Draw(spriteBatch, this, Screen.Selected == GameMain.GameScreen ? higlightColor : editorHighlightColor, drawOffset, depth + 0.00001f, 0.7f); section.Draw(spriteBatch, this, Screen.Selected == GameMain.GameScreen ? higlightColor : editorHighlightColor, drawOffset, depth + 0.00001f, Width * 2.0f);
} }
} }
else if (item.IsSelected) else if (item.IsSelected)
{ {
foreach (WireSection section in sections) foreach (WireSection section in sections)
{ {
section.Draw(spriteBatch, this, editorSelectedColor, drawOffset, depth + 0.00001f, 0.7f); section.Draw(spriteBatch, this, editorSelectedColor, drawOffset, depth + 0.00001f, Width * 2.0f);
} }
} }
foreach (WireSection section in sections) foreach (WireSection section in sections)
{ {
section.Draw(spriteBatch, this, item.Color, drawOffset, depth, 0.3f); section.Draw(spriteBatch, this, item.Color, drawOffset, depth, Width);
} }
if (nodes.Count > 0) if (nodes.Count > 0)
@@ -167,13 +174,13 @@ namespace Barotrauma.Items.Components
spriteBatch, this, spriteBatch, this,
new Vector2(nodes[nodes.Count - 1].X, nodes[nodes.Count - 1].Y) + drawOffset, new Vector2(nodes[nodes.Count - 1].X, nodes[nodes.Count - 1].Y) + drawOffset,
new Vector2(newNodePos.X, newNodePos.Y) + drawOffset, new Vector2(newNodePos.X, newNodePos.Y) + drawOffset,
item.Color, 0.0f, 0.3f); item.Color, 0.0f, Width);
WireSection.Draw( WireSection.Draw(
spriteBatch, this, spriteBatch, this,
new Vector2(newNodePos.X, newNodePos.Y) + drawOffset, new Vector2(newNodePos.X, newNodePos.Y) + drawOffset,
item.DrawPosition, item.DrawPosition,
item.Color, itemDepth, 0.3f); item.Color, itemDepth, Width);
GUI.DrawRectangle(spriteBatch, new Vector2(newNodePos.X + drawOffset.X, -(newNodePos.Y + drawOffset.Y)) - Vector2.One * 3, Vector2.One * 6, item.Color); GUI.DrawRectangle(spriteBatch, new Vector2(newNodePos.X + drawOffset.X, -(newNodePos.Y + drawOffset.Y)) - Vector2.One * 3, Vector2.One * 6, item.Color);
} }
@@ -183,7 +190,7 @@ namespace Barotrauma.Items.Components
spriteBatch, this, spriteBatch, this,
new Vector2(nodes[nodes.Count - 1].X, nodes[nodes.Count - 1].Y) + drawOffset, new Vector2(nodes[nodes.Count - 1].X, nodes[nodes.Count - 1].Y) + drawOffset,
item.DrawPosition, item.DrawPosition,
item.Color, 0.0f, 0.3f); item.Color, 0.0f, Width);
} }
} }
} }
@@ -142,7 +142,7 @@ namespace Barotrauma.Items.Components
private void DrawCharacterInfo(SpriteBatch spriteBatch, Character target, float alpha = 1.0f) private void DrawCharacterInfo(SpriteBatch spriteBatch, Character target, float alpha = 1.0f)
{ {
Vector2 hudPos = GameMain.GameScreen.Cam.WorldToScreen(target.WorldPosition); Vector2 hudPos = GameMain.GameScreen.Cam.WorldToScreen(target.DrawPosition);
hudPos += Vector2.UnitX * 50.0f; hudPos += Vector2.UnitX * 50.0f;
List<string> texts = new List<string>(); List<string> texts = new List<string>();
@@ -223,11 +223,14 @@ namespace Barotrauma.Items.Components
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1) public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
{ {
Vector2 drawPos = new Vector2(item.Rect.X + transformedBarrelPos.X, item.Rect.Y - transformedBarrelPos.Y); Vector2 drawPos = new Vector2(item.Rect.X + transformedBarrelPos.X, item.Rect.Y - transformedBarrelPos.Y);
if (item.Submarine != null) drawPos += item.Submarine.DrawPosition; if (item.Submarine != null)
{
drawPos += item.Submarine.DrawPosition;
}
drawPos.Y = -drawPos.Y; drawPos.Y = -drawPos.Y;
float recoilOffset = 0.0f; float recoilOffset = 0.0f;
if (RecoilDistance > 0.0f && recoilTimer > 0.0f) if (Math.Abs(RecoilDistance) > 0.0f && recoilTimer > 0.0f)
{ {
//move the barrel backwards 0.1 seconds after launching //move the barrel backwards 0.1 seconds after launching
if (recoilTimer >= Math.Max(Reload, 0.1f) - 0.1f) if (recoilTimer >= Math.Max(Reload, 0.1f) - 0.1f)
@@ -369,13 +372,26 @@ namespace Barotrauma.Items.Components
tooltipOffset = new Vector2(size / 2 + 5, -10), tooltipOffset = new Vector2(size / 2 + 5, -10),
inputAreaMargin = 20, inputAreaMargin = 20,
RequireMouseOn = false RequireMouseOn = false
}; };
widgets.Add(id, widget); widgets.Add(id, widget);
initMethod?.Invoke(widget); initMethod?.Invoke(widget);
} }
return widget; return widget;
} }
private void GetAvailablePower(out float availableCharge, out float availableCapacity)
{
var batteries = item.GetConnectedComponents<PowerContainer>();
availableCharge = 0.0f;
availableCapacity = 0.0f;
foreach (PowerContainer battery in batteries)
{
availableCharge += battery.Charge;
availableCapacity += battery.Capacity;
}
}
/// <summary> /// <summary>
/// Returns correct angle between -2PI and +2PI /// Returns correct angle between -2PI and +2PI
/// </summary> /// </summary>
@@ -488,11 +504,17 @@ namespace Barotrauma.Items.Components
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{ {
UInt16 projectileID = msg.ReadUInt16(); UInt16 projectileID = msg.ReadUInt16();
//projectile removed, do nothing float newTargetRotation = msg.ReadRangedSingle(minRotation, maxRotation, 8);
if (projectileID == 0) return;
Item projectile = Entity.FindEntityByID(projectileID) as Item; if (Character.Controlled == null || user != Character.Controlled)
if (projectile == null) {
targetRotation = newTargetRotation;
}
//projectile removed, do nothing
if (projectileID == 0) { return; }
if (!(Entity.FindEntityByID(projectileID) is Item projectile))
{ {
DebugConsole.ThrowError("Failed to launch a projectile - item with the ID \"" + projectileID + " not found"); DebugConsole.ThrowError("Failed to launch a projectile - item with the ID \"" + projectileID + " not found");
return; return;
@@ -150,9 +150,9 @@ namespace Barotrauma.Items.Components
if (joint == null) if (joint == null)
{ {
string errorMsg = "Error while reading a docking port network event (Dock method did not create a joint between the ports)." + string errorMsg = "Error while reading a docking port network event (Dock method did not create a joint between the ports)." +
" Submarine: " + (item.Submarine?.Name ?? "null") + " Submarine: " + (item.Submarine?.Info.Name ?? "null") +
", target submarine: " + (DockingTarget.item.Submarine?.Name ?? "null"); ", target submarine: " + (DockingTarget.item.Submarine?.Info.Name ?? "null");
if (item.Submarine?.DockedTo.Contains(DockingTarget.item.Submarine) ?? false) if (item.Submarine?.ConnectedDockingPorts.ContainsKey(DockingTarget.item.Submarine) ?? false)
{ {
errorMsg += "\nAlready docked."; errorMsg += "\nAlready docked.";
} }
@@ -45,12 +45,17 @@ namespace Barotrauma
public float QuickUseTimer; public float QuickUseTimer;
public string QuickUseButtonToolTip; public string QuickUseButtonToolTip;
public bool IsMoving = false;
private static Rectangle offScreenRect = new Rectangle(new Point(-1000, 0), Point.Zero);
public GUIComponent.ComponentState EquipButtonState; public GUIComponent.ComponentState EquipButtonState;
public Rectangle EquipButtonRect public Rectangle EquipButtonRect
{ {
get get
{ {
// Returns a point off-screen, Rectangle.Empty places buttons in the top left of the screen
if (IsMoving) return offScreenRect;
int buttonDir = Math.Sign(SubInventoryDir); int buttonDir = Math.Sign(SubInventoryDir);
float sizeY = Inventory.UnequippedIndicator.size.Y * Inventory.UIScale * Inventory.IndicatorScaleAdjustment; float sizeY = Inventory.UnequippedIndicator.size.Y * Inventory.UIScale * Inventory.IndicatorScaleAdjustment;
@@ -284,6 +289,7 @@ namespace Barotrauma
} }
protected static HashSet<SlotReference> highlightedSubInventorySlots = new HashSet<SlotReference>(); protected static HashSet<SlotReference> highlightedSubInventorySlots = new HashSet<SlotReference>();
private static List<SlotReference> subInventorySlotsToDraw = new List<SlotReference>();
protected static SlotReference selectedSlot; protected static SlotReference selectedSlot;
@@ -1048,11 +1054,14 @@ namespace Barotrauma
return hoverArea; return hoverArea;
} }
public static void DrawFront(SpriteBatch spriteBatch) public static void DrawFront(SpriteBatch spriteBatch)
{ {
if (GUI.PauseMenuOpen || GUI.SettingsMenuOpen) return; if (GUI.PauseMenuOpen || GUI.SettingsMenuOpen) { return; }
foreach (var slot in highlightedSubInventorySlots) subInventorySlotsToDraw.Clear();
subInventorySlotsToDraw.AddRange(highlightedSubInventorySlots);
foreach (var slot in subInventorySlotsToDraw)
{ {
int slotIndex = Array.IndexOf(slot.ParentInventory.slots, slot.Slot); int slotIndex = Array.IndexOf(slot.ParentInventory.slots, slot.Slot);
if (slotIndex > -1 && slotIndex < slot.ParentInventory.slots.Length) if (slotIndex > -1 && slotIndex < slot.ParentInventory.slots.Length)
@@ -1136,11 +1145,11 @@ 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; slotColor = slot.IsHighlighted ? GUI.Style.EquipmentSlotColor : GUI.Style.EquipmentSlotColor * 0.8f;
} }
else else
{ {
slotColor = slot.IsHighlighted ? GUIColorSettings.InventorySlotColor : GUIColorSettings.InventorySlotColor * 0.8f; slotColor = slot.IsHighlighted ? GUI.Style.InventorySlotColor : GUI.Style.InventorySlotColor * 0.8f;
}*/ }*/
if (inventory != null && inventory.Locked) { slotColor = Color.Gray * 0.5f; } if (inventory != null && inventory.Locked) { slotColor = Color.Gray * 0.5f; }
@@ -1199,13 +1208,15 @@ namespace Barotrauma
dir < 0 ? rect.Bottom + HUDLayoutSettings.Padding / 2 : rect.Y - HUDLayoutSettings.Padding / 2 - ContainedIndicatorHeight, rect.Width, ContainedIndicatorHeight); dir < 0 ? rect.Bottom + HUDLayoutSettings.Padding / 2 : rect.Y - HUDLayoutSettings.Padding / 2 - ContainedIndicatorHeight, rect.Width, ContainedIndicatorHeight);
containedIndicatorArea.Inflate(-4, 0); containedIndicatorArea.Inflate(-4, 0);
Color backgroundColor = GUI.Style.ColorInventoryBackground;
if (itemContainer.ContainedStateIndicator?.Texture == null) if (itemContainer.ContainedStateIndicator?.Texture == null)
{ {
containedIndicatorArea.Inflate(0, -2); containedIndicatorArea.Inflate(0, -2);
GUI.DrawRectangle(spriteBatch, containedIndicatorArea, Color.Gray * 0.9f, true); GUI.DrawRectangle(spriteBatch, containedIndicatorArea, backgroundColor, true);
GUI.DrawRectangle(spriteBatch, GUI.DrawRectangle(spriteBatch,
new Rectangle(containedIndicatorArea.X, containedIndicatorArea.Y, (int)(containedIndicatorArea.Width * containedState), containedIndicatorArea.Height), new Rectangle(containedIndicatorArea.X, containedIndicatorArea.Y, (int)(containedIndicatorArea.Width * containedState), containedIndicatorArea.Height),
ToolBox.GradientLerp(containedState, Color.Red, Color.Orange, Color.LightGreen) * 0.8f, true); ToolBox.GradientLerp(containedState, GUI.Style.ColorInventoryEmpty, GUI.Style.ColorInventoryHalf, GUI.Style.ColorInventoryFull) * 0.8f, true);
GUI.DrawLine(spriteBatch, GUI.DrawLine(spriteBatch,
new Vector2(containedIndicatorArea.X + (int)(containedIndicatorArea.Width * containedState), containedIndicatorArea.Y), new Vector2(containedIndicatorArea.X + (int)(containedIndicatorArea.Width * containedState), containedIndicatorArea.Y),
new Vector2(containedIndicatorArea.X + (int)(containedIndicatorArea.Width * containedState), containedIndicatorArea.Bottom), new Vector2(containedIndicatorArea.X + (int)(containedIndicatorArea.Width * containedState), containedIndicatorArea.Bottom),
@@ -1224,12 +1235,12 @@ namespace Barotrauma
} }
indicatorSprite.Draw(spriteBatch, containedIndicatorArea.Center.ToVector2(), indicatorSprite.Draw(spriteBatch, containedIndicatorArea.Center.ToVector2(),
(inventory != null && inventory.Locked) ? Color.Gray * 0.5f : Color.Gray * 0.9f, (inventory != null && inventory.Locked) ? backgroundColor * 0.5f : backgroundColor,
origin: indicatorSprite.size / 2, origin: indicatorSprite.size / 2,
rotate: 0.0f, rotate: 0.0f,
scale: indicatorScale); scale: indicatorScale);
Color indicatorColor = ToolBox.GradientLerp(containedState, Color.Red, Color.Orange, Color.LightGreen); Color indicatorColor = ToolBox.GradientLerp(containedState, GUI.Style.ColorInventoryEmpty, GUI.Style.ColorInventoryHalf, GUI.Style.ColorInventoryFull);
if (inventory != null && inventory.Locked) { indicatorColor *= 0.5f; } if (inventory != null && inventory.Locked) { indicatorColor *= 0.5f; }
spriteBatch.Draw(indicatorSprite.Texture, containedIndicatorArea.Center.ToVector2(), spriteBatch.Draw(indicatorSprite.Texture, containedIndicatorArea.Center.ToVector2(),
@@ -29,14 +29,8 @@ namespace Barotrauma
private bool editingHUDRefreshPending; private bool editingHUDRefreshPending;
private float editingHUDRefreshTimer; private float editingHUDRefreshTimer;
class SpriteState
{
public float RotationState;
public float OffsetState;
public bool IsActive = true;
}
private Dictionary<DecorativeSprite, SpriteState> spriteAnimState = new Dictionary<DecorativeSprite, SpriteState>(); private readonly Dictionary<DecorativeSprite, DecorativeSprite.State> spriteAnimState = new Dictionary<DecorativeSprite, DecorativeSprite.State>();
private Sprite activeSprite; private Sprite activeSprite;
public override Sprite Sprite public override Sprite Sprite
@@ -160,7 +154,7 @@ namespace Barotrauma
foreach (var decorativeSprite in ((ItemPrefab)prefab).DecorativeSprites) foreach (var decorativeSprite in ((ItemPrefab)prefab).DecorativeSprites)
{ {
decorativeSprite.Sprite.EnsureLazyLoaded(); decorativeSprite.Sprite.EnsureLazyLoaded();
spriteAnimState.Add(decorativeSprite, new SpriteState()); spriteAnimState.Add(decorativeSprite, new DecorativeSprite.State());
} }
} }
@@ -197,8 +191,8 @@ namespace Barotrauma
public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true) public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true)
{ {
if (!Visible || (!editing && HiddenInGame)) return; if (!Visible || (!editing && HiddenInGame)) { return; }
if (editing && !ShowItems) return; if (editing && !ShowItems) { return; }
Color color = IsHighlighted && !GUI.DisableItemHighlights && Screen.Selected != GameMain.GameScreen ? GUI.Style.Orange : GetSpriteColor(); Color color = IsHighlighted && !GUI.DisableItemHighlights && Screen.Selected != GameMain.GameScreen ? GUI.Style.Orange : GetSpriteColor();
//if (IsSelected && editing) color = Color.Lerp(color, Color.Gold, 0.5f); //if (IsSelected && editing) color = Color.Lerp(color, Color.Gold, 0.5f);
@@ -333,15 +327,6 @@ namespace Barotrauma
if (GameMain.DebugDraw) if (GameMain.DebugDraw)
{ {
aiTarget?.Draw(spriteBatch);
var containedItems = ContainedItems;
if (containedItems != null)
{
foreach (Item item in containedItems)
{
item.AiTarget?.Draw(spriteBatch);
}
}
if (body != null) if (body != null)
{ {
body.DebugDraw(spriteBatch, Color.White); body.DebugDraw(spriteBatch, Color.White);
@@ -407,46 +392,7 @@ namespace Barotrauma
public void UpdateSpriteStates(float deltaTime) public void UpdateSpriteStates(float deltaTime)
{ {
foreach (int spriteGroup in Prefab.DecorativeSpriteGroups.Keys) DecorativeSprite.UpdateSpriteStates(Prefab.DecorativeSpriteGroups, spriteAnimState, ID, deltaTime, ConditionalMatches);
{
for (int i = 0; i < Prefab.DecorativeSpriteGroups[spriteGroup].Count; i++)
{
var decorativeSprite = Prefab.DecorativeSpriteGroups[spriteGroup][i];
if (decorativeSprite == null) { continue; }
if (spriteGroup > 0)
{
int activeSpriteIndex = ID % Prefab.DecorativeSpriteGroups[spriteGroup].Count;
if (i != activeSpriteIndex)
{
spriteAnimState[decorativeSprite].IsActive = false;
continue;
}
}
//check if the sprite is active (whether it should be drawn or not)
var spriteState = spriteAnimState[decorativeSprite];
spriteState.IsActive = true;
foreach (PropertyConditional conditional in decorativeSprite.IsActiveConditionals)
{
if (!ConditionalMatches(conditional))
{
spriteState.IsActive = false;
break;
}
}
if (!spriteState.IsActive) { continue; }
//check if the sprite should be animated
bool animate = true;
foreach (PropertyConditional conditional in decorativeSprite.AnimationConditionals)
{
if (!ConditionalMatches(conditional)) { animate = false; break; }
}
if (!animate) { continue; }
spriteState.OffsetState += deltaTime;
spriteState.RotationState += deltaTime;
}
}
} }
public override void UpdateEditing(Camera cam) public override void UpdateEditing(Camera cam)
@@ -1227,6 +1173,8 @@ namespace Barotrauma
} }
} }
byte bodyType = msg.ReadByte();
byte teamID = msg.ReadByte(); byte teamID = msg.ReadByte();
bool tagsChanged = msg.ReadBoolean(); bool tagsChanged = msg.ReadBoolean();
string tags = ""; string tags = "";
@@ -1284,6 +1232,11 @@ namespace Barotrauma
ID = itemId ID = itemId
}; };
if (item.body != null)
{
item.body.BodyType = (BodyType)bodyType;
}
foreach (WifiComponent wifiComponent in item.GetComponents<WifiComponent>()) foreach (WifiComponent wifiComponent in item.GetComponents<WifiComponent>())
{ {
wifiComponent.TeamID = (Character.TeamType)teamID; wifiComponent.TeamID = (Character.TeamType)teamID;
@@ -24,7 +24,7 @@ namespace Barotrauma
public override void Draw(SpriteBatch sb, bool editing, bool back = true) public override void Draw(SpriteBatch sb, bool editing, bool back = true)
{ {
if (GameMain.DebugDraw) if (!GameMain.DebugDraw && Screen.Selected.Cam.Zoom > 0.1f)
{ {
Vector2 center = new Vector2(WorldRect.X + rect.Width / 2.0f, -(WorldRect.Y - rect.Height / 2.0f)); Vector2 center = new Vector2(WorldRect.X + rect.Width / 2.0f, -(WorldRect.Y - rect.Height / 2.0f));
GUI.DrawLine(sb, center, center + new Vector2(flowForce.X, -flowForce.Y) / 10.0f, GUI.Style.Red); GUI.DrawLine(sb, center, center + new Vector2(flowForce.X, -flowForce.Y) / 10.0f, GUI.Style.Red);
@@ -41,7 +41,7 @@ namespace Barotrauma
} }
} }
if (!editing || !ShowGaps) return; if (!editing || !ShowGaps) { return; }
Color clr = (open == 0.0f) ? GUI.Style.Red : Color.Cyan; Color clr = (open == 0.0f) ? GUI.Style.Red : Color.Cyan;
if (IsHighlighted) clr = Color.Gold; if (IsHighlighted) clr = Color.Gold;
@@ -76,32 +76,35 @@ namespace Barotrauma
clr * 0.6f, width: lineWidth); clr * 0.6f, width: lineWidth);
} }
for (int i = 0; i < linkedTo.Count; i++) if (linkedTo.Count != 2 || linkedTo[0] != linkedTo[1])
{ {
Vector2 dir = IsHorizontal ? for (int i = 0; i < linkedTo.Count; i++)
new Vector2(Math.Sign(linkedTo[i].Rect.Center.X - rect.Center.X), 0.0f)
: new Vector2(0.0f, Math.Sign((linkedTo[i].Rect.Y - linkedTo[i].Rect.Height / 2.0f) - (rect.Y - rect.Height / 2.0f)));
Vector2 arrowPos = new Vector2(WorldRect.Center.X, -(WorldRect.Y - WorldRect.Height / 2));
arrowPos += new Vector2(dir.X * (WorldRect.Width / 2), dir.Y * (WorldRect.Height / 2));
float arrowWidth = 32.0f;
float arrowSize = 15.0f;
bool invalidDir = false;
if (dir == Vector2.Zero)
{ {
invalidDir = true; Vector2 dir = IsHorizontal ?
dir = IsHorizontal ? Vector2.UnitX : Vector2.UnitY; new Vector2(Math.Sign(linkedTo[i].Rect.Center.X - rect.Center.X), 0.0f)
} : new Vector2(0.0f, Math.Sign((linkedTo[i].Rect.Y - linkedTo[i].Rect.Height / 2.0f) - (rect.Y - rect.Height / 2.0f)));
GUI.Arrow.Draw(sb, Vector2 arrowPos = new Vector2(WorldRect.Center.X, -(WorldRect.Y - WorldRect.Height / 2));
arrowPos, invalidDir ? Color.Red : clr * 0.8f, arrowPos += new Vector2(dir.X * (WorldRect.Width / 2), dir.Y * (WorldRect.Height / 2));
GUI.Arrow.Origin, MathUtils.VectorToAngle(dir) + MathHelper.PiOver2,
IsHorizontal ? float arrowWidth = 32.0f;
new Vector2(Math.Min(rect.Height, arrowWidth) / GUI.Arrow.size.X, arrowSize / GUI.Arrow.size.Y) : float arrowSize = 15.0f;
new Vector2(Math.Min(rect.Width, arrowWidth) / GUI.Arrow.size.X, arrowSize / GUI.Arrow.size.Y),
SpriteEffects.None, depth); bool invalidDir = false;
if (dir == Vector2.Zero)
{
invalidDir = true;
dir = IsHorizontal ? Vector2.UnitX : Vector2.UnitY;
}
GUI.Arrow.Draw(sb,
arrowPos, invalidDir ? Color.Red : clr * 0.8f,
GUI.Arrow.Origin, MathUtils.VectorToAngle(dir) + MathHelper.PiOver2,
IsHorizontal ?
new Vector2(Math.Min(rect.Height, arrowWidth) / GUI.Arrow.size.X, arrowSize / GUI.Arrow.size.Y) :
new Vector2(Math.Min(rect.Width, arrowWidth) / GUI.Arrow.size.X, arrowSize / GUI.Arrow.size.Y),
SpriteEffects.None, depth);
}
} }
if (IsSelected) if (IsSelected)
@@ -246,9 +246,7 @@ namespace Barotrauma
if (!ShowHulls && !GameMain.DebugDraw) return; if (!ShowHulls && !GameMain.DebugDraw) return;
if (!editing && !GameMain.DebugDraw) return; if (!editing && (!GameMain.DebugDraw || Screen.Selected.Cam.Zoom < 0.1f)) return;
if (aiTarget != null) aiTarget.Draw(spriteBatch);
Rectangle drawRect = Rectangle drawRect =
Submarine == null ? rect : new Rectangle((int)(Submarine.DrawPosition.X + rect.X), (int)(Submarine.DrawPosition.Y + rect.Y), rect.Width, rect.Height); Submarine == null ? rect : new Rectangle((int)(Submarine.DrawPosition.X + rect.X), (int)(Submarine.DrawPosition.Y + rect.Y), rect.Width, rect.Height);
@@ -271,7 +269,8 @@ namespace Barotrauma
GUI.DrawRectangle(spriteBatch, new Rectangle(drawRect.Center.X, -drawRect.Y + drawRect.Height / 2, 10, (int)(100 * Math.Min(waterVolume / Volume, 1.0f))), Color.Cyan, true); GUI.DrawRectangle(spriteBatch, new Rectangle(drawRect.Center.X, -drawRect.Y + drawRect.Height / 2, 10, (int)(100 * Math.Min(waterVolume / Volume, 1.0f))), Color.Cyan, true);
if (WaterVolume > Volume) if (WaterVolume > Volume)
{ {
GUI.DrawRectangle(spriteBatch, new Rectangle(drawRect.Center.X, -drawRect.Y + drawRect.Height / 2, 10, (int)(100 * (waterVolume - Volume) / MaxCompress)), GUI.Style.Red, true); float maxExcessWater = Volume * MaxCompress;
GUI.DrawRectangle(spriteBatch, new Rectangle(drawRect.Center.X, -drawRect.Y + drawRect.Height / 2, 10, (int)(100 * (waterVolume - Volume) / maxExcessWater)), GUI.Style.Red, true);
} }
GUI.DrawRectangle(spriteBatch, new Rectangle(drawRect.Center.X, -drawRect.Y + drawRect.Height / 2, 10, 100), Color.Black); GUI.DrawRectangle(spriteBatch, new Rectangle(drawRect.Center.X, -drawRect.Y + drawRect.Height / 2, 10, 100), Color.Black);
@@ -39,7 +39,7 @@ namespace Barotrauma
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
{ {
if (subElement.Name.ToString().ToLowerInvariant() != "sprite") continue; if (!subElement.Name.ToString().Equals("sprite", System.StringComparison.OrdinalIgnoreCase)) { continue; }
Sprite = new Sprite(subElement, lazyLoad: true); Sprite = new Sprite(subElement, lazyLoad: true);
break; break;
@@ -1,7 +1,7 @@
using Barotrauma.Networking; using Barotrauma.Networking;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using FarseerPhysics.Dynamics; using System;
using System.Linq; using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using FarseerPhysics; using FarseerPhysics;
@@ -54,7 +54,7 @@ namespace Barotrauma
if (renderer == null) return; if (renderer == null) return;
renderer.Draw(spriteBatch, cam); renderer.Draw(spriteBatch, cam);
if (GameMain.DebugDraw) if (GameMain.DebugDraw && Screen.Selected.Cam.Zoom > 0.1f)
{ {
foreach (InterestingPosition pos in positionsOfInterest) foreach (InterestingPosition pos in positionsOfInterest)
{ {
@@ -78,6 +78,35 @@ namespace Barotrauma
GUI.DrawRectangle(spriteBatch, ruinArea, Color.DarkSlateBlue, false, 0, 5); GUI.DrawRectangle(spriteBatch, ruinArea, Color.DarkSlateBlue, false, 0, 5);
} }
foreach (var positions in wreckPositions.Values)
{
for (int i = 0; i < positions.Count; i++)
{
float t = (i + 1) / (float)positions.Count;
float multiplier = MathHelper.Lerp(0, 1, t);
Color color = Color.Red * multiplier;
var pos = positions[i];
pos.Y = -pos.Y;
var size = new Vector2(100);
GUI.DrawRectangle(spriteBatch, pos - size / 2, size, color, thickness: 10);
if (i < positions.Count - 1)
{
var nextPos = positions[i + 1];
nextPos.Y = -nextPos.Y;
GUI.DrawLine(spriteBatch, pos, nextPos, color, width: 10);
}
}
}
foreach (var rects in blockedRects.Values)
{
foreach (var rect in rects)
{
Rectangle newRect = rect;
newRect.Y = -newRect.Y;
GUI.DrawRectangle(spriteBatch, newRect, Color.Red, thickness: 5);
}
}
} }
} }
@@ -126,7 +126,7 @@ namespace Barotrauma
int j = 0; int j = 0;
foreach (XElement subElement in Prefab.Config.Elements()) foreach (XElement subElement in Prefab.Config.Elements())
{ {
if (subElement.Name.ToString().ToLowerInvariant() != "deformablesprite") continue; if (!subElement.Name.ToString().Equals("deformablesprite", StringComparison.OrdinalIgnoreCase)) { continue; }
foreach (XElement animationElement in subElement.Elements()) foreach (XElement animationElement in subElement.Elements())
{ {
var newDeformation = SpriteDeformation.Load(animationElement, Prefab.Name); var newDeformation = SpriteDeformation.Load(animationElement, Prefab.Name);
@@ -4,6 +4,7 @@ using Barotrauma.SpriteDeformations;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
namespace Barotrauma namespace Barotrauma
@@ -120,7 +121,7 @@ namespace Barotrauma
SerializableProperty.SerializeProperties(this, element); SerializableProperty.SerializeProperties(this, element);
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements().ToList())
{ {
switch (subElement.Name.ToString().ToLowerInvariant()) switch (subElement.Name.ToString().ToLowerInvariant())
{ {
@@ -139,7 +140,7 @@ namespace Barotrauma
break; break;
} }
} }
foreach (LightSourceParams lightSourceParams in LightSourceParams) foreach (LightSourceParams lightSourceParams in LightSourceParams)
{ {
var lightElement = new XElement("LightSource"); var lightElement = new XElement("LightSource");
@@ -160,7 +161,7 @@ namespace Barotrauma
bool elementFound = false; bool elementFound = false;
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
{ {
if (subElement.Name.ToString().ToLowerInvariant() == "overridecommonness" if (subElement.Name.ToString().Equals("overridecommonness", System.StringComparison.OrdinalIgnoreCase)
&& subElement.GetAttributeString("leveltype", "") == overrideCommonness.Key) && subElement.GetAttributeString("leveltype", "") == overrideCommonness.Key)
{ {
subElement.Attribute("commonness").Value = overrideCommonness.Value.ToString("G", CultureInfo.InvariantCulture); subElement.Attribute("commonness").Value = overrideCommonness.Value.ToString("G", CultureInfo.InvariantCulture);
@@ -228,7 +228,7 @@ namespace Barotrauma
public void Draw(SpriteBatch spriteBatch, Camera cam) public void Draw(SpriteBatch spriteBatch, Camera cam)
{ {
if (GameMain.DebugDraw && cam.Zoom > 0.05f) if (GameMain.DebugDraw && cam.Zoom > 0.1f)
{ {
var cells = level.GetCells(cam.WorldViewCenter, 2); var cells = level.GetCells(cam.WorldViewCenter, 2);
foreach (VoronoiCell cell in cells) foreach (VoronoiCell cell in cells)
@@ -977,7 +977,7 @@ namespace Barotrauma.Lights
origin, -Rotation, SpriteScale, LightSpriteEffect); origin, -Rotation, SpriteScale, LightSpriteEffect);
} }
if (GameMain.DebugDraw) if (GameMain.DebugDraw && Screen.Selected.Cam.Zoom > 0.1f)
{ {
Vector2 drawPos = position; Vector2 drawPos = position;
if (ParentSub != null) { drawPos += ParentSub.DrawPosition; } if (ParentSub != null) { drawPos += ParentSub.DrawPosition; }
@@ -12,10 +12,10 @@ namespace Barotrauma
{ {
public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true) public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true)
{ {
if (!editing || wallVertices == null) return; if (!editing || wallVertices == null) { return; }
Color color = (IsHighlighted) ? GUI.Style.Orange : GUI.Style.Green; Color color = IsHighlighted ? GUI.Style.Orange : GUI.Style.Green;
if (IsSelected) color = GUI.Style.Red; if (IsSelected) { color = GUI.Style.Red; }
Vector2 pos = Position; Vector2 pos = Position;
@@ -37,11 +37,7 @@ namespace Barotrauma
GUI.DrawLine(spriteBatch, pos + Vector2.UnitY * 50.0f, pos - Vector2.UnitY * 50.0f, color, 0.0f, 5); GUI.DrawLine(spriteBatch, pos + Vector2.UnitY * 50.0f, pos - Vector2.UnitY * 50.0f, color, 0.0f, 5);
GUI.DrawLine(spriteBatch, pos + Vector2.UnitX * 50.0f, pos - Vector2.UnitX * 50.0f, color, 0.0f, 5); GUI.DrawLine(spriteBatch, pos + Vector2.UnitX * 50.0f, pos - Vector2.UnitX * 50.0f, color, 0.0f, 5);
Rectangle drawRect = rect; if (!Item.ShowLinks) { return; }
drawRect.Y = -rect.Y;
GUI.DrawRectangle(spriteBatch, drawRect, GUI.Style.Red, true);
if (!Item.ShowLinks) return;
foreach (MapEntity e in linkedTo) foreach (MapEntity e in linkedTo)
{ {
@@ -135,7 +131,7 @@ namespace Barotrauma
return false; return false;
} }
XDocument doc = Submarine.OpenFile(pathBox.Text); XDocument doc = SubmarineInfo.OpenFile(pathBox.Text);
if (doc == null || doc.Root == null) return false; if (doc == null || doc.Root == null) return false;
pathBox.Flash(GUI.Style.Green); pathBox.Flash(GUI.Style.Green);
@@ -18,6 +18,11 @@ namespace Barotrauma
public static Vector2 StartMovingPos => startMovingPos; public static Vector2 StartMovingPos => startMovingPos;
// Quick undo/redo for size and movement only. TODO: Remove if we do a more general implementation.
private Memento<Rectangle> rectMemento;
public event Action<Rectangle> Resized;
private static bool resizing; private static bool resizing;
private int resizeDirX, resizeDirY; private int resizeDirX, resizeDirY;
@@ -589,6 +594,14 @@ namespace Barotrauma
if (structure.FlippedX && structure.Prefab.CanSpriteFlipX) spriteEffects ^= SpriteEffects.FlipHorizontally; if (structure.FlippedX && structure.Prefab.CanSpriteFlipX) spriteEffects ^= SpriteEffects.FlipHorizontally;
if (structure.flippedY && structure.Prefab.CanSpriteFlipY) spriteEffects ^= SpriteEffects.FlipVertically; if (structure.flippedY && structure.Prefab.CanSpriteFlipY) spriteEffects ^= SpriteEffects.FlipVertically;
} }
else if (e is WayPoint wayPoint)
{
Vector2 drawPos = e.WorldPosition;
drawPos.Y = -drawPos.Y;
drawPos += moveAmount;
wayPoint.Draw(spriteBatch, drawPos);
continue;
}
e.prefab?.DrawPlacing(spriteBatch, e.prefab?.DrawPlacing(spriteBatch,
new Rectangle(e.WorldRect.Location + new Point((int)moveAmount.X, (int)-moveAmount.Y), e.WorldRect.Size), e.Scale, spriteEffects); new Rectangle(e.WorldRect.Location + new Point((int)moveAmount.X, (int)-moveAmount.Y), e.WorldRect.Size), e.Scale, spriteEffects);
GUI.DrawRectangle(spriteBatch, GUI.DrawRectangle(spriteBatch,
@@ -18,11 +18,13 @@ namespace Barotrauma
private List<ConvexHull> convexHulls; private List<ConvexHull> convexHulls;
private readonly Dictionary<DecorativeSprite, DecorativeSprite.State> spriteAnimState = new Dictionary<DecorativeSprite, DecorativeSprite.State>();
public override bool SelectableInEditor public override bool SelectableInEditor
{ {
get get
{ {
return HasBody ? ShowWalls : ShowStructures;; return HasBody ? ShowWalls : ShowStructures;
} }
} }
@@ -38,6 +40,12 @@ namespace Barotrauma
{ {
Prefab.sprite?.EnsureLazyLoaded(); Prefab.sprite?.EnsureLazyLoaded();
Prefab.BackgroundSprite?.EnsureLazyLoaded(); Prefab.BackgroundSprite?.EnsureLazyLoaded();
foreach (var decorativeSprite in Prefab.DecorativeSprites)
{
decorativeSprite.Sprite.EnsureLazyLoaded();
spriteAnimState.Add(decorativeSprite, new DecorativeSprite.State());
}
} }
partial void CreateConvexHull(Vector2 position, Vector2 size, float rotation) partial void CreateConvexHull(Vector2 position, Vector2 size, float rotation)
@@ -156,19 +164,19 @@ namespace Barotrauma
{ {
Rectangle worldRect = WorldRect; Rectangle worldRect = WorldRect;
if (worldRect.X > worldView.Right || worldRect.Right < worldView.X) return false; if (worldRect.X > worldView.Right || worldRect.Right < worldView.X) { return false; }
if (worldRect.Y < worldView.Y - worldView.Height || worldRect.Y - worldRect.Height > worldView.Y) return false; if (worldRect.Y < worldView.Y - worldView.Height || worldRect.Y - worldRect.Height > worldView.Y) { return false; }
return true; return true;
} }
public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true) public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true)
{ {
if (prefab.sprite == null) return; if (prefab.sprite == null) { return; }
if (editing) if (editing)
{ {
if (!HasBody && !ShowStructures) return; if (!HasBody && !ShowStructures) { return; }
if (HasBody && !ShowWalls) return; if (HasBody && !ShowWalls) { return; }
} }
Draw(spriteBatch, editing, back, null); Draw(spriteBatch, editing, back, null);
@@ -188,12 +196,13 @@ namespace Barotrauma
private void Draw(SpriteBatch spriteBatch, bool editing, bool back = true, Effect damageEffect = null) private void Draw(SpriteBatch spriteBatch, bool editing, bool back = true, Effect damageEffect = null)
{ {
if (prefab.sprite == null) return; if (prefab.sprite == null) { return; }
if (editing) if (editing)
{ {
if (!HasBody && !ShowStructures) return; if (!HasBody && !ShowStructures) { return; }
if (HasBody && !ShowWalls) return; if (HasBody && !ShowWalls) { return; }
} }
else if (HiddenInGame) { return; }
Color color = IsHighlighted ? GUI.Style.Orange : spriteColor; Color color = IsHighlighted ? GUI.Style.Orange : spriteColor;
if (IsSelected && editing) if (IsSelected && editing)
@@ -254,7 +263,7 @@ namespace Barotrauma
spriteBatch, spriteBatch,
new Vector2(rect.X + drawOffset.X, -(rect.Y + drawOffset.Y)), new Vector2(rect.X + drawOffset.X, -(rect.Y + drawOffset.Y)),
new Vector2(rect.Width, rect.Height), new Vector2(rect.Width, rect.Height),
color: color, color: Prefab.BackgroundSpriteColor,
textureScale: TextureScale * Scale, textureScale: TextureScale * Scale,
startOffset: backGroundOffset, startOffset: backGroundOffset,
depth: Math.Max(Prefab.BackgroundSprite.Depth + (ID % 255) * 0.000001f, depth + 0.000001f)); depth: Math.Max(Prefab.BackgroundSprite.Depth + (ID % 255) * 0.000001f, depth + 0.000001f));
@@ -318,10 +327,20 @@ namespace Barotrauma
depth: depth, depth: depth,
textureScale: TextureScale * Scale); textureScale: TextureScale * Scale);
} }
foreach (var decorativeSprite in Prefab.DecorativeSprites)
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState);
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState) * Scale;
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + offset.X, -(DrawPosition.Y + offset.Y)), color,
rotation, Scale, prefab.sprite.effects,
depth: Math.Min(depth + (decorativeSprite.Sprite.Depth - prefab.sprite.Depth), 0.999f));
}
prefab.sprite.effects = oldEffects; prefab.sprite.effects = oldEffects;
} }
if (GameMain.DebugDraw) if (GameMain.DebugDraw && Screen.Selected.Cam.Zoom > 0.5f)
{ {
if (Bodies != null) if (Bodies != null)
{ {
@@ -350,11 +369,67 @@ namespace Barotrauma
} }
} }
} }
AiTarget?.Draw(spriteBatch);
} }
} }
public void UpdateSpriteStates(float deltaTime)
{
DecorativeSprite.UpdateSpriteStates(Prefab.DecorativeSpriteGroups, spriteAnimState, ID, deltaTime, ConditionalMatches);
foreach (int spriteGroup in Prefab.DecorativeSpriteGroups.Keys)
{
for (int i = 0; i < Prefab.DecorativeSpriteGroups[spriteGroup].Count; i++)
{
var decorativeSprite = Prefab.DecorativeSpriteGroups[spriteGroup][i];
if (decorativeSprite == null) { continue; }
if (spriteGroup > 0)
{
int activeSpriteIndex = ID % Prefab.DecorativeSpriteGroups[spriteGroup].Count;
if (i != activeSpriteIndex)
{
spriteAnimState[decorativeSprite].IsActive = false;
continue;
}
}
//check if the sprite is active (whether it should be drawn or not)
var spriteState = spriteAnimState[decorativeSprite];
spriteState.IsActive = true;
foreach (PropertyConditional conditional in decorativeSprite.IsActiveConditionals)
{
if (!ConditionalMatches(conditional))
{
spriteState.IsActive = false;
break;
}
}
if (!spriteState.IsActive) { continue; }
//check if the sprite should be animated
bool animate = true;
foreach (PropertyConditional conditional in decorativeSprite.AnimationConditionals)
{
if (!ConditionalMatches(conditional)) { animate = false; break; }
}
if (!animate) { continue; }
spriteState.OffsetState += deltaTime;
spriteState.RotationState += deltaTime;
}
}
}
private bool ConditionalMatches(PropertyConditional conditional)
{
if (!string.IsNullOrEmpty(conditional.TargetItemComponentName))
{
return false;
}
else
{
if (!conditional.Matches(this)) { return false; }
}
return true;
}
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{ {
byte sectionCount = msg.ReadByte(); byte sectionCount = msg.ReadByte();
@@ -1,11 +1,21 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Collections.Generic;
namespace Barotrauma namespace Barotrauma
{ {
partial class StructurePrefab : MapEntityPrefab partial class StructurePrefab : MapEntityPrefab
{ {
public Color BackgroundSpriteColor
{
get;
private set;
}
public List<DecorativeSprite> DecorativeSprites = new List<DecorativeSprite>();
public Dictionary<int, List<DecorativeSprite>> DecorativeSpriteGroups = new Dictionary<int, List<DecorativeSprite>>();
public override void UpdatePlacing(Camera cam) public override void UpdatePlacing(Camera cam)
{ {
Vector2 position = Submarine.MouseToWorldGrid(cam, Submarine.MainSub); Vector2 position = Submarine.MouseToWorldGrid(cam, Submarine.MainSub);
@@ -40,7 +50,7 @@ namespace Barotrauma
if (PlayerInput.PrimaryMouseButtonReleased()) if (PlayerInput.PrimaryMouseButtonReleased())
{ {
newRect.Location -= MathUtils.ToPoint(Submarine.MainSub.Position); newRect.Location -= MathUtils.ToPoint(Submarine.MainSub.Position);
var structure = new Structure(newRect, this, Submarine.MainSub) new Structure(newRect, this, Submarine.MainSub)
{ {
Submarine = Submarine.MainSub Submarine = Submarine.MainSub
}; };
@@ -35,7 +35,6 @@ namespace Barotrauma
partial class Submarine : Entity, IServerSerializable partial class Submarine : Entity, IServerSerializable
{ {
public Sprite PreviewImage;
public static Vector2 MouseToWorldGrid(Camera cam, Submarine sub) public static Vector2 MouseToWorldGrid(Camera cam, Submarine sub)
{ {
Vector2 position = PlayerInput.MousePosition; Vector2 position = PlayerInput.MousePosition;
@@ -56,6 +55,8 @@ namespace Barotrauma
private static List<RoundSound> roundSounds = null; private static List<RoundSound> roundSounds = null;
public static RoundSound LoadRoundSound(XElement element, bool stream = false) public static RoundSound LoadRoundSound(XElement element, bool stream = false)
{ {
if (GameMain.SoundManager?.Disabled ?? true) { return null; }
string filename = element.GetAttributeString("file", ""); string filename = element.GetAttributeString("file", "");
if (string.IsNullOrEmpty(filename)) filename = element.GetAttributeString("sound", ""); if (string.IsNullOrEmpty(filename)) filename = element.GetAttributeString("sound", "");
@@ -222,7 +223,7 @@ namespace Barotrauma
GUI.DrawRectangle(spriteBatch, worldBorders, Color.White, false, 0, 5); GUI.DrawRectangle(spriteBatch, worldBorders, Color.White, false, 0, 5);
if (sub.subBody.PositionBuffer.Count < 2) continue; if (sub.subBody == null || sub.subBody.PositionBuffer.Count < 2) continue;
Vector2 prevPos = ConvertUnits.ToDisplayUnits(sub.subBody.PositionBuffer[0].Position); Vector2 prevPos = ConvertUnits.ToDisplayUnits(sub.subBody.PositionBuffer[0].Position);
prevPos.Y = -prevPos.Y; prevPos.Y = -prevPos.Y;
@@ -329,125 +330,6 @@ namespace Barotrauma
} }
} }
public static bool SaveCurrent(string filePath, MemoryStream previewImage = null)
{
if (MainSub == null)
{
MainSub = new Submarine(filePath);
}
MainSub.filePath = filePath;
return MainSub.SaveAs(filePath, previewImage);
}
public void CreatePreviewWindow(GUIComponent parent)
{
var content = new GUIFrame(new RectTransform(Vector2.One, parent.RectTransform), style: null);
if (PreviewImage == null)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), content.RectTransform), TextManager.Get(SavedSubmarines.Contains(this) ? "SubPreviewImageNotFound" : "SubNotDownloaded"));
}
else
{
var submarinePreviewBackground = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.5f), content.RectTransform), style: null) { Color = Color.Black };
new GUIImage(new RectTransform(new Vector2(0.98f), submarinePreviewBackground.RectTransform, Anchor.Center), PreviewImage, scaleToFit: true);
new GUIFrame(new RectTransform(Vector2.One, submarinePreviewBackground.RectTransform), "InnerGlow", color: Color.Black);
}
var descriptionBox = new GUIListBox(new RectTransform(new Vector2(1, 0.5f), content.RectTransform, Anchor.BottomCenter))
{
UserData = "descriptionbox",
ScrollBarVisible = true,
Spacing = 5
};
//space
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.03f), descriptionBox.Content.RectTransform), style: null);
new GUITextBlock(new RectTransform(new Vector2(1, 0), descriptionBox.Content.RectTransform), TextManager.Get("submarine.name." + Name, true) ?? Name, font: GUI.LargeFont, wrap: true) { ForceUpperCase = true, CanBeFocused = false };
float leftPanelWidth = 0.6f;
float rightPanelWidth = 0.4f / leftPanelWidth;
ScalableFont font = descriptionBox.Rect.Width < 350 ? GUI.SmallFont : GUI.Font;
Vector2 realWorldDimensions = Dimensions * Physics.DisplayToRealWorldRatio;
if (realWorldDimensions != Vector2.Zero)
{
string dimensionsStr = TextManager.GetWithVariables("DimensionsFormat", new string[2] { "[width]", "[height]" }, new string[2] { ((int)realWorldDimensions.X).ToString(), ((int)realWorldDimensions.Y).ToString() });
var dimensionsText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), descriptionBox.Content.RectTransform),
TextManager.Get("Dimensions"), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), dimensionsText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
dimensionsStr, textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
dimensionsText.RectTransform.MinSize = new Point(0, dimensionsText.Children.First().Rect.Height);
}
if (RecommendedCrewSizeMax > 0)
{
var crewSizeText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), descriptionBox.Content.RectTransform),
TextManager.Get("RecommendedCrewSize"), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), crewSizeText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
RecommendedCrewSizeMin + " - " + RecommendedCrewSizeMax, textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
crewSizeText.RectTransform.MinSize = new Point(0, crewSizeText.Children.First().Rect.Height);
}
if (!string.IsNullOrEmpty(RecommendedCrewExperience))
{
var crewExperienceText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), descriptionBox.Content.RectTransform),
TextManager.Get("RecommendedCrewExperience"), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), crewExperienceText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
TextManager.Get(RecommendedCrewExperience), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
crewExperienceText.RectTransform.MinSize = new Point(0, crewExperienceText.Children.First().Rect.Height);
}
if (RequiredContentPackages.Any())
{
var contentPackagesText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), descriptionBox.Content.RectTransform),
TextManager.Get("RequiredContentPackages"), textAlignment: Alignment.TopLeft, font: font)
{ CanBeFocused = false };
new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), contentPackagesText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
string.Join(", ", RequiredContentPackages), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
contentPackagesText.RectTransform.MinSize = new Point(0, contentPackagesText.Children.First().Rect.Height);
}
// show what game version the submarine was created on
if (!IsVanillaSubmarine() && GameVersion != null)
{
var versionText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), descriptionBox.Content.RectTransform),
TextManager.Get("serverlistversion"), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), versionText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
GameVersion.ToString(), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
versionText.RectTransform.MinSize = new Point(0, versionText.Children.First().Rect.Height);
}
GUITextBlock.AutoScaleAndNormalize(descriptionBox.Content.Children.Where(c => c is GUITextBlock).Cast<GUITextBlock>());
//space
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), descriptionBox.Content.RectTransform), style: null);
if (!string.IsNullOrEmpty(Description))
{
new GUITextBlock(new RectTransform(new Vector2(1, 0), descriptionBox.Content.RectTransform),
TextManager.Get("SaveSubDialogDescription", fallBackTag: "WorkshopItemDescription"), font: GUI.Font, wrap: true) { CanBeFocused = false, ForceUpperCase = true };
}
new GUITextBlock(new RectTransform(new Vector2(1, 0), descriptionBox.Content.RectTransform), Description, font: font, wrap: true)
{
CanBeFocused = false
};
}
public void CreateMiniMap(GUIComponent parent, IEnumerable<Entity> pointsOfInterest = null) public void CreateMiniMap(GUIComponent parent, IEnumerable<Entity> pointsOfInterest = null)
{ {
Rectangle worldBorders = GetDockedBorders(); Rectangle worldBorders = GetDockedBorders();
@@ -496,21 +378,6 @@ namespace Barotrauma
} }
} }
public bool IsVanillaSubmarine()
{
var vanilla = GameMain.VanillaContent;
if (vanilla != null)
{
var vanillaSubs = vanilla.GetFilesOfType(ContentType.Submarine);
string pathToCompare = filePath.Replace(@"\", @"/").ToLowerInvariant();
if (vanillaSubs.Any(sub => sub.Replace(@"\", @"/").ToLowerInvariant() == pathToCompare))
{
return true;
}
}
return false;
}
public void CheckForErrors() public void CheckForErrors()
{ {
List<string> errorMsgs = new List<string>(); List<string> errorMsgs = new List<string>();
@@ -531,6 +398,11 @@ namespace Barotrauma
} }
} }
if (!WayPoint.WayPointList.Any(wp => wp.ShouldBeSaved && wp.SpawnType == SpawnType.Human))
{
errorMsgs.Add(TextManager.Get("NoHumanSpawnpointWarning"));
}
if (!WayPoint.WayPointList.Any(wp => wp.ShouldBeSaved && wp.SpawnType == SpawnType.Path)) if (!WayPoint.WayPointList.Any(wp => wp.ShouldBeSaved && wp.SpawnType == SpawnType.Path))
{ {
errorMsgs.Add(TextManager.Get("NoWaypointsWarning")); errorMsgs.Add(TextManager.Get("NoWaypointsWarning"));
@@ -605,7 +477,7 @@ namespace Barotrauma
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{ {
var posInfo = PhysicsBody.ClientRead(type, msg, sendingTime, parentDebugName: Name); var posInfo = PhysicsBody.ClientRead(type, msg, sendingTime, parentDebugName: Info.Name);
msg.ReadPadBits(); msg.ReadPadBits();
if (posInfo != null) if (posInfo != null)
@@ -0,0 +1,149 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Linq;
namespace Barotrauma
{
partial class SubmarineInfo : IDisposable
{
public Sprite PreviewImage;
partial void InitProjectSpecific()
{
string previewImageData = SubmarineElement.GetAttributeString("previewimage", "");
if (!string.IsNullOrEmpty(previewImageData))
{
try
{
using (MemoryStream mem = new MemoryStream(Convert.FromBase64String(previewImageData)))
{
var texture = TextureLoader.FromStream(mem, path: FilePath);
if (texture == null) { throw new Exception("PreviewImage texture returned null"); }
PreviewImage = new Sprite(texture, null, null);
}
}
catch (Exception e)
{
DebugConsole.ThrowError("Loading the preview image of the submarine \"" + Name + "\" failed. The file may be corrupted.", e);
GameAnalyticsManager.AddErrorEventOnce("Submarine..ctor:PreviewImageLoadingFailed", GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Loading the preview image of the submarine \"" + Name + "\" failed. The file may be corrupted.");
PreviewImage = null;
}
}
}
public void CreatePreviewWindow(GUIComponent parent)
{
var content = new GUIFrame(new RectTransform(Vector2.One, parent.RectTransform), style: null);
if (PreviewImage == null)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), content.RectTransform), TextManager.Get(SavedSubmarines.Contains(this) ? "SubPreviewImageNotFound" : "SubNotDownloaded"));
}
else
{
var submarinePreviewBackground = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.5f), content.RectTransform), style: null) { Color = Color.Black };
new GUIImage(new RectTransform(new Vector2(0.98f), submarinePreviewBackground.RectTransform, Anchor.Center), PreviewImage, scaleToFit: true);
new GUIFrame(new RectTransform(Vector2.One, submarinePreviewBackground.RectTransform), "InnerGlow", color: Color.Black);
}
var descriptionBox = new GUIListBox(new RectTransform(new Vector2(1, 0.5f), content.RectTransform, Anchor.BottomCenter))
{
UserData = "descriptionbox",
ScrollBarVisible = true,
Spacing = 5
};
//space
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.03f), descriptionBox.Content.RectTransform), style: null);
new GUITextBlock(new RectTransform(new Vector2(1, 0), descriptionBox.Content.RectTransform), TextManager.Get("submarine.name." + Name, true) ?? Name, font: GUI.LargeFont, wrap: true) { ForceUpperCase = true, CanBeFocused = false };
float leftPanelWidth = 0.6f;
float rightPanelWidth = 0.4f / leftPanelWidth;
ScalableFont font = descriptionBox.Rect.Width < 350 ? GUI.SmallFont : GUI.Font;
Vector2 realWorldDimensions = Dimensions * Physics.DisplayToRealWorldRatio;
if (realWorldDimensions != Vector2.Zero)
{
string dimensionsStr = TextManager.GetWithVariables("DimensionsFormat", new string[2] { "[width]", "[height]" }, new string[2] { ((int)realWorldDimensions.X).ToString(), ((int)realWorldDimensions.Y).ToString() });
var dimensionsText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), descriptionBox.Content.RectTransform),
TextManager.Get("Dimensions"), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), dimensionsText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
dimensionsStr, textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
dimensionsText.RectTransform.MinSize = new Point(0, dimensionsText.Children.First().Rect.Height);
}
if (RecommendedCrewSizeMax > 0)
{
var crewSizeText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), descriptionBox.Content.RectTransform),
TextManager.Get("RecommendedCrewSize"), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), crewSizeText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
RecommendedCrewSizeMin + " - " + RecommendedCrewSizeMax, textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
crewSizeText.RectTransform.MinSize = new Point(0, crewSizeText.Children.First().Rect.Height);
}
if (!string.IsNullOrEmpty(RecommendedCrewExperience))
{
var crewExperienceText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), descriptionBox.Content.RectTransform),
TextManager.Get("RecommendedCrewExperience"), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), crewExperienceText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
TextManager.Get(RecommendedCrewExperience), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
crewExperienceText.RectTransform.MinSize = new Point(0, crewExperienceText.Children.First().Rect.Height);
}
if (RequiredContentPackages.Any())
{
var contentPackagesText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), descriptionBox.Content.RectTransform),
TextManager.Get("RequiredContentPackages"), textAlignment: Alignment.TopLeft, font: font)
{ CanBeFocused = false };
new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), contentPackagesText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
string.Join(", ", RequiredContentPackages), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
contentPackagesText.RectTransform.MinSize = new Point(0, contentPackagesText.Children.First().Rect.Height);
}
// show what game version the submarine was created on
if (!IsVanillaSubmarine() && GameVersion != null)
{
var versionText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), descriptionBox.Content.RectTransform),
TextManager.Get("serverlistversion"), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), versionText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
GameVersion.ToString(), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
versionText.RectTransform.MinSize = new Point(0, versionText.Children.First().Rect.Height);
}
GUITextBlock.AutoScaleAndNormalize(descriptionBox.Content.Children.Where(c => c is GUITextBlock).Cast<GUITextBlock>());
//space
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), descriptionBox.Content.RectTransform), style: null);
if (!string.IsNullOrEmpty(Description))
{
new GUITextBlock(new RectTransform(new Vector2(1, 0), descriptionBox.Content.RectTransform),
TextManager.Get("SaveSubDialogDescription", fallBackTag: "WorkshopItemDescription"), font: GUI.Font, wrap: true)
{ CanBeFocused = false, ForceUpperCase = true };
}
new GUITextBlock(new RectTransform(new Vector2(1, 0), descriptionBox.Content.RectTransform), Description, font: font, wrap: true)
{
CanBeFocused = false
};
}
}
}
@@ -1,17 +1,15 @@
using Microsoft.Xna.Framework; using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Barotrauma.Items.Components;
using System.Linq;
namespace Barotrauma namespace Barotrauma
{ {
partial class WayPoint : MapEntity partial class WayPoint : MapEntity
{ {
private static Texture2D iconTexture; private static Dictionary<SpawnType, Sprite> iconSprites;
private const int IconSize = 32; private const int WaypointSize = 12, SpawnPointSize = 32;
private static int[] iconIndices = { 3, 0, 1, 2 };
public override bool IsVisible(Rectangle worldView) public override bool IsVisible(Rectangle worldView)
{ {
@@ -23,58 +21,58 @@ namespace Barotrauma
get { return !IsHidden(); } get { return !IsHidden(); }
} }
public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true) public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true)
{ {
if (!editing && !GameMain.DebugDraw) { return; } if (!editing && (!GameMain.DebugDraw || Screen.Selected.Cam.Zoom < 0.1f)) { return; }
if (IsHidden()) { return; } if (IsHidden()) { return; }
//Rectangle drawRect =
// Submarine == null ? rect : new Rectangle((int)(Submarine.DrawPosition.X + rect.X), (int)(Submarine.DrawPosition.Y + rect.Y), rect.Width, rect.Height);
Vector2 drawPos = Position; Vector2 drawPos = Position;
if (Submarine != null) drawPos += Submarine.DrawPosition; if (Submarine != null) { drawPos += Submarine.DrawPosition; }
drawPos.Y = -drawPos.Y; drawPos.Y = -drawPos.Y;
Color clr = currentHull == null ? Color.Blue : Color.White; Draw(spriteBatch, drawPos);
}
public void Draw(SpriteBatch spriteBatch, Vector2 drawPos)
{
Color clr = currentHull == null ? Color.CadetBlue : GUI.Style.Green;
if (spawnType != SpawnType.Path) { clr = Color.Gray; }
if (isObstructed) if (isObstructed)
{ {
clr = Color.Black; clr = Color.Black;
} }
if (IsSelected) clr = GUI.Style.Red; if (IsHighlighted || IsHighlighted) { clr = Color.Lerp(clr, Color.White, 0.8f); }
if (IsHighlighted) clr = Color.DarkRed;
int iconX = iconIndices[(int)spawnType] * IconSize % iconTexture.Width; int iconSize = spawnType == SpawnType.Path ? WaypointSize : SpawnPointSize;
int iconY = (int)(Math.Floor(iconIndices[(int)spawnType] * IconSize / (float)iconTexture.Width)) * IconSize; if (ConnectedGap != null || Ladders != null || Stairs != null || SpawnType != SpawnType.Path) { iconSize = (int)(iconSize * 1.5f); }
int iconSize = IconSize; if (IsSelected || IsHighlighted)
if (ConnectedGap != null)
{ {
iconSize = (int)(iconSize * 1.5f); int glowSize = (int)(iconSize * 1.5f);
} GUI.Style.UIGlowCircular.Draw(spriteBatch,
if (Ladders != null) new Rectangle((int)(drawPos.X - glowSize / 2), (int)(drawPos.Y - glowSize / 2), glowSize, glowSize),
{ Color.White);
iconSize = (int)(iconSize * 1.5f);
}
if (Stairs != null)
{
iconSize = (int)(iconSize * 1.5f);
} }
spriteBatch.Draw(iconTexture, Sprite sprite = iconSprites[SpawnType];
new Rectangle((int)(drawPos.X - iconSize / 2), (int)(drawPos.Y - iconSize / 2), iconSize, iconSize), if (spawnType == SpawnType.Human && AssignedJob?.Icon != null)
new Rectangle(iconX, iconY, IconSize, IconSize), clr); {
sprite = iconSprites[SpawnType.Path];
//GUI.DrawRectangle(spriteBatch, new Rectangle(drawRect.X, -drawRect.Y, rect.Width, rect.Height), clr, true); }
sprite.Draw(spriteBatch, drawPos, clr, scale: iconSize / (float)sprite.SourceRect.Width, depth: 0.001f);
//GUI.SmallFont.DrawString(spriteBatch, Position.ToString(), new Vector2(Position.X, -Position.Y), Color.White); sprite.RelativeOrigin = Vector2.One * 0.5f;
if (spawnType == SpawnType.Human && AssignedJob?.Icon != null)
{
AssignedJob.Icon.Draw(spriteBatch, drawPos, AssignedJob.UIColor, scale: iconSize / (float)AssignedJob.Icon.SourceRect.Width * 0.8f, depth: 0.0f);
}
foreach (MapEntity e in linkedTo) foreach (MapEntity e in linkedTo)
{ {
GUI.DrawLine(spriteBatch, GUI.DrawLine(spriteBatch,
drawPos, drawPos,
new Vector2(e.DrawPosition.X, -e.DrawPosition.Y), new Vector2(e.DrawPosition.X, -e.DrawPosition.Y),
isObstructed ? Color.Gray : GUI.Style.Green, width: 5); (isObstructed ? Color.Gray : GUI.Style.Green) * 0.7f, width: 5, depth: 0.002f);
} }
GUI.SmallFont.DrawString(spriteBatch, GUI.SmallFont.DrawString(spriteBatch,
@@ -83,6 +81,14 @@ namespace Barotrauma
Color.WhiteSmoke); Color.WhiteSmoke);
} }
public override bool IsMouseOn(Vector2 position)
{
if (IsHidden()) { return false; }
float dist = Vector2.DistanceSquared(position, WorldPosition);
float radius = (SpawnType == SpawnType.Path ? WaypointSize : SpawnPointSize) * 0.6f;
return dist < radius * radius;
}
private bool IsHidden() private bool IsHidden()
{ {
if (spawnType == SpawnType.Path) if (spawnType == SpawnType.Path)
@@ -178,14 +184,19 @@ namespace Barotrauma
private bool ChangeSpawnType(GUIButton button, object obj) private bool ChangeSpawnType(GUIButton button, object obj)
{ {
GUITextBlock spawnTypeText = button.Parent.GetChildByUserData("spawntypetext") as GUITextBlock; GUITextBlock spawnTypeText = button.Parent.GetChildByUserData("spawntypetext") as GUITextBlock;
spawnType += (int)button.UserData; spawnType += (int)button.UserData;
var values = Enum.GetValues(typeof(SpawnType));
if (spawnType > SpawnType.Cargo) spawnType = SpawnType.Human; int firstIndex = 1;
if (spawnType < SpawnType.Human) spawnType = SpawnType.Cargo; int lastIndex = values.Length - 1;
if ((int)spawnType > lastIndex)
{
spawnType = (SpawnType)firstIndex;
}
if ((int)spawnType < firstIndex)
{
spawnType = (SpawnType)values.GetValue(lastIndex);
}
spawnTypeText.Text = spawnType.ToString(); spawnTypeText.Text = spawnType.ToString();
return true; return true;
} }
@@ -15,6 +15,7 @@ namespace Barotrauma.Networking
public byte ID; public byte ID;
public UInt16 CharacterID; public UInt16 CharacterID;
public bool Muted; public bool Muted;
public bool InGame;
public bool AllowKicking; public bool AllowKicking;
} }
@@ -71,7 +72,8 @@ namespace Barotrauma.Networking
else else
{ {
VoipSound.SetPosition(null); VoipSound.SetPosition(null);
} VoipSound.Gain = 1.0f;
}
} }
partial void InitProjSpecific() partial void InitProjSpecific()
@@ -8,6 +8,7 @@ using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
namespace Barotrauma.Networking namespace Barotrauma.Networking
{ {
@@ -60,11 +61,24 @@ namespace Barotrauma.Networking
private bool connected; private bool connected;
private enum RoundInitStatus
{
NotStarted,
Starting,
WaitingForStartGameFinalize,
Started,
TimedOut,
Error,
Interrupted
}
private RoundInitStatus roundInitStatus = RoundInitStatus.NotStarted;
private byte myID; private byte myID;
private List<Client> otherClients; private List<Client> otherClients;
private readonly List<Submarine> serverSubmarines = new List<Submarine>(); private readonly List<SubmarineInfo> serverSubmarines = new List<SubmarineInfo>();
private string serverIP, serverName; private string serverIP, serverName;
@@ -148,6 +162,8 @@ namespace Barotrauma.Networking
this.ownerKey = ownerKey; this.ownerKey = ownerKey;
this.steamP2POwner = steamP2POwner; this.steamP2POwner = steamP2POwner;
roundInitStatus = RoundInitStatus.NotStarted;
allowReconnect = true; allowReconnect = true;
netStats = new NetStats(); netStats = new NetStats();
@@ -472,13 +488,17 @@ namespace Barotrauma.Networking
var msgBox = new GUIMessageBox(pwMsg, "", new string[] { TextManager.Get("OK"), TextManager.Get("Cancel") }, var msgBox = new GUIMessageBox(pwMsg, "", new string[] { TextManager.Get("OK"), TextManager.Get("Cancel") },
relativeSize: new Vector2(0.25f, 0.1f), minSize: new Point(400, 170)); relativeSize: new Vector2(0.25f, 0.1f), minSize: new Point(400, 170));
var passwordHolder = new GUILayoutGroup(new RectTransform(Vector2.One, msgBox.Content.RectTransform), childAnchor: Anchor.TopCenter); var passwordHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), msgBox.Content.RectTransform), childAnchor: Anchor.TopCenter);
var passwordBox = new GUITextBox(new RectTransform(new Vector2(0.8f, 1f) , passwordHolder.RectTransform) { MinSize = new Point(0, 20) }) var passwordBox = new GUITextBox(new RectTransform(new Vector2(0.8f, 1f), passwordHolder.RectTransform) { MinSize = new Point(0, 20) })
{ {
UserData = "password", UserData = "password",
Censor = true Censor = true
}; };
msgBox.Content.Recalculate();
msgBox.Content.RectTransform.MinSize = new Point(0, msgBox.Content.RectTransform.Children.Sum(c => c.Rect.Height));
msgBox.Content.Parent.RectTransform.MinSize = new Point(0, (int)(msgBox.Content.RectTransform.MinSize.Y / msgBox.Content.RectTransform.RelativeSize.Y));
var okButton = msgBox.Buttons[0]; var okButton = msgBox.Buttons[0];
var cancelButton = msgBox.Buttons[1]; var cancelButton = msgBox.Buttons[1];
@@ -524,6 +544,7 @@ namespace Barotrauma.Networking
foreach (Client c in ConnectedClients) foreach (Client c in ConnectedClients)
{ {
if (c.Character != null && c.Character.Removed) { c.Character = null; }
c.UpdateSoundPosition(); c.UpdateSoundPosition();
} }
@@ -558,6 +579,13 @@ namespace Barotrauma.Networking
try try
{ {
incomingMessagesToProcess.Clear();
incomingMessagesToProcess.AddRange(pendingIncomingMessages);
foreach (var inc in incomingMessagesToProcess)
{
ReadDataMessage(inc);
}
pendingIncomingMessages.Clear();
clientPeer?.Update(deltaTime); clientPeer?.Update(deltaTime);
} }
catch (Exception e) catch (Exception e)
@@ -632,11 +660,23 @@ namespace Barotrauma.Networking
} }
} }
private CoroutineHandle startGameCoroutine; private readonly List<IReadMessage> pendingIncomingMessages = new List<IReadMessage>();
private readonly List<IReadMessage> incomingMessagesToProcess = new List<IReadMessage>();
private void ReadDataMessage(IReadMessage inc) private void ReadDataMessage(IReadMessage inc)
{ {
ServerPacketHeader header = (ServerPacketHeader)inc.ReadByte(); ServerPacketHeader header = (ServerPacketHeader)inc.ReadByte();
if (header != ServerPacketHeader.STARTGAMEFINALIZE &&
header != ServerPacketHeader.ENDGAME &&
roundInitStatus == RoundInitStatus.WaitingForStartGameFinalize)
{
//rewind the header byte we just read
inc.BitPosition -= 8;
pendingIncomingMessages.Add(inc);
return;
}
switch (header) switch (header)
{ {
case ServerPacketHeader.UPDATE_LOBBY: case ServerPacketHeader.UPDATE_LOBBY:
@@ -714,7 +754,13 @@ namespace Barotrauma.Networking
} }
break; break;
case ServerPacketHeader.STARTGAME: case ServerPacketHeader.STARTGAME:
startGameCoroutine = GameMain.Instance.ShowLoading(StartGame(inc), false); GameMain.Instance.ShowLoading(StartGame(inc), false);
break;
case ServerPacketHeader.STARTGAMEFINALIZE:
if (roundInitStatus == RoundInitStatus.WaitingForStartGameFinalize)
{
ReadStartGameFinalize(inc);
}
break; break;
case ServerPacketHeader.ENDGAME: case ServerPacketHeader.ENDGAME:
string endMessage = inc.ReadString(); string endMessage = inc.ReadString();
@@ -725,6 +771,8 @@ namespace Barotrauma.Networking
GameMain.GameSession.WinningTeam = winningTeam; GameMain.GameSession.WinningTeam = winningTeam;
GameMain.GameSession.Mission.Completed = true; GameMain.GameSession.Mission.Completed = true;
} }
roundInitStatus = RoundInitStatus.Interrupted;
CoroutineManager.StartCoroutine(EndGame(endMessage), "EndGame"); CoroutineManager.StartCoroutine(EndGame(endMessage), "EndGame");
break; break;
case ServerPacketHeader.CAMPAIGN_SETUP_INFO: case ServerPacketHeader.CAMPAIGN_SETUP_INFO:
@@ -775,7 +823,37 @@ namespace Barotrauma.Networking
break; break;
} }
} }
private void ReadStartGameFinalize(IReadMessage inc)
{
ushort contentToPreloadCount = inc.ReadUInt16();
List<ContentFile> contentToPreload = new List<ContentFile>();
for (int i = 0; i < contentToPreloadCount; i++)
{
ContentType contentType = (ContentType)inc.ReadByte();
string filePath = inc.ReadString();
contentToPreload.Add(new ContentFile(filePath, contentType));
}
GameMain.GameSession.EventManager.PreloadContent(contentToPreload);
int levelEqualityCheckVal = inc.ReadInt32();
if (Level.Loaded.EqualityCheckVal != levelEqualityCheckVal)
{
string errorMsg = "Level equality check failed. The level generated at your end doesn't match the level generated by the server (seed: " + Level.Loaded.Seed +
", sub: " + Submarine.MainSub.Info.Name + " (" + Submarine.MainSub.Info.MD5Hash.ShortHash + ")" +
", mirrored: " + Level.Loaded.Mirrored + ").";
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:LevelsDontMatch" + Level.Loaded.Seed, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
throw new Exception(errorMsg);
}
GameMain.GameSession.Mission?.ClientReadInitial(inc);
roundInitStatus = RoundInitStatus.Started;
}
private void OnDisconnect() private void OnDisconnect()
{ {
if (SteamManager.IsInitialized) if (SteamManager.IsInitialized)
@@ -884,6 +962,7 @@ namespace Barotrauma.Networking
} }
else else
{ {
connected = false;
connectCancelled = true; connectCancelled = true;
string msg = ""; string msg = "";
@@ -1108,6 +1187,7 @@ namespace Barotrauma.Networking
if (Character != null) Character.Remove(); if (Character != null) Character.Remove();
HasSpawned = false; HasSpawned = false;
eventErrorWritten = false; eventErrorWritten = false;
GameMain.NetLobbyScreen.StopWaitingForStartRound();
while (CoroutineManager.IsCoroutineRunning("EndGame")) while (CoroutineManager.IsCoroutineRunning("EndGame"))
{ {
@@ -1126,9 +1206,11 @@ namespace Barotrauma.Networking
EndVoteTickBox.Selected = false; EndVoteTickBox.Selected = false;
roundInitStatus = RoundInitStatus.Starting;
int seed = inc.ReadInt32(); int seed = inc.ReadInt32();
string levelSeed = inc.ReadString(); string levelSeed = inc.ReadString();
int levelEqualityCheckVal = inc.ReadInt32(); //int levelEqualityCheckVal = inc.ReadInt32();
float levelDifficulty = inc.ReadSingle(); float levelDifficulty = inc.ReadSingle();
byte losMode = inc.ReadByte(); byte losMode = inc.ReadByte();
@@ -1146,24 +1228,16 @@ namespace Barotrauma.Networking
int missionIndex = inc.ReadInt16(); int missionIndex = inc.ReadInt16();
bool respawnAllowed = inc.ReadBoolean(); bool respawnAllowed = inc.ReadBoolean();
bool loadSecondSub = inc.ReadBoolean();
bool disguisesAllowed = inc.ReadBoolean(); bool disguisesAllowed = inc.ReadBoolean();
bool rewiringAllowed = inc.ReadBoolean(); bool rewiringAllowed = inc.ReadBoolean();
bool allowRagdollButton = inc.ReadBoolean(); bool allowRagdollButton = inc.ReadBoolean();
ushort contentToPreloadCount = inc.ReadUInt16();
List<ContentFile> contentToPreload = new List<ContentFile>();
for (int i = 0; i < contentToPreloadCount; i++)
{
ContentType contentType = (ContentType)inc.ReadByte();
string filePath = inc.ReadString();
contentToPreload.Add(new ContentFile(filePath, contentType));
}
serverSettings.ReadMonsterEnabled(inc); serverSettings.ReadMonsterEnabled(inc);
bool includesFinalize = inc.ReadBoolean(); inc.ReadPadBits();
GameModePreset gameMode = GameModePreset.List.Find(gm => gm.Identifier == modeIdentifier); GameModePreset gameMode = GameModePreset.List.Find(gm => gm.Identifier == modeIdentifier);
MultiPlayerCampaign campaign = MultiPlayerCampaign campaign =
GameMain.NetLobbyScreen.SelectedMode == GameMain.GameSession?.GameMode.Preset && gameMode == GameMain.NetLobbyScreen.SelectedMode ? GameMain.NetLobbyScreen.SelectedMode == GameMain.GameSession?.GameMode.Preset && gameMode == GameMain.NetLobbyScreen.SelectedMode ?
@@ -1237,31 +1311,114 @@ namespace Barotrauma.Networking
yield return CoroutineStatus.Failure; yield return CoroutineStatus.Failure;
} }
MissionPrefab missionPrefab = missionIndex < 0 ? null : MissionPrefab.List[missionIndex];
GameMain.GameSession = missionIndex < 0 ? GameMain.GameSession = missionIndex < 0 ?
new GameSession(GameMain.NetLobbyScreen.SelectedSub, "", gameMode, MissionType.None) : new GameSession(GameMain.NetLobbyScreen.SelectedSub, "", gameMode, MissionType.None) :
new GameSession(GameMain.NetLobbyScreen.SelectedSub, "", gameMode, MissionPrefab.List[missionIndex]); new GameSession(GameMain.NetLobbyScreen.SelectedSub, "", gameMode, missionPrefab);
GameMain.GameSession.StartRound(levelSeed, levelDifficulty, loadSecondSub);
//startRoundTask = Task.Run(async () => { await Task.Yield(); GameMain.GameSession.StartRound(levelSeed, levelDifficulty); });
GameMain.GameSession.StartRound(levelSeed, levelDifficulty);
} }
else else
{ {
if (GameMain.GameSession?.CrewManager != null) GameMain.GameSession.CrewManager.Reset(); if (GameMain.GameSession?.CrewManager != null) GameMain.GameSession.CrewManager.Reset();
/*startRoundTask = Task.Run(async () =>
{
await Task.Yield();
GameMain.GameSession.StartRound(campaign.Map.SelectedConnection.Level,
reloadSub: true,
mirrorLevel: campaign.Map.CurrentLocation != campaign.Map.SelectedConnection.Locations[0]);
});*/
GameMain.GameSession.StartRound(campaign.Map.SelectedConnection.Level, GameMain.GameSession.StartRound(campaign.Map.SelectedConnection.Level,
reloadSub: true, mirrorLevel: campaign.Map.CurrentLocation != campaign.Map.SelectedConnection.Locations[0]);
loadSecondSub: false,
mirrorLevel: campaign.Map.CurrentLocation != campaign.Map.SelectedConnection.Locations[0]);
} }
GameMain.GameSession.Mission?.ClientReadInitial(inc); roundInitStatus = RoundInitStatus.WaitingForStartGameFinalize;
if (GameMain.GameSession.Submarine.IsFileCorrupted) DateTime? timeOut = null;
DateTime requestFinalizeTime = DateTime.Now;
TimeSpan requestFinalizeInterval = new TimeSpan(0, 0, 2);
while (true)
{ {
DebugConsole.ThrowError($"Failed to start a round. Could not load the submarine \"{GameMain.GameSession.Submarine.Name}\"."); try
{
if (timeOut.HasValue)
{
if (DateTime.Now > requestFinalizeTime)
{
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ClientPacketHeader.REQUEST_STARTGAMEFINALIZE);
clientPeer.Send(msg, DeliveryMethod.Unreliable);
requestFinalizeTime = DateTime.Now + requestFinalizeInterval;
}
if (DateTime.Now > timeOut)
{
DebugConsole.ThrowError("Error while starting the round (did not receive STARTGAMEFINALIZE message from the server). Stopping the round...");
roundInitStatus = RoundInitStatus.TimedOut;
break;
}
}
else
{
if (includesFinalize)
{
ReadStartGameFinalize(inc);
break;
}
//wait for up to 30 seconds for the server to send the STARTGAMEFINALIZE message
timeOut = DateTime.Now + new TimeSpan(0, 0, seconds: 30);
}
if (!connected)
{
roundInitStatus = RoundInitStatus.Interrupted;
break;
}
if (roundInitStatus != RoundInitStatus.WaitingForStartGameFinalize)
{
break;
}
clientPeer.Update((float)Timing.Step);
}
catch (Exception e)
{
DebugConsole.ThrowError("There was an error initializing the round.", e, true);
roundInitStatus = RoundInitStatus.Error;
break;
}
//waiting for a STARTGAMEFINALIZE message
yield return CoroutineStatus.Running;
}
if (roundInitStatus != RoundInitStatus.Started)
{
if (roundInitStatus != RoundInitStatus.Interrupted)
{
DebugConsole.ThrowError(roundInitStatus.ToString());
CoroutineManager.StartCoroutine(EndGame(""));
yield return CoroutineStatus.Failure;
}
else
{
yield return CoroutineStatus.Success;
}
}
if (GameMain.GameSession.Submarine.Info.IsFileCorrupted)
{
DebugConsole.ThrowError($"Failed to start a round. Could not load the submarine \"{GameMain.GameSession.Submarine.Info.Name}\".");
yield return CoroutineStatus.Failure; yield return CoroutineStatus.Failure;
} }
for (int i = 0; i < Submarine.MainSubs.Length; i++) for (int i = 0; i < Submarine.MainSubs.Length; i++)
{ {
if (!loadSecondSub && i > 0) { break; } if (Submarine.MainSubs[i] == null) { break; }
var teamID = i == 0 ? Character.TeamType.Team1 : Character.TeamType.Team2; var teamID = i == 0 ? Character.TeamType.Team1 : Character.TeamType.Team2;
Submarine.MainSubs[i].TeamID = teamID; Submarine.MainSubs[i].TeamID = teamID;
@@ -1271,23 +1428,10 @@ namespace Barotrauma.Networking
} }
} }
if (Level.Loaded.EqualityCheckVal != levelEqualityCheckVal)
{
string errorMsg = "Level equality check failed. The level generated at your end doesn't match the level generated by the server (seed: " + Level.Loaded.Seed +
", sub: " + Submarine.MainSub.Name + " (" + Submarine.MainSub.MD5Hash.ShortHash + ")" +
", mirrored: " + Level.Loaded.Mirrored + ").";
DebugConsole.ThrowError(errorMsg, createMessageBox: true);
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:LevelsDontMatch" + levelSeed, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
CoroutineManager.StartCoroutine(EndGame(""));
yield return CoroutineStatus.Failure;
}
if (respawnAllowed) { respawnManager = new RespawnManager(this, GameMain.NetLobbyScreen.UsingShuttle ? GameMain.NetLobbyScreen.SelectedShuttle : null); } if (respawnAllowed) { respawnManager = new RespawnManager(this, GameMain.NetLobbyScreen.UsingShuttle ? GameMain.NetLobbyScreen.SelectedShuttle : null); }
GameMain.GameSession.EventManager.PreloadContent(contentToPreload);
ServerSettings.ServerDetailsChanged = true;
gameStarted = true; gameStarted = true;
ServerSettings.ServerDetailsChanged = true;
GameMain.GameScreen.Select(); GameMain.GameScreen.Select();
@@ -1358,8 +1502,8 @@ namespace Barotrauma.Networking
bool requiredContentPackagesInstalled = inc.ReadBoolean(); bool requiredContentPackagesInstalled = inc.ReadBoolean();
var matchingSub = var matchingSub =
Submarine.SavedSubmarines.FirstOrDefault(s => s.Name == subName && s.MD5Hash.Hash == subHash) ?? SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == subName && s.MD5Hash.Hash == subHash) ??
new Submarine(Path.Combine(Submarine.SavePath, subName) + ".sub", subHash, false); new SubmarineInfo(Path.Combine(SubmarineInfo.SavePath, subName) + ".sub", subHash);
matchingSub.RequiredContentPackagesInstalled = requiredContentPackagesInstalled; matchingSub.RequiredContentPackagesInstalled = requiredContentPackagesInstalled;
serverSubmarines.Add(matchingSub); serverSubmarines.Add(matchingSub);
@@ -1394,6 +1538,7 @@ namespace Barotrauma.Networking
string preferredJob = inc.ReadString(); string preferredJob = inc.ReadString();
UInt16 characterID = inc.ReadUInt16(); UInt16 characterID = inc.ReadUInt16();
bool muted = inc.ReadBoolean(); bool muted = inc.ReadBoolean();
bool inGame = inc.ReadBoolean();
bool allowKicking = inc.ReadBoolean(); bool allowKicking = inc.ReadBoolean();
inc.ReadPadBits(); inc.ReadPadBits();
@@ -1406,6 +1551,7 @@ namespace Barotrauma.Networking
PreferredJob = preferredJob, PreferredJob = preferredJob,
CharacterID = characterID, CharacterID = characterID,
Muted = muted, Muted = muted,
InGame = inGame,
AllowKicking = allowKicking AllowKicking = allowKicking
}); });
} }
@@ -1424,6 +1570,7 @@ namespace Barotrauma.Networking
{ {
SteamID = tc.SteamID, SteamID = tc.SteamID,
Muted = tc.Muted, Muted = tc.Muted,
InGame = tc.InGame,
AllowKicking = tc.AllowKicking AllowKicking = tc.AllowKicking
}; };
ConnectedClients.Add(existingClient); ConnectedClients.Add(existingClient);
@@ -1433,15 +1580,12 @@ namespace Barotrauma.Networking
existingClient.PreferredJob = tc.PreferredJob; existingClient.PreferredJob = tc.PreferredJob;
existingClient.Character = null; existingClient.Character = null;
existingClient.Muted = tc.Muted; existingClient.Muted = tc.Muted;
existingClient.InGame = tc.InGame;
existingClient.AllowKicking = tc.AllowKicking; existingClient.AllowKicking = tc.AllowKicking;
GameMain.NetLobbyScreen.SetPlayerNameAndJobPreference(existingClient); GameMain.NetLobbyScreen.SetPlayerNameAndJobPreference(existingClient);
if (tc.CharacterID > 0) if (Screen.Selected != GameMain.NetLobbyScreen && tc.CharacterID > 0)
{ {
existingClient.Character = Entity.FindEntityByID(tc.CharacterID) as Character; existingClient.CharacterID = tc.CharacterID;
if (existingClient.Character == null)
{
updateClientListId = false;
}
} }
if (existingClient.ID == myID) if (existingClient.ID == myID)
{ {
@@ -1888,15 +2032,15 @@ namespace Barotrauma.Networking
{ {
case FileTransferType.Submarine: case FileTransferType.Submarine:
new GUIMessageBox(TextManager.Get("ServerDownloadFinished"), TextManager.GetWithVariable("FileDownloadedNotification", "[filename]", transfer.FileName)); new GUIMessageBox(TextManager.Get("ServerDownloadFinished"), TextManager.GetWithVariable("FileDownloadedNotification", "[filename]", transfer.FileName));
var newSub = new Submarine(transfer.FilePath); var newSub = new SubmarineInfo(transfer.FilePath);
if (newSub.IsFileCorrupted) { return; } if (newSub.IsFileCorrupted) { return; }
var existingSubs = Submarine.SavedSubmarines.Where(s => s.Name == newSub.Name && s.MD5Hash.Hash == newSub.MD5Hash.Hash).ToList(); var existingSubs = SubmarineInfo.SavedSubmarines.Where(s => s.Name == newSub.Name && s.MD5Hash.Hash == newSub.MD5Hash.Hash).ToList();
foreach (Submarine existingSub in existingSubs) foreach (SubmarineInfo existingSub in existingSubs)
{ {
existingSub.Dispose(); existingSub.Dispose();
} }
Submarine.AddToSavedSubs(newSub); SubmarineInfo.AddToSavedSubs(newSub);
for (int i = 0; i < 2; i++) for (int i = 0; i < 2; i++)
{ {
@@ -1905,8 +2049,8 @@ namespace Barotrauma.Networking
GameMain.NetLobbyScreen.SubList.Content.Children; GameMain.NetLobbyScreen.SubList.Content.Children;
var subElement = subListChildren.FirstOrDefault(c => var subElement = subListChildren.FirstOrDefault(c =>
((Submarine)c.UserData).Name == newSub.Name && ((SubmarineInfo)c.UserData).Name == newSub.Name &&
((Submarine)c.UserData).MD5Hash.Hash == newSub.MD5Hash.Hash); ((SubmarineInfo)c.UserData).MD5Hash.Hash == newSub.MD5Hash.Hash);
if (subElement == null) continue; if (subElement == null) continue;
subElement.GetChild<GUITextBlock>().TextColor = new Color(subElement.GetChild<GUITextBlock>().TextColor, 1.0f); subElement.GetChild<GUITextBlock>().TextColor = new Color(subElement.GetChild<GUITextBlock>().TextColor, 1.0f);
@@ -1934,17 +2078,17 @@ namespace Barotrauma.Networking
if (campaign == null) { return; } if (campaign == null) { return; }
GameMain.GameSession.SavePath = transfer.FilePath; GameMain.GameSession.SavePath = transfer.FilePath;
if (GameMain.GameSession.Submarine == null) if (GameMain.GameSession.SubmarineInfo == null)
{ {
var gameSessionDoc = SaveUtil.LoadGameSessionDoc(GameMain.GameSession.SavePath); var gameSessionDoc = SaveUtil.LoadGameSessionDoc(GameMain.GameSession.SavePath);
string subPath = Path.Combine(SaveUtil.TempPath, gameSessionDoc.Root.GetAttributeString("submarine", "")) + ".sub"; string subPath = Path.Combine(SaveUtil.TempPath, gameSessionDoc.Root.GetAttributeString("submarine", "")) + ".sub";
GameMain.GameSession.Submarine = new Submarine(subPath, ""); GameMain.GameSession.SubmarineInfo = new SubmarineInfo(subPath, "");
} }
SaveUtil.LoadGame(GameMain.GameSession.SavePath, GameMain.GameSession); SaveUtil.LoadGame(GameMain.GameSession.SavePath, GameMain.GameSession);
GameMain.GameSession?.Submarine?.CheckSubsLeftBehind(); GameMain.GameSession?.Submarine?.CheckSubsLeftBehind();
if (GameMain.GameSession?.Submarine?.Name != null) if (GameMain.GameSession?.SubmarineInfo?.Name != null)
{ {
GameMain.NetLobbyScreen.TryDisplayCampaignSubmarine(GameMain.GameSession.Submarine); GameMain.NetLobbyScreen.TryDisplayCampaignSubmarine(GameMain.GameSession.SubmarineInfo);
} }
campaign.LastSaveID = campaign.PendingSaveID; campaign.LastSaveID = campaign.PendingSaveID;
@@ -1979,8 +2123,7 @@ namespace Barotrauma.Networking
{ {
if (!permissions.HasFlag(ClientPermissions.ConsoleCommands)) { return false; } if (!permissions.HasFlag(ClientPermissions.ConsoleCommands)) { return false; }
commandName = commandName.ToLowerInvariant(); if (permittedConsoleCommands.Any(c => c.Equals(commandName, StringComparison.OrdinalIgnoreCase))) { return true; }
if (permittedConsoleCommands.Any(c => c.ToLowerInvariant() == commandName)) { return true; }
//check aliases //check aliases
foreach (DebugConsole.Command command in DebugConsole.Commands) foreach (DebugConsole.Command command in DebugConsole.Commands)
@@ -2231,7 +2374,7 @@ namespace Barotrauma.Networking
clientPeer.Send(msg, DeliveryMethod.Reliable); clientPeer.Send(msg, DeliveryMethod.Reliable);
} }
public void SetupNewCampaign(Submarine sub, string saveName, string mapSeed) public void SetupNewCampaign(SubmarineInfo sub, string saveName, string mapSeed)
{ {
GameMain.NetLobbyScreen.CampaignSetupFrame.Visible = false; GameMain.NetLobbyScreen.CampaignSetupFrame.Visible = false;
@@ -2440,7 +2583,7 @@ namespace Barotrauma.Networking
if (GUI.KeyboardDispatcher.Subscriber == null) if (GUI.KeyboardDispatcher.Subscriber == null)
{ {
bool chatKeyHit = PlayerInput.KeyHit(InputType.Chat); bool chatKeyHit = PlayerInput.KeyHit(InputType.Chat);
bool radioKeyHit = PlayerInput.KeyHit(InputType.RadioChat); bool radioKeyHit = PlayerInput.KeyHit(InputType.RadioChat) && (Character.Controlled == null || Character.Controlled.SpeechImpediment < 0);
if (chatKeyHit || radioKeyHit) if (chatKeyHit || radioKeyHit)
{ {
@@ -2498,6 +2641,7 @@ namespace Barotrauma.Networking
{ {
var transfer = fileReceiver.ActiveTransfers.First(); var transfer = fileReceiver.ActiveTransfers.First();
GameMain.NetLobbyScreen.FileTransferFrame.Visible = true; GameMain.NetLobbyScreen.FileTransferFrame.Visible = true;
GameMain.NetLobbyScreen.FileTransferFrame.UserData = transfer;
GameMain.NetLobbyScreen.FileTransferTitle.Text = GameMain.NetLobbyScreen.FileTransferTitle.Text =
ToolBox.LimitString( ToolBox.LimitString(
TextManager.GetWithVariable("DownloadingFile", "[filename]", transfer.FileName), TextManager.GetWithVariable("DownloadingFile", "[filename]", transfer.FileName),
@@ -2789,7 +2933,7 @@ namespace Barotrauma.Networking
} }
if (GameMain.GameSession?.Submarine != null) if (GameMain.GameSession?.Submarine != null)
{ {
errorLines.Add("Submarine: " + GameMain.GameSession.Submarine.Name); errorLines.Add("Submarine: " + GameMain.GameSession.Submarine.Info.Name);
} }
if (Level.Loaded != null) if (Level.Loaded != null)
{ {
@@ -2811,8 +2955,8 @@ namespace Barotrauma.Networking
errorLines.Add(" " + DebugConsole.Messages[i].Time + " - " + DebugConsole.Messages[i].Text); errorLines.Add(" " + DebugConsole.Messages[i].Time + " - " + DebugConsole.Messages[i].Text);
} }
string filePath = "event_error_log_client_" + Name + "_" + ToolBox.RemoveInvalidFileNameChars(DateTime.UtcNow.ToShortTimeString() + ".log"); string filePath = "event_error_log_client_" + Name + "_" + DateTime.UtcNow.ToShortTimeString() + ".log";
filePath = Path.Combine(ServerLog.SavePath, filePath); filePath = Path.Combine(ServerLog.SavePath, ToolBox.RemoveInvalidFileNameChars(filePath));
if (!Directory.Exists(ServerLog.SavePath)) if (!Directory.Exists(ServerLog.SavePath))
{ {
@@ -91,6 +91,7 @@ namespace Barotrauma.Networking
return; return;
} }
incomingLidgrenMessages.Clear();
netClient.ReadMessages(incomingLidgrenMessages); netClient.ReadMessages(incomingLidgrenMessages);
foreach (NetIncomingMessage inc in incomingLidgrenMessages) foreach (NetIncomingMessage inc in incomingLidgrenMessages)
@@ -107,8 +108,6 @@ namespace Barotrauma.Networking
break; break;
} }
} }
incomingLidgrenMessages.Clear();
} }
private void HandleDataMessage(NetIncomingMessage inc) private void HandleDataMessage(NetIncomingMessage inc)
@@ -204,8 +204,9 @@ namespace Barotrauma.Networking
} }
} }
while (Steamworks.SteamNetworking.IsP2PPacketAvailable()) for (int i=0;i<100;i++)
{ {
if (!Steamworks.SteamNetworking.IsP2PPacketAvailable()) { break; }
var packet = Steamworks.SteamNetworking.ReadP2PPacket(); var packet = Steamworks.SteamNetworking.ReadP2PPacket();
if (packet.HasValue) if (packet.HasValue)
{ {
@@ -571,6 +571,9 @@ namespace Barotrauma.Networking
var ragdollButtonBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), TextManager.Get("ServerSettingsAllowRagdollButton")); var ragdollButtonBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), TextManager.Get("ServerSettingsAllowRagdollButton"));
GetPropertyData("AllowRagdollButton").AssignGUIComponent(ragdollButtonBox); GetPropertyData("AllowRagdollButton").AssignGUIComponent(ragdollButtonBox);
var disableBotConversationsBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), TextManager.Get("ServerSettingsDisableBotConversations"));
GetPropertyData("DisableBotConversations").AssignGUIComponent(disableBotConversationsBox);
/*var traitorRatioBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), TextManager.Get("ServerSettingsUseTraitorRatio")); /*var traitorRatioBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), TextManager.Get("ServerSettingsUseTraitorRatio"));
CreateLabeledSlider(roundsTab, "", out slider, out sliderLabel); CreateLabeledSlider(roundsTab, "", out slider, out sliderLabel);
@@ -15,6 +15,8 @@ namespace Barotrauma.Steam
{ {
static partial class SteamManager static partial class SteamManager
{ {
private static Dictionary<Steamworks.Data.PublishedFileId, Task> modCopiesInProgress = new Dictionary<Steamworks.Data.PublishedFileId, Task>();
private static void InitializeProjectSpecific() private static void InitializeProjectSpecific()
{ {
if (isInitialized) { return; } if (isInitialized) { return; }
@@ -63,20 +65,6 @@ namespace Barotrauma.Steam
private static void UpdateProjectSpecific(float deltaTime) private static void UpdateProjectSpecific(float deltaTime)
{ {
for (int i=0;i<(ugcResultPageTasks?.Count ?? 0);i++)
{
var task = ugcResultPageTasks[i];
if (task.IsCompleted)
{
if (!task.IsCompletedSuccessfully)
{
DebugConsole.ThrowError("Failed to retrieve Steam Workshop page info: TaskStatus = "+task.Status.ToString());
}
ugcResultPageTasks.RemoveAt(i);
i--;
}
}
if (ugcSubscriptionTasks != null) if (ugcSubscriptionTasks != null)
{ {
var ugcSubscriptionKeys = ugcSubscriptionTasks.Keys.ToList(); var ugcSubscriptionKeys = ugcSubscriptionTasks.Keys.ToList();
@@ -525,7 +513,37 @@ namespace Barotrauma.Steam
} }
} }
private static List<Task> ugcResultPageTasks; private static async Task<List<Steamworks.Ugc.Item>> GetWorkshopItemsAsync(Steamworks.Ugc.Query query, int clampResults = 0, Predicate<Steamworks.Ugc.Item> itemPredicate=null)
{
await Task.Yield();
int pageIndex = 1;
Steamworks.Ugc.ResultPage? resultPage = await query.GetPageAsync(pageIndex);
List<Steamworks.Ugc.Item> retVal = new List<Steamworks.Ugc.Item>();
while (resultPage.HasValue && resultPage?.ResultCount > 0)
{
if (itemPredicate != null)
{
retVal.AddRange(resultPage.Value.Entries.Where(it => itemPredicate(it)));
}
else
{
retVal.AddRange(resultPage.Value.Entries);
}
if (clampResults > 0 && retVal.Count >= clampResults)
{
retVal = retVal.Take(clampResults).ToList();
break;
}
pageIndex++;
resultPage = await query.GetPageAsync(pageIndex);
}
return retVal;
}
public static void GetSubscribedWorkshopItems(Action<IList<Steamworks.Ugc.Item>> onItemsFound, List<string> requireTags = null) public static void GetSubscribedWorkshopItems(Action<IList<Steamworks.Ugc.Item>> onItemsFound, List<string> requireTags = null)
{ {
@@ -535,30 +553,9 @@ namespace Barotrauma.Steam
.RankedByTotalUniqueSubscriptions() .RankedByTotalUniqueSubscriptions()
.WhereUserSubscribed() .WhereUserSubscribed()
.WithLongDescription(); .WithLongDescription();
if (requireTags != null) query.WithTags(requireTags); if (requireTags != null) { query = query.WithTags(requireTags); }
ugcResultPageTasks ??= new List<Task>(); TaskPool.Add(GetWorkshopItemsAsync(query), (task) => { onItemsFound?.Invoke(task.Result); });
ugcResultPageTasks.Add(Task.Run(async () =>
{
int processedResults = 0; int pageIndex = 1;
Steamworks.Ugc.ResultPage? resultPage = await query.GetPageAsync(pageIndex);
while (resultPage.HasValue && resultPage?.ResultCount > 0)
{
onItemsFound?.Invoke(resultPage.Value.Entries.ToList());
processedResults += resultPage.Value.ResultCount;
pageIndex++;
if (processedResults < resultPage?.TotalCount)
{
resultPage = await query.GetPageAsync(pageIndex);
}
else
{
resultPage = null;
}
}
}));
} }
public static void GetPopularWorkshopItems(Action<IList<Steamworks.Ugc.Item>> onItemsFound, int amount, List<string> requireTags = null) public static void GetPopularWorkshopItems(Action<IList<Steamworks.Ugc.Item>> onItemsFound, int amount, List<string> requireTags = null)
@@ -570,65 +567,40 @@ namespace Barotrauma.Steam
.WithLongDescription(); .WithLongDescription();
if (requireTags != null) query.WithTags(requireTags); if (requireTags != null) query.WithTags(requireTags);
ugcResultPageTasks ??= new List<Task>(); TaskPool.Add(GetWorkshopItemsAsync(query, amount, (item) => !item.IsSubscribed), (task) => {
ugcResultPageTasks.Add(Task.Run(async () => var entries = task.Result;
{
int processedResults = 0; int pageIndex = 1;
Steamworks.Ugc.ResultPage? resultPage = await query.GetPageAsync(pageIndex);
while (resultPage.HasValue && resultPage?.ResultCount > 0) //count the number of each unique tag
foreach (var item in entries)
{ {
var entries = resultPage.Value.Entries.ToList(); foreach (string tag in item.Tags)
//count the number of each unique tag
foreach (var item in entries)
{ {
foreach (string tag in item.Tags) if (string.IsNullOrEmpty(tag)) { continue; }
string caseInvariantTag = tag.ToLowerInvariant();
if (!tagCommonness.ContainsKey(caseInvariantTag))
{ {
if (string.IsNullOrEmpty(tag)) { continue; } tagCommonness[caseInvariantTag] = 1;
string caseInvariantTag = tag.ToLowerInvariant();
if (!tagCommonness.ContainsKey(caseInvariantTag))
{
tagCommonness[caseInvariantTag] = 1;
}
else
{
tagCommonness[caseInvariantTag]++;
}
} }
} else
//populate the popularTags list with tags sorted by commonness
popularTags.Clear();
foreach (KeyValuePair<string, int> tagCommonnessKVP in tagCommonness)
{
int i = 0;
while (i < popularTags.Count &&
tagCommonness[popularTags[i]] > tagCommonnessKVP.Value)
{ {
i++; tagCommonness[caseInvariantTag]++;
} }
popularTags.Insert(i, tagCommonnessKVP.Key);
}
var nonSubscribedItems = entries.Where(it => !it.IsSubscribed);
if (nonSubscribedItems.Count() > (amount-processedResults))
{
nonSubscribedItems = nonSubscribedItems.Take(amount - processedResults);
}
onItemsFound?.Invoke(nonSubscribedItems.ToList());
processedResults += resultPage.Value.ResultCount;
pageIndex++;
if (processedResults < resultPage?.TotalCount && processedResults < amount)
{
resultPage = await query.GetPageAsync(pageIndex);
}
else
{
resultPage = null;
} }
} }
})); //populate the popularTags list with tags sorted by commonness
popularTags.Clear();
foreach (KeyValuePair<string, int> tagCommonnessKVP in tagCommonness)
{
int i = 0;
while (i < popularTags.Count &&
tagCommonness[popularTags[i]] > tagCommonnessKVP.Value)
{
i++;
}
popularTags.Insert(i, tagCommonnessKVP.Key);
}
onItemsFound?.Invoke(task.Result);
});
} }
public static void GetPublishedWorkshopItems(Action<IList<Steamworks.Ugc.Item>> onItemsFound, List<string> requireTags = null) public static void GetPublishedWorkshopItems(Action<IList<Steamworks.Ugc.Item>> onItemsFound, List<string> requireTags = null)
@@ -641,28 +613,7 @@ namespace Barotrauma.Steam
.WithLongDescription(); .WithLongDescription();
if (requireTags != null) query.WithTags(requireTags); if (requireTags != null) query.WithTags(requireTags);
ugcResultPageTasks ??= new List<Task>(); TaskPool.Add(GetWorkshopItemsAsync(query), (task) => { onItemsFound?.Invoke(task.Result); });
ugcResultPageTasks.Add(Task.Run(async () =>
{
int processedResults = 0; int pageIndex = 1;
Steamworks.Ugc.ResultPage? resultPage = await query.GetPageAsync(pageIndex);
while (resultPage.HasValue && resultPage?.ResultCount > 0)
{
onItemsFound?.Invoke(resultPage.Value.Entries.ToList());
processedResults += resultPage.Value.ResultCount;
pageIndex++;
if (processedResults < resultPage?.TotalCount)
{
resultPage = await query.GetPageAsync(pageIndex);
}
else
{
resultPage = null;
}
}
}));
} }
private static Dictionary<ulong, Task> ugcSubscriptionTasks; private static Dictionary<ulong, Task> ugcSubscriptionTasks;
@@ -926,7 +877,7 @@ namespace Barotrauma.Steam
/// <summary> /// <summary>
/// Enables a workshop item by moving it to the game folder. /// Enables a workshop item by moving it to the game folder.
/// </summary> /// </summary>
public static bool EnableWorkShopItem(Steamworks.Ugc.Item? item, bool allowFileOverwrite, out string errorMsg) public static bool EnableWorkShopItem(Steamworks.Ugc.Item? item, bool allowFileOverwrite, out string errorMsg, bool selectContentPackage = false, bool suppressInstallNotif = false)
{ {
if (!(item?.IsInstalled ?? false)) if (!(item?.IsInstalled ?? false))
{ {
@@ -972,57 +923,116 @@ namespace Barotrauma.Steam
return false; return false;
} }
GameMain.Config.SuppressModFolderWatcher = true; Task<string> newTask = null;
CopyWorkShopItem(item, contentPackage, newContentPackagePath, metaDataFilePath, allowFileOverwrite, out errorMsg); lock (modCopiesInProgress)
var newPackage = new ContentPackage(contentPackage.Path, newContentPackagePath)
{ {
SteamWorkshopUrl = item?.Url, if (modCopiesInProgress.ContainsKey(item.Value.Id))
InstallTime = item?.Updated > item?.Created ? item?.Updated : item?.Created {
}; if (!modCopiesInProgress[item.Value.Id].IsCompleted &&
!modCopiesInProgress[item.Value.Id].IsFaulted &&
foreach (ContentFile contentFile in newPackage.Files) !modCopiesInProgress[item.Value.Id].IsCanceled)
{ {
contentFile.Path = CorrectContentFilePath(contentFile.Path, contentPackage, true); errorMsg = ""; return true;
}
modCopiesInProgress.Remove(item.Value.Id);
}
newTask = CopyWorkShopItemAsync(item, contentPackage, newContentPackagePath, metaDataFilePath, allowFileOverwrite);
modCopiesInProgress.Add(item.Value.Id, newTask);
} }
TaskPool.Add(newTask,
contentPackage,
(task, cp) =>
{
if (task.IsFaulted || task.IsCanceled)
{
DebugConsole.ThrowError($"Failed to copy \"{item?.Title}\"", task.Exception);
GameMain.SteamWorkshopScreen?.SetReinstallButtonStatus(item, true, GUI.Style.Red);
return;
}
if (!string.IsNullOrWhiteSpace(task.Result))
{
DebugConsole.ThrowError($"Failed to copy \"{item?.Title}\": {task.Result}");
GameMain.SteamWorkshopScreen?.SetReinstallButtonStatus(item, true, GUI.Style.Red);
return;
}
if (!Directory.Exists(Path.GetDirectoryName(newContentPackagePath))) GameMain.Config.SuppressModFolderWatcher = true;
{
Directory.CreateDirectory(Path.GetDirectoryName(newContentPackagePath));
}
newPackage.Save(newContentPackagePath);
ContentPackage.List.Add(newPackage);
if (newPackage.CorePackage)
{
GameMain.Config.SelectCorePackage(newPackage);
}
else
{
GameMain.Config.SelectContentPackage(newPackage);
}
GameMain.Config.SaveNewPlayerConfig();
GameMain.Config.WarnIfContentPackageSelectionDirty(); var newPackage = new ContentPackage(cp.Path, newContentPackagePath)
{
SteamWorkshopUrl = item?.Url,
InstallTime = item?.Updated > item?.Created ? item?.Updated : item?.Created
};
GameMain.Config.SuppressModFolderWatcher = false; foreach (ContentFile contentFile in newPackage.Files)
{
contentFile.Path = CorrectContentFilePath(contentFile.Path, cp, true);
}
if (newPackage.Files.Any(f => f.Type == ContentType.Submarine)) if (!Directory.Exists(Path.GetDirectoryName(newContentPackagePath)))
{ {
Submarine.RefreshSavedSubs(); Directory.CreateDirectory(Path.GetDirectoryName(newContentPackagePath));
} }
newPackage.Save(newContentPackagePath);
ContentPackage.List.Add(newPackage);
if (selectContentPackage)
{
if (newPackage.CorePackage)
{
GameMain.Config.SelectCorePackage(newPackage);
}
else
{
GameMain.Config.SelectContentPackage(newPackage);
}
GameMain.Config.SaveNewPlayerConfig();
GameMain.Config.WarnIfContentPackageSelectionDirty();
if (newPackage.Files.Any(f => f.Type == ContentType.Submarine))
{
SubmarineInfo.RefreshSavedSubs();
}
}
else if (!suppressInstallNotif)
{
GameMain.MainMenuScreen?.SetEnableModsNotification(true);
}
GameMain.Config.SuppressModFolderWatcher = false;
GameMain.SteamWorkshopScreen?.SetReinstallButtonStatus(item, true, GUI.Style.Green);
});
errorMsg = ""; errorMsg = "";
return true; return true;
} }
private static bool CopyWorkShopItem(Steamworks.Ugc.Item? item, ContentPackage contentPackage, string newContentPackagePath, string metaDataFilePath, bool allowFileOverwrite, out string errorMsg) /// <summary>
/// Asynchronously copies a Workshop item into the Mods folder.
/// </summary>
/// <returns>Returns an empty string on success, otherwise returns an error message.</returns>
private async static Task<string> CopyWorkShopItemAsync(Steamworks.Ugc.Item? item, ContentPackage contentPackage, string newContentPackagePath, string metaDataFilePath, bool allowFileOverwrite)
{ {
errorMsg = ""; await Task.Yield();
string targetPath = Path.GetDirectoryName(GetWorkshopItemContentPackagePath(contentPackage));
string copyingPath = Path.Combine(targetPath, CopyIndicatorFileName);
string errorMsg = "";
if (contentPackage.GameVersion > new Version(0, 9, 1, 0)) if (contentPackage.GameVersion > new Version(0, 9, 1, 0))
{ {
SaveUtil.CopyFolder(item?.Directory, Path.GetDirectoryName(GetWorkshopItemContentPackagePath(contentPackage)), copySubDirs: true, overwriteExisting: true); Directory.CreateDirectory(targetPath);
return true; File.WriteAllText(copyingPath, "TEMPORARY FILE");
SaveUtil.CopyFolder(item?.Directory, targetPath, copySubDirs: true, overwriteExisting: true);
File.Delete(copyingPath);
return "";
} }
var allPackageFiles = Directory.GetFiles(item?.Directory, "*", SearchOption.AllDirectories); var allPackageFiles = Directory.GetFiles(item?.Directory, "*", SearchOption.AllDirectories);
@@ -1042,7 +1052,7 @@ namespace Barotrauma.Steam
{ {
errorMsg = TextManager.GetWithVariables("WorkshopErrorOverwriteOnEnable", new string[2] { "[itemname]", "[filename]" }, new string[2] { item?.Title, newContentPackagePath }); errorMsg = TextManager.GetWithVariables("WorkshopErrorOverwriteOnEnable", new string[2] { "[itemname]", "[filename]" }, new string[2] { item?.Title, newContentPackagePath });
DebugConsole.NewMessage(errorMsg, Color.Red); DebugConsole.NewMessage(errorMsg, Color.Red);
return false; return errorMsg;
} }
foreach (ContentFile contentFile in contentPackage.Files) foreach (ContentFile contentFile in contentPackage.Files)
@@ -1053,84 +1063,79 @@ namespace Barotrauma.Steam
{ {
errorMsg = TextManager.GetWithVariables("WorkshopErrorOverwriteOnEnable", new string[2] { "[itemname]", "[filename]" }, new string[2] { item?.Title, contentFile.Path }); errorMsg = TextManager.GetWithVariables("WorkshopErrorOverwriteOnEnable", new string[2] { "[itemname]", "[filename]" }, new string[2] { item?.Title, contentFile.Path });
DebugConsole.NewMessage(errorMsg, Color.Red); DebugConsole.NewMessage(errorMsg, Color.Red);
return false; return errorMsg;
} }
} }
} }
try Directory.CreateDirectory(targetPath);
File.WriteAllText(copyingPath, "TEMPORARY FILE");
foreach (ContentFile contentFile in contentPackage.Files)
{ {
foreach (ContentFile contentFile in contentPackage.Files) contentFile.Path = contentFile.Path.CleanUpPath();
string sourceFile = Path.Combine(item?.Directory, contentFile.Path);
if (!File.Exists(sourceFile))
{ {
contentFile.Path = contentFile.Path.CleanUpPath(); string[] splitPath = contentFile.Path.Split('/');
string sourceFile = Path.Combine(item?.Directory, contentFile.Path); if (splitPath.Length >= 2 && splitPath[0] == "Mods")
if (!File.Exists(sourceFile))
{ {
string[] splitPath = contentFile.Path.Split('/'); sourceFile = Path.Combine(item?.Directory, string.Join("/", splitPath.Skip(2)));
if (splitPath.Length >= 2 && splitPath[0] == "Mods")
{
sourceFile = Path.Combine(item?.Directory, string.Join("/", splitPath.Skip(2)));
}
} }
}
contentFile.Path = CorrectContentFilePath(contentFile.Path, contentPackage, false); contentFile.Path = CorrectContentFilePath(contentFile.Path, contentPackage,
contentFile.Type != ContentType.Submarine);
//path not allowed -> the content file must be a reference to an external file (such as some vanilla file outside the Mods folder)
//path not allowed -> the content file must be a reference to an external file (such as some vanilla file outside the Mods folder) if (!ContentPackage.IsModFilePathAllowed(contentFile))
if (!ContentPackage.IsModFilePathAllowed(contentFile)) {
//the content package is trying to copy a file to a prohibited path, which is not allowed
if (File.Exists(sourceFile))
{ {
//the content package is trying to copy a file to a prohibited path, which is not allowed errorMsg = TextManager.GetWithVariable("WorkshopErrorIllegalPathOnEnable", "[filename]", contentFile.Path);
if (File.Exists(sourceFile)) return errorMsg;
{ }
errorMsg = TextManager.GetWithVariable("WorkshopErrorIllegalPathOnEnable", "[filename]", contentFile.Path); //not trying to copy anything, so this is a reference to an external file
return false; //if the external file doesn't exist, we cannot enable the package
} else if (!File.Exists(contentFile.Path))
//not trying to copy anything, so this is a reference to an external file {
//if the external file doesn't exist, we cannot enable the package errorMsg = TextManager.GetWithVariable("WorkshopErrorEnableFailed", "[itemname]", item?.Title) + " " + TextManager.GetWithVariable("WorkshopFileNotFound", "[path]", "\"" + contentFile.Path + "\"");
else if (!File.Exists(contentFile.Path)) return errorMsg;
{ }
errorMsg = TextManager.GetWithVariable("WorkshopErrorEnableFailed", "[itemname]", item?.Title) + " " + TextManager.GetWithVariable("WorkshopFileNotFound", "[path]", "\"" + contentFile.Path + "\""); continue;
return false; }
} else if (!File.Exists(sourceFile))
{
if (File.Exists(contentFile.Path))
{
//the file is already present in the game folder, all good
continue; continue;
} }
else if (!File.Exists(sourceFile)) else
{ {
if (File.Exists(contentFile.Path)) //file not present in either the mod or the game folder -> cannot enable the package
{ errorMsg = TextManager.GetWithVariable("WorkshopErrorEnableFailed", "[itemname]", item?.Title) + " " + TextManager.GetWithVariable("WorkshopFileNotFound", "[path]", "\"" + contentFile.Path + "\"");
//the file is already present in the game folder, all good return errorMsg;
continue;
}
else
{
//file not present in either the mod or the game folder -> cannot enable the package
errorMsg = TextManager.GetWithVariable("WorkshopErrorEnableFailed", "[itemname]", item?.Title) + " " + TextManager.GetWithVariable("WorkshopFileNotFound", "[path]", "\"" + contentFile.Path + "\"");
return false;
}
} }
//make sure the destination directory exists
Directory.CreateDirectory(Path.GetDirectoryName(contentFile.Path));
CorrectContentFileCopy(contentPackage, sourceFile, contentFile.Path, overwrite: true);
} }
foreach (string nonContentFile in nonContentFiles) //make sure the destination directory exists
{ Directory.CreateDirectory(Path.GetDirectoryName(contentFile.Path));
string sourceFile = Path.Combine(item?.Directory, nonContentFile); CorrectContentFileCopy(contentPackage, sourceFile, contentFile.Path, overwrite: true);
if (!File.Exists(sourceFile)) { continue; }
string destinationPath = CorrectContentFilePath(nonContentFile, contentPackage, false);
Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));
CorrectContentFileCopy(contentPackage, sourceFile, destinationPath, overwrite: true);
}
} }
catch (Exception e)
foreach (string nonContentFile in nonContentFiles)
{ {
errorMsg = TextManager.GetWithVariable("WorkshopErrorEnableFailed", "[itemname]", item?.Title) + " {" + e.Message + "}"; string sourceFile = Path.Combine(item?.Directory, nonContentFile);
DebugConsole.NewMessage(errorMsg, Color.Red); if (!File.Exists(sourceFile)) { continue; }
return false; string destinationPath = CorrectContentFilePath(nonContentFile, contentPackage, false);
Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));
CorrectContentFileCopy(contentPackage, sourceFile, destinationPath, overwrite: true);
} }
return true; File.Delete(copyingPath);
return "";
} }
private static bool CheckFileEquality(string filePath1, string filePath2) private static bool CheckFileEquality(string filePath1, string filePath2)
@@ -1149,6 +1154,44 @@ namespace Barotrauma.Steam
} }
} }
private static void RemoveMods(Func<ContentPackage, bool> predicate)
{
var toRemove = ContentPackage.List.Where(predicate).ToList();
var packagesToDeselect = GameMain.Config.SelectedContentPackages.Where(p => toRemove.Contains(p)).ToList();
foreach (var cp in packagesToDeselect)
{
if (cp.CorePackage)
{
GameMain.Config.SelectCorePackage(ContentPackage.List.Find(cpp => cpp.CorePackage && !toRemove.Contains(cpp)));
}
else
{
GameMain.Config.DeselectContentPackage(cp);
}
}
foreach (var cp in toRemove)
{
try
{
string path = Path.GetDirectoryName(cp.Path);
if (Directory.Exists(path)) { Directory.Delete(path, true); }
}
catch (Exception e)
{
DebugConsole.ThrowError($"An error occurred while attempting to delete {Path.GetDirectoryName(cp.Path)}", e);
}
}
ContentPackage.List.RemoveAll(cp => toRemove.Contains(cp));
GameMain.Config.SelectedContentPackages.RemoveAll(cp => !ContentPackage.List.Contains(cp));
ContentPackage.SortContentPackages();
GameMain.Config.SaveNewPlayerConfig();
GameMain.Config.WarnIfContentPackageSelectionDirty();
}
/// <summary> /// <summary>
/// Disables a workshop item by removing the files from the game folder. /// Disables a workshop item by removing the files from the game folder.
/// </summary> /// </summary>
@@ -1173,44 +1216,11 @@ namespace Barotrauma.Steam
GameMain.Config.SuppressModFolderWatcher = true; GameMain.Config.SuppressModFolderWatcher = true;
try try
{ {
RemoveMods(cp => !string.IsNullOrWhiteSpace(cp.SteamWorkshopUrl) && cp.SteamWorkshopUrl == contentPackage.SteamWorkshopUrl);
var toRemove = ContentPackage.List.Where(cp => !string.IsNullOrWhiteSpace(cp.SteamWorkshopUrl) && cp.SteamWorkshopUrl == contentPackage.SteamWorkshopUrl).ToList();
var packagesToDeselect = GameMain.Config.SelectedContentPackages.Where(p => toRemove.Contains(p)).ToList();
foreach (var cp in packagesToDeselect)
{
if (cp.CorePackage)
{
GameMain.Config.SelectCorePackage(ContentPackage.List.Find(cpp => cpp.CorePackage && !toRemove.Contains(cpp)));
}
else
{
GameMain.Config.DeselectContentPackage(cp);
}
}
foreach (var cp in toRemove)
{
try
{
Directory.Delete(Path.GetDirectoryName(cp.Path), true);
}
catch (Exception e)
{
DebugConsole.ThrowError($"An error occurred while attempting to delete {Path.GetDirectoryName(cp.Path)}", e);
}
}
ContentPackage.List.RemoveAll(cp => toRemove.Contains(cp));
GameMain.Config.SelectedContentPackages.RemoveAll(cp => !ContentPackage.List.Contains(cp));
ContentPackage.SortContentPackages();
GameMain.Config.SaveNewPlayerConfig();
GameMain.Config.WarnIfContentPackageSelectionDirty();
} }
catch (Exception e) catch (Exception e)
{ {
errorMsg = "Disabling the workshop item \"" + item?.Title + "\" failed. " + e.Message; errorMsg = "Disabling the workshop item \"" + item?.Title + "\" failed. " + e.Message + "\n" + e.StackTrace;
if (!noLog) if (!noLog)
{ {
DebugConsole.NewMessage(errorMsg, Microsoft.Xna.Framework.Color.Red); DebugConsole.NewMessage(errorMsg, Microsoft.Xna.Framework.Color.Red);
@@ -1219,6 +1229,8 @@ namespace Barotrauma.Steam
} }
GameMain.Config.SuppressModFolderWatcher = false; GameMain.Config.SuppressModFolderWatcher = false;
GameMain.SteamWorkshopScreen?.SetReinstallButtonStatus(item, false, null);
errorMsg = ""; errorMsg = "";
return true; return true;
} }
@@ -1314,79 +1326,77 @@ namespace Barotrauma.Steam
return upToDate; return upToDate;
} }
public static bool AutoUpdateWorkshopItems() public static async Task<bool> AutoUpdateWorkshopItemsAsync()
{ {
if (!isInitialized) { return false; } if (!isInitialized) { return false; }
var query = new Steamworks.Ugc.Query(Steamworks.UgcType.All) var query = new Steamworks.Ugc.Query(Steamworks.UgcType.All)
.WhereUserSubscribed() .WhereUserSubscribed()
.WithLongDescription(); .WithLongDescription();
//ugcResultPageTasks ??= new List<Task>();
//ugcResultPageTasks.Add();
CancellationTokenSource cancelTokenSource = new CancellationTokenSource();
CancellationToken cancelToken = cancelTokenSource.Token;
Task task = Task.Factory.StartNew(async () =>
{
int processedResults = 0; int pageIndex = 1;
Steamworks.Ugc.ResultPage? resultPage = await query.GetPageAsync(pageIndex);
while (resultPage.HasValue && resultPage?.ResultCount > 0) List<Steamworks.Ugc.Item> items = await GetWorkshopItemsAsync(query);
GameMain.Config.SuppressModFolderWatcher = true;
//remove mods that the player is no longer subscribed to
RemoveMods(cp => !string.IsNullOrWhiteSpace(cp.SteamWorkshopUrl) && !items.Any(it => it.Id == GetWorkshopItemIDFromUrl(cp.SteamWorkshopUrl)));
GameMain.Config.SuppressModFolderWatcher = false;
foreach (var item in items)
{
try
{ {
foreach (var item in resultPage.Value.Entries) if (!item.IsInstalled) { continue; }
bool installedSuccessfully = false;
string errorMsg;
if (!CheckWorkshopItemEnabled(item))
{ {
if (cancelToken.IsCancellationRequested) installedSuccessfully = EnableWorkShopItem(item, true, out errorMsg);
{
cancelToken.ThrowIfCancellationRequested();
}
try
{
if (!item.IsInstalled || !CheckWorkshopItemEnabled(item) || CheckWorkshopItemUpToDate(item)) { continue; }
if (!UpdateWorkshopItem(item, out string errorMsg))
{
DebugConsole.ThrowError(errorMsg);
new GUIMessageBox(
TextManager.Get("Error"),
TextManager.GetWithVariables("WorkshopItemUpdateFailed", new string[2] { "[itemname]", "[errormessage]" }, new string[2] { item.Title, errorMsg }));
}
else
{
//TODO: potential race condition
new GUIMessageBox("", TextManager.GetWithVariable("WorkshopItemUpdated", "[itemname]", item.Title));
}
}
catch (Exception e)
{
new GUIMessageBox(
TextManager.Get("Error"),
TextManager.GetWithVariables("WorkshopItemUpdateFailed", new string[2] { "[itemname]", "[errormessage]" }, new string[2] { item.Title, e.Message + ", " + e.TargetSite }));
GameAnalyticsManager.AddErrorEventOnce(
"SteamManager.AutoUpdateWorkshopItems:" + e.Message,
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Failed to autoupdate workshop item \"" + item.Title + "\". " + e.Message + "\n" + e.StackTrace);
}
} }
else if (!CheckWorkshopItemUpToDate(item))
processedResults += resultPage.Value.ResultCount;
pageIndex++;
if (processedResults < resultPage?.TotalCount)
{ {
resultPage = await query.GetPageAsync(pageIndex); installedSuccessfully = UpdateWorkshopItem(item, out errorMsg);
} }
else else
{ {
resultPage = null; continue;
}
if (!installedSuccessfully)
{
DebugConsole.ThrowError(errorMsg);
new GUIMessageBox(
TextManager.Get("Error"),
TextManager.GetWithVariables("WorkshopItemUpdateFailed", new string[2] { "[itemname]", "[errormessage]" }, new string[2] { item.Title, errorMsg }));
}
else
{
//TODO: potential race condition
new GUIMessageBox("", TextManager.GetWithVariable("WorkshopItemUpdated", "[itemname]", item.Title));
} }
} }
}, cancelToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); catch (Exception e)
{
task.Wait(10000); new GUIMessageBox(
if (!task.IsCompleted) TextManager.Get("Error"),
{ TextManager.GetWithVariables("WorkshopItemUpdateFailed", new string[2] { "[itemname]", "[errormessage]" }, new string[2] { item.Title, e.Message + ", " + e.TargetSite }));
cancelTokenSource.Cancel(); GameAnalyticsManager.AddErrorEventOnce(
task.Wait(); "SteamManager.AutoUpdateWorkshopItems:" + e.Message,
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Failed to autoupdate workshop item \"" + item.Title + "\". " + e.Message + "\n" + e.StackTrace);
}
} }
return task.Status == TaskStatus.RanToCompletion; List<Task> tasks;
lock (modCopiesInProgress)
{
tasks = modCopiesInProgress.Values.ToList();
}
await Task.WhenAll(tasks);
return true;
} }
public static bool UpdateWorkshopItem(Steamworks.Ugc.Item? item, out string errorMsg) public static bool UpdateWorkshopItem(Steamworks.Ugc.Item? item, out string errorMsg)
@@ -1431,7 +1441,7 @@ namespace Barotrauma.Steam
private static void CorrectContentFileCopy(ContentPackage package, string src, string dest, bool overwrite) private static void CorrectContentFileCopy(ContentPackage package, string src, string dest, bool overwrite)
{ {
if (Path.GetExtension(src).ToLowerInvariant() == ".xml") if (Path.GetExtension(src).Equals(".xml", StringComparison.OrdinalIgnoreCase))
{ {
XDocument doc = XMLExtensions.TryLoadXml(src); XDocument doc = XMLExtensions.TryLoadXml(src);
if (doc != null) if (doc != null)
@@ -1481,7 +1491,7 @@ namespace Barotrauma.Steam
{ {
if (checkIfFileExists) if (checkIfFileExists)
{ {
ContentPackage otherContentPackage = ContentPackage.List.Find(cp => cp.Name.ToLowerInvariant() == splitPath[1].ToLowerInvariant()); ContentPackage otherContentPackage = ContentPackage.List.Find(cp => cp.Name.Equals(splitPath[1], StringComparison.OrdinalIgnoreCase));
if (otherContentPackage != null) if (otherContentPackage != null)
{ {
string otherPackageName = Path.GetDirectoryName(otherContentPackage.Path); string otherPackageName = Path.GetDirectoryName(otherContentPackage.Path);
@@ -1493,7 +1503,8 @@ namespace Barotrauma.Steam
} }
} }
} }
newPath = Path.Combine(packageName, string.Join("/", splitPath.Skip(2))); splitPath = splitPath.Skip(Math.Clamp(splitPath.Length-1, 0, 2)).ToArray();
newPath = Path.Combine(packageName, string.Join("/", splitPath));
} }
else else
{ {
@@ -93,7 +93,7 @@ namespace Barotrauma.Networking
if (client.VoipSound == null) if (client.VoipSound == null)
{ {
DebugConsole.Log("Recreating voipsound " + queueId); DebugConsole.Log("Recreating voipsound " + queueId);
client.VoipSound = new VoipSound(GameMain.SoundManager, client.VoipQueue); client.VoipSound = new VoipSound(client.Name, GameMain.SoundManager, client.VoipQueue);
} }
if (client.Character != null && !client.Character.IsDead && !client.Character.Removed && client.Character.SpeechImpediment <= 100.0f) if (client.Character != null && !client.Character.IsDead && !client.Character.Removed && client.Character.SpeechImpediment <= 100.0f)
@@ -119,7 +119,7 @@ namespace Barotrauma.Networking
GameMain.NetLobbyScreen?.SetPlayerSpeaking(client); GameMain.NetLobbyScreen?.SetPlayerSpeaking(client);
GameMain.GameSession?.CrewManager?.SetClientSpeaking(client); GameMain.GameSession?.CrewManager?.SetClientSpeaking(client);
if (client.VoipSound.CurrentAmplitude > 0.1f) //TODO: might need to tweak if ((client.VoipSound.CurrentAmplitude * client.VoipSound.Gain * GameMain.SoundManager.GetCategoryGainMultiplier("voip")) > 0.1f) //TODO: might need to tweak
{ {
if (client.Character != null && !client.Character.Removed) if (client.Character != null && !client.Character.Removed)
{ {
@@ -116,7 +116,7 @@ namespace Barotrauma
Submarine sub = data as Submarine; Submarine sub = data as Submarine;
if (sub == null) return; if (sub == null) return;
msg.Write(sub.Name); msg.Write(sub.Info.Name);
break; break;
case VoteType.Mode: case VoteType.Mode:
GameModePreset gameMode = data as GameModePreset; GameModePreset gameMode = data as GameModePreset;
@@ -158,7 +158,7 @@ namespace Barotrauma
{ {
if (item.UserData != null && item.UserData is Submarine) serversubs.Add(item.UserData as Submarine); if (item.UserData != null && item.UserData is Submarine) serversubs.Add(item.UserData as Submarine);
} }
Submarine sub = serversubs.FirstOrDefault(sm => sm.Name == subName); Submarine sub = serversubs.FirstOrDefault(sm => sm.Info.Name == subName);
SetVoteText(GameMain.NetLobbyScreen.SubList, sub, votes); SetVoteText(GameMain.NetLobbyScreen.SubList, sub, votes);
} }
} }
@@ -11,7 +11,18 @@ namespace Barotrauma.Particles
public string OriginalName { get { return Name; } } public string OriginalName { get { return Name; } }
public string Identifier { get { return Name.ToLowerInvariant(); } } private string _identifier;
public string Identifier
{
get
{
if (_identifier == null)
{
_identifier = Name.ToLowerInvariant();
}
return _identifier;
}
}
public string FilePath { get; private set; } public string FilePath { get; private set; }
@@ -46,7 +57,7 @@ namespace Barotrauma.Particles
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
{ {
if (subElement.Name.ToString().ToLowerInvariant() == "sprite") if (subElement.Name.ToString().Equals("sprite", StringComparison.OrdinalIgnoreCase))
{ {
Sprites.Add(new Sprite(subElement)); Sprites.Add(new Sprite(subElement));
} }
@@ -108,7 +108,8 @@ namespace Barotrauma.Particles
public readonly bool CopyEntityAngle; public readonly bool CopyEntityAngle;
public readonly bool DrawOnTop; public bool DrawOnTop => forceDrawOnTop || ParticlePrefab.DrawOnTop;
private readonly bool forceDrawOnTop;
public ParticleEmitterPrefab(XElement element) public ParticleEmitterPrefab(XElement element)
{ {
@@ -150,6 +151,12 @@ namespace Barotrauma.Particles
{ {
DistanceMin = DistanceMax = element.GetAttributeFloat("distance", 0.0f); DistanceMin = DistanceMax = element.GetAttributeFloat("distance", 0.0f);
} }
if (DistanceMax < DistanceMin)
{
var temp = DistanceMin;
DistanceMin = DistanceMax;
DistanceMax = temp;
}
if (element.Attribute("velocity") == null) if (element.Attribute("velocity") == null)
{ {
@@ -160,13 +167,19 @@ namespace Barotrauma.Particles
{ {
VelocityMin = VelocityMax = element.GetAttributeFloat("velocity", 0.0f); VelocityMin = VelocityMax = element.GetAttributeFloat("velocity", 0.0f);
} }
if (VelocityMax < VelocityMin)
{
var temp = VelocityMin;
VelocityMin = VelocityMax;
VelocityMax = temp;
}
EmitInterval = element.GetAttributeFloat("emitinterval", 0.0f); EmitInterval = element.GetAttributeFloat("emitinterval", 0.0f);
ParticlesPerSecond = element.GetAttributeInt("particlespersecond", 0); ParticlesPerSecond = element.GetAttributeInt("particlespersecond", 0);
ParticleAmount = element.GetAttributeInt("particleamount", 0); ParticleAmount = element.GetAttributeInt("particleamount", 0);
HighQualityCollisionDetection = element.GetAttributeBool("highqualitycollisiondetection", false); HighQualityCollisionDetection = element.GetAttributeBool("highqualitycollisiondetection", false);
CopyEntityAngle = element.GetAttributeBool("copyentityangle", false); CopyEntityAngle = element.GetAttributeBool("copyentityangle", false);
DrawOnTop = element.GetAttributeBool("drawontop", false); forceDrawOnTop = element.GetAttributeBool("drawontop", false);
} }
} }
} }
@@ -228,14 +228,7 @@ namespace Barotrauma.Particles
if (inSub.HasValue) if (inSub.HasValue)
{ {
bool isOutside = particle.CurrentHull == null; bool isOutside = particle.CurrentHull == null;
if (particle.DrawOnTop) if (!particle.DrawOnTop && isOutside == inSub.Value)
{
if (isOutside != inSub.Value)
{
continue;
}
}
else if (isOutside == inSub.Value)
{ {
continue; continue;
} }
@@ -199,6 +199,9 @@ namespace Barotrauma.Particles
[Editable, Serialize(DrawTargetType.Air, false, description: "Should the particle be rendered in air, water or both.")] [Editable, Serialize(DrawTargetType.Air, false, description: "Should the particle be rendered in air, water or both.")]
public DrawTargetType DrawTarget { get; private set; } public DrawTargetType DrawTarget { get; private set; }
[Editable, Serialize(false, false, description: "Should the particle be always rendered on top of entities?")]
public bool DrawOnTop { get; private set; }
[Editable, Serialize(ParticleBlendState.AlphaBlend, false, description: "The type of blending to use when rendering the particle.")] [Editable, Serialize(ParticleBlendState.AlphaBlend, false, description: "The type of blending to use when rendering the particle.")]
public ParticleBlendState BlendState { get; private set; } public ParticleBlendState BlendState { get; private set; }
@@ -7,6 +7,8 @@ using System.Text;
using GameAnalyticsSDK.Net; using GameAnalyticsSDK.Net;
using Barotrauma.Steam; using Barotrauma.Steam;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Xml.Linq;
#if WINDOWS #if WINDOWS
using SharpDX; using SharpDX;
@@ -22,42 +24,56 @@ namespace Barotrauma
/// </summary> /// </summary>
public static class Program public static class Program
{ {
#if LINUX
/// <summary>
/// Sets the required environment variables for the game to initialize Steamworks correctly.
/// </summary>
[DllImport("linux_steam_env", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern void setLinuxEnv();
#endif
/// <summary> /// <summary>
/// The main entry point for the application. /// The main entry point for the application.
/// </summary> /// </summary>
[STAThread] [STAThread]
static void Main(string[] args) static void Main(string[] args)
{ {
GameMain game = null;
string executableDir = ""; string executableDir = "";
#if !DEBUG #if !DEBUG
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(CrashHandler);
#endif
#if LINUX
setLinuxEnv();
#endif
Game = null;
executableDir = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location);
Directory.SetCurrentDirectory(executableDir);
SteamManager.Initialize();
Game = new GameMain(args);
Game.Run();
Game.Dispose();
}
private static GameMain Game;
private static void CrashHandler(object sender, UnhandledExceptionEventArgs args)
{
try try
{ {
#endif Game?.Exit();
executableDir = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location); CrashDump(Game, "crashreport.log", (Exception)args.ExceptionObject);
Directory.SetCurrentDirectory(executableDir); Game?.Dispose();
SteamManager.Initialize();
game = new GameMain(args);
game.Run();
game.Dispose();
#if !DEBUG
} }
catch (Exception e) catch
{ {
try //exception handler is broken, we have a serious problem here!!
{
CrashDump(game, Path.Combine(executableDir,"crashreport.log"), e);
}
catch (Exception e2)
{
CrashMessageBox("Barotrauma seems to have crashed, and failed to generate a crash report: "
+ e2.Message + "\n" + e2.StackTrace.ToString(),
null);
}
game?.Dispose();
return; return;
} }
#endif
} }
public static void CrashMessageBox(string message, string filePath) public static void CrashMessageBox(string message, string filePath)
@@ -83,16 +99,9 @@ namespace Barotrauma
string exePath = System.Reflection.Assembly.GetEntryAssembly().Location; string exePath = System.Reflection.Assembly.GetEntryAssembly().Location;
var md5 = System.Security.Cryptography.MD5.Create(); var md5 = System.Security.Cryptography.MD5.Create();
Md5Hash exeHash = null; Md5Hash exeHash = null;
try using (var stream = File.OpenRead(exePath))
{ {
using (var stream = File.OpenRead(exePath)) exeHash = new Md5Hash(stream);
{
exeHash = new Md5Hash(stream);
}
}
catch
{
//gotta catch them all, we don't want to throw an exception while writing a crash report
} }
StreamWriter sw = new StreamWriter(filePath); StreamWriter sw = new StreamWriter(filePath);
@@ -102,6 +111,33 @@ namespace Barotrauma
sb.AppendLine("\n"); sb.AppendLine("\n");
sb.AppendLine("Barotrauma seems to have crashed. Sorry for the inconvenience! "); sb.AppendLine("Barotrauma seems to have crashed. Sorry for the inconvenience! ");
sb.AppendLine("\n"); sb.AppendLine("\n");
try
{
if (exception is GameMain.LoadingException)
{
//exception occurred in loading screen:
//assume content packages are the culprit and reset them
XDocument doc = XMLExtensions.TryLoadXml(GameSettings.PlayerSavePath);
XDocument baseDoc = XMLExtensions.TryLoadXml(GameSettings.SavePath);
if (doc != null && baseDoc != null)
{
XElement newElement = new XElement(doc.Root.Name);
newElement.Add(doc.Root.Attributes());
newElement.Add(doc.Root.Elements().Where(e => !e.Name.LocalName.Equals("contentpackage", StringComparison.InvariantCultureIgnoreCase)));
newElement.Add(baseDoc.Root.Elements().Where(e => e.Name.LocalName.Equals("contentpackage", StringComparison.InvariantCultureIgnoreCase)));
XDocument newDoc = new XDocument(newElement);
newDoc.Save(GameSettings.PlayerSavePath);
sb.AppendLine("To prevent further startup errors, installed mods will be disabled the next time you launch the game.");
sb.AppendLine("\n");
}
}
}
catch
{
//welp i guess we couldn't reset the config!
}
if (exeHash?.Hash != null) if (exeHash?.Hash != null)
{ {
sb.AppendLine(exeHash.Hash); sb.AppendLine(exeHash.Hash);
@@ -119,7 +155,7 @@ namespace Barotrauma
sb.AppendLine("Selected content packages: " + (!GameMain.SelectedPackages.Any() ? "None" : string.Join(", ", GameMain.SelectedPackages.Select(c => c.Name)))); sb.AppendLine("Selected content packages: " + (!GameMain.SelectedPackages.Any() ? "None" : string.Join(", ", GameMain.SelectedPackages.Select(c => c.Name))));
} }
sb.AppendLine("Level seed: " + ((Level.Loaded == null) ? "no level loaded" : Level.Loaded.Seed)); sb.AppendLine("Level seed: " + ((Level.Loaded == null) ? "no level loaded" : Level.Loaded.Seed));
sb.AppendLine("Loaded submarine: " + ((Submarine.MainSub == null) ? "None" : Submarine.MainSub.Name + " (" + Submarine.MainSub.MD5Hash + ")")); sb.AppendLine("Loaded submarine: " + ((Submarine.MainSub == null) ? "None" : Submarine.MainSub.Info.Name + " (" + Submarine.MainSub.Info.MD5Hash + ")"));
sb.AppendLine("Selected screen: " + (Screen.Selected == null ? "None" : Screen.Selected.ToString())); sb.AppendLine("Selected screen: " + (Screen.Selected == null ? "None" : Screen.Selected.ToString()));
if (SteamManager.IsInitialized) if (SteamManager.IsInitialized)
{ {
@@ -217,4 +253,4 @@ namespace Barotrauma
} }
} }
#endif #endif
} }
@@ -21,7 +21,7 @@ namespace Barotrauma
private GUIButton loadGameButton, deleteMpSaveButton; private GUIButton loadGameButton, deleteMpSaveButton;
public Action<Submarine, string, string> StartNewGame; public Action<SubmarineInfo, string, string> StartNewGame;
public Action<string> LoadGame; public Action<string> LoadGame;
public GUIButton StartButton public GUIButton StartButton
@@ -32,7 +32,7 @@ namespace Barotrauma
private readonly bool isMultiplayer; private readonly bool isMultiplayer;
public CampaignSetupUI(bool isMultiplayer, GUIComponent newGameContainer, GUIComponent loadGameContainer, IEnumerable<Submarine> submarines, IEnumerable<string> saveFiles = null) public CampaignSetupUI(bool isMultiplayer, GUIComponent newGameContainer, GUIComponent loadGameContainer, IEnumerable<SubmarineInfo> submarines, IEnumerable<string> saveFiles = null)
{ {
this.isMultiplayer = isMultiplayer; this.isMultiplayer = isMultiplayer;
this.newGameContainer = newGameContainer; this.newGameContainer = newGameContainer;
@@ -115,12 +115,12 @@ namespace Barotrauma
return false; return false;
} }
Submarine selectedSub = null; SubmarineInfo selectedSub = null;
if (!isMultiplayer) if (!isMultiplayer)
{ {
if (!(subList.SelectedData is Submarine)) { return false; } if (!(subList.SelectedData is SubmarineInfo)) { return false; }
selectedSub = subList.SelectedData as Submarine; selectedSub = subList.SelectedData as SubmarineInfo;
} }
else else
{ {
@@ -226,7 +226,7 @@ namespace Barotrauma
{ {
foreach (GUIComponent child in subList.Content.Children) foreach (GUIComponent child in subList.Content.Children)
{ {
var sub = child.UserData as Submarine; var sub = child.UserData as SubmarineInfo;
if (sub == null) { return; } if (sub == null) { return; }
child.Visible = string.IsNullOrEmpty(filter) ? true : sub.DisplayName.ToLower().Contains(filter.ToLower()); child.Visible = string.IsNullOrEmpty(filter) ? true : sub.DisplayName.ToLower().Contains(filter.ToLower());
} }
@@ -238,7 +238,7 @@ namespace Barotrauma
(subPreviewContainer.Parent as GUILayoutGroup)?.Recalculate(); (subPreviewContainer.Parent as GUILayoutGroup)?.Recalculate();
subPreviewContainer.ClearChildren(); subPreviewContainer.ClearChildren();
Submarine sub = obj as Submarine; SubmarineInfo sub = obj as SubmarineInfo;
if (sub == null) { return true; } if (sub == null) { return true; }
sub.CreatePreviewWindow(subPreviewContainer); sub.CreatePreviewWindow(subPreviewContainer);
@@ -278,7 +278,7 @@ namespace Barotrauma
saveNameBox.Text = Path.GetFileNameWithoutExtension(savePath); saveNameBox.Text = Path.GetFileNameWithoutExtension(savePath);
} }
public void UpdateSubList(IEnumerable<Submarine> submarines) public void UpdateSubList(IEnumerable<SubmarineInfo> submarines)
{ {
#if !DEBUG #if !DEBUG
var subsToShow = submarines.Where(s => !s.HasTag(SubmarineTag.HideInMenus)); var subsToShow = submarines.Where(s => !s.HasTag(SubmarineTag.HideInMenus));
@@ -288,7 +288,7 @@ namespace Barotrauma
subList.ClearChildren(); subList.ClearChildren();
foreach (Submarine sub in subsToShow) foreach (SubmarineInfo sub in subsToShow)
{ {
var textBlock = new GUITextBlock( var textBlock = new GUITextBlock(
new RectTransform(new Vector2(1, 0.1f), subList.Content.RectTransform) { MinSize = new Point(0, 30) }, new RectTransform(new Vector2(1, 0.1f), subList.Content.RectTransform) { MinSize = new Point(0, 30) },
@@ -319,7 +319,7 @@ namespace Barotrauma
}; };
} }
} }
if (Submarine.SavedSubmarines.Any()) if (SubmarineInfo.SavedSubmarines.Any())
{ {
var nonShuttles = subsToShow.Where(s => !s.HasTag(SubmarineTag.Shuttle)).ToList(); var nonShuttles = subsToShow.Where(s => !s.HasTag(SubmarineTag.Shuttle)).ToList();
if (nonShuttles.Count > 0) if (nonShuttles.Count > 0)
@@ -392,18 +392,19 @@ namespace Barotrauma
{ {
nameText.Text = Path.GetFileNameWithoutExtension(saveFile); nameText.Text = Path.GetFileNameWithoutExtension(saveFile);
XDocument doc = SaveUtil.LoadGameSessionDoc(saveFile); XDocument doc = SaveUtil.LoadGameSessionDoc(saveFile);
if (doc.Root.GetChildElement("multiplayercampaign") != null)
{
//multiplayer campaign save in the wrong folder -> don't show the save
saveList.Content.RemoveChild(saveFrame);
continue;
}
if (doc?.Root == null) if (doc?.Root == null)
{ {
DebugConsole.ThrowError("Error loading save file \"" + saveFile + "\". The file may be corrupted."); DebugConsole.ThrowError("Error loading save file \"" + saveFile + "\". The file may be corrupted.");
nameText.TextColor = GUI.Style.Red; nameText.TextColor = GUI.Style.Red;
continue; continue;
} }
if (doc.Root.GetChildElement("multiplayercampaign") != null)
{
//multiplayer campaign save in the wrong folder -> don't show the save
saveList.Content.RemoveChild(saveFrame);
continue;
}
subName = doc.Root.GetAttributeString("submarine", ""); subName = doc.Root.GetAttributeString("submarine", "");
saveTime = doc.Root.GetAttributeString("savetime", ""); saveTime = doc.Root.GetAttributeString("savetime", "");
contentPackageStr = doc.Root.GetAttributeString("selectedcontentpackages", ""); contentPackageStr = doc.Root.GetAttributeString("selectedcontentpackages", "");
@@ -119,8 +119,8 @@ namespace Barotrauma.CharacterEditor
if (Submarine.MainSub == null) if (Submarine.MainSub == null)
{ {
ResetVariables(); ResetVariables();
Submarine.MainSub = new Submarine("Content/AnimEditor.sub"); var subInfo = new SubmarineInfo("Content/AnimEditor.sub");
Submarine.MainSub.Load(unloadPrevious: false, showWarningMessages: false); Submarine.MainSub = new Submarine(subInfo);
Submarine.MainSub.PhysicsBody.Enabled = false; Submarine.MainSub.PhysicsBody.Enabled = false;
originalWall = new WallGroup(new List<Structure>(Structure.WallList)); originalWall = new WallGroup(new List<Structure>(Structure.WallList));
CloneWalls(); CloneWalls();
@@ -3347,6 +3347,7 @@ namespace Barotrauma.CharacterEditor
void CreateCloseButton(SerializableEntityEditor editor, Action onButtonClicked, float size = 1) void CreateCloseButton(SerializableEntityEditor editor, Action onButtonClicked, float size = 1)
{ {
if (editor == null) { return; }
int height = 30; int height = 30;
var parent = new GUIFrame(new RectTransform(new Point(editor.Rect.Width, (int)(height * size * GUI.yScale)), editor.RectTransform, isFixedSize: true), style: null) var parent = new GUIFrame(new RectTransform(new Point(editor.Rect.Width, (int)(height * size * GUI.yScale)), editor.RectTransform, isFixedSize: true), style: null)
{ {
@@ -3366,6 +3367,7 @@ namespace Barotrauma.CharacterEditor
void CreateAddButtonAtLast(ParamsEditor editor, Action onButtonClicked, string text) void CreateAddButtonAtLast(ParamsEditor editor, Action onButtonClicked, string text)
{ {
if (editor == null) { return; }
var parentFrame = new GUIFrame(new RectTransform(new Point(editor.EditorBox.Rect.Width, (int)(50 * GUI.yScale)), editor.EditorBox.Content.RectTransform), style: null, color: ParamsEditor.Color) var parentFrame = new GUIFrame(new RectTransform(new Point(editor.EditorBox.Rect.Width, (int)(50 * GUI.yScale)), editor.EditorBox.Content.RectTransform), style: null, color: ParamsEditor.Color)
{ {
CanBeFocused = false CanBeFocused = false
@@ -3383,6 +3385,7 @@ namespace Barotrauma.CharacterEditor
void CreateAddButton(SerializableEntityEditor editor, Action onButtonClicked, string text) void CreateAddButton(SerializableEntityEditor editor, Action onButtonClicked, string text)
{ {
if (editor == null) { return; }
var parent = new GUIFrame(new RectTransform(new Point(editor.Rect.Width, (int)(60 * GUI.yScale)), editor.RectTransform), style: null) var parent = new GUIFrame(new RectTransform(new Point(editor.Rect.Width, (int)(60 * GUI.yScale)), editor.RectTransform), style: null)
{ {
CanBeFocused = false CanBeFocused = false
@@ -4386,7 +4389,7 @@ namespace Barotrauma.CharacterEditor
ResetParamsEditor(); ResetParamsEditor();
} }
limb.PullJointWorldAnchorA = ScreenToSim(PlayerInput.MousePosition); limb.PullJointWorldAnchorA = ScreenToSim(PlayerInput.MousePosition);
TryUpdateLimbParam(limb, "pullpos", ConvertUnits.ToDisplayUnits(limb.PullJointLocalAnchorA / limb.Params.Ragdoll.LimbScale)); TryUpdateLimbParam(limb, "pullpos", ConvertUnits.ToDisplayUnits(limb.PullJointLocalAnchorA / limb.Params.Scale / limb.Params.Ragdoll.LimbScale));
GUI.DrawLine(spriteBatch, SimToScreen(limb.SimPosition), tformedPullPos, Color.MediumPurple); GUI.DrawLine(spriteBatch, SimToScreen(limb.SimPosition), tformedPullPos, Color.MediumPurple);
}); });
} }
@@ -4469,7 +4472,7 @@ namespace Barotrauma.CharacterEditor
if (joint.BodyA == limb.body.FarseerBody) if (joint.BodyA == limb.body.FarseerBody)
{ {
joint.LocalAnchorA += input; joint.LocalAnchorA += input;
Vector2 transformedValue = ConvertUnits.ToDisplayUnits(joint.LocalAnchorA / RagdollParams.JointScale); Vector2 transformedValue = ConvertUnits.ToDisplayUnits(joint.LocalAnchorA / joint.Scale);
TryUpdateJointParam(joint, "limb1anchor", transformedValue); TryUpdateJointParam(joint, "limb1anchor", transformedValue);
// Snap all selected joints to the first selected // Snap all selected joints to the first selected
if (copyJointSettings) if (copyJointSettings)
@@ -4484,7 +4487,7 @@ namespace Barotrauma.CharacterEditor
else if (joint.BodyB == limb.body.FarseerBody) else if (joint.BodyB == limb.body.FarseerBody)
{ {
joint.LocalAnchorB += input; joint.LocalAnchorB += input;
Vector2 transformedValue = ConvertUnits.ToDisplayUnits(joint.LocalAnchorB / RagdollParams.JointScale); Vector2 transformedValue = ConvertUnits.ToDisplayUnits(joint.LocalAnchorB / joint.Scale);
TryUpdateJointParam(joint, "limb2anchor", transformedValue); TryUpdateJointParam(joint, "limb2anchor", transformedValue);
// Snap all selected joints to the first selected // Snap all selected joints to the first selected
if (copyJointSettings) if (copyJointSettings)
@@ -4504,12 +4507,12 @@ namespace Barotrauma.CharacterEditor
if (joint.BodyA == limb.body.FarseerBody && otherJoint.BodyA == otherLimb.body.FarseerBody) if (joint.BodyA == limb.body.FarseerBody && otherJoint.BodyA == otherLimb.body.FarseerBody)
{ {
otherJoint.LocalAnchorA = joint.LocalAnchorA; otherJoint.LocalAnchorA = joint.LocalAnchorA;
TryUpdateJointParam(otherJoint, "limb1anchor", ConvertUnits.ToDisplayUnits(joint.LocalAnchorA / RagdollParams.JointScale)); TryUpdateJointParam(otherJoint, "limb1anchor", ConvertUnits.ToDisplayUnits(joint.LocalAnchorA / joint.Scale));
} }
else if (joint.BodyB == limb.body.FarseerBody && otherJoint.BodyB == otherLimb.body.FarseerBody) else if (joint.BodyB == limb.body.FarseerBody && otherJoint.BodyB == otherLimb.body.FarseerBody)
{ {
otherJoint.LocalAnchorB = joint.LocalAnchorB; otherJoint.LocalAnchorB = joint.LocalAnchorB;
TryUpdateJointParam(otherJoint, "limb2anchor", ConvertUnits.ToDisplayUnits(joint.LocalAnchorB / RagdollParams.JointScale)); TryUpdateJointParam(otherJoint, "limb2anchor", ConvertUnits.ToDisplayUnits(joint.LocalAnchorB / joint.Scale));
} }
}); });
} }
@@ -4873,10 +4876,10 @@ namespace Barotrauma.CharacterEditor
{ {
// We want the collider to be slightly smaller than the source rect, because the source rect is usually a bit bigger than the graphic. // We want the collider to be slightly smaller than the source rect, because the source rect is usually a bit bigger than the graphic.
float multiplier = 0.85f; float multiplier = 0.85f;
l.body.SetSize(new Vector2(ConvertUnits.ToSimUnits(width), ConvertUnits.ToSimUnits(height)) * RagdollParams.LimbScale * RagdollParams.TextureScale * multiplier); l.body.SetSize(new Vector2(ConvertUnits.ToSimUnits(width), ConvertUnits.ToSimUnits(height)) * l.Scale * RagdollParams.TextureScale * multiplier);
TryUpdateLimbParam(l, "radius", ConvertUnits.ToDisplayUnits(l.body.radius / RagdollParams.LimbScale / RagdollParams.TextureScale)); TryUpdateLimbParam(l, "radius", ConvertUnits.ToDisplayUnits(l.body.radius / l.Params.Scale / RagdollParams.LimbScale / RagdollParams.TextureScale));
TryUpdateLimbParam(l, "width", ConvertUnits.ToDisplayUnits(l.body.width / RagdollParams.LimbScale / RagdollParams.TextureScale)); TryUpdateLimbParam(l, "width", ConvertUnits.ToDisplayUnits(l.body.width / l.Params.Scale / RagdollParams.LimbScale / RagdollParams.TextureScale));
TryUpdateLimbParam(l, "height", ConvertUnits.ToDisplayUnits(l.body.height / RagdollParams.LimbScale / RagdollParams.TextureScale)); TryUpdateLimbParam(l, "height", ConvertUnits.ToDisplayUnits(l.body.height / l.Params.Scale / RagdollParams.LimbScale / RagdollParams.TextureScale));
} }
void RecalculateOrigin(Limb l) void RecalculateOrigin(Limb l)
{ {
@@ -4963,7 +4966,7 @@ namespace Barotrauma.CharacterEditor
{ {
continue; continue;
} }
Vector2 tformedJointPos = jointPos = jointPos / RagdollParams.JointScale / limb.TextureScale * spriteSheetZoom; Vector2 tformedJointPos = jointPos = jointPos / joint.Scale / limb.TextureScale * spriteSheetZoom;
tformedJointPos.Y = -tformedJointPos.Y; tformedJointPos.Y = -tformedJointPos.Y;
tformedJointPos.X *= character.AnimController.Dir; tformedJointPos.X *= character.AnimController.Dir;
tformedJointPos += limbScreenPos; tformedJointPos += limbScreenPos;
@@ -4991,11 +4994,11 @@ namespace Barotrauma.CharacterEditor
Vector2 input = ConvertUnits.ToSimUnits(scaledMouseSpeed); Vector2 input = ConvertUnits.ToSimUnits(scaledMouseSpeed);
input.Y = -input.Y; input.Y = -input.Y;
input.X *= character.AnimController.Dir; input.X *= character.AnimController.Dir;
input *= RagdollParams.JointScale * limb.TextureScale / spriteSheetZoom; input *= joint.Scale * limb.TextureScale / spriteSheetZoom;
if (joint.BodyA == limb.body.FarseerBody) if (joint.BodyA == limb.body.FarseerBody)
{ {
joint.LocalAnchorA += input; joint.LocalAnchorA += input;
Vector2 transformedValue = ConvertUnits.ToDisplayUnits(joint.LocalAnchorA / RagdollParams.JointScale); Vector2 transformedValue = ConvertUnits.ToDisplayUnits(joint.LocalAnchorA / joint.Scale);
TryUpdateJointParam(joint, "limb1anchor", transformedValue); TryUpdateJointParam(joint, "limb1anchor", transformedValue);
// Snap all selected joints to the first selected // Snap all selected joints to the first selected
if (copyJointSettings) if (copyJointSettings)
@@ -5010,7 +5013,7 @@ namespace Barotrauma.CharacterEditor
else if (joint.BodyB == limb.body.FarseerBody) else if (joint.BodyB == limb.body.FarseerBody)
{ {
joint.LocalAnchorB += input; joint.LocalAnchorB += input;
Vector2 transformedValue = ConvertUnits.ToDisplayUnits(joint.LocalAnchorB / RagdollParams.JointScale); Vector2 transformedValue = ConvertUnits.ToDisplayUnits(joint.LocalAnchorB / joint.Scale);
TryUpdateJointParam(joint, "limb2anchor", transformedValue); TryUpdateJointParam(joint, "limb2anchor", transformedValue);
// Snap all selected joints to the first selected // Snap all selected joints to the first selected
if (copyJointSettings) if (copyJointSettings)
@@ -5029,12 +5032,12 @@ namespace Barotrauma.CharacterEditor
if (joint.BodyA == limb.body.FarseerBody && otherJoint.BodyA == otherLimb.body.FarseerBody) if (joint.BodyA == limb.body.FarseerBody && otherJoint.BodyA == otherLimb.body.FarseerBody)
{ {
otherJoint.LocalAnchorA = joint.LocalAnchorA; otherJoint.LocalAnchorA = joint.LocalAnchorA;
TryUpdateJointParam(otherJoint, "limb1anchor", ConvertUnits.ToDisplayUnits(joint.LocalAnchorA / RagdollParams.JointScale)); TryUpdateJointParam(otherJoint, "limb1anchor", ConvertUnits.ToDisplayUnits(joint.LocalAnchorA / joint.Scale));
} }
else if (joint.BodyB == limb.body.FarseerBody && otherJoint.BodyB == otherLimb.body.FarseerBody) else if (joint.BodyB == limb.body.FarseerBody && otherJoint.BodyB == otherLimb.body.FarseerBody)
{ {
otherJoint.LocalAnchorB = joint.LocalAnchorB; otherJoint.LocalAnchorB = joint.LocalAnchorB;
TryUpdateJointParam(otherJoint, "limb2anchor", ConvertUnits.ToDisplayUnits(joint.LocalAnchorB / RagdollParams.JointScale)); TryUpdateJointParam(otherJoint, "limb2anchor", ConvertUnits.ToDisplayUnits(joint.LocalAnchorB / joint.Scale));
} }
}); });
} }
@@ -394,7 +394,7 @@ namespace Barotrauma.CharacterEditor
return false; return false;
} }
var path = Path.GetFileName(TexturePath); var path = Path.GetFileName(TexturePath);
if (!path.EndsWith(".png", StringComparison.InvariantCultureIgnoreCase)) if (!path.EndsWith(".png", StringComparison.OrdinalIgnoreCase))
{ {
GUI.AddMessage(TextManager.Get("WrongFileType"), GUI.Style.Red); GUI.AddMessage(TextManager.Get("WrongFileType"), GUI.Style.Red);
texturePathElement.Flash(GUI.Style.Red); texturePathElement.Flash(GUI.Style.Red);
@@ -724,8 +724,8 @@ namespace Barotrauma.CharacterEditor
{ {
ParseLimbsFromGUIElements(); ParseLimbsFromGUIElements();
ParseJointsFromGUIElements(); ParseJointsFromGUIElements();
var main = LimbXElements.Values.Select(xe => xe.Attribute("type")).Where(a => a.Value.ToLowerInvariant() == "torso").FirstOrDefault() ?? var main = LimbXElements.Values.Select(xe => xe.Attribute("type")).Where(a => a.Value.Equals("torso", StringComparison.OrdinalIgnoreCase)).FirstOrDefault() ??
LimbXElements.Values.Select(xe => xe.Attribute("type")).Where(a => a.Value.ToLowerInvariant() == "head").FirstOrDefault(); LimbXElements.Values.Select(xe => xe.Attribute("type")).Where(a => a.Value.Equals("head", StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
if (main == null) if (main == null)
{ {
GUI.AddMessage(GetCharacterEditorTranslation("MissingTorsoOrHead"), GUI.Style.Red); GUI.AddMessage(GetCharacterEditorTranslation("MissingTorsoOrHead"), GUI.Style.Red);
@@ -91,7 +91,13 @@ namespace Barotrauma
c.DoVisibilityCheck(cam); c.DoVisibilityCheck(cam);
if (c.IsVisible != wasVisible) if (c.IsVisible != wasVisible)
{ {
c.AnimController.Limbs.ForEach(l => { if (l.LightSource != null) l.LightSource.Enabled = c.IsVisible; }); c.AnimController.Limbs.ForEach(l =>
{
if (l.LightSource != null)
{
l.LightSource.Enabled = c.IsVisible;
}
});
} }
} }
@@ -116,10 +122,12 @@ namespace Barotrauma
{ {
if (Submarine.MainSubs[i] == null) continue; if (Submarine.MainSubs[i] == null) continue;
if (Level.Loaded != null && Submarine.MainSubs[i].WorldPosition.Y < Level.MaxEntityDepth) continue; if (Level.Loaded != null && Submarine.MainSubs[i].WorldPosition.Y < Level.MaxEntityDepth) continue;
Vector2 position = Submarine.MainSubs[i].SubBody != null ? Submarine.MainSubs[i].WorldPosition : Submarine.MainSubs[i].HiddenSubPosition;
Color indicatorColor = i == 0 ? Color.LightBlue * 0.5f : GUI.Style.Red * 0.5f; Color indicatorColor = i == 0 ? Color.LightBlue * 0.5f : GUI.Style.Red * 0.5f;
GUI.DrawIndicator( GUI.DrawIndicator(
spriteBatch, Submarine.MainSubs[i].WorldPosition, cam, spriteBatch, position, cam,
Math.Max(Submarine.MainSub.Borders.Width, Submarine.MainSub.Borders.Height), Math.Max(Submarine.MainSub.Borders.Width, Submarine.MainSub.Borders.Height),
GUI.SubmarineIcon, indicatorColor); GUI.SubmarineIcon, indicatorColor);
} }
@@ -282,14 +290,23 @@ namespace Barotrauma
} }
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.LinearWrap, DepthStencilState.None, null, null, cam.Transform); spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.LinearWrap, DepthStencilState.None, null, null, cam.Transform);
foreach (Character c in Character.CharacterList) c.DrawFront(spriteBatch, cam); foreach (Character c in Character.CharacterList)
if (Level.Loaded != null) Level.Loaded.DrawFront(spriteBatch, cam);
if (GameMain.DebugDraw && GameMain.GameSession?.EventManager != null)
{ {
GameMain.GameSession.EventManager.DebugDraw(spriteBatch); c.DrawFront(spriteBatch, cam);
}
if (Level.Loaded != null)
{
Level.Loaded.DrawFront(spriteBatch, cam);
}
if (GameMain.DebugDraw)
{
MapEntity.mapEntityList.ForEach(me => me.AiTarget?.Draw(spriteBatch));
Character.CharacterList.ForEach(c => c.AiTarget?.Draw(spriteBatch));
if (GameMain.GameSession?.EventManager != null)
{
GameMain.GameSession.EventManager.DebugDraw(spriteBatch);
}
} }
spriteBatch.End(); spriteBatch.End();
if (GameMain.LightManager.LosEnabled && GameMain.LightManager.LosMode != LosMode.None && Character.Controlled != null) if (GameMain.LightManager.LosEnabled && GameMain.LightManager.LosMode != LosMode.None && Character.Controlled != null)
@@ -466,6 +466,7 @@ namespace Barotrauma
Submarine.Draw(spriteBatch, false); Submarine.Draw(spriteBatch, false);
Submarine.DrawFront(spriteBatch); Submarine.DrawFront(spriteBatch);
Submarine.DrawDamageable(spriteBatch, null); Submarine.DrawDamageable(spriteBatch, null);
GUI.DrawRectangle(spriteBatch, new Rectangle(new Point(0, -Level.Loaded.Size.Y), Level.Loaded.Size), Color.White, thickness: (int)(1.0f / cam.Zoom));
spriteBatch.End(); spriteBatch.End();
if (lightingEnabled.Selected) if (lightingEnabled.Selected)
@@ -517,8 +518,21 @@ namespace Barotrauma
{ {
foreach (XElement element in doc.Root.Elements()) foreach (XElement element in doc.Root.Elements())
{ {
if (element.Name.ToString().ToLowerInvariant() != genParams.Name.ToLowerInvariant()) continue; XElement levelParamElement = element;
SerializableProperty.SerializeProperties(genParams, element, true); if (element.IsOverride())
{
foreach (XElement subElement in element.Elements())
{
if (subElement.Name.ToString().Equals(genParams.Name, StringComparison.OrdinalIgnoreCase))
{
SerializableProperty.SerializeProperties(genParams, subElement, true);
}
}
}
else if (element.Name.ToString().Equals(genParams.Name, StringComparison.OrdinalIgnoreCase))
{
SerializableProperty.SerializeProperties(genParams, element, true);
}
break; break;
} }
} }
@@ -539,7 +553,7 @@ namespace Barotrauma
{ {
foreach (XElement element in doc.Root.Elements()) foreach (XElement element in doc.Root.Elements())
{ {
if (element.Name.ToString().ToLowerInvariant() != levelObjPrefab.Name.ToLowerInvariant()) continue; if (!element.Name.ToString().Equals(levelObjPrefab.Name, StringComparison.OrdinalIgnoreCase)) { continue; }
levelObjPrefab.Save(element); levelObjPrefab.Save(element);
break; break;
} }
@@ -564,7 +578,7 @@ namespace Barotrauma
bool elementFound = false; bool elementFound = false;
foreach (XElement element in doc.Root.Elements()) foreach (XElement element in doc.Root.Elements())
{ {
if (element.Name.ToString().ToLowerInvariant() != genParams.Name.ToLowerInvariant()) continue; if (!element.Name.ToString().Equals(genParams.Name, StringComparison.OrdinalIgnoreCase)) { continue; }
SerializableProperty.SerializeProperties(genParams, element, true); SerializableProperty.SerializeProperties(genParams, element, true);
elementFound = true; elementFound = true;
} }
@@ -78,9 +78,7 @@ namespace Barotrauma
private IEnumerable<object> LoadRound() private IEnumerable<object> LoadRound()
{ {
GameMain.GameSession.StartRound(campaignUI.SelectedLevel, GameMain.GameSession.StartRound(campaignUI.SelectedLevel,
reloadSub: true,
loadSecondSub: false,
mirrorLevel: GameMain.GameSession.Map.CurrentLocation != GameMain.GameSession.Map.SelectedConnection.Locations[0]); mirrorLevel: GameMain.GameSession.Map.CurrentLocation != GameMain.GameSession.Map.SelectedConnection.Locations[0]);
GameMain.GameScreen.Select(); GameMain.GameScreen.Select();
@@ -31,6 +31,7 @@ namespace Barotrauma
private GUITextBox serverNameBox, /*portBox, queryPortBox,*/ passwordBox, maxPlayersBox; private GUITextBox serverNameBox, /*portBox, queryPortBox,*/ passwordBox, maxPlayersBox;
private GUITickBox isPublicBox, wrongPasswordBanBox, karmaEnabledBox; private GUITickBox isPublicBox, wrongPasswordBanBox, karmaEnabledBox;
private GUIDropDown karmaPresetDD; private GUIDropDown karmaPresetDD;
private readonly GUIFrame downloadingModsContainer, enableModsContainer;
private readonly GUIButton joinServerButton, hostServerButton, steamWorkshopButton; private readonly GUIButton joinServerButton, hostServerButton, steamWorkshopButton;
private readonly GameMain game; private readonly GameMain game;
@@ -230,13 +231,31 @@ namespace Barotrauma
}; };
#if USE_STEAM #if USE_STEAM
steamWorkshopButton = new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), customizeList.RectTransform), TextManager.Get("SteamWorkshopButton"), textAlignment: Alignment.Left, style: "MainMenuGUIButton") var steamWorkshopButtonContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 1.0f), customizeList.RectTransform), style: null);
steamWorkshopButton = new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), steamWorkshopButtonContainer.RectTransform), TextManager.Get("SteamWorkshopButton"), textAlignment: Alignment.Left, style: "MainMenuGUIButton")
{ {
ForceUpperCase = true, ForceUpperCase = true,
Enabled = false, Enabled = false,
UserData = Tab.SteamWorkshop, UserData = Tab.SteamWorkshop,
OnClicked = SelectTab OnClicked = SelectTab
}; };
downloadingModsContainer = new GUIFrame(new RectTransform(new Vector2(1.4f, 0.9f), steamWorkshopButtonContainer.RectTransform,
Anchor.CenterRight, Pivot.CenterLeft)
{ RelativeOffset = new Vector2(0.3f, 0.0f) },
"MainMenuNotifBackground", Color.Yellow)
{
CanBeFocused = false,
UserData = "workshopnotif",
Visible = false
};
new GUITextBlock(new RectTransform(Vector2.One * 0.9f, downloadingModsContainer.RectTransform, Anchor.CenterLeft, Pivot.CenterLeft) { RelativeOffset = new Vector2(0.05f, 0.0f) },
TextManager.Get("ModsDownloadingNotif"), Color.Black)
{
CanBeFocused = false,
};
#endif #endif
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), customizeList.RectTransform), TextManager.Get("SubEditorButton"), textAlignment: Alignment.Left, style: "MainMenuGUIButton") new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), customizeList.RectTransform), TextManager.Get("SubEditorButton"), textAlignment: Alignment.Left, style: "MainMenuGUIButton")
@@ -280,12 +299,28 @@ namespace Barotrauma
RelativeSpacing = 0.035f RelativeSpacing = 0.035f
}; };
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), optionList.RectTransform), TextManager.Get("SettingsButton"), textAlignment: Alignment.Left, style: "MainMenuGUIButton") var settingsButtonContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 1.0f), optionList.RectTransform), style: null);
new GUIButton(new RectTransform(Vector2.One, settingsButtonContainer.RectTransform), TextManager.Get("SettingsButton"), textAlignment: Alignment.Left, style: "MainMenuGUIButton")
{ {
ForceUpperCase = true, ForceUpperCase = true,
UserData = Tab.Settings, UserData = Tab.Settings,
OnClicked = SelectTab OnClicked = SelectTab
}; };
enableModsContainer = new GUIFrame(new RectTransform(new Vector2(1.4f, 0.9f), settingsButtonContainer.RectTransform,
Anchor.CenterRight, Pivot.CenterLeft) { RelativeOffset = new Vector2(0.5f, 0.0f) },
"MainMenuNotifBackground", Color.Yellow)
{
CanBeFocused = false,
UserData = "settingsnotif",
Visible = false
};
new GUITextBlock(new RectTransform(Vector2.One * 0.9f, enableModsContainer.RectTransform, Anchor.CenterLeft, Pivot.CenterLeft) { RelativeOffset = new Vector2(0.05f, 0.0f) },
TextManager.Get("ModsInstalledNotif"), Color.Black)
{
CanBeFocused = false
};
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), optionList.RectTransform), TextManager.Get("CreditsButton"), textAlignment: Alignment.Left, style: "MainMenuGUIButton") new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), optionList.RectTransform), TextManager.Get("CreditsButton"), textAlignment: Alignment.Left, style: "MainMenuGUIButton")
{ {
@@ -401,12 +436,18 @@ namespace Barotrauma
GameMain.Client = null; GameMain.Client = null;
} }
GameMain.SubEditorScreen?.ClearBackedUpSubInfo();
Submarine.Unload(); Submarine.Unload();
ResetButtonStates(null); ResetButtonStates(null);
GameAnalyticsManager.SetCustomDimension01(""); GameAnalyticsManager.SetCustomDimension01("");
if (GameMain.SteamWorkshopScreen != null)
{
CoroutineManager.StartCoroutine(GameMain.SteamWorkshopScreen.RefreshDownloadState());
}
#if OSX #if OSX
// Hack for adjusting the viewport properly after splash screens on older Macs // Hack for adjusting the viewport properly after splash screens on older Macs
if (firstLoadOnMac) if (firstLoadOnMac)
@@ -495,12 +536,13 @@ namespace Barotrauma
} }
campaignSetupUI.CreateDefaultSaveName(); campaignSetupUI.CreateDefaultSaveName();
campaignSetupUI.RandomizeSeed(); campaignSetupUI.RandomizeSeed();
campaignSetupUI.UpdateSubList(Submarine.SavedSubmarines); campaignSetupUI.UpdateSubList(SubmarineInfo.SavedSubmarines);
break; break;
case Tab.LoadGame: case Tab.LoadGame:
campaignSetupUI.UpdateLoadMenu(); campaignSetupUI.UpdateLoadMenu();
break; break;
case Tab.Settings: case Tab.Settings:
GameMain.MainMenuScreen?.SetEnableModsNotification(false);
menuTabs[(int)Tab.Settings].RectTransform.ClearChildren(); menuTabs[(int)Tab.Settings].RectTransform.ClearChildren();
GameMain.Config.SettingsFrame.RectTransform.Parent = menuTabs[(int)Tab.Settings].RectTransform; GameMain.Config.SettingsFrame.RectTransform.Parent = menuTabs[(int)Tab.Settings].RectTransform;
GameMain.Config.SettingsFrame.RectTransform.RelativeSize = Vector2.One; GameMain.Config.SettingsFrame.RectTransform.RelativeSize = Vector2.One;
@@ -631,12 +673,12 @@ namespace Barotrauma
Rand.SetLocalRandom(1); Rand.SetLocalRandom(1);
} }
Submarine selectedSub = null; SubmarineInfo selectedSub = null;
string subName = GameMain.Config.QuickStartSubmarineName; string subName = GameMain.Config.QuickStartSubmarineName;
if (!string.IsNullOrEmpty(subName)) if (!string.IsNullOrEmpty(subName))
{ {
DebugConsole.NewMessage($"Loading the predefined quick start sub \"{subName}\"", Color.White); DebugConsole.NewMessage($"Loading the predefined quick start sub \"{subName}\"", Color.White);
selectedSub = Submarine.SavedSubmarines.FirstOrDefault(s => selectedSub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s =>
s.Name.ToLower() == subName.ToLower()); s.Name.ToLower() == subName.ToLower());
if (selectedSub == null) if (selectedSub == null)
@@ -647,7 +689,7 @@ namespace Barotrauma
if (selectedSub == null) if (selectedSub == null)
{ {
DebugConsole.NewMessage("Loading a random sub.", Color.White); DebugConsole.NewMessage("Loading a random sub.", Color.White);
var subs = Submarine.SavedSubmarines.Where(s => !s.HasTag(SubmarineTag.Shuttle) && !s.HasTag(SubmarineTag.HideInMenus)); var subs = SubmarineInfo.SavedSubmarines.Where(s => !s.HasTag(SubmarineTag.Shuttle) && !s.HasTag(SubmarineTag.HideInMenus));
selectedSub = subs.ElementAt(Rand.Int(subs.Count())); selectedSub = subs.ElementAt(Rand.Int(subs.Count()));
} }
var gamesession = new GameSession( var gamesession = new GameSession(
@@ -684,6 +726,16 @@ namespace Barotrauma
} }
} }
public void SetEnableModsNotification(bool visible)
{
if (enableModsContainer != null) { enableModsContainer.Visible = visible; }
}
public void SetDownloadingModsNotification(bool visible)
{
if (downloadingModsContainer != null) { downloadingModsContainer.Visible = visible; }
}
private void ShowTutorialSkipWarning(Tab tabToContinueTo) private void ShowTutorialSkipWarning(Tab tabToContinueTo)
{ {
var tutorialSkipWarning = new GUIMessageBox("", TextManager.Get("tutorialskipwarning"), new string[] { TextManager.Get("tutorialwarningskiptutorials"), TextManager.Get("tutorialwarningplaytutorials") }); var tutorialSkipWarning = new GUIMessageBox("", TextManager.Get("tutorialskipwarning"), new string[] { TextManager.Get("tutorialwarningskiptutorials"), TextManager.Get("tutorialwarningplaytutorials") });
@@ -990,7 +1042,7 @@ namespace Barotrauma
spriteBatch.End(); spriteBatch.End();
} }
private void StartGame(Submarine selectedSub, string saveName, string mapSeed) private void StartGame(SubmarineInfo selectedSub, string saveName, string mapSeed)
{ {
if (string.IsNullOrEmpty(saveName)) return; if (string.IsNullOrEmpty(saveName)) return;
@@ -1027,7 +1079,7 @@ namespace Barotrauma
return; return;
} }
selectedSub = new Submarine(Path.Combine(SaveUtil.TempPath, selectedSub.Name + ".sub"), ""); selectedSub = new SubmarineInfo(Path.Combine(SaveUtil.TempPath, selectedSub.Name + ".sub"));
GameMain.GameSession = new GameSession(selectedSub, saveName, GameMain.GameSession = new GameSession(selectedSub, saveName,
GameModePreset.List.Find(g => g.Identifier == "singleplayercampaign")); GameModePreset.List.Find(g => g.Identifier == "singleplayercampaign"));
@@ -1072,7 +1124,7 @@ namespace Barotrauma
var paddedLoadGame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), menuTabs[(int)Tab.LoadGame].RectTransform, Anchor.Center) { AbsoluteOffset = new Point(0, 10) }, var paddedLoadGame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), menuTabs[(int)Tab.LoadGame].RectTransform, Anchor.Center) { AbsoluteOffset = new Point(0, 10) },
style: null); style: null);
campaignSetupUI = new CampaignSetupUI(false, paddedNewGame, paddedLoadGame, Submarine.SavedSubmarines) campaignSetupUI = new CampaignSetupUI(false, paddedNewGame, paddedLoadGame, SubmarineInfo.SavedSubmarines)
{ {
LoadGame = LoadGame, LoadGame = LoadGame,
StartNewGame = StartGame StartNewGame = StartGame
@@ -208,15 +208,15 @@ namespace Barotrauma
private set; private set;
} }
public Submarine SelectedSub public SubmarineInfo SelectedSub
{ {
get { return subList.SelectedData as Submarine; } get { return subList.SelectedData as SubmarineInfo; }
set { subList.Select(value); } set { subList.Select(value); }
} }
public Submarine SelectedShuttle public SubmarineInfo SelectedShuttle
{ {
get { return shuttleList.SelectedData as Submarine; } get { return shuttleList.SelectedData as SubmarineInfo; }
} }
public bool UsingShuttle public bool UsingShuttle
@@ -443,7 +443,7 @@ namespace Barotrauma
{ {
OnClicked = (btn, userdata) => OnClicked = (btn, userdata) =>
{ {
if (!(userdata is FileReceiver.FileTransferIn transfer)) { return false; } if (!(FileTransferFrame.UserData is FileReceiver.FileTransferIn transfer)) { return false; }
GameMain.Client?.CancelFileTransfer(transfer); GameMain.Client?.CancelFileTransfer(transfer);
GameMain.Client.FileReceiver.StopTransfer(transfer); GameMain.Client.FileReceiver.StopTransfer(transfer);
return true; return true;
@@ -658,7 +658,7 @@ namespace Barotrauma
OnClicked = (btn, obj) => OnClicked = (btn, obj) =>
{ {
GameMain.Client.RequestStartRound(); GameMain.Client.RequestStartRound();
CoroutineManager.StartCoroutine(WaitForStartRound(StartButton, allowCancel: true), "WaitForStartRound"); CoroutineManager.StartCoroutine(WaitForStartRound(StartButton, allowCancel: false), "WaitForStartRound");
return true; return true;
} }
}; };
@@ -1147,6 +1147,22 @@ namespace Barotrauma
clientDisabledElements.AddRange(botSpawnModeButtons); clientDisabledElements.AddRange(botSpawnModeButtons);
} }
public void StopWaitingForStartRound()
{
CoroutineManager.StopCoroutines("WaitForStartRound");
GUIMessageBox.CloseAll();
if (StartButton != null)
{
StartButton.Enabled = true;
}
if (campaignUI?.StartButton != null)
{
campaignUI.StartButton.Enabled = true;
}
GUI.ClearCursorWait();
}
public IEnumerable<object> WaitForStartRound(GUIButton startButton, bool allowCancel) public IEnumerable<object> WaitForStartRound(GUIButton startButton, bool allowCancel)
{ {
GUI.SetCursorWaiting(); GUI.SetCursorWaiting();
@@ -1173,7 +1189,8 @@ namespace Barotrauma
} }
DateTime timeOut = DateTime.Now + new TimeSpan(0, 0, 10); DateTime timeOut = DateTime.Now + new TimeSpan(0, 0, 10);
while (Selected == GameMain.NetLobbyScreen && DateTime.Now < timeOut) while (Selected == GameMain.NetLobbyScreen &&
DateTime.Now < timeOut)
{ {
msgBox.Header.Text = headerText + new string('.', ((int)Timing.TotalTime % 3 + 1)); msgBox.Header.Text = headerText + new string('.', ((int)Timing.TotalTime % 3 + 1));
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
@@ -1322,6 +1339,8 @@ namespace Barotrauma
if (GameMain.Client == null) return; if (GameMain.Client == null) return;
spectateButton.Visible = true; spectateButton.Visible = true;
spectateButton.Enabled = true; spectateButton.Enabled = true;
StartButton.Visible = false;
} }
public void SetCampaignCharacterInfo(CharacterInfo newCampaignCharacterInfo) public void SetCampaignCharacterInfo(CharacterInfo newCampaignCharacterInfo)
@@ -1609,19 +1628,19 @@ namespace Barotrauma
MissionType = missionType; MissionType = missionType;
} }
public void UpdateSubList(GUIComponent subList, List<Submarine> submarines) public void UpdateSubList(GUIComponent subList, List<SubmarineInfo> submarines)
{ {
if (subList == null) { return; } if (subList == null) { return; }
subList.ClearChildren(); subList.ClearChildren();
foreach (Submarine sub in submarines) foreach (SubmarineInfo sub in submarines)
{ {
AddSubmarine(subList, sub); AddSubmarine(subList, sub);
} }
} }
private void AddSubmarine(GUIComponent subList, Submarine sub) private void AddSubmarine(GUIComponent subList, SubmarineInfo sub)
{ {
if (subList is GUIListBox) if (subList is GUIListBox)
{ {
@@ -1646,8 +1665,8 @@ namespace Barotrauma
CanBeFocused = false CanBeFocused = false
}; };
var matchingSub = Submarine.SavedSubmarines.FirstOrDefault(s => s.Name == sub.Name && s.MD5Hash?.Hash == sub.MD5Hash?.Hash); var matchingSub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == sub.Name && s.MD5Hash?.Hash == sub.MD5Hash?.Hash);
if (matchingSub == null) matchingSub = Submarine.SavedSubmarines.FirstOrDefault(s => s.Name == sub.Name); if (matchingSub == null) matchingSub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == sub.Name);
if (matchingSub == null) if (matchingSub == null)
{ {
@@ -1704,7 +1723,7 @@ namespace Barotrauma
{ {
if (!GameMain.Client.ServerSettings.Voting.AllowSubVoting) if (!GameMain.Client.ServerSettings.Voting.AllowSubVoting)
{ {
var selectedSub = component.UserData as Submarine; var selectedSub = component.UserData as SubmarineInfo;
if (!selectedSub.RequiredContentPackagesInstalled) if (!selectedSub.RequiredContentPackagesInstalled)
{ {
var msgBox = new GUIMessageBox(TextManager.Get("ContentPackageMismatch"), var msgBox = new GUIMessageBox(TextManager.Get("ContentPackageMismatch"),
@@ -1729,7 +1748,7 @@ namespace Barotrauma
} }
return false; return false;
} }
if (component.UserData is Submarine sub) if (component.UserData is SubmarineInfo sub)
{ {
CreateSubPreview(sub); CreateSubPreview(sub);
} }
@@ -1761,7 +1780,7 @@ namespace Barotrauma
} }
GameMain.Client.RequestSelectMode(component.Parent.GetChildIndex(component)); GameMain.Client.RequestSelectMode(component.Parent.GetChildIndex(component));
HighlightMode(SelectedModeIndex); HighlightMode(SelectedModeIndex);
return (presetName.ToLowerInvariant() != "multiplayercampaign"); return !presetName.Equals("multiplayercampaign", StringComparison.OrdinalIgnoreCase);
} }
return false; return false;
} }
@@ -1793,6 +1812,7 @@ namespace Barotrauma
SelectedColor = Color.White * 0.85f, SelectedColor = Color.White * 0.85f,
OutlineColor = Color.White * 0.5f, OutlineColor = Color.White * 0.5f,
TextColor = Color.White, TextColor = Color.White,
SelectedTextColor = Color.Black,
UserData = client UserData = client
}; };
var soundIcon = new GUIImage(new RectTransform(new Point((int)(textBlock.Rect.Height * 0.8f)), textBlock.RectTransform, Anchor.CenterRight) { AbsoluteOffset = new Point(5, 0) }, var soundIcon = new GUIImage(new RectTransform(new Point((int)(textBlock.Rect.Height * 0.8f)), textBlock.RectTransform, Anchor.CenterRight) { AbsoluteOffset = new Point(5, 0) },
@@ -1884,7 +1904,7 @@ namespace Barotrauma
OnClicked = (btn, userdata) => { if (GUI.MouseOn == btn || GUI.MouseOn == btn.TextBlock) ClosePlayerFrame(btn, userdata); return true; } OnClicked = (btn, userdata) => { if (GUI.MouseOn == btn || GUI.MouseOn == btn.TextBlock) ClosePlayerFrame(btn, userdata); return true; }
}; };
Vector2 frameSize = GameMain.Client.HasPermission(ClientPermissions.ManagePermissions) ? new Vector2(.24f, .5f) : new Vector2(.24f, .24f); Vector2 frameSize = GameMain.Client.HasPermission(ClientPermissions.ManagePermissions) ? new Vector2(.28f, .5f) : new Vector2(.28f, .24f);
var playerFrameInner = new GUIFrame(new RectTransform(frameSize, playerFrame.RectTransform, Anchor.Center) { MinSize = new Point(550, 0) }); var playerFrameInner = new GUIFrame(new RectTransform(frameSize, playerFrame.RectTransform, Anchor.Center) { MinSize = new Point(550, 0) });
var paddedPlayerFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.88f), playerFrameInner.RectTransform, Anchor.Center)) var paddedPlayerFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.88f), playerFrameInner.RectTransform, Anchor.Center))
@@ -2111,7 +2131,7 @@ namespace Barotrauma
{ {
if (GameMain.Client.HasPermission(ClientPermissions.Ban)) if (GameMain.Client.HasPermission(ClientPermissions.Ban))
{ {
var banButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonAreaTop.RectTransform), var banButton = new GUIButton(new RectTransform(new Vector2(0.34f, 1.0f), buttonAreaTop.RectTransform),
TextManager.Get("Ban")) TextManager.Get("Ban"))
{ {
UserData = selectedClient UserData = selectedClient
@@ -2119,7 +2139,7 @@ namespace Barotrauma
banButton.OnClicked = (bt, userdata) => { BanPlayer(selectedClient); return true; }; banButton.OnClicked = (bt, userdata) => { BanPlayer(selectedClient); return true; };
banButton.OnClicked += ClosePlayerFrame; banButton.OnClicked += ClosePlayerFrame;
var rangebanButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonAreaTop.RectTransform), var rangebanButton = new GUIButton(new RectTransform(new Vector2(0.34f, 1.0f), buttonAreaTop.RectTransform),
TextManager.Get("BanRange")) TextManager.Get("BanRange"))
{ {
UserData = selectedClient UserData = selectedClient
@@ -2132,7 +2152,7 @@ namespace Barotrauma
if (GameMain.Client != null && GameMain.Client.ServerSettings.Voting.AllowVoteKick && if (GameMain.Client != null && GameMain.Client.ServerSettings.Voting.AllowVoteKick &&
selectedClient != null && selectedClient.AllowKicking) selectedClient != null && selectedClient.AllowKicking)
{ {
var kickVoteButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonAreaLower.RectTransform), var kickVoteButton = new GUIButton(new RectTransform(new Vector2(0.34f, 1.0f), buttonAreaLower.RectTransform),
TextManager.Get("VoteToKick")) TextManager.Get("VoteToKick"))
{ {
Enabled = !selectedClient.HasKickVoteFromID(GameMain.Client.ID), Enabled = !selectedClient.HasKickVoteFromID(GameMain.Client.ID),
@@ -2144,7 +2164,7 @@ namespace Barotrauma
if (GameMain.Client.HasPermission(ClientPermissions.Kick) && if (GameMain.Client.HasPermission(ClientPermissions.Kick) &&
selectedClient != null && selectedClient.AllowKicking) selectedClient != null && selectedClient.AllowKicking)
{ {
var kickButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonAreaLower.RectTransform), var kickButton = new GUIButton(new RectTransform(new Vector2(0.34f, 1.0f), buttonAreaLower.RectTransform),
TextManager.Get("Kick")) TextManager.Get("Kick"))
{ {
UserData = selectedClient UserData = selectedClient
@@ -2153,6 +2173,9 @@ namespace Barotrauma
kickButton.OnClicked += ClosePlayerFrame; kickButton.OnClicked += ClosePlayerFrame;
} }
GUITextBlock.AutoScaleAndNormalize(
buttonAreaTop.Children.Select(c => ((GUIButton)c).TextBlock).Concat(buttonAreaLower.Children.Select(c => ((GUIButton)c).TextBlock)));
new GUITickBox(new RectTransform(new Vector2(0.25f, 1.0f), buttonAreaTop.RectTransform, Anchor.TopRight), new GUITickBox(new RectTransform(new Vector2(0.25f, 1.0f), buttonAreaTop.RectTransform, Anchor.TopRight),
TextManager.Get("Mute")) TextManager.Get("Mute"))
{ {
@@ -2162,7 +2185,7 @@ namespace Barotrauma
}; };
} }
var closeButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonAreaLower.RectTransform, Anchor.BottomRight), var closeButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonAreaLower.RectTransform, Anchor.TopRight),
TextManager.Get("Close")) TextManager.Get("Close"))
{ {
IgnoreLayoutGroups = true, IgnoreLayoutGroups = true,
@@ -2241,7 +2264,7 @@ namespace Barotrauma
targetMicStyle = "GUIMicrophoneDisabled"; targetMicStyle = "GUIMicrophoneDisabled";
} }
if (targetMicStyle.ToLowerInvariant() != currMicStyle.ToLowerInvariant()) if (!targetMicStyle.Equals(currMicStyle, StringComparison.OrdinalIgnoreCase))
{ {
GUI.Style.Apply(micIcon, targetMicStyle); GUI.Style.Apply(micIcon, targetMicStyle);
} }
@@ -2597,7 +2620,7 @@ namespace Barotrauma
GUILayoutGroup row = null; GUILayoutGroup row = null;
int itemsInRow = 0; int itemsInRow = 0;
XElement headElement = info.Ragdoll.MainElement.Elements().FirstOrDefault(e => e.GetAttributeString("type", "").ToLowerInvariant() == "head"); XElement headElement = info.Ragdoll.MainElement.Elements().FirstOrDefault(e => e.GetAttributeString("type", "").Equals("head", StringComparison.OrdinalIgnoreCase));
XElement headSpriteElement = headElement.Element("sprite"); XElement headSpriteElement = headElement.Element("sprite");
string spritePathWithTags = headSpriteElement.Attribute("texture").Value; string spritePathWithTags = headSpriteElement.Attribute("texture").Value;
@@ -2655,8 +2678,11 @@ namespace Barotrauma
private bool SwitchJob(GUIButton button, object obj) private bool SwitchJob(GUIButton button, object obj)
{ {
if (JobList == null) { return false; }
int childIndex = JobList.SelectedIndex; int childIndex = JobList.SelectedIndex;
var child = JobList.SelectedComponent; var child = JobList.SelectedComponent;
if (child == null) { return false; }
bool moveToNext = obj != null; bool moveToNext = obj != null;
@@ -2745,11 +2771,11 @@ namespace Barotrauma
availableJobs = availableJobs.ToList(); availableJobs = availableJobs.ToList();
int itemsInRow = 1; int itemsInRow = 0;
foreach (var jobPrefab in availableJobs) foreach (var jobPrefab in availableJobs)
{ {
if (itemsInRow >= 4) if (itemsInRow >= 3)
{ {
row = new GUILayoutGroup(new RectTransform(Vector2.One, rows.RectTransform), true); row = new GUILayoutGroup(new RectTransform(Vector2.One, rows.RectTransform), true);
itemsInRow = 0; itemsInRow = 0;
@@ -3025,7 +3051,7 @@ namespace Barotrauma
}*/ }*/
} }
public void TryDisplayCampaignSubmarine(Submarine submarine) public void TryDisplayCampaignSubmarine(SubmarineInfo submarine)
{ {
string name = submarine?.Name; string name = submarine?.Name;
bool displayed = false; bool displayed = false;
@@ -3034,13 +3060,13 @@ namespace Barotrauma
subPreviewContainer.ClearChildren(); subPreviewContainer.ClearChildren();
foreach (GUIComponent child in subList.Content.Children) foreach (GUIComponent child in subList.Content.Children)
{ {
if (!(child.UserData is Submarine sub)) { continue; } if (!(child.UserData is SubmarineInfo sub)) { continue; }
//just check the name, even though the campaign sub may not be the exact same version //just check the name, even though the campaign sub may not be the exact same version
//we're selecting the sub just for show, the selection is not actually used for anything //we're selecting the sub just for show, the selection is not actually used for anything
if (sub.Name == name) if (sub.Name == name)
{ {
subList.Select(sub); subList.Select(sub);
if (Submarine.SavedSubmarines.Contains(sub)) if (SubmarineInfo.SavedSubmarines.Contains(sub))
{ {
CreateSubPreview(sub); CreateSubPreview(sub);
displayed = true; displayed = true;
@@ -3200,10 +3226,10 @@ namespace Barotrauma
{ {
return false; return false;
} }
Submarine sub = subList.Content.Children SubmarineInfo sub = subList.Content.Children
.FirstOrDefault(c => c.UserData is Submarine s && s.Name == subName && s.MD5Hash?.Hash == md5Hash)? .FirstOrDefault(c => c.UserData is SubmarineInfo s && s.Name == subName && s.MD5Hash?.Hash == md5Hash)?
.UserData as Submarine; .UserData as SubmarineInfo;
//matching sub found and already selected, all good //matching sub found and already selected, all good
if (sub != null) if (sub != null)
@@ -3212,7 +3238,7 @@ namespace Barotrauma
{ {
CreateSubPreview(sub); CreateSubPreview(sub);
} }
if (subList.SelectedData is Submarine selectedSub && selectedSub.MD5Hash?.Hash == md5Hash && System.IO.File.Exists(sub.FilePath)) if (subList.SelectedData is SubmarineInfo selectedSub && selectedSub.MD5Hash?.Hash == md5Hash && System.IO.File.Exists(sub.FilePath))
{ {
return true; return true;
} }
@@ -3222,8 +3248,8 @@ namespace Barotrauma
if (sub == null) if (sub == null)
{ {
sub = subList.Content.Children sub = subList.Content.Children
.FirstOrDefault(c => c.UserData is Submarine s && s.Name == subName)? .FirstOrDefault(c => c.UserData is SubmarineInfo s && s.Name == subName)?
.UserData as Submarine; .UserData as SubmarineInfo;
} }
//found a sub that at least has the same name, select it //found a sub that at least has the same name, select it
@@ -3246,7 +3272,7 @@ namespace Barotrauma
FailedSelectedShuttle = null; FailedSelectedShuttle = null;
//hashes match, all good //hashes match, all good
if (sub.MD5Hash?.Hash == md5Hash && Submarine.SavedSubmarines.Contains(sub)) if (sub.MD5Hash?.Hash == md5Hash && SubmarineInfo.SavedSubmarines.Contains(sub))
{ {
return true; return true;
} }
@@ -3261,7 +3287,7 @@ namespace Barotrauma
FailedSelectedShuttle = new Pair<string, string>(subName, md5Hash); FailedSelectedShuttle = new Pair<string, string>(subName, md5Hash);
string errorMsg = ""; string errorMsg = "";
if (sub == null || !Submarine.SavedSubmarines.Contains(sub)) if (sub == null || !SubmarineInfo.SavedSubmarines.Contains(sub))
{ {
errorMsg = TextManager.GetWithVariable("SubNotFoundError", "[subname]", subName) + " "; errorMsg = TextManager.GetWithVariable("SubNotFoundError", "[subname]", subName) + " ";
} }
@@ -3303,7 +3329,7 @@ namespace Barotrauma
return false; return false;
} }
private void CreateSubPreview(Submarine sub) private void CreateSubPreview(SubmarineInfo sub)
{ {
subPreviewContainer?.ClearChildren(); subPreviewContainer?.ClearChildren();
sub.CreatePreviewWindow(subPreviewContainer); sub.CreatePreviewWindow(subPreviewContainer);
@@ -237,7 +237,7 @@ namespace Barotrauma
{ {
foreach (XElement element in doc.Root.Elements()) foreach (XElement element in doc.Root.Elements())
{ {
if (element.Name.ToString().ToLowerInvariant() != prefab.Name.ToLowerInvariant()) continue; if (!element.Name.ToString().Equals(prefab.Name, StringComparison.OrdinalIgnoreCase)) { continue; }
SerializableProperty.SerializeProperties(prefab, element, true); SerializableProperty.SerializeProperties(prefab, element, true);
} }
} }
@@ -965,7 +965,7 @@ namespace Barotrauma
child.Visible = child.Visible =
serverInfo.OwnerVerified && serverInfo.OwnerVerified &&
serverInfo.ServerName.ToLowerInvariant().Contains(searchBox.Text.ToLowerInvariant()) && serverInfo.ServerName.Contains(searchBox.Text, StringComparison.OrdinalIgnoreCase) &&
(!filterSameVersion.Selected || (remoteVersion != null && NetworkMember.IsCompatible(remoteVersion, GameMain.Version))) && (!filterSameVersion.Selected || (remoteVersion != null && NetworkMember.IsCompatible(remoteVersion, GameMain.Version))) &&
(!filterPassword.Selected || !serverInfo.HasPassword) && (!filterPassword.Selected || !serverInfo.HasPassword) &&
(!filterIncompatible.Selected || !incompatible) && (!filterIncompatible.Selected || !incompatible) &&
@@ -996,7 +996,7 @@ namespace Barotrauma
foreach (GUITickBox tickBox in gameModeTickBoxes) foreach (GUITickBox tickBox in gameModeTickBoxes)
{ {
var gameMode = (string)tickBox.UserData; var gameMode = (string)tickBox.UserData;
if (!tickBox.Selected && (serverInfo.GameMode == gameMode.ToLowerInvariant() || serverInfo.GameMode == gameMode)) if (!tickBox.Selected && serverInfo.GameMode.Equals(gameMode, StringComparison.OrdinalIgnoreCase))
{ {
child.Visible = false; child.Visible = false;
break; break;
@@ -1304,6 +1304,8 @@ namespace Barotrauma
{ {
#if DEBUG #if DEBUG
DebugConsole.ThrowError($"Failed to parse a Steam friend's connect command ({connectCommand})", e); DebugConsole.ThrowError($"Failed to parse a Steam friend's connect command ({connectCommand})", e);
#else
DebugConsole.Log($"Failed to parse a Steam friend's connect command ({connectCommand})\n" + e.StackTrace);
#endif #endif
info.ConnectName = null; info.ConnectName = null;
info.ConnectEndpoint = null; info.ConnectEndpoint = null;
@@ -1512,7 +1514,7 @@ namespace Barotrauma
{ {
serverList.ClearChildren(); serverList.ClearChildren();
if (masterServerData.Substring(0, 5).ToLowerInvariant() == "error") if (masterServerData.Substring(0, 5).Equals("error", StringComparison.OrdinalIgnoreCase))
{ {
DebugConsole.ThrowError("Error while connecting to master server (" + masterServerData + ")!"); DebugConsole.ThrowError("Error while connecting to master server (" + masterServerData + ")!");
return; return;
@@ -26,10 +26,24 @@ namespace Barotrauma
//listbox that shows the files included in the item being created //listbox that shows the files included in the item being created
private GUIListBox createItemFileList; private GUIListBox createItemFileList;
private FileSystemWatcher createItemWatcher;
private readonly List<GUIButton> tabButtons = new List<GUIButton>(); private readonly List<GUIButton> tabButtons = new List<GUIButton>();
private readonly HashSet<string> pendingPreviewImageDownloads = new HashSet<string>(); private class PendingPreviewImageDownload
private readonly Dictionary<string, Sprite> itemPreviewSprites = new Dictionary<string, Sprite>(); {
/// <summary>
/// Was the image downloaded
/// </summary>
public bool Downloaded = false;
/// <summary>
/// How many tasks are looking to create a preview image based on this download
/// </summary>
public int PendingLoads = 1;
}
private readonly Dictionary<ulong, PendingPreviewImageDownload> pendingPreviewImageDownloads = new Dictionary<ulong, PendingPreviewImageDownload>();
private Dictionary<string, Sprite> itemPreviewSprites = new Dictionary<string, Sprite>();
private enum Tab private enum Tab
{ {
@@ -54,6 +68,8 @@ namespace Barotrauma
{ {
GameMain.Instance.OnResolutionChanged += CreateUI; GameMain.Instance.OnResolutionChanged += CreateUI;
CreateUI(); CreateUI();
Steamworks.SteamUGC.GlobalOnItemInstalled += OnItemInstalled;
} }
private void CreateUI() private void CreateUI()
@@ -199,7 +215,7 @@ namespace Barotrauma
{ {
if (GUI.MouseOn is GUIButton || GUI.MouseOn?.Parent is GUIButton) { return false; } if (GUI.MouseOn is GUIButton || GUI.MouseOn?.Parent is GUIButton) { return false; }
publishedItemList.Deselect(); publishedItemList.Deselect();
if (userdata is Submarine sub) if (userdata is SubmarineInfo sub)
{ {
CreateWorkshopItem(sub); CreateWorkshopItem(sub);
} }
@@ -215,6 +231,8 @@ namespace Barotrauma
createItemFrame = new GUIFrame(new RectTransform(new Vector2(0.58f, 1.0f), tabs[(int)Tab.Publish].RectTransform, Anchor.TopRight), style: null); createItemFrame = new GUIFrame(new RectTransform(new Vector2(0.58f, 1.0f), tabs[(int)Tab.Publish].RectTransform, Anchor.TopRight), style: null);
SelectTab(Tab.Mods); SelectTab(Tab.Mods);
subscribedCoroutine = CoroutineManager.StartCoroutine(PollSubscribedItems());
} }
public override void Select() public override void Select()
@@ -230,11 +248,37 @@ namespace Barotrauma
SelectTab(Tab.Mods); SelectTab(Tab.Mods);
} }
private void OnItemInstalled(ulong itemId)
{
RefreshSubscribedItems();
}
CoroutineHandle subscribedCoroutine;
private IEnumerable<object> PollSubscribedItems()
{
if (!SteamManager.IsInitialized) { yield return CoroutineStatus.Success; }
uint numSubscribed = 0;
while (true)
{
while (CoroutineManager.IsCoroutineRunning("Load")) { yield return new WaitForSeconds(1.0f); }
uint newNumSubscribed = Steamworks.SteamUGC.NumSubscribedItems;
if (newNumSubscribed != numSubscribed)
{
RefreshSubscribedItems();
numSubscribed = newNumSubscribed;
}
yield return new WaitForSeconds(1.0f);
}
}
private void SelectTab(Tab tab) private void SelectTab(Tab tab)
{ {
for (int i = 0; i < tabs.Length; i++) for (int i = 0; i < tabs.Length; i++)
{ {
tabButtons[i].Selected = tabs[i].Visible = i == (int)tab; tabButtons[i].Selected = tabs[i].Visible = i == (int)tab;
} }
if (createItemFrame.CountChildren == 0) if (createItemFrame.CountChildren == 0)
@@ -246,6 +290,7 @@ namespace Barotrauma
}; };
} }
createItemWatcher?.Dispose(); createItemWatcher = null;
if (Screen.Selected == this) if (Screen.Selected == this)
{ {
switch (tab) switch (tab)
@@ -272,6 +317,25 @@ namespace Barotrauma
GameMain.SteamWorkshopScreen.Select(); GameMain.SteamWorkshopScreen.Select();
} }
public IEnumerable<object> RefreshDownloadState()
{
bool isDownloading = true;
while (true)
{
SteamManager.GetSubscribedWorkshopItems((items) =>
{
isDownloading = items.Any(it => it.IsDownloading || it.IsDownloadPending);
GameMain.MainMenuScreen.SetDownloadingModsNotification(isDownloading);
});
if (!isDownloading) { break; }
yield return new WaitForSeconds(0.5f);
}
yield return CoroutineStatus.Success;
}
private void RefreshSubscribedItems() private void RefreshSubscribedItems()
{ {
SteamManager.GetSubscribedWorkshopItems((items) => SteamManager.GetSubscribedWorkshopItems((items) =>
@@ -279,6 +343,8 @@ namespace Barotrauma
//filter out the items published by the player (they're shown in the publish tab) //filter out the items published by the player (they're shown in the publish tab)
var mySteamID = SteamManager.GetSteamID(); var mySteamID = SteamManager.GetSteamID();
OnItemsReceived(GetVisibleItems(items.Where(it => it.Owner.Id != mySteamID)), subscribedItemList); OnItemsReceived(GetVisibleItems(items.Where(it => it.Owner.Id != mySteamID)), subscribedItemList);
GameMain.MainMenuScreen.SetDownloadingModsNotification(items.Any(it => it.IsDownloading || it.IsDownloadPending));
}); });
} }
@@ -312,7 +378,7 @@ namespace Barotrauma
{ {
CanBeFocused = false CanBeFocused = false
}; };
foreach (Submarine sub in Submarine.SavedSubmarines) foreach (SubmarineInfo sub in SubmarineInfo.SavedSubmarines)
{ {
if (sub.HasTag(SubmarineTag.HideInMenus)) { continue; } if (sub.HasTag(SubmarineTag.HideInMenus)) { continue; }
string subPath = Path.GetFullPath(sub.FilePath); string subPath = Path.GetFullPath(sub.FilePath);
@@ -414,7 +480,7 @@ namespace Barotrauma
CanBeFocused = false CanBeFocused = false
}; };
} }
else else if (Screen.Selected == this)
{ {
new GUIImage(new RectTransform(new Point(iconSize), innerFrame.RectTransform), SteamManager.DefaultPreviewImage, scaleToFit: true) new GUIImage(new RectTransform(new Point(iconSize), innerFrame.RectTransform), SteamManager.DefaultPreviewImage, scaleToFit: true)
{ {
@@ -430,16 +496,20 @@ namespace Barotrauma
bool isNewImage; bool isNewImage;
lock (pendingPreviewImageDownloads) lock (pendingPreviewImageDownloads)
{ {
isNewImage = !pendingPreviewImageDownloads.Contains(item?.PreviewImageUrl); isNewImage = !pendingPreviewImageDownloads.ContainsKey(item.Value.Id);
if (isNewImage) { pendingPreviewImageDownloads.Add(item?.PreviewImageUrl); } if (isNewImage)
{
if (File.Exists(imagePreviewPath))
{
File.Delete(imagePreviewPath);
}
pendingPreviewImageDownloads.Add(item.Value.Id, new PendingPreviewImageDownload());
}
} }
if (isNewImage) if (isNewImage)
{ {
if (File.Exists(imagePreviewPath))
{
File.Delete(imagePreviewPath);
}
Directory.CreateDirectory(SteamManager.WorkshopItemPreviewImageFolder); Directory.CreateDirectory(SteamManager.WorkshopItemPreviewImageFolder);
Uri baseAddress = new Uri(item?.PreviewImageUrl); Uri baseAddress = new Uri(item?.PreviewImageUrl);
@@ -450,16 +520,23 @@ namespace Barotrauma
var request = new RestRequest(fileName, Method.GET); var request = new RestRequest(fileName, Method.GET);
client.ExecuteAsync(request, response => client.ExecuteAsync(request, response =>
{ {
lock (pendingPreviewImageDownloads) OnPreviewImageDownloaded(response, imagePreviewPath,
{ () =>
pendingPreviewImageDownloads.Remove(item?.PreviewImageUrl); {
} lock (pendingPreviewImageDownloads)
OnPreviewImageDownloaded(response, imagePreviewPath); {
CoroutineManager.StartCoroutine(WaitForItemPreviewDownloaded(item, listBox, imagePreviewPath)); pendingPreviewImageDownloads[item.Value.Id].Downloaded = true;
}
CoroutineManager.StartCoroutine(WaitForItemPreviewDownloaded(item, listBox, imagePreviewPath));
});
}); });
} }
else else
{ {
lock (pendingPreviewImageDownloads)
{
pendingPreviewImageDownloads[item.Value.Id].PendingLoads++;
}
CoroutineManager.StartCoroutine(WaitForItemPreviewDownloaded(item, listBox, imagePreviewPath)); CoroutineManager.StartCoroutine(WaitForItemPreviewDownloaded(item, listBox, imagePreviewPath));
} }
} }
@@ -468,7 +545,7 @@ namespace Barotrauma
{ {
lock (pendingPreviewImageDownloads) lock (pendingPreviewImageDownloads)
{ {
pendingPreviewImageDownloads.Remove(item?.PreviewImageUrl); pendingPreviewImageDownloads.Remove(item.Value.Id);
} }
DebugConsole.ThrowError("Downloading the preview image of the Workshop item \"" + item?.Title + "\" failed.", e); DebugConsole.ThrowError("Downloading the preview image of the Workshop item \"" + item?.Title + "\" failed.", e);
} }
@@ -488,10 +565,11 @@ namespace Barotrauma
CanBeFocused = false CanBeFocused = false
}; };
if ((item?.IsSubscribed ?? false) && (item?.IsInstalled ?? false)) if ((item?.IsSubscribed ?? false) && (item?.IsInstalled ?? false) && Directory.Exists(item?.Directory))
{ {
GUITickBox enabledTickBox = null; bool installed = SteamManager.CheckWorkshopItemEnabled(item);
try
if (!installed)
{ {
bool? compatible = SteamManager.CheckWorkshopItemCompatibility(item); bool? compatible = SteamManager.CheckWorkshopItemCompatibility(item);
if (compatible.HasValue && !compatible.Value) if (compatible.HasValue && !compatible.Value)
@@ -504,63 +582,32 @@ namespace Barotrauma
} }
else else
{ {
enabledTickBox = new GUITickBox(new RectTransform(new Point(32, 32), rightColumn.RectTransform), null) installed = SteamManager.EnableWorkShopItem(item, true, out string errorMsg, Screen.Selected == this);
{
ToolTip = TextManager.Get("WorkshopItemEnabled"),
UserData = item,
};
enabledTickBox.Selected = SteamManager.CheckWorkshopItemEnabled(item);
enabledTickBox.OnSelected = ToggleItemEnabled;
}
}
catch (Exception e)
{
if (enabledTickBox != null) { enabledTickBox.Enabled = false; }
itemFrame.ToolTip = e.Message;
itemFrame.Color = GUI.Style.Red;
itemFrame.HoverColor = GUI.Style.Red;
itemFrame.SelectedColor = GUI.Style.Red;
titleText.TextColor = GUI.Style.Red;
if (item?.IsSubscribed ?? false) if (!installed)
{
new GUIButton(new RectTransform(new Vector2(0.5f, 0.5f), rightColumn.RectTransform), TextManager.Get("WorkshopItemUnsubscribe"))
{ {
UserData = item, DebugConsole.ThrowError(errorMsg);
OnClicked = (btn, userdata) => new GUIMessageBox(
{ TextManager.Get("Error"),
item?.Unsubscribe(); TextManager.GetWithVariables("WorkshopItemUpdateFailed", new string[2] { "[itemname]", "[errormessage]" }, new string[2] { TextManager.EnsureUTF8(item?.Title), errorMsg }));
subscribedItemList.RemoveChild(subscribedItemList.Content.GetChildByUserData(item));
return true;
}
};
}
}
if (listBox != publishedItemList && SteamManager.CheckWorkshopItemEnabled(item) && !SteamManager.CheckWorkshopItemUpToDate(item))
{
new GUIButton(new RectTransform(new Vector2(0.4f, 0.5f), rightColumn.RectTransform, Anchor.BottomLeft), text: TextManager.Get("WorkshopItemUpdate"))
{
UserData = "updatebutton",
Font = GUI.SmallFont,
OnClicked = (btn, userdata) =>
{
if (SteamManager.UpdateWorkshopItem(item, out string errorMsg))
{
new GUIMessageBox("", TextManager.GetWithVariable("WorkshopItemUpdated", "[itemname]", item?.Title));
}
else
{
DebugConsole.ThrowError(errorMsg);
new GUIMessageBox(
TextManager.Get("Error"),
TextManager.GetWithVariables("WorkshopItemUpdateFailed", new string[2] { "[itemname]", "[errormessage]" }, new string[2] { item?.Title, errorMsg }));
}
btn.Enabled = false;
btn.Visible = false;
return true;
} }
}; }
}
if (installed)
{
bool upToDate = SteamManager.CheckWorkshopItemUpToDate(item);
if (!upToDate)
{
if (!SteamManager.UpdateWorkshopItem(item, out string errorMsg))
{
DebugConsole.ThrowError(errorMsg);
new GUIMessageBox(
TextManager.Get("Error"),
TextManager.GetWithVariables("WorkshopItemUpdateFailed", new string[2] { "[itemname]", "[errormessage]" }, new string[2] { TextManager.EnsureUTF8(item?.Title), errorMsg }));
}
}
} }
} }
@@ -568,7 +615,11 @@ namespace Barotrauma
{ {
new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.5f), rightColumn.RectTransform), TextManager.Get("WorkshopItemDownloading")); new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.5f), rightColumn.RectTransform), TextManager.Get("WorkshopItemDownloading"));
} }
else else if (item?.IsDownloadPending ?? false)
{
new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.5f), rightColumn.RectTransform), TextManager.Get("WorkshopItemDownloadPending"));
}
else if (!(item?.IsSubscribed ?? false))
{ {
var downloadBtn = new GUIButton(new RectTransform(new Point((int)(32 * GUI.Scale)), rightColumn.RectTransform), "", style: "GUIPlusButton") var downloadBtn = new GUIButton(new RectTransform(new Point((int)(32 * GUI.Scale)), rightColumn.RectTransform), "", style: "GUIPlusButton")
{ {
@@ -579,10 +630,66 @@ namespace Barotrauma
downloadBtn.OnClicked = (btn, userdata) => { DownloadItem(itemFrame, downloadBtn, item); return true; }; downloadBtn.OnClicked = (btn, userdata) => { DownloadItem(itemFrame, downloadBtn, item); return true; };
} }
if ((item?.IsSubscribed ?? false) && listBox == subscribedItemList)
{
var reinstallBtn = new GUIButton(new RectTransform(new Point((int)(32 * GUI.Scale)), rightColumn.RectTransform), "", style: "GUIReloadButton")
{
ToolTip = TextManager.Get("WorkshopItemReinstall"),
ForceUpperCase = true,
UserData = "reinstall"
};
reinstallBtn.OnClicked = (btn, userdata) =>
{
var elem = subscribedItemList.Content.GetChildByUserData(item);
try
{
bool reselect = GameMain.Config.SelectedContentPackages.Any(cp => !string.IsNullOrWhiteSpace(cp.SteamWorkshopUrl) && cp.SteamWorkshopUrl == item?.Url);
if (!SteamManager.DisableWorkShopItem(item, false, out string errorMsg) ||
!SteamManager.EnableWorkShopItem(item, true, out errorMsg, reselect, true))
{
DebugConsole.ThrowError($"Failed to reinstall \"{item?.Title}\": {errorMsg}", null, true);
elem.Flash(GUI.Style.Red);
}
}
catch (Exception e)
{
DebugConsole.ThrowError($"Failed to reinstall \"{item?.Title}\"", e, true);
elem.Flash(GUI.Style.Red);
}
return true;
};
var unsubBtn = new GUIButton(new RectTransform(new Point((int)(32 * GUI.Scale)), rightColumn.RectTransform), "", style: "GUIMinusButton")
{
ToolTip = TextManager.Get("WorkshopItemUnsubscribe"),
ForceUpperCase = true,
UserData = "unsubscribe"
};
unsubBtn.OnClicked = (btn, userdata) =>
{
SteamManager.DisableWorkShopItem(item, true, out _);
item?.Unsubscribe();
subscribedItemList.RemoveChild(subscribedItemList.Content.GetChildByUserData(item));
return true;
};
}
innerFrame.Recalculate(); innerFrame.Recalculate();
listBox.RecalculateChildren(); listBox.RecalculateChildren();
} }
public void SetReinstallButtonStatus(Steamworks.Ugc.Item? item, bool enabled, Color? flashColor)
{
var child = subscribedItemList.Content.FindChild((component) => { return (component.UserData is Steamworks.Ugc.Item?) && (component.UserData as Steamworks.Ugc.Item?)?.Id == item?.Id; });
if (child != null)
{
var reinstallBtn = child.FindChild("reinstall", true);
if (reinstallBtn != null) { reinstallBtn.Enabled = enabled; }
var unsubBtn = child.FindChild("unsubscribe", true);
if (unsubBtn != null) { unsubBtn.Enabled = enabled; }
if (flashColor.HasValue) { child.Flash(flashColor); }
}
}
private void RemoveItemFromLists(ulong itemID) private void RemoveItemFromLists(ulong itemID)
{ {
RemoveItemFromList(publishedItemList); RemoveItemFromList(publishedItemList);
@@ -596,7 +703,7 @@ namespace Barotrauma
} }
} }
private void CreateMyItemFrame(Submarine submarine, GUIListBox listBox) private void CreateMyItemFrame(SubmarineInfo submarine, GUIListBox listBox)
{ {
var itemFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.1f), listBox.Content.RectTransform, minSize: new Point(0, 80)), var itemFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.1f), listBox.Content.RectTransform, minSize: new Point(0, 80)),
style: "ListBoxElement") style: "ListBoxElement")
@@ -629,21 +736,27 @@ namespace Barotrauma
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.4f), innerFrame.RectTransform), contentPackage.Name, textAlignment: Alignment.CenterLeft); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.4f), innerFrame.RectTransform), contentPackage.Name, textAlignment: Alignment.CenterLeft);
} }
private void OnPreviewImageDownloaded(IRestResponse response, string previewImagePath) private void OnPreviewImageDownloaded(IRestResponse response, string previewImagePath, Action action)
{ {
if (response.ResponseStatus == ResponseStatus.Completed) if (response.ResponseStatus == ResponseStatus.Completed)
{ {
try TaskPool.Add(WritePreviewImageAsync(response, previewImagePath), (task) => { action?.Invoke(); });
{ }
File.WriteAllBytes(previewImagePath, response.RawBytes); }
}
catch (Exception e) private async Task WritePreviewImageAsync(IRestResponse response, string previewImagePath)
{ {
string errorMsg = "Failed to save workshop item preview image to \"" + previewImagePath + "\"."; await Task.Yield();
GameAnalyticsManager.AddErrorEventOnce("SteamWorkshopScreen.OnItemPreviewDownloaded:WriteAllBytesFailed" + previewImagePath, try
GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg + "\n" + e.Message); {
return; File.WriteAllBytes(previewImagePath, response.RawBytes);
} }
catch (Exception e)
{
string errorMsg = "Failed to save workshop item preview image to \"" + previewImagePath + "\".";
GameAnalyticsManager.AddErrorEventOnce("SteamWorkshopScreen.OnItemPreviewDownloaded:WriteAllBytesFailed" + previewImagePath,
GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg + "\n" + e.Message);
return;
} }
} }
@@ -653,47 +766,67 @@ namespace Barotrauma
{ {
lock (pendingPreviewImageDownloads) lock (pendingPreviewImageDownloads)
{ {
if (!pendingPreviewImageDownloads.Contains(item?.PreviewImageUrl)) { break; } if (pendingPreviewImageDownloads[item.Value.Id].Downloaded){ break; }
} }
yield return CoroutineStatus.Running; yield return new WaitForSeconds(0.2f);
} }
if (File.Exists(previewImagePath)) if (File.Exists(previewImagePath))
{ {
Sprite newSprite; TaskPool.Add(LoadPreviewImageAsync(item?.PreviewImageUrl, previewImagePath),
if (itemPreviewSprites.ContainsKey(item?.PreviewImageUrl)) new Tuple<Steamworks.Ugc.Item?, GUIListBox>(item, listBox),
(task, tuple) =>
{ {
newSprite = itemPreviewSprites[item?.PreviewImageUrl]; (var it, var lb) = tuple;
} var previewImage = lb.Content.FindChild(item)?.GetChildByUserData("previewimage") as GUIImage;
else if (previewImage != null)
{ {
newSprite = new Sprite(previewImagePath, sourceRectangle: null); previewImage.Sprite = task.Result;
itemPreviewSprites.Add(item?.PreviewImageUrl, newSprite); }
} else
{
CreateWorkshopItemFrame(it, lb);
}
if (listBox.Content.FindChild(item)?.GetChildByUserData("previewimage") is GUIImage previewImage) if (modsPreviewFrame.FindChild(it) != null)
{ {
previewImage.Sprite = newSprite; ShowItemPreview(it, modsPreviewFrame);
} }
else if (browsePreviewFrame.FindChild(item) != null)
{ {
CreateWorkshopItemFrame(item, listBox); ShowItemPreview(it, browsePreviewFrame);
} }
if (modsPreviewFrame.FindChild(item) != null) lock (pendingPreviewImageDownloads)
{ {
ShowItemPreview(item, modsPreviewFrame); pendingPreviewImageDownloads[it.Value.Id].PendingLoads--;
} if (pendingPreviewImageDownloads[it.Value.Id].PendingLoads <= 0) { pendingPreviewImageDownloads.Remove(it.Value.Id); }
if (browsePreviewFrame.FindChild(item) != null) }
{ });
ShowItemPreview(item, browsePreviewFrame);
}
} }
yield return CoroutineStatus.Success; yield return CoroutineStatus.Success;
} }
private async Task<Sprite> LoadPreviewImageAsync(string previewImageUrl, string previewImagePath)
{
await Task.Yield();
lock (itemPreviewSprites)
{
if (itemPreviewSprites.ContainsKey(previewImageUrl))
{
return itemPreviewSprites[previewImageUrl];
}
else
{
Sprite newSprite = new Sprite(previewImagePath, sourceRectangle: null);
itemPreviewSprites.Add(previewImageUrl, newSprite);
return newSprite;
}
}
}
private bool DownloadItem(GUIComponent frame, GUIButton downloadButton, Steamworks.Ugc.Item? item) private bool DownloadItem(GUIComponent frame, GUIButton downloadButton, Steamworks.Ugc.Item? item)
{ {
if (item == null) { return false; } if (item == null) { return false; }
@@ -721,49 +854,6 @@ namespace Barotrauma
return true; return true;
} }
private bool ToggleItemEnabled(GUITickBox tickBox)
{
if (!(tickBox.UserData is Steamworks.Ugc.Item?)) { return false; }
var item = tickBox.UserData as Steamworks.Ugc.Item?;
if (item == null) { return false; }
//currently editing the item, don't allow enabling/disabling it
if (itemEditor?.FileId == item?.Id) { tickBox.Selected = true; return false; }
var updateButton = tickBox.Parent.FindChild("updatebutton");
string errorMsg;
if (tickBox.Selected)
{
if (!SteamManager.EnableWorkShopItem(item, false, out errorMsg))
{
tickBox.Visible = false;
tickBox.Selected = false;
if (tickBox.Parent.GetChildByUserData("titletext") is GUITextBlock titleText) { titleText.TextColor = GUI.Style.Red; }
}
}
else
{
if (!SteamManager.DisableWorkShopItem(item, false, out errorMsg))
{
tickBox.Enabled = false;
}
GameMain.Config.EnsureCoreContentPackageSelected();
}
if (updateButton != null)
{
//cannot update if enabling/disabling the item failed or if the item is not enabled
updateButton.Enabled = tickBox.Enabled && tickBox.Selected;
}
if (!string.IsNullOrEmpty(errorMsg))
{
new GUIMessageBox(TextManager.Get("Error"), errorMsg);
}
return true;
}
private void ShowItemPreview(Steamworks.Ugc.Item? item, GUIFrame itemPreviewFrame) private void ShowItemPreview(Steamworks.Ugc.Item? item, GUIFrame itemPreviewFrame)
{ {
itemPreviewFrame.ClearChildren(); itemPreviewFrame.ClearChildren();
@@ -920,7 +1010,7 @@ namespace Barotrauma
}; };
} }
private void CreateWorkshopItem(Submarine sub) private void CreateWorkshopItem(SubmarineInfo sub)
{ {
string destinationFolder = Path.Combine("Mods", sub.Name); string destinationFolder = Path.Combine("Mods", sub.Name);
itemContentPackage = ContentPackage.CreatePackage(sub.Name, Path.Combine(destinationFolder, SteamManager.MetadataFileName), corePackage: false); itemContentPackage = ContentPackage.CreatePackage(sub.Name, Path.Combine(destinationFolder, SteamManager.MetadataFileName), corePackage: false);
@@ -940,8 +1030,8 @@ namespace Barotrauma
itemContentPackage.AddFile(sub.FilePath, ContentType.Submarine); itemContentPackage.AddFile(sub.FilePath, ContentType.Submarine);
itemContentPackage.Name = sub.Name; itemContentPackage.Name = sub.Name;
itemContentPackage.Save(itemContentPackage.Path); itemContentPackage.Save(itemContentPackage.Path);
ContentPackage.List.Add(itemContentPackage); //ContentPackage.List.Add(itemContentPackage);
GameMain.Config.SelectContentPackage(itemContentPackage); //GameMain.Config.SelectContentPackage(itemContentPackage);
itemEditor = itemEditor?.WithTitle(sub.Name).WithTag("Submarine").WithDescription(sub.Description); itemEditor = itemEditor?.WithTitle(sub.Name).WithTag("Submarine").WithDescription(sub.Description);
@@ -1097,7 +1187,7 @@ namespace Barotrauma
var tagBtn = new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), tagHolder.Content.RectTransform, anchor: Anchor.CenterLeft), var tagBtn = new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), tagHolder.Content.RectTransform, anchor: Anchor.CenterLeft),
tag.CapitaliseFirstInvariant(), style: "GUIButtonRound"); tag.CapitaliseFirstInvariant(), style: "GUIButtonRound");
tagBtn.TextBlock.AutoScaleHorizontal = true; tagBtn.TextBlock.AutoScaleHorizontal = true;
tagBtn.Selected = itemEditor?.Tags?.Any(t => t.ToLowerInvariant() == tag) ?? false; tagBtn.Selected = itemEditor?.Tags?.Any(t => t.Equals(tag, StringComparison.OrdinalIgnoreCase)) ?? false;
tagBtn.OnClicked = (btn, userdata) => tagBtn.OnClicked = (btn, userdata) =>
{ {
@@ -1108,7 +1198,7 @@ namespace Barotrauma
} }
else else
{ {
itemEditor?.Tags?.RemoveAll(t => t.ToLowerInvariant() == tagBtn.Text.ToLowerInvariant()); itemEditor?.Tags?.RemoveAll(t => t.Equals(tagBtn.Text, StringComparison.OrdinalIgnoreCase));
tagBtn.Selected = false; tagBtn.Selected = false;
} }
return true; return true;
@@ -1201,6 +1291,16 @@ namespace Barotrauma
OnClicked = (btn, userdata) => { ToolBox.OpenFileWithShell(Path.GetFullPath(Path.GetDirectoryName(itemContentPackage.Path))); return true; } OnClicked = (btn, userdata) => { ToolBox.OpenFileWithShell(Path.GetFullPath(Path.GetDirectoryName(itemContentPackage.Path))); return true; }
}; };
createItemFileList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.35f), createItemContent.RectTransform)); createItemFileList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.35f), createItemContent.RectTransform));
createItemWatcher?.Dispose();
createItemWatcher = new FileSystemWatcher(Path.GetDirectoryName(itemContentPackage.Path))
{
Filter = "*",
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName
};
createItemWatcher.Created += OnFileSystemChanges;
createItemWatcher.Deleted += OnFileSystemChanges;
createItemWatcher.Renamed += OnFileSystemChanges;
createItemWatcher.EnableRaisingEvents = true;
RefreshCreateItemFileList(); RefreshCreateItemFileList();
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), createItemContent.RectTransform), isHorizontal: true) var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), createItemContent.RectTransform), isHorizontal: true)
@@ -1447,23 +1547,54 @@ namespace Barotrauma
{ {
destinationPath = Path.Combine(modFolder, filePathRelativeToModFolder); destinationPath = Path.Combine(modFolder, filePathRelativeToModFolder);
} }
itemContentPackage.AddFile(destinationPath, ContentType.None);
} }
itemContentPackage.Save(itemContentPackage.Path);
RefreshCreateItemFileList(); RefreshCreateItemFileList();
} }
volatile bool refreshFileList = false;
private void OnFileSystemChanges(object sender, FileSystemEventArgs e)
{
refreshFileList = true;
}
private void RefreshCreateItemFileList() private void RefreshCreateItemFileList()
{ {
createItemFileList.ClearChildren(); createItemFileList.ClearChildren();
if (itemContentPackage == null) return; if (itemContentPackage == null) return;
var contentTypes = Enum.GetValues(typeof(ContentType)); var contentTypes = Enum.GetValues(typeof(ContentType));
foreach (ContentFile contentFile in itemContentPackage.Files) List<ContentFile> files = itemContentPackage.Files.ToList();
foreach (ContentFile contentFile in files)
{
bool fileExists = File.Exists(contentFile.Path);
if (!fileExists) { itemContentPackage.Files.Remove(contentFile); continue; }
}
List<ContentFile> allFiles = Directory.GetFiles(Path.GetDirectoryName(itemContentPackage.Path), "*", SearchOption.AllDirectories)
.Select(f => new ContentFile(f, ContentType.None))
.Where(file => Path.GetFileName(file.Path) != SteamManager.MetadataFileName &&
Path.GetFileName(file.Path) != SteamManager.PreviewImageName)
.ToList();
for (int i=0;i<allFiles.Count;i++)
{
ContentFile file = allFiles[i];
ContentFile otherFile = itemContentPackage.Files.Find(f => string.Equals(Path.GetFullPath(f.Path).CleanUpPath(),
Path.GetFullPath(file.Path).CleanUpPath(),
StringComparison.InvariantCultureIgnoreCase));
if (otherFile != null)
{
//replace the generated ContentFile object with the one that's present in the
//content package to determine which tickboxes should already be checked
allFiles[i] = otherFile;
}
}
foreach (ContentFile contentFile in allFiles)
{ {
bool illegalPath = !ContentPackage.IsModFilePathAllowed(contentFile); bool illegalPath = !ContentPackage.IsModFilePathAllowed(contentFile);
//string pathInStagingFolder = Path.Combine(SteamManager.WorkshopItemStagingFolder, contentFile.Path);
//bool fileInStagingFolder = File.Exists(pathInStagingFolder);
bool fileExists = File.Exists(contentFile.Path); bool fileExists = File.Exists(contentFile.Path);
var fileFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.12f), createItemFileList.Content.RectTransform) { MinSize = new Point(0, 20) }, var fileFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.12f), createItemFileList.Content.RectTransform) { MinSize = new Point(0, 20) },
@@ -1479,11 +1610,25 @@ namespace Barotrauma
RelativeSpacing = 0.05f RelativeSpacing = 0.05f
}; };
var tickBox = new GUITickBox(new RectTransform(new Vector2(0.1f, 0.8f), content.RectTransform), "") var tickBox = new GUITickBox(new RectTransform(Vector2.One, content.RectTransform, scaleBasis: ScaleBasis.BothHeight), "")
{ {
Selected = fileExists && !illegalPath, Selected = itemContentPackage.Files.Contains(contentFile),
Enabled = false, UserData = contentFile
ToolTip = TextManager.Get(illegalPath ? "WorkshopItemFileNotIncluded" : "WorkshopItemFileIncluded") };
tickBox.OnSelected = (tb) =>
{
ContentFile f = tb.UserData as ContentFile;
if (tb.Selected)
{
if (!itemContentPackage.Files.Contains(f)) { itemContentPackage.Files.Add(f); }
}
else
{
if (itemContentPackage.Files.Contains(f)) { itemContentPackage.Files.Remove(f); }
}
return true;
}; };
var nameText = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), content.RectTransform, Anchor.CenterLeft), contentFile.Path, font: GUI.SmallFont) var nameText = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), content.RectTransform, Anchor.CenterLeft), contentFile.Path, font: GUI.SmallFont)
@@ -1523,10 +1668,29 @@ namespace Barotrauma
{ {
OnClicked = (btn, userdata) => OnClicked = (btn, userdata) =>
{ {
itemContentPackage.RemoveFile(contentFile); var msgBox = new GUIMessageBox(TextManager.Get("ConfirmFileDeletionHeader"),
itemContentPackage.Save(itemContentPackage.Path); TextManager.GetWithVariable("ConfirmFileDeletion", "[file]", contentFile.Path),
RefreshCreateItemFileList(); new string[] { TextManager.Get("Yes"), TextManager.Get("Cancel") })
RefreshMyItemList(); {
UserData = "verificationprompt"
};
msgBox.Buttons[0].OnClicked = (applyButton, obj) =>
{
try
{
File.Delete(contentFile.Path);
if (contentFile.Type == ContentType.Submarine) { SubmarineInfo.RefreshSavedSub(contentFile.Path); }
}
catch (Exception e)
{
DebugConsole.ThrowError($"Failed to delete \"${contentFile.Path}\".", e);
}
//RefreshCreateItemFileList();
RefreshMyItemList();
return true;
};
msgBox.Buttons[0].OnClicked += msgBox.Close;
msgBox.Buttons[1].OnClicked = msgBox.Close;
return true; return true;
} }
}; };
@@ -1536,6 +1700,8 @@ namespace Barotrauma
new Point(0, (int)(content.RectTransform.Children.Max(c => c.MinSize.Y) / content.RectTransform.RelativeSize.Y)); new Point(0, (int)(content.RectTransform.Children.Max(c => c.MinSize.Y) / content.RectTransform.RelativeSize.Y));
nameText.Text = ToolBox.LimitString(nameText.Text, nameText.Font, maxWidth: nameText.Rect.Width); nameText.Text = ToolBox.LimitString(nameText.Text, nameText.Font, maxWidth: nameText.Rect.Width);
} }
itemContentPackage.Save(itemContentPackage.Path);
} }
private void PublishWorkshopItem() private void PublishWorkshopItem()
@@ -1640,6 +1806,11 @@ namespace Barotrauma
public override void Update(double deltaTime) public override void Update(double deltaTime)
{ {
if (refreshFileList)
{
RefreshCreateItemFileList();
refreshFileList = false;
}
} }
#endregion #endregion
@@ -7,6 +7,8 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
using EventInput;
using Microsoft.Xna.Framework.Input;
namespace Barotrauma namespace Barotrauma
{ {
@@ -30,6 +32,8 @@ namespace Barotrauma
private readonly Camera cam; private readonly Camera cam;
private SubmarineInfo backedUpSubInfo;
private Point screenResolution; private Point screenResolution;
private bool lightingEnabled; private bool lightingEnabled;
@@ -88,6 +92,9 @@ namespace Barotrauma
private GUITextBlock submarineDescriptionCharacterCount; private GUITextBlock submarineDescriptionCharacterCount;
private Mode mode; private Mode mode;
// Prevent the mode from changing
private bool lockMode;
public override Camera Cam public override Camera Cam
{ {
@@ -96,9 +103,9 @@ namespace Barotrauma
public string GetSubDescription() public string GetSubDescription()
{ {
string localizedDescription = TextManager.Get("submarine.description." + (Submarine.MainSub?.Name ?? ""), true); string localizedDescription = TextManager.Get("submarine.description." + (Submarine.MainSub?.Info.Name ?? ""), true);
if (localizedDescription != null) { return localizedDescription; } if (localizedDescription != null) { return localizedDescription; }
return (Submarine.MainSub == null) ? "" : Submarine.MainSub.Description; return (Submarine.MainSub == null) ? "" : Submarine.MainSub.Info.Description;
} }
private string GetTotalHullVolume() private string GetTotalHullVolume()
@@ -214,63 +221,10 @@ namespace Barotrauma
new GUIFrame(new RectTransform(new Vector2(0.01f, 0.9f), paddedTopPanel.RectTransform), style: "VerticalLine"); new GUIFrame(new RectTransform(new Vector2(0.01f, 0.9f), paddedTopPanel.RectTransform), style: "VerticalLine");
subNameLabel = new GUITextBlock(new RectTransform(new Vector2(0.3f, 0.9f), paddedTopPanel.RectTransform, Anchor.CenterLeft), new GUIButton(new RectTransform(new Vector2(0.9f, 0.9f), paddedTopPanel.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "TestButton")
TextManager.Get("unspecifiedsubfilename"), font: GUI.LargeFont, textAlignment: Alignment.CenterLeft);
linkedSubBox = new GUIDropDown(new RectTransform(new Vector2(0.15f, 0.9f), paddedTopPanel.RectTransform),
TextManager.Get("AddSubButton"), elementCount: 20)
{ {
ToolTip = TextManager.Get("AddSubToolTip") ToolTip = TextManager.Get("TestSubButton"),
}; OnClicked = TestSubmarine
foreach (Submarine sub in Submarine.SavedSubmarines)
{
linkedSubBox.AddItem(sub.Name, sub);
}
linkedSubBox.OnSelected += SelectLinkedSub;
linkedSubBox.OnDropped += (component, obj) =>
{
MapEntity.SelectedList.Clear();
return true;
};
new GUIFrame(new RectTransform(new Vector2(0.01f, 0.9f), paddedTopPanel.RectTransform), style: "VerticalLine");
defaultModeTickBox = new GUITickBox(new RectTransform(new Vector2(0.9f, 0.9f), paddedTopPanel.RectTransform, scaleBasis: ScaleBasis.BothHeight), "", style: "EditSubButton")
{
ToolTip = TextManager.Get("SubEditorEditingMode"),
OnSelected = (GUITickBox tBox) =>
{
if (tBox.Selected) { SetMode(Mode.Default); }
return true;
}
};
characterModeTickBox = new GUITickBox(new RectTransform(new Vector2(0.9f, 0.9f), paddedTopPanel.RectTransform, scaleBasis: ScaleBasis.BothHeight), "", style: "CharacterModeButton")
{
ToolTip = TextManager.Get("CharacterModeButton") + '\n' + TextManager.Get("CharacterModeToolTip"),
OnSelected = (GUITickBox tBox) =>
{
SetMode(tBox.Selected ? Mode.Character : Mode.Default);
return true;
}
};
wiringModeTickBox = new GUITickBox(new RectTransform(new Vector2(0.9f, 0.9f), paddedTopPanel.RectTransform, scaleBasis: ScaleBasis.BothHeight), "", style: "WiringModeButton")
{
ToolTip = TextManager.Get("WiringModeButton") + '\n' + TextManager.Get("WiringModeToolTip"),
OnSelected = (GUITickBox tBox) =>
{
SetMode(tBox.Selected ? Mode.Wiring : Mode.Default);
return true;
}
};
new GUIFrame(new RectTransform(new Vector2(0.01f, 0.9f), paddedTopPanel.RectTransform), style: "VerticalLine");
new GUIButton(new RectTransform(new Vector2(0.9f, 0.9f), paddedTopPanel.RectTransform, scaleBasis: ScaleBasis.BothHeight), "", style: "GenerateWaypointsButton")
{
ToolTip = TextManager.Get("GenerateWaypointsButton") + '\n' + TextManager.Get("GenerateWaypointsToolTip"),
OnClicked = GenerateWaypoints
}; };
new GUIFrame(new RectTransform(new Vector2(0.01f, 0.9f), paddedTopPanel.RectTransform), style: "VerticalLine"); new GUIFrame(new RectTransform(new Vector2(0.01f, 0.9f), paddedTopPanel.RectTransform), style: "VerticalLine");
@@ -282,7 +236,7 @@ namespace Barotrauma
{ {
previouslyUsedPanel.Visible = false; previouslyUsedPanel.Visible = false;
showEntitiesPanel.Visible = !showEntitiesPanel.Visible; showEntitiesPanel.Visible = !showEntitiesPanel.Visible;
showEntitiesPanel.RectTransform.AbsoluteOffset = new Point(btn.Rect.X, TopPanel.Rect.Height); showEntitiesPanel.RectTransform.AbsoluteOffset = new Point(Math.Max(Math.Max(btn.Rect.X, entityCountPanel.Rect.Right), saveAssemblyFrame.Rect.Right), TopPanel.Rect.Height);
return true; return true;
} }
}; };
@@ -294,7 +248,110 @@ namespace Barotrauma
{ {
showEntitiesPanel.Visible = false; showEntitiesPanel.Visible = false;
previouslyUsedPanel.Visible = !previouslyUsedPanel.Visible; previouslyUsedPanel.Visible = !previouslyUsedPanel.Visible;
previouslyUsedPanel.RectTransform.AbsoluteOffset = new Point(btn.Rect.X, TopPanel.Rect.Height); previouslyUsedPanel.RectTransform.AbsoluteOffset = new Point(Math.Max(Math.Max(btn.Rect.X, entityCountPanel.Rect.Right), saveAssemblyFrame.Rect.Right), TopPanel.Rect.Height);
return true;
}
};
new GUIFrame(new RectTransform(new Vector2(0.01f, 0.9f), paddedTopPanel.RectTransform), style: "VerticalLine");
subNameLabel = new GUITextBlock(new RectTransform(new Vector2(0.3f, 0.9f), paddedTopPanel.RectTransform, Anchor.CenterLeft),
TextManager.Get("unspecifiedsubfilename"), font: GUI.LargeFont, textAlignment: Alignment.CenterLeft);
linkedSubBox = new GUIDropDown(new RectTransform(new Vector2(0.15f, 0.9f), paddedTopPanel.RectTransform),
TextManager.Get("AddSubButton"), elementCount: 20)
{
ToolTip = TextManager.Get("AddSubToolTip")
};
foreach (SubmarineInfo sub in SubmarineInfo.SavedSubmarines)
{
linkedSubBox.AddItem(sub.Name, sub);
}
linkedSubBox.OnSelected += SelectLinkedSub;
linkedSubBox.OnDropped += (component, obj) =>
{
MapEntity.SelectedList.Clear();
return true;
};
var spacing = new GUIFrame(new RectTransform(new Vector2(0.02f, 1.0f), paddedTopPanel.RectTransform), style: null);
new GUIFrame(new RectTransform(new Vector2(0.1f, 0.9f), spacing.RectTransform, Anchor.Center), style: "VerticalLine");
defaultModeTickBox = new GUITickBox(new RectTransform(new Vector2(0.9f, 0.9f), paddedTopPanel.RectTransform, scaleBasis: ScaleBasis.BothHeight), "", style: "EditSubButton")
{
ToolTip = TextManager.Get("SubEditorEditingMode"),
OnSelected = (GUITickBox tBox) =>
{
if (!lockMode)
{
if (tBox.Selected) { SetMode(Mode.Default); }
return true;
}
else { return false; }
}
};
characterModeTickBox = new GUITickBox(new RectTransform(new Vector2(0.9f, 0.9f), paddedTopPanel.RectTransform, scaleBasis: ScaleBasis.BothHeight), "", style: "CharacterModeButton")
{
ToolTip = TextManager.Get("CharacterModeButton") + '\n' + TextManager.Get("CharacterModeToolTip"),
OnSelected = (GUITickBox tBox) =>
{
if (!lockMode)
{
SetMode(tBox.Selected ? Mode.Character : Mode.Default);
return true;
}
else { return false; }
}
};
wiringModeTickBox = new GUITickBox(new RectTransform(new Vector2(0.9f, 0.9f), paddedTopPanel.RectTransform, scaleBasis: ScaleBasis.BothHeight), "", style: "WiringModeButton")
{
ToolTip = TextManager.Get("WiringModeButton") + '\n' + TextManager.Get("WiringModeToolTip"),
OnSelected = (GUITickBox tBox) =>
{
if (!lockMode)
{
SetMode(tBox.Selected ? Mode.Wiring : Mode.Default);
return true;
}
else { return false; }
}
};
spacing = new GUIFrame(new RectTransform(new Vector2(0.02f, 1.0f), paddedTopPanel.RectTransform), style: null);
new GUIFrame(new RectTransform(new Vector2(0.1f, 0.9f), spacing.RectTransform, Anchor.Center), style: "VerticalLine");
new GUIButton(new RectTransform(new Vector2(0.9f, 0.9f), paddedTopPanel.RectTransform, scaleBasis: ScaleBasis.BothHeight), "", style: "GenerateWaypointsButton")
{
ToolTip = TextManager.Get("GenerateWaypointsButton") + '\n' + TextManager.Get("GenerateWaypointsToolTip"),
OnClicked = (btn, userdata) =>
{
if (WayPoint.WayPointList.Any())
{
var generateWaypointsVerification = new GUIMessageBox("", TextManager.Get("generatewaypointsverification"), new string[] { TextManager.Get("ok"), TextManager.Get("cancel") });
generateWaypointsVerification.Buttons[0].OnClicked = (btn, userdata) =>
{
if (GenerateWaypoints())
{
GUI.AddMessage(TextManager.Get("waypointsgeneratedsuccesfully"), GUI.Style.Green);
}
WayPoint.ShowWayPoints = true;
generateWaypointsVerification.Close();
return true;
};
generateWaypointsVerification.Buttons[1].OnClicked = generateWaypointsVerification.Close;
}
else
{
if (GenerateWaypoints())
{
GUI.AddMessage(TextManager.Get("waypointsgeneratedsuccesfully"), GUI.Style.Green);
}
WayPoint.ShowWayPoints = true;
}
return true; return true;
} }
}; };
@@ -325,8 +382,7 @@ namespace Barotrauma
showEntitiesPanel = new GUIFrame(new RectTransform(new Vector2(0.08f, 0.5f), GUI.Canvas) showEntitiesPanel = new GUIFrame(new RectTransform(new Vector2(0.08f, 0.5f), GUI.Canvas)
{ {
MinSize = new Point(170, 0), MinSize = new Point(170, 0)
AbsoluteOffset = new Point(visibilityButton.Rect.X, TopPanel.Rect.Height)
}) })
{ {
Visible = false Visible = false
@@ -613,6 +669,41 @@ namespace Barotrauma
screenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight); screenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
} }
private bool TestSubmarine(GUIButton button, object obj)
{
List<string> errorMsgs = new List<string>();
if (!Hull.hullList.Any())
{
errorMsgs.Add(TextManager.Get("NoHullsWarning"));
}
if (!WayPoint.WayPointList.Any(wp => wp.ShouldBeSaved && wp.SpawnType == SpawnType.Human))
{
errorMsgs.Add(TextManager.Get("NoHumanSpawnpointWarning"));
}
if (errorMsgs.Any())
{
new GUIMessageBox(TextManager.Get("Error"), string.Join("\n\n", errorMsgs), new Vector2(0.25f, 0.0f), new Point(400, 200));
return true;
}
backedUpSubInfo = new SubmarineInfo(Submarine.MainSub);
GameMain.GameScreen.Select();
GameSession gameSession = new GameSession(backedUpSubInfo, "", GameModePreset.List.Find(gm => gm.Identifier == "subtest"), null);
gameSession.StartRound(null, false);
return true;
}
public void ClearBackedUpSubInfo()
{
backedUpSubInfo = null;
}
private void UpdateEntityList() private void UpdateEntityList()
{ {
entityList.Content.ClearChildren(); entityList.Content.ClearChildren();
@@ -741,9 +832,20 @@ namespace Barotrauma
{ {
base.Select(); base.Select();
GameMain.LightManager.AmbientLight =
Level.Loaded?.GenerationParams?.AmbientLightColor ??
LevelGenerationParams.LevelParams?.FirstOrDefault()?.AmbientLightColor ??
new Color(20, 20, 20, 255);
UpdateEntityList(); UpdateEntityList();
string name = (Submarine.MainSub == null) ? TextManager.Get("unspecifiedsubfilename") : Submarine.MainSub.Name; if (backedUpSubInfo != null)
{
Submarine.Unload();
}
string name = (Submarine.MainSub == null) ? TextManager.Get("unspecifiedsubfilename") : Submarine.MainSub.Info.Name;
if (backedUpSubInfo != null) { name = backedUpSubInfo.Name; }
subNameLabel.Text = ToolBox.LimitString(name, subNameLabel.Font, subNameLabel.Rect.Width); subNameLabel.Text = ToolBox.LimitString(name, subNameLabel.Font, subNameLabel.Rect.Width);
foreach (MapEntityPrefab prefab in MapEntityPrefab.List) foreach (MapEntityPrefab prefab in MapEntityPrefab.List)
@@ -760,23 +862,26 @@ namespace Barotrauma
GUI.ForceMouseOn(null); GUI.ForceMouseOn(null);
SetMode(Mode.Default); SetMode(Mode.Default);
if (Submarine.MainSub != null) if (backedUpSubInfo != null)
{ {
Submarine.MainSub.SetPrevTransform(Submarine.MainSub.Position); Submarine.MainSub = new Submarine(backedUpSubInfo);
Submarine.MainSub.UpdateTransform(); backedUpSubInfo = null;
cam.Position = Submarine.MainSub.Position + Submarine.MainSub.HiddenSubPosition;
} }
else else if (Submarine.MainSub == null)
{ {
Submarine.MainSub = new Submarine(Path.Combine(Submarine.SavePath, TextManager.Get("UnspecifiedSubFileName") + ".sub"), "", false); var subInfo = new SubmarineInfo();
cam.Position = Submarine.MainSub.Position; Submarine.MainSub = new Submarine(subInfo);
} }
Submarine.MainSub.SetPrevTransform(Submarine.MainSub.Position);
Submarine.MainSub.UpdateTransform(interpolate: false);
cam.Position = Submarine.MainSub.Position + Submarine.MainSub.HiddenSubPosition;
GameMain.SoundManager.SetCategoryGainMultiplier("default", 0.0f, 0); GameMain.SoundManager.SetCategoryGainMultiplier("default", 0.0f, 0);
GameMain.SoundManager.SetCategoryGainMultiplier("waterambience", 0.0f, 0); GameMain.SoundManager.SetCategoryGainMultiplier("waterambience", 0.0f, 0);
linkedSubBox.ClearChildren(); linkedSubBox.ClearChildren();
foreach (Submarine sub in Submarine.SavedSubmarines) foreach (SubmarineInfo sub in SubmarineInfo.SavedSubmarines)
{ {
linkedSubBox.AddItem(sub.Name, sub); linkedSubBox.AddItem(sub.Name, sub);
} }
@@ -987,27 +1092,37 @@ namespace Barotrauma
nameBox.Flash(); nameBox.Flash();
return false; return false;
} }
var result = SaveSubToFile(nameBox.Text);
foreach (char illegalChar in Path.GetInvalidFileNameChars()) saveFrame = null;
return result;
}
private bool SaveSubToFile(string name)
{
if (string.IsNullOrWhiteSpace(name))
{ {
if (nameBox.Text.Contains(illegalChar)) GUI.AddMessage(TextManager.Get("SubNameMissingWarning"), GUI.Style.Red);
{ return false;
GUI.AddMessage(TextManager.GetWithVariable("SubNameIllegalCharsWarning", "[illegalchar]", illegalChar.ToString()), GUI.Style.Red);
nameBox.Flash();
return false;
}
} }
string savePath = nameBox.Text + ".sub"; foreach (var illegalChar in Path.GetInvalidFileNameChars())
string prevSavePath = null;
if (Submarine.MainSub != null)
{ {
prevSavePath = Submarine.MainSub.FilePath; if (!name.Contains(illegalChar)) continue;
savePath = Path.Combine(Path.GetDirectoryName(Submarine.MainSub.FilePath), savePath); GUI.AddMessage(TextManager.GetWithVariable("SubNameIllegalCharsWarning", "[illegalchar]", illegalChar.ToString()), GUI.Style.Red);
return false;
}
string savePath = name + ".sub";
string prevSavePath = null;
if (!string.IsNullOrEmpty(Submarine.MainSub?.Info.FilePath) &&
Submarine.MainSub.Info.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))
{
prevSavePath = Submarine.MainSub.Info.FilePath.CleanUpPath();
savePath = Path.Combine(Path.GetDirectoryName(Submarine.MainSub.Info.FilePath), savePath).CleanUpPath();
} }
else else
{ {
savePath = Path.Combine(Submarine.SavePath, savePath); savePath = Path.Combine(SubmarineInfo.SavePath, savePath);
} }
#if !DEBUG #if !DEBUG
@@ -1015,8 +1130,8 @@ namespace Barotrauma
if (vanilla != null) if (vanilla != null)
{ {
var vanillaSubs = vanilla.GetFilesOfType(ContentType.Submarine); var vanillaSubs = vanilla.GetFilesOfType(ContentType.Submarine);
string pathToCompare = savePath.Replace(@"\", @"/").ToLowerInvariant(); string pathToCompare = savePath.Replace(@"\", @"/");
if (vanillaSubs.Any(sub => sub.Replace(@"\", @"/").ToLowerInvariant() == pathToCompare)) if (vanillaSubs.Any(sub => sub.Replace(@"\", @"/").Equals(pathToCompare, StringComparison.OrdinalIgnoreCase)))
{ {
GUI.AddMessage(TextManager.Get("CannotEditVanillaSubs"), GUI.Style.Red, font: GUI.LargeFont); GUI.AddMessage(TextManager.Get("CannotEditVanillaSubs"), GUI.Style.Red, font: GUI.LargeFont);
return false; return false;
@@ -1024,38 +1139,35 @@ namespace Barotrauma
} }
#endif #endif
if (previewImage.Sprite?.Texture != null) if (previewImage?.Sprite?.Texture != null)
{ {
using (MemoryStream imgStream = new MemoryStream()) using (MemoryStream imgStream = new MemoryStream())
{ {
previewImage.Sprite.Texture.SaveAsPng(imgStream, previewImage.Sprite.Texture.Width, previewImage.Sprite.Texture.Height); previewImage.Sprite.Texture.SaveAsPng(imgStream, previewImage.Sprite.Texture.Width, previewImage.Sprite.Texture.Height);
Submarine.SaveCurrent(savePath, imgStream); Submarine.MainSub.SaveAs(savePath, imgStream);
} }
} }
else else
{ {
Submarine.SaveCurrent(savePath); Submarine.MainSub.SaveAs(savePath);
} }
Submarine.MainSub.CheckForErrors(); Submarine.MainSub?.CheckForErrors();
GUI.AddMessage(TextManager.GetWithVariable("SubSavedNotification", "[filepath]", Submarine.MainSub.FilePath), GUI.Style.Green); GUI.AddMessage(TextManager.GetWithVariable("SubSavedNotification", "[filepath]", savePath), GUI.Style.Green);
SubmarineInfo.RefreshSavedSub(savePath);
Submarine.RefreshSavedSub(savePath);
if (prevSavePath != null && prevSavePath != savePath) if (prevSavePath != null && prevSavePath != savePath)
{ {
Submarine.RefreshSavedSub(prevSavePath); SubmarineInfo.RefreshSavedSub(prevSavePath);
} }
linkedSubBox.ClearChildren(); linkedSubBox.ClearChildren();
foreach (Submarine sub in Submarine.SavedSubmarines) foreach (SubmarineInfo sub in SubmarineInfo.SavedSubmarines)
{ {
linkedSubBox.AddItem(sub.Name, sub); linkedSubBox.AddItem(sub.Name, sub);
} }
subNameLabel.Text = ToolBox.LimitString(Submarine.MainSub.Name, subNameLabel.Font, subNameLabel.Rect.Width); subNameLabel.Text = ToolBox.LimitString(Submarine.MainSub.Info.Name, subNameLabel.Font, subNameLabel.Rect.Width);
saveFrame = null;
return false; return false;
} }
@@ -1160,15 +1272,15 @@ namespace Barotrauma
crewSizeMin.OnValueChanged += (numberInput) => crewSizeMin.OnValueChanged += (numberInput) =>
{ {
crewSizeMax.IntValue = Math.Max(crewSizeMax.IntValue, numberInput.IntValue); crewSizeMax.IntValue = Math.Max(crewSizeMax.IntValue, numberInput.IntValue);
Submarine.MainSub.RecommendedCrewSizeMin = crewSizeMin.IntValue; Submarine.MainSub.Info.RecommendedCrewSizeMin = crewSizeMin.IntValue;
Submarine.MainSub.RecommendedCrewSizeMax = crewSizeMax.IntValue; Submarine.MainSub.Info.RecommendedCrewSizeMax = crewSizeMax.IntValue;
}; };
crewSizeMax.OnValueChanged += (numberInput) => crewSizeMax.OnValueChanged += (numberInput) =>
{ {
crewSizeMin.IntValue = Math.Min(crewSizeMin.IntValue, numberInput.IntValue); crewSizeMin.IntValue = Math.Min(crewSizeMin.IntValue, numberInput.IntValue);
Submarine.MainSub.RecommendedCrewSizeMin = crewSizeMin.IntValue; Submarine.MainSub.Info.RecommendedCrewSizeMin = crewSizeMin.IntValue;
Submarine.MainSub.RecommendedCrewSizeMax = crewSizeMax.IntValue; Submarine.MainSub.Info.RecommendedCrewSizeMax = crewSizeMax.IntValue;
}; };
var crewExpArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.04f), leftColumn.RectTransform), isHorizontal: true) var crewExpArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.04f), leftColumn.RectTransform), isHorizontal: true)
@@ -1191,7 +1303,7 @@ namespace Barotrauma
if (currentIndex < 0) currentIndex = crewExperienceLevels.Length - 1; if (currentIndex < 0) currentIndex = crewExperienceLevels.Length - 1;
experienceText.UserData = crewExperienceLevels[currentIndex]; experienceText.UserData = crewExperienceLevels[currentIndex];
experienceText.Text = TextManager.Get(crewExperienceLevels[currentIndex]); experienceText.Text = TextManager.Get(crewExperienceLevels[currentIndex]);
Submarine.MainSub.RecommendedCrewExperience = (string)experienceText.UserData; Submarine.MainSub.Info.RecommendedCrewExperience = (string)experienceText.UserData;
return true; return true;
}; };
@@ -1202,18 +1314,18 @@ namespace Barotrauma
if (currentIndex >= crewExperienceLevels.Length) currentIndex = 0; if (currentIndex >= crewExperienceLevels.Length) currentIndex = 0;
experienceText.UserData = crewExperienceLevels[currentIndex]; experienceText.UserData = crewExperienceLevels[currentIndex];
experienceText.Text = TextManager.Get(crewExperienceLevels[currentIndex]); experienceText.Text = TextManager.Get(crewExperienceLevels[currentIndex]);
Submarine.MainSub.RecommendedCrewExperience = (string)experienceText.UserData; Submarine.MainSub.Info.RecommendedCrewExperience = (string)experienceText.UserData;
return true; return true;
}; };
if (Submarine.MainSub != null) if (Submarine.MainSub != null)
{ {
int min = Submarine.MainSub.RecommendedCrewSizeMin; int min = Submarine.MainSub.Info.RecommendedCrewSizeMin;
int max = Submarine.MainSub.RecommendedCrewSizeMax; int max = Submarine.MainSub.Info.RecommendedCrewSizeMax;
crewSizeMin.IntValue = min; crewSizeMin.IntValue = min;
crewSizeMax.IntValue = max; crewSizeMax.IntValue = max;
experienceText.UserData = string.IsNullOrEmpty(Submarine.MainSub.RecommendedCrewExperience) ? experienceText.UserData = string.IsNullOrEmpty(Submarine.MainSub.Info.RecommendedCrewExperience) ?
crewExperienceLevels[0] : Submarine.MainSub.RecommendedCrewExperience; crewExperienceLevels[0] : Submarine.MainSub.Info.RecommendedCrewExperience;
experienceText.Text = TextManager.Get((string)experienceText.UserData); experienceText.Text = TextManager.Get((string)experienceText.UserData);
} }
@@ -1222,7 +1334,7 @@ namespace Barotrauma
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), rightColumn.RectTransform), TextManager.Get("SubPreviewImage"), font: GUI.SubHeadingFont); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), rightColumn.RectTransform), TextManager.Get("SubPreviewImage"), font: GUI.SubHeadingFont);
var previewImageHolder = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.5f), rightColumn.RectTransform), style: null) { Color = Color.Black, CanBeFocused = false }; var previewImageHolder = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.5f), rightColumn.RectTransform), style: null) { Color = Color.Black, CanBeFocused = false };
previewImage = new GUIImage(new RectTransform(Vector2.One, previewImageHolder.RectTransform), Submarine.MainSub?.PreviewImage, scaleToFit: true); previewImage = new GUIImage(new RectTransform(Vector2.One, previewImageHolder.RectTransform), Submarine.MainSub?.Info.PreviewImage, scaleToFit: true);
var previewImageButtonHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), isHorizontal: true) { Stretch = true, RelativeSpacing = 0.05f }; var previewImageButtonHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), isHorizontal: true) { Stretch = true, RelativeSpacing = 0.05f };
@@ -1236,7 +1348,7 @@ namespace Barotrauma
previewImage.Sprite = new Sprite(TextureLoader.FromStream(imgStream), null, null); previewImage.Sprite = new Sprite(TextureLoader.FromStream(imgStream), null, null);
if (Submarine.MainSub != null) if (Submarine.MainSub != null)
{ {
Submarine.MainSub.PreviewImage = previewImage.Sprite; Submarine.MainSub.Info.PreviewImage = previewImage.Sprite;
} }
} }
return true; return true;
@@ -1258,7 +1370,7 @@ namespace Barotrauma
previewImage.Sprite = new Sprite(file, sourceRectangle: null); previewImage.Sprite = new Sprite(file, sourceRectangle: null);
if (Submarine.MainSub != null) if (Submarine.MainSub != null)
{ {
Submarine.MainSub.PreviewImage = previewImage.Sprite; Submarine.MainSub.Info.PreviewImage = previewImage.Sprite;
} }
}; };
FileSelection.ClearFileTypeFilters(); FileSelection.ClearFileTypeFilters();
@@ -1288,7 +1400,7 @@ namespace Barotrauma
var tagTickBox = new GUITickBox(new RectTransform(new Vector2(0.2f, 0.2f), tagContainer.Content.RectTransform), var tagTickBox = new GUITickBox(new RectTransform(new Vector2(0.2f, 0.2f), tagContainer.Content.RectTransform),
tagStr, font: GUI.SmallFont) tagStr, font: GUI.SmallFont)
{ {
Selected = Submarine.MainSub == null ? false : Submarine.MainSub.HasTag(tag), Selected = Submarine.MainSub == null ? false : Submarine.MainSub.Info.HasTag(tag),
UserData = tag, UserData = tag,
OnSelected = (GUITickBox tickBox) => OnSelected = (GUITickBox tickBox) =>
@@ -1296,11 +1408,11 @@ namespace Barotrauma
if (Submarine.MainSub == null) return false; if (Submarine.MainSub == null) return false;
if (tickBox.Selected) if (tickBox.Selected)
{ {
Submarine.MainSub.AddTag((SubmarineTag)tickBox.UserData); Submarine.MainSub.Info.AddTag((SubmarineTag)tickBox.UserData);
} }
else else
{ {
Submarine.MainSub.RemoveTag((SubmarineTag)tickBox.UserData); Submarine.MainSub.Info.RemoveTag((SubmarineTag)tickBox.UserData);
} }
return true; return true;
} }
@@ -1313,7 +1425,7 @@ namespace Barotrauma
var contentPackList = new GUIListBox(new RectTransform(new Vector2(0.5f, 1.0f - contentPackagesLabel.RectTransform.RelativeSize.Y), var contentPackList = new GUIListBox(new RectTransform(new Vector2(0.5f, 1.0f - contentPackagesLabel.RectTransform.RelativeSize.Y),
horizontalArea.RectTransform, Anchor.BottomRight)); horizontalArea.RectTransform, Anchor.BottomRight));
List<string> contentPacks = Submarine.MainSub.RequiredContentPackages.ToList(); List<string> contentPacks = Submarine.MainSub.Info.RequiredContentPackages.ToList();
foreach (ContentPackage contentPack in ContentPackage.List) foreach (ContentPackage contentPack in ContentPackage.List)
{ {
//don't show content packages that only define submarine files //don't show content packages that only define submarine files
@@ -1326,18 +1438,18 @@ namespace Barotrauma
{ {
var cpTickBox = new GUITickBox(new RectTransform(new Vector2(0.2f, 0.2f), contentPackList.Content.RectTransform), contentPackageName, font: GUI.SmallFont) var cpTickBox = new GUITickBox(new RectTransform(new Vector2(0.2f, 0.2f), contentPackList.Content.RectTransform), contentPackageName, font: GUI.SmallFont)
{ {
Selected = Submarine.MainSub.RequiredContentPackages.Contains(contentPackageName), Selected = Submarine.MainSub.Info.RequiredContentPackages.Contains(contentPackageName),
UserData = contentPackageName UserData = contentPackageName
}; };
cpTickBox.OnSelected += (GUITickBox tickBox) => cpTickBox.OnSelected += (GUITickBox tickBox) =>
{ {
if (tickBox.Selected) if (tickBox.Selected)
{ {
Submarine.MainSub.RequiredContentPackages.Add((string)tickBox.UserData); Submarine.MainSub.Info.RequiredContentPackages.Add((string)tickBox.UserData);
} }
else else
{ {
Submarine.MainSub.RequiredContentPackages.Remove((string)tickBox.UserData); Submarine.MainSub.Info.RequiredContentPackages.Remove((string)tickBox.UserData);
} }
return true; return true;
}; };
@@ -1363,7 +1475,7 @@ namespace Barotrauma
}; };
paddedSaveFrame.Recalculate(); paddedSaveFrame.Recalculate();
leftColumn.Recalculate(); leftColumn.Recalculate();
descriptionBox.Text = Submarine.MainSub == null ? "" : Submarine.MainSub.Description; descriptionBox.Text = Submarine.MainSub == null ? "" : Submarine.MainSub.Info.Description;
submarineDescriptionCharacterCount.Text = descriptionBox.Text.Length + " / " + submarineDescriptionLimit; submarineDescriptionCharacterCount.Text = descriptionBox.Text.Length + " / " + submarineDescriptionLimit;
} }
@@ -1513,7 +1625,7 @@ namespace Barotrauma
#if DEBUG #if DEBUG
deleteBtn.Enabled = true; deleteBtn.Enabled = true;
#else #else
deleteBtn.Enabled = userData is Submarine sub && !sub.IsVanillaSubmarine(); deleteBtn.Enabled = userData is Submarine sub && !sub.Info.IsVanillaSubmarine();
#endif #endif
} }
return true; return true;
@@ -1524,7 +1636,7 @@ namespace Barotrauma
searchBox.OnDeselected += (sender, userdata) => { searchTitle.Visible = true; }; searchBox.OnDeselected += (sender, userdata) => { searchTitle.Visible = true; };
searchBox.OnTextChanged += (textBox, text) => { FilterSubs(subList, text); return true; }; searchBox.OnTextChanged += (textBox, text) => { FilterSubs(subList, text); return true; };
foreach (Submarine sub in Submarine.SavedSubmarines) foreach (SubmarineInfo sub in SubmarineInfo.SavedSubmarines)
{ {
GUITextBlock textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), subList.Content.RectTransform) { MinSize = new Point(0, 30) }, GUITextBlock textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), subList.Content.RectTransform) { MinSize = new Point(0, 30) },
ToolBox.LimitString(sub.Name, GUI.Font, subList.Rect.Width - 80)) ToolBox.LimitString(sub.Name, GUI.Font, subList.Rect.Width - 80))
@@ -1554,7 +1666,7 @@ namespace Barotrauma
{ {
if (subList.SelectedComponent != null) if (subList.SelectedComponent != null)
{ {
TryDeleteSub(subList.SelectedComponent.UserData as Submarine); TryDeleteSub(subList.SelectedComponent.UserData as SubmarineInfo);
} }
deleteButton.Enabled = false; deleteButton.Enabled = false;
return true; return true;
@@ -1586,7 +1698,7 @@ namespace Barotrauma
foreach (GUIComponent child in subList.Content.Children) foreach (GUIComponent child in subList.Content.Children)
{ {
if (!(child.UserData is Submarine sub)) { return; } if (!(child.UserData is Submarine sub)) { return; }
child.Visible = string.IsNullOrEmpty(filter) ? true : sub.Name.ToLower().Contains(filter.ToLower()); child.Visible = string.IsNullOrEmpty(filter) ? true : sub.Info.Name.ToLower().Contains(filter.ToLower());
} }
} }
@@ -1606,14 +1718,14 @@ namespace Barotrauma
} }
if (subList.SelectedComponent == null) { return false; } if (subList.SelectedComponent == null) { return false; }
if (!(subList.SelectedComponent.UserData is Submarine selectedSub)) { return false; } if (!(subList.SelectedComponent.UserData is SubmarineInfo selectedSubInfo)) { return false; }
selectedSub.Load(true); Submarine.Unload();
var selectedSub = new Submarine(selectedSubInfo);
Submarine.MainSub = selectedSub; Submarine.MainSub = selectedSub;
Submarine.MainSub.SetPrevTransform(Submarine.MainSub.Position); Submarine.MainSub.UpdateTransform(interpolate: false);
Submarine.MainSub.UpdateTransform();
string name = Submarine.MainSub.Name; string name = Submarine.MainSub.Info.Name;
subNameLabel.Text = ToolBox.LimitString(name, subNameLabel.Font, subNameLabel.Rect.Width); subNameLabel.Text = ToolBox.LimitString(name, subNameLabel.Font, subNameLabel.Rect.Width);
cam.Position = Submarine.MainSub.Position + Submarine.MainSub.HiddenSubPosition; cam.Position = Submarine.MainSub.Position + Submarine.MainSub.HiddenSubPosition;
@@ -1624,10 +1736,13 @@ namespace Barotrauma
foreach (Item item in Item.ItemList) foreach (Item item in Item.ItemList)
{ {
var lightComponent = item.GetComponent<LightComponent>(); var lightComponent = item.GetComponent<LightComponent>();
if (lightComponent != null) lightComponent.Light.Enabled = item.ParentInventory == null; if (lightComponent != null)
{
lightComponent.Light.Enabled = item.ParentInventory == null;
}
} }
if (selectedSub.GameVersion < new Version("0.8.9.0")) if (selectedSub.Info.GameVersion < new Version("0.8.9.0"))
{ {
var adjustLightsPrompt = new GUIMessageBox(TextManager.Get("Warning"), TextManager.Get("AdjustLightsPrompt"), var adjustLightsPrompt = new GUIMessageBox(TextManager.Get("Warning"), TextManager.Get("AdjustLightsPrompt"),
new string[] { TextManager.Get("Yes"), TextManager.Get("No") }); new string[] { TextManager.Get("Yes"), TextManager.Get("No") });
@@ -1649,7 +1764,7 @@ namespace Barotrauma
return true; return true;
} }
private void TryDeleteSub(Submarine sub) private void TryDeleteSub(SubmarineInfo sub)
{ {
if (sub == null) { return; } if (sub == null) { return; }
@@ -1674,9 +1789,9 @@ namespace Barotrauma
{ {
try try
{ {
sub.Remove(); sub.Dispose();
File.Delete(sub.FilePath); File.Delete(sub.FilePath);
Submarine.RefreshSavedSubs(); SubmarineInfo.RefreshSavedSubs();
CreateLoadScreen(); CreateLoadScreen();
} }
catch (Exception e) catch (Exception e)
@@ -1754,18 +1869,20 @@ namespace Barotrauma
return true; return true;
} }
public void SetMode(Mode mode) public void SetMode(Mode newMode)
{ {
if (mode == this.mode) { return; } if (newMode == mode) { return; }
this.mode = mode; mode = newMode;
defaultModeTickBox.Selected = mode == Mode.Default; lockMode = true;
defaultModeTickBox.Selected = newMode == Mode.Default;
defaultModeTickBox.CanBeFocused = !defaultModeTickBox.Selected; defaultModeTickBox.CanBeFocused = !defaultModeTickBox.Selected;
characterModeTickBox.Selected = mode == Mode.Character; characterModeTickBox.Selected = newMode == Mode.Character;
wiringModeTickBox.Selected = mode == Mode.Wiring; wiringModeTickBox.Selected = newMode == Mode.Wiring;
lockMode = false;
switch (mode)
switch (newMode)
{ {
case Mode.Character: case Mode.Character:
CreateDummyCharacter(); CreateDummyCharacter();
@@ -1790,6 +1907,7 @@ namespace Barotrauma
} }
MapEntity.DeselectAll(); MapEntity.DeselectAll();
MapEntity.FilteredSelectedList.Clear();
} }
private void RemoveDummyCharacter() private void RemoveDummyCharacter()
@@ -1954,7 +2072,7 @@ namespace Barotrauma
return false; return false;
} }
if (Submarine.MainSub != null) Submarine.MainSub.Name = text; if (Submarine.MainSub != null) Submarine.MainSub.Info.Name = text;
textBox.Deselect(); textBox.Deselect();
textBox.Text = text; textBox.Text = text;
@@ -1968,7 +2086,7 @@ namespace Barotrauma
{ {
if (Submarine.MainSub != null) if (Submarine.MainSub != null)
{ {
Submarine.MainSub.Description = text; Submarine.MainSub.Info.Description = text;
} }
else else
{ {
@@ -1994,6 +2112,7 @@ namespace Barotrauma
{ {
previouslyUsedPanel.Visible = false; previouslyUsedPanel.Visible = false;
showEntitiesPanel.Visible = true; showEntitiesPanel.Visible = true;
showEntitiesPanel.RectTransform.AbsoluteOffset = new Point(Math.Max(entityCountPanel.Rect.Right, saveAssemblyFrame.Rect.Right), TopPanel.Rect.Height);
matchingTickBox.Selected = true; matchingTickBox.Selected = true;
matchingTickBox.Flash(GUI.Style.Green); matchingTickBox.Flash(GUI.Style.Green);
} }
@@ -2004,12 +2123,10 @@ namespace Barotrauma
return false; return false;
} }
private bool GenerateWaypoints(GUIButton button, object obj) private bool GenerateWaypoints()
{ {
if (Submarine.MainSub == null) return false; if (Submarine.MainSub == null) { return false; }
return WayPoint.GenerateSubWaypoints(Submarine.MainSub);
WayPoint.GenerateSubWaypoints(Submarine.MainSub);
return true;
} }
private void AddPreviouslyUsed(MapEntityPrefab mapEntityPrefab) private void AddPreviouslyUsed(MapEntityPrefab mapEntityPrefab)
@@ -2406,12 +2523,53 @@ namespace Barotrauma
hullVolumeFrame.Visible = MapEntity.SelectedList.Any(s => s is Hull); hullVolumeFrame.Visible = MapEntity.SelectedList.Any(s => s is Hull);
saveAssemblyFrame.Visible = MapEntity.SelectedList.Count > 0; saveAssemblyFrame.Visible = MapEntity.SelectedList.Count > 0;
if (PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Tab)) if (GUI.KeyboardDispatcher.Subscriber == null)
{ {
entityFilterBox.Select(); // TODO adjust when the new inventory stuff rolls in
if (PlayerInput.KeyHit(Keys.Q) && mode == Mode.Default)
{
toggleEntityMenuButton.OnClicked?.Invoke(toggleEntityMenuButton, toggleEntityMenuButton.UserData);
}
if (PlayerInput.KeyHit(Keys.Tab))
{
entityFilterBox.Select();
}
if (PlayerInput.KeyDown(Keys.LeftControl))
{
// Save menu
if (PlayerInput.KeyHit(Keys.S))
{
if (PlayerInput.KeyDown(Keys.LeftShift))
{
// Save the sub without a menu
if (subNameLabel != null)
{
SaveSubToFile(subNameLabel.Text);
}
}
else
{
// Save menu
if (saveFrame == null)
{
CreateSaveScreen();
}
}
}
// 1-3 keys on the keyboard for switching modes
if (PlayerInput.KeyHit(Keys.D1)) { SetMode(Mode.Default); }
if (PlayerInput.KeyHit(Keys.D2)) { SetMode(Mode.Character); }
if (PlayerInput.KeyHit(Keys.D3)) { SetMode(Mode.Wiring); }
}
else
{
cam.MoveCamera((float) deltaTime, true);
}
} }
cam.MoveCamera((float)deltaTime, true);
if (PlayerInput.MidButtonHeld()) if (PlayerInput.MidButtonHeld())
{ {
Vector2 moveSpeed = PlayerInput.MouseSpeed * (float)deltaTime * 100.0f / cam.Zoom; Vector2 moveSpeed = PlayerInput.MouseSpeed * (float)deltaTime * 100.0f / cam.Zoom;
@@ -2647,11 +2805,30 @@ namespace Barotrauma
if (Submarine.MainSub != null) if (Submarine.MainSub != null)
{ {
Vector2 position = Submarine.MainSub.SubBody != null ? Submarine.MainSub.WorldPosition : Submarine.MainSub.HiddenSubPosition;
GUI.DrawIndicator( GUI.DrawIndicator(
spriteBatch, Submarine.MainSub.WorldPosition, cam, spriteBatch, position, cam,
cam.WorldView.Width, cam.WorldView.Width,
GUI.SubmarineIcon, Color.LightBlue * 0.5f); GUI.SubmarineIcon, Color.LightBlue * 0.5f);
} }
var notificationIcon = GUI.Style.GetComponentStyle("GUINotificationButton");
var tooltipStyle = GUI.Style.GetComponentStyle("GUIToolTip");
foreach (Gap gap in Gap.GapList)
{
if (gap.linkedTo.Count == 2 && gap.linkedTo[0] == gap.linkedTo[1])
{
Vector2 screenPos = Cam.WorldToScreen(gap.WorldPosition);
Rectangle rect = new Rectangle(screenPos.ToPoint() - new Point(20), new Point(40));
tooltipStyle.Sprites[GUIComponent.ComponentState.None][0].Draw(spriteBatch, rect, Color.White);
notificationIcon.Sprites[GUIComponent.ComponentState.None][0].Draw(spriteBatch, rect, GUI.Style.Orange);
if (Vector2.Distance(PlayerInput.MousePosition, screenPos) < 30 * Cam.Zoom)
{
GUIComponent.DrawToolTip(spriteBatch, TextManager.Get("gapinsidehullwarning"), new Rectangle(screenPos.ToPoint(), new Point(10)));
}
}
}
if ((CharacterMode || WiringMode) && dummyCharacter != null) if ((CharacterMode || WiringMode) && dummyCharacter != null)
{ {
@@ -569,15 +569,13 @@ namespace Barotrauma
Font = GUI.SmallFont, Font = GUI.SmallFont,
Text = value, Text = value,
OverflowClip = true, OverflowClip = true,
OnEnterPressed = (textBox, text) => };
propertyBox.OnDeselected += (textBox, keys) =>
{
if (property.TrySetValue(entity, textBox.Text))
{ {
if (property.TrySetValue(entity, text)) TrySendNetworkUpdate(entity, property);
{ textBox.Text = (string)property.GetValue(entity);
TrySendNetworkUpdate(entity, property);
textBox.Text = (string)property.GetValue(entity);
textBox.Deselect();
}
return true;
} }
}; };
if (translationTextTag != null) if (translationTextTag != null)
@@ -97,6 +97,32 @@ namespace Barotrauma.Sounds
if (position != null) if (position != null)
{ {
if (float.IsNaN(position.Value.X))
{
throw new Exception("Failed to set source's position: " + debugName + ", position.X is NaN");
}
if (float.IsNaN(position.Value.Y))
{
throw new Exception("Failed to set source's position: " + debugName + ", position.Y is NaN");
}
if (float.IsNaN(position.Value.Z))
{
throw new Exception("Failed to set source's position: " + debugName + ", position.Z is NaN");
}
if (float.IsInfinity(position.Value.X))
{
throw new Exception("Failed to set source's position: " + debugName + ", position.X is Infinity");
}
if (float.IsInfinity(position.Value.Y))
{
throw new Exception("Failed to set source's position: " + debugName + ", position.Y is Infinity");
}
if (float.IsInfinity(position.Value.Z))
{
throw new Exception("Failed to set source's position: " + debugName + ", position.Z is Infinity");
}
uint alSource = Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex); uint alSource = Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex);
Al.Sourcei(alSource, Al.SourceRelative, Al.False); Al.Sourcei(alSource, Al.SourceRelative, Al.False);
int alError = Al.GetError(); int alError = Al.GetError();
@@ -378,12 +404,12 @@ namespace Barotrauma.Sounds
uint alSource = Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex); uint alSource = Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex);
if (!Al.IsSource(alSource)) return false; if (!Al.IsSource(alSource)) return false;
Al.GetSourcei(alSource, Al.SourceState, out state); Al.GetSourcei(alSource, Al.SourceState, out state);
bool playing = state == Al.Playing;
int alError = Al.GetError(); int alError = Al.GetError();
if (alError != Al.NoError) if (alError != Al.NoError)
{ {
throw new Exception("Failed to determine playing state from source: " + debugName + ", " + Al.GetErrorString(alError)); throw new Exception("Failed to determine playing state from source: " + debugName + ", " + Al.GetErrorString(alError));
} }
bool playing = state == Al.Playing;
return playing; return playing;
} }
} }
@@ -615,7 +641,7 @@ namespace Barotrauma.Sounds
uint alSource = Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex); uint alSource = Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex);
int state; int state;
Al.GetSourcei(Sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex), Al.SourceState, out state); Al.GetSourcei(alSource, Al.SourceState, out state);
bool playing = state == Al.Playing; bool playing = state == Al.Playing;
int alError = Al.GetError(); int alError = Al.GetError();
if (alError != Al.NoError) if (alError != Al.NoError)
@@ -630,7 +656,7 @@ namespace Barotrauma.Sounds
{ {
throw new Exception("Failed to determine processed buffers from streamed source: " + debugName + ", " + Al.GetErrorString(alError)); throw new Exception("Failed to determine processed buffers from streamed source: " + debugName + ", " + Al.GetErrorString(alError));
} }
Al.SourceUnqueueBuffers(alSource, unqueuedBufferCount, unqueuedBuffers); Al.SourceUnqueueBuffers(alSource, unqueuedBufferCount, unqueuedBuffers);
alError = Al.GetError(); alError = Al.GetError();
if (alError != Al.NoError) if (alError != Al.NoError)
@@ -727,9 +753,20 @@ namespace Barotrauma.Sounds
streamAmplitude = streamBufferAmplitudes[queueStartIndex]; streamAmplitude = streamBufferAmplitudes[queueStartIndex];
Al.GetSourcei(alSource, Al.SourceState, out state); Al.GetSourcei(alSource, Al.SourceState, out state);
alError = Al.GetError();
if (alError != Al.NoError)
{
throw new Exception("Failed to retrieve stream source state: " + debugName + ", " + Al.GetErrorString(alError));
}
if (state != Al.Playing) if (state != Al.Playing)
{ {
Al.SourcePlay(alSource); Al.SourcePlay(alSource);
alError = Al.GetError();
if (alError != Al.NoError)
{
throw new Exception("Failed to start stream playback: " + debugName + ", " + Al.GetErrorString(alError));
}
} }
} }
@@ -738,6 +775,10 @@ namespace Barotrauma.Sounds
streamAmplitude = 0.0f; streamAmplitude = 0.0f;
} }
} }
catch (Exception e)
{
DebugConsole.ThrowError($"An exception was thrown when updating a sound stream ({debugName})", e);
}
finally finally
{ {
Monitor.Exit(mutex); Monitor.Exit(mutex);
@@ -123,7 +123,7 @@ namespace Barotrauma
string filePathB = b.GetAttributeString("file", "").CleanUpPath(); string filePathB = b.GetAttributeString("file", "").CleanUpPath();
float baseGainB = b.GetAttributeFloat("volume", 1.0f); float baseGainB = b.GetAttributeFloat("volume", 1.0f);
float rangeB = b.GetAttributeFloat("range", 1000.0f); float rangeB = b.GetAttributeFloat("range", 1000.0f);
return a.Name.ToString().ToLowerInvariant() == b.Name.ToString().ToLowerInvariant() && return a.Name.ToString().Equals(b.Name.ToString(), StringComparison.OrdinalIgnoreCase) &&
filePathA == filePathB && MathUtils.NearlyEqual(baseGainA, baseGainB) && filePathA == filePathB && MathUtils.NearlyEqual(baseGainA, baseGainB) &&
MathUtils.NearlyEqual(rangeA, rangeB); MathUtils.NearlyEqual(rangeA, rangeB);
} }
@@ -151,7 +151,7 @@ namespace Barotrauma
SoundCount = 1 + soundElements.Count(); SoundCount = 1 + soundElements.Count();
var startUpSoundElement = soundElements.Find(e => e.Name.ToString().ToLowerInvariant() == "startupsound"); var startUpSoundElement = soundElements.Find(e => e.Name.ToString().Equals("startupsound", StringComparison.OrdinalIgnoreCase));
if (startUpSoundElement != null) if (startUpSoundElement != null)
{ {
startUpSound = GameMain.SoundManager.LoadSound(startUpSoundElement, false); startUpSound = GameMain.SoundManager.LoadSound(startUpSoundElement, false);
@@ -182,7 +182,7 @@ namespace Barotrauma
musicClips.AddIfNotNull(newMusicClip); musicClips.AddIfNotNull(newMusicClip);
if (loadedSoundElements != null) if (loadedSoundElements != null)
{ {
if (newMusicClip.Type.ToLowerInvariant() == "menu") if (newMusicClip.Type.Equals("menu", StringComparison.OrdinalIgnoreCase))
{ {
targetMusic[0] = newMusicClip; targetMusic[0] = newMusicClip;
} }
@@ -742,22 +742,30 @@ namespace Barotrauma
Screen.Selected == GameMain.LevelEditorScreen || Screen.Selected == GameMain.LevelEditorScreen ||
Screen.Selected == GameMain.ParticleEditorScreen || Screen.Selected == GameMain.ParticleEditorScreen ||
Screen.Selected == GameMain.SpriteEditorScreen || Screen.Selected == GameMain.SpriteEditorScreen ||
Screen.Selected == GameMain.SubEditorScreen) Screen.Selected == GameMain.SubEditorScreen ||
(Screen.Selected == GameMain.GameScreen && GameMain.GameSession?.GameMode is SubTestMode))
{ {
return "editor"; return "editor";
} }
if (Screen.Selected != GameMain.GameScreen) { return "menu"; } if (Screen.Selected != GameMain.GameScreen) { return "menu"; }
if (Character.Controlled != null &&
Level.Loaded != null && Level.Loaded.Ruins != null && if (Character.Controlled != null)
Level.Loaded.Ruins.Any(r => r.Area.Contains(Character.Controlled.WorldPosition)))
{ {
return "ruins"; if (Level.Loaded != null && Level.Loaded.Ruins != null &&
Level.Loaded.Ruins.Any(r => r.Area.Contains(Character.Controlled.WorldPosition)))
{
return "ruins";
}
if (Character.Controlled.Submarine?.Info?.IsWreck ?? false)
{
return "wreck";
}
} }
Submarine targetSubmarine = Character.Controlled?.Submarine; Submarine targetSubmarine = Character.Controlled?.Submarine;
if ((targetSubmarine != null && targetSubmarine.AtDamageDepth) || if ((targetSubmarine != null && targetSubmarine.AtDamageDepth) ||
(GameMain.GameScreen != null && Screen.Selected == GameMain.GameScreen && GameMain.GameScreen.Cam.Position.Y < SubmarineBody.DamageDepth)) (GameMain.GameScreen != null && Screen.Selected == GameMain.GameScreen && GameMain.GameScreen.Cam.Position.Y < SubmarineBody.DamageDepth))
{ {
@@ -59,8 +59,10 @@ namespace Barotrauma.Sounds
get { return soundChannel?.CurrentAmplitude ?? 0.0f; } get { return soundChannel?.CurrentAmplitude ?? 0.0f; }
} }
public VoipSound(SoundManager owner, VoipQueue q) : base(owner, "voip", true, true) public VoipSound(string name, SoundManager owner, VoipQueue q) : base(owner, "voip", true, true)
{ {
Filename = $"VoIP ({name})";
VoipConfig.SetupEncoding(); VoipConfig.SetupEncoding();
ALFormat = Al.FormatMono16; ALFormat = Al.FormatMono16;
@@ -93,9 +95,28 @@ namespace Barotrauma.Sounds
public void ApplyFilters(short[] buffer, int readSamples) public void ApplyFilters(short[] buffer, int readSamples)
{ {
for (int i = 0; i < readSamples; i++)
{
float fVal = ShortToFloat(buffer[i]);
if (UseMuffleFilter)
{
foreach (var filter in muffleFilters)
{
fVal = filter.Process(fVal);
}
}
if (UseRadioFilter)
{
foreach (var filter in radioFilters)
{
fVal = filter.Process(fVal);
}
}
buffer[i] = FloatToShort(fVal);
}
if (UseMuffleFilter) if (UseMuffleFilter)
{ {
ApplyFilters(radioFilters, buffer, readSamples); ApplyFilters(muffleFilters, buffer, readSamples);
} }
if (UseRadioFilter) if (UseRadioFilter)
@@ -106,15 +127,6 @@ namespace Barotrauma.Sounds
private void ApplyFilters(IEnumerable<BiQuad> filters, short[] buffer, int readSamples) private void ApplyFilters(IEnumerable<BiQuad> filters, short[] buffer, int readSamples)
{ {
for (int i = 0; i < readSamples; i++)
{
float fVal = ShortToFloat(buffer[i]);
foreach (var filter in filters)
{
fVal = filter.Process(fVal);
}
buffer[i] = FloatToShort(fVal);
}
} }
public override SoundChannel Play(float gain, float range, Vector2 position, bool muffle = false) public override SoundChannel Play(float gain, float range, Vector2 position, bool muffle = false)
@@ -8,6 +8,13 @@ namespace Barotrauma
{ {
class DecorativeSprite : ISerializableEntity class DecorativeSprite : ISerializableEntity
{ {
public class State
{
public float RotationState;
public float OffsetState;
public bool IsActive = true;
}
public string Name => $"Decorative Sprite"; public string Name => $"Decorative Sprite";
public Dictionary<string, SerializableProperty> SerializableProperties { get; set; } public Dictionary<string, SerializableProperty> SerializableProperties { get; set; }
@@ -113,10 +120,10 @@ namespace Barotrauma
switch (OffsetAnim) switch (OffsetAnim)
{ {
case AnimationType.Sine: case AnimationType.Sine:
offsetState = offsetState % (MathHelper.TwoPi / OffsetAnimSpeed); offsetState %= (MathHelper.TwoPi / OffsetAnimSpeed);
return Offset * (float)Math.Sin(offsetState * OffsetAnimSpeed); return Offset * (float)Math.Sin(offsetState * OffsetAnimSpeed);
case AnimationType.Noise: case AnimationType.Noise:
offsetState = offsetState % (1.0f / (OffsetAnimSpeed * 0.1f)); offsetState %= (1.0f / (OffsetAnimSpeed * 0.1f));
float t = offsetState * 0.1f * OffsetAnimSpeed; float t = offsetState * 0.1f * OffsetAnimSpeed;
return new Vector2( return new Vector2(
@@ -146,6 +153,51 @@ namespace Barotrauma
} }
} }
public static void UpdateSpriteStates(Dictionary<int, List<DecorativeSprite>> spriteGroups, Dictionary<DecorativeSprite, State> animStates,
int entityID, float deltaTime, Func<PropertyConditional,bool> checkConditional)
{
foreach (int spriteGroup in spriteGroups.Keys)
{
for (int i = 0; i < spriteGroups.Count; i++)
{
var decorativeSprite = spriteGroups[spriteGroup][i];
if (decorativeSprite == null) { continue; }
if (spriteGroup > 0)
{
int activeSpriteIndex = entityID % spriteGroups[spriteGroup].Count;
if (i != activeSpriteIndex)
{
animStates[decorativeSprite].IsActive = false;
continue;
}
}
//check if the sprite is active (whether it should be drawn or not)
var spriteState = animStates[decorativeSprite];
spriteState.IsActive = true;
foreach (PropertyConditional conditional in decorativeSprite.IsActiveConditionals)
{
if (!checkConditional(conditional))
{
spriteState.IsActive = false;
break;
}
}
if (!spriteState.IsActive) { continue; }
//check if the sprite should be animated
bool animate = true;
foreach (PropertyConditional conditional in decorativeSprite.AnimationConditionals)
{
if (!checkConditional(conditional)) { animate = false; break; }
}
if (!animate) { continue; }
spriteState.OffsetState += deltaTime;
spriteState.RotationState += deltaTime;
}
}
}
public void Remove() public void Remove()
{ {
Sprite?.Remove(); Sprite?.Remove();

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