This commit is contained in:
Evil Factory
2021-02-10 17:00:49 -03:00
354 changed files with 12898 additions and 5029 deletions
@@ -47,6 +47,8 @@ namespace Barotrauma
set { maxZoom = MathHelper.Clamp(value, 1.0f, 10.0f); } set { maxZoom = MathHelper.Clamp(value, 1.0f, 10.0f); }
} }
public float FreeCamMoveSpeed = 1.0f;
private float zoom; private float zoom;
private float offsetAmount; private float offsetAmount;
@@ -197,10 +199,15 @@ namespace Barotrauma
private void CreateMatrices() private void CreateMatrices()
{ {
resolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight); SetResolution(new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight));
worldView = new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight); }
viewMatrix = Matrix.CreateTranslation(new Vector3(GameMain.GraphicsWidth / 2.0f, GameMain.GraphicsHeight / 2.0f, 0));
public void SetResolution(Point res)
{
resolution = res;
worldView = new Rectangle(0, 0, res.X, res.Y);
viewMatrix = Matrix.CreateTranslation(new Vector3(res.X / 2.0f, res.Y / 2.0f, 0));
globalZoomScale = (float)Math.Pow(new Vector2(GUI.UIWidth, resolution.Y).Length() / GUI.ReferenceResolution.Length(), 2); globalZoomScale = (float)Math.Pow(new Vector2(GUI.UIWidth, resolution.Y).Length() / GUI.ReferenceResolution.Length(), 2);
} }
@@ -265,17 +272,17 @@ namespace Barotrauma
{ {
if (GUI.KeyboardDispatcher.Subscriber == null) if (GUI.KeyboardDispatcher.Subscriber == null)
{ {
if (PlayerInput.KeyDown(Keys.LeftShift)) moveSpeed *= 2.0f; if (PlayerInput.KeyDown(Keys.LeftShift)) { moveSpeed *= 2.0f; }
if (PlayerInput.KeyDown(Keys.LeftControl)) moveSpeed *= 0.5f; if (PlayerInput.KeyDown(Keys.LeftControl)) { moveSpeed *= 0.5f; }
if (GameMain.Config.KeyBind(InputType.Left).IsDown()) moveInput.X -= 1.0f; if (GameMain.Config.KeyBind(InputType.Left).IsDown()) { moveInput.X -= 1.0f; }
if (GameMain.Config.KeyBind(InputType.Right).IsDown()) moveInput.X += 1.0f; if (GameMain.Config.KeyBind(InputType.Right).IsDown()) { moveInput.X += 1.0f; }
if (GameMain.Config.KeyBind(InputType.Down).IsDown()) moveInput.Y -= 1.0f; if (GameMain.Config.KeyBind(InputType.Down).IsDown()) { moveInput.Y -= 1.0f; }
if (GameMain.Config.KeyBind(InputType.Up).IsDown()) moveInput.Y += 1.0f; if (GameMain.Config.KeyBind(InputType.Up).IsDown()) { moveInput.Y += 1.0f; }
} }
velocity = Vector2.Lerp(velocity, moveInput, deltaTime * 10.0f); velocity = Vector2.Lerp(velocity, moveInput, deltaTime * 10.0f);
moveCam = velocity * moveSpeed * deltaTime * 60.0f; moveCam = velocity * moveSpeed * deltaTime * FreeCamMoveSpeed * 60.0f;
if (Screen.Selected == GameMain.GameScreen && FollowSub) if (Screen.Selected == GameMain.GameScreen && FollowSub)
{ {
@@ -291,14 +298,21 @@ namespace Barotrauma
{ {
Vector2 mouseInWorld = ScreenToWorld(PlayerInput.MousePosition); Vector2 mouseInWorld = ScreenToWorld(PlayerInput.MousePosition);
Vector2 diffViewCenter; Vector2 diffViewCenter;
diffViewCenter = ((mouseInWorld - Position) * Zoom); diffViewCenter = (mouseInWorld - Position) * Zoom;
targetZoom = MathHelper.Clamp( targetZoom = MathHelper.Clamp(
targetZoom + (PlayerInput.ScrollWheelSpeed / 1000.0f) * zoom, targetZoom + PlayerInput.ScrollWheelSpeed / 1000.0f * zoom,
GameMain.DebugDraw ? MinZoom * 0.1f : MinZoom, GameMain.DebugDraw ? MinZoom * 0.1f : MinZoom,
MaxZoom); MaxZoom);
Zoom = MathHelper.Lerp(Zoom, targetZoom, deltaTime * 10.0f); if (PlayerInput.KeyDown(Keys.LeftControl))
if (!PlayerInput.KeyDown(Keys.F)) Position = mouseInWorld - (diffViewCenter / Zoom); {
Zoom += (targetZoom - zoom) / (ZoomSmoothness * 10.0f);
}
else
{
Zoom = MathHelper.Lerp(Zoom, targetZoom, deltaTime * 10.0f);
}
if (!PlayerInput.KeyDown(Keys.F)) { Position = mouseInWorld - (diffViewCenter / Zoom); }
} }
} }
else if (allowMove) else if (allowMove)
@@ -31,57 +31,54 @@ namespace Barotrauma
GUI.DrawString(spriteBatch, pos + textOffset, Character.Name, Color.White, Color.Black); GUI.DrawString(spriteBatch, pos + textOffset, Character.Name, Color.White, Color.Black);
if (ObjectiveManager != null) var currentOrder = ObjectiveManager.CurrentOrder;
if (currentOrder != null)
{ {
var currentOrder = ObjectiveManager.CurrentOrder; GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 20), $"ORDER: {currentOrder.DebugTag} ({currentOrder.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
if (currentOrder != null) }
else if (ObjectiveManager.WaitTimer > 0)
{
GUI.DrawString(spriteBatch, pos + new Vector2(0, 20), $"Waiting... {ObjectiveManager.WaitTimer.FormatZeroDecimal()}", Color.White, Color.Black);
}
var currentObjective = ObjectiveManager.CurrentObjective;
if (currentObjective != null)
{
int offset = currentOrder != null ? 20 : 0;
if (currentOrder == null || currentOrder.Priority <= 0)
{ {
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 20), $"ORDER: {currentOrder.DebugTag} ({currentOrder.Priority.FormatZeroDecimal()})", Color.White, Color.Black); GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 20 + offset), $"MAIN OBJECTIVE: {currentObjective.DebugTag} ({currentObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
} }
else if (ObjectiveManager.WaitTimer > 0) var subObjective = currentObjective.CurrentSubObjective;
if (subObjective != null)
{ {
GUI.DrawString(spriteBatch, pos + new Vector2(0, 20), $"Waiting... {ObjectiveManager.WaitTimer.FormatZeroDecimal()}", Color.White, Color.Black); GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 40 + offset), $"SUBOBJECTIVE: {subObjective.DebugTag} ({subObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
} }
var currentObjective = ObjectiveManager.CurrentObjective; var activeObjective = ObjectiveManager.GetActiveObjective();
if (currentObjective != null) if (activeObjective != null)
{ {
int offset = currentOrder != null ? 20 : 0; GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 60 + offset), $"ACTIVE OBJECTIVE: {activeObjective.DebugTag} ({activeObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
if (currentOrder == null || currentOrder.Priority <= 0)
{
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 20 + offset), $"MAIN OBJECTIVE: {currentObjective.DebugTag} ({currentObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
}
var subObjective = currentObjective.CurrentSubObjective;
if (subObjective != null)
{
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 40 + offset), $"SUBOBJECTIVE: {subObjective.DebugTag} ({subObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
}
var activeObjective = ObjectiveManager.GetActiveObjective();
if (activeObjective != null)
{
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 60 + offset), $"ACTIVE OBJECTIVE: {activeObjective.DebugTag} ({activeObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
}
} }
for (int i = 0; i < ObjectiveManager.Objectives.Count; i++) }
for (int i = 0; i < ObjectiveManager.Objectives.Count; i++)
{
var objective = ObjectiveManager.Objectives[i];
int offsetMultiplier;
if (ObjectiveManager.CurrentOrder == null)
{ {
var objective = ObjectiveManager.Objectives[i]; if (i == 0)
int offsetMultiplier;
if (ObjectiveManager.CurrentOrder == null)
{ {
if (i == 0) continue;
{
continue;
}
else
{
offsetMultiplier = i - 1;
}
} }
else else
{ {
offsetMultiplier = i + 1; offsetMultiplier = i - 1;
} }
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(120, offsetMultiplier * 18 + 100), $"{objective.DebugTag} ({objective.Priority.FormatZeroDecimal()})", Color.White, Color.Black * 0.5f);
} }
else
{
offsetMultiplier = i + 1;
}
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(120, offsetMultiplier * 18 + 100), $"{objective.DebugTag} ({objective.Priority.FormatZeroDecimal()})", Color.White, Color.Black * 0.5f);
} }
if (steeringManager is IndoorsSteeringManager pathSteering) if (steeringManager is IndoorsSteeringManager pathSteering)
@@ -116,6 +113,14 @@ namespace Barotrauma
GUI.DrawLine(spriteBatch, pos, pos + ConvertUnits.ToDisplayUnits(new Vector2(Character.AnimController.TargetMovement.X, -Character.AnimController.TargetMovement.Y)), Color.SteelBlue, width: 2); GUI.DrawLine(spriteBatch, pos, pos + ConvertUnits.ToDisplayUnits(new Vector2(Character.AnimController.TargetMovement.X, -Character.AnimController.TargetMovement.Y)), Color.SteelBlue, width: 2);
GUI.DrawLine(spriteBatch, pos, pos + ConvertUnits.ToDisplayUnits(new Vector2(Steering.X, -Steering.Y)), Color.Blue, width: 3); GUI.DrawLine(spriteBatch, pos, pos + ConvertUnits.ToDisplayUnits(new Vector2(Steering.X, -Steering.Y)), Color.Blue, width: 3);
if (Character.AnimController.InWater && objectiveManager.GetActiveObjective() is AIObjectiveGoTo gotoObjective && gotoObjective.TargetGap != null)
{
Vector2 gapPosition = gotoObjective.TargetGap.WorldPosition;
gapPosition.Y = -gapPosition.Y;
GUI.DrawRectangle(spriteBatch, gapPosition - new Vector2(10.0f, 10.0f), new Vector2(20.0f, 20.0f), Color.Orange, false);
GUI.DrawLine(spriteBatch, pos, gapPosition, Color.Orange * 0.5f, 0, 5);
}
//if (Character.IsKeyDown(InputType.Aim)) //if (Character.IsKeyDown(InputType.Aim))
//{ //{
// GUI.DrawLine(spriteBatch, pos, new Vector2(Character.CursorWorldPosition.X, -Character.CursorWorldPosition.Y), Color.Yellow, width: 4); // GUI.DrawLine(spriteBatch, pos, new Vector2(Character.CursorWorldPosition.X, -Character.CursorWorldPosition.Y), Color.Yellow, width: 4);
@@ -481,13 +481,13 @@ namespace Barotrauma
var controller = character.SelectedConstruction?.GetComponent<Controller>(); var controller = character.SelectedConstruction?.GetComponent<Controller>();
if (controller != null && controller.ControlCharacterPose && controller.User == character) if (controller != null && controller.ControlCharacterPose && controller.User == character)
{ {
if (controller.Item.SpriteDepth > maxDepth) if (controller.Item.SpriteDepth <= maxDepth || controller.DrawUserBehind)
{ {
depthOffset = Math.Max(controller.Item.SpriteDepth - 0.0001f - maxDepth, 0.0f); depthOffset = Math.Max(controller.Item.GetDrawDepth() + 0.0001f - minDepth, -minDepth);
} }
else else
{ {
depthOffset = Math.Max(controller.Item.SpriteDepth + 0.0001f - minDepth, -minDepth); depthOffset = Math.Max(controller.Item.GetDrawDepth() - 0.0001f - maxDepth, 0.0f);
} }
} }
} }
@@ -46,7 +46,7 @@ namespace Barotrauma
if (sound != null) if (sound != null)
{ {
SoundPlayer.PlaySound(sound.Sound, worldPosition, sound.Volume, sound.Range); SoundPlayer.PlaySound(sound.Sound, worldPosition, sound.Volume, sound.Range, ignoreMuffling: sound.IgnoreMuffling);
} }
} }
} }
@@ -457,7 +457,7 @@ namespace Barotrauma
if (draggingItemToWorld) if (draggingItemToWorld)
{ {
if (item.OwnInventory == null || if (item.OwnInventory == null ||
!item.OwnInventory.CanBePut(CharacterInventory.draggingItem) || !item.OwnInventory.CanBePut(CharacterInventory.DraggingItems.First()) ||
!CanAccessInventory(item.OwnInventory)) !CanAccessInventory(item.OwnInventory))
{ {
continue; continue;
@@ -561,7 +561,7 @@ namespace Barotrauma
{ {
if (InvisibleTimer > 0.0f) if (InvisibleTimer > 0.0f)
{ {
if (Controlled == null || (Controlled.CharacterHealth.GetAffliction("psychosis")?.Strength ?? 0.0f) <= 0.0f) if (Controlled == null || Controlled == this || (Controlled.CharacterHealth.GetAffliction("psychosis")?.Strength ?? 0.0f) <= 0.0f)
{ {
InvisibleTimer = 0.0f; InvisibleTimer = 0.0f;
} }
@@ -579,15 +579,15 @@ namespace Barotrauma
{ {
soundTimer -= deltaTime; soundTimer -= deltaTime;
} }
else if (AIController != null) else if (AIController is EnemyAIController enemyAI)
{ {
switch (AIController.State) switch (enemyAI.State)
{ {
case AIState.Attack: case AIState.Attack:
PlaySound(CharacterSound.SoundType.Attack); PlaySound(CharacterSound.SoundType.Attack);
break; break;
default: default:
var petBehavior = (AIController as EnemyAIController)?.PetBehavior; var petBehavior = enemyAI.PetBehavior;
if (petBehavior != null && petBehavior.Happiness < petBehavior.MaxHappiness * 0.25f) if (petBehavior != null && petBehavior.Happiness < petBehavior.MaxHappiness * 0.25f)
{ {
PlaySound(CharacterSound.SoundType.Unhappy); PlaySound(CharacterSound.SoundType.Unhappy);
@@ -741,7 +741,7 @@ namespace Barotrauma
if (speechBubbleTimer > 0.0f) if (speechBubbleTimer > 0.0f)
{ {
GUI.SpeechBubbleIcon.Draw(spriteBatch, pos - Vector2.UnitY * 30, GUI.SpeechBubbleIcon.Draw(spriteBatch, pos - Vector2.UnitY * 5,
speechBubbleColor * Math.Min(speechBubbleTimer, 1.0f), 0.0f, speechBubbleColor * Math.Min(speechBubbleTimer, 1.0f), 0.0f,
Math.Min(speechBubbleTimer, 1.0f)); Math.Min(speechBubbleTimer, 1.0f));
} }
@@ -803,7 +803,7 @@ namespace Barotrauma
Color nameColor = Color.White; Color nameColor = Color.White;
if (Controlled != null && TeamID != Controlled.TeamID) if (Controlled != null && TeamID != Controlled.TeamID)
{ {
nameColor = TeamID == TeamType.FriendlyNPC ? Color.SkyBlue : GUI.Style.Red; nameColor = TeamID == CharacterTeamType.FriendlyNPC ? Color.SkyBlue : GUI.Style.Red;
} }
if (CampaignInteractionType != CampaignMode.InteractionType.None && AllowCustomInteract) if (CampaignInteractionType != CampaignMode.InteractionType.None && AllowCustomInteract)
{ {
@@ -902,7 +902,7 @@ namespace Barotrauma
} }
var selectedSound = matchingSounds.GetRandom(); var selectedSound = matchingSounds.GetRandom();
if (selectedSound?.Sound == null) { return; } if (selectedSound?.Sound == null) { return; }
soundChannel = SoundPlayer.PlaySound(selectedSound.Sound, AnimController.WorldPosition, selectedSound.Volume, selectedSound.Range, hullGuess: CurrentHull); soundChannel = SoundPlayer.PlaySound(selectedSound.Sound, AnimController.WorldPosition, selectedSound.Volume, selectedSound.Range, hullGuess: CurrentHull, ignoreMuffling: selectedSound.IgnoreMuffling);
soundTimer = soundInterval; soundTimer = soundInterval;
} }
@@ -57,7 +57,7 @@ namespace Barotrauma
!ConversationAction.FadeScreenToBlack; !ConversationAction.FadeScreenToBlack;
} }
private static string GetCachedHudText(string textTag, string keyBind) public static string GetCachedHudText(string textTag, string keyBind)
{ {
if (cachedHudTexts.TryGetValue(textTag + keyBind, out string text)) if (cachedHudTexts.TryGetValue(textTag + keyBind, out string text))
{ {
@@ -76,10 +76,10 @@ namespace Barotrauma
{ {
if (character.Inventory != null) if (character.Inventory != null)
{ {
for (int i = 0; i < character.Inventory.Items.Length - 1; i++) for (int i = 0; i < character.Inventory.Capacity; i++)
{ {
var item = character.Inventory.Items[i]; var item = character.Inventory.GetItemAt(i);
if (item == null || character.Inventory.SlotTypes[i] == InvSlotType.Any) continue; if (item == null || character.Inventory.SlotTypes[i] == InvSlotType.Any) { continue; }
foreach (ItemComponent ic in item.Components) foreach (ItemComponent ic in item.Components)
{ {
@@ -131,10 +131,10 @@ namespace Barotrauma
character.Inventory.ClearSubInventories(); character.Inventory.ClearSubInventories();
} }
for (int i = 0; i < character.Inventory.Items.Length - 1; i++) for (int i = 0; i < character.Inventory.Capacity; i++)
{ {
var item = character.Inventory.Items[i]; var item = character.Inventory.GetItemAt(i);
if (item == null || character.Inventory.SlotTypes[i] == InvSlotType.Any) continue; if (item == null || character.Inventory.SlotTypes[i] == InvSlotType.Any) { continue; }
foreach (ItemComponent ic in item.Components) foreach (ItemComponent ic in item.Components)
{ {
@@ -206,22 +206,31 @@ namespace Barotrauma
orderIndicatorCount.Clear(); orderIndicatorCount.Clear();
foreach (Pair<Order, float?> activeOrder in GameMain.GameSession.CrewManager.ActiveOrders) foreach (Pair<Order, float?> activeOrder in GameMain.GameSession.CrewManager.ActiveOrders)
{ {
if (!DrawIcon(activeOrder.First)) { continue; }
if (activeOrder.Second.HasValue) if (activeOrder.Second.HasValue)
{ {
DrawOrderIndicator(spriteBatch, cam, character, activeOrder.First, iconAlpha: MathHelper.Clamp(activeOrder.Second.Value / 10.0f, 0.2f, 1.0f)); DrawOrderIndicator(spriteBatch, cam, character, activeOrder.First, iconAlpha: MathHelper.Clamp(activeOrder.Second.Value / 10.0f, 0.2f, 1.0f));
} }
else else
{ {
float iconAlpha = GetDistanceBasedIconAlpha(activeOrder.First.TargetSpatialEntity, maxDistance: 350.0f); float iconAlpha = GetDistanceBasedIconAlpha(activeOrder.First.TargetSpatialEntity, maxDistance: 450.0f);
if (iconAlpha <= 0.0f) { continue; } if (iconAlpha <= 0.0f) { continue; }
DrawOrderIndicator(spriteBatch, cam, character, activeOrder.First, iconAlpha: iconAlpha, createOffset: false, scaleMultiplier: 0.5f); DrawOrderIndicator(spriteBatch, cam, character, activeOrder.First,
iconAlpha: iconAlpha, createOffset: false, scaleMultiplier: 0.5f, overrideAlpha: true);
} }
} }
if (character.CurrentOrder != null) if (DrawIcon(character.CurrentOrder))
{ {
DrawOrderIndicator(spriteBatch, cam, character, character.CurrentOrder, 1.0f); DrawOrderIndicator(spriteBatch, cam, character, character.CurrentOrder, 1.0f);
} }
static bool DrawIcon(Order o) =>
o != null &&
(!(o.TargetEntity is Item i) ||
o.DrawIconWhenContained ||
i.GetRootInventoryOwner() == i);
} }
foreach (Character.ObjectiveEntity objectiveEntity in character.ActiveObjectiveEntities) foreach (Character.ObjectiveEntity objectiveEntity in character.ActiveObjectiveEntities)
@@ -231,7 +240,7 @@ namespace Barotrauma
foreach (Item brokenItem in brokenItems) foreach (Item brokenItem in brokenItems)
{ {
if (brokenItem.NonInteractable) { continue; } if (!brokenItem.IsInteractable(character)) { continue; }
float alpha = GetDistanceBasedIconAlpha(brokenItem); float alpha = GetDistanceBasedIconAlpha(brokenItem);
if (alpha <= 0.0f) continue; if (alpha <= 0.0f) continue;
GUI.DrawIndicator(spriteBatch, brokenItem.DrawPosition, cam, 100.0f, GUI.BrokenIcon, GUI.DrawIndicator(spriteBatch, brokenItem.DrawPosition, cam, 100.0f, GUI.BrokenIcon,
@@ -244,7 +253,7 @@ namespace Barotrauma
return Math.Min((maxDistance - dist) / maxDistance * 2.0f, 1.0f); return Math.Min((maxDistance - dist) / maxDistance * 2.0f, 1.0f);
} }
if (!character.IsIncapacitated && character.Stun <= 0.0f && !IsCampaignInterfaceOpen && (!character.IsKeyDown(InputType.Aim) || character.SelectedItems.Any(it => it?.GetComponent<Sprayer>() == null))) if (!character.IsIncapacitated && character.Stun <= 0.0f && !IsCampaignInterfaceOpen && (!character.IsKeyDown(InputType.Aim) || character.HeldItems.Any(it => it?.GetComponent<Sprayer>() == null)))
{ {
if (character.FocusedCharacter != null && character.FocusedCharacter.CanBeSelected) if (character.FocusedCharacter != null && character.FocusedCharacter.CanBeSelected)
{ {
@@ -281,7 +290,7 @@ namespace Barotrauma
if (!GUI.DisableItemHighlights && !Inventory.DraggingItemToWorld) if (!GUI.DisableItemHighlights && !Inventory.DraggingItemToWorld)
{ {
bool shiftDown = PlayerInput.KeyDown(Keys.LeftShift) || PlayerInput.KeyDown(Keys.RightShift); bool shiftDown = PlayerInput.KeyDown(Keys.LeftShift) || PlayerInput.KeyDown(Keys.RightShift);
if(shouldRecreateHudTexts || heldDownShiftWhenGotHudTexts != shiftDown) if (shouldRecreateHudTexts || heldDownShiftWhenGotHudTexts != shiftDown)
{ {
shouldRecreateHudTexts = true; shouldRecreateHudTexts = true;
heldDownShiftWhenGotHudTexts = shiftDown; heldDownShiftWhenGotHudTexts = shiftDown;
@@ -291,8 +300,8 @@ namespace Barotrauma
int dir = Math.Sign(focusedItem.WorldPosition.X - character.WorldPosition.X); int dir = Math.Sign(focusedItem.WorldPosition.X - character.WorldPosition.X);
Vector2 textSize = GUI.Font.MeasureString(focusedItem.Name); Vector2 textSize = GUI.Font.MeasureString(hudTexts.First().Text);
Vector2 largeTextSize = GUI.SubHeadingFont.MeasureString(focusedItem.Name); Vector2 largeTextSize = GUI.SubHeadingFont.MeasureString(hudTexts.First().Text);
Vector2 startPos = cam.WorldToScreen(focusedItem.DrawPosition); Vector2 startPos = cam.WorldToScreen(focusedItem.DrawPosition);
startPos.Y -= (hudTexts.Count + 1) * textSize.Y; startPos.Y -= (hudTexts.Count + 1) * textSize.Y;
@@ -307,11 +316,11 @@ namespace Barotrauma
float alpha = MathHelper.Clamp((focusedItemOverlayTimer - ItemOverlayDelay) * 2.0f, 0.0f, 1.0f); float alpha = MathHelper.Clamp((focusedItemOverlayTimer - ItemOverlayDelay) * 2.0f, 0.0f, 1.0f);
GUI.DrawString(spriteBatch, textPos, focusedItem.Name, GUI.Style.TextColor * alpha, Color.Black * alpha * 0.7f, 2, font: GUI.SubHeadingFont); GUI.DrawString(spriteBatch, textPos, hudTexts.First().Text, hudTexts.First().Color * alpha, Color.Black * alpha * 0.7f, 2, font: GUI.SubHeadingFont);
startPos.X += dir * 10.0f * GUI.Scale; startPos.X += dir * 10.0f * GUI.Scale;
textPos.X += dir * 10.0f * GUI.Scale; textPos.X += dir * 10.0f * GUI.Scale;
textPos.Y += largeTextSize.Y; textPos.Y += largeTextSize.Y;
foreach (ColoredText coloredText in hudTexts) foreach (ColoredText coloredText in hudTexts.Skip(1))
{ {
if (dir == -1) textPos.X = (int)(startPos.X - GUI.SmallFont.MeasureString(coloredText.Text).X); if (dir == -1) textPos.X = (int)(startPos.X - GUI.SmallFont.MeasureString(coloredText.Text).X);
GUI.DrawString(spriteBatch, textPos, coloredText.Text, coloredText.Color * alpha, Color.Black * alpha * 0.7f, 2, GUI.SmallFont); GUI.DrawString(spriteBatch, textPos, coloredText.Text, coloredText.Color * alpha, Color.Black * alpha * 0.7f, 2, GUI.SmallFont);
@@ -341,9 +350,8 @@ namespace Barotrauma
} }
if (Character.Controlled.Inventory != null) if (Character.Controlled.Inventory != null)
{ {
foreach (Item item in Character.Controlled.Inventory.Items) foreach (Item item in Character.Controlled.Inventory.AllItems)
{ {
if (item == null) { continue; }
if (Character.Controlled.HasEquippedItem(item)) if (Character.Controlled.HasEquippedItem(item))
{ {
item.DrawHUD(spriteBatch, cam, Character.Controlled); item.DrawHUD(spriteBatch, cam, Character.Controlled);
@@ -355,10 +363,10 @@ namespace Barotrauma
if (character.Inventory != null) if (character.Inventory != null)
{ {
for (int i = 0; i < character.Inventory.Items.Length - 1; i++) for (int i = 0; i < character.Inventory.Capacity; i++)
{ {
var item = character.Inventory.Items[i]; var item = character.Inventory.GetItemAt(i);
if (item == null || character.Inventory.SlotTypes[i] == InvSlotType.Any) continue; if (item == null || character.Inventory.SlotTypes[i] == InvSlotType.Any) { continue; }
foreach (ItemComponent ic in item.Components) foreach (ItemComponent ic in item.Components)
{ {
@@ -432,12 +440,16 @@ namespace Barotrauma
private static void DrawCharacterHoverTexts(SpriteBatch spriteBatch, Camera cam, Character character) private static void DrawCharacterHoverTexts(SpriteBatch spriteBatch, Camera cam, Character character)
{ {
foreach (Item item in character.Inventory.Items) var allItems = character.Inventory?.AllItems;
if (allItems != null)
{ {
var statusHUD = item?.GetComponent<StatusHUD>(); foreach (Item item in allItems)
if (statusHUD != null && statusHUD.IsActive && statusHUD.VisibleCharacters.Contains(character.FocusedCharacter))
{ {
return; var statusHUD = item?.GetComponent<StatusHUD>();
if (statusHUD != null && statusHUD.IsActive && statusHUD.VisibleCharacters.Contains(character.FocusedCharacter))
{
return;
}
} }
} }
@@ -454,7 +466,7 @@ namespace Barotrauma
Color nameColor = GUI.Style.TextColor; Color nameColor = GUI.Style.TextColor;
if (character.TeamID != character.FocusedCharacter.TeamID) if (character.TeamID != character.FocusedCharacter.TeamID)
{ {
nameColor = character.FocusedCharacter.TeamID == Character.TeamType.FriendlyNPC ? Color.SkyBlue : GUI.Style.Red; nameColor = character.FocusedCharacter.TeamID == CharacterTeamType.FriendlyNPC ? Color.SkyBlue : GUI.Style.Red;
} }
GUI.DrawString(spriteBatch, textPos, focusName, nameColor, Color.Black * 0.7f, 2, GUI.SubHeadingFont); GUI.DrawString(spriteBatch, textPos, focusName, nameColor, Color.Black * 0.7f, 2, GUI.SubHeadingFont);
@@ -493,7 +505,9 @@ namespace Barotrauma
return character.ShouldLockHud(); return character.ShouldLockHud();
} }
private static void DrawOrderIndicator(SpriteBatch spriteBatch, Camera cam, Character character, Order order, float iconAlpha = 1.0f, bool createOffset = true, float scaleMultiplier = 1.0f) /// <param name="overrideAlpha">Override the distance-based alpha value with the iconAlpha parameter value</param>
private static void DrawOrderIndicator(SpriteBatch spriteBatch, Camera cam, Character character, Order order,
float iconAlpha = 1.0f, bool createOffset = true, float scaleMultiplier = 1.0f, bool overrideAlpha = false)
{ {
if (order?.SymbolSprite == null) { return; } if (order?.SymbolSprite == null) { return; }
if (order.IsReport && order.OrderGiver != character && !order.HasAppropriateJob(character)) { return; } if (order.IsReport && order.OrderGiver != character && !order.HasAppropriateJob(character)) { return; }
@@ -514,7 +528,8 @@ namespace Barotrauma
Vector2 drawPos = target is Entity ? (target as Entity).DrawPosition : Vector2 drawPos = target is Entity ? (target as Entity).DrawPosition :
target.Submarine == null ? target.Position : target.Position + target.Submarine.DrawPosition; target.Submarine == null ? target.Position : target.Position + target.Submarine.DrawPosition;
drawPos += Vector2.UnitX * order.SymbolSprite.size.X * 1.5f * orderIndicatorCount[target]; drawPos += Vector2.UnitX * order.SymbolSprite.size.X * 1.5f * orderIndicatorCount[target];
GUI.DrawIndicator(spriteBatch, drawPos, cam, 100.0f, order.SymbolSprite, order.Color * iconAlpha, createOffset: createOffset, scaleMultiplier: scaleMultiplier); GUI.DrawIndicator(spriteBatch, drawPos, cam, 100.0f, order.SymbolSprite, order.Color * iconAlpha,
createOffset: createOffset, scaleMultiplier: scaleMultiplier, overrideAlpha: overrideAlpha ? (float?)iconAlpha : null);
orderIndicatorCount[target] = orderIndicatorCount[target] + 1; orderIndicatorCount[target] = orderIndicatorCount[target] + 1;
} }
@@ -152,34 +152,19 @@ namespace Barotrauma
partial void OnSkillChanged(string skillIdentifier, float prevLevel, float newLevel, Vector2 textPopupPos) partial void OnSkillChanged(string skillIdentifier, float prevLevel, float newLevel, Vector2 textPopupPos)
{ {
if (TeamID == Character.TeamType.FriendlyNPC) { return; } if (TeamID == CharacterTeamType.FriendlyNPC) { return; }
if (Character.Controlled != null && Character.Controlled.TeamID != TeamID) { return; } if (Character.Controlled != null && Character.Controlled.TeamID != TeamID) { return; }
if (newLevel - prevLevel > 0.1f)
{
GUI.AddMessage(
"+" + ((int)((newLevel - prevLevel) * 100.0f)).ToString() + " XP",
GUI.Style.Green,
textPopupPos,
Vector2.UnitY * 10.0f,
playSound: false);
}
else if (prevLevel % 0.1f > 0.05f && newLevel % 0.1f < 0.05f)
{
GUI.AddMessage(
"+10 XP",
GUI.Style.Green,
textPopupPos,
Vector2.UnitY * 10.0f,
playSound: false);
}
if ((int)newLevel > (int)prevLevel) if ((int)newLevel > (int)prevLevel)
{ {
int increase = Math.Max((int)newLevel - (int)prevLevel, 1);
GUI.AddMessage( GUI.AddMessage(
TextManager.GetWithVariables("SkillIncreased", new string[3] { "[name]", "[skillname]", "[newlevel]" }, string.Format("+{0} {1}", increase, TextManager.Get("SkillName." + skillIdentifier)),
new string[3] { Name, TextManager.Get("SkillName." + skillIdentifier), ((int)newLevel).ToString() }, GUI.Style.Green,
new bool[3] { false, true, false }), GUI.Style.Green); textPopupPos,
Vector2.UnitY * 10.0f,
playSound: false,
subId: Character?.Submarine?.ID ?? -1);
} }
} }
@@ -349,7 +349,7 @@ namespace Barotrauma
{ {
string skillIdentifier = msg.ReadString(); string skillIdentifier = msg.ReadString();
float skillLevel = msg.ReadSingle(); float skillLevel = msg.ReadSingle();
info?.SetSkillLevel(skillIdentifier, skillLevel, WorldPosition + Vector2.UnitY * 150.0f); info?.SetSkillLevel(skillIdentifier, skillLevel, Position + Vector2.UnitY * 150.0f);
} }
break; break;
case 4: //NetEntityEvent.Type.ExecuteAttack case 4: //NetEntityEvent.Type.ExecuteAttack
@@ -434,7 +434,7 @@ namespace Barotrauma
CharacterInfo info = CharacterInfo.ClientRead(infoSpeciesName, inc); CharacterInfo info = CharacterInfo.ClientRead(infoSpeciesName, inc);
character = Create(speciesName, position, seed, characterInfo: info, id: id, isRemotePlayer: ownerId > 0 && GameMain.Client.ID != ownerId, hasAi: hasAi); character = Create(speciesName, position, seed, characterInfo: info, id: id, isRemotePlayer: ownerId > 0 && GameMain.Client.ID != ownerId, hasAi: hasAi);
character.TeamID = (TeamType)teamID; character.TeamID = (CharacterTeamType)teamID;
character.CampaignInteractionType = (CampaignMode.InteractionType)inc.ReadByte(); character.CampaignInteractionType = (CampaignMode.InteractionType)inc.ReadByte();
if (character.CampaignInteractionType != CampaignMode.InteractionType.None) if (character.CampaignInteractionType != CampaignMode.InteractionType.None)
{ {
@@ -487,7 +487,7 @@ namespace Barotrauma
character.ReadStatus(inc); character.ReadStatus(inc);
} }
if (character.IsHuman && character.TeamID != TeamType.FriendlyNPC && !character.IsDead) if (character.IsHuman && character.TeamID != CharacterTeamType.FriendlyNPC && !character.IsDead)
{ {
CharacterInfo duplicateCharacterInfo = GameMain.GameSession.CrewManager.GetCharacterInfos().FirstOrDefault(c => c.ID == info.ID); CharacterInfo duplicateCharacterInfo = GameMain.GameSession.CrewManager.GetCharacterInfos().FirstOrDefault(c => c.ID == info.ID);
GameMain.GameSession.CrewManager.RemoveCharacterInfo(duplicateCharacterInfo); GameMain.GameSession.CrewManager.RemoveCharacterInfo(duplicateCharacterInfo);
@@ -18,6 +18,11 @@ namespace Barotrauma
public float Range => roundSound == null ? 0.0f : roundSound.Range; public float Range => roundSound == null ? 0.0f : roundSound.Range;
public Sound Sound => roundSound?.Sound; public Sound Sound => roundSound?.Sound;
public bool IgnoreMuffling
{
get { return roundSound?.IgnoreMuffling ?? false; }
}
public CharacterSound(CharacterParams.SoundParams soundParams) public CharacterSound(CharacterParams.SoundParams soundParams)
{ {
Params = soundParams; Params = soundParams;
@@ -72,7 +72,7 @@ namespace Barotrauma
FadeTimer = 1.0f; FadeTimer = 1.0f;
if (!string.IsNullOrEmpty(textTag)) if (!string.IsNullOrEmpty(textTag))
{ {
textTag = textTag; this.textTag = textTag;
Text = TextManager.Get(textTag); Text = TextManager.Get(textTag);
} }
} }
@@ -10,6 +10,7 @@ namespace Barotrauma
{ {
partial void UpdateMessages() partial void UpdateMessages()
{ {
if (Prefab is AfflictionPrefabHusk { SendMessages: false }) { return; }
switch (State) switch (State)
{ {
case InfectionState.Dormant: case InfectionState.Dormant:
@@ -912,7 +912,7 @@ namespace Barotrauma
lowSkillIndicator.Color = new Color(lowSkillIndicator.Color, MathHelper.Lerp(0.5f, 1.0f, (float)(Math.Sin(Timing.TotalTime * 5.0f) + 1.0f) / 2.0f)); lowSkillIndicator.Color = new Color(lowSkillIndicator.Color, MathHelper.Lerp(0.5f, 1.0f, (float)(Math.Sin(Timing.TotalTime * 5.0f) + 1.0f) / 2.0f));
if (Inventory.draggingItem != null) if (Inventory.DraggingItems.Any())
{ {
if (highlightedLimbIndex > -1) if (highlightedLimbIndex > -1)
{ {
@@ -1632,8 +1632,8 @@ namespace Barotrauma
} }
//can't apply treatment to dead characters //can't apply treatment to dead characters
if (Character.IsDead) return true; if (Character.IsDead) { return true; }
if (item == null || !item.UseInHealthInterface) return true; if (item == null || !item.UseInHealthInterface) { return true; }
if (!ignoreMousePos) if (!ignoreMousePos)
{ {
if (highlightedLimbIndex > -1) if (highlightedLimbIndex > -1)
@@ -1652,33 +1652,25 @@ namespace Barotrauma
private List<Item> GetAvailableMedicalItems() private List<Item> GetAvailableMedicalItems()
{ {
List<Item> allInventoryItems = new List<Item>(); List<Item> allInventoryItems = new List<Item>();
allInventoryItems.AddRange(Character.Inventory.Items); allInventoryItems.AddRange(Character.Inventory.AllItems);
if (Character.SelectedCharacter?.Inventory != null && Character.CanAccessInventory(Character.SelectedCharacter.Inventory)) if (Character.SelectedCharacter?.Inventory != null && Character.CanAccessInventory(Character.SelectedCharacter.Inventory))
{ {
allInventoryItems.AddRange(Character.SelectedCharacter.Inventory.Items); allInventoryItems.AddRange(Character.SelectedCharacter.Inventory.AllItems);
} }
if (Character.SelectedBy?.Inventory != null) if (Character.SelectedBy?.Inventory != null)
{ {
allInventoryItems.AddRange(Character.SelectedBy.Inventory.Items); allInventoryItems.AddRange(Character.SelectedBy.Inventory.AllItems);
} }
List<Item> medicalItems = new List<Item>(); List<Item> medicalItems = new List<Item>();
foreach (Item item in allInventoryItems) foreach (Item item in allInventoryItems)
{ {
if (item == null) continue; foreach (Item containedItem in item.ContainedItems)
var containedItems = item.ContainedItems;
if (containedItems != null)
{ {
foreach (Item containedItem in containedItems) if (!containedItem.HasTag("medical") && !containedItem.HasTag("chem")) { continue; }
{ medicalItems.Add(containedItem);
if (containedItem == null) continue;
if (!containedItem.HasTag("medical") && !containedItem.HasTag("chem")) continue;
medicalItems.Add(containedItem);
}
} }
if (!item.HasTag("medical") && !item.HasTag("chem")) continue; if (!item.HasTag("medical") && !item.HasTag("chem")) { continue; }
medicalItems.Add(item); medicalItems.Add(item);
} }
@@ -1804,24 +1796,27 @@ namespace Barotrauma
spriteBatch.Begin(SpriteSortMode.Deferred, Lights.CustomBlendStates.Multiplicative); spriteBatch.Begin(SpriteSortMode.Deferred, Lights.CustomBlendStates.Multiplicative);
float overlayScale = Math.Min( if (limbIndicatorOverlay != null)
drawArea.Width / (float)limbIndicatorOverlay.FrameSize.X,
drawArea.Height / (float)limbIndicatorOverlay.FrameSize.Y);
int frame = 0;
int frameCount = 17;
if (limbIndicatorOverlayAnimState >= frameCount * 2) limbIndicatorOverlayAnimState = 0.0f;
if (limbIndicatorOverlayAnimState < frameCount)
{ {
frame = (int)limbIndicatorOverlayAnimState; float overlayScale = Math.Min(
} drawArea.Width / (float)limbIndicatorOverlay.FrameSize.X,
else drawArea.Height / (float)limbIndicatorOverlay.FrameSize.Y);
{
frame = frameCount - (int)(limbIndicatorOverlayAnimState - (frameCount - 1));
}
limbIndicatorOverlay.Draw(spriteBatch, frame, drawArea.Center.ToVector2(), Color.Gray, origin: limbIndicatorOverlay.FrameSize.ToVector2() / 2, rotate: 0.0f, int frame = 0;
scale: Vector2.One * overlayScale); int frameCount = 17;
if (limbIndicatorOverlayAnimState >= frameCount * 2) limbIndicatorOverlayAnimState = 0.0f;
if (limbIndicatorOverlayAnimState < frameCount)
{
frame = (int)limbIndicatorOverlayAnimState;
}
else
{
frame = frameCount - (int)(limbIndicatorOverlayAnimState - (frameCount - 1));
}
limbIndicatorOverlay.Draw(spriteBatch, frame, drawArea.Center.ToVector2(), Color.Gray, origin: limbIndicatorOverlay.FrameSize.ToVector2() / 2, rotate: 0.0f,
scale: Vector2.One * overlayScale);
}
if (allowHighlight) if (allowHighlight)
{ {
@@ -22,8 +22,8 @@ namespace Barotrauma
float strength = MathHelper.Lerp(0, 1, MathUtils.InverseLerp(0, MathHelper.Pi, diff)); float strength = MathHelper.Lerp(0, 1, MathUtils.InverseLerp(0, MathHelper.Pi, diff));
float jointAngle = JointAngle * strength; float jointAngle = JointAngle * strength;
JointBendDeformation limbADeformation = LimbA.Deformations.Find(d => d is JointBendDeformation) as JointBendDeformation; JointBendDeformation limbADeformation = LimbA.ActiveDeformations.Find(d => d is JointBendDeformation) as JointBendDeformation;
JointBendDeformation limbBDeformation = LimbB.Deformations.Find(d => d is JointBendDeformation) as JointBendDeformation; JointBendDeformation limbBDeformation = LimbB.ActiveDeformations.Find(d => d is JointBendDeformation) as JointBendDeformation;
if (limbADeformation != null && limbBDeformation != null) if (limbADeformation != null && limbBDeformation != null)
{ {
@@ -114,7 +114,10 @@ namespace Barotrauma
/// Note that different limbs can share the same deformations. /// Note that different limbs can share the same deformations.
/// Use ragdoll.SpriteDeformations for a collection that cannot have duplicates. /// Use ragdoll.SpriteDeformations for a collection that cannot have duplicates.
/// </summary> /// </summary>
public List<SpriteDeformation> Deformations { get; private set; } = new List<SpriteDeformation>(); private List<SpriteDeformation> Deformations { get; set; } = new List<SpriteDeformation>();
private List<SpriteDeformation> NonConditionalDeformations { get; set; } = new List<SpriteDeformation>();
private List<(ConditionalSprite, IEnumerable<SpriteDeformation>)> ConditionalDeformations { get; set; } = new List<(ConditionalSprite, IEnumerable<SpriteDeformation>)>();
public List<SpriteDeformation> ActiveDeformations { get; set; } = new List<SpriteDeformation>();
public Sprite Sprite { get; protected set; } public Sprite Sprite { get; protected set; }
@@ -178,6 +181,9 @@ namespace Barotrauma
{ {
public float RotationState; public float RotationState;
public float OffsetState; public float OffsetState;
public Vector2 RandomOffsetMultiplier = new Vector2(Rand.Range(-1.0f, 1.0f), Rand.Range(-1.0f, 1.0f));
public float RandomRotationFactor = Rand.Range(0.0f, 1.0f);
public float RandomScaleFactor = Rand.Range(0.0f, 1.0f);
public bool IsActive = true; public bool IsActive = true;
} }
@@ -282,12 +288,16 @@ namespace Barotrauma
ConditionalSprites.Add(conditionalSprite); ConditionalSprites.Add(conditionalSprite);
if (conditionalSprite.DeformableSprite != null) if (conditionalSprite.DeformableSprite != null)
{ {
CreateDeformations(subElement.GetChildElement("deformablesprite")); var conditionalDeformations = CreateDeformations(subElement.GetChildElement("deformablesprite"));
Deformations.AddRange(conditionalDeformations);
ConditionalDeformations.Add((conditionalSprite, conditionalDeformations));
} }
break; break;
case "deformablesprite": case "deformablesprite":
_deformSprite = new DeformableSprite(subElement, filePath: GetSpritePath(subElement, Params.deformSpriteParams)); _deformSprite = new DeformableSprite(subElement, filePath: GetSpritePath(subElement, Params.deformSpriteParams));
CreateDeformations(subElement); var deformations = CreateDeformations(subElement);
Deformations.AddRange(deformations);
NonConditionalDeformations.AddRange(deformations);
break; break;
case "lightsource": case "lightsource":
LightSource = new LightSource(subElement, GetConditionalTarget()) LightSource = new LightSource(subElement, GetConditionalTarget())
@@ -315,8 +325,9 @@ namespace Barotrauma
return targetEntity; return targetEntity;
} }
void CreateDeformations(XElement e) IEnumerable<SpriteDeformation> CreateDeformations(XElement e)
{ {
List<SpriteDeformation> deformations = new List<SpriteDeformation>();
foreach (XElement animationElement in e.GetChildElements("spritedeformation")) foreach (XElement animationElement in e.GetChildElements("spritedeformation"))
{ {
int sync = animationElement.GetAttributeInt("sync", -1); int sync = animationElement.GetAttributeInt("sync", -1);
@@ -340,14 +351,39 @@ namespace Barotrauma
} }
if (deformation != null) if (deformation != null)
{ {
Deformations.Add(deformation); deformations.Add(deformation);
} }
} }
return deformations;
} }
} }
LightSource?.CheckConditionals(); LightSource?.CheckConditionals();
} }
private void RefreshDeformations()
{
if (_deformSprite == null) { return; }
if (ConditionalSprites.None())
{
ActiveDeformations = Deformations;
}
else
{
ActiveDeformations.Clear();
if (_deformSprite == DeformSprite)
{
ActiveDeformations.AddRange(NonConditionalDeformations);
}
foreach (var conditionalDeformation in ConditionalDeformations)
{
if (conditionalDeformation.Item1.IsActive)
{
ActiveDeformations.AddRange(conditionalDeformation.Item2);
}
}
}
}
public void RecreateSprites() public void RecreateSprites()
{ {
if (Sprite != null) if (Sprite != null)
@@ -390,18 +426,24 @@ namespace Barotrauma
character.Info?.CalculateHeadPosition(sprite); character.Info?.CalculateHeadPosition(sprite);
} }
private string _texturePath;
private string GetSpritePath(XElement element, SpriteParams spriteParams) private string GetSpritePath(XElement element, SpriteParams spriteParams)
{ {
if (spriteParams != null) if (_texturePath == null)
{ {
return GetSpritePath(spriteParams.GetTexturePath()); if (spriteParams != null)
} {
else string texturePath = character.Params.VariantFile?.Root?.GetAttributeString("texture", null) ?? spriteParams.GetTexturePath();
{ _texturePath = GetSpritePath(texturePath);
string texturePath = element.GetAttributeString("texture", null); }
texturePath = string.IsNullOrWhiteSpace(texturePath) ? ragdoll.RagdollParams.Texture : texturePath; else
return GetSpritePath(texturePath); {
string texturePath = element.GetAttributeString("texture", null);
texturePath = string.IsNullOrWhiteSpace(texturePath) ? ragdoll.RagdollParams.Texture : texturePath;
_texturePath = GetSpritePath(texturePath);
}
} }
return _texturePath;
} }
/// <summary> /// <summary>
@@ -537,7 +579,7 @@ namespace Barotrauma
else else
{ {
var spriteParams = Params.GetSprite(); var spriteParams = Params.GetSprite();
if (spriteParams.DeadColorTime > 0 && deadTimer < spriteParams.DeadColorTime) if (spriteParams != null && spriteParams.DeadColorTime > 0 && deadTimer < spriteParams.DeadColorTime)
{ {
deadTimer += deltaTime; deadTimer += deltaTime;
} }
@@ -587,6 +629,7 @@ namespace Barotrauma
} }
UpdateSpriteStates(deltaTime); UpdateSpriteStates(deltaTime);
RefreshDeformations();
} }
public void Draw(SpriteBatch spriteBatch, Camera cam, Color? overrideColor = null) public void Draw(SpriteBatch spriteBatch, Camera cam, Color? overrideColor = null)
@@ -637,13 +680,13 @@ namespace Barotrauma
var deformSprite = DeformSprite; var deformSprite = DeformSprite;
if (deformSprite != null) if (deformSprite != null)
{ {
if (Deformations != null && Deformations.Any()) if (ActiveDeformations.Any())
{ {
var deformation = SpriteDeformation.GetDeformation(Deformations, deformSprite.Size); var deformation = SpriteDeformation.GetDeformation(ActiveDeformations, deformSprite.Size);
deformSprite.Deform(deformation); deformSprite.Deform(deformation);
if (LightSource != null && LightSource.DeformableLightSprite != null) if (LightSource != null && LightSource.DeformableLightSprite != null)
{ {
deformation = SpriteDeformation.GetDeformation(Deformations, deformSprite.Size, dir == Direction.Left); deformation = SpriteDeformation.GetDeformation(ActiveDeformations, deformSprite.Size, dir == Direction.Left);
LightSource.DeformableLightSprite.Deform(deformation); LightSource.DeformableLightSprite.Deform(deformation);
} }
} }
@@ -666,9 +709,9 @@ namespace Barotrauma
if (conditionalSprite.DeformableSprite != null) if (conditionalSprite.DeformableSprite != null)
{ {
var defSprite = conditionalSprite.DeformableSprite; var defSprite = conditionalSprite.DeformableSprite;
if (Deformations != null && Deformations.Any()) if (ActiveDeformations.Any())
{ {
var deformation = SpriteDeformation.GetDeformation(Deformations, defSprite.Size); var deformation = SpriteDeformation.GetDeformation(ActiveDeformations, defSprite.Size);
defSprite.Deform(deformation); defSprite.Deform(deformation);
} }
else else
@@ -705,13 +748,13 @@ namespace Barotrauma
c = Color.Lerp(c, spriteParams.DeadColor, MathUtils.InverseLerp(0, Params.GetSprite().DeadColorTime, deadTimer)); c = Color.Lerp(c, spriteParams.DeadColor, MathUtils.InverseLerp(0, Params.GetSprite().DeadColorTime, deadTimer));
} }
c = overrideColor ?? c; c = overrideColor ?? c;
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState); float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState, spriteAnimState[decorativeSprite].RandomRotationFactor);
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState) * Scale; Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier) * Scale;
var ca = (float)Math.Cos(-body.Rotation); var ca = (float)Math.Cos(-body.Rotation);
var sa = (float)Math.Sin(-body.Rotation); var sa = (float)Math.Sin(-body.Rotation);
Vector2 transformedOffset = new Vector2(ca * offset.X + sa * offset.Y, -sa * offset.X + ca * offset.Y); Vector2 transformedOffset = new Vector2(ca * offset.X + sa * offset.Y, -sa * offset.X + ca * offset.Y);
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(body.DrawPosition.X + transformedOffset.X, -(body.DrawPosition.Y + transformedOffset.Y)), c, decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(body.DrawPosition.X + transformedOffset.X, -(body.DrawPosition.Y + transformedOffset.Y)), c,
-body.Rotation + rotation, decorativeSprite.Scale * Scale, spriteEffect, -body.Rotation + rotation, decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, spriteEffect,
depth: decorativeSprite.Sprite.Depth); depth: decorativeSprite.Sprite.Depth);
} }
float depthStep = 0.000001f; float depthStep = 0.000001f;
@@ -13,6 +13,8 @@ using System.Globalization;
using FarseerPhysics; using FarseerPhysics;
using Barotrauma.Extensions; using Barotrauma.Extensions;
using Barotrauma.Steam; using Barotrauma.Steam;
using System.Threading.Tasks;
using Barotrauma.MapCreatures.Behavior;
namespace Barotrauma namespace Barotrauma
{ {
@@ -69,6 +71,8 @@ namespace Barotrauma
private static readonly ChatManager chatManager = new ChatManager(true, 64); private static readonly ChatManager chatManager = new ChatManager(true, 64);
public static Dictionary<Keys, string> Keybinds = new Dictionary<Keys, string>();
public static void Init() public static void Init()
{ {
OpenAL.Alc.SetErrorReasonCallback((string msg) => NewMessage(msg, Color.Orange)); OpenAL.Alc.SetErrorReasonCallback((string msg) => NewMessage(msg, Color.Orange));
@@ -145,6 +149,17 @@ namespace Barotrauma
} }
} }
if (!IsOpen && GUI.KeyboardDispatcher.Subscriber == null)
{
foreach (var (key, command) in Keybinds)
{
if (PlayerInput.KeyHit(key))
{
ExecuteCommand(command);
}
}
}
activeQuestionText?.SetAsLastChild(); activeQuestionText?.SetAsLastChild();
if (PlayerInput.KeyHit(Keys.F3)) if (PlayerInput.KeyHit(Keys.F3))
@@ -227,6 +242,13 @@ namespace Barotrauma
case "fpscounter": case "fpscounter":
case "dumptofile": case "dumptofile":
case "findentityids": case "findentityids":
case "setfreecamspeed":
case "togglevoicechatfilters":
case "bindkey":
case "savebinds":
case "unbindkey":
case "wikiimage_character":
case "wikiimage_sub":
return true; return true;
default: default:
return client.HasConsoleCommandPermission(command); return client.HasConsoleCommandPermission(command);
@@ -235,20 +257,23 @@ namespace Barotrauma
public static void DequeueMessages() public static void DequeueMessages()
{ {
while (queuedMessages.Count > 0) lock (queuedMessages)
{ {
var newMsg = queuedMessages.Dequeue(); while (queuedMessages.Count > 0)
if (listBox == null)
{ {
//don't attempt to add to the listbox if it hasn't been created yet var newMsg = queuedMessages.Dequeue();
Messages.Add(newMsg); if (listBox == null)
} {
else //don't attempt to add to the listbox if it hasn't been created yet
{ Messages.Add(newMsg);
AddMessage(newMsg); }
} else
{
AddMessage(newMsg);
}
if (GameSettings.SaveDebugConsoleLogs) unsavedMessages.Add(newMsg); if (GameSettings.SaveDebugConsoleLogs) unsavedMessages.Add(newMsg);
}
} }
} }
@@ -487,6 +512,24 @@ namespace Barotrauma
GameMain.CharacterEditorScreen.Select(); GameMain.CharacterEditorScreen.Select();
})); }));
commands.Add(new Command("quickstart", "Starts a singleplayer sandbox", (string[] args) =>
{
if (Screen.Selected != GameMain.MainMenuScreen)
{
ThrowError("This command can only be executed from the main menu.");
return;
}
string subName = args.Length > 0 ? args[0] : "";
if (string.IsNullOrWhiteSpace(subName))
{
ThrowError("No submarine specified.");
return;
}
GameMain.MainMenuScreen.QuickStart(fixedSeed: false, subName);
}, getValidArgs: () => new[] { SubmarineInfo.SavedSubmarines.Select(s => s.Name).Distinct().ToArray() }));
commands.Add(new Command("steamnetdebug", "steamnetdebug: Toggles Steamworks networking debug logging.", (string[] args) => commands.Add(new Command("steamnetdebug", "steamnetdebug: Toggles Steamworks networking debug logging.", (string[] args) =>
{ {
SteamManager.NetworkingDebugLog = !SteamManager.NetworkingDebugLog; SteamManager.NetworkingDebugLog = !SteamManager.NetworkingDebugLog;
@@ -498,6 +541,97 @@ namespace Barotrauma
NewMessage("Ready checks can only be commenced in multiplayer.", Color.Red); NewMessage("Ready checks can only be commenced in multiplayer.", Color.Red);
})); }));
commands.Add(new Command("bindkey", "bindkey [key] [command]: Binds a key to a command.", (string[] args) =>
{
if (args.Length < 2)
{
ThrowError("No key or command specified.");
return;
}
string keyString = args[0];
string command = args[1];
if (Enum.TryParse(typeof(Keys), keyString, ignoreCase: true, out object outKey) && outKey is Keys key)
{
if (Keybinds.ContainsKey(key))
{
Keybinds[key] = command;
}
else
{
Keybinds.Add(key, command);
}
NewMessage($"\"{command}\" bound to {key}.", GUI.Style.Green);
if (GameMain.Config.keyMapping.FirstOrDefault(bind => bind.Key != Keys.None && bind.Key == key) is { } existingBind)
{
AddWarning($"\"{key}\" has already been bound to {(InputType)GameMain.Config.keyMapping.IndexOf(existingBind)}. The keybind will perform both actions when pressed.");
}
return;
}
ThrowError($"Invalid key {keyString}.");
}, isCheat: false, getValidArgs: () => new[] { Enum.GetNames(typeof(Keys)), new[] { "\"\"" } }));
commands.Add(new Command("unbindkey", "unbindkey [key]: Unbinds a command.", (string[] args) =>
{
if (args.Length < 1)
{
ThrowError("No key specified.");
return;
}
string keyString = args[0];
if (Enum.TryParse(typeof(Keys), keyString, ignoreCase: true, out object outKey) && outKey is Keys key)
{
if (Keybinds.ContainsKey(key))
{
Keybinds.Remove(key);
}
NewMessage("Keybind unbound.", GUI.Style.Green);
return;
}
ThrowError($"Invalid key {keyString}.");
}, isCheat: false, getValidArgs: () => new[] { Keybinds.Keys.Select(keys => keys.ToString()).Distinct().ToArray() }));
commands.Add(new Command("savebinds", "savebinds: Writes current keybinds into the config file.", (string[] args) =>
{
ShowQuestionPrompt($"Some keybinds may render the game unusable, are you sure you want to make these keybinds persistent? ({Keybinds.Count} keybind(s) assigned) Y/N",
(option2) =>
{
if (option2.ToLower() != "y")
{
NewMessage("Aborted.", GUI.Style.Red);
return;
}
GameSettings.ConsoleKeybinds = new Dictionary<Keys, string>(Keybinds);
GameMain.Config.SaveNewPlayerConfig();
NewMessage($"{Keybinds.Count} keybind(s) written to the config file.", GUI.Style.Green);
});
}, isCheat: false));
commands.Add(new Command("togglegrid", "Toggle visual snap grid in sub editor.", (string[] args) =>
{
SubEditorScreen.ShouldDrawGrid = !SubEditorScreen.ShouldDrawGrid;
NewMessage(SubEditorScreen.ShouldDrawGrid ? "Enabled submarine grid." : "Disabled submarine grid.", GUI.Style.Green);
}));
commands.Add(new Command("wikiimage_character", "Save an image of the currently controlled character with a transparent background.", (string[] args) =>
{
if (Character.Controlled == null) { return; }
WikiImage.Create(Character.Controlled);
}));
commands.Add(new Command("wikiimage_sub", "Save an image of the main submarine with a transparent background.", (string[] args) =>
{
if (Submarine.MainSub == null) { return; }
WikiImage.Create(Submarine.MainSub);
}));
AssignRelayToServer("kick", false); AssignRelayToServer("kick", false);
AssignRelayToServer("kickid", false); AssignRelayToServer("kickid", false);
AssignRelayToServer("ban", false); AssignRelayToServer("ban", false);
@@ -510,11 +644,18 @@ namespace Barotrauma
AssignRelayToServer("verboselogging", false); AssignRelayToServer("verboselogging", false);
AssignRelayToServer("freecam", false); AssignRelayToServer("freecam", false);
AssignRelayToServer("steamnetdebug", false); AssignRelayToServer("steamnetdebug", false);
AssignRelayToServer("quickstart", false);
AssignRelayToServer("togglegrid", false);
AssignRelayToServer("bindkey", false);
AssignRelayToServer("unbindkey", false);
AssignRelayToServer("savebinds", false);
#if DEBUG #if DEBUG
AssignRelayToServer("crash", false); AssignRelayToServer("crash", false);
AssignRelayToServer("showballastflorasprite", false);
AssignRelayToServer("simulatedlatency", false); AssignRelayToServer("simulatedlatency", false);
AssignRelayToServer("simulatedloss", false); AssignRelayToServer("simulatedloss", false);
AssignRelayToServer("simulatedduplicateschance", false); AssignRelayToServer("simulatedduplicateschance", false);
AssignRelayToServer("storeinfo", false);
#endif #endif
commands.Add(new Command("clientlist", "", (string[] args) => { })); commands.Add(new Command("clientlist", "", (string[] args) => { }));
@@ -998,6 +1139,17 @@ namespace Barotrauma
}); });
AssignRelayToServer("debugdraw", false); AssignRelayToServer("debugdraw", false);
AssignOnExecute("togglevoicechatfilters", (string[] args) =>
{
if (args.None() || !bool.TryParse(args[0], out bool state))
{
state = !GameMain.Config.DisableVoiceChatFilters;
}
GameMain.Config.DisableVoiceChatFilters = state;
NewMessage("Voice chat filters " + (GameMain.Config.DisableVoiceChatFilters ? "disabled" : "enabled"), Color.White);
});
AssignRelayToServer("togglevoicechatfilters", false);
commands.Add(new Command("fpscounter", "fpscounter: Toggle the FPS counter.", (string[] args) => commands.Add(new Command("fpscounter", "fpscounter: Toggle the FPS counter.", (string[] args) =>
{ {
GameMain.ShowFPS = !GameMain.ShowFPS; GameMain.ShowFPS = !GameMain.ShowFPS;
@@ -1383,6 +1535,16 @@ namespace Barotrauma
File.WriteAllLines(filePath, debugLines); File.WriteAllLines(filePath, debugLines);
ToolBox.OpenFileWithShell(Path.GetFullPath(filePath)); ToolBox.OpenFileWithShell(Path.GetFullPath(filePath));
})); }));
commands.Add(new Command("setfreecamspeed", "setfreecamspeed [speed]: Set the camera movement speed when not controlling a character. Defaults to 1.", (string[] args) =>
{
if (args.Length > 0)
{
float.TryParse(args[0], NumberStyles.Number, CultureInfo.InvariantCulture, out float speed);
Screen.Selected.Cam.FreeCamMoveSpeed = speed;
}
}));
#if DEBUG #if DEBUG
commands.Add(new Command("setplanthealth", "setplanthealth [value]: Sets the health of the selected plant in sub editor.", (string[] args) => commands.Add(new Command("setplanthealth", "setplanthealth [value]: Sets the health of the selected plant in sub editor.", (string[] args) =>
{ {
@@ -1417,6 +1579,12 @@ namespace Barotrauma
} }
})); }));
commands.Add(new Command("showballastflorasprite", "", (string[] args) =>
{
BallastFloraBehavior.AlwaysShowBallastFloraSprite = !BallastFloraBehavior.AlwaysShowBallastFloraSprite;
NewMessage("ok", GUI.Style.Green);
}));
commands.Add(new Command("printreceivertransfers", "", (string[] args) => commands.Add(new Command("printreceivertransfers", "", (string[] args) =>
{ {
GameMain.Client.PrintReceiverTransters(); GameMain.Client.PrintReceiverTransters();
@@ -1834,13 +2002,15 @@ namespace Barotrauma
#if DEBUG #if DEBUG
commands.Add(new Command("querylobbies", "Queries all SteamP2P lobbies", (args) => commands.Add(new Command("querylobbies", "Queries all SteamP2P lobbies", (args) =>
{ {
Steamworks.Data.LobbyQuery lobbyQuery = Steamworks.SteamMatchmaking.CreateLobbyQuery().FilterDistanceWorldwide(); TaskPool.Add("DebugQueryLobbies",
SteamManager.LobbyQueryRequest(), (t) => {
Steamworks.Data.Lobby[] lobbies = lobbyQuery.RequestAsync().Result; var lobbies = ((Task<List<Steamworks.Data.Lobby>>)t).Result;
foreach (var lobby in lobbies) foreach (var lobby in lobbies)
{ {
DebugConsole.NewMessage(lobby.GetData("name") + ", " + lobby.GetData("lobbyowner")); NewMessage(lobby.GetData("name") + ", " + lobby.GetData("lobbyowner"), Color.Yellow);
} }
NewMessage($"Retrieved a total of {lobbies.Count} lobbies", Color.Lime);
});
})); }));
commands.Add(new Command("checkduplicates", "Checks the given language for duplicate translation keys and writes to file.", (string[] args) => commands.Add(new Command("checkduplicates", "Checks the given language for duplicate translation keys and writes to file.", (string[] args) =>
@@ -210,9 +210,33 @@ namespace Barotrauma
} }
}; };
double allowCloseTime = Timing.TotalTime + 0.5;
closeButton.Children.ForEach(child => child.SpriteEffects = SpriteEffects.FlipVertically); closeButton.Children.ForEach(child => child.SpriteEffects = SpriteEffects.FlipVertically);
closeButton.Frame.FadeIn(0.5f, 0.5f); closeButton.Frame.FadeIn(0.5f, 0.5f);
closeButton.SlideIn(0.5f, 0.33f, 16, SlideDirection.Down); closeButton.SlideIn(0.5f, 0.33f, 16, SlideDirection.Down);
InputType? closeInput = null;
if (GameMain.Config.KeyBind(InputType.Use).MouseButton == MouseButton.None)
{
closeInput = InputType.Use;
}
else if (GameMain.Config.KeyBind(InputType.Select).MouseButton == MouseButton.None)
{
closeInput = InputType.Select;
}
if (closeInput.HasValue)
{
closeButton.ToolTip = TextManager.ParseInputTypes($"{TextManager.Get("Close")} ([InputType.{closeInput.Value}])");
closeButton.OnAddedToGUIUpdateList += (GUIComponent component) =>
{
if (Timing.TotalTime > allowCloseTime && PlayerInput.KeyHit(closeInput.Value))
{
GUIButton btn = component as GUIButton;
btn?.OnClicked(btn, btn.UserData);
btn?.Flash(GUI.Style.Green);
}
};
}
} }
for (int i = 0; i < optionButtons.Count; i++) for (int i = 0; i < optionButtons.Count; i++)
@@ -43,15 +43,15 @@ namespace Barotrauma
} }
GUI.DrawString(spriteBatch, new Vector2(10, y), "EventManager", Color.White, Color.Black * 0.6f, 0, GUI.SmallFont); GUI.DrawString(spriteBatch, new Vector2(10, y), "EventManager", Color.White, Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 20), "Event cooldown: " + eventCoolDown, Color.White, Color.Black * 0.6f, 0, GUI.SmallFont); GUI.DrawString(spriteBatch, new Vector2(15, y + 20), "Event cooldown: " + (int)Math.Max(eventCoolDown, 0), Color.White, Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 35), "Current intensity: " + (int) Math.Round(currentIntensity * 100), Color.Lerp(Color.White, GUI.Style.Red, currentIntensity), Color.Black * 0.6f, 0, GUI.SmallFont); GUI.DrawString(spriteBatch, new Vector2(15, y + 35), "Current intensity: " + (int)Math.Round(currentIntensity * 100), Color.Lerp(Color.White, GUI.Style.Red, currentIntensity), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 50), "Target intensity: " + (int) Math.Round(targetIntensity * 100), Color.Lerp(Color.White, GUI.Style.Red, targetIntensity), Color.Black * 0.6f, 0, GUI.SmallFont); GUI.DrawString(spriteBatch, new Vector2(15, y + 50), "Target intensity: " + (int)Math.Round(targetIntensity * 100), Color.Lerp(Color.White, GUI.Style.Red, targetIntensity), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 65), "AvgHealth: " + (int) Math.Round(avgCrewHealth * 100), Color.Lerp(GUI.Style.Red, GUI.Style.Green, avgCrewHealth), Color.Black * 0.6f, 0, GUI.SmallFont); GUI.DrawString(spriteBatch, new Vector2(15, y + 65), "AvgHealth: " + (int)Math.Round(avgCrewHealth * 100), Color.Lerp(GUI.Style.Red, GUI.Style.Green, avgCrewHealth), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 80), "AvgHullIntegrity: " + (int) Math.Round(avgHullIntegrity * 100), Color.Lerp(GUI.Style.Red, GUI.Style.Green, avgHullIntegrity), Color.Black * 0.6f, 0, GUI.SmallFont); GUI.DrawString(spriteBatch, new Vector2(15, y + 80), "AvgHullIntegrity: " + (int)Math.Round(avgHullIntegrity * 100), Color.Lerp(GUI.Style.Red, GUI.Style.Green, avgHullIntegrity), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 95), "FloodingAmount: " + (int) Math.Round(floodingAmount * 100), Color.Lerp(GUI.Style.Green, GUI.Style.Red, floodingAmount), Color.Black * 0.6f, 0, GUI.SmallFont); GUI.DrawString(spriteBatch, new Vector2(15, y + 95), "FloodingAmount: " + (int)Math.Round(floodingAmount * 100), Color.Lerp(GUI.Style.Green, GUI.Style.Red, floodingAmount), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 110), "FireAmount: " + (int) Math.Round(fireAmount * 100), Color.Lerp(GUI.Style.Green, GUI.Style.Red, fireAmount), Color.Black * 0.6f, 0, GUI.SmallFont); GUI.DrawString(spriteBatch, new Vector2(15, y + 110), "FireAmount: " + (int)Math.Round(fireAmount * 100), Color.Lerp(GUI.Style.Green, GUI.Style.Red, fireAmount), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 125), "EnemyDanger: " + (int) Math.Round(enemyDanger * 100), Color.Lerp(GUI.Style.Green, GUI.Style.Red, enemyDanger), Color.Black * 0.6f, 0, GUI.SmallFont); GUI.DrawString(spriteBatch, new Vector2(15, y + 125), "EnemyDanger: " + (int)Math.Round(enemyDanger * 100), Color.Lerp(GUI.Style.Green, GUI.Style.Red, enemyDanger), Color.Black * 0.6f, 0, GUI.SmallFont);
#if DEBUG #if DEBUG
if (PlayerInput.KeyDown(Microsoft.Xna.Framework.Input.Keys.LeftAlt) && if (PlayerInput.KeyDown(Microsoft.Xna.Framework.Input.Keys.LeftAlt) &&
@@ -86,15 +86,26 @@ namespace Barotrauma
new Vector2(graphRect.Right + 5, graphRect.Y + graphRect.Height * (1.0f - eventThreshold)), Color.Orange, 0, 1); new Vector2(graphRect.Right + 5, graphRect.Y + graphRect.Height * (1.0f - eventThreshold)), Color.Orange, 0, 1);
y = graphRect.Bottom + 20; y = graphRect.Bottom + 20;
if (eventCoolDown > 0.0f) int x = graphRect.X;
if (isCrewAway && crewAwayDuration < settings.FreezeDurationWhenCrewAway)
{ {
GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y), "Event cooldown active: " + (int) eventCoolDown, Color.LightGreen * 0.8f, null, 0, GUI.SmallFont); GUI.DrawString(spriteBatch, new Vector2(x, y), "Events frozen (crew away from sub): " + ToolBox.SecondsToReadableTime(settings.FreezeDurationWhenCrewAway - crewAwayDuration), Color.LightGreen * 0.8f, null, 0, GUI.SmallFont);
y += 15;
}
else if (crewAwayResetTimer > 0.0f)
{
GUI.DrawString(spriteBatch, new Vector2(x, y), "Events frozen (crew just returned to the sub): " + ToolBox.SecondsToReadableTime(crewAwayResetTimer), Color.LightGreen * 0.8f, null, 0, GUI.SmallFont);
y += 15;
}
else if (eventCoolDown > 0.0f)
{
GUI.DrawString(spriteBatch, new Vector2(x, y), "Event cooldown active: " + ToolBox.SecondsToReadableTime(eventCoolDown), Color.LightGreen * 0.8f, null, 0, GUI.SmallFont);
y += 15; y += 15;
} }
else if (currentIntensity > eventThreshold) else if (currentIntensity > eventThreshold)
{ {
GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y), GUI.DrawString(spriteBatch, new Vector2(x, y),
"Intensity too high for new events: " + (int) (currentIntensity * 100) + "%/" + (int) (eventThreshold * 100) + "%", Color.LightGreen * 0.8f, null, 0, GUI.SmallFont); "Intensity too high for new events: " + (int)(currentIntensity * 100) + "%/" + (int)(eventThreshold * 100) + "%", Color.LightGreen * 0.8f, null, 0, GUI.SmallFont);
y += 15; y += 15;
} }
@@ -102,22 +113,27 @@ namespace Barotrauma
{ {
if (Submarine.MainSub == null) { break; } if (Submarine.MainSub == null) { break; }
GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y), "New event (ID " + eventSet.DebugIdentifier + ") after: ", Color.Orange * 0.8f, null, 0, GUI.SmallFont); GUI.DrawString(spriteBatch, new Vector2(x, y), "New event (ID " + eventSet.DebugIdentifier + ") after: ", Color.Orange * 0.8f, null, 0, GUI.SmallFont);
y += 12; y += 12;
if (eventSet.PerCave)
{
GUI.DrawString(spriteBatch, new Vector2(x, y), " submarine near cave", Color.Orange * 0.8f, null, 0, GUI.SmallFont);
y += 12;
}
if (eventSet.PerWreck) if (eventSet.PerWreck)
{ {
GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y), " submarine near the wreck", Color.Orange * 0.8f, null, 0, GUI.SmallFont); GUI.DrawString(spriteBatch, new Vector2(x, y), " submarine near the wreck", Color.Orange * 0.8f, null, 0, GUI.SmallFont);
y += 12; y += 12;
} }
if (eventSet.PerRuin) if (eventSet.PerRuin)
{ {
GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y), " submarine near the ruins", Color.Orange * 0.8f, null, 0, GUI.SmallFont); GUI.DrawString(spriteBatch, new Vector2(x, y), " submarine near the ruins", Color.Orange * 0.8f, null, 0, GUI.SmallFont);
y += 12; y += 12;
} }
if (roundDuration < eventSet.MinMissionTime) if (roundDuration < eventSet.MinMissionTime)
{ {
GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y), GUI.DrawString(spriteBatch, new Vector2(x, y),
" " + (int) (eventSet.MinDistanceTraveled * 100.0f) + "% travelled (current: " + (int) (distanceTraveled * 100.0f) + " %)", " " + (int) (eventSet.MinDistanceTraveled * 100.0f) + "% travelled (current: " + (int) (distanceTraveled * 100.0f) + " %)",
((Submarine.MainSub == null || distanceTraveled < eventSet.MinDistanceTraveled) ? Color.Lerp(GUI.Style.Yellow, GUI.Style.Red, eventSet.MinDistanceTraveled - distanceTraveled) : GUI.Style.Green) * 0.8f, null, 0, GUI.SmallFont); ((Submarine.MainSub == null || distanceTraveled < eventSet.MinDistanceTraveled) ? Color.Lerp(GUI.Style.Yellow, GUI.Style.Red, eventSet.MinDistanceTraveled - distanceTraveled) : GUI.Style.Green) * 0.8f, null, 0, GUI.SmallFont);
y += 12; y += 12;
@@ -125,7 +141,7 @@ namespace Barotrauma
if (CurrentIntensity < eventSet.MinIntensity || CurrentIntensity > eventSet.MaxIntensity) if (CurrentIntensity < eventSet.MinIntensity || CurrentIntensity > eventSet.MaxIntensity)
{ {
GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y), GUI.DrawString(spriteBatch, new Vector2(x, y),
" intensity between " + ((int) eventSet.MinIntensity) + " and " + ((int) eventSet.MaxIntensity), " intensity between " + ((int) eventSet.MinIntensity) + " and " + ((int) eventSet.MaxIntensity),
Color.Orange * 0.8f, null, 0, GUI.SmallFont); Color.Orange * 0.8f, null, 0, GUI.SmallFont);
y += 12; y += 12;
@@ -133,22 +149,28 @@ namespace Barotrauma
if (roundDuration < eventSet.MinMissionTime) if (roundDuration < eventSet.MinMissionTime)
{ {
GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y), GUI.DrawString(spriteBatch, new Vector2(x, y),
" " + (int) (eventSet.MinMissionTime - roundDuration) + " s", " " + (int) (eventSet.MinMissionTime - roundDuration) + " s",
Color.Lerp(GUI.Style.Yellow, GUI.Style.Red, (eventSet.MinMissionTime - roundDuration)), null, 0, GUI.SmallFont); Color.Lerp(GUI.Style.Yellow, GUI.Style.Red, (eventSet.MinMissionTime - roundDuration)), null, 0, GUI.SmallFont);
} }
y += 15; y += 15;
if (y > GameMain.GraphicsHeight * 0.9f)
{
y = graphRect.Bottom + 35;
x += 250;
}
} }
GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y), "Current events: ", Color.White * 0.9f, null, 0, GUI.SmallFont); GUI.DrawString(spriteBatch, new Vector2(x, y), "Current events: ", Color.White * 0.9f, null, 0, GUI.SmallFont);
y += 15; y += 15;
foreach (Event ev in activeEvents.Where(ev => !ev.IsFinished || PlayerInput.IsShiftDown())) foreach (Event ev in activeEvents.Where(ev => !ev.IsFinished || PlayerInput.IsShiftDown()))
{ {
GUI.DrawString(spriteBatch, new Vector2(graphRect.X + 5, y), ev.ToString(), (!ev.IsFinished ? Color.White : Color.Red) * 0.8f, null, 0, GUI.SmallFont); GUI.DrawString(spriteBatch, new Vector2(x + 5, y), ev.ToString(), (!ev.IsFinished ? Color.White : Color.Red) * 0.8f, null, 0, GUI.SmallFont);
Rectangle rect = new Rectangle(new Point(graphRect.X + 5, y), GUI.SmallFont.MeasureString(ev.ToString()).ToPoint()); Rectangle rect = new Rectangle(new Point(x + 5, y), GUI.SmallFont.MeasureString(ev.ToString()).ToPoint());
Rectangle outlineRect = new Rectangle(rect.Location, rect.Size); Rectangle outlineRect = new Rectangle(rect.Location, rect.Size);
outlineRect.Inflate(4, 4); outlineRect.Inflate(4, 4);
@@ -176,6 +198,11 @@ namespace Barotrauma
} }
y += 18; y += 18;
if (y > GameMain.GraphicsHeight * 0.9f)
{
y = graphRect.Bottom + 35;
x += 250;
}
} }
} }
@@ -333,7 +360,7 @@ namespace Barotrauma
$"Spawn pending: {artifactEvent.SpawnPending.ColorizeObject()}\n" + $"Spawn pending: {artifactEvent.SpawnPending.ColorizeObject()}\n" +
$"Spawn position: {artifactEvent.SpawnPos.ColorizeObject()}\n"; $"Spawn position: {artifactEvent.SpawnPos.ColorizeObject()}\n";
if (artifactEvent.Item != null) if (artifactEvent.Item != null && !artifactEvent.Item.Removed)
{ {
Vector2 pos = artifactEvent.Item.WorldPosition; Vector2 pos = artifactEvent.Item.WorldPosition;
positions.Add(new DebugLine(pos, Color.White)); positions.Add(new DebugLine(pos, Color.White));
@@ -364,7 +391,8 @@ namespace Barotrauma
foreach (Character monster in monsterEvent.Monsters) foreach (Character monster in monsterEvent.Monsters)
{ {
text += $" {monster.ColorizeObject()} -> (Dead: {monster.IsDead.ColorizeObject()}, Health: {monster.HealthPercentage.ColorizeObject()}%, AIState: {(monster.AIController?.State).ColorizeObject()})\n"; text += $" {monster.ColorizeObject()} -> (Dead: {monster.IsDead.ColorizeObject()}, Health: {monster.HealthPercentage.ColorizeObject()}%, AIState: {(monster.AIController is EnemyAIController enemyAI ? enemyAI.State : AIState.Idle ).ColorizeObject()})\n";
if (monster.Removed) { continue; }
positions.Add(new DebugLine(monster.WorldPosition, Color.Red)); positions.Add(new DebugLine(monster.WorldPosition, Color.Red));
} }
} }
@@ -1,7 +1,4 @@
using Barotrauma.Networking; using Barotrauma.Networking;
using System;
using System.Collections.Generic;
using System.Text;
namespace Barotrauma namespace Barotrauma
{ {
@@ -17,7 +17,7 @@ namespace Barotrauma
} }
//team specific //team specific
return descriptions[GameMain.Client.Character.TeamID == Character.TeamType.Team1 ? 1 : 2]; return descriptions[GameMain.Client.Character.TeamID == CharacterTeamType.Team1 ? 1 : 2];
} }
} }
@@ -12,6 +12,23 @@ namespace Barotrauma
{ {
public override void ClientReadInitial(IReadMessage msg) public override void ClientReadInitial(IReadMessage msg)
{ {
byte caveCount = msg.ReadByte();
for (int i = 0; i < caveCount; i++)
{
byte selectedCave = msg.ReadByte();
if (selectedCave < 255 && Level.Loaded != null)
{
if (selectedCave < Level.Loaded.Caves.Count)
{
Level.Loaded.Caves[selectedCave].DisplayOnSonar = true;
}
else
{
DebugConsole.ThrowError($"Cave index out of bounds when reading nest mission data. Index: {selectedCave}, number of caves: {Level.Loaded.Caves.Count}");
}
}
}
for (int i = 0; i < ResourceClusters.Count; i++) for (int i = 0; i < ResourceClusters.Count; i++)
{ {
var amount = msg.ReadByte(); var amount = msg.ReadByte();
@@ -8,9 +8,23 @@ namespace Barotrauma
{ {
public override void ClientReadInitial(IReadMessage msg) public override void ClientReadInitial(IReadMessage msg)
{ {
byte selectedCaveIndex = msg.ReadByte();
nestPosition = new Vector2( nestPosition = new Vector2(
msg.ReadSingle(), msg.ReadSingle(),
msg.ReadSingle()); msg.ReadSingle());
if (selectedCaveIndex < 255 && Level.Loaded != null)
{
if (selectedCaveIndex < Level.Loaded.Caves.Count)
{
Level.Loaded.Caves[selectedCaveIndex].DisplayOnSonar = true;
SpawnNestObjects(Level.Loaded, Level.Loaded.Caves[selectedCaveIndex]);
}
else
{
DebugConsole.ThrowError($"Cave index out of bounds when reading nest mission data. Index: {selectedCaveIndex}, number of caves: {Level.Loaded.Caves.Count}");
}
}
ushort itemCount = msg.ReadUInt16(); ushort itemCount = msg.ReadUInt16();
for (int i = 0; i < itemCount; i++) for (int i = 0; i < itemCount; i++)
{ {
@@ -287,6 +287,7 @@ namespace Barotrauma
{ {
lock (mutex) lock (mutex)
{ {
usedIndicatorAngles.Clear();
if (ScreenChanged) if (ScreenChanged)
{ {
@@ -1117,15 +1118,26 @@ namespace Barotrauma
/// Set the cursor to an hourglass. /// Set the cursor to an hourglass.
/// Will automatically revert after 10 seconds or when <see cref="ClearCursorWait"/> is called. /// Will automatically revert after 10 seconds or when <see cref="ClearCursorWait"/> is called.
/// </summary> /// </summary>
public static void SetCursorWaiting() public static void SetCursorWaiting(int waitSeconds = 10, Func<bool> endCondition = null)
{ {
CoroutineManager.StartCoroutine(WaitCursorCoroutine(), "WaitCursorTimeout"); CoroutineManager.StartCoroutine(WaitCursorCoroutine(), "WaitCursorTimeout");
static IEnumerable<object> WaitCursorCoroutine() IEnumerable<object> WaitCursorCoroutine()
{ {
MouseCursor = CursorState.Waiting; MouseCursor = CursorState.Waiting;
var timeOut = DateTime.Now + new TimeSpan(0, 0, 10); var timeOut = DateTime.Now + new TimeSpan(0, 0, waitSeconds);
while (DateTime.Now < timeOut) { yield return CoroutineStatus.Running; } while (DateTime.Now < timeOut)
{
if (endCondition != null)
{
try
{
if (endCondition.Invoke()) { break; }
}
catch { break; }
}
yield return CoroutineStatus.Running;
}
if (MouseCursor == CursorState.Waiting) { MouseCursor = CursorState.Default; } if (MouseCursor == CursorState.Waiting) { MouseCursor = CursorState.Default; }
yield return CoroutineStatus.Success; yield return CoroutineStatus.Success;
} }
@@ -1219,7 +1231,7 @@ namespace Barotrauma
{ {
foreach (GUIMessage msg in messages) foreach (GUIMessage msg in messages)
{ {
if (msg.WorldSpace) continue; if (msg.WorldSpace) { continue; }
msg.Timer -= deltaTime; msg.Timer -= deltaTime;
if (msg.Size.X > HUDLayoutSettings.MessageAreaTop.Width) if (msg.Size.X > HUDLayoutSettings.MessageAreaTop.Width)
@@ -1244,7 +1256,7 @@ namespace Barotrauma
foreach (GUIMessage msg in messages) foreach (GUIMessage msg in messages)
{ {
if (!msg.WorldSpace) continue; if (!msg.WorldSpace) { continue; }
msg.Timer -= deltaTime; msg.Timer -= deltaTime;
msg.Pos += msg.Velocity * deltaTime; msg.Pos += msg.Velocity * deltaTime;
} }
@@ -1256,18 +1268,21 @@ namespace Barotrauma
#region Element drawing #region Element drawing
private static List<float> usedIndicatorAngles = new List<float>();
/// <param name="createOffset">Should the indicator move based on the camera position?</param> /// <param name="createOffset">Should the indicator move based on the camera position?</param>
public static void DrawIndicator(SpriteBatch spriteBatch, Vector2 worldPosition, Camera cam, float hideDist, Sprite sprite, Color color, bool createOffset = true, float scaleMultiplier = 1.0f) /// <param name="overrideAlpha">Override the distance-based alpha value with the specified alpha value</param>
public static void DrawIndicator(SpriteBatch spriteBatch, Vector2 worldPosition, Camera cam, float hideDist, Sprite sprite, Color color,
bool createOffset = true, float scaleMultiplier = 1.0f, float? overrideAlpha = null)
{ {
Vector2 diff = worldPosition - cam.WorldViewCenter; Vector2 diff = worldPosition - cam.WorldViewCenter;
float dist = diff.Length(); float dist = diff.Length();
float symbolScale = Math.Min(64.0f / sprite.size.X, 1.0f) * scaleMultiplier; float symbolScale = Math.Min(64.0f / sprite.size.X, 1.0f) * scaleMultiplier;
if (dist > hideDist) if (overrideAlpha.HasValue || dist > hideDist)
{ {
float alpha = Math.Min((dist - hideDist) / 100.0f, 1.0f); float alpha = overrideAlpha ?? Math.Min((dist - hideDist) / 100.0f, 1.0f);
Vector2 targetScreenPos = cam.WorldToScreen(worldPosition); Vector2 targetScreenPos = cam.WorldToScreen(worldPosition);
if (!createOffset) if (!createOffset)
@@ -1279,6 +1294,28 @@ namespace Barotrauma
float screenDist = Vector2.Distance(cam.WorldToScreen(cam.WorldViewCenter), targetScreenPos); float screenDist = Vector2.Distance(cam.WorldToScreen(cam.WorldViewCenter), targetScreenPos);
float angle = MathUtils.VectorToAngle(diff); float angle = MathUtils.VectorToAngle(diff);
float minAngleDiff = 0.05f;
bool overlapFound = true;
int iterations = 0;
while (overlapFound && iterations < 10)
{
overlapFound = false;
foreach (float usedIndicatorAngle in usedIndicatorAngles)
{
float shortestAngle = MathUtils.GetShortestAngle(angle, usedIndicatorAngle);
if (MathUtils.NearlyEqual(shortestAngle, 0.0f)) { shortestAngle = 0.01f; }
if (Math.Abs(shortestAngle) < minAngleDiff)
{
angle -= Math.Sign(shortestAngle) * (minAngleDiff - Math.Abs(shortestAngle));
overlapFound = true;
break;
}
}
iterations++;
}
usedIndicatorAngles.Add(angle);
Vector2 unclampedDiff = new Vector2( Vector2 unclampedDiff = new Vector2(
(float)Math.Cos(angle) * screenDist, (float)Math.Cos(angle) * screenDist,
(float)-Math.Sin(angle) * screenDist); (float)-Math.Sin(angle) * screenDist);
@@ -1489,12 +1526,12 @@ namespace Barotrauma
foreach (GUIMessage msg in messages) foreach (GUIMessage msg in messages)
{ {
if (msg.WorldSpace) continue; if (msg.WorldSpace) { continue; }
Vector2 drawPos = new Vector2(HUDLayoutSettings.MessageAreaTop.Right, HUDLayoutSettings.MessageAreaTop.Center.Y); Vector2 drawPos = new Vector2(HUDLayoutSettings.MessageAreaTop.Right, HUDLayoutSettings.MessageAreaTop.Center.Y);
msg.Font.DrawString(spriteBatch, msg.Text, drawPos + msg.Pos + Vector2.One, Color.Black, 0, msg.Origin, 1.0f, SpriteEffects.None, 0); msg.Font.DrawString(spriteBatch, msg.Text, drawPos + msg.DrawPos + Vector2.One, Color.Black, 0, msg.Origin, 1.0f, SpriteEffects.None, 0);
msg.Font.DrawString(spriteBatch, msg.Text, drawPos + msg.Pos, msg.Color, 0, msg.Origin, 1.0f, SpriteEffects.None, 0); msg.Font.DrawString(spriteBatch, msg.Text, drawPos + msg.DrawPos, msg.Color, 0, msg.Origin, 1.0f, SpriteEffects.None, 0);
break; break;
} }
@@ -1507,14 +1544,14 @@ namespace Barotrauma
foreach (GUIMessage msg in messages) foreach (GUIMessage msg in messages)
{ {
if (!msg.WorldSpace) continue; if (!msg.WorldSpace) { continue; }
if (cam != null) if (cam != null)
{ {
float alpha = 1.0f; float alpha = 1.0f;
if (msg.Timer < 1.0f) alpha -= 1.0f - msg.Timer; if (msg.Timer < 1.0f) { alpha -= 1.0f - msg.Timer; }
Vector2 drawPos = cam.WorldToScreen(msg.Pos); Vector2 drawPos = cam.WorldToScreen(msg.DrawPos);
msg.Font.DrawString(spriteBatch, msg.Text, drawPos + Vector2.One, Color.Black * alpha, 0, msg.Origin, 1.0f, SpriteEffects.None, 0); msg.Font.DrawString(spriteBatch, msg.Text, drawPos + Vector2.One, Color.Black * alpha, 0, msg.Origin, 1.0f, SpriteEffects.None, 0);
msg.Font.DrawString(spriteBatch, msg.Text, drawPos, msg.Color * alpha, 0, msg.Origin, 1.0f, SpriteEffects.None, 0); msg.Font.DrawString(spriteBatch, msg.Text, drawPos, msg.Color * alpha, 0, msg.Origin, 1.0f, SpriteEffects.None, 0);
} }
@@ -1608,7 +1645,8 @@ namespace Barotrauma
public static Texture2D CreateCapsule(int radius, int height) public static Texture2D CreateCapsule(int radius, int height)
{ {
int textureWidth = radius * 2, textureHeight = height + radius * 2; int textureWidth = Math.Max(radius * 2, 1);
int textureHeight = Math.Max(height + radius * 2, 1);
Color[] data = new Color[textureWidth * textureHeight]; Color[] data = new Color[textureWidth * textureHeight];
@@ -2079,7 +2117,7 @@ namespace Barotrauma
if (pauseMenuOpen) if (pauseMenuOpen)
{ {
Inventory.draggingItem = null; Inventory.DraggingItems.Clear();
Inventory.DraggingInventory = null; Inventory.DraggingInventory = null;
PauseMenu = new GUIFrame(new RectTransform(Vector2.One, Canvas, Anchor.Center), style: null); PauseMenu = new GUIFrame(new RectTransform(Vector2.One, Canvas, Anchor.Center), style: null);
@@ -2288,9 +2326,10 @@ namespace Barotrauma
if (playSound) SoundPlayer.PlayUISound(GUISoundType.UIMessage); if (playSound) SoundPlayer.PlayUISound(GUISoundType.UIMessage);
} }
public static void AddMessage(string message, Color color, Vector2 worldPos, Vector2 velocity, float lifeTime = 3.0f, bool playSound = true, GUISoundType soundType = GUISoundType.UIMessage) public static void AddMessage(string message, Color color, Vector2 pos, Vector2 velocity, float lifeTime = 3.0f, bool playSound = true, GUISoundType soundType = GUISoundType.UIMessage, int subId = -1)
{ {
messages.Add(new GUIMessage(message, color, worldPos, velocity, lifeTime, Alignment.Center, LargeFont)); Submarine sub = Submarine.Loaded.FirstOrDefault(s => s.ID == subId);
messages.Add(new GUIMessage(message, color, pos, velocity, lifeTime, Alignment.Center, LargeFont, sub: sub));
if (playSound) SoundPlayer.PlayUISound(soundType); if (playSound) SoundPlayer.PlayUISound(soundType);
} }
@@ -0,0 +1,210 @@
#nullable enable
using System;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
namespace Barotrauma
{
public class GUIColorPicker : GUIComponent
{
public delegate bool OnColorSelectedHandler(GUIColorPicker component, Color color);
public OnColorSelectedHandler? OnColorSelected;
public float SelectedHue;
public float SelectedSaturation;
public float SelectedValue;
public Color CurrentColor = Color.Black;
private Rectangle MainArea,
HueArea;
private Texture2D? mainTexture,
hueTexture;
private Color[]? colorData;
private Rectangle selectedRect;
private bool mouseHeld;
private bool isInitialized;
private readonly Color transparentWhite = Color.White * 0.8f,
transparentBlack = Color.Black * 0.8f;
public GUIColorPicker(RectTransform rectT, string? style = null) : base(style, rectT) { }
~GUIColorPicker()
{
DisposeTextures();
}
private void Init()
{
int tWidth = Rect.Width;
int sliceWidth = Rect.Width / 8;
int mainWidth = tWidth - sliceWidth;
int hueWidth = sliceWidth;
MainArea = new Rectangle(0, 0, mainWidth, Rect.Height);
HueArea = new Rectangle(mainWidth, 0, hueWidth, Rect.Height);
colorData = new Color[MainArea.Width * MainArea.Height];
if (mainTexture == null)
{
int width = MainArea.Width,
height = MainArea.Height;
GenerateGradient(ref colorData!, width, height, DrawHVArea);
mainTexture = CreateGradientTexture(colorData!, MainArea.Width, MainArea.Height);
}
if (hueTexture == null)
{
int width = HueArea.Width,
height = HueArea.Height;
Color[] hueData = new Color[width * height];
GenerateGradient(ref hueData, width, height, DrawHueArea);
hueTexture = CreateGradientTexture(hueData, width, height);
}
}
protected override void Draw(SpriteBatch spriteBatch)
{
if (mainTexture == null || hueTexture == null || !isInitialized) { return; }
Rectangle mainArea = MainArea,
hueArea = HueArea;
hueArea.Location += Rect.Location;
mainArea.Location += Rect.Location;
Vector2 mainLocation = mainArea.Location.ToVector2(),
hueLocation = hueArea.Location.ToVector2();
spriteBatch.Draw(mainTexture, mainLocation, Color.White);
spriteBatch.Draw(hueTexture, hueLocation, Color.White);
float hueY = hueLocation.Y + ((SelectedHue / 360f) * hueArea.Height);
spriteBatch.DrawLine(hueArea.Left, hueY, hueArea.Right, hueY, transparentWhite, thickness: 3);
spriteBatch.DrawLine(hueArea.Left, hueY, hueArea.Right, hueY, transparentBlack, thickness: 1);
float saturationX = mainLocation.X + SelectedSaturation * MainArea.Width;
float valueY = mainLocation.Y + (1.0f - SelectedValue) * MainArea.Height;
spriteBatch.DrawLine(saturationX, mainArea.Top,saturationX, mainArea.Bottom, transparentWhite, thickness: 3);
spriteBatch.DrawLine(mainArea.Left,valueY, mainArea.Right, valueY, transparentWhite, thickness: 3);
spriteBatch.DrawLine(saturationX, mainArea.Top,saturationX, mainArea.Bottom, transparentBlack, thickness: 1);
spriteBatch.DrawLine(mainArea.Left,valueY, mainArea.Right, valueY, transparentBlack, thickness: 1);
}
protected override void Update(float deltaTime)
{
base.Update(deltaTime);
if (!isInitialized)
{
Init();
isInitialized = true;
}
if (!PlayerInput.PrimaryMouseButtonHeld())
{
mouseHeld = false;
}
if (GUI.MouseOn != this) { return; }
Rectangle mainArea = MainArea,
hueArea = HueArea;
hueArea.Location += Rect.Location;
mainArea.Location += Rect.Location;
if (PlayerInput.PrimaryMouseButtonDown())
{
mouseHeld = true;
if (hueArea.Contains(PlayerInput.MousePosition))
{
selectedRect = HueArea;
}
else if (mainArea.Contains(PlayerInput.MousePosition))
{
selectedRect = MainArea;
}
else
{
mouseHeld = false;
}
}
if (!PlayerInput.PrimaryMouseButtonHeld())
{
mouseHeld = false;
}
if (mouseHeld && (PlayerInput.MouseSpeed != Vector2.Zero || PlayerInput.PrimaryMouseButtonDown()))
{
if (selectedRect == HueArea)
{
Vector2 pos = PlayerInput.MousePosition - hueArea.Location.ToVector2();
SelectedHue = Math.Clamp(pos.Y / hueArea.Height * 360f, 0, 360);
RefreshHue();
}
else if (selectedRect == MainArea)
{
var (x, y) = PlayerInput.MousePosition - mainArea.Location.ToVector2();
SelectedSaturation = Math.Clamp(x / mainArea.Width, 0, 1);
SelectedValue = Math.Clamp(1f - (y / mainArea.Height), 0, 1);
}
CurrentColor = ToolBox.HSVToRGB(SelectedHue, SelectedSaturation, SelectedValue);
OnColorSelected?.Invoke(this, CurrentColor);
}
}
public void DisposeTextures()
{
mainTexture?.Dispose();
hueTexture?.Dispose();
}
public void RefreshHue()
{
if (colorData == null || mainTexture == null) { return; }
GenerateGradient(ref colorData, mainTexture.Width, mainTexture.Height, DrawHVArea);
mainTexture.SetData(colorData);
}
private Texture2D CreateGradientTexture(Color[] data, int width, int height)
{
Texture2D texture = new Texture2D(GameMain.GraphicsDeviceManager.GraphicsDevice, width, height);
texture.SetData(data);
return texture;
}
private void GenerateGradient(ref Color[] data, int width, int height, Func<float, float, Color> algorithm)
{
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
float relativeX = x / (float) width,
relativeY = y / (float) height;
data[y * width + x] = algorithm(relativeX, relativeY);
}
}
}
private Color DrawHVArea(float x, float y) => ToolBox.HSVToRGB(SelectedHue, x, 1.0f - y);
private Color DrawHueArea(float x, float y) => ToolBox.HSVToRGB(y * 360f, 1f, 1f);
}
}
@@ -510,10 +510,10 @@ namespace Barotrauma
for (int i = 0; i < Content.CountChildren; i++) for (int i = 0; i < Content.CountChildren; i++)
{ {
var child = Content.RectTransform.GetChild(i)?.GUIComponent; var child = Content.RectTransform.GetChild(i)?.GUIComponent;
if (child == null) continue; if (child == null || !child.Visible) { continue; }
// selecting // selecting
if (Enabled && CanBeFocused && child.CanBeFocused && (GUI.IsMouseOn(child)) && child.Rect.Contains(PlayerInput.MousePosition)) if (Enabled && CanBeFocused && child.CanBeFocused && child.Rect.Contains(PlayerInput.MousePosition) && GUI.IsMouseOn(child))
{ {
child.State = ComponentState.Hover; child.State = ComponentState.Hover;
@@ -55,6 +55,20 @@ namespace Barotrauma
private set; private set;
} }
public Submarine Submarine
{
get;
private set;
}
public Vector2 DrawPos
{
get
{
return Submarine == null ? Pos : Pos + Submarine.DrawPosition;
}
}
public GUIMessage(string text, Color color, float lifeTime, ScalableFont font = null) public GUIMessage(string text, Color color, float lifeTime, ScalableFont font = null)
{ {
coloredText = new ColoredText(text, color, false, false); coloredText = new ColoredText(text, color, false, false);
@@ -67,11 +81,11 @@ namespace Barotrauma
Font = font; Font = font;
} }
public GUIMessage(string text, Color color, Vector2 worldPosition, Vector2 velocity, float lifeTime, Alignment textAlignment = Alignment.Center, ScalableFont font = null) public GUIMessage(string text, Color color, Vector2 position, Vector2 velocity, float lifeTime, Alignment textAlignment = Alignment.Center, ScalableFont font = null, Submarine sub = null)
{ {
coloredText = new ColoredText(text, color, false, false); coloredText = new ColoredText(text, color, false, false);
WorldSpace = true; WorldSpace = true;
pos = worldPosition; pos = position;
Timer = lifeTime; Timer = lifeTime;
Velocity = velocity; Velocity = velocity;
this.lifeTime = lifeTime; this.lifeTime = lifeTime;
@@ -92,6 +106,8 @@ namespace Barotrauma
if (textAlignment.HasFlag(Alignment.Bottom)) if (textAlignment.HasFlag(Alignment.Bottom))
Origin.Y += size.Y * 0.5f; Origin.Y += size.Y * 0.5f;
Submarine = sub;
} }
} }
} }
@@ -67,6 +67,8 @@ namespace Barotrauma
private readonly Type type; private readonly Type type;
public Type MessageBoxType => type;
public static GUIComponent VisibleBox => MessageBoxes.LastOrDefault(); public static GUIComponent VisibleBox => MessageBoxes.LastOrDefault();
public GUIMessageBox(string headerText, string text, Vector2? relativeSize = null, Point? minSize = null) public GUIMessageBox(string headerText, string text, Vector2? relativeSize = null, Point? minSize = null)
@@ -210,6 +212,29 @@ namespace Barotrauma
} }
}; };
InputType? closeInput = null;
if (GameMain.Config.KeyBind(InputType.Use).MouseButton == MouseButton.None)
{
closeInput = InputType.Use;
}
else if (GameMain.Config.KeyBind(InputType.Select).MouseButton == MouseButton.None)
{
closeInput = InputType.Select;
}
if (closeInput.HasValue)
{
Buttons[0].ToolTip = TextManager.ParseInputTypes($"{TextManager.Get("Close")} ([InputType.{closeInput.Value}])");
Buttons[0].OnAddedToGUIUpdateList += (GUIComponent component) =>
{
if (!closing && openState >= 1.0f && PlayerInput.KeyHit(closeInput.Value))
{
GUIButton btn = component as GUIButton;
btn?.OnClicked(btn, btn.UserData);
btn?.Flash(GUI.Style.Green);
}
};
}
Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), headerText, wrap: true); Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), headerText, wrap: true);
GUI.Style.Apply(Header, "", this); GUI.Style.Apply(Header, "", this);
Header.RectTransform.MinSize = new Point(0, Header.Rect.Height); Header.RectTransform.MinSize = new Point(0, Header.Rect.Height);
@@ -291,7 +316,8 @@ namespace Barotrauma
{ {
if (Draggable) if (Draggable)
{ {
if ((GUI.MouseOn == InnerFrame || InnerFrame.IsParentOf(GUI.MouseOn)) && !(GUI.MouseOn is GUIButton)) GUIComponent parent = GUI.MouseOn?.Parent?.Parent;
if ((GUI.MouseOn == InnerFrame || InnerFrame.IsParentOf(GUI.MouseOn)) && !(GUI.MouseOn is GUIButton || GUI.MouseOn is GUIColorPicker || GUI.MouseOn is GUITextBox || parent is GUITextBox))
{ {
GUI.MouseCursor = CursorState.Move; GUI.MouseCursor = CursorState.Move;
if (PlayerInput.PrimaryMouseButtonDown()) if (PlayerInput.PrimaryMouseButtonDown())
@@ -70,6 +70,7 @@ namespace Barotrauma
public Color ColorInventoryHalf { get; private set; } = Color.Orange; public Color ColorInventoryHalf { get; private set; } = Color.Orange;
public Color ColorInventoryFull { get; private set; } = Color.LightGreen; public Color ColorInventoryFull { get; private set; } = Color.LightGreen;
public Color ColorInventoryBackground { get; private set; } = Color.Gray; public Color ColorInventoryBackground { get; private set; } = Color.Gray;
public Color ColorInventoryEmptyOverlay { get; private set; } = Color.Red;
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;
@@ -150,6 +151,9 @@ namespace Barotrauma
case "colorinventorybackground": case "colorinventorybackground":
ColorInventoryBackground = subElement.GetAttributeColor("color", ColorInventoryBackground); ColorInventoryBackground = subElement.GetAttributeColor("color", ColorInventoryBackground);
break; break;
case "colorinventoryemptyoverlay":
ColorInventoryEmptyOverlay = subElement.GetAttributeColor("color", ColorInventoryEmptyOverlay);
break;
case "textcolordark": case "textcolordark":
TextColorDark = subElement.GetAttributeColor("color", TextColorDark); TextColorDark = subElement.GetAttributeColor("color", TextColorDark);
break; break;
@@ -344,7 +348,7 @@ namespace Barotrauma
if (GameMain.Config.Language.Equals(subElement.GetAttributeString("language", ""), StringComparison.OrdinalIgnoreCase)) if (GameMain.Config.Language.Equals(subElement.GetAttributeString("language", ""), StringComparison.OrdinalIgnoreCase))
{ {
uint overrideFontSize = GetFontSize(subElement, 0); uint overrideFontSize = GetFontSize(subElement, 0);
if (overrideFontSize > 0) { return overrideFontSize; } if (overrideFontSize > 0) { return (uint)Math.Round(overrideFontSize * GameSettings.TextScale); }
} }
} }
@@ -354,10 +358,10 @@ namespace Barotrauma
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)
{ {
return (uint)subElement.GetAttributeInt("size", 14); return (uint)Math.Round(subElement.GetAttributeInt("size", 14) * GameSettings.TextScale);
} }
} }
return defaultSize; return (uint)Math.Round(defaultSize * GameSettings.TextScale);
} }
private string GetFontFilePath(XElement element) private string GetFontFilePath(XElement element)
@@ -240,20 +240,20 @@ namespace Barotrauma
public class StrikethroughSettings public class StrikethroughSettings
{ {
private Color color = GUI.Style.Red; public Color Color { get; set; } = GUI.Style.Red;
private int thickness; private int thickness;
private int expand; private int expand;
public StrikethroughSettings(Color? color = null, int thickness = 1, int expand = 0) public StrikethroughSettings(Color? color = null, int thickness = 1, int expand = 0)
{ {
if (color != null) this.color = color.Value; if (color != null) { Color = color.Value; }
this.thickness = thickness; this.thickness = thickness;
this.expand = expand; this.expand = expand;
} }
public void Draw(SpriteBatch spriteBatch, float textSizeHalf, float xPos, float yPos) public void Draw(SpriteBatch spriteBatch, float textSizeHalf, float xPos, float yPos)
{ {
ShapeExtensions.DrawLine(spriteBatch, new Vector2(xPos - textSizeHalf - expand, yPos), new Vector2(xPos + textSizeHalf + expand, yPos), color, thickness); ShapeExtensions.DrawLine(spriteBatch, new Vector2(xPos - textSizeHalf - expand, yPos), new Vector2(xPos + textSizeHalf + expand, yPos), Color, thickness);
} }
} }
@@ -251,7 +251,7 @@ namespace Barotrauma
public bool Readonly { get; set; } public bool Readonly { get; set; }
public GUITextBox(RectTransform rectT, string text = "", Color? textColor = null, ScalableFont font = null, public GUITextBox(RectTransform rectT, string text = "", Color? textColor = null, ScalableFont font = null,
Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null, bool createClearButton = false) Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null, bool createClearButton = false, bool createPenIcon = true)
: base(style, rectT) : base(style, rectT)
{ {
HoverCursor = CursorState.IBeam; HoverCursor = CursorState.IBeam;
@@ -283,7 +283,7 @@ namespace Barotrauma
clearButtonWidth = (int)(clearButton.Rect.Width * 1.2f); clearButtonWidth = (int)(clearButton.Rect.Width * 1.2f);
} }
if (this.style != null && this.style.ChildStyles.ContainsKey("textboxicon")) if (this.style != null && this.style.ChildStyles.ContainsKey("textboxicon") && createPenIcon)
{ {
icon = new GUIImage(new RectTransform(new Vector2(0.6f, 0.6f), frame.RectTransform, Anchor.CenterRight, scaleBasis: ScaleBasis.BothHeight) { AbsoluteOffset = new Point(5 + clearButtonWidth, 0) }, null, scaleToFit: true); icon = new GUIImage(new RectTransform(new Vector2(0.6f, 0.6f), frame.RectTransform, Anchor.CenterRight, scaleBasis: ScaleBasis.BothHeight) { AbsoluteOffset = new Point(5 + clearButtonWidth, 0) }, null, scaleToFit: true);
icon.ApplyStyle(this.style.ChildStyles["textboxicon"]); icon.ApplyStyle(this.style.ChildStyles["textboxicon"]);
@@ -457,6 +457,11 @@ namespace Barotrauma
isSelecting = PlayerInput.KeyDown(Keys.LeftShift) || PlayerInput.KeyDown(Keys.RightShift); isSelecting = PlayerInput.KeyDown(Keys.LeftShift) || PlayerInput.KeyDown(Keys.RightShift);
} }
if (mouseHeldInside && !PlayerInput.PrimaryMouseButtonHeld())
{
mouseHeldInside = false;
}
if (CaretEnabled) if (CaretEnabled)
{ {
if (textBlock.OverflowClipActive) if (textBlock.OverflowClipActive)
@@ -24,9 +24,17 @@ namespace Barotrauma
private int buyTotal, sellTotal; private int buyTotal, sellTotal;
private GUITextBlock merchantBalanceBlock; private GUITextBlock merchantBalanceBlock;
private GUILayoutGroup valueChangeGroup;
private GUITextBlock currentSellValueBlock, newSellValueBlock;
private GUIImage sellValueChangeArrow;
private GUIDropDown sortingDropDown; private GUIDropDown sortingDropDown;
private GUITextBox searchBox; private GUITextBox searchBox;
private GUIListBox storeDealsList, storeBuyList, storeSellList; private GUIListBox storeBuyList, storeSellList;
/// <summary>
/// Can be null when there are no deals at the current location
/// </summary>
private GUILayoutGroup storeDailySpecialsGroup, storeRequestedGoodGroup;
private Color storeSpecialColor;
private GUIListBox shoppingCrateBuyList, shoppingCrateSellList; private GUIListBox shoppingCrateBuyList, shoppingCrateSellList;
private GUITextBlock shoppingCrateTotal; private GUITextBlock shoppingCrateTotal;
@@ -49,7 +57,6 @@ namespace Barotrauma
private enum StoreTab private enum StoreTab
{ {
Deals,
Buy, Buy,
Sell Sell
} }
@@ -78,19 +85,19 @@ namespace Barotrauma
CurrentLocation.Reputation.OnReputationValueChanged += () => { needsRefresh = true; }; CurrentLocation.Reputation.OnReputationValueChanged += () => { needsRefresh = true; };
} }
campaignUI.Campaign.CargoManager.OnItemsInBuyCrateChanged += () => { needsBuyingRefresh = true; }; campaignUI.Campaign.CargoManager.OnItemsInBuyCrateChanged += () => { needsBuyingRefresh = true; };
campaignUI.Campaign.CargoManager.OnPurchasedItemsChanged += () => { needsBuyingRefresh = true; }; campaignUI.Campaign.CargoManager.OnPurchasedItemsChanged += () => { needsRefresh = true; };
campaignUI.Campaign.CargoManager.OnItemsInSellCrateChanged += () => { needsSellingRefresh = true; }; campaignUI.Campaign.CargoManager.OnItemsInSellCrateChanged += () => { needsSellingRefresh = true; };
campaignUI.Campaign.CargoManager.OnSoldItemsChanged += () => campaignUI.Campaign.CargoManager.OnSoldItemsChanged += () =>
{ {
needsItemsToSellRefresh = true; needsItemsToSellRefresh = true;
needsSellingRefresh = true; needsRefresh = true;
}; };
} }
public void Refresh() public void Refresh(bool updateOwned = true)
{ {
hadPermissions = HasPermissions; hadPermissions = HasPermissions;
UpdateOwnedItems(); if (updateOwned) { UpdateOwnedItems(); }
RefreshBuying(updateOwned: false); RefreshBuying(updateOwned: false);
RefreshSelling(updateOwned: false); RefreshSelling(updateOwned: false);
needsRefresh = false; needsRefresh = false;
@@ -100,10 +107,8 @@ namespace Barotrauma
{ {
if (updateOwned) { UpdateOwnedItems(); } if (updateOwned) { UpdateOwnedItems(); }
RefreshShoppingCrateBuyList(); RefreshShoppingCrateBuyList();
//RefreshStoreDealsList();
RefreshStoreBuyList(); RefreshStoreBuyList();
var hasPermissions = HasPermissions; var hasPermissions = HasPermissions;
//storeDealsList.Enabled = hasPermissions;
storeBuyList.Enabled = hasPermissions; storeBuyList.Enabled = hasPermissions;
shoppingCrateBuyList.Enabled = hasPermissions; shoppingCrateBuyList.Enabled = hasPermissions;
needsBuyingRefresh = false; needsBuyingRefresh = false;
@@ -166,7 +171,12 @@ namespace Barotrauma
}; };
// Merchant balance ------------------------------------------------ // Merchant balance ------------------------------------------------
var merchantBalanceContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.75f / 14.0f), storeContent.RectTransform)) var balanceAndValueGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.75f / 14.0f), storeContent.RectTransform), isHorizontal: true)
{
RelativeSpacing = 0.005f
};
var merchantBalanceContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), balanceAndValueGroup.RectTransform))
{ {
RelativeSpacing = 0.005f RelativeSpacing = 0.005f
}; };
@@ -177,29 +187,111 @@ namespace Barotrauma
ForceUpperCase = true ForceUpperCase = true
}; };
merchantBalanceBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), merchantBalanceContainer.RectTransform), merchantBalanceBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), merchantBalanceContainer.RectTransform),
"", font: GUI.SubHeadingFont, textAlignment: Alignment.TopLeft) "", font: GUI.SubHeadingFont)
{ {
AutoScaleVertical = true, AutoScaleVertical = true,
TextScale = 1.1f, TextScale = 1.1f,
TextGetter = () => TextGetter = () =>
{ {
var balance = CurrentLocation != null ? CurrentLocation.StoreCurrentBalance : 0; if (CurrentLocation != null)
if (balance < (int)(0.25f * Location.StoreInitialBalance))
{ {
merchantBalanceBlock.TextColor = Color.Red; merchantBalanceBlock.TextColor = CurrentLocation.BalanceColor;
} return GetCurrencyFormatted(CurrentLocation.StoreCurrentBalance);
else if (balance < (int)(0.5f * Location.StoreInitialBalance))
{
merchantBalanceBlock.TextColor = Color.Orange;
} }
else else
{ {
merchantBalanceBlock.TextColor = Color.White; merchantBalanceBlock.TextColor = Color.Red;
return GetCurrencyFormatted(0);
} }
return GetCurrencyFormatted(balance);
} }
}; };
// Item sell value ------------------------------------------------
var sellValueContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), balanceAndValueGroup.RectTransform))
{
CanBeFocused = false,
RelativeSpacing = 0.005f
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), sellValueContainer.RectTransform),
TextManager.Get("campaignstore.sellvalue"), font: GUI.Font, textAlignment: Alignment.BottomLeft)
{
AutoScaleVertical = true,
CanBeFocused = false,
ForceUpperCase = true
};
valueChangeGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), sellValueContainer.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
CanBeFocused = true,
RelativeSpacing = 0.02f
};
float blockWidth = GUI.IsFourByThree() ? 0.32f : 0.28f;
Point blockMaxSize = new Point((int)(GameSettings.TextScale * 60), valueChangeGroup.Rect.Height);
currentSellValueBlock = new GUITextBlock(new RectTransform(new Vector2(blockWidth, 1.0f), valueChangeGroup.RectTransform) { MaxSize = blockMaxSize },
"", font: GUI.SubHeadingFont)
{
AutoScaleVertical = true,
CanBeFocused = false,
TextScale = 1.1f,
TextGetter = () =>
{
if (CurrentLocation != null)
{
int balanceAfterTransaction = IsBuying ?
CurrentLocation.StoreCurrentBalance + buyTotal :
CurrentLocation.StoreCurrentBalance - sellTotal;
if (balanceAfterTransaction != CurrentLocation.StoreCurrentBalance)
{
var newStatus = Location.GetStoreBalanceStatus(balanceAfterTransaction);
if (CurrentLocation.ActiveStoreBalanceStatus.SellPriceModifier != newStatus.SellPriceModifier)
{
string tooltipTag = newStatus.SellPriceModifier > CurrentLocation.ActiveStoreBalanceStatus.SellPriceModifier ?
"campaingstore.valueincreasetooltip" : "campaingstore.valuedecreasetooltip";
valueChangeGroup.ToolTip = TextManager.Get(tooltipTag);
currentSellValueBlock.TextColor = newStatus.Color;
sellValueChangeArrow.Color = newStatus.Color;
sellValueChangeArrow.Visible = true;
newSellValueBlock.TextColor = newStatus.Color;
newSellValueBlock.Text = $"{(newStatus.SellPriceModifier * 100).FormatZeroDecimal()} %";
return $"{(CurrentLocation.ActiveStoreBalanceStatus.SellPriceModifier * 100).FormatZeroDecimal()} %";
}
}
valueChangeGroup.ToolTip = null;
currentSellValueBlock.TextColor = CurrentLocation.BalanceColor;
sellValueChangeArrow.Visible = false;
newSellValueBlock.Text = null;
return $"{(CurrentLocation.ActiveStoreBalanceStatus.SellPriceModifier * 100).FormatZeroDecimal()} %";
}
else
{
valueChangeGroup.ToolTip = null;
sellValueChangeArrow.Visible = false;
newSellValueBlock.Text = null;
return null;
}
}
};
Vector4 newPadding = currentSellValueBlock.Padding;
newPadding.Z = 0;
currentSellValueBlock.Padding = newPadding;
float relativeHeight = 0.45f;
float relativeWidth = (relativeHeight * valueChangeGroup.Rect.Height) / valueChangeGroup.Rect.Width;
sellValueChangeArrow = new GUIImage(new RectTransform(new Vector2(relativeWidth, relativeHeight), valueChangeGroup.RectTransform), "StoreArrow", scaleToFit: true)
{
CanBeFocused = false,
Visible = false
};
newSellValueBlock = new GUITextBlock(new RectTransform(new Vector2(blockWidth, 1.0f), valueChangeGroup.RectTransform) { MaxSize = blockMaxSize },
"", font: GUI.SubHeadingFont)
{
AutoScaleVertical = true,
CanBeFocused = false,
TextScale = 1.1f
};
newPadding = newSellValueBlock.Padding;
newPadding.X = 0;
newSellValueBlock.Padding = newPadding;
// Store mode buttons ------------------------------------------------ // Store mode buttons ------------------------------------------------
var modeButtonFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.6f / 14.0f), storeContent.RectTransform), style: null); var modeButtonFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.6f / 14.0f), storeContent.RectTransform), style: null);
var modeButtonContainer = new GUILayoutGroup(new RectTransform(Vector2.One, modeButtonFrame.RectTransform), isHorizontal: true); var modeButtonContainer = new GUILayoutGroup(new RectTransform(Vector2.One, modeButtonFrame.RectTransform), isHorizontal: true);
@@ -209,8 +301,6 @@ namespace Barotrauma
tabSortingMethods.Clear(); tabSortingMethods.Clear();
foreach (StoreTab tab in tabs) foreach (StoreTab tab in tabs)
{ {
// TODO: Remove the row below once the deal page is implemented
if (tab == StoreTab.Deals) { continue; }
var tabButton = new GUIButton(new RectTransform(new Vector2(1.0f / (tabs.Length + 1), 1.0f), modeButtonContainer.RectTransform), var tabButton = new GUIButton(new RectTransform(new Vector2(1.0f / (tabs.Length + 1), 1.0f), modeButtonContainer.RectTransform),
text: TextManager.Get("campaignstoretab." + tab), style: "GUITabButton") text: TextManager.Get("campaignstoretab." + tab), style: "GUITabButton")
{ {
@@ -309,24 +399,22 @@ namespace Barotrauma
searchBox.OnTextChanged += (textBox, text) => { FilterStoreItems(null, text); return true; }; searchBox.OnTextChanged += (textBox, text) => { FilterStoreItems(null, text); return true; };
var storeItemListContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.92f), sortFilterListContainer.RectTransform), style: null); var storeItemListContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.92f), sortFilterListContainer.RectTransform), style: null);
storeDealsList = new GUIListBox(new RectTransform(Vector2.One, storeItemListContainer.RectTransform))
{
AutoHideScrollBar = false,
Visible = false
};
tabLists.Clear(); tabLists.Clear();
tabLists.Add(StoreTab.Deals, storeDealsList);
storeBuyList = new GUIListBox(new RectTransform(Vector2.One, storeItemListContainer.RectTransform)) storeBuyList = new GUIListBox(new RectTransform(Vector2.One, storeItemListContainer.RectTransform))
{ {
AutoHideScrollBar = false, AutoHideScrollBar = false,
Visible = false Visible = false
}; };
storeDailySpecialsGroup = CreateDealsGroup(storeBuyList);
tabLists.Add(StoreTab.Buy, storeBuyList); tabLists.Add(StoreTab.Buy, storeBuyList);
storeSellList = new GUIListBox(new RectTransform(Vector2.One, storeItemListContainer.RectTransform)) storeSellList = new GUIListBox(new RectTransform(Vector2.One, storeItemListContainer.RectTransform))
{ {
AutoHideScrollBar = false, AutoHideScrollBar = false,
Visible = false Visible = false
}; };
storeRequestedGoodGroup = CreateDealsGroup(storeSellList);
tabLists.Add(StoreTab.Sell, storeSellList); tabLists.Add(StoreTab.Sell, storeSellList);
// Shopping Crate ------------------------------------------------------------------------------------------------------------------------------------------ // Shopping Crate ------------------------------------------------------------------------------------------------------------------------------------------
@@ -428,6 +516,26 @@ namespace Barotrauma
resolutionWhenCreated = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight); resolutionWhenCreated = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
} }
private GUILayoutGroup CreateDealsGroup(GUIListBox parentList)
{
var elementHeight = (int)(GUI.yScale * 80);
var frame = new GUIFrame(new RectTransform(new Point(parentList.Content.Rect.Width, 4 * elementHeight + 3), parent: parentList.Content.RectTransform), style: null);
frame.UserData = "deals";
var dealsGroup = new GUILayoutGroup(new RectTransform(Vector2.One, frame.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter);
var dealsHeader = new GUILayoutGroup(new RectTransform(new Point((int)(0.95f * parentList.Content.Rect.Width), elementHeight), parent: dealsGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
dealsHeader.UserData = "header";
var iconWidth = (0.9f * dealsHeader.Rect.Height) / dealsHeader.Rect.Width;
var dealsIcon = new GUIImage(new RectTransform(new Vector2(iconWidth, 0.9f), dealsHeader.RectTransform), "StoreDealIcon", scaleToFit: true);
var text = TextManager.Get(parentList == storeBuyList ? "campaignstore.dailyspecials" : "campaignstore.requestedgoods");
var dealsText = new GUITextBlock(new RectTransform(new Vector2(1.0f - iconWidth, 0.9f), dealsHeader.RectTransform), text, font: GUI.LargeFont);
storeSpecialColor = dealsIcon.Color;
dealsText.TextColor = storeSpecialColor;
var divider = new GUIImage(new RectTransform(new Point(dealsGroup.Rect.Width, 3), dealsGroup.RectTransform), "HorizontalLine");
divider.UserData = "divider";
frame.CanBeFocused = dealsGroup.CanBeFocused = dealsHeader.CanBeFocused = dealsIcon.CanBeFocused = dealsText.CanBeFocused = divider.CanBeFocused = false;
return dealsGroup;
}
private void UpdateLocation(Location prevLocation, Location newLocation) private void UpdateLocation(Location prevLocation, Location newLocation)
{ {
if (prevLocation == newLocation) { return; } if (prevLocation == newLocation) { return; }
@@ -464,17 +572,8 @@ namespace Barotrauma
SetConfirmButtonBehavior(); SetConfirmButtonBehavior();
SetConfirmButtonStatus(); SetConfirmButtonStatus();
FilterStoreItems(); FilterStoreItems();
if (tab == StoreTab.Deals) if (tab == StoreTab.Buy)
{ {
storeBuyList.Visible = false;
storeSellList.Visible = false;
storeDealsList.Visible = true;
shoppingCrateSellList.Visible = false;
shoppingCrateBuyList.Visible = true;
}
else if (tab == StoreTab.Buy)
{
storeDealsList.Visible = false;
storeSellList.Visible = false; storeSellList.Visible = false;
storeBuyList.Visible = true; storeBuyList.Visible = true;
shoppingCrateSellList.Visible = false; shoppingCrateSellList.Visible = false;
@@ -482,7 +581,6 @@ namespace Barotrauma
} }
else if (tab == StoreTab.Sell) else if (tab == StoreTab.Sell)
{ {
storeDealsList.Visible = false;
storeBuyList.Visible = false; storeBuyList.Visible = false;
storeSellList.Visible = true; storeSellList.Visible = true;
shoppingCrateBuyList.Visible = false; shoppingCrateBuyList.Visible = false;
@@ -525,37 +623,71 @@ namespace Barotrauma
bool hasPermissions = HasPermissions; bool hasPermissions = HasPermissions;
HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>(); HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>();
if ((storeDailySpecialsGroup != null) != CurrentLocation.DailySpecials.Any())
{
if (storeDailySpecialsGroup == null)
{
storeDailySpecialsGroup = CreateDealsGroup(storeBuyList);
storeDailySpecialsGroup.Parent.SetAsFirstChild();
}
else
{
storeBuyList.RemoveChild(storeDailySpecialsGroup.Parent);
storeDailySpecialsGroup = null;
}
storeBuyList.RecalculateChildren();
}
foreach (PurchasedItem item in CurrentLocation.StoreStock) foreach (PurchasedItem item in CurrentLocation.StoreStock)
{ {
if (item.ItemPrefab.CanBeBoughtAtLocation(CurrentLocation, out PriceInfo priceInfo)) CreateOrUpdateItemFrame(item.ItemPrefab, item.Quantity);
}
foreach (ItemPrefab itemPrefab in CurrentLocation.DailySpecials)
{
if (CurrentLocation.StoreStock.Any(pi => pi.ItemPrefab == itemPrefab)) { continue; }
CreateOrUpdateItemFrame(itemPrefab, 0);
}
void CreateOrUpdateItemFrame(ItemPrefab itemPrefab, int quantity)
{
if (itemPrefab.CanBeBoughtAtLocation(CurrentLocation, out PriceInfo priceInfo))
{ {
var itemFrame = storeBuyList.Content.Children.FirstOrDefault(c => c.UserData is PurchasedItem pi && pi.ItemPrefab == item.ItemPrefab); var isDailySpecial = CurrentLocation.DailySpecials.Contains(itemPrefab);
var quantity = item.Quantity; var itemFrame = isDailySpecial ?
if (CargoManager.PurchasedItems.Find(i => i.ItemPrefab == item.ItemPrefab) is PurchasedItem purchasedItem) storeDailySpecialsGroup.FindChild(c => c.UserData is PurchasedItem pi && pi.ItemPrefab == itemPrefab) :
storeBuyList.Content.FindChild(c => c.UserData is PurchasedItem pi && pi.ItemPrefab == itemPrefab);
if (CargoManager.PurchasedItems.Find(i => i.ItemPrefab == itemPrefab) is PurchasedItem purchasedItem)
{ {
quantity = Math.Max(quantity - purchasedItem.Quantity, 0); quantity = Math.Max(quantity - purchasedItem.Quantity, 0);
} }
if (CargoManager.ItemsInBuyCrate.Find(i => i.ItemPrefab == item.ItemPrefab) is PurchasedItem itemInBuyCrate) if (CargoManager.ItemsInBuyCrate.Find(i => i.ItemPrefab == itemPrefab) is PurchasedItem itemInBuyCrate)
{ {
quantity = Math.Max(quantity - itemInBuyCrate.Quantity, 0); quantity = Math.Max(quantity - itemInBuyCrate.Quantity, 0);
} }
if (itemFrame == null) if (itemFrame == null)
{ {
itemFrame = CreateItemFrame(new PurchasedItem(item.ItemPrefab, quantity), priceInfo, storeBuyList, forceDisable: !hasPermissions); var parentComponent = isDailySpecial ? storeDailySpecialsGroup : storeBuyList as GUIComponent;
itemFrame = CreateItemFrame(new PurchasedItem(itemPrefab, quantity), priceInfo, parentComponent, forceDisable: !hasPermissions);
} }
else else
{ {
(itemFrame.UserData as PurchasedItem).Quantity = quantity; (itemFrame.UserData as PurchasedItem).Quantity = quantity;
SetQuantityLabelText(StoreTab.Buy, itemFrame); SetQuantityLabelText(StoreTab.Buy, itemFrame);
SetOwnedLabelText(itemFrame); SetOwnedLabelText(itemFrame);
SetItemFrameStatus(itemFrame, hasPermissions && quantity > 0);
} }
SetItemFrameStatus(itemFrame, hasPermissions && quantity > 0);
existingItemFrames.Add(itemFrame); existingItemFrames.Add(itemFrame);
} }
} }
var removedItemFrames = storeBuyList.Content.Children.Except(existingItemFrames).ToList(); var removedItemFrames = storeBuyList.Content.Children.Where(c => c.UserData is PurchasedItem).Except(existingItemFrames).ToList();
removedItemFrames.ForEach(f => storeBuyList.Content.RemoveChild(f)); if (storeDailySpecialsGroup != null)
{
removedItemFrames.AddRange(storeDailySpecialsGroup.Children.Where(c => c.UserData is PurchasedItem).Except(existingItemFrames).ToList());
}
removedItemFrames.ForEach(f => f.RectTransform.Parent = null);
if (IsBuying) { FilterStoreItems(); } if (IsBuying) { FilterStoreItems(); }
SortItems(StoreTab.Buy); SortItems(StoreTab.Buy);
@@ -567,36 +699,72 @@ namespace Barotrauma
{ {
float prevSellListScroll = storeSellList.BarScroll; float prevSellListScroll = storeSellList.BarScroll;
float prevShoppingCrateScroll = shoppingCrateSellList.BarScroll; float prevShoppingCrateScroll = shoppingCrateSellList.BarScroll;
bool hasPermissions = HasPermissions; bool hasPermissions = HasPermissions;
HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>(); HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>();
foreach (PurchasedItem item in itemsToSell)
if ((storeRequestedGoodGroup != null) != CurrentLocation.RequestedGoods.Any())
{ {
PriceInfo priceInfo = item.ItemPrefab.GetPriceInfo(CurrentLocation); if (storeRequestedGoodGroup == null)
if (priceInfo == null) { continue; }
var itemFrame = storeSellList.Content.FindChild(c => c.UserData is PurchasedItem i && i.ItemPrefab == item.ItemPrefab);
var quantity = item.Quantity;
if (CargoManager.ItemsInSellCrate.Find(i => i.ItemPrefab == item.ItemPrefab) is PurchasedItem itemInSellCrate)
{ {
quantity = Math.Max(quantity - itemInSellCrate.Quantity, 0); storeRequestedGoodGroup = CreateDealsGroup(storeSellList);
} storeRequestedGoodGroup.Parent.SetAsFirstChild();
if (itemFrame == null)
{
itemFrame = CreateItemFrame(new PurchasedItem(item.ItemPrefab, quantity), priceInfo, storeSellList, forceDisable: !hasPermissions);
} }
else else
{ {
(itemFrame.UserData as PurchasedItem).Quantity = quantity; storeSellList.RemoveChild(storeRequestedGoodGroup.Parent);
storeRequestedGoodGroup = null;
}
storeSellList.RecalculateChildren();
}
foreach (PurchasedItem item in itemsToSell)
{
CreateOrUpdateItemFrame(item.ItemPrefab, item.Quantity);
}
foreach (var requestedGood in CurrentLocation.RequestedGoods)
{
if (itemsToSell.Any(pi => pi.ItemPrefab == requestedGood)) { continue; }
CreateOrUpdateItemFrame(requestedGood, 0);
}
void CreateOrUpdateItemFrame(ItemPrefab itemPrefab, int itemQuantity)
{
PriceInfo priceInfo = itemPrefab.GetPriceInfo(CurrentLocation);
if (priceInfo == null) { return; }
var isRequestedGood = CurrentLocation.RequestedGoods.Contains(itemPrefab);
var itemFrame = isRequestedGood ?
storeRequestedGoodGroup.FindChild(c => c.UserData is PurchasedItem pi && pi.ItemPrefab == itemPrefab) :
storeSellList.Content.FindChild(c => c.UserData is PurchasedItem pi && pi.ItemPrefab == itemPrefab);
if (CargoManager.ItemsInSellCrate.Find(i => i.ItemPrefab == itemPrefab) is PurchasedItem itemInSellCrate)
{
itemQuantity = Math.Max(itemQuantity - itemInSellCrate.Quantity, 0);
}
if (itemFrame == null)
{
var parentComponent = isRequestedGood ? storeRequestedGoodGroup : storeSellList as GUIComponent;
itemFrame = CreateItemFrame(new PurchasedItem(itemPrefab, itemQuantity), priceInfo, parentComponent, forceDisable: !hasPermissions);
}
else
{
(itemFrame.UserData as PurchasedItem).Quantity = itemQuantity;
SetQuantityLabelText(StoreTab.Sell, itemFrame); SetQuantityLabelText(StoreTab.Sell, itemFrame);
SetOwnedLabelText(itemFrame); SetOwnedLabelText(itemFrame);
SetItemFrameStatus(itemFrame, hasPermissions);
} }
if (quantity < 1) { itemFrame.Visible = false; } SetItemFrameStatus(itemFrame, hasPermissions && itemQuantity > 0);
if (itemQuantity < 1 && !isRequestedGood)
{
itemFrame.Visible = false;
}
existingItemFrames.Add(itemFrame); existingItemFrames.Add(itemFrame);
} }
var removedItemFrames = storeSellList.Content.Children.Except(existingItemFrames).ToList(); var removedItemFrames = storeSellList.Content.Children.Where(c => c.UserData is PurchasedItem).Except(existingItemFrames).ToList();
removedItemFrames.ForEach(f => storeSellList.Content.RemoveChild(f)); if (storeRequestedGoodGroup != null)
{
removedItemFrames.AddRange(storeRequestedGoodGroup.Children.Where(c => c.UserData is PurchasedItem).Except(existingItemFrames).ToList());
}
removedItemFrames.ForEach(f => f.RectTransform.Parent = null);
if (IsSelling) { FilterStoreItems(); } if (IsSelling) { FilterStoreItems(); }
SortItems(StoreTab.Sell); SortItems(StoreTab.Sell);
@@ -673,13 +841,10 @@ namespace Barotrauma
} }
suppressBuySell = false; suppressBuySell = false;
if (priceInfo != null) var price = listBox == shoppingCrateBuyList ?
{ CurrentLocation.GetAdjustedItemBuyPrice(item.ItemPrefab, priceInfo: priceInfo) :
var price = listBox == shoppingCrateBuyList ? CurrentLocation.GetAdjustedItemSellPrice(item.ItemPrefab, priceInfo: priceInfo);
CurrentLocation.GetAdjustedItemBuyPrice(priceInfo) : totalPrice += item.Quantity * price;
CurrentLocation.GetAdjustedItemSellPrice(priceInfo);
totalPrice += item.Quantity * price;
}
} }
var removedItemFrames = listBox.Content.Children.Except(existingItemFrames).ToList(); var removedItemFrames = listBox.Content.Children.Except(existingItemFrames).ToList();
@@ -711,32 +876,138 @@ namespace Barotrauma
if (sortingMethod == SortingMethod.AlphabeticalAsc || sortingMethod == SortingMethod.AlphabeticalDesc) if (sortingMethod == SortingMethod.AlphabeticalAsc || sortingMethod == SortingMethod.AlphabeticalDesc)
{ {
list.Content.RectTransform.SortChildren( list.Content.RectTransform.SortChildren(CompareByName);
(x, y) => (x.GUIComponent.UserData as PurchasedItem).ItemPrefab.Name.CompareTo((y.GUIComponent.UserData as PurchasedItem).ItemPrefab.Name)); if (GetSpecialsGroup() is GUILayoutGroup specialsGroup)
if (sortingMethod == SortingMethod.AlphabeticalDesc) { list.Content.RectTransform.ReverseChildren(); } {
specialsGroup.RectTransform.SortChildren(CompareByName);
specialsGroup.Recalculate();
}
int CompareByName(RectTransform x, RectTransform y)
{
if (x.GUIComponent.UserData is PurchasedItem itemX && y.GUIComponent.UserData is PurchasedItem itemY)
{
var sortResult = itemX.ItemPrefab.Name.CompareTo(itemY.ItemPrefab.Name);
if (sortingMethod == SortingMethod.AlphabeticalDesc) { sortResult *= -1; }
return sortResult;
}
else
{
return CompareByElement(x, y);
}
}
} }
else if (sortingMethod == SortingMethod.PriceAsc || sortingMethod == SortingMethod.PriceDesc) else if (sortingMethod == SortingMethod.PriceAsc || sortingMethod == SortingMethod.PriceDesc)
{ {
SortItems(list, SortingMethod.AlphabeticalAsc); SortItems(list, SortingMethod.AlphabeticalAsc);
if (list == storeSellList || list == shoppingCrateSellList) if (list == storeSellList || list == shoppingCrateSellList)
{ {
list.Content.RectTransform.SortChildren( list.Content.RectTransform.SortChildren(CompareBySellPrice);
(x, y) => CurrentLocation.GetAdjustedItemSellPrice((x.GUIComponent.UserData as PurchasedItem).ItemPrefab).CompareTo( if (GetSpecialsGroup() is GUILayoutGroup specialsGroup)
CurrentLocation.GetAdjustedItemSellPrice((y.GUIComponent.UserData as PurchasedItem).ItemPrefab))); {
specialsGroup.RectTransform.SortChildren(CompareBySellPrice);
specialsGroup.Recalculate();
}
int CompareBySellPrice(RectTransform x, RectTransform y)
{
if (x.GUIComponent.UserData is PurchasedItem itemX && y.GUIComponent.UserData is PurchasedItem itemY)
{
var sortResult = CurrentLocation.GetAdjustedItemSellPrice(itemX.ItemPrefab).CompareTo(
CurrentLocation.GetAdjustedItemSellPrice(itemY.ItemPrefab));
if (sortingMethod == SortingMethod.PriceDesc) { sortResult *= -1; }
return sortResult;
}
else
{
return CompareByElement(x, y);
}
}
} }
else else
{ {
list.Content.RectTransform.SortChildren( list.Content.RectTransform.SortChildren(CompareByBuyPrice);
(x, y) => CurrentLocation.GetAdjustedItemBuyPrice((x.GUIComponent.UserData as PurchasedItem).ItemPrefab).CompareTo( if (GetSpecialsGroup() is GUILayoutGroup specialsGroup)
CurrentLocation.GetAdjustedItemBuyPrice((y.GUIComponent.UserData as PurchasedItem).ItemPrefab))); {
specialsGroup.RectTransform.SortChildren(CompareByBuyPrice);
specialsGroup.Recalculate();
}
int CompareByBuyPrice(RectTransform x, RectTransform y)
{
if (x.GUIComponent.UserData is PurchasedItem itemX && y.GUIComponent.UserData is PurchasedItem itemY)
{
var sortResult = CurrentLocation.GetAdjustedItemBuyPrice(itemX.ItemPrefab).CompareTo(
CurrentLocation.GetAdjustedItemBuyPrice(itemY.ItemPrefab));
if (sortingMethod == SortingMethod.PriceDesc) { sortResult *= -1; }
return sortResult;
}
else
{
return CompareByElement(x, y);
}
}
} }
if (sortingMethod == SortingMethod.PriceDesc) { list.Content.RectTransform.ReverseChildren(); }
} }
else if (sortingMethod == SortingMethod.CategoryAsc) else if (sortingMethod == SortingMethod.CategoryAsc)
{ {
SortItems(list, SortingMethod.AlphabeticalAsc); SortItems(list, SortingMethod.AlphabeticalAsc);
list.Content.RectTransform.SortChildren((x, y) => list.Content.RectTransform.SortChildren(CompareByCategory);
(x.GUIComponent.UserData as PurchasedItem).ItemPrefab.Category.CompareTo((y.GUIComponent.UserData as PurchasedItem).ItemPrefab.Category)); if (GetSpecialsGroup() is GUILayoutGroup specialsGroup)
{
specialsGroup.RectTransform.SortChildren(CompareByCategory);
specialsGroup.Recalculate();
}
static int CompareByCategory(RectTransform x, RectTransform y)
{
if (x.GUIComponent.UserData is PurchasedItem itemX && y.GUIComponent.UserData is PurchasedItem itemY)
{
return itemX.ItemPrefab.Category.CompareTo(itemY.ItemPrefab.Category);
}
else
{
return CompareByElement(x, y);
}
}
}
GUILayoutGroup GetSpecialsGroup()
{
if (list == storeBuyList)
{
return storeDailySpecialsGroup;
}
else if (list == storeSellList)
{
return storeRequestedGoodGroup;
}
else
{
return null;
}
}
static int CompareByElement(RectTransform x, RectTransform y)
{
if (ShouldBeOnTop(x) || ShouldBeOnBottom(y))
{
return -1;
}
else if (ShouldBeOnBottom(x) || ShouldBeOnTop(y))
{
return 1;
}
else
{
return 0;
}
static bool ShouldBeOnTop(RectTransform rt) =>
rt.GUIComponent.UserData is string id && (id == "deals" || id == "header");
static bool ShouldBeOnBottom(RectTransform rt) =>
rt.GUIComponent.UserData is string id && id == "divider";
} }
} }
@@ -750,7 +1021,7 @@ namespace Barotrauma
private void SortActiveTabItems(SortingMethod sortingMethod) => SortItems(activeTab, sortingMethod); private void SortActiveTabItems(SortingMethod sortingMethod) => SortItems(activeTab, sortingMethod);
private GUIComponent CreateItemFrame(PurchasedItem pi, PriceInfo priceInfo, GUIListBox listBox, bool forceDisable = false) private GUIComponent CreateItemFrame(PurchasedItem pi, PriceInfo priceInfo, GUIComponent parentComponent, bool forceDisable = false)
{ {
var tooltip = pi.ItemPrefab.Name; var tooltip = pi.ItemPrefab.Name;
if (!string.IsNullOrWhiteSpace(pi.ItemPrefab.Description)) if (!string.IsNullOrWhiteSpace(pi.ItemPrefab.Description))
@@ -758,7 +1029,21 @@ namespace Barotrauma
tooltip += "\n" + pi.ItemPrefab.Description; tooltip += "\n" + pi.ItemPrefab.Description;
} }
GUIFrame frame = new GUIFrame(new RectTransform(new Point(listBox.Content.Rect.Width, (int)(GUI.yScale * 80)), parent: listBox.Content.RectTransform), style: "ListBoxElement") GUIListBox parentListBox = parentComponent as GUIListBox;
int width = 0;
RectTransform parent = null;
if (parentListBox != null)
{
width = parentListBox.Content.Rect.Width;
parent = parentListBox.Content.RectTransform;
}
else
{
width = parentComponent.Rect.Width;
parent = parentComponent.RectTransform;
}
GUIFrame frame = new GUIFrame(new RectTransform(new Point(width, (int)(GUI.yScale * 80)), parent: parent), style: "ListBoxElement")
{ {
ToolTip = tooltip, ToolTip = tooltip,
UserData = pi UserData = pi
@@ -788,33 +1073,58 @@ namespace Barotrauma
img.RectTransform.MaxSize = img.Rect.Size; img.RectTransform.MaxSize = img.Rect.Size;
} }
GUILayoutGroup nameAndQuantityGroup = new GUILayoutGroup(new RectTransform(new Vector2(nameAndIconRelativeWidth - iconRelativeWidth, 1.0f), mainGroup.RectTransform)) GUIFrame nameAndQuantityFrame = new GUIFrame(new RectTransform(new Vector2(nameAndIconRelativeWidth - iconRelativeWidth, 1.0f), mainGroup.RectTransform), style: null)
{
CanBeFocused = false
};
GUILayoutGroup nameAndQuantityGroup = new GUILayoutGroup(new RectTransform(Vector2.One, nameAndQuantityFrame.RectTransform))
{ {
CanBeFocused = false, CanBeFocused = false,
Stretch = true Stretch = true
}; };
var isSellingRelatedList = parentComponent == storeSellList || parentComponent == storeRequestedGoodGroup || parentComponent == shoppingCrateSellList;
var locationHasDealOnItem = isSellingRelatedList ?
CurrentLocation.RequestedGoods.Contains(pi.ItemPrefab) : CurrentLocation.DailySpecials.Contains(pi.ItemPrefab);
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.4f), nameAndQuantityGroup.RectTransform), GUITextBlock nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.4f), nameAndQuantityGroup.RectTransform),
pi.ItemPrefab.Name, font: GUI.SubHeadingFont, textAlignment: Alignment.BottomLeft) pi.ItemPrefab.Name, font: GUI.SubHeadingFont, textAlignment: Alignment.BottomLeft)
{ {
CanBeFocused = false, CanBeFocused = false,
Shadow = locationHasDealOnItem,
TextColor = Color.White * (forceDisable ? 0.5f : 1.0f), TextColor = Color.White * (forceDisable ? 0.5f : 1.0f),
TextScale = 0.85f, TextScale = 0.85f,
UserData = "name" UserData = "name"
}; };
if (locationHasDealOnItem)
{
var relativeWidth = (0.9f * nameAndQuantityFrame.Rect.Height) / nameAndQuantityFrame.Rect.Width;
var dealIcon = new GUIImage(
new RectTransform(new Vector2(relativeWidth, 0.9f), nameAndQuantityFrame.RectTransform, anchor: Anchor.CenterLeft)
{
AbsoluteOffset = new Point((int)nameBlock.Padding.X, 0)
},
"StoreDealIcon", scaleToFit: true)
{
CanBeFocused = false
};
dealIcon.SetAsFirstChild();
}
var isParentOnLeftSideOfInterface = parentComponent == storeBuyList || parentComponent == storeDailySpecialsGroup ||
parentComponent == storeSellList || parentComponent == storeRequestedGoodGroup;
GUILayoutGroup shoppingCrateAmountGroup = null; GUILayoutGroup shoppingCrateAmountGroup = null;
GUINumberInput amountInput = null; GUINumberInput amountInput = null;
if (listBox == storeBuyList || listBox == storeSellList) if (isParentOnLeftSideOfInterface)
{ {
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), nameAndQuantityGroup.RectTransform), new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), nameAndQuantityGroup.RectTransform),
CreateQuantityLabelText(listBox == storeSellList ? StoreTab.Sell : StoreTab.Buy, pi.Quantity), font: GUI.Font, textAlignment: Alignment.BottomLeft) CreateQuantityLabelText(isSellingRelatedList ? StoreTab.Sell : StoreTab.Buy, pi.Quantity), font: GUI.Font, textAlignment: Alignment.BottomLeft)
{ {
CanBeFocused = false, CanBeFocused = false,
Shadow = locationHasDealOnItem,
TextColor = Color.White * (forceDisable ? 0.5f : 1.0f), TextColor = Color.White * (forceDisable ? 0.5f : 1.0f),
TextScale = 0.85f, TextScale = 0.85f,
UserData = "quantitylabel" UserData = "quantitylabel"
}; };
} }
else if (listBox == shoppingCrateBuyList || listBox == shoppingCrateSellList) else if (!isParentOnLeftSideOfInterface)
{ {
var relativePadding = nameBlock.Padding.X / nameBlock.Rect.Width; var relativePadding = nameBlock.Padding.X / nameBlock.Rect.Width;
shoppingCrateAmountGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f - relativePadding, 0.6f), nameAndQuantityGroup.RectTransform) { RelativeOffset = new Vector2(relativePadding, 0) }, shoppingCrateAmountGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f - relativePadding, 0.6f), nameAndQuantityGroup.RectTransform) { RelativeOffset = new Vector2(relativePadding, 0) },
@@ -825,7 +1135,7 @@ namespace Barotrauma
amountInput = new GUINumberInput(new RectTransform(new Vector2(0.4f, 1.0f), shoppingCrateAmountGroup.RectTransform), GUINumberInput.NumberType.Int) amountInput = new GUINumberInput(new RectTransform(new Vector2(0.4f, 1.0f), shoppingCrateAmountGroup.RectTransform), GUINumberInput.NumberType.Int)
{ {
MinValueInt = 0, MinValueInt = 0,
MaxValueInt = GetMaxAvailable(pi.ItemPrefab, listBox == shoppingCrateBuyList ? StoreTab.Buy : StoreTab.Sell), MaxValueInt = GetMaxAvailable(pi.ItemPrefab, isSellingRelatedList ? StoreTab.Sell : StoreTab.Buy),
UserData = pi, UserData = pi,
IntValue = pi.Quantity IntValue = pi.Quantity
}; };
@@ -856,6 +1166,7 @@ namespace Barotrauma
textAlignment: shoppingCrateAmountGroup == null ? Alignment.TopLeft : Alignment.CenterLeft) textAlignment: shoppingCrateAmountGroup == null ? Alignment.TopLeft : Alignment.CenterLeft)
{ {
CanBeFocused = false, CanBeFocused = false,
Shadow = locationHasDealOnItem,
TextColor = Color.White * (forceDisable ? 0.5f : 1.0f), TextColor = Color.White * (forceDisable ? 0.5f : 1.0f),
TextScale = 0.85f, TextScale = 0.85f,
UserData = "owned" UserData = "owned"
@@ -864,22 +1175,53 @@ namespace Barotrauma
var buttonRelativeWidth = (0.9f * mainGroup.Rect.Height) / mainGroup.Rect.Width; var buttonRelativeWidth = (0.9f * mainGroup.Rect.Height) / mainGroup.Rect.Width;
var priceBlock = new GUITextBlock(new RectTransform(new Vector2(priceAndButtonRelativeWidth - buttonRelativeWidth, 1.0f), mainGroup.RectTransform), "", font: GUI.SubHeadingFont, textAlignment: Alignment.Right) var priceFrame = new GUIFrame(new RectTransform(new Vector2(priceAndButtonRelativeWidth - buttonRelativeWidth, 1.0f), mainGroup.RectTransform), style: null)
{
CanBeFocused = false
};
var priceBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), priceFrame.RectTransform, anchor: Anchor.Center),
"0 MK", font: GUI.SubHeadingFont, textAlignment: Alignment.Right)
{ {
CanBeFocused = false, CanBeFocused = false,
TextColor = Color.White * (forceDisable ? 0.5f : 1.0f), TextColor = locationHasDealOnItem ? storeSpecialColor : Color.White,
UserData = "price" UserData = "price"
}; };
if (listBox == storeSellList || listBox == shoppingCrateSellList) priceBlock.Color *= (forceDisable ? 0.5f : 1.0f);
priceBlock.CalculateHeightFromText();
if (isSellingRelatedList)
{ {
priceBlock.TextGetter = () => GetCurrencyFormatted(CurrentLocation?.GetAdjustedItemSellPrice(priceInfo) ?? 0); priceBlock.TextGetter = () => GetCurrencyFormatted(CurrentLocation?.GetAdjustedItemSellPrice(pi.ItemPrefab, priceInfo: priceInfo) ?? 0);
} }
else else
{ {
priceBlock.TextGetter = () => GetCurrencyFormatted(CurrentLocation?.GetAdjustedItemBuyPrice(priceInfo) ?? 0); priceBlock.TextGetter = () => GetCurrencyFormatted(CurrentLocation?.GetAdjustedItemBuyPrice(pi.ItemPrefab, priceInfo: priceInfo) ?? 0);
}
if (locationHasDealOnItem)
{
var undiscounterPriceBlock = new GUITextBlock(
new RectTransform(new Vector2(1.0f, 0.25f), priceFrame.RectTransform, anchor: Anchor.Center)
{
AbsoluteOffset = new Point(0, priceBlock.RectTransform.ScaledSize.Y)
}, "", font: GUI.SmallFont, textAlignment: Alignment.Center)
{
CanBeFocused = false,
Strikethrough = new GUITextBlock.StrikethroughSettings(color: priceBlock.TextColor, expand: 1),
TextColor = priceBlock.TextColor,
UserData = "undiscountedprice"
};
if (isSellingRelatedList)
{
undiscounterPriceBlock.TextGetter = () => GetCurrencyFormatted(
CurrentLocation?.GetAdjustedItemSellPrice(pi.ItemPrefab, priceInfo: priceInfo, considerRequestedGoods: false) ?? 0);
}
else
{
undiscounterPriceBlock.TextGetter = () => GetCurrencyFormatted(
CurrentLocation?.GetAdjustedItemBuyPrice(pi.ItemPrefab, priceInfo: priceInfo, considerDailySpecials: false) ?? 0);
}
} }
if (listBox == storeDealsList || listBox == storeBuyList || listBox == storeSellList) if (isParentOnLeftSideOfInterface)
{ {
new GUIButton(new RectTransform(new Vector2(buttonRelativeWidth, 0.9f), mainGroup.RectTransform), style: "StoreAddToCrateButton") new GUIButton(new RectTransform(new Vector2(buttonRelativeWidth, 0.9f), mainGroup.RectTransform), style: "StoreAddToCrateButton")
{ {
@@ -902,7 +1244,14 @@ namespace Barotrauma
}; };
} }
listBox.RecalculateChildren(); if (parentListBox != null)
{
parentListBox.RecalculateChildren();
}
else if (parentComponent is GUILayoutGroup parentLayoutGroup)
{
parentLayoutGroup.Recalculate();
}
mainGroup.Recalculate(); mainGroup.Recalculate();
mainGroup.RectTransform.RecalculateChildren(true, true); mainGroup.RectTransform.RecalculateChildren(true, true);
amountInput?.LayoutGroup.Recalculate(); amountInput?.LayoutGroup.Recalculate();
@@ -923,15 +1272,20 @@ namespace Barotrauma
.ForEach(i => AddToOwnedItems(i.Prefab)); .ForEach(i => AddToOwnedItems(i.Prefab));
// Add items in character inventories // Add items in character inventories
foreach (Character c in GameMain.GameSession.CrewManager.GetCharacters()) foreach (var item in Item.ItemList)
{ {
Item.ItemList.Where(i => i != null && i.GetRootInventoryOwner() == c) if (item == null || item.Removed) { continue; }
.ForEach(i => AddToOwnedItems(i.Prefab)); var rootInventoryOwner = item.GetRootInventoryOwner();
var ownedByCrewMember = GameMain.GameSession.CrewManager.GetCharacters().Any(c => c == rootInventoryOwner);
if (!ownedByCrewMember) { continue; }
AddToOwnedItems(item.Prefab);
} }
// Add items already purchased // Add items already purchased
CargoManager?.PurchasedItems?.ForEach(pi => AddToOwnedItems(pi.ItemPrefab, amount: pi.Quantity)); CargoManager?.PurchasedItems?.ForEach(pi => AddToOwnedItems(pi.ItemPrefab, amount: pi.Quantity));
ownedItemsUpdateTimer = 0.0f;
void AddToOwnedItems(ItemPrefab itemPrefab, int amount = 1) void AddToOwnedItems(ItemPrefab itemPrefab, int amount = 1)
{ {
if (OwnedItems.ContainsKey(itemPrefab)) if (OwnedItems.ContainsKey(itemPrefab))
@@ -977,14 +1331,22 @@ namespace Barotrauma
numberInput.Enabled = enabled; numberInput.Enabled = enabled;
} }
if (itemFrame.FindChild("owned", recursive: true) is GUITextBlock owned) if (itemFrame.FindChild("owned", recursive: true) is GUITextBlock ownedBlock)
{ {
owned.TextColor = color; ownedBlock.TextColor = color;
} }
if (itemFrame.FindChild("price", recursive: true) is GUITextBlock price) var isDiscounted = false;
if (itemFrame.FindChild("undiscountedprice", recursive: true) is GUITextBlock undiscountedPriceBlock)
{ {
price.TextColor = color; undiscountedPriceBlock.TextColor = color;
undiscountedPriceBlock.Strikethrough.Color = color;
isDiscounted = true;
}
if (itemFrame.FindChild("price", recursive: true) is GUITextBlock priceBlock)
{
priceBlock.TextColor = isDiscounted ? storeSpecialColor * (enabled ? 1.0f : 0.5f) : color;
} }
if (itemFrame.FindChild("addbutton", recursive: true) is GUIButton addButton) if (itemFrame.FindChild("addbutton", recursive: true) is GUIButton addButton)
@@ -1101,7 +1463,7 @@ namespace Barotrauma
itemsToRemove.Add(item); itemsToRemove.Add(item);
continue; continue;
} }
totalPrice += item.Quantity * CurrentLocation.GetAdjustedItemBuyPrice(priceInfo); totalPrice += item.Quantity * CurrentLocation.GetAdjustedItemBuyPrice(item.ItemPrefab, priceInfo: priceInfo);
} }
itemsToRemove.ForEach(i => itemsToPurchase.Remove(i)); itemsToRemove.ForEach(i => itemsToPurchase.Remove(i));
@@ -1135,7 +1497,7 @@ namespace Barotrauma
} }
if (item.ItemPrefab.GetPriceInfo(CurrentLocation) is PriceInfo priceInfo) if (item.ItemPrefab.GetPriceInfo(CurrentLocation) is PriceInfo priceInfo)
{ {
totalValue += item.Quantity * CurrentLocation.GetAdjustedItemSellPrice(priceInfo); totalValue += item.Quantity * CurrentLocation.GetAdjustedItemSellPrice(item.ItemPrefab, priceInfo: priceInfo);
} }
else else
{ {
@@ -1197,16 +1559,38 @@ namespace Barotrauma
private void SetClearAllButtonStatus() => clearAllButton.Enabled = private void SetClearAllButtonStatus() => clearAllButton.Enabled =
HasPermissions && ActiveShoppingCrateList.Content.RectTransform.Children.Any(); HasPermissions && ActiveShoppingCrateList.Content.RectTransform.Children.Any();
public void Update() private float ownedItemsUpdateTimer = 0.0f;
private readonly float ownedItemsUpdateInterval = 1.5f;
public void Update(float deltaTime)
{ {
if (GameMain.GraphicsWidth != resolutionWhenCreated.X || GameMain.GraphicsHeight != resolutionWhenCreated.Y) if (GameMain.GraphicsWidth != resolutionWhenCreated.X || GameMain.GraphicsHeight != resolutionWhenCreated.Y)
{ {
CreateUI(); CreateUI();
needsRefresh = false;
} }
if (needsRefresh || hadPermissions != HasPermissions) { Refresh(); } else
if (needsBuyingRefresh) { RefreshBuying(); } {
// Update the owned items at short intervals and check if the interface should be refreshed
ownedItemsUpdateTimer += deltaTime;
if (ownedItemsUpdateTimer >= ownedItemsUpdateInterval)
{
var prevOwnedItems = new Dictionary<ItemPrefab, int>(OwnedItems);
UpdateOwnedItems();
var refresh = (prevOwnedItems.Count != OwnedItems.Count) ||
(prevOwnedItems.Select(kvp => kvp.Value).Sum() != OwnedItems.Select(kvp => kvp.Value).Sum()) ||
(OwnedItems.Any(kvp => kvp.Value > 0 && !prevOwnedItems.ContainsKey(kvp.Key)) ||
prevOwnedItems.Any(kvp => !OwnedItems.TryGetValue(kvp.Key, out var itemCount) || kvp.Value != itemCount));
if (refresh)
{
needsItemsToSellRefresh = true;
needsRefresh = true;
}
}
}
if (needsItemsToSellRefresh) { RefreshItemsToSell(); } if (needsItemsToSellRefresh) { RefreshItemsToSell(); }
if (needsRefresh || hadPermissions != HasPermissions) { Refresh(updateOwned: ownedItemsUpdateTimer > 0.0f); }
if (needsBuyingRefresh) { RefreshBuying(); }
if (needsSellingRefresh) { RefreshSelling(); } if (needsSellingRefresh) { RefreshSelling(); }
} }
} }
@@ -610,8 +610,8 @@ namespace Barotrauma
{ {
if (GameMain.Client == null) if (GameMain.Client == null)
{ {
GameMain.GameSession.SwitchSubmarine(selectedSubmarine, deliveryFee); SubmarineInfo newSub = GameMain.GameSession.SwitchSubmarine(selectedSubmarine, deliveryFee);
GameMain.GameSession.Campaign.UpgradeManager.RefundResetAndReload(selectedSubmarine); GameMain.GameSession.Campaign.UpgradeManager.RefundResetAndReload(newSub);
RefreshSubmarineDisplay(true); RefreshSubmarineDisplay(true);
} }
else else
@@ -645,8 +645,8 @@ namespace Barotrauma
if (GameMain.Client == null) if (GameMain.Client == null)
{ {
GameMain.GameSession.PurchaseSubmarine(selectedSubmarine); GameMain.GameSession.PurchaseSubmarine(selectedSubmarine);
GameMain.GameSession.SwitchSubmarine(selectedSubmarine, 0); SubmarineInfo newSub = GameMain.GameSession.SwitchSubmarine(selectedSubmarine, 0);
GameMain.GameSession.Campaign.UpgradeManager.RefundResetAndReload(selectedSubmarine); GameMain.GameSession.Campaign.UpgradeManager.RefundResetAndReload(newSub);
RefreshSubmarineDisplay(true); RefreshSubmarineDisplay(true);
} }
else else
@@ -29,7 +29,7 @@ namespace Barotrauma
private float sizeMultiplier = 1f; private float sizeMultiplier = 1f;
private IEnumerable<Character> crew; private IEnumerable<Character> crew;
private List<Character.TeamType> teamIDs; private List<CharacterTeamType> teamIDs;
private const string inLobbyString = "\u2022 \u2022 \u2022"; private const string inLobbyString = "\u2022 \u2022 \u2022";
public static Color OwnCharacterBGColor = Color.Gold * 0.7f; public static Color OwnCharacterBGColor = Color.Gold * 0.7f;
@@ -281,11 +281,11 @@ namespace Barotrauma
// Show own team first when there's more than one team // Show own team first when there's more than one team
if (teamIDs.Count > 1 && GameMain.Client?.Character != null) if (teamIDs.Count > 1 && GameMain.Client?.Character != null)
{ {
Character.TeamType ownTeam = GameMain.Client.Character.TeamID; CharacterTeamType ownTeam = GameMain.Client.Character.TeamID;
teamIDs = teamIDs.OrderBy(i => i != ownTeam).ThenBy(i => i).ToList(); teamIDs = teamIDs.OrderBy(i => i != ownTeam).ThenBy(i => i).ToList();
} }
if (!teamIDs.Any()) teamIDs.Add(Character.TeamType.None); if (!teamIDs.Any()) { teamIDs.Add(CharacterTeamType.None); }
var content = new GUILayoutGroup(new RectTransform(Vector2.One, crewFrame.RectTransform)); var content = new GUILayoutGroup(new RectTransform(Vector2.One, crewFrame.RectTransform));
@@ -465,15 +465,14 @@ namespace Barotrauma
{ {
foreach (Character character in crew.Where(c => c.TeamID == teamIDs[i])) foreach (Character character in crew.Where(c => c.TeamID == teamIDs[i]))
{ {
if (!(character is AICharacter) && connectedClients.Find(c => c.Character == null && c.Name == character.Name) != null) continue; if (!(character is AICharacter) && connectedClients.Any(c => c.Character == null && c.Name == character.Name)) { continue; }
CreateMultiPlayerCharacterElement(character, GameMain.Client.ConnectedClients.Find(c => c.Character == character), i); CreateMultiPlayerCharacterElement(character, GameMain.Client.PreviouslyConnectedClients.FirstOrDefault(c => c.Character == character), i);
} }
} }
for (int j = 0; j < connectedClients.Count; j++) for (int j = 0; j < connectedClients.Count; j++)
{ {
Client client = connectedClients[j]; Client client = connectedClients[j];
if (!client.InGame || client.Character == null || client.Character.IsDead) if (!client.InGame || client.Character == null || client.Character.IsDead)
{ {
CreateMultiPlayerClientElement(client); CreateMultiPlayerClientElement(client);
@@ -565,7 +564,7 @@ namespace Barotrauma
private int GetTeamIndex(Client client) private int GetTeamIndex(Client client)
{ {
if (teamIDs.Count <= 1) return 0; if (teamIDs.Count <= 1) { return 0; }
if (client.Character != null) if (client.Character != null)
{ {
@@ -707,7 +706,7 @@ namespace Barotrauma
{ {
GUIComponent paddedFrame; GUIComponent paddedFrame;
if (client.Character == null) if (client.Character?.Info == null)
{ {
paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.874f, 0.58f), frame.RectTransform, Anchor.TopCenter) { RelativeOffset = new Vector2(0.0f, 0.05f) }) paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.874f, 0.58f), frame.RectTransform, Anchor.TopCenter) { RelativeOffset = new Vector2(0.0f, 0.05f) })
{ {
@@ -625,13 +625,19 @@ namespace Barotrauma
selectedUpgradeCategoryLayout?.ClearChildren(); selectedUpgradeCategoryLayout?.ClearChildren();
GUIFrame frame = new GUIFrame(rectT(1, 0.4f, selectedUpgradeCategoryLayout)); GUIFrame frame = new GUIFrame(rectT(1, 0.4f, selectedUpgradeCategoryLayout));
GUIListBox prefabList = new GUIListBox(rectT(0.93f, 0.9f, frame, Anchor.Center)) { UserData = "prefablist" }; GUIListBox prefabList = new GUIListBox(rectT(0.93f, 0.9f, frame, Anchor.Center)) { UserData = "prefablist" };
List<Item> entitiesOnSub = null;
if (!category.IsWallUpgrade)
{
entitiesOnSub = submarine.GetItems(true).Where(i => submarine.IsEntityFoundOnThisSub(i, true)).ToList();
}
foreach (UpgradePrefab prefab in prefabs) foreach (UpgradePrefab prefab in prefabs)
{ {
CreateUpgradeEntry(prefab, category, prefabList.Content); CreateUpgradeEntry(prefab, category, prefabList.Content, entitiesOnSub);
} }
} }
private void CreateUpgradeEntry(UpgradePrefab prefab, UpgradeCategory category, GUIComponent parent) private void CreateUpgradeEntry(UpgradePrefab prefab, UpgradeCategory category, GUIComponent parent, List<Item> itemsOnSubmarine)
{ {
/* UPGRADE PREFAB ENTRY /* UPGRADE PREFAB ENTRY
* |------------------------------------------------------------------| * |------------------------------------------------------------------|
@@ -680,12 +686,14 @@ namespace Barotrauma
progressLayout.Recalculate(); progressLayout.Recalculate();
buyButtonLayout.Recalculate(); buyButtonLayout.Recalculate();
if (!HasPermission) if (!HasPermission || itemsOnSubmarine != null && !itemsOnSubmarine.Any(it => category.CanBeApplied(it, prefab)))
{ {
prefabFrame.Enabled = false; prefabFrame.Enabled = false;
description.Enabled = false; description.Enabled = false;
name.Enabled = false; name.Enabled = false;
icon.Color = Color.Gray; icon.Color = Color.Gray;
buyButton.Enabled = false;
buyButtonLayout.UserData = null; // prevent UpdateUpgradeEntry() from enabling the button
} }
buyButton.OnClicked += (button, o) => buyButton.OnClicked += (button, o) =>
@@ -731,7 +739,7 @@ namespace Barotrauma
// include pending upgrades into the tooltip // include pending upgrades into the tooltip
foreach (var (prefab, category, level) in Campaign.UpgradeManager.PendingUpgrades) foreach (var (prefab, category, level) in Campaign.UpgradeManager.PendingUpgrades)
{ {
if (entity is Item item && category.CanBeApplied(item) || entity is Structure && category.IsWallUpgrade) if (entity is Item item && category.CanBeApplied(item, prefab) || entity is Structure && category.IsWallUpgrade)
{ {
bool found = false; bool found = false;
foreach (GUITextBlock textBlock in upgradeList.Content.Children.Where(c => c is GUITextBlock).Cast<GUITextBlock>()) foreach (GUITextBlock textBlock in upgradeList.Content.Children.Where(c => c is GUITextBlock).Cast<GUITextBlock>())
@@ -786,7 +794,7 @@ namespace Barotrauma
foreach (UpgradeCategory category in UpgradeCategory.Categories) foreach (UpgradeCategory category in UpgradeCategory.Categories)
{ {
if (entitiesOnSub.Any(item => category.CanBeApplied(item) && !item.disallowedUpgrades.Contains(category.Identifier))) if (entitiesOnSub.Any(item => category.CanBeApplied(item, null)))
{ {
applicableCategories.Add(category); applicableCategories.Add(category);
} }
@@ -826,7 +834,7 @@ namespace Barotrauma
HoveredItem = item; HoveredItem = item;
if (PlayerInput.PrimaryMouseButtonClicked() && selectedUpgradTab == UpgradeTab.Upgrade && currentStoreLayout != null) if (PlayerInput.PrimaryMouseButtonClicked() && selectedUpgradTab == UpgradeTab.Upgrade && currentStoreLayout != null)
{ {
ScrollToCategory(data => data.Category.CanBeApplied(item)); ScrollToCategory(data => data.Category.CanBeApplied(item, null));
} }
found = true; found = true;
break; break;
@@ -895,7 +903,7 @@ namespace Barotrauma
submarineInfoFrame.RectTransform.ScreenSpaceOffset = new Point(0, (int)(16 * GUI.Scale)); submarineInfoFrame.RectTransform.ScreenSpaceOffset = new Point(0, (int)(16 * GUI.Scale));
description.Padding = new Vector4(description.Padding.X, 24 * GUI.Scale, description.Padding.Z, description.Padding.W); description.Padding = new Vector4(description.Padding.X, 24 * GUI.Scale, description.Padding.Z, description.Padding.W);
List<Entity> pointsOfInterest = (from category in UpgradeCategory.Categories from item in submarine.GetItems(UpgradeManager.UpgradeAlsoConnectedSubs) where category.CanBeApplied(item) && !item.NonInteractable select item).Cast<Entity>().ToList(); List<Entity> pointsOfInterest = (from category in UpgradeCategory.Categories from item in submarine.GetItems(UpgradeManager.UpgradeAlsoConnectedSubs) where category.CanBeApplied(item, null) && item.IsPlayerTeamInteractable select item).Cast<Entity>().ToList();
List<ushort> ids = GameMain.GameSession.SubmarineInfo?.LeftBehindDockingPortIDs ?? new List<ushort>(); List<ushort> ids = GameMain.GameSession.SubmarineInfo?.LeftBehindDockingPortIDs ?? new List<ushort>();
pointsOfInterest.AddRange(submarine.GetItems(UpgradeManager.UpgradeAlsoConnectedSubs).Where(item => ids.Contains(item.ID))); pointsOfInterest.AddRange(submarine.GetItems(UpgradeManager.UpgradeAlsoConnectedSubs).Where(item => ids.Contains(item.ID)));
@@ -1112,7 +1120,7 @@ namespace Barotrauma
List<GUIFrame> frames = new List<GUIFrame>(); List<GUIFrame> frames = new List<GUIFrame>();
foreach (var (item, guiFrame) in itemPreviews) foreach (var (item, guiFrame) in itemPreviews)
{ {
if (category.CanBeApplied(item)) if (category.CanBeApplied(item, null))
{ {
frames.Add(guiFrame); frames.Add(guiFrame);
} }
@@ -557,6 +557,7 @@ namespace Barotrauma
GameModePreset.Init(); GameModePreset.Init();
SaveUtil.DeleteDownloadedSubs();
SubmarineInfo.RefreshSavedSubs(); SubmarineInfo.RefreshSavedSubs();
TitleScreen.LoadState = 65.0f; TitleScreen.LoadState = 65.0f;
@@ -1113,7 +1114,6 @@ namespace Barotrauma
{ {
new Pair<string, string>(TextManager.Get("EditorDisclaimerWikiLink"), TextManager.Get("EditorDisclaimerWikiUrl")), new Pair<string, string>(TextManager.Get("EditorDisclaimerWikiLink"), TextManager.Get("EditorDisclaimerWikiUrl")),
new Pair<string, string>(TextManager.Get("EditorDisclaimerDiscordLink"), TextManager.Get("EditorDisclaimerDiscordUrl")), new Pair<string, string>(TextManager.Get("EditorDisclaimerDiscordLink"), TextManager.Get("EditorDisclaimerDiscordUrl")),
new Pair<string, string>(TextManager.Get("EditorDisclaimerForumLink"), TextManager.Get("EditorDisclaimerForumUrl")),
}; };
foreach (var link in links) foreach (var link in links)
{ {
@@ -39,61 +39,37 @@ namespace Barotrauma
private List<SoldEntity> SoldEntities { get; } = new List<SoldEntity>(); private List<SoldEntity> SoldEntities { get; } = new List<SoldEntity>();
public List<Item> GetSellableItems(Character character) public IEnumerable<Item> GetSellableItems(Character character)
{ {
if (character == null) { return new List<Item>(); } if (character == null) { return new List<Item>(); }
// Only consider items which have been: // Only consider items which have been:
// a) sold in singleplayer or confirmed by server (SellStatus.Confirmed); or // a) sold in singleplayer or confirmed by server (SellStatus.Confirmed); or
// b) sold locally in multiplayer (SellStatus.Local), but the client has not received a campaing state update yet after selling them // b) sold locally in multiplayer (SellStatus.Local), but the client has not received a campaing state update yet after selling them
var soldEntities = SoldEntities.Where(se => se.Status != SoldEntity.SellStatus.Unconfirmed); var confirmedSoldEntities = SoldEntities.Where(se => se.Status != SoldEntity.SellStatus.Unconfirmed);
// The bag slot is intentionally left out since we want to be able to sell items from there
var sellables = Item.ItemList.FindAll(i => i?.Prefab != null && !i.Removed &&
i.GetRootInventoryOwner() == character &&
!i.SpawnedInOutpost &&
(i.ContainedItems == null || i.ContainedItems.None() || i.ContainedItems.All(ci => soldEntities.Any(se => se.Item == ci))) &&
(i.Condition >= 0.9f * i.MaxCondition || i.Prefab.AllowSellingWhenBroken) && soldEntities.None(se => se.Item == i));
// Prevent selling items in equipment slots
var equipmentSlots = new List<InvSlotType>() { InvSlotType.Head, InvSlotType.InnerClothes, InvSlotType.OuterClothes, InvSlotType.Headset, InvSlotType.Card }; var equipmentSlots = new List<InvSlotType>() { InvSlotType.Head, InvSlotType.InnerClothes, InvSlotType.OuterClothes, InvSlotType.Headset, InvSlotType.Card };
foreach (InvSlotType slot in equipmentSlots) return character.Inventory.FindAllItems(item =>
{ {
var index = character.Inventory.FindLimbSlot(slot); if (item.SpawnedInOutpost) { return false; }
if (character.Inventory.Items[index] is Item item) if (!item.Prefab.AllowSellingWhenBroken && item.ConditionPercentage < 90.0f) { return false; }
{ if (confirmedSoldEntities.Any(it => it.Item == item)) { return false; }
// Don't prevent selling of items which can only be put in equipment slots (like diving suits) // There must be no contained items or the contained items must be confirmed as sold
if (item.AllowedSlots.Contains(InvSlotType.Any)) if (!item.ContainedItems.All(it => confirmedSoldEntities.Any(se => se.Item == it))) { return false; }
{ // Item must be in a non-equipment slot if possible
sellables.Remove(item); if (!item.AllowedSlots.All(s => equipmentSlots.Contains(s)) && IsInEquipmentSlot(item)) { return false; }
} // Item must not be contained inside an item in an equipment slot
} if (item.GetRootContainer() is Item rootContainer && IsInEquipmentSlot(rootContainer)) { return false; }
} return true;
}, recursive: true).Distinct();
// Prevent selling items contained inside equipped items bool IsInEquipmentSlot(Item item)
foreach (InvSlotType slot in equipmentSlots)
{ {
var index = character.Inventory.FindLimbSlot(slot); foreach (InvSlotType slot in equipmentSlots)
if (character.Inventory.Items[index] is Item item &&
item.ContainedItems != null && item.AllowedSlots.Contains(InvSlotType.Any))
{ {
RemoveContainedFromSellables(item); if (character.Inventory.IsInLimbSlot(item, slot)) { return true; }
} }
return false;
} }
void RemoveContainedFromSellables(Item item)
{
foreach (Item containedItem in item.ContainedItems)
{
if (containedItem == null) { continue; }
if (containedItem.ContainedItems != null)
{
RemoveContainedFromSellables(containedItem);
}
sellables.Remove(containedItem);
}
}
return sellables;
} }
public void SetItemsInBuyCrate(List<PurchasedItem> items) public void SetItemsInBuyCrate(List<PurchasedItem> items)
@@ -149,15 +125,20 @@ namespace Barotrauma
var canAddToRemoveQueue = campaign.IsSinglePlayer && Entity.Spawner != null; var canAddToRemoveQueue = campaign.IsSinglePlayer && Entity.Spawner != null;
var sellerId = GameMain.Client?.ID ?? 0; var sellerId = GameMain.Client?.ID ?? 0;
// Check all the prices before starting the transaction
// to make sure the modifiers stay the same for the whole transaction
Dictionary<ItemPrefab, int> sellValues = GetSellValuesAtCurrentLocation(itemsToSell.Select(i => i.ItemPrefab));
foreach (PurchasedItem item in itemsToSell) foreach (PurchasedItem item in itemsToSell)
{ {
var itemValue = GetSellValueAtCurrentLocation(item.ItemPrefab, quantity: item.Quantity); var itemValue = item.Quantity * sellValues[item.ItemPrefab];
// check if the store can afford the item // check if the store can afford the item
if (Location.StoreCurrentBalance < itemValue) { continue; } if (Location.StoreCurrentBalance < itemValue) { continue; }
var matchingItems = itemsInInventory.FindAll(i => i.Prefab == item.ItemPrefab); // TODO: Write logic for prioritizing certain items over others (e.g. lone Battery Cell should be preferred over one inside a Stun Baton)
if (matchingItems.Count <= item.Quantity) var matchingItems = itemsInInventory.Where(i => i.Prefab == item.ItemPrefab);
if (matchingItems.Count() <= item.Quantity)
{ {
foreach (Item i in matchingItems) foreach (Item i in matchingItems)
{ {
@@ -170,7 +151,7 @@ namespace Barotrauma
{ {
for (int i = 0; i < item.Quantity; i++) for (int i = 0; i < item.Quantity; i++)
{ {
var matchingItem = matchingItems[i]; var matchingItem = matchingItems.ElementAt(i);
SoldItems.Add(new SoldItem(matchingItem.Prefab, matchingItem.ID, canAddToRemoveQueue, sellerId)); SoldItems.Add(new SoldItem(matchingItem.Prefab, matchingItem.ID, canAddToRemoveQueue, sellerId));
SoldEntities.Add(campaign.IsSinglePlayer ? SoldEntity.CreateInSinglePlayer(matchingItem) : SoldEntity.CreateInMultiPlayer(matchingItem)); SoldEntities.Add(campaign.IsSinglePlayer ? SoldEntity.CreateInSinglePlayer(matchingItem) : SoldEntity.CreateInMultiPlayer(matchingItem));
if (canAddToRemoveQueue) { Entity.Spawner.AddToRemoveQueue(matchingItem); } if (canAddToRemoveQueue) { Entity.Spawner.AddToRemoveQueue(matchingItem); }
@@ -134,6 +134,7 @@ namespace Barotrauma
isScrollBarOnDefaultSide: false) isScrollBarOnDefaultSide: false)
{ {
AutoHideScrollBar = false, AutoHideScrollBar = false,
CanBeFocused = false,
OnSelected = (component, userData) => false, OnSelected = (component, userData) => false,
SelectMultiple = false, SelectMultiple = false,
Spacing = (int)(GUI.Scale * 10) Spacing = (int)(GUI.Scale * 10)
@@ -232,7 +233,7 @@ namespace Barotrauma
}; };
} }
var reports = Order.PrefabList.FindAll(o => o.IsReport && o.SymbolSprite != null); var reports = Order.PrefabList.FindAll(o => o.IsReport && o.SymbolSprite != null && !o.Hidden);
if (reports.None()) if (reports.None())
{ {
DebugConsole.ThrowError("No valid orders for report buttons found! Cannot create report buttons. The orders for the report buttons must have 'targetallcharacters' attribute enabled and a valid 'symbolsprite' defined."); DebugConsole.ThrowError("No valid orders for report buttons found! Cannot create report buttons. The orders for the report buttons must have 'targetallcharacters' attribute enabled and a valid 'symbolsprite' defined.");
@@ -252,7 +253,7 @@ namespace Barotrauma
//report buttons //report buttons
foreach (Order order in reports) foreach (Order order in reports)
{ {
if (!order.IsReport || order.SymbolSprite == null) { continue; } if (!order.IsReport || order.SymbolSprite == null || order.Hidden) { continue; }
var btn = new GUIButton(new RectTransform(new Point(ReportButtonFrame.Rect.Width), ReportButtonFrame.RectTransform), style: null) var btn = new GUIButton(new RectTransform(new Point(ReportButtonFrame.Rect.Width), ReportButtonFrame.RectTransform), style: null)
{ {
OnClicked = (GUIButton button, object userData) => OnClicked = (GUIButton button, object userData) =>
@@ -602,11 +603,11 @@ namespace Barotrauma
private WifiComponent GetHeadset(Character character, bool requireEquipped) private WifiComponent GetHeadset(Character character, bool requireEquipped)
{ {
if (character?.Inventory == null) return null; if (character?.Inventory == null) { return null; }
var radioItem = character.Inventory.Items.FirstOrDefault(it => it != null && it.GetComponent<WifiComponent>() != null); var radioItem = character.Inventory.AllItems.FirstOrDefault(it => it.GetComponent<WifiComponent>() != null);
if (radioItem == null) return null; if (radioItem == null) { return null; }
if (requireEquipped && !character.HasEquippedItem(radioItem)) return null; if (requireEquipped && !character.HasEquippedItem(radioItem)) { return null; }
return radioItem.GetComponent<WifiComponent>(); return radioItem.GetComponent<WifiComponent>();
} }
@@ -687,18 +688,10 @@ namespace Barotrauma
else if(order.IsIgnoreOrder) else if(order.IsIgnoreOrder)
{ {
WallSection ws = null; WallSection ws = null;
if (order.TargetType == Order.OrderTargetType.Entity && order.TargetEntity is MapEntity me) if (order.TargetType == Order.OrderTargetType.Entity && order.TargetEntity is IIgnorable ignorable)
{ {
if (order.Identifier == "ignorethis") ignorable.OrderedToBeIgnored = order.Identifier == "ignorethis";
{ AddOrder(new Order(order.Prefab ?? order, order.TargetEntity, order.TargetItemComponent, orderGiver), null);
me.SetIgnoreByAI(true);
AddOrder(new Order(order.Prefab ?? order, order.TargetEntity, order.TargetItemComponent, orderGiver), null);
}
else
{
me.SetIgnoreByAI(false);
ActiveOrders.RemoveAll(p => p.First.Identifier == "ignorethis" && p.First.TargetEntity == order.TargetEntity);
}
} }
else if (order.TargetType == Order.OrderTargetType.WallSection && order.TargetEntity is Structure s) else if (order.TargetType == Order.OrderTargetType.WallSection && order.TargetEntity is Structure s)
{ {
@@ -706,18 +699,14 @@ namespace Barotrauma
ws = s.GetSection(wallSectionIndex); ws = s.GetSection(wallSectionIndex);
if (ws != null) if (ws != null)
{ {
if (order.Identifier == "ignorethis") ws.OrderedToBeIgnored = order.Identifier == "ignorethis";
{ AddOrder(new Order(order.Prefab ?? order, s, wallSectionIndex, orderGiver), null);
ws.SetIgnoreByAI(true);
AddOrder(new Order(order.Prefab ?? order, s, wallSectionIndex, orderGiver), null);
}
else
{
ws.SetIgnoreByAI(false);
ActiveOrders.RemoveAll(p => p.First.Identifier == "ignorethis" && p.First.TargetEntity == s && p.First.WallSectionIndex == wallSectionIndex);
}
} }
} }
else
{
return;
}
if (ws != null) if (ws != null)
{ {
@@ -781,7 +770,16 @@ namespace Barotrauma
{ {
currentOrderInfo = (OrderInfo)currentOrderIcon.UserData; currentOrderInfo = (OrderInfo)currentOrderIcon.UserData;
// No need to recreate icons if the current order matches the new order // No need to recreate icons if the current order matches the new order
if (currentOrderInfo.Value.MatchesOrder(order, option)) { return; } if (currentOrderInfo.Value.MatchesOrder(order, option))
{
currentOrderIcon.UserData = new OrderInfo(order, option);
if (currentOrderIcon.FindChild(c => (string)c.UserData == "colorsource") is GUIImage image)
{
image.Sprite = GetOrderIconSprite(order, option);
image.ToolTip = CreateOrderTooltip(order, option);
}
return;
}
} }
// Remove the current order icon // Remove the current order icon
@@ -826,7 +824,10 @@ namespace Barotrauma
} }
}; };
CreateNodeIcon(orderFrame.RectTransform, order.SymbolSprite, order.Color, tooltip: order.Name); CreateNodeIcon(orderFrame.RectTransform,
GetOrderIconSprite(order, option),
order.Color,
tooltip: CreateOrderTooltip(order, option));
new GUIImage(new RectTransform(Vector2.One, orderFrame.RectTransform), cancelIcon, scaleToFit: true) new GUIImage(new RectTransform(Vector2.One, orderFrame.RectTransform), cancelIcon, scaleToFit: true)
{ {
@@ -870,11 +871,10 @@ namespace Barotrauma
new RectTransform(new Vector2(0.8f), prevOrderFrame.RectTransform, anchor: Anchor.BottomLeft), new RectTransform(new Vector2(0.8f), prevOrderFrame.RectTransform, anchor: Anchor.BottomLeft),
style: null); style: null);
CreateNodeIcon( CreateNodeIcon(prevOrderIconFrame.RectTransform,
prevOrderIconFrame.RectTransform, GetOrderIconSprite(previousOrderInfo),
previousOrderInfo.Order.SymbolSprite,
previousOrderInfo.Order.Color, previousOrderInfo.Order.Color,
tooltip: previousOrderInfo.Order.Name); tooltip: CreateOrderTooltip(previousOrderInfo));
foreach (GUIComponent c in prevOrderIconFrame.Children) foreach (GUIComponent c in prevOrderIconFrame.Children)
{ {
@@ -947,6 +947,48 @@ namespace Barotrauma
private IEnumerable<GUIComponent> GetPreviousOrderIcons(GUILayoutGroup characterComponent) => private IEnumerable<GUIComponent> GetPreviousOrderIcons(GUILayoutGroup characterComponent) =>
characterComponent?.FindChildren(c => c?.UserData is OrderInfo orderInfo && orderInfo.ComponentIdentifier == "previousorder"); characterComponent?.FindChildren(c => c?.UserData is OrderInfo orderInfo && orderInfo.ComponentIdentifier == "previousorder");
private string CreateOrderTooltip(Order order, string option)
{
if (order == null) { return ""; }
if (!string.IsNullOrEmpty(option))
{
return TextManager.GetWithVariables("crewlistordericontooltip",
new string[2] { "[ordername]", "[orderoption]" },
new string[2] { order.Name, order.GetOptionName(option) });
}
else if (order.TargetEntity is Item targetItem && order.MinimapIcons.ContainsKey(targetItem.Prefab.Identifier))
{
return TextManager.GetWithVariables("crewlistordericontooltip",
new string[2] { "[ordername]", "[orderoption]" },
new string[2] { order.Name, targetItem.Name });
}
else
{
return order.Name;
}
}
private string CreateOrderTooltip(OrderInfo orderInfo) =>
CreateOrderTooltip(orderInfo.Order, orderInfo.OrderOption);
private Sprite GetOrderIconSprite(Order order, string option)
{
if (order == null) { return null; }
Sprite sprite = null;
if (option != null && order.Prefab.OptionSprites.Any())
{
order.Prefab.OptionSprites.TryGetValue(option, out sprite);
}
if (sprite == null && order.TargetEntity is Item targetItem && order.MinimapIcons.Any())
{
order.MinimapIcons.TryGetValue(targetItem.Prefab.Identifier, out sprite);
}
return sprite ?? order.SymbolSprite;
}
private Sprite GetOrderIconSprite(OrderInfo orderInfo) =>
GetOrderIconSprite(orderInfo.Order, orderInfo.OrderOption);
#endregion #endregion
#region Updating and drawing the UI #region Updating and drawing the UI
@@ -982,7 +1024,7 @@ namespace Barotrauma
public void CreateModerationContextMenu(Point mousePos, Client client) public void CreateModerationContextMenu(Point mousePos, Client client)
{ {
if (IsSinglePlayer || client == null || (GameMain.NetworkMember?.ConnectedClients?.All(match => match != client) ?? true)) { return; } if (IsSinglePlayer || client == null || (!GameMain.Client?.PreviouslyConnectedClients?.Contains(client) ?? true)) { return; }
contextMenu = new GUIFrame(new RectTransform(new Vector2(0.1f, 0.15f), GUI.Canvas) { ScreenSpaceOffset = mousePos }, style: "GUIToolTip") { UserData = client }; contextMenu = new GUIFrame(new RectTransform(new Vector2(0.1f, 0.15f), GUI.Canvas) { ScreenSpaceOffset = mousePos }, style: "GUIToolTip") { UserData = client };
@@ -1032,19 +1074,22 @@ namespace Barotrauma
UserData = "promote" UserData = "promote"
}; };
new GUITextBlock(new RectTransform(Point.Zero, parent), TextManager.Get(client.MutedLocally ? "unmute" : "mute"), font: GUI.SmallFont) if (GameMain.Client.ConnectedClients.Contains(client))
{ {
Padding = new Vector4(4), new GUITextBlock(new RectTransform(Point.Zero, parent), TextManager.Get(client.MutedLocally ? "unmute" : "mute"), font: GUI.SmallFont)
Enabled = client.ID != GameMain.Client?.ID, {
UserData = "mute" Padding = new Vector4(4),
}; Enabled = client.ID != GameMain.Client?.ID,
UserData = "mute"
};
new GUITextBlock(new RectTransform(Point.Zero, parent), TextManager.Get(canKick ? "kick" : "votetokick"), font: GUI.SmallFont) new GUITextBlock(new RectTransform(Point.Zero, parent), TextManager.Get(canKick ? "kick" : "votetokick"), font: GUI.SmallFont)
{ {
Padding = new Vector4(4), Padding = new Vector4(4),
Enabled = client.ID != GameMain.Client?.ID && client.AllowKicking, Enabled = client.ID != GameMain.Client?.ID && client.AllowKicking,
UserData = canKick ? "kick" : "votekick" UserData = canKick ? "kick" : "votekick"
}; };
}
new GUITextBlock(new RectTransform(Point.Zero, parent), TextManager.Get("ban"), font: GUI.SmallFont) new GUITextBlock(new RectTransform(Point.Zero, parent), TextManager.Get("ban"), font: GUI.SmallFont)
{ {
@@ -1363,23 +1408,13 @@ namespace Barotrauma
isSelectionHighlighted = false; isSelectionHighlighted = false;
} }
if (!CanIssueOrders) // When using Deselect to close the interface, make sure it's not a seconday mouse button click on a node
{ // That should be reserved for opening manual assignment
DisableCommandUI(); var hitDeselect = PlayerInput.KeyHit(InputType.Deselect) && (!PlayerInput.SecondaryMouseButtonClicked() ||
} (optionNodes.None(n => GUI.IsMouseOn(n.Item1)) && shortcutNodes.None(n => GUI.IsMouseOn(n))));
else if (PlayerInput.SecondaryMouseButtonClicked() && characterContext == null &&
(optionNodes.Any(n => GUI.IsMouseOn(n.Item1)) || shortcutNodes.Any(n => GUI.IsMouseOn(n))))
{
var node = optionNodes.Find(n => GUI.IsMouseOn(n.Item1))?.Item1 ?? shortcutNodes.Find(n => GUI.IsMouseOn(n));
// Make sure the node is for an option-less order or an order option
if ((node.UserData is Order order && !order.HasOptions && (!order.MustSetTarget || itemContext != null)) || node.UserData is Tuple<Order, string>)
{
CreateAssignmentNodes(node);
}
}
// TODO: Consider using HUD.CloseHUD() instead of KeyHit(Escape), the former method is also used for health UI // TODO: Consider using HUD.CloseHUD() instead of KeyHit(Escape), the former method is also used for health UI
else if ((PlayerInput.KeyHit(InputType.Command) && selectedNode == null && !clicklessSelectionActive) || if (hitDeselect || PlayerInput.KeyHit(Keys.Escape) || !CanIssueOrders ||
PlayerInput.KeyHit(InputType.Deselect) || PlayerInput.KeyHit(Keys.Escape)) (PlayerInput.KeyHit(InputType.Command) && selectedNode == null && !clicklessSelectionActive))
{ {
DisableCommandUI(); DisableCommandUI();
} }
@@ -1438,7 +1473,14 @@ namespace Barotrauma
timeSelected += deltaTime; timeSelected += deltaTime;
if (timeSelected >= selectionTime) if (timeSelected >= selectionTime)
{ {
selectedNode.OnClicked?.Invoke(selectedNode, selectedNode.UserData); if (PlayerInput.IsShiftDown() && selectedNode.OnSecondaryClicked != null)
{
selectedNode.OnSecondaryClicked.Invoke(selectedNode, selectedNode.UserData);
}
else
{
selectedNode.OnClicked?.Invoke(selectedNode, selectedNode.UserData);
}
ResetNodeSelection(); ResetNodeSelection();
} }
else if (timeSelected >= 0.15f && !isSelectionHighlighted) else if (timeSelected >= 0.15f && !isSelectionHighlighted)
@@ -1463,7 +1505,15 @@ namespace Barotrauma
{ {
if (node.Item2 != Keys.None && PlayerInput.KeyHit(node.Item2)) if (node.Item2 != Keys.None && PlayerInput.KeyHit(node.Item2))
{ {
(node.Item1 as GUIButton)?.OnClicked?.Invoke(node.Item1 as GUIButton, node.Item1.UserData); var b = node.Item1 as GUIButton;
if (PlayerInput.IsShiftDown() && b?.OnSecondaryClicked != null)
{
b.OnSecondaryClicked.Invoke(node.Item1 as GUIButton, node.Item1.UserData);
}
else
{
b?.OnClicked?.Invoke(node.Item1 as GUIButton, node.Item1.UserData);
}
ResetNodeSelection(); ResetNodeSelection();
hotkeyHit = true; hotkeyHit = true;
break; break;
@@ -1947,7 +1997,12 @@ namespace Barotrauma
} }
// When the mini map is shown, always position the return node on the bottom // When the mini map is shown, always position the return node on the bottom
var offset = node?.UserData is Order order && order.GetMatchingItems(true).Count > 1 ? List<Item> matchingItems = null;
if (node?.UserData is Order order)
{
matchingItems = order.GetMatchingItems(true, interactableFor: characterContext ?? Character.Controlled);
}
var offset = matchingItems != null && matchingItems.Count > 1 ?
new Point(0, (int)(returnNodeDistanceModifier * nodeDistance)) : new Point(0, (int)(returnNodeDistanceModifier * nodeDistance)) :
node.RectTransform.AbsoluteOffset.Multiply(-returnNodeDistanceModifier); node.RectTransform.AbsoluteOffset.Multiply(-returnNodeDistanceModifier);
SetReturnNode(centerNode, offset); SetReturnNode(centerNode, offset);
@@ -2027,6 +2082,7 @@ namespace Barotrauma
SetCharacterTooltip(c, characterContext); SetCharacterTooltip(c, characterContext);
} }
node.OnClicked = null; node.OnClicked = null;
node.OnSecondaryClicked = null;
centerNode = node; centerNode = node;
} }
@@ -2042,6 +2098,7 @@ namespace Barotrauma
c.ToolTip = TextManager.Get("commandui.return"); c.ToolTip = TextManager.Get("commandui.return");
} }
node.OnClicked = NavigateBackward; node.OnClicked = NavigateBackward;
node.OnSecondaryClicked = null;
returnNode = node; returnNode = node;
} }
@@ -2072,11 +2129,14 @@ namespace Barotrauma
private void RemoveOptionNodes() private void RemoveOptionNodes()
{ {
optionNodes.ForEach(node => commandFrame.RemoveChild(node.Item1)); if (commandFrame != null)
{
optionNodes.ForEach(node => commandFrame.RemoveChild(node.Item1));
shortcutNodes.ForEach(node => commandFrame.RemoveChild(node));
commandFrame.RemoveChild(expandNode);
}
optionNodes.Clear(); optionNodes.Clear();
shortcutNodes.ForEach(node => commandFrame.RemoveChild(node));
shortcutNodes.Clear(); shortcutNodes.Clear();
commandFrame.RemoveChild(expandNode);
expandNode = null; expandNode = null;
expandNodeHotkey = Keys.None; expandNodeHotkey = Keys.None;
RemoveExtraOptionNodes(); RemoveExtraOptionNodes();
@@ -2084,7 +2144,10 @@ namespace Barotrauma
private void RemoveExtraOptionNodes() private void RemoveExtraOptionNodes()
{ {
extraOptionNodes.ForEach(node => commandFrame.RemoveChild(node)); if (commandFrame != null)
{
extraOptionNodes.ForEach(node => commandFrame.RemoveChild(node));
}
extraOptionNodes.Clear(); extraOptionNodes.Clear();
} }
@@ -2125,7 +2188,8 @@ namespace Barotrauma
shortcutNodes.Clear(); shortcutNodes.Clear();
if (shortcutNodes.Count < maxShortCutNodeCount && sub.GetItems(false).Find(i => i.HasTag("reactor") && !i.NonInteractable)?.GetComponent<Reactor>() is Reactor reactor) if (shortcutNodes.Count < maxShortCutNodeCount &&
sub.GetItems(false).Find(i => i.HasTag("reactor") && i.IsPlayerTeamInteractable)?.GetComponent<Reactor>() is Reactor reactor)
{ {
var reactorOutput = -reactor.CurrPowerConsumption; var reactorOutput = -reactor.CurrPowerConsumption;
// If player is not an engineer AND the reactor is not powered up AND nobody is using the reactor // If player is not an engineer AND the reactor is not powered up AND nobody is using the reactor
@@ -2144,7 +2208,7 @@ namespace Barotrauma
// If player is not a captain AND nobody is using the nav terminal AND the nav terminal is powered up // If player is not a captain AND nobody is using the nav terminal AND the nav terminal is powered up
// --> Create shortcut node for Steer order // --> Create shortcut node for Steer order
if (shortcutNodes.Count < maxShortCutNodeCount && (Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("captain")) && if (shortcutNodes.Count < maxShortCutNodeCount && (Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("captain")) &&
sub.GetItems(false).Find(i => i.HasTag("navterminal") && !i.NonInteractable) is Item nav && characters.None(c => c.SelectedConstruction == nav) && sub.GetItems(false).Find(i => i.HasTag("navterminal") && i.IsPlayerTeamInteractable) is Item nav && characters.None(c => c.SelectedConstruction == nav) &&
nav.GetComponent<Steering>() is Steering steering && steering.Voltage > steering.MinVoltage) nav.GetComponent<Steering>() is Steering steering && steering.Voltage > steering.MinVoltage)
{ {
shortcutNodes.Add( shortcutNodes.Add(
@@ -2195,7 +2259,7 @@ namespace Barotrauma
(n.UserData is Tuple<Order, string> orderWithOption && orderWithOption.Item1.Identifier == orderIdentifier)) && (n.UserData is Tuple<Order, string> orderWithOption && orderWithOption.Item1.Identifier == orderIdentifier)) &&
!orderPrefab.IsReport && orderPrefab.Category != null) !orderPrefab.IsReport && orderPrefab.Category != null)
{ {
if (!orderPrefab.MustSetTarget || orderPrefab.GetMatchingItems(sub, true).Any()) if (!orderPrefab.MustSetTarget || orderPrefab.GetMatchingItems(sub, true, interactableFor: characterContext ?? Character.Controlled).Any())
{ {
shortcutNodes.Add(CreateOrderNode(shortcutNodeSize, null, Point.Zero, orderPrefab, -1)); shortcutNodes.Add(CreateOrderNode(shortcutNodeSize, null, Point.Zero, orderPrefab, -1));
} }
@@ -2245,7 +2309,8 @@ namespace Barotrauma
{ {
order = orders[i]; order = orders[i];
disableNode = !CanSomeoneHearCharacter() || disableNode = !CanSomeoneHearCharacter() ||
(order.MustSetTarget && (order.ItemComponentType != null || order.TargetItems.Length > 0) && order.GetMatchingItems(true).None()); (order.MustSetTarget && (order.ItemComponentType != null || order.TargetItems.Length > 0) &&
order.GetMatchingItems(true, interactableFor: characterContext ?? Character.Controlled).None());
optionNodes.Add(new Tuple<GUIComponent, Keys>( optionNodes.Add(new Tuple<GUIComponent, Keys>(
CreateOrderNode(nodeSize, commandFrame.RectTransform, offsets[i].ToPoint(), order, (i + 1) % 10, disableNode: disableNode, checkIfOrderCanBeHeard: false), CreateOrderNode(nodeSize, commandFrame.RectTransform, offsets[i].ToPoint(), order, (i + 1) % 10, disableNode: disableNode, checkIfOrderCanBeHeard: false),
!disableNode ? Keys.D0 + (i + 1) % 10 : Keys.None)); !disableNode ? Keys.D0 + (i + 1) % 10 : Keys.None));
@@ -2262,7 +2327,7 @@ namespace Barotrauma
string orderIdentifier; string orderIdentifier;
// Check if targeting an item or a hull // Check if targeting an item or a hull
if (itemContext != null && !itemContext.NonInteractable) if (itemContext != null && itemContext.IsPlayerTeamInteractable)
{ {
ItemComponent targetComponent; ItemComponent targetComponent;
foreach (Order p in Order.PrefabList) foreach (Order p in Order.PrefabList)
@@ -2314,9 +2379,12 @@ namespace Barotrauma
if (contextualOrders.None()) if (contextualOrders.None())
{ {
orderIdentifier = "cleanupitems"; orderIdentifier = "cleanupitems";
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)) && AIObjectiveCleanupItems.IsValidTarget(itemContext, Character.Controlled, checkInventory: false)) if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)))
{ {
contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), itemContext, targetItem: null, Character.Controlled)); if (AIObjectiveCleanupItems.IsValidTarget(itemContext, Character.Controlled, checkInventory: false) || AIObjectiveCleanupItems.IsValidContainer(itemContext, Character.Controlled))
{
contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), itemContext, targetItem: null, Character.Controlled));
}
} }
} }
@@ -2332,6 +2400,35 @@ namespace Barotrauma
} }
} }
void AddIgnoreOrder(IIgnorable target)
{
var orderIdentifier = "ignorethis";
if (!target.OrderedToBeIgnored && contextualOrders.None(o => o.Identifier == orderIdentifier))
{
AddOrder();
}
else
{
orderIdentifier = "unignorethis";
if (target.OrderedToBeIgnored && contextualOrders.None(o => o.Identifier == orderIdentifier))
{
AddOrder();
}
}
void AddOrder()
{
if (target is WallSection ws)
{
contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), ws.Wall, ws.Wall.Sections.IndexOf(ws), orderGiver: Character.Controlled));
}
else
{
contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), target as Entity, null, Character.Controlled));
}
}
}
orderIdentifier = "wait"; orderIdentifier = "wait";
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier))) if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)))
{ {
@@ -2366,35 +2463,6 @@ namespace Barotrauma
CreateOrderNode(nodeSize, commandFrame.RectTransform, offsets[i].ToPoint(), contextualOrders[i], (i + 1) % 10, disableNode: disableNode, checkIfOrderCanBeHeard: false), CreateOrderNode(nodeSize, commandFrame.RectTransform, offsets[i].ToPoint(), contextualOrders[i], (i + 1) % 10, disableNode: disableNode, checkIfOrderCanBeHeard: false),
!disableNode ? Keys.D0 + (i + 1) % 10 : Keys.None)); !disableNode ? Keys.D0 + (i + 1) % 10 : Keys.None));
} }
void AddIgnoreOrder(ISpatialEntity target)
{
var orderIdentifier = "ignorethis";
if (!target.IgnoreByAI && contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)))
{
AddOrder(orderIdentifier, target);
}
else
{
orderIdentifier = "unignorethis";
if (target.IgnoreByAI && contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)))
{
AddOrder(orderIdentifier, target);
}
}
void AddOrder(string id, ISpatialEntity target)
{
if (target is WallSection ws)
{
contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), ws.Wall, ws.Wall.Sections.IndexOf(ws), orderGiver: Character.Controlled));
}
else
{
contextualOrders.Add(new Order(Order.GetPrefab(orderIdentifier), target as Entity, null, Character.Controlled));
}
}
}
} }
// TODO: there's duplicate logic here and above -> would be better to refactor so that the conditions are only defined in one place // TODO: there's duplicate logic here and above -> would be better to refactor so that the conditions are only defined in one place
@@ -2404,6 +2472,7 @@ namespace Barotrauma
if (Order.PrefabList.Any(o => item.HasTag(o.TargetItems))) { return true; } if (Order.PrefabList.Any(o => item.HasTag(o.TargetItems))) { return true; }
if (Order.PrefabList.Any(o => o.TryGetTargetItemComponent(item, out _))) { return true; } if (Order.PrefabList.Any(o => o.TryGetTargetItemComponent(item, out _))) { return true; }
if (AIObjectiveCleanupItems.IsValidTarget(item, Character.Controlled, checkInventory: false)) { return true; } if (AIObjectiveCleanupItems.IsValidTarget(item, Character.Controlled, checkInventory: false)) { return true; }
if (AIObjectiveCleanupItems.IsValidContainer(item, Character.Controlled)) { return true; }
if (item.Repairables.Any(r => item.ConditionPercentage < r.RepairThreshold)) { return true; } if (item.Repairables.Any(r => item.ConditionPercentage < r.RepairThreshold)) { return true; }
var operateWeaponsPrefab = Order.GetPrefab("operateweapons"); var operateWeaponsPrefab = Order.GetPrefab("operateweapons");
@@ -2435,7 +2504,7 @@ namespace Barotrauma
// so we know to directly target that with the order // so we know to directly target that with the order
if (!mustSetOptionOrTarget && order.MustSetTarget && itemContext == null) if (!mustSetOptionOrTarget && order.MustSetTarget && itemContext == null)
{ {
var matchingItems = order.GetMatchingItems(GetTargetSubmarine(), true); var matchingItems = order.GetMatchingItems(GetTargetSubmarine(), true, interactableFor: characterContext ?? Character.Controlled);
if (matchingItems.Count > 1) if (matchingItems.Count > 1)
{ {
mustSetOptionOrTarget = true; mustSetOptionOrTarget = true;
@@ -2470,9 +2539,14 @@ namespace Barotrauma
} }
return true; return true;
}; };
// TODO: Might need to edit the tooltip if (CanOpenManualAssignment(node))
{
node.OnSecondaryClicked = (button, _) => CreateAssignmentNodes(button);
}
var showAssignmentTooltip = !mustSetOptionOrTarget && characterContext == null && !order.MustManuallyAssign && !order.TargetAllCharacters;
var orderName = GetOrderNameBasedOnContextuality(order);
var icon = CreateNodeIcon(node.RectTransform, order.SymbolSprite, order.Color, var icon = CreateNodeIcon(node.RectTransform, order.SymbolSprite, order.Color,
tooltip: mustSetOptionOrTarget || characterContext != null ? order.Name : order.Name + tooltip: !showAssignmentTooltip ? orderName : orderName +
"\n" + (!PlayerInput.MouseButtonsSwapped() ? TextManager.Get("input.leftmouse") : TextManager.Get("input.rightmouse")) + ": " + TextManager.Get("commandui.quickassigntooltip") + "\n" + (!PlayerInput.MouseButtonsSwapped() ? TextManager.Get("input.leftmouse") : TextManager.Get("input.rightmouse")) + ": " + TextManager.Get("commandui.quickassigntooltip") +
"\n" + (!PlayerInput.MouseButtonsSwapped() ? TextManager.Get("input.rightmouse") : TextManager.Get("input.leftmouse")) + ": " + TextManager.Get("commandui.manualassigntooltip")); "\n" + (!PlayerInput.MouseButtonsSwapped() ? TextManager.Get("input.rightmouse") : TextManager.Get("input.leftmouse")) + ": " + TextManager.Get("commandui.manualassigntooltip"));
@@ -2491,7 +2565,7 @@ namespace Barotrauma
private void CreateOrderOptions(Order order) private void CreateOrderOptions(Order order)
{ {
Submarine submarine = GetTargetSubmarine(); Submarine submarine = GetTargetSubmarine();
var matchingItems = (itemContext == null && order.MustSetTarget) ? order.GetMatchingItems(submarine, true) : new List<Item>(); var matchingItems = (itemContext == null && order.MustSetTarget) ? order.GetMatchingItems(submarine, true, interactableFor: characterContext ?? Character.Controlled) : new List<Item>();
//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
if (itemContext == null && matchingItems.Count > 1) if (itemContext == null && matchingItems.Count > 1)
@@ -2572,30 +2646,33 @@ namespace Barotrauma
Stretch = true Stretch = true
}; };
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), optionContainer.RectTransform), item != null ? item.Name : order.Name); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), optionContainer.RectTransform),
item?.Name ?? GetOrderNameBasedOnContextuality(order));
for (int i = 0; i < order.Options.Length; i++) for (int i = 0; i < order.Options.Length; i++)
{ {
optionNodes.Add(new Tuple<GUIComponent, Keys>( var optionButton = new GUIButton(
new GUIButton( new RectTransform(new Vector2(1.0f, 0.2f), optionContainer.RectTransform),
new RectTransform(new Vector2(1.0f, 0.2f), optionContainer.RectTransform), text: order.GetOptionName(i), style: "GUITextBox")
text: order.GetOptionName(i), {
style: "GUITextBox") UserData = new Tuple<Order, string>(
item == null ? order : new Order(order, item, order.GetTargetItemComponent(item)),
order.Options[i]),
Font = GUI.SmallFont,
OnClicked = (_, userData) =>
{ {
UserData = new Tuple<Order, string>( if (!CanIssueOrders) { return false; }
item == null ? order : new Order(order, item, order.GetTargetItemComponent(item)), var o = userData as Tuple<Order, string>;
order.Options[i]), SetCharacterOrder(characterContext ?? GetCharacterForQuickAssignment(o.Item1), o.Item1, o.Item2, Character.Controlled);
Font = GUI.SmallFont, DisableCommandUI();
OnClicked = (_, userData) => return true;
{ }
if (!CanIssueOrders) { return false; } };
var o = userData as Tuple<Order, string>; if (CanOpenManualAssignment(optionButton))
SetCharacterOrder(characterContext ?? GetCharacterForQuickAssignment(o.Item1), o.Item1, o.Item2, Character.Controlled); {
DisableCommandUI(); optionButton.OnSecondaryClicked = (button, _) => CreateAssignmentNodes(button);
return true; }
} optionNodes.Add(new Tuple<GUIComponent, Keys>(optionButton, Keys.None));
},
Keys.None));
} }
} }
else else
@@ -2610,7 +2687,7 @@ namespace Barotrauma
{ {
UserData = userData, UserData = userData,
Font = GUI.SmallFont, Font = GUI.SmallFont,
ToolTip = item?.Name ?? order.Name, ToolTip = item?.Name ?? GetOrderNameBasedOnContextuality(order),
OnClicked = (_, userData) => OnClicked = (_, userData) =>
{ {
if (!CanIssueOrders) { return false; } if (!CanIssueOrders) { return false; }
@@ -2620,7 +2697,10 @@ namespace Barotrauma
return true; return true;
} }
}; };
if (CanOpenManualAssignment(optionElement))
{
optionElement.OnSecondaryClicked = (button, _) => CreateAssignmentNodes(button);
}
Sprite icon = null; Sprite icon = null;
order.MinimapIcons?.TryGetValue(item.Prefab.Identifier, out icon); order.MinimapIcons?.TryGetValue(item.Prefab.Identifier, out icon);
if (item.Prefab.MinimapIcon != null) if (item.Prefab.MinimapIcon != null)
@@ -2677,11 +2757,16 @@ namespace Barotrauma
return true; return true;
} }
}; };
if (CanOpenManualAssignment(node))
{
node.OnSecondaryClicked = (button, _) => CreateAssignmentNodes(button);
}
node.RectTransform.MoveOverTime(offset, CommandNodeAnimDuration); node.RectTransform.MoveOverTime(offset, CommandNodeAnimDuration);
GUIImage icon = null; GUIImage icon = null;
if (order.Prefab.OptionSprites.TryGetValue(option, out Sprite sprite)) if (order.Prefab.OptionSprites.TryGetValue(option, out Sprite sprite))
{ {
var showAssignmentTooltip = characterContext == null && !order.MustManuallyAssign && !order.TargetAllCharacters;
icon = CreateNodeIcon(node.RectTransform, sprite, order.Color, icon = CreateNodeIcon(node.RectTransform, sprite, order.Color,
tooltip: characterContext != null ? optionName : optionName + tooltip: characterContext != null ? optionName : optionName +
"\n" + (!PlayerInput.MouseButtonsSwapped() ? TextManager.Get("input.leftmouse") : TextManager.Get("input.rightmouse")) + ": " + TextManager.Get("commandui.quickassigntooltip") + "\n" + (!PlayerInput.MouseButtonsSwapped() ? TextManager.Get("input.leftmouse") : TextManager.Get("input.rightmouse")) + ": " + TextManager.Get("commandui.quickassigntooltip") +
@@ -2700,13 +2785,13 @@ namespace Barotrauma
return node; return node;
} }
private void CreateAssignmentNodes(GUIComponent node) private bool CreateAssignmentNodes(GUIComponent node)
{ {
var order = (node.UserData is Order) ? var order = (node.UserData is Order) ?
new Tuple<Order, string>(node.UserData as Order, null) : new Tuple<Order, string>(node.UserData as Order, null) :
node.UserData as Tuple<Order, string>; node.UserData as Tuple<Order, string>;
var characters = GetCharactersForManualAssignment(order.Item1); var characters = GetCharactersForManualAssignment(order.Item1);
if (characters.None()) { return; } if (characters.None()) { return false; }
if (!(optionNodes.Find(n => n.Item1 == node) is Tuple<GUIComponent, Keys> optionNode) || !optionNodes.Remove(optionNode)) if (!(optionNodes.Find(n => n.Item1 == node) is Tuple<GUIComponent, Keys> optionNode) || !optionNodes.Remove(optionNode))
{ {
@@ -2791,7 +2876,7 @@ namespace Barotrauma
CreateHotkeyIcon(returnNode.RectTransform, hotkey % 10, true); CreateHotkeyIcon(returnNode.RectTransform, hotkey % 10, true);
returnNodeHotkey = Keys.D0 + hotkey % 10; returnNodeHotkey = Keys.D0 + hotkey % 10;
expandNodeHotkey = Keys.None; expandNodeHotkey = Keys.None;
return; return true;
} }
extraOptionCharacters.Clear(); extraOptionCharacters.Clear();
@@ -2816,6 +2901,7 @@ namespace Barotrauma
expandNodeHotkey = Keys.D0 + hotkey % 10; expandNodeHotkey = Keys.D0 + hotkey % 10;
CreateHotkeyIcon(returnNode.RectTransform, ++hotkey % 10, true); CreateHotkeyIcon(returnNode.RectTransform, ++hotkey % 10, true);
returnNodeHotkey = Keys.D0 + hotkey % 10; returnNodeHotkey = Keys.D0 + hotkey % 10;
return true;
} }
private Vector2[] GetAssignmentNodeOffsets(int characters, bool firstRing = true) private Vector2[] GetAssignmentNodeOffsets(int characters, bool firstRing = true)
@@ -3083,7 +3169,7 @@ namespace Barotrauma
if (Character.Controlled != null) if (Character.Controlled != null)
{ {
// Pick the second main sub when we have two teams (in combat mission) // Pick the second main sub when we have two teams (in combat mission)
if (Character.Controlled.TeamID == Character.TeamType.Team2 && Submarine.MainSubs.Length > 1) if (Character.Controlled.TeamID == CharacterTeamType.Team2 && Submarine.MainSubs.Length > 1)
{ {
sub = Submarine.MainSubs[1]; sub = Submarine.MainSubs[1];
} }
@@ -3105,7 +3191,29 @@ namespace Barotrauma
component.ToolTip = tooltip; component.ToolTip = tooltip;
} }
private string GetOrderNameBasedOnContextuality(Order order)
{
if (order == null) { return ""; }
if (isContextual) { return order.ContextualName; }
return order.Name;
}
#region Crew Member Assignment Logic #region Crew Member Assignment Logic
private bool CanOpenManualAssignment(GUIComponent node)
{
if (node == null || characterContext != null) { return false; }
if (node.UserData is Tuple<Order, string> orderInfo)
{
return !orderInfo.Item1.TargetAllCharacters;
}
if (node.UserData is Order order)
{
return !order.TargetAllCharacters && !order.HasOptions &&
(!order.MustSetTarget || itemContext != null ||
order.GetMatchingItems(GetTargetSubmarine(), true, interactableFor: Character.Controlled).Count < 2);
}
return false;
}
private Character GetCharacterForQuickAssignment(Order order) private Character GetCharacterForQuickAssignment(Order order)
{ {
@@ -3145,7 +3253,7 @@ namespace Barotrauma
// 4. Prioritize bots over player controlled characters // 4. Prioritize bots over player controlled characters
.ThenByDescending(c => c.IsBot) .ThenByDescending(c => c.IsBot)
// 5. Use the priority value of the current objective // 5. Use the priority value of the current objective
.ThenBy(c => c.AIController?.ObjectiveManager.CurrentObjective?.Priority) .ThenBy(c => c.AIController is HumanAIController humanAI ? humanAI.ObjectiveManager.CurrentObjective?.Priority : 0)
// 6. Prioritize those with the best skill for the order // 6. Prioritize those with the best skill for the order
.ThenByDescending(c => c.GetSkillLevel(order.AppropriateSkill)); .ThenByDescending(c => c.GetSkillLevel(order.AppropriateSkill));
} }
@@ -450,7 +450,7 @@ namespace Barotrauma
{ {
if (mb is GUIMessageBox msgBox) if (mb is GUIMessageBox msgBox)
{ {
if (mb.UserData is Pair<string, ushort> pair && pair.First.Equals("conversationaction", StringComparison.OrdinalIgnoreCase)) if (ReadyCheck.IsReadyCheck(mb) || mb.UserData is Pair<string, ushort> pair && pair.First.Equals("conversationaction", StringComparison.OrdinalIgnoreCase))
{ {
msgBox.Close(); msgBox.Close();
} }
@@ -812,8 +812,7 @@ namespace Barotrauma
return; return;
} }
Load(doc.Root.Element("MultiPlayerCampaign")); Load(doc.Root.Element("MultiPlayerCampaign"));
SubmarineInfo selectedSub; GameMain.GameSession.OwnedSubmarines = SaveUtil.LoadOwnedSubmarines(doc, out SubmarineInfo selectedSub);
GameMain.GameSession.OwnedSubmarines = SaveUtil.LoadOwnedSubmarines(doc, out selectedSub);
GameMain.GameSession.SubmarineInfo = selectedSub; GameMain.GameSession.SubmarineInfo = selectedSub;
} }
} }
@@ -11,6 +11,8 @@ namespace Barotrauma
{ {
class SinglePlayerCampaign : CampaignMode class SinglePlayerCampaign : CampaignMode
{ {
public const int MinimumInitialMoney = 0;
public override bool Paused public override bool Paused
{ {
get { return ForceMapUI || CoroutineManager.IsCoroutineRunning("LevelTransition") || ShowCampaignUI && CampaignUI.SelectedTab == InteractionType.Map; } get { return ForceMapUI || CoroutineManager.IsCoroutineRunning("LevelTransition") || ShowCampaignUI && CampaignUI.SelectedTab == InteractionType.Map; }
@@ -105,7 +107,6 @@ namespace Barotrauma
} }
CampaignMetadata ??= new CampaignMetadata(this); CampaignMetadata ??= new CampaignMetadata(this);
UpgradeManager ??= new UpgradeManager(this); UpgradeManager ??= new UpgradeManager(this);
InitCampaignData(); InitCampaignData();
@@ -113,6 +114,9 @@ namespace Barotrauma
InitUI(); InitUI();
Money = element.GetAttributeInt("money", 0); Money = element.GetAttributeInt("money", 0);
PurchasedLostShuttles = element.GetAttributeBool("purchasedlostshuttles", false);
PurchasedHullRepairs = element.GetAttributeBool("purchasedhullrepairs", false);
PurchasedItemRepairs = element.GetAttributeBool("purchaseditemrepairs", false);
CheatsEnabled = element.GetAttributeBool("cheatsenabled", false); CheatsEnabled = element.GetAttributeBool("cheatsenabled", false);
if (CheatsEnabled) if (CheatsEnabled)
{ {
@@ -137,7 +141,7 @@ namespace Barotrauma
/// <summary> /// <summary>
/// Start a completely new single player campaign /// Start a completely new single player campaign
/// </summary> /// </summary>
public static SinglePlayerCampaign StartNew(string mapSeed) public static SinglePlayerCampaign StartNew(string mapSeed, SubmarineInfo selectedSub)
{ {
var campaign = new SinglePlayerCampaign(mapSeed); var campaign = new SinglePlayerCampaign(mapSeed);
return campaign; return campaign;
@@ -699,6 +703,9 @@ namespace Barotrauma
{ {
XElement modeElement = new XElement("SinglePlayerCampaign", XElement modeElement = new XElement("SinglePlayerCampaign",
new XAttribute("money", Money), new XAttribute("money", Money),
new XAttribute("purchasedlostshuttles", PurchasedLostShuttles),
new XAttribute("purchasedhullrepairs", PurchasedHullRepairs),
new XAttribute("purchaseditemrepairs", PurchasedItemRepairs),
new XAttribute("cheatsenabled", CheatsEnabled)); new XAttribute("cheatsenabled", CheatsEnabled));
//save and remove all items that are in someone's inventory so they don't get included in the sub file as well //save and remove all items that are in someone's inventory so they don't get included in the sub file as well
@@ -182,7 +182,7 @@ namespace Barotrauma.Tutorials
+ " Equip a screwdriver by pulling it to either of the slots with a hand symbol, and then use it on the terminal by left clicking."); + " Equip a screwdriver by pulling it to either of the slots with a hand symbol, and then use it on the terminal by left clicking.");
while (Controlled.SelectedConstruction != steering.Item || while (Controlled.SelectedConstruction != steering.Item ||
Controlled.SelectedItems.FirstOrDefault(i => i != null && i.Prefab.Identifier == "screwdriver") == null) Controlled.HeldItems.FirstOrDefault(i => i.Prefab.Identifier == "screwdriver") == null)
{ {
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running; yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
} }
@@ -203,16 +203,16 @@ namespace Barotrauma.Tutorials
while ((Controlled.SelectedConstruction != junctionBox.Item && while ((Controlled.SelectedConstruction != junctionBox.Item &&
Controlled.SelectedConstruction != steering.Item) || Controlled.SelectedConstruction != steering.Item) ||
Controlled.SelectedItems.FirstOrDefault(i => i != null && i.Prefab.Identifier == "screwdriver") == null) !Controlled.HeldItems.Any(i => i.Prefab.Identifier == "screwdriver"))
{ {
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running; yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
} }
if (Controlled.SelectedItems.FirstOrDefault(i => i != null && i.GetComponent<Wire>() != null) == null) if (!Controlled.HeldItems.Any(i => i.GetComponent<Wire>() != null))
{ {
infoBox = CreateInfoFrame("", "Equip the wire by dragging it to one of the slots with a hand symbol."); infoBox = CreateInfoFrame("", "Equip the wire by dragging it to one of the slots with a hand symbol.");
while (Controlled.SelectedItems.FirstOrDefault(i => i != null && i.GetComponent<Wire>() != null) == null) while (!Controlled.HeldItems.Any(i => i.GetComponent<Wire>() != null))
{ {
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running; yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
} }
@@ -501,7 +501,7 @@ namespace Barotrauma.Tutorials
do do
{ {
var weldingTool = Controlled.Inventory.Items.FirstOrDefault(i => i != null && i.Prefab.Identifier == "weldingtool"); var weldingTool = Controlled.Inventory.FindItemByIdentifier("weldingtool");
if (weldingTool != null && if (weldingTool != null &&
weldingTool.ContainedItems.FirstOrDefault(contained => contained != null && contained.Prefab.Identifier == "weldingfueltank") != null) break; weldingTool.ContainedItems.FirstOrDefault(contained => contained != null && contained.Prefab.Identifier == "weldingfueltank") != null) break;
@@ -661,7 +661,10 @@ namespace Barotrauma.Tutorials
//TODO: reimplement //TODO: reimplement
//enemy.Health = 50.0f; //enemy.Health = 50.0f;
enemy.AIController.State = AIState.Idle; if (enemy.AIController is EnemyAIController enemyAI)
{
enemyAI.State = AIState.Idle;
}
Vector2 targetPos = Character.Controlled.WorldPosition + new Vector2(0.0f, 3000.0f); Vector2 targetPos = Character.Controlled.WorldPosition + new Vector2(0.0f, 3000.0f);
@@ -101,7 +101,7 @@ namespace Barotrauma.Tutorials
tutorial_submarineDoorLight = Item.ItemList.Find(i => i.HasTag("tutorial_submarinedoorlight")).GetComponent<LightComponent>(); tutorial_submarineDoorLight = Item.ItemList.Find(i => i.HasTag("tutorial_submarinedoorlight")).GetComponent<LightComponent>();
var medicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("medicaldoctor")); var medicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("medicaldoctor"));
captain_medic = Character.Create(medicInfo, captain_medicSpawnPos, "medicaldoctor"); captain_medic = Character.Create(medicInfo, captain_medicSpawnPos, "medicaldoctor");
captain_medic.TeamID = Character.TeamType.Team1; captain_medic.TeamID = CharacterTeamType.Team1;
captain_medic.GiveJobItems(null); captain_medic.GiveJobItems(null);
captain_medic.CanSpeak = captain_medic.AIController.Enabled = false; captain_medic.CanSpeak = captain_medic.AIController.Enabled = false;
SetDoorAccess(tutorial_submarineDoor, tutorial_submarineDoorLight, false); SetDoorAccess(tutorial_submarineDoor, tutorial_submarineDoorLight, false);
@@ -124,17 +124,17 @@ namespace Barotrauma.Tutorials
var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("mechanic")); var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("mechanic"));
captain_mechanic = Character.Create(mechanicInfo, WayPoint.GetRandom(SpawnType.Human, mechanicInfo.Job, Submarine.MainSub).WorldPosition, "mechanic"); captain_mechanic = Character.Create(mechanicInfo, WayPoint.GetRandom(SpawnType.Human, mechanicInfo.Job, Submarine.MainSub).WorldPosition, "mechanic");
captain_mechanic.TeamID = Character.TeamType.Team1; captain_mechanic.TeamID = CharacterTeamType.Team1;
captain_mechanic.GiveJobItems(); captain_mechanic.GiveJobItems();
var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("securityofficer")); var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("securityofficer"));
captain_security = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job, Submarine.MainSub).WorldPosition, "securityofficer"); captain_security = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job, Submarine.MainSub).WorldPosition, "securityofficer");
captain_security.TeamID = Character.TeamType.Team1; captain_security.TeamID = CharacterTeamType.Team1;
captain_security.GiveJobItems(); captain_security.GiveJobItems();
var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer")); var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer"));
captain_engineer = Character.Create(engineerInfo, WayPoint.GetRandom(SpawnType.Human, engineerInfo.Job, Submarine.MainSub).WorldPosition, "engineer"); captain_engineer = Character.Create(engineerInfo, WayPoint.GetRandom(SpawnType.Human, engineerInfo.Job, Submarine.MainSub).WorldPosition, "engineer");
captain_engineer.TeamID = Character.TeamType.Team1; captain_engineer.TeamID = CharacterTeamType.Team1;
captain_engineer.GiveJobItems(); captain_engineer.GiveJobItems();
captain_mechanic.CanSpeak = captain_security.CanSpeak = captain_engineer.CanSpeak = false; captain_mechanic.CanSpeak = captain_security.CanSpeak = captain_engineer.CanSpeak = false;
@@ -80,34 +80,34 @@ namespace Barotrauma.Tutorials
var assistantInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("assistant")); var assistantInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("assistant"));
patient1 = Character.Create(assistantInfo, patientHull1.WorldPosition, "1"); patient1 = Character.Create(assistantInfo, patientHull1.WorldPosition, "1");
patient1.TeamID = Character.TeamType.Team1; patient1.TeamID = CharacterTeamType.Team1;
patient1.GiveJobItems(null); patient1.GiveJobItems(null);
patient1.CanSpeak = false; patient1.CanSpeak = false;
patient1.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 45.0f) }, stun: 0, playSound: false); patient1.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 15.0f) }, stun: 0, playSound: false);
patient1.AIController.Enabled = false; patient1.AIController.Enabled = false;
assistantInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("assistant")); assistantInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("assistant"));
patient2 = Character.Create(assistantInfo, patientHull2.WorldPosition, "2"); patient2 = Character.Create(assistantInfo, patientHull2.WorldPosition, "2");
patient2.TeamID = Character.TeamType.Team1; patient2.TeamID = CharacterTeamType.Team1;
patient2.GiveJobItems(null); patient2.GiveJobItems(null);
patient2.CanSpeak = false; patient2.CanSpeak = false;
patient2.AIController.Enabled = false; patient2.AIController.Enabled = false;
var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer")); var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer"));
var subPatient1 = Character.Create(mechanicInfo, WayPoint.GetRandom(SpawnType.Human, mechanicInfo.Job, Submarine.MainSub).WorldPosition, "3"); var subPatient1 = Character.Create(mechanicInfo, WayPoint.GetRandom(SpawnType.Human, mechanicInfo.Job, Submarine.MainSub).WorldPosition, "3");
subPatient1.TeamID = Character.TeamType.Team1; subPatient1.TeamID = CharacterTeamType.Team1;
subPatient1.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 40.0f) }, stun: 0, playSound: false); subPatient1.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 40.0f) }, stun: 0, playSound: false);
subPatients.Add(subPatient1); subPatients.Add(subPatient1);
var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("securityofficer")); var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("securityofficer"));
var subPatient2 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job, Submarine.MainSub).WorldPosition, "3"); var subPatient2 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job, Submarine.MainSub).WorldPosition, "3");
subPatient2.TeamID = Character.TeamType.Team1; subPatient2.TeamID = CharacterTeamType.Team1;
subPatient2.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.InternalDamage, 40.0f) }, stun: 0, playSound: false); subPatient2.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.InternalDamage, 40.0f) }, stun: 0, playSound: false);
subPatients.Add(subPatient2); subPatients.Add(subPatient2);
var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer")); var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer"));
var subPatient3 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, engineerInfo.Job, Submarine.MainSub).WorldPosition, "3"); var subPatient3 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, engineerInfo.Job, Submarine.MainSub).WorldPosition, "3");
subPatient3.TeamID = Character.TeamType.Team1; subPatient3.TeamID = CharacterTeamType.Team1;
subPatient3.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 20.0f) }, stun: 0, playSound: false); subPatient3.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 20.0f) }, stun: 0, playSound: false);
subPatients.Add(subPatient3); subPatients.Add(subPatient3);
@@ -200,18 +200,18 @@ namespace Barotrauma.Tutorials
do do
{ {
for (int i = 0; i < doctor_suppliesCabinet.Inventory.Items.Length; i++) for (int i = 0; i < doctor_suppliesCabinet.Inventory.Capacity; i++)
{ {
if (doctor_suppliesCabinet.Inventory.Items[i] != null) if (doctor_suppliesCabinet.Inventory.GetItemAt(i) != null)
{ {
HighlightInventorySlot(doctor_suppliesCabinet.Inventory, i, highlightColor, .5f, .5f, 0f); HighlightInventorySlot(doctor_suppliesCabinet.Inventory, i, highlightColor, .5f, .5f, 0f);
} }
} }
if (doctor.SelectedConstruction == doctor_suppliesCabinet.Item) if (doctor.SelectedConstruction == doctor_suppliesCabinet.Item)
{ {
for (int i = 0; i < doctor.Inventory.slots.Length; i++) for (int i = 0; i < doctor.Inventory.Capacity; i++)
{ {
if (doctor.Inventory.Items[i] == null) HighlightInventorySlot(doctor.Inventory, i, highlightColor, .5f, .5f, 0f); if (doctor.Inventory.GetItemAt(i) == null) { HighlightInventorySlot(doctor.Inventory, i, highlightColor, .5f, .5f, 0f); }
} }
} }
yield return null; yield return null;
@@ -309,16 +309,16 @@ namespace Barotrauma.Tutorials
{ {
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
{ {
if (doctor_medBayCabinet.Inventory.Items[i] != null) if (doctor_medBayCabinet.Inventory.GetItemAt(i) != null)
{ {
HighlightInventorySlot(doctor_medBayCabinet.Inventory, i, highlightColor, .5f, .5f, 0f); HighlightInventorySlot(doctor_medBayCabinet.Inventory, i, highlightColor, .5f, .5f, 0f);
} }
} }
if (doctor.SelectedConstruction == doctor_medBayCabinet.Item) if (doctor.SelectedConstruction == doctor_medBayCabinet.Item)
{ {
for (int i = 0; i < doctor.Inventory.slots.Length; i++) for (int i = 0; i < doctor.Inventory.Capacity; i++)
{ {
if (doctor.Inventory.Items[i] == null) HighlightInventorySlot(doctor.Inventory, i, highlightColor, .5f, .5f, 0f); if (doctor.Inventory.GetItemAt(i) == null) { HighlightInventorySlot(doctor.Inventory, i, highlightColor, .5f, .5f, 0f); }
} }
} }
yield return null; yield return null;
@@ -247,30 +247,30 @@ namespace Barotrauma.Tutorials
if (!firstSlotRemoved) if (!firstSlotRemoved)
{ {
HighlightInventorySlot(engineer_equipmentCabinet.Inventory, 0, highlightColor, .5f, .5f, 0f); HighlightInventorySlot(engineer_equipmentCabinet.Inventory, 0, highlightColor, .5f, .5f, 0f);
if (engineer_equipmentCabinet.Inventory.Items[0] == null) firstSlotRemoved = true; if (engineer_equipmentCabinet.Inventory.GetItemAt(0) == null) { firstSlotRemoved = true; }
} }
if (!secondSlotRemoved) if (!secondSlotRemoved)
{ {
HighlightInventorySlot(engineer_equipmentCabinet.Inventory, 1, highlightColor, .5f, .5f, 0f); HighlightInventorySlot(engineer_equipmentCabinet.Inventory, 1, highlightColor, .5f, .5f, 0f);
if (engineer_equipmentCabinet.Inventory.Items[1] == null) secondSlotRemoved = true; if (engineer_equipmentCabinet.Inventory.GetItemAt(1) == null) { secondSlotRemoved = true; }
} }
if (!thirdSlotRemoved) if (!thirdSlotRemoved)
{ {
HighlightInventorySlot(engineer_equipmentCabinet.Inventory, 2, highlightColor, .5f, .5f, 0f); HighlightInventorySlot(engineer_equipmentCabinet.Inventory, 2, highlightColor, .5f, .5f, 0f);
if (engineer_equipmentCabinet.Inventory.Items[2] == null) thirdSlotRemoved = true; if (engineer_equipmentCabinet.Inventory.GetItemAt(2) == null) { thirdSlotRemoved = true; }
} }
if (!fourthSlotRemoved) if (!fourthSlotRemoved)
{ {
HighlightInventorySlot(engineer_equipmentCabinet.Inventory, 3, highlightColor, .5f, .5f, 0f); HighlightInventorySlot(engineer_equipmentCabinet.Inventory, 3, highlightColor, .5f, .5f, 0f);
if (engineer_equipmentCabinet.Inventory.Items[2] == null) fourthSlotRemoved = true; if (engineer_equipmentCabinet.Inventory.GetItemAt(2) == null) { fourthSlotRemoved = true; }
} }
for (int i = 0; i < engineer.Inventory.slots.Length; i++) for (int i = 0; i < engineer.Inventory.visualSlots.Length; i++)
{ {
if (engineer.Inventory.Items[i] == null) HighlightInventorySlot(engineer.Inventory, i, highlightColor, .5f, .5f, 0f); if (engineer.Inventory.GetItemAt(i) == null) { HighlightInventorySlot(engineer.Inventory, i, highlightColor, .5f, .5f, 0f); }
} }
} }
@@ -299,12 +299,12 @@ namespace Barotrauma.Tutorials
} while (!engineer_reactor.PowerOn); } while (!engineer_reactor.PowerOn);
do do
{ {
if (IsSelectedItem(engineer_reactor.Item) && engineer_reactor.Item.OwnInventory.slots != null) if (IsSelectedItem(engineer_reactor.Item) && engineer_reactor.Item.OwnInventory.visualSlots != null)
{ {
engineer_reactor.AutoTemp = false; engineer_reactor.AutoTemp = false;
HighlightInventorySlot(engineer.Inventory, "fuelrod", highlightColor, 0.5f, 0.5f, 0f); HighlightInventorySlot(engineer.Inventory, "fuelrod", highlightColor, 0.5f, 0.5f, 0f);
for (int i = 0; i < engineer_reactor.Item.OwnInventory.slots.Length; i++) for (int i = 0; i < engineer_reactor.Item.OwnInventory.visualSlots.Length; i++)
{ {
HighlightInventorySlot(engineer_reactor.Item.OwnInventory, i, highlightColor, 0.5f, 0.5f, 0f); HighlightInventorySlot(engineer_reactor.Item.OwnInventory, i, highlightColor, 0.5f, 0.5f, 0f);
} }
@@ -165,21 +165,23 @@ namespace Barotrauma.Tutorials
// Room 6 // Room 6
mechanic_divingSuitObjectiveSensor = Item.ItemList.Find(i => i.HasTag("mechanic_divingsuitobjectivesensor")).GetComponent<MotionSensor>(); mechanic_divingSuitObjectiveSensor = Item.ItemList.Find(i => i.HasTag("mechanic_divingsuitobjectivesensor")).GetComponent<MotionSensor>();
mechanic_divingSuitContainer = Item.ItemList.Find(i => i.HasTag("mechanic_divingsuitcontainer")).GetComponent<ItemContainer>(); mechanic_divingSuitContainer = Item.ItemList.Find(i => i.HasTag("mechanic_divingsuitcontainer")).GetComponent<ItemContainer>();
for (int i = 0; i < mechanic_divingSuitContainer.Inventory.Items.Length; i++) foreach (Item item in mechanic_divingSuitContainer.Inventory.AllItems)
{ {
foreach (ItemComponent ic in mechanic_divingSuitContainer.Inventory.Items[i].Components) foreach (ItemComponent ic in item.Components)
{ {
ic.CanBePicked = true; ic.CanBePicked = true;
} }
} }
mechanic_oxygenContainer = Item.ItemList.Find(i => i.HasTag("mechanic_oxygencontainer")).GetComponent<ItemContainer>(); mechanic_oxygenContainer = Item.ItemList.Find(i => i.HasTag("mechanic_oxygencontainer")).GetComponent<ItemContainer>();
for (int i = 0; i < mechanic_oxygenContainer.Inventory.Items.Length; i++) foreach (Item item in mechanic_oxygenContainer.Inventory.AllItems)
{ {
foreach (ItemComponent ic in mechanic_oxygenContainer.Inventory.Items[i].Components) foreach (ItemComponent ic in item.Components)
{ {
ic.CanBePicked = true; ic.CanBePicked = true;
} }
} }
tutorial_mechanicFinalDoor = Item.ItemList.Find(i => i.HasTag("tutorial_mechanicfinaldoor")).GetComponent<Door>(); tutorial_mechanicFinalDoor = Item.ItemList.Find(i => i.HasTag("tutorial_mechanicfinaldoor")).GetComponent<Door>();
tutorial_mechanicFinalDoorLight = Item.ItemList.Find(i => i.HasTag("tutorial_mechanicfinaldoorlight")).GetComponent<LightComponent>(); tutorial_mechanicFinalDoorLight = Item.ItemList.Find(i => i.HasTag("tutorial_mechanicfinaldoorlight")).GetComponent<LightComponent>();
@@ -266,24 +268,24 @@ namespace Barotrauma.Tutorials
if (!firstSlotRemoved) if (!firstSlotRemoved)
{ {
HighlightInventorySlot(mechanic_equipmentCabinet.Inventory, 0, highlightColor, .5f, .5f, 0f); HighlightInventorySlot(mechanic_equipmentCabinet.Inventory, 0, highlightColor, .5f, .5f, 0f);
if (mechanic_equipmentCabinet.Inventory.Items[0] == null) firstSlotRemoved = true; if (mechanic_equipmentCabinet.Inventory.GetItemAt(0) == null) { firstSlotRemoved = true; }
} }
if (!secondSlotRemoved) if (!secondSlotRemoved)
{ {
HighlightInventorySlot(mechanic_equipmentCabinet.Inventory, 1, highlightColor, .5f, .5f, 0f); HighlightInventorySlot(mechanic_equipmentCabinet.Inventory, 1, highlightColor, .5f, .5f, 0f);
if (mechanic_equipmentCabinet.Inventory.Items[1] == null) secondSlotRemoved = true; if (mechanic_equipmentCabinet.Inventory.GetItemAt(1) == null) { secondSlotRemoved = true; }
} }
if (!thirdSlotRemoved) if (!thirdSlotRemoved)
{ {
HighlightInventorySlot(mechanic_equipmentCabinet.Inventory, 2, highlightColor, .5f, .5f, 0f); HighlightInventorySlot(mechanic_equipmentCabinet.Inventory, 2, highlightColor, .5f, .5f, 0f);
if (mechanic_equipmentCabinet.Inventory.Items[2] == null) thirdSlotRemoved = true; if (mechanic_equipmentCabinet.Inventory.GetItemAt(2) == null) { thirdSlotRemoved = true; }
} }
for (int i = 0; i < mechanic.Inventory.slots.Length; i++) for (int i = 0; i < mechanic.Inventory.Capacity; i++)
{ {
if (mechanic.Inventory.Items[i] == null) HighlightInventorySlot(mechanic.Inventory, i, highlightColor, .5f, .5f, 0f); if (mechanic.Inventory.GetItemAt(i) == null) { HighlightInventorySlot(mechanic.Inventory, i, highlightColor, .5f, .5f, 0f); }
} }
} }
@@ -355,16 +357,16 @@ namespace Barotrauma.Tutorials
{ {
if (mechanic.SelectedConstruction == mechanic_craftingCabinet.Item) if (mechanic.SelectedConstruction == mechanic_craftingCabinet.Item)
{ {
for (int i = 0; i < mechanic.Inventory.slots.Length; i++) for (int i = 0; i < mechanic.Inventory.Capacity; i++)
{ {
if (mechanic.Inventory.Items[i] == null) HighlightInventorySlot(mechanic.Inventory, i, highlightColor, .5f, .5f, 0f); if (mechanic.Inventory.GetItemAt(i) == null) { HighlightInventorySlot(mechanic.Inventory, i, highlightColor, .5f, .5f, 0f); }
} }
if (mechanic.Inventory.FindItemByIdentifier("oxygentank") == null && mechanic.Inventory.FindItemByIdentifier("aluminium") == null) if (mechanic.Inventory.FindItemByIdentifier("oxygentank") == null && mechanic.Inventory.FindItemByIdentifier("aluminium") == null)
{ {
for (int i = 0; i < mechanic_craftingCabinet.Inventory.Items.Length; i++) for (int i = 0; i < mechanic_craftingCabinet.Capacity; i++)
{ {
Item item = mechanic_craftingCabinet.Inventory.Items[i]; Item item = mechanic_craftingCabinet.Inventory.GetItemAt(i);
if (item != null && item.prefab.Identifier == "oxygentank") if (item != null && item.prefab.Identifier == "oxygentank")
{ {
HighlightInventorySlot(mechanic_craftingCabinet.Inventory, i, highlightColor, .5f, .5f, 0f); HighlightInventorySlot(mechanic_craftingCabinet.Inventory, i, highlightColor, .5f, .5f, 0f);
@@ -374,9 +376,9 @@ namespace Barotrauma.Tutorials
if (mechanic.Inventory.FindItemByIdentifier("sodium") == null) if (mechanic.Inventory.FindItemByIdentifier("sodium") == null)
{ {
for (int i = 0; i < mechanic_craftingCabinet.Inventory.Items.Length; i++) for (int i = 0; i < mechanic_craftingCabinet.Inventory.Capacity; i++)
{ {
Item item = mechanic_craftingCabinet.Inventory.Items[i]; Item item = mechanic_craftingCabinet.Inventory.GetItemAt(i);
if (item != null && item.prefab.Identifier == "sodium") if (item != null && item.prefab.Identifier == "sodium")
{ {
HighlightInventorySlot(mechanic_craftingCabinet.Inventory, i, highlightColor, .5f, .5f, 0f); HighlightInventorySlot(mechanic_craftingCabinet.Inventory, i, highlightColor, .5f, .5f, 0f);
@@ -408,9 +410,9 @@ namespace Barotrauma.Tutorials
{ {
HighlightInventorySlot(mechanic_deconstructor.OutputContainer.Inventory, "aluminium", highlightColor, .5f, .5f, 0f); HighlightInventorySlot(mechanic_deconstructor.OutputContainer.Inventory, "aluminium", highlightColor, .5f, .5f, 0f);
for (int i = 0; i < mechanic.Inventory.slots.Length; i++) for (int i = 0; i < mechanic.Inventory.Capacity; i++)
{ {
if (mechanic.Inventory.Items[i] == null) HighlightInventorySlot(mechanic.Inventory, i, highlightColor, .5f, .5f, 0f); if (mechanic.Inventory.GetItemAt(i) == null) { HighlightInventorySlot(mechanic.Inventory, i, highlightColor, .5f, .5f, 0f); }
} }
} }
else else
@@ -418,13 +420,9 @@ namespace Barotrauma.Tutorials
if (mechanic.Inventory.FindItemByIdentifier("oxygentank") != null && mechanic_deconstructor.InputContainer.Inventory.FindItemByIdentifier("oxygentank") == null) if (mechanic.Inventory.FindItemByIdentifier("oxygentank") != null && mechanic_deconstructor.InputContainer.Inventory.FindItemByIdentifier("oxygentank") == null)
{ {
HighlightInventorySlot(mechanic.Inventory, "oxygentank", highlightColor, .5f, .5f, 0f); HighlightInventorySlot(mechanic.Inventory, "oxygentank", highlightColor, .5f, .5f, 0f);
for (int i = 0; i < mechanic_deconstructor.InputContainer.Inventory.Capacity; i++)
if (mechanic_deconstructor.InputContainer.Inventory.slots != null)
{ {
for (int i = 0; i < mechanic_deconstructor.InputContainer.Inventory.slots.Length; i++) HighlightInventorySlot(mechanic_deconstructor.InputContainer.Inventory, i, highlightColor, .5f, .5f, 0f);
{
HighlightInventorySlot(mechanic_deconstructor.InputContainer.Inventory, i, highlightColor, .5f, .5f, 0f);
}
} }
} }
@@ -461,7 +459,7 @@ namespace Barotrauma.Tutorials
{ {
HighlightInventorySlot(mechanic_fabricator.OutputContainer.Inventory, "extinguisher", highlightColor, .5f, .5f, 0f); HighlightInventorySlot(mechanic_fabricator.OutputContainer.Inventory, "extinguisher", highlightColor, .5f, .5f, 0f);
/*for (int i = 0; i < mechanic.Inventory.slots.Length; i++) /*for (int i = 0; i < mechanic.Inventory.Capacity; i++)
{ {
if (mechanic.Inventory.Items[i] == null) HighlightInventorySlot(mechanic.Inventory, i, highlightColor, .5f, .5f, 0f); if (mechanic.Inventory.Items[i] == null) HighlightInventorySlot(mechanic.Inventory, i, highlightColor, .5f, .5f, 0f);
}*/ }*/
@@ -478,12 +476,12 @@ namespace Barotrauma.Tutorials
HighlightInventorySlot(mechanic.Inventory, "aluminium", highlightColor, .5f, .5f, 0f); HighlightInventorySlot(mechanic.Inventory, "aluminium", highlightColor, .5f, .5f, 0f);
HighlightInventorySlot(mechanic.Inventory, "sodium", highlightColor, .5f, .5f, 0f); HighlightInventorySlot(mechanic.Inventory, "sodium", highlightColor, .5f, .5f, 0f);
if (mechanic_fabricator.InputContainer.Inventory.Items[0] == null) if (mechanic_fabricator.InputContainer.Inventory.GetItemAt(0) == null)
{ {
HighlightInventorySlot(mechanic_fabricator.InputContainer.Inventory, 0, highlightColor, .5f, .5f, 0f); HighlightInventorySlot(mechanic_fabricator.InputContainer.Inventory, 0, highlightColor, .5f, .5f, 0f);
} }
if (mechanic_fabricator.InputContainer.Inventory.Items[1] == null) if (mechanic_fabricator.InputContainer.Inventory.GetItemAt(1) == null)
{ {
HighlightInventorySlot(mechanic_fabricator.InputContainer.Inventory, 1, highlightColor, .5f, .5f, 0f); HighlightInventorySlot(mechanic_fabricator.InputContainer.Inventory, 1, highlightColor, .5f, .5f, 0f);
} }
@@ -524,9 +522,9 @@ namespace Barotrauma.Tutorials
{ {
if (IsSelectedItem(mechanic_divingSuitContainer.Item)) if (IsSelectedItem(mechanic_divingSuitContainer.Item))
{ {
if (mechanic_divingSuitContainer.Inventory.slots != null) if (mechanic_divingSuitContainer.Inventory.visualSlots != null)
{ {
for (int i = 0; i < mechanic_divingSuitContainer.Inventory.slots.Length; i++) for (int i = 0; i < mechanic_divingSuitContainer.Inventory.Capacity; i++)
{ {
HighlightInventorySlot(mechanic_divingSuitContainer.Inventory, i, highlightColor, 0.5f, 0.5f, 0f); HighlightInventorySlot(mechanic_divingSuitContainer.Inventory, i, highlightColor, 0.5f, 0.5f, 0f);
} }
@@ -234,24 +234,24 @@ namespace Barotrauma.Tutorials
if (!firstSlotRemoved) if (!firstSlotRemoved)
{ {
HighlightInventorySlot(officer_equipmentCabinet.Inventory, 0, highlightColor, .5f, .5f, 0f); HighlightInventorySlot(officer_equipmentCabinet.Inventory, 0, highlightColor, .5f, .5f, 0f);
if (officer_equipmentCabinet.Inventory.Items[0] == null) firstSlotRemoved = true; if (officer_equipmentCabinet.Inventory.GetItemAt(0) == null) { firstSlotRemoved = true; }
} }
if (!secondSlotRemoved) if (!secondSlotRemoved)
{ {
HighlightInventorySlot(officer_equipmentCabinet.Inventory, 1, highlightColor, .5f, .5f, 0f); HighlightInventorySlot(officer_equipmentCabinet.Inventory, 1, highlightColor, .5f, .5f, 0f);
if (officer_equipmentCabinet.Inventory.Items[1] == null) secondSlotRemoved = true; if (officer_equipmentCabinet.Inventory.GetItemAt(1) == null) { secondSlotRemoved = true; }
} }
if (!thirdSlotRemoved) if (!thirdSlotRemoved)
{ {
HighlightInventorySlot(officer_equipmentCabinet.Inventory, 2, highlightColor, .5f, .5f, 0f); HighlightInventorySlot(officer_equipmentCabinet.Inventory, 2, highlightColor, .5f, .5f, 0f);
if (officer_equipmentCabinet.Inventory.Items[2] == null) thirdSlotRemoved = true; if (officer_equipmentCabinet.Inventory.GetItemAt(2) == null) { thirdSlotRemoved = true; }
} }
for (int i = 0; i < officer.Inventory.slots.Length; i++) for (int i = 0; i < officer.Inventory.visualSlots.Length; i++)
{ {
if (officer.Inventory.Items[i] == null) HighlightInventorySlot(officer.Inventory, i, highlightColor, .5f, .5f, 0f); if (officer.Inventory.GetItemAt(i) == null) { HighlightInventorySlot(officer.Inventory, i, highlightColor, .5f, .5f, 0f); }
} }
} }
@@ -298,7 +298,7 @@ namespace Barotrauma.Tutorials
TriggerTutorialSegment(3); // Arm coilgun TriggerTutorialSegment(3); // Arm coilgun
do do
{ {
SetHighlight(officer_coilgunLoader.Item, officer_coilgunLoader.Inventory.Items[0] == null || officer_coilgunLoader.Inventory.Items[0].Condition == 0); SetHighlight(officer_coilgunLoader.Item, officer_coilgunLoader.Inventory.GetItemAt(0) == null || officer_coilgunLoader.Inventory.GetItemAt(0).Condition == 0);
HighlightInventorySlot(officer_coilgunLoader.Inventory, 0, highlightColor, .5f, .5f, 0f); HighlightInventorySlot(officer_coilgunLoader.Inventory, 0, highlightColor, .5f, .5f, 0f);
SetHighlight(officer_superCapacitor.Item, officer_superCapacitor.RechargeSpeed < superCapacitorRechargeRate); SetHighlight(officer_superCapacitor.Item, officer_superCapacitor.RechargeSpeed < superCapacitorRechargeRate);
SetHighlight(officer_ammoShelf_1.Item, officer_coilgunLoader.Item.ExternalHighlight ); SetHighlight(officer_ammoShelf_1.Item, officer_coilgunLoader.Item.ExternalHighlight );
@@ -308,7 +308,7 @@ namespace Barotrauma.Tutorials
HighlightInventorySlot(officer.Inventory, "coilgunammobox", highlightColor, .5f, .5f, 0f); HighlightInventorySlot(officer.Inventory, "coilgunammobox", highlightColor, .5f, .5f, 0f);
} }
yield return null; yield return null;
} while (officer_coilgunLoader.Inventory.Items[0] == null || officer_superCapacitor.RechargeSpeed < superCapacitorRechargeRate || officer_coilgunLoader.Inventory.Items[0].Condition == 0); } while (officer_coilgunLoader.Inventory.GetItemAt(0) == null || officer_superCapacitor.RechargeSpeed < superCapacitorRechargeRate || officer_coilgunLoader.Inventory.GetItemAt(0).Condition == 0);
SetHighlight(officer_coilgunLoader.Item, false); SetHighlight(officer_coilgunLoader.Item, false);
SetHighlight(officer_superCapacitor.Item, false); SetHighlight(officer_superCapacitor.Item, false);
SetHighlight(officer_ammoShelf_1.Item, false); SetHighlight(officer_ammoShelf_1.Item, false);
@@ -371,12 +371,11 @@ namespace Barotrauma.Tutorials
{ {
if (IsSelectedItem(officer_rangedWeaponCabinet.Item)) if (IsSelectedItem(officer_rangedWeaponCabinet.Item))
{ {
if (officer_rangedWeaponCabinet.Inventory.slots != null) if (officer_rangedWeaponCabinet.Inventory.visualSlots != null)
{ {
for (int i = 0; i < officer_rangedWeaponCabinet.Inventory.Items.Length; i++) for (int i = 0; i < officer_rangedWeaponCabinet.Inventory.Capacity; i++)
{ {
if (officer_rangedWeaponCabinet.Inventory.Items[i] == null) continue; if (officer_rangedWeaponCabinet.Inventory.GetItemAt(i)?.Prefab.Identifier == "shotgunshell")
if (officer_rangedWeaponCabinet.Inventory.Items[i].Prefab.Identifier == "shotgunshell")
{ {
HighlightInventorySlot(officer_rangedWeaponCabinet.Inventory, i, highlightColor, 0.5f, 0.5f, 0f); HighlightInventorySlot(officer_rangedWeaponCabinet.Inventory, i, highlightColor, 0.5f, 0.5f, 0f);
} }
@@ -384,10 +383,9 @@ namespace Barotrauma.Tutorials
} }
} }
for (int i = 0; i < officer.Inventory.Items.Length; i++) for (int i = 0; i < officer.Inventory.Capacity; i++)
{ {
if (officer.Inventory.Items[i] == null) continue; if (officer.Inventory.GetItemAt(i)?.Prefab.Identifier == "shotgunshell")
if (officer.Inventory.Items[i].Prefab.Identifier == "shotgunshell")
{ {
HighlightInventorySlot(officer.Inventory, i, highlightColor, 0.5f, 0.5f, 0f); HighlightInventorySlot(officer.Inventory, i, highlightColor, 0.5f, 0.5f, 0f);
} }
@@ -398,7 +396,7 @@ namespace Barotrauma.Tutorials
HighlightInventorySlot(officer.Inventory, "shotgun", highlightColor, 0.5f, 0.5f, 0f); HighlightInventorySlot(officer.Inventory, "shotgun", highlightColor, 0.5f, 0.5f, 0f);
} }
yield return null; yield return null;
} while (!shotGunChamber.Inventory.IsFull()); // Wait until all six harpoons loaded } while (!shotGunChamber.Inventory.IsFull(takeStacksIntoAccount: true)); // Wait until all six harpoons loaded
RemoveCompletedObjective(segments[5]); RemoveCompletedObjective(segments[5]);
SetHighlight(officer_rangedWeaponCabinet.Item, false); SetHighlight(officer_rangedWeaponCabinet.Item, false);
SetDoorAccess(officer_fourthDoor, officer_fourthDoorLight, true); SetDoorAccess(officer_fourthDoor, officer_fourthDoorLight, true);
@@ -425,8 +423,8 @@ namespace Barotrauma.Tutorials
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Officer.Radio.Submarine"), ChatMessageType.Radio, null); GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Officer.Radio.Submarine"), ChatMessageType.Radio, null);
do do
{ {
SetHighlight(officer_subLoader_1.Item, officer_subLoader_1.Inventory.Items[0] == null || officer_subLoader_1.Inventory.Items[0].Condition == 0); SetHighlight(officer_subLoader_1.Item, officer_subLoader_1.Inventory.GetItemAt(0) == null || officer_subLoader_1.Inventory.GetItemAt(0).Condition == 0);
SetHighlight(officer_subLoader_2.Item, officer_subLoader_2.Inventory.Items[0] == null || officer_subLoader_2.Inventory.Items[0].Condition == 0); SetHighlight(officer_subLoader_2.Item, officer_subLoader_2.Inventory.GetItemAt(0) == null || officer_subLoader_2.Inventory.GetItemAt(0).Condition == 0);
HighlightInventorySlot(officer_subLoader_1.Inventory, 0, highlightColor, .5f, .5f, 0f); HighlightInventorySlot(officer_subLoader_1.Inventory, 0, highlightColor, .5f, .5f, 0f);
HighlightInventorySlot(officer_subLoader_2.Inventory, 0, highlightColor, .5f, .5f, 0f); HighlightInventorySlot(officer_subLoader_2.Inventory, 0, highlightColor, .5f, .5f, 0f);
@@ -122,7 +122,7 @@ namespace Barotrauma.Tutorials
} }
character = Character.Create(charInfo, wayPoint.WorldPosition, "", isRemotePlayer: false, hasAi: false); character = Character.Create(charInfo, wayPoint.WorldPosition, "", isRemotePlayer: false, hasAi: false);
character.TeamID = Character.TeamType.Team1; character.TeamID = CharacterTeamType.Team1;
Character.Controlled = character; Character.Controlled = character;
character.GiveJobItems(null); character.GiveJobItems(null);
@@ -264,7 +264,7 @@ namespace Barotrauma.Tutorials
protected virtual void TriggerTutorialSegment(int index, params object[] args) protected virtual void TriggerTutorialSegment(int index, params object[] args)
{ {
Inventory.draggingItem = null; Inventory.DraggingItems.Clear();
ContentRunning = true; ContentRunning = true;
activeContentSegment = segments[index]; activeContentSegment = segments[index];
segments[index].Args = args; segments[index].Args = args;
@@ -410,7 +410,7 @@ namespace Barotrauma.Tutorials
private void ReplaySegmentVideo(TutorialSegment segment) private void ReplaySegmentVideo(TutorialSegment segment)
{ {
if (ContentRunning) return; if (ContentRunning) return;
Inventory.draggingItem = null; Inventory.DraggingItems.Clear();
ContentRunning = true; ContentRunning = true;
LoadVideo(segment); LoadVideo(segment);
//videoPlayer.LoadContent(playableContentPath, new VideoPlayer.VideoSettings(segment.VideoContent), new VideoPlayer.TextSettings(segment.VideoContent), segment.Id, true, callback: () => ContentRunning = false); //videoPlayer.LoadContent(playableContentPath, new VideoPlayer.VideoSettings(segment.VideoContent), new VideoPlayer.TextSettings(segment.VideoContent), segment.Id, true, callback: () => ContentRunning = false);
@@ -419,7 +419,7 @@ namespace Barotrauma.Tutorials
private void ShowSegmentText(TutorialSegment segment) private void ShowSegmentText(TutorialSegment segment)
{ {
if (ContentRunning) return; if (ContentRunning) return;
Inventory.draggingItem = null; Inventory.DraggingItems.Clear();
ContentRunning = true; ContentRunning = true;
string tutorialText = TextManager.GetFormatted(segment.TextContent.GetAttributeString("tag", ""), true, segment.Args); string tutorialText = TextManager.GetFormatted(segment.TextContent.GetAttributeString("tag", ""), true, segment.Args);
@@ -609,10 +609,10 @@ namespace Barotrauma.Tutorials
#region Highlights #region Highlights
protected void HighlightInventorySlot(Inventory inventory, string identifier, Color color, float fadeInDuration, float fadeOutDuration, float scaleUpAmount) protected void HighlightInventorySlot(Inventory inventory, string identifier, Color color, float fadeInDuration, float fadeOutDuration, float scaleUpAmount)
{ {
if (inventory.slots == null) { return; } if (inventory.visualSlots == null) { return; }
for (int i = 0; i < inventory.Items.Length; i++) for (int i = 0; i < inventory.Capacity; i++)
{ {
if (inventory.Items[i] != null && inventory.Items[i].Prefab.Identifier == identifier) if (inventory.GetItemAt(i)?.Prefab.Identifier == identifier)
{ {
HighlightInventorySlot(inventory, i, color, fadeInDuration, fadeOutDuration, scaleUpAmount); HighlightInventorySlot(inventory, i, color, fadeInDuration, fadeOutDuration, scaleUpAmount);
} }
@@ -621,10 +621,10 @@ namespace Barotrauma.Tutorials
protected void HighlightInventorySlotWithTag(Inventory inventory, string tag, Color color, float fadeInDuration, float fadeOutDuration, float scaleUpAmount) protected void HighlightInventorySlotWithTag(Inventory inventory, string tag, Color color, float fadeInDuration, float fadeOutDuration, float scaleUpAmount)
{ {
if (inventory.slots == null) { return; } if (inventory.visualSlots == null) { return; }
for (int i = 0; i < inventory.Items.Length; i++) for (int i = 0; i < inventory.Capacity; i++)
{ {
if (inventory.Items[i] != null && inventory.Items[i].HasTag(tag)) if (inventory.GetItemAt(i)?.HasTag(tag) ?? false)
{ {
HighlightInventorySlot(inventory, i, color, fadeInDuration, fadeOutDuration, scaleUpAmount); HighlightInventorySlot(inventory, i, color, fadeInDuration, fadeOutDuration, scaleUpAmount);
} }
@@ -633,8 +633,8 @@ namespace Barotrauma.Tutorials
protected void HighlightInventorySlot(Inventory inventory, int index, Color color, float fadeInDuration, float fadeOutDuration, float scaleUpAmount) protected void HighlightInventorySlot(Inventory inventory, int index, Color color, float fadeInDuration, float fadeOutDuration, float scaleUpAmount)
{ {
if (inventory.slots == null || index < 0 || inventory.slots[index].HighlightTimer > 0) return; if (inventory.visualSlots == null || index < 0 || inventory.visualSlots[index].HighlightTimer > 0) { return; }
inventory.slots[index].ShowBorderHighlight(color, fadeInDuration, fadeOutDuration, scaleUpAmount); inventory.visualSlots[index].ShowBorderHighlight(color, fadeInDuration, fadeOutDuration, scaleUpAmount);
} }
#endregion #endregion
} }
@@ -33,6 +33,8 @@ namespace Barotrauma
public static DateTime lastReadyCheck = DateTime.MinValue; public static DateTime lastReadyCheck = DateTime.MinValue;
public static bool IsReadyCheck(GUIComponent? msgBox) => msgBox?.UserData as string == PromptData || msgBox?.UserData as string == ResultData;
private void CreateMessageBox(string author) private void CreateMessageBox(string author)
{ {
Vector2 relativeSize = new Vector2(GUI.IsFourByThree() ? 0.3f : 0.2f, 0.15f); Vector2 relativeSize = new Vector2(GUI.IsFourByThree() ? 0.3f : 0.2f, 0.15f);
@@ -120,7 +122,10 @@ namespace Barotrauma
int second = (int) Math.Ceiling(time); int second = (int) Math.Ceiling(time);
if (second < lastSecond) if (second < lastSecond)
{ {
SoundPlayer.PlayUISound(GUISoundType.PopupMenu); if (msgBox != null && !msgBox.Closed)
{
SoundPlayer.PlayUISound(GUISoundType.PopupMenu);
}
lastSecond = second; lastSecond = second;
} }
} }
@@ -87,12 +87,12 @@ namespace Barotrauma
TextManager.Get("crew"), textAlignment: Alignment.TopLeft, font: GUI.SubHeadingFont); TextManager.Get("crew"), textAlignment: Alignment.TopLeft, font: GUI.SubHeadingFont);
crewHeader.RectTransform.MinSize = new Point(0, GUI.IntScale(crewHeader.Rect.Height * 2.0f)); crewHeader.RectTransform.MinSize = new Point(0, GUI.IntScale(crewHeader.Rect.Height * 2.0f));
CreateCrewList(crewContent, gameSession.CrewManager.GetCharacterInfos().Where(c => c.TeamID != Character.TeamType.Team2)); CreateCrewList(crewContent, gameSession.CrewManager.GetCharacterInfos().Where(c => c.TeamID != CharacterTeamType.Team2));
//another crew frame for the 2nd team in combat missions //another crew frame for the 2nd team in combat missions
if (gameSession.Mission is CombatMission) if (gameSession.Mission is CombatMission)
{ {
crewHeader.Text = CombatMission.GetTeamName(Character.TeamType.Team1); crewHeader.Text = CombatMission.GetTeamName(CharacterTeamType.Team1);
GUIFrame crewFrame2 = new GUIFrame(new RectTransform(new Vector2(0.35f, 0.55f), background.RectTransform, Anchor.TopCenter, minSize: new Point(minWidth, minHeight))); GUIFrame crewFrame2 = new GUIFrame(new RectTransform(new Vector2(0.35f, 0.55f), background.RectTransform, Anchor.TopCenter, minSize: new Point(minWidth, minHeight)));
rightPanels.Add(crewFrame2); rightPanels.Add(crewFrame2);
GUIFrame crewFrameInner2 = new GUIFrame(new RectTransform(new Point(crewFrame2.Rect.Width - padding * 2, crewFrame2.Rect.Height - padding * 2), crewFrame2.RectTransform, Anchor.Center), style: "InnerFrame"); GUIFrame crewFrameInner2 = new GUIFrame(new RectTransform(new Point(crewFrame2.Rect.Width - padding * 2, crewFrame2.Rect.Height - padding * 2), crewFrame2.RectTransform, Anchor.Center), style: "InnerFrame");
@@ -101,9 +101,9 @@ namespace Barotrauma
Stretch = true Stretch = true
}; };
var crewHeader2 = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), crewContent2.RectTransform), var crewHeader2 = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), crewContent2.RectTransform),
CombatMission.GetTeamName(Character.TeamType.Team2), textAlignment: Alignment.TopLeft, font: GUI.SubHeadingFont); CombatMission.GetTeamName(CharacterTeamType.Team2), textAlignment: Alignment.TopLeft, font: GUI.SubHeadingFont);
crewHeader2.RectTransform.MinSize = new Point(0, GUI.IntScale(crewHeader2.Rect.Height * 2.0f)); crewHeader2.RectTransform.MinSize = new Point(0, GUI.IntScale(crewHeader2.Rect.Height * 2.0f));
CreateCrewList(crewContent2, gameSession.CrewManager.GetCharacterInfos().Where(c => c.TeamID == Character.TeamType.Team2)); CreateCrewList(crewContent2, gameSession.CrewManager.GetCharacterInfos().Where(c => c.TeamID == CharacterTeamType.Team2));
} }
//header ------------------------------------------------------------------------------- //header -------------------------------------------------------------------------------
@@ -68,6 +68,9 @@ namespace Barotrauma
keyMapping[(int)InputType.PreviousFireMode] = new KeyOrMouse(MouseButton.MouseWheelDown); keyMapping[(int)InputType.PreviousFireMode] = new KeyOrMouse(MouseButton.MouseWheelDown);
keyMapping[(int)InputType.NextFireMode] = new KeyOrMouse(MouseButton.MouseWheelUp); keyMapping[(int)InputType.NextFireMode] = new KeyOrMouse(MouseButton.MouseWheelUp);
keyMapping[(int)InputType.TakeHalfFromInventorySlot] = new KeyOrMouse(Keys.LeftShift);
keyMapping[(int)InputType.TakeOneFromInventorySlot] = new KeyOrMouse(Keys.LeftControl);
if (Language == "French") if (Language == "French")
{ {
keyMapping[(int)InputType.Up] = new KeyOrMouse(Keys.Z); keyMapping[(int)InputType.Up] = new KeyOrMouse(Keys.Z);
@@ -173,6 +176,13 @@ namespace Barotrauma
{ {
foreach (XAttribute attribute in element.Attributes()) foreach (XAttribute attribute in element.Attributes())
{ {
//backwards compatibility
if (attribute.Name.ToString() == "TakeAllFromInventorySlot")
{
keyMapping[(int)InputType.TakeHalfFromInventorySlot] = new KeyOrMouse(Keys.LeftShift);
keyMapping[(int)InputType.TakeOneFromInventorySlot] = new KeyOrMouse(Keys.LeftControl);
}
if (!Enum.TryParse(attribute.Name.ToString(), true, out InputType inputType)) { continue; } if (!Enum.TryParse(attribute.Name.ToString(), true, out InputType inputType)) { continue; }
if (int.TryParse(attribute.Value.ToString(), out int mouseButtonInt)) if (int.TryParse(attribute.Value.ToString(), out int mouseButtonInt))
@@ -223,6 +233,40 @@ namespace Barotrauma
{ {
LoadInventoryKeybinds(inventoryKeyMapping); LoadInventoryKeybinds(inventoryKeyMapping);
} }
XElement debugConsoleMapping = doc.Root.Element("debugconsolemapping");
if (debugConsoleMapping == null) { return; }
ConsoleKeybinds.Clear();
DebugConsole.Keybinds.Clear();
foreach (XElement element in debugConsoleMapping.Elements())
{
string keyString = element.GetAttributeString("key", string.Empty);
string command = element.GetAttributeString("command", string.Empty);
if (string.IsNullOrWhiteSpace(keyString) || string.IsNullOrWhiteSpace(command)) { continue; }
if (Enum.TryParse(typeof(Keys), keyString, ignoreCase: true, out object @out) && @out is Keys key)
{
ConsoleKeybinds.TryAdd(key, command);
}
}
DebugConsole.Keybinds = new Dictionary<Keys, string>(ConsoleKeybinds);
}
private void LoadSubEditorImages(XDocument doc)
{
XElement element = doc.Root?.Element("editorimages");
if (element == null)
{
SubEditorScreen.ImageManager.Clear(alsoPending: true);
return;
}
SubEditorScreen.ImageManager.Load(element);
} }
public KeyOrMouse KeyBind(InputType inputType) public KeyOrMouse KeyBind(InputType inputType)
@@ -242,7 +286,7 @@ namespace Barotrauma
private GUIListBox contentPackageList; private GUIListBox contentPackageList;
private bool ChangeSliderText(GUIScrollBar scrollBar, float barScroll) private bool ChangeSliderText(GUIScrollBar scrollBar, float scale)
{ {
UnsavedSettings = true; UnsavedSettings = true;
GUITextBlock text = scrollBar.UserData as GUITextBlock; GUITextBlock text = scrollBar.UserData as GUITextBlock;
@@ -261,7 +305,7 @@ namespace Barotrauma
} }
label = text.Text.Substring(0, index); label = text.Text.Substring(0, index);
} }
text.Text = label + " " + (int)(barScroll * 100) + "%"; text.Text = label + " " + (int)Math.Round(scale * 100) + "%";
return true; return true;
} }
@@ -705,6 +749,18 @@ namespace Barotrauma
} }
};*/ };*/
new GUITickBox(new RectTransform(tickBoxScale, rightColumn.RectTransform), TextManager.Get("RadialDistortion"))
{
ToolTip = TextManager.Get("RadialDistortionToolTip"),
Selected = EnableRadialDistortion,
OnSelected = (tickBox) =>
{
EnableRadialDistortion = tickBox.Selected;
UnsavedSettings = true;
return true;
}
};
new GUITickBox(new RectTransform(tickBoxScale, rightColumn.RectTransform), TextManager.Get("ChromaticAberration")) new GUITickBox(new RectTransform(tickBoxScale, rightColumn.RectTransform), TextManager.Get("ChromaticAberration"))
{ {
ToolTip = TextManager.Get("ChromaticAberrationToolTip"), ToolTip = TextManager.Get("ChromaticAberrationToolTip"),
@@ -725,13 +781,12 @@ namespace Barotrauma
BarScroll = (HUDScale - MinHUDScale) / (MaxHUDScale - MinHUDScale), BarScroll = (HUDScale - MinHUDScale) / (MaxHUDScale - MinHUDScale),
OnMoved = (scrollBar, scroll) => OnMoved = (scrollBar, scroll) =>
{ {
ChangeSliderText(scrollBar, scroll);
HUDScale = MathHelper.Lerp(MinHUDScale, MaxHUDScale, scroll); HUDScale = MathHelper.Lerp(MinHUDScale, MaxHUDScale, scroll);
UnsavedSettings = true; ChangeSliderText(scrollBar, HUDScale);
OnHUDScaleChanged?.Invoke(); OnHUDScaleChanged?.Invoke();
return true; return true;
}, },
Step = 0.05f Step = 0.02f
}; };
HUDScaleScrollBar.OnMoved(HUDScaleScrollBar, HUDScaleScrollBar.BarScroll); HUDScaleScrollBar.OnMoved(HUDScaleScrollBar, HUDScaleScrollBar.BarScroll);
@@ -743,15 +798,31 @@ namespace Barotrauma
BarScroll = (InventoryScale - MinInventoryScale) / (MaxInventoryScale - MinInventoryScale), BarScroll = (InventoryScale - MinInventoryScale) / (MaxInventoryScale - MinInventoryScale),
OnMoved = (scrollBar, scroll) => OnMoved = (scrollBar, scroll) =>
{ {
ChangeSliderText(scrollBar, scroll);
InventoryScale = MathHelper.Lerp(MinInventoryScale, MaxInventoryScale, scroll); InventoryScale = MathHelper.Lerp(MinInventoryScale, MaxInventoryScale, scroll);
UnsavedSettings = true; ChangeSliderText(scrollBar, InventoryScale);
return true; return true;
}, },
Step = 0.05f Step = 0.02f
}; };
inventoryScaleScrollBar.OnMoved(inventoryScaleScrollBar, inventoryScaleScrollBar.BarScroll); inventoryScaleScrollBar.OnMoved(inventoryScaleScrollBar, inventoryScaleScrollBar.BarScroll);
GUITextBlock textScaleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("TextScale"), font: GUI.SubHeadingFont);
GUIScrollBar textScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform),
style: "GUISlider", barSize: 0.1f)
{
UserData = textScaleText,
BarScroll = (TextScale - MinTextScale) / (MaxTextScale - MinTextScale),
OnMoved = (scrollBar, scroll) =>
{
TextScale = MathHelper.Lerp(MinTextScale, MaxTextScale, scroll);
textScaleDirty = true;
ChangeSliderText(scrollBar, TextScale);
return true;
},
Step = 0.01f
};
textScaleScrollBar.OnMoved(textScaleScrollBar, textScaleScrollBar.BarScroll);
/// Audio tab ---------------------------------------------------------------- /// Audio tab ----------------------------------------------------------------
var audioContent = new GUILayoutGroup(new RectTransform(new Vector2(0.97f, 0.97f), tabs[(int)Tab.Audio].RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter) var audioContent = new GUILayoutGroup(new RectTransform(new Vector2(0.97f, 0.97f), tabs[(int)Tab.Audio].RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter)
@@ -809,8 +880,7 @@ namespace Barotrauma
ChangeSliderText(scrollBar, scroll); ChangeSliderText(scrollBar, scroll);
SoundVolume = scroll; SoundVolume = scroll;
return true; return true;
}, }
Step = 0.05f
}; };
soundScrollBar.OnMoved(soundScrollBar, soundScrollBar.BarScroll); soundScrollBar.OnMoved(soundScrollBar, soundScrollBar.BarScroll);
@@ -825,8 +895,7 @@ namespace Barotrauma
ChangeSliderText(scrollBar, scroll); ChangeSliderText(scrollBar, scroll);
MusicVolume = scroll; MusicVolume = scroll;
return true; return true;
}, }
Step = 0.05f
}; };
musicScrollBar.OnMoved(musicScrollBar, musicScrollBar.BarScroll); musicScrollBar.OnMoved(musicScrollBar, musicScrollBar.BarScroll);
@@ -835,8 +904,7 @@ namespace Barotrauma
style: "GUISlider", barSize: 0.05f) style: "GUISlider", barSize: 0.05f)
{ {
UserData = voiceChatVolumeText, UserData = voiceChatVolumeText,
Range = new Vector2(0.0f, 2.0f), Range = new Vector2(0.0f, 2.0f)
Step = 0.05f
}; };
voiceChatScrollBar.BarScrollValue = VoiceChatVolume; voiceChatScrollBar.BarScrollValue = VoiceChatVolume;
voiceChatScrollBar.OnMoved = (scrollBar, scroll) => voiceChatScrollBar.OnMoved = (scrollBar, scroll) =>
@@ -1014,6 +1082,19 @@ namespace Barotrauma
{ {
Visible = VoiceSetting != VoiceMode.Disabled Visible = VoiceSetting != VoiceMode.Disabled
}; };
GUITickBox localVoiceByDefault = new GUITickBox(
new RectTransform(tickBoxScale, voiceActivityGroup.RectTransform), TextManager.Get("LocalVoiceByDefault"))
{
Visible = VoiceSetting == VoiceMode.Activity,
Selected = UseLocalVoiceByDefault,
ToolTip = TextManager.Get("LocalVoiceByDefaultTooltip"),
OnSelected = (tickBox) =>
{
UseLocalVoiceByDefault = tickBox.Selected;
UnsavedSettings = true;
return true;
}
};
GUITextBlock noiseGateText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), voiceActivityGroup.RectTransform), TextManager.Get("NoiseGateThreshold"), font: GUI.SubHeadingFont) GUITextBlock noiseGateText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), voiceActivityGroup.RectTransform), TextManager.Get("NoiseGateThreshold"), font: GUI.SubHeadingFont)
{ {
Visible = VoiceSetting == VoiceMode.Activity, Visible = VoiceSetting == VoiceMode.Activity,
@@ -1140,6 +1221,7 @@ namespace Barotrauma
noiseGateText.Visible = (vMode == VoiceMode.Activity); noiseGateText.Visible = (vMode == VoiceMode.Activity);
noiseGateSlider.Visible = (vMode == VoiceMode.Activity); noiseGateSlider.Visible = (vMode == VoiceMode.Activity);
localVoiceByDefault.Visible = (vMode == VoiceMode.Activity);
voiceActivityGroup.Visible = (vMode != VoiceMode.Disabled); voiceActivityGroup.Visible = (vMode != VoiceMode.Disabled);
voiceInputContainerHorizontal.Visible = (vMode == VoiceMode.PushToTalk); voiceInputContainerHorizontal.Visible = (vMode == VoiceMode.PushToTalk);
UnsavedSettings = true; UnsavedSettings = true;
@@ -1183,7 +1265,7 @@ namespace Barotrauma
AimAssistAmount = MathHelper.Lerp(0.0f, 5.0f, scroll); AimAssistAmount = MathHelper.Lerp(0.0f, 5.0f, scroll);
return true; return true;
}, },
Step = 0.1f Step = 0.01f
}; };
aimAssistSlider.OnMoved(aimAssistSlider, aimAssistSlider.BarScroll); aimAssistSlider.OnMoved(aimAssistSlider, aimAssistSlider.BarScroll);
@@ -1199,19 +1281,21 @@ namespace Barotrauma
} }
}; };
var inputFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.75f), controlsLayoutGroup.RectTransform), isHorizontal: true) var controlListBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.75f), controlsLayoutGroup.RectTransform));
{ Stretch = true, RelativeSpacing = 0.03f };
var inputFrame = new GUILayoutGroup(new RectTransform(Vector2.One, controlListBox.Content.RectTransform), isHorizontal: true)
{ Stretch = true, RelativeSpacing = 0.01f };
var inputColumnLeft = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), inputFrame.RectTransform)) var inputColumnLeft = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), inputFrame.RectTransform))
{ Stretch = true, RelativeSpacing = 0.02f }; { Stretch = true, RelativeSpacing = 0.005f };
var inputColumnRight = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), inputFrame.RectTransform)) var inputColumnRight = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), inputFrame.RectTransform))
{ Stretch = true, RelativeSpacing = 0.02f }; { Stretch = true, RelativeSpacing = 0.005f };
var inputNames = Enum.GetValues(typeof(InputType)); var inputNames = Enum.GetValues(typeof(InputType));
var inputNameBlocks = new List<GUITextBlock>(); var inputNameBlocks = new List<GUITextBlock>();
for (int i = 0; i < inputNames.Length; i++) for (int i = 0; i < inputNames.Length; i++)
{ {
var inputContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.06f),(i <= (inputNames.Length / 2.2f) ? inputColumnLeft : inputColumnRight).RectTransform)) var inputContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.06f),(i <= (inputNames.Length / 2) ? inputColumnLeft : inputColumnRight).RectTransform))
{ Stretch = true, IsHorizontal = true, RelativeSpacing = 0.01f, Color = new Color(12, 14, 15, 215) }; { Stretch = true, IsHorizontal = true, RelativeSpacing = 0.01f, Color = new Color(12, 14, 15, 215) };
var inputName = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), inputContainer.RectTransform, Anchor.TopLeft) { MinSize = new Point(100, 0) }, var inputName = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), inputContainer.RectTransform, Anchor.TopLeft) { MinSize = new Point(100, 0) },
TextManager.Get("InputType." + ((InputType)i)), font: GUI.SmallFont) { ForceUpperCase = true }; TextManager.Get("InputType." + ((InputType)i)), font: GUI.SmallFont) { ForceUpperCase = true };
@@ -1226,14 +1310,17 @@ namespace Barotrauma
{ {
keyBox.Text = ToolBox.LimitString(keyText, keyBox.Font, (int)(keyBox.Rect.Width - keyBox.Padding.X - keyBox.Padding.Z)); keyBox.Text = ToolBox.LimitString(keyText, keyBox.Font, (int)(keyBox.Rect.Width - keyBox.Padding.X - keyBox.Padding.Z));
}; };
inputContainer.RectTransform.MinSize = keyBox.RectTransform.MinSize;
keyBox.OnSelected += KeyBoxSelected; keyBox.OnSelected += KeyBoxSelected;
keyBox.SelectedColor = Color.Gold * 0.3f; keyBox.SelectedColor = Color.Gold * 0.3f;
} }
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.06f), inputColumnRight.RectTransform, minSize: inputColumnRight.Children.First().RectTransform.MinSize), style: null);
for (int i = 0; i < inventoryHotkeyCount; i++) for (int i = 0; i < inventoryHotkeyCount; i++)
{ {
var inputContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.06f), ((i + 1) <= inventoryHotkeyCount / 2 ? inputColumnLeft : inputColumnRight).RectTransform)) var inputContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.06f), ((i + 1) <= inventoryHotkeyCount / 2 ? inputColumnLeft : inputColumnRight).RectTransform))
{ Stretch = true, IsHorizontal = true, RelativeSpacing = 0.01f, Color = new Color(12, 14, 15, 215) }; { Stretch = true, IsHorizontal = true, RelativeSpacing = 0.01f, Color = new Color(12, 14, 15, 215), CanBeFocused = true };
var inputName = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), inputContainer.RectTransform, Anchor.TopLeft) { MinSize = new Point(100, 0) }, var inputName = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), inputContainer.RectTransform, Anchor.TopLeft) { MinSize = new Point(100, 0) },
TextManager.GetWithVariable("inventoryslotkeybind", "[slotnumber]", (i + 1).ToString()), font: GUI.SmallFont) TextManager.GetWithVariable("inventoryslotkeybind", "[slotnumber]", (i + 1).ToString()), font: GUI.SmallFont)
{ ForceUpperCase = true }; { ForceUpperCase = true };
@@ -1244,11 +1331,20 @@ namespace Barotrauma
UserData = i UserData = i
}; };
keyBox.Text = ToolBox.LimitString(keyBox.Text, keyBox.Font, (int)(keyBox.Rect.Width - keyBox.Padding.X - keyBox.Padding.Z)); keyBox.Text = ToolBox.LimitString(keyBox.Text, keyBox.Font, (int)(keyBox.Rect.Width - keyBox.Padding.X - keyBox.Padding.Z));
inputContainer.RectTransform.MinSize = keyBox.RectTransform.MinSize;
keyBox.OnSelected += InventoryKeyBoxSelected; keyBox.OnSelected += InventoryKeyBoxSelected;
keyBox.SelectedColor = Color.Gold * 0.3f; keyBox.SelectedColor = Color.Gold * 0.3f;
} }
GUITextBlock.AutoScaleAndNormalize(inputNameBlocks); inputNameBlocks.First().RectTransform.SizeChanged += () =>
{
GUITextBlock.AutoScaleAndNormalize(inputNameBlocks);
};
inputFrame.RectTransform.MinSize = new Point(0,
(int)Math.Max(
inputColumnLeft.Children.Sum(c => c.Rect.Height * (1.0f + inputColumnLeft.RelativeSpacing)),
inputColumnRight.Children.Sum(c => c.Rect.Height * (1.0f + inputColumnLeft.RelativeSpacing))));
var resetControlsArea = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.07f), controlsLayoutGroup.RectTransform), style: null); var resetControlsArea = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.07f), controlsLayoutGroup.RectTransform), style: null);
var resetControlsHolder = new GUILayoutGroup(new RectTransform(new Vector2(buttonArea.RectTransform.RelativeSize.X / controlsLayoutGroup.RectTransform.RelativeSize.X / rightPanel.RectTransform.RelativeSize.X, 1.0f), resetControlsArea.RectTransform, Anchor.Center), var resetControlsHolder = new GUILayoutGroup(new RectTransform(new Vector2(buttonArea.RectTransform.RelativeSize.X / controlsLayoutGroup.RectTransform.RelativeSize.X / rightPanel.RectTransform.RelativeSize.X, 1.0f), resetControlsArea.RectTransform, Anchor.Center),
@@ -1597,7 +1693,7 @@ namespace Barotrauma
if (!EnabledRegularPackages.Contains(contentPackage)) { return; } if (!EnabledRegularPackages.Contains(contentPackage)) { return; }
} }
ContentPackage.SortContentPackages(cp => listBox.Content.GetChildIndex(listBox.Content.GetChildByUserData(cp)), true); ContentPackage.SortContentPackages(cp => listBox.Content.GetChildIndex(listBox.Content.GetChildByUserData(cp)), true, this);
UnsavedSettings = true; UnsavedSettings = true;
} }
@@ -1617,6 +1713,8 @@ namespace Barotrauma
DisableRegularPackage(contentPackage); DisableRegularPackage(contentPackage);
} }
ContentPackage.SortContentPackages(cp => contentPackageList.Content.GetChildIndex(contentPackageList.Content.GetChildByUserData(cp)), false, this);
UnsavedSettings = true; UnsavedSettings = true;
return true; return true;
} }
@@ -1707,22 +1805,11 @@ namespace Barotrauma
SettingsFrame.Flash(GUI.Style.Green); SettingsFrame.Flash(GUI.Style.Green);
if (GameMain.WindowMode != GameMain.Config.WindowMode || GameMain.Config.GraphicsWidth != GameMain.GraphicsWidth || GameMain.Config.GraphicsHeight != GameMain.GraphicsHeight) if (textScaleDirty || GameMain.WindowMode != GameMain.Config.WindowMode || GameMain.Config.GraphicsWidth != GameMain.GraphicsWidth || GameMain.Config.GraphicsHeight != GameMain.GraphicsHeight)
{ {
GameMain.Instance.ApplyGraphicsSettings(); GameMain.Instance.ApplyGraphicsSettings();
textScaleDirty = false;
} }
/*if (GameMain.GraphicsWidth != GameMain.Config.GraphicsWidth || GameMain.GraphicsHeight != GameMain.Config.GraphicsHeight)
{
#if OSX
if (GameMain.Config.WindowMode != WindowMode.BorderlessWindowed)
{
#endif
new GUIMessageBox(TextManager.Get("RestartRequiredLabel"), TextManager.Get("RestartRequiredResolution"));
#if OSX
}
#endif
}*/
} }
private bool ApplyClicked(GUIButton button, object userData) private bool ApplyClicked(GUIButton button, object userData)
@@ -94,8 +94,8 @@ namespace Barotrauma
get { return personalSlotArea; } get { return personalSlotArea; }
} }
private GUIImage[] indicators = new GUIImage[5]; private readonly GUIImage[] indicators = new GUIImage[5];
private int[] indicatorIndexes = new int[5]; private readonly int[] indicatorIndices = new int[5];
private Vector2 indicatorSpriteSize; private Vector2 indicatorSpriteSize;
private GUILayoutGroup indicatorGroup; private GUILayoutGroup indicatorGroup;
@@ -117,11 +117,11 @@ namespace Barotrauma
indicators[3] = new GUIImage(new RectTransform(Point.Zero, indicatorGroup.RectTransform), "EquipmentIndicatorHeadwear"); indicators[3] = new GUIImage(new RectTransform(Point.Zero, indicatorGroup.RectTransform), "EquipmentIndicatorHeadwear");
indicators[4] = new GUIImage(new RectTransform(Point.Zero, indicatorGroup.RectTransform), "EquipmentIndicatorHeadphones"); indicators[4] = new GUIImage(new RectTransform(Point.Zero, indicatorGroup.RectTransform), "EquipmentIndicatorHeadphones");
indicatorIndexes[0] = FindLimbSlot(InvSlotType.OuterClothes); indicatorIndices[0] = FindLimbSlot(InvSlotType.OuterClothes);
indicatorIndexes[1] = FindLimbSlot(InvSlotType.Card); indicatorIndices[1] = FindLimbSlot(InvSlotType.Card);
indicatorIndexes[2] = FindLimbSlot(InvSlotType.InnerClothes); indicatorIndices[2] = FindLimbSlot(InvSlotType.InnerClothes);
indicatorIndexes[3] = FindLimbSlot(InvSlotType.Head); indicatorIndices[3] = FindLimbSlot(InvSlotType.Head);
indicatorIndexes[4] = FindLimbSlot(InvSlotType.Headset); indicatorIndices[4] = FindLimbSlot(InvSlotType.Headset);
for (int i = 0; i < indicators.Length; i++) for (int i = 0; i < indicators.Length; i++)
{ {
@@ -143,7 +143,7 @@ namespace Barotrauma
protected override ItemInventory GetActiveEquippedSubInventory(int slotIndex) protected override ItemInventory GetActiveEquippedSubInventory(int slotIndex)
{ {
var item = Items[slotIndex]; Item item = slots[slotIndex].FirstOrDefault();
if (item == null) { return null; } if (item == null) { return null; }
var container = item.GetComponent<ItemContainer>(); var container = item.GetComponent<ItemContainer>();
@@ -162,38 +162,35 @@ namespace Barotrauma
public override void RemoveItem(Item item) public override void RemoveItem(Item item)
{ {
if (!Items.Contains(item)) { return; } if (!Contains(item)) { return; }
base.RemoveItem(item); base.RemoveItem(item);
CreateSlots(); CreateSlots();
} }
public override void CreateSlots() public override void CreateSlots()
{ {
if (slots == null) { slots = new InventorySlot[capacity]; } if (visualSlots == null) { visualSlots = new VisualSlot[capacity]; }
float multiplier = !GUI.IsFourByThree() ? UIScale : UIScale * 0.925f; float multiplier = !GUI.IsFourByThree() ? UIScale : UIScale * 0.925f;
for (int i = 0; i < capacity; i++) for (int i = 0; i < capacity; i++)
{ {
InventorySlot prevSlot = slots[i]; VisualSlot prevSlot = visualSlots[i];
Sprite slotSprite = SlotSpriteSmall; Sprite slotSprite = SlotSpriteSmall;
Rectangle slotRect = new Rectangle( Rectangle slotRect = new Rectangle(
(int)(SlotPositions[i].X), (int)SlotPositions[i].X,
(int)(SlotPositions[i].Y), (int)SlotPositions[i].Y,
(int)(slotSprite.size.X * multiplier), (int)(slotSprite.size.Y * multiplier)); (int)(slotSprite.size.X * multiplier), (int)(slotSprite.size.Y * multiplier));
if (Items[i] != null) ItemContainer itemContainer = slots[i].FirstOrDefault()?.GetComponent<ItemContainer>();
if (itemContainer != null)
{ {
ItemContainer itemContainer = Items[i].GetComponent<ItemContainer>(); if (itemContainer.InventoryTopSprite != null) slotRect.Width = Math.Max(slotRect.Width, (int)(itemContainer.InventoryTopSprite.size.X * UIScale));
if (itemContainer != null) if (itemContainer.InventoryBottomSprite != null) slotRect.Width = Math.Max(slotRect.Width, (int)(itemContainer.InventoryBottomSprite.size.X * UIScale));
{
if (itemContainer.InventoryTopSprite != null) slotRect.Width = Math.Max(slotRect.Width, (int)(itemContainer.InventoryTopSprite.size.X * UIScale));
if (itemContainer.InventoryBottomSprite != null) slotRect.Width = Math.Max(slotRect.Width, (int)(itemContainer.InventoryBottomSprite.size.X * UIScale));
}
} }
slots[i] = new InventorySlot(slotRect) visualSlots[i] = new VisualSlot(slotRect)
{ {
SubInventoryDir = Math.Sign(GameMain.GraphicsHeight / 2 - slotRect.Center.Y), SubInventoryDir = Math.Sign(GameMain.GraphicsHeight / 2 - slotRect.Center.Y),
Disabled = false, Disabled = false,
@@ -202,13 +199,13 @@ namespace Barotrauma
}; };
if (prevSlot != null) if (prevSlot != null)
{ {
slots[i].DrawOffset = prevSlot.DrawOffset; visualSlots[i].DrawOffset = prevSlot.DrawOffset;
slots[i].Color = prevSlot.Color; visualSlots[i].Color = prevSlot.Color;
prevSlot.MoveBorderHighlight(visualSlots[i]);
} }
if (selectedSlot?.ParentInventory == this && selectedSlot.SlotIndex == i) if (selectedSlot?.ParentInventory == this && selectedSlot.SlotIndex == i)
{ {
selectedSlot = new SlotReference(this, slots[i], i, selectedSlot.IsSubSlot, selectedSlot.Inventory); selectedSlot = new SlotReference(this, visualSlots[i], i, selectedSlot.IsSubSlot, selectedSlot.Inventory);
} }
} }
@@ -217,9 +214,9 @@ namespace Barotrauma
highlightedSubInventorySlots.RemoveWhere(s => s.Inventory.OpenState <= 0.0f); highlightedSubInventorySlots.RemoveWhere(s => s.Inventory.OpenState <= 0.0f);
foreach (var subSlot in highlightedSubInventorySlots) foreach (var subSlot in highlightedSubInventorySlots)
{ {
if (subSlot.ParentInventory == this && subSlot.SlotIndex > 0 && subSlot.SlotIndex < slots.Length) if (subSlot.ParentInventory == this && subSlot.SlotIndex > 0 && subSlot.SlotIndex < visualSlots.Length)
{ {
subSlot.Slot = slots[subSlot.SlotIndex]; subSlot.Slot = visualSlots[subSlot.SlotIndex];
} }
} }
@@ -235,10 +232,10 @@ namespace Barotrauma
if (HideSlot(i)) continue; if (HideSlot(i)) continue;
if (frame == Rectangle.Empty) if (frame == Rectangle.Empty)
{ {
frame = slots[i].Rect; frame = visualSlots[i].Rect;
continue; continue;
} }
frame = Rectangle.Union(frame, slots[i].Rect); frame = Rectangle.Union(frame, visualSlots[i].Rect);
} }
frame.Inflate(10, 30); frame.Inflate(10, 30);
frame.Location -= new Point(0, 25); frame.Location -= new Point(0, 25);
@@ -247,26 +244,25 @@ namespace Barotrauma
protected override bool HideSlot(int i) protected override bool HideSlot(int i)
{ {
if (slots[i].Disabled || (hideEmptySlot[i] && Items[i] == null)) return true; if (visualSlots[i].Disabled || (slots[i].HideIfEmpty && slots[i].Empty())) { return true; }
if (layout == Layout.Default) if (layout == Layout.Default)
{ {
if (PersonalSlots.HasFlag(SlotTypes[i]) && !personalSlotArea.Contains(slots[i].Rect.Center + slots[i].DrawOffset.ToPoint())) return true; if (PersonalSlots.HasFlag(SlotTypes[i]) && !personalSlotArea.Contains(visualSlots[i].Rect.Center + visualSlots[i].DrawOffset.ToPoint())) { return true; }
} }
Item item = slots[i].FirstOrDefault();
//no need to draw the right hand slot if the item is in both hands //no need to draw the right hand slot if the item is in both hands
if (Items[i] != null && SlotTypes[i] == InvSlotType.RightHand && IsInLimbSlot(Items[i], InvSlotType.LeftHand)) if (item != null && SlotTypes[i] == InvSlotType.RightHand && IsInLimbSlot(item, InvSlotType.LeftHand))
{ {
return true; return true;
} }
//don't show the equip slot if the item is also in the default inventory //don't show the limb-specific slot if the item is also in an Any slot
if (SlotTypes[i] != InvSlotType.Any && Items[i] != null) if (item != null && SlotTypes[i] != InvSlotType.Any)
{ {
for (int j = 0; j < capacity; j++) if (IsInLimbSlot(item, InvSlotType.Any)) { return true; }
{
if (SlotTypes[j] == InvSlotType.Any && Items[j] == Items[i]) return true;
}
} }
return false; return false;
@@ -308,7 +304,8 @@ namespace Barotrauma
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;
if (slots == null) { CreateSlots(); } if (visualSlots == null) { CreateSlots(); }
if (visualSlots.None()) { return; }
hideButton.Visible = false; hideButton.Visible = false;
@@ -359,7 +356,7 @@ namespace Barotrauma
{ {
int x = HUDLayoutSettings.InventoryAreaLower.Right; int x = HUDLayoutSettings.InventoryAreaLower.Right;
int personalSlotX = HUDLayoutSettings.InventoryAreaLower.Right - SlotSize.X - Spacing; int personalSlotX = HUDLayoutSettings.InventoryAreaLower.Right - SlotSize.X - Spacing;
for (int i = 0; i < slots.Length; i++) for (int i = 0; i < visualSlots.Length; i++)
{ {
if (HideSlot(i)) continue; if (HideSlot(i)) continue;
if (PersonalSlots.HasFlag(SlotTypes[i])) if (PersonalSlots.HasFlag(SlotTypes[i]))
@@ -380,12 +377,12 @@ namespace Barotrauma
if (PersonalSlots.HasFlag(SlotTypes[i])) if (PersonalSlots.HasFlag(SlotTypes[i]))
{ {
SlotPositions[i] = new Vector2(personalSlotX, personalSlotY); SlotPositions[i] = new Vector2(personalSlotX, personalSlotY);
personalSlotX -= slots[i].Rect.Width + Spacing; personalSlotX -= visualSlots[i].Rect.Width + Spacing;
} }
else else
{ {
SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset); SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset);
x += slots[i].Rect.Width + Spacing; x += visualSlots[i].Rect.Width + Spacing;
} }
} }
@@ -393,7 +390,7 @@ namespace Barotrauma
for (int i = 0; i < SlotPositions.Length; i++) for (int i = 0; i < SlotPositions.Length; i++)
{ {
if (!HideSlot(i)) continue; if (!HideSlot(i)) continue;
x -= slots[i].Rect.Width + Spacing; x -= visualSlots[i].Rect.Width + Spacing;
SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset); SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset);
} }
} }
@@ -410,19 +407,19 @@ namespace Barotrauma
if (PersonalSlots.HasFlag(SlotTypes[i])) if (PersonalSlots.HasFlag(SlotTypes[i]))
{ {
SlotPositions[i] = new Vector2(personalSlotX, personalSlotY); SlotPositions[i] = new Vector2(personalSlotX, personalSlotY);
personalSlotX += slots[i].Rect.Width + Spacing; personalSlotX += visualSlots[i].Rect.Width + Spacing;
} }
else else
{ {
SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset); SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset);
x += slots[i].Rect.Width + Spacing; x += visualSlots[i].Rect.Width + Spacing;
} }
} }
for (int i = 0; i < SlotPositions.Length; i++) for (int i = 0; i < SlotPositions.Length; i++)
{ {
if (!HideSlot(i)) continue; if (!HideSlot(i)) continue;
SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset); SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset);
x += slots[i].Rect.Width + Spacing; x += visualSlots[i].Rect.Width + Spacing;
} }
} }
break; break;
@@ -438,10 +435,10 @@ namespace Barotrauma
if (SlotTypes[i] == InvSlotType.Card || SlotTypes[i] == InvSlotType.Headset || SlotTypes[i] == InvSlotType.InnerClothes) if (SlotTypes[i] == InvSlotType.Card || SlotTypes[i] == InvSlotType.Headset || SlotTypes[i] == InvSlotType.InnerClothes)
{ {
SlotPositions[i] = new Vector2(x, y); SlotPositions[i] = new Vector2(x, y);
x += slots[i].Rect.Width + Spacing; x += visualSlots[i].Rect.Width + Spacing;
} }
} }
y += slots[0].Rect.Height + Spacing + ContainedIndicatorHeight + slots[0].EquipButtonRect.Height; y += visualSlots[0].Rect.Height + Spacing + ContainedIndicatorHeight + visualSlots[0].EquipButtonRect.Height;
x = startX; x = startX;
int n = 0; int n = 0;
for (int i = 0; i < SlotPositions.Length; i++) for (int i = 0; i < SlotPositions.Length; i++)
@@ -450,12 +447,12 @@ namespace Barotrauma
if (SlotTypes[i] != InvSlotType.Card && SlotTypes[i] != InvSlotType.Headset && SlotTypes[i] != InvSlotType.InnerClothes) if (SlotTypes[i] != InvSlotType.Card && SlotTypes[i] != InvSlotType.Headset && SlotTypes[i] != InvSlotType.InnerClothes)
{ {
SlotPositions[i] = new Vector2(x, y); SlotPositions[i] = new Vector2(x, y);
x += slots[i].Rect.Width + Spacing; x += visualSlots[i].Rect.Width + Spacing;
n++; n++;
if (n >= columns) if (n >= columns)
{ {
x = startX; x = startX;
y += slots[i].Rect.Height + Spacing + ContainedIndicatorHeight + slots[i].EquipButtonRect.Height; y += visualSlots[i].Rect.Height + Spacing + ContainedIndicatorHeight + visualSlots[i].EquipButtonRect.Height;
n = 0; n = 0;
} }
} }
@@ -467,7 +464,7 @@ namespace Barotrauma
CreateSlots(); CreateSlots();
if (layout == Layout.Default) if (layout == Layout.Default)
{ {
HUDLayoutSettings.InventoryTopY = slots[0].EquipButtonRect.Y - (int)(15 * GUI.Scale); HUDLayoutSettings.InventoryTopY = visualSlots[0].EquipButtonRect.Y - (int)(15 * GUI.Scale);
} }
} }
@@ -484,7 +481,8 @@ namespace Barotrauma
public override void Update(float deltaTime, Camera cam, bool isSubInventory = false) public override void Update(float deltaTime, Camera cam, bool isSubInventory = false)
{ {
if (!AccessibleWhenAlive && !character.IsDead) // Need to update the infiltrator's inventory because they use id cards to access the sub. TODO: We don't probably need to update everything.
if (!AccessibleWhenAlive && !character.IsDead && (character.Params.AI == null || !character.Params.AI.Infiltrate))
{ {
syncItemsDelay = Math.Max(syncItemsDelay - deltaTime, 0.0f); syncItemsDelay = Math.Max(syncItemsDelay - deltaTime, 0.0f);
return; return;
@@ -493,7 +491,7 @@ namespace Barotrauma
base.Update(deltaTime, cam); base.Update(deltaTime, cam);
bool hoverOnInventory = GUI.MouseOn == null && bool hoverOnInventory = GUI.MouseOn == null &&
((selectedSlot != null && selectedSlot.IsSubSlot) || (draggingItem != null && (draggingSlot == null || !draggingSlot.MouseOn()))); ((selectedSlot != null && selectedSlot.IsSubSlot) || (DraggingItems.Any() && (DraggingSlot == null || !DraggingSlot.MouseOn())));
if (CharacterHealth.OpenHealthWindow != null) hoverOnInventory = true; if (CharacterHealth.OpenHealthWindow != null) hoverOnInventory = true;
if (layout == Layout.Default && (Screen.Selected != GameMain.SubEditorScreen || Screen.Selected is SubEditorScreen editor && editor.WiringMode)) if (layout == Layout.Default && (Screen.Selected != GameMain.SubEditorScreen || Screen.Selected is SubEditorScreen editor && editor.WiringMode))
@@ -508,16 +506,16 @@ namespace Barotrauma
Math.Max(hidePersonalSlotsState - deltaTime * 5.0f, 0.0f); Math.Max(hidePersonalSlotsState - deltaTime * 5.0f, 0.0f);
bool personalSlotsMoving = hidePersonalSlotsState > 0 && hidePersonalSlotsState < 1f; bool personalSlotsMoving = hidePersonalSlotsState > 0 && hidePersonalSlotsState < 1f;
for (int i = 0; i < slots.Length; i++) for (int i = 0; i < visualSlots.Length; i++)
{ {
if (!PersonalSlots.HasFlag(SlotTypes[i])) { continue; } if (!PersonalSlots.HasFlag(SlotTypes[i])) { continue; }
if (HidePersonalSlots) if (HidePersonalSlots)
{ {
if (selectedSlot?.Slot == slots[i]) { selectedSlot = null; } if (selectedSlot?.Slot == visualSlots[i]) { selectedSlot = null; }
highlightedSubInventorySlots.RemoveWhere(s => s.Slot == slots[i]); highlightedSubInventorySlots.RemoveWhere(s => s.Slot == visualSlots[i]);
} }
slots[i].IsMoving = personalSlotsMoving; visualSlots[i].IsMoving = personalSlotsMoving;
slots[i].DrawOffset = Vector2.Lerp(Vector2.Zero, new Vector2(personalSlotArea.Width, 0.0f), hidePersonalSlotsState); visualSlots[i].DrawOffset = Vector2.Lerp(Vector2.Zero, new Vector2(personalSlotArea.Width, 0.0f), hidePersonalSlotsState);
} }
} }
} }
@@ -530,13 +528,15 @@ namespace Barotrauma
//force personal slots open if an item is running out of battery/fuel/oxygen/etc //force personal slots open if an item is running out of battery/fuel/oxygen/etc
if (hidePersonalSlots) if (hidePersonalSlots)
{ {
for (int i = 0; i < slots.Length; i++) for (int i = 0; i < visualSlots.Length; i++)
{ {
if (Items[i]?.OwnInventory != null && Items[i].OwnInventory.Capacity == 1 && PersonalSlots.HasFlag(SlotTypes[i])) var item = slots[i].FirstOrDefault();
if (item?.OwnInventory != null && item.OwnInventory.Capacity == 1 && PersonalSlots.HasFlag(SlotTypes[i]))
{ {
if (Items[i].OwnInventory.Items[0] != null && var containedItem = item.OwnInventory.AllItems.FirstOrDefault();
Items[i].OwnInventory.Items[0].Condition > 0.0f && if (containedItem != null &&
Items[i].OwnInventory.Items[0].Condition / Items[i].OwnInventory.Items[0].MaxCondition < 0.15f) containedItem.Condition > 0.0f &&
containedItem.Condition / containedItem.MaxCondition < 0.15f)
{ {
hidePersonalSlots = false; hidePersonalSlots = false;
} }
@@ -547,7 +547,7 @@ namespace Barotrauma
List<SlotReference> hideSubInventories = new List<SlotReference>(); List<SlotReference> hideSubInventories = new List<SlotReference>();
highlightedSubInventorySlots.RemoveWhere(s => highlightedSubInventorySlots.RemoveWhere(s =>
s.ParentInventory == this && s.ParentInventory == this &&
((s.SlotIndex < 0 || s.SlotIndex >= Items.Length || Items[s.SlotIndex] == null) || (Character.Controlled != null && !Character.Controlled.CanAccessInventory(s.Inventory)))); ((s.SlotIndex < 0 || s.SlotIndex >= slots.Length || slots[s.SlotIndex] == null) || (Character.Controlled != null && !Character.Controlled.CanAccessInventory(s.Inventory))));
foreach (var highlightedSubInventorySlot in highlightedSubInventorySlots) foreach (var highlightedSubInventorySlot in highlightedSubInventorySlots)
{ {
if (highlightedSubInventorySlot.ParentInventory == this) if (highlightedSubInventorySlot.ParentInventory == this)
@@ -558,7 +558,7 @@ namespace Barotrauma
if (!highlightedSubInventorySlot.Inventory.IsInventoryHoverAvailable(character, null)) continue; if (!highlightedSubInventorySlot.Inventory.IsInventoryHoverAvailable(character, null)) continue;
Rectangle hoverArea = GetSubInventoryHoverArea(highlightedSubInventorySlot); Rectangle hoverArea = GetSubInventoryHoverArea(highlightedSubInventorySlot);
if (highlightedSubInventorySlot.Inventory?.slots == null || (!hoverArea.Contains(PlayerInput.MousePosition))) if (highlightedSubInventorySlot.Inventory?.visualSlots == null || (!hoverArea.Contains(PlayerInput.MousePosition)))
{ {
hideSubInventories.Add(highlightedSubInventorySlot); hideSubInventories.Add(highlightedSubInventorySlot);
} }
@@ -585,19 +585,19 @@ namespace Barotrauma
// In sub editor we cannot hover over the slot because they are not rendered so we override it here // In sub editor we cannot hover over the slot because they are not rendered so we override it here
if (Screen.Selected is SubEditorScreen subEditor && !subEditor.WiringMode) if (Screen.Selected is SubEditorScreen subEditor && !subEditor.WiringMode)
{ {
for (int i = 0; i < slots.Length; i++) for (int i = 0; i < visualSlots.Length; i++)
{ {
var subInventory = GetSubInventory(i); var subInventory = GetSubInventory(i);
if (subInventory != null) if (subInventory != null)
{ {
ShowSubInventory(new SlotReference(this, slots[i], i, false, Items[i].GetComponent<ItemContainer>().Inventory), deltaTime, cam, hideSubInventories, true); ShowSubInventory(new SlotReference(this, visualSlots[i], i, false, subInventory), deltaTime, cam, hideSubInventories, true);
} }
} }
} }
foreach (var subInventorySlot in hideSubInventories) foreach (var subInventorySlot in hideSubInventories)
{ {
if (subInventorySlot.Inventory == null) continue; if (subInventorySlot.Inventory == null) { continue; }
subInventorySlot.Inventory.HideTimer -= deltaTime; subInventorySlot.Inventory.HideTimer -= deltaTime;
if (subInventorySlot.Inventory.HideTimer < 0.25f) if (subInventorySlot.Inventory.HideTimer < 0.25f)
{ {
@@ -614,10 +614,10 @@ namespace Barotrauma
for (int i = 0; i < capacity; i++) for (int i = 0; i < capacity; i++)
{ {
var item = Items[i]; var item = slots[i].FirstOrDefault();
if (item != null) if (item != null)
{ {
if (HideSlot(i)) continue; if (HideSlot(i)) { continue; }
if (character.HasEquippedItem(item)) // Keep a subinventory display open permanently when the container is equipped if (character.HasEquippedItem(item)) // Keep a subinventory display open permanently when the container is equipped
{ {
var itemContainer = item.GetComponent<ItemContainer>(); var itemContainer = item.GetComponent<ItemContainer>();
@@ -626,24 +626,32 @@ namespace Barotrauma
character.CanAccessInventory(itemContainer.Inventory) && character.CanAccessInventory(itemContainer.Inventory) &&
!highlightedSubInventorySlots.Any(s => s.Inventory == itemContainer.Inventory)) !highlightedSubInventorySlots.Any(s => s.Inventory == itemContainer.Inventory))
{ {
ShowSubInventory(new SlotReference(this, slots[i], i, false, itemContainer.Inventory), deltaTime, cam, hideSubInventories, true); ShowSubInventory(new SlotReference(this, visualSlots[i], i, false, itemContainer.Inventory), deltaTime, cam, hideSubInventories, true);
} }
} }
} }
} }
} }
if (doubleClickedItem != null) if (doubleClickedItems.Any())
{ {
QuickUseItem(doubleClickedItem, true, true, true); var quickUseAction = GetQuickUseAction(doubleClickedItems.First(), true, true, true);
foreach (Item doubleClickedItem in doubleClickedItems)
{
QuickUseItem(doubleClickedItem, true, true, true, quickUseAction, playSound: doubleClickedItem == doubleClickedItems.First());
if (quickUseAction == QuickUseAction.Equip || quickUseAction == QuickUseAction.UseTreatment)
{
break;
}
}
} }
for (int i = 0; i < capacity; i++) for (int i = 0; i < capacity; i++)
{ {
var item = Items[i]; var item = slots[i].FirstOrDefault();
if (item != null) if (item != null)
{ {
var slot = slots[i]; var slot = visualSlots[i];
if (item.AllowedSlots.Any(a => a != InvSlotType.Any)) if (item.AllowedSlots.Any(a => a != InvSlotType.Any))
{ {
HandleButtonEquipStates(item, slot, deltaTime); HandleButtonEquipStates(item, slot, deltaTime);
@@ -652,10 +660,10 @@ namespace Barotrauma
} }
//cancel dragging if too far away from the container of the dragged item //cancel dragging if too far away from the container of the dragged item
if (draggingItem != null) if (DraggingItems.Any())
{ {
var rootContainer = draggingItem.GetRootContainer(); var rootContainer = DraggingItems.First().GetRootContainer();
var rootInventory = draggingItem.ParentInventory; var rootInventory = DraggingItems.First().ParentInventory;
if (rootContainer != null) if (rootContainer != null)
{ {
@@ -673,27 +681,39 @@ namespace Barotrauma
Character.Controlled.SelectedConstruction != null && Character.Controlled.SelectedConstruction != null &&
rootContainer.linkedTo.Contains(Character.Controlled.SelectedConstruction))) rootContainer.linkedTo.Contains(Character.Controlled.SelectedConstruction)))
{ {
draggingItem = null; DraggingItems.Clear();
} }
} }
} }
doubleClickedItems.Clear();
doubleClickedItem = null;
} }
public void UpdateSlotInput() public void UpdateSlotInput()
{ {
for (int i = 0; i < capacity; i++) for (int i = 0; i < capacity; i++)
{ {
if (Items[i] != null && Items[i] != draggingItem && Character.Controlled?.Inventory == this && var firstItem = slots[i].FirstOrDefault();
GUI.KeyboardDispatcher.Subscriber == null && !CrewManager.IsCommandInterfaceOpen && PlayerInput.InventoryKeyHit(slots[i].InventoryKeyIndex)) if (firstItem != null && !DraggingItems.Contains(firstItem) && Character.Controlled?.Inventory == this &&
GUI.KeyboardDispatcher.Subscriber == null && !CrewManager.IsCommandInterfaceOpen && PlayerInput.InventoryKeyHit(visualSlots[i].InventoryKeyIndex))
{ {
QuickUseItem(Items[i], true, false, true); #if LINUX
// some window managers on Linux use windows key + number to change workspaces or perform other actions
if (PlayerInput.KeyDown(Keys.RightWindows) || PlayerInput.KeyDown(Keys.LeftWindows)) { continue; }
#endif
var quickUseAction = GetQuickUseAction(firstItem, true, false, true);
foreach (Item itemToUse in slots[i].Items.ToList())
{
QuickUseItem(itemToUse, true, true, true, quickUseAction, playSound: itemToUse == firstItem);
if (quickUseAction == QuickUseAction.Equip || quickUseAction == QuickUseAction.UseTreatment)
{
break;
}
}
} }
} }
} }
private void HandleButtonEquipStates(Item item, InventorySlot slot, float deltaTime) private void HandleButtonEquipStates(Item item, VisualSlot slot, float deltaTime)
{ {
slot.EquipButtonState = slot.EquipButtonRect.Contains(PlayerInput.MousePosition) ? slot.EquipButtonState = slot.EquipButtonRect.Contains(PlayerInput.MousePosition) ?
GUIComponent.ComponentState.Hover : GUIComponent.ComponentState.None; GUIComponent.ComponentState.Hover : GUIComponent.ComponentState.None;
@@ -713,7 +733,7 @@ namespace Barotrauma
if (quickUseAction != QuickUseAction.Drop) if (quickUseAction != QuickUseAction.Drop)
{ {
slot.QuickUseButtonToolTip = quickUseAction == QuickUseAction.None ? slot.QuickUseButtonToolTip = quickUseAction == QuickUseAction.None ?
"" : TextManager.GetWithVariable("QuickUseAction." + quickUseAction.ToString(), "[equippeditem]", character.SelectedItems.FirstOrDefault(i => i != null)?.Name); "" : TextManager.GetWithVariable("QuickUseAction." + quickUseAction.ToString(), "[equippeditem]", item?.Name);
if (PlayerInput.PrimaryMouseButtonDown()) { slot.EquipButtonState = GUIComponent.ComponentState.Pressed; } if (PlayerInput.PrimaryMouseButtonDown()) { slot.EquipButtonState = GUIComponent.ComponentState.Pressed; }
if (PlayerInput.PrimaryMouseButtonClicked()) if (PlayerInput.PrimaryMouseButtonClicked())
{ {
@@ -726,8 +746,8 @@ namespace Barotrauma
{ {
for (int i = 0; i < indicators.Length; i++) for (int i = 0; i < indicators.Length; i++)
{ {
if (indicatorIndexes[i] < 0) { continue; } if (indicatorIndices[i] < 0) { continue; }
Item item = Items[indicatorIndexes[i]]; Item item = slots[indicatorIndices[i]].FirstOrDefault();
if (item != null) if (item != null)
{ {
Wearable wearable = item.GetComponent<Wearable>(); Wearable wearable = item.GetComponent<Wearable>();
@@ -795,12 +815,12 @@ namespace Barotrauma
public void AssignQuickUseNumKeys() public void AssignQuickUseNumKeys()
{ {
int keyBindIndex = 0; int keyBindIndex = 0;
for (int i = 0; i < slots.Length; i++) for (int i = 0; i < visualSlots.Length; i++)
{ {
if (HideSlot(i)) continue; if (HideSlot(i)) continue;
if (SlotTypes[i] == InvSlotType.Any) if (SlotTypes[i] == InvSlotType.Any)
{ {
slots[i].InventoryKeyIndex = keyBindIndex; visualSlots[i].InventoryKeyIndex = keyBindIndex;
keyBindIndex++; keyBindIndex++;
} }
} }
@@ -824,7 +844,7 @@ namespace Barotrauma
{ {
if (item.Container == null || character.Inventory.FindIndex(item.Container) == -1) // Not a subinventory in the character's inventory if (item.Container == null || character.Inventory.FindIndex(item.Container) == -1) // Not a subinventory in the character's inventory
{ {
if (character.SelectedItems.Any(i => i?.OwnInventory != null && i.OwnInventory.CanBePut(item))) if (character.HeldItems.Any(i => i.OwnInventory != null && i.OwnInventory.CanBePut(item)))
{ {
return QuickUseAction.PutToEquippedItem; return QuickUseAction.PutToEquippedItem;
} }
@@ -875,7 +895,7 @@ namespace Barotrauma
{ {
return QuickUseAction.TakeFromCharacter; return QuickUseAction.TakeFromCharacter;
} }
else if (character.SelectedItems.Any(i => i?.OwnInventory != null && i.OwnInventory.CanBePut(item)) && allowInventorySwap) else if (character.HeldItems.Any(i => i.OwnInventory != null && i.OwnInventory.CanBePut(item)) && allowInventorySwap)
{ {
return QuickUseAction.PutToEquippedItem; return QuickUseAction.PutToEquippedItem;
} }
@@ -901,23 +921,20 @@ namespace Barotrauma
return QuickUseAction.None; return QuickUseAction.None;
} }
private void QuickUseItem(Item item, bool allowEquip, bool allowInventorySwap, bool allowApplyTreatment) private void QuickUseItem(Item item, bool allowEquip, bool allowInventorySwap, bool allowApplyTreatment, QuickUseAction? action = null, bool playSound = true)
{ {
if (Screen.Selected is SubEditorScreen editor && !editor.WiringMode && !Submarine.Unloading) if (Screen.Selected is SubEditorScreen editor && !editor.WiringMode && !Submarine.Unloading)
{ {
// Find the slot the item was contained in and flash it // Find the slot the item was contained in and flash it
if (item.ParentInventory?.slots != null) if (item.ParentInventory?.visualSlots != null)
{ {
var invSlots = item.ParentInventory.slots; var invSlots = item.ParentInventory.visualSlots;
var invItems = item.ParentInventory.Items;
for (int i = 0; i < invSlots.Length; i++) for (int i = 0; i < invSlots.Length; i++)
{ {
if (i < 0 || invSlots.Length <= i || i < 0 || invItems.Length <= i) { break; } if (i < 0 || invSlots.Length <= i || i < 0 || item.ParentInventory.Capacity <= i) { break; }
var slot = invSlots[i]; var slot = invSlots[i];
var slotItem = invItems[i]; if (item.ParentInventory.GetItemAt(i) == item)
if (slotItem == item)
{ {
slot.ShowBorderHighlight(GUI.Style.Red, 0.1f, 0.4f); slot.ShowBorderHighlight(GUI.Style.Red, 0.1f, 0.4f);
SoundPlayer.PlayUISound(GUISoundType.PickItem); SoundPlayer.PlayUISound(GUISoundType.PickItem);
@@ -932,7 +949,7 @@ namespace Barotrauma
return; return;
} }
var quickUseAction = GetQuickUseAction(item, allowEquip, allowInventorySwap, allowApplyTreatment); QuickUseAction quickUseAction = action ?? GetQuickUseAction(item, allowEquip, allowInventorySwap, allowApplyTreatment);
bool success = false; bool success = false;
switch (quickUseAction) switch (quickUseAction)
{ {
@@ -963,7 +980,7 @@ namespace Barotrauma
//attempt to put in a free slot first //attempt to put in a free slot first
for (int i = capacity - 1; i >= 0; i--) for (int i = capacity - 1; i >= 0; i--)
{ {
if (Items[i] != null) { continue; } if (!slots[i].Empty()) { continue; }
if (SlotTypes[i] == InvSlotType.Any || !item.AllowedSlots.Any(a => a.HasFlag(SlotTypes[i]))) { continue; } if (SlotTypes[i] == InvSlotType.Any || !item.AllowedSlots.Any(a => a.HasFlag(SlotTypes[i]))) { continue; }
success = TryPutItem(item, i, true, false, Character.Controlled, true); success = TryPutItem(item, i, true, false, Character.Controlled, true);
if (success) { break; } if (success) { break; }
@@ -975,9 +992,10 @@ namespace Barotrauma
{ {
if (SlotTypes[i] == InvSlotType.Any || !item.AllowedSlots.Any(a => a.HasFlag(SlotTypes[i]))) { continue; } if (SlotTypes[i] == InvSlotType.Any || !item.AllowedSlots.Any(a => a.HasFlag(SlotTypes[i]))) { continue; }
// something else already equipped in a hand slot, attempt to unequip it so items aren't unnecessarily swapped to it // something else already equipped in a hand slot, attempt to unequip it so items aren't unnecessarily swapped to it
if (Items[i] != null && Items[i].AllowedSlots.Contains(InvSlotType.Any) && (SlotTypes[i] == InvSlotType.LeftHand || SlotTypes[i] == InvSlotType.RightHand)) if (!slots[i].Empty() && slots[i].First().AllowedSlots.Contains(InvSlotType.Any) &&
(SlotTypes[i] == InvSlotType.LeftHand || SlotTypes[i] == InvSlotType.RightHand))
{ {
TryPutItem(Items[i], Character.Controlled, new List<InvSlotType>() { InvSlotType.Any }, true); TryPutItem(slots[i].First(), Character.Controlled, new List<InvSlotType>() { InvSlotType.Any }, true);
} }
success = TryPutItem(item, i, true, false, Character.Controlled, true); success = TryPutItem(item, i, true, false, Character.Controlled, true);
if (success) { break; } if (success) { break; }
@@ -1040,15 +1058,15 @@ namespace Barotrauma
} }
break; break;
case QuickUseAction.PutToEquippedItem: case QuickUseAction.PutToEquippedItem:
for (int i = 0; i < character.SelectedItems.Length; i++) foreach (Item heldItem in character.HeldItems)
{ {
if (character.SelectedItems[i]?.OwnInventory != null && if (heldItem.OwnInventory != null &&
character.SelectedItems[i].OwnInventory.TryPutItem(item, Character.Controlled)) heldItem.OwnInventory.TryPutItem(item, Character.Controlled))
{ {
success = true; success = true;
for (int j = 0; j < capacity; j++) for (int j = 0; j < capacity; j++)
{ {
if (Items[j] == character.SelectedItems[i]) slots[j].ShowBorderHighlight(GUI.Style.Green, 0.1f, 0.4f); if (slots[j].Contains(heldItem)) { visualSlots[j].ShowBorderHighlight(GUI.Style.Green, 0.1f, 0.4f); }
} }
break; break;
} }
@@ -1060,18 +1078,21 @@ namespace Barotrauma
{ {
for (int i = 0; i < capacity; i++) for (int i = 0; i < capacity; i++)
{ {
if (Items[i] == item) slots[i].ShowBorderHighlight(GUI.Style.Green, 0.1f, 0.4f); if (slots[i].Contains(item)) { visualSlots[i].ShowBorderHighlight(GUI.Style.Green, 0.1f, 0.4f); }
} }
} }
draggingItem = null; DraggingItems.Clear();
SoundPlayer.PlayUISound(success ? GUISoundType.PickItem : GUISoundType.PickItemFail); if (playSound)
{
SoundPlayer.PlayUISound(success ? GUISoundType.PickItem : GUISoundType.PickItemFail);
}
} }
public void DrawOwn(SpriteBatch spriteBatch) public void DrawOwn(SpriteBatch spriteBatch)
{ {
if (!AccessibleWhenAlive && !character.IsDead) { return; } if (!AccessibleWhenAlive && !character.IsDead) { return; }
if (slots == null) { CreateSlots(); } if (visualSlots == null) { CreateSlots(); }
if (GameMain.GraphicsWidth != screenResolution.X || if (GameMain.GraphicsWidth != screenResolution.X ||
GameMain.GraphicsHeight != screenResolution.Y || GameMain.GraphicsHeight != screenResolution.Y ||
prevUIScale != UIScale || prevUIScale != UIScale ||
@@ -1096,12 +1117,10 @@ namespace Barotrauma
{ {
if (HideSlot(i)) { continue; } if (HideSlot(i)) { continue; }
Rectangle interactRect = slots[i].InteractRect;
interactRect.Location += slots[i].DrawOffset.ToPoint();
//don't draw the item if it's being dragged out of the slot //don't draw the item if it's being dragged out of the slot
bool drawItem = draggingItem == null || draggingItem != Items[i] || interactRect.Contains(PlayerInput.MousePosition); bool drawItem = !DraggingItems.Any() || !slots[i].Items.All(it => DraggingItems.Contains(it)) || visualSlots[i].MouseOn();
DrawSlot(spriteBatch, this, slots[i], Items[i], i, drawItem, SlotTypes[i]);
DrawSlot(spriteBatch, this, visualSlots[i], slots[i].FirstOrDefault(), i, drawItem, SlotTypes[i]);
} }
if (hideButton != null && hideButton.Visible && !Locked) if (hideButton != null && hideButton.Visible && !Locked)
@@ -1109,48 +1128,48 @@ namespace Barotrauma
hideButton.DrawManually(spriteBatch, alsoChildren: true); hideButton.DrawManually(spriteBatch, alsoChildren: true);
} }
InventorySlot highlightedQuickUseSlot = null; VisualSlot highlightedQuickUseSlot = null;
Rectangle inventoryArea = Rectangle.Empty; Rectangle inventoryArea = Rectangle.Empty;
for (int i = 0; i < capacity; i++) for (int i = 0; i < capacity; i++)
{ {
if (HideSlot(i)) { continue; } if (HideSlot(i)) { continue; }
inventoryArea = inventoryArea == Rectangle.Empty ? slots[i].InteractRect : Rectangle.Union(inventoryArea, slots[i].InteractRect); inventoryArea = inventoryArea == Rectangle.Empty ? visualSlots[i].InteractRect : Rectangle.Union(inventoryArea, visualSlots[i].InteractRect);
if (Items[i] == null || if (slots[i].Empty() ||
(draggingItem == Items[i] && !slots[i].InteractRect.Contains(PlayerInput.MousePosition)) || (DraggingItems.Any(it => slots[i].Contains(it)) && !visualSlots[i].InteractRect.Contains(PlayerInput.MousePosition)) ||
!Items[i].AllowedSlots.Any(a => a != InvSlotType.Any)) !slots[i].First().AllowedSlots.Any(a => a != InvSlotType.Any))
{ {
//draw limb icons on empty slots //draw limb icons on empty slots
if (LimbSlotIcons.ContainsKey(SlotTypes[i])) if (LimbSlotIcons.ContainsKey(SlotTypes[i]))
{ {
var icon = LimbSlotIcons[SlotTypes[i]]; var icon = LimbSlotIcons[SlotTypes[i]];
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); icon.Draw(spriteBatch, visualSlots[i].Rect.Center.ToVector2() + visualSlots[i].DrawOffset, GUI.Style.EquipmentSlotIconColor, origin: icon.size / 2, scale: visualSlots[i].Rect.Width / icon.size.X);
} }
continue; continue;
} }
if (draggingItem == Items[i] && !slots[i].IsHighlighted) { continue; } if (DraggingItems.Any(it => slots[i].Contains(it)) && !visualSlots[i].IsHighlighted) { continue; }
//draw hand icons if the item is equipped in a hand slot //draw hand icons if the item is equipped in a hand slot
if (IsInLimbSlot(Items[i], InvSlotType.LeftHand)) if (IsInLimbSlot(slots[i].First(), InvSlotType.LeftHand))
{ {
var icon = LimbSlotIcons[InvSlotType.LeftHand]; var icon = LimbSlotIcons[InvSlotType.LeftHand];
icon.Draw(spriteBatch, new Vector2(slots[i].Rect.X, slots[i].Rect.Bottom) + slots[i].DrawOffset, Color.White * 0.6f, origin: new Vector2(icon.size.X * 0.35f, icon.size.Y * 0.75f), scale: slots[i].Rect.Width / icon.size.X * 0.7f); icon.Draw(spriteBatch, new Vector2(visualSlots[i].Rect.X, visualSlots[i].Rect.Bottom) + visualSlots[i].DrawOffset, Color.White * 0.6f, origin: new Vector2(icon.size.X * 0.35f, icon.size.Y * 0.75f), scale: visualSlots[i].Rect.Width / icon.size.X * 0.7f);
} }
if (IsInLimbSlot(Items[i], InvSlotType.RightHand)) if (IsInLimbSlot(slots[i].First(), InvSlotType.RightHand))
{ {
var icon = LimbSlotIcons[InvSlotType.RightHand]; var icon = LimbSlotIcons[InvSlotType.RightHand];
icon.Draw(spriteBatch, new Vector2(slots[i].Rect.Right, slots[i].Rect.Bottom) + slots[i].DrawOffset, Color.White * 0.6f, origin: new Vector2(icon.size.X * 0.65f, icon.size.Y * 0.75f), scale: slots[i].Rect.Width / icon.size.X * 0.7f); icon.Draw(spriteBatch, new Vector2(visualSlots[i].Rect.Right, visualSlots[i].Rect.Bottom) + visualSlots[i].DrawOffset, Color.White * 0.6f, origin: new Vector2(icon.size.X * 0.65f, icon.size.Y * 0.75f), scale: visualSlots[i].Rect.Width / icon.size.X * 0.7f);
} }
GUIComponent.ComponentState state = slots[i].EquipButtonState; GUIComponent.ComponentState state = visualSlots[i].EquipButtonState;
if (state == GUIComponent.ComponentState.Hover) if (state == GUIComponent.ComponentState.Hover)
{ {
highlightedQuickUseSlot = slots[i]; highlightedQuickUseSlot = visualSlots[i];
} }
if (!Items[i].AllowedSlots.Any(a => a == InvSlotType.Any)) if (!slots[i].First().AllowedSlots.Any(a => a == InvSlotType.Any))
{ {
continue; continue;
} }
@@ -1161,20 +1180,20 @@ namespace Barotrauma
color *= 0.5f; color *= 0.5f;
} }
if (character.HasEquippedItem(Items[i])) if (character.HasEquippedItem(slots[i].First()))
{ {
switch (state) switch (state)
{ {
case GUIComponent.ComponentState.None: case GUIComponent.ComponentState.None:
EquippedIndicator.Draw(spriteBatch, slots[i].EquipButtonRect.Center.ToVector2(), color, EquippedIndicator.Origin, 0, UIScale * IndicatorScaleAdjustment); EquippedIndicator.Draw(spriteBatch, visualSlots[i].EquipButtonRect.Center.ToVector2(), color, EquippedIndicator.Origin, 0, UIScale * IndicatorScaleAdjustment);
break; break;
case GUIComponent.ComponentState.Hover: case GUIComponent.ComponentState.Hover:
EquippedHoverIndicator.Draw(spriteBatch, slots[i].EquipButtonRect.Center.ToVector2(), color, EquippedIndicator.Origin, 0, UIScale * IndicatorScaleAdjustment); EquippedHoverIndicator.Draw(spriteBatch, visualSlots[i].EquipButtonRect.Center.ToVector2(), color, EquippedIndicator.Origin, 0, UIScale * IndicatorScaleAdjustment);
break; break;
case GUIComponent.ComponentState.Pressed: case GUIComponent.ComponentState.Pressed:
case GUIComponent.ComponentState.Selected: case GUIComponent.ComponentState.Selected:
case GUIComponent.ComponentState.HoverSelected: case GUIComponent.ComponentState.HoverSelected:
EquippedClickedIndicator.Draw(spriteBatch, slots[i].EquipButtonRect.Center.ToVector2(), color, EquippedIndicator.Origin, 0, UIScale * IndicatorScaleAdjustment); EquippedClickedIndicator.Draw(spriteBatch, visualSlots[i].EquipButtonRect.Center.ToVector2(), color, EquippedIndicator.Origin, 0, UIScale * IndicatorScaleAdjustment);
break; break;
} }
} }
@@ -1183,15 +1202,15 @@ namespace Barotrauma
switch (state) switch (state)
{ {
case GUIComponent.ComponentState.None: case GUIComponent.ComponentState.None:
UnequippedIndicator.Draw(spriteBatch, slots[i].EquipButtonRect.Center.ToVector2(), color, EquippedIndicator.Origin, 0, UIScale * IndicatorScaleAdjustment); UnequippedIndicator.Draw(spriteBatch, visualSlots[i].EquipButtonRect.Center.ToVector2(), color, EquippedIndicator.Origin, 0, UIScale * IndicatorScaleAdjustment);
break; break;
case GUIComponent.ComponentState.Hover: case GUIComponent.ComponentState.Hover:
UnequippedHoverIndicator.Draw(spriteBatch, slots[i].EquipButtonRect.Center.ToVector2(), color, EquippedIndicator.Origin, 0, UIScale * IndicatorScaleAdjustment); UnequippedHoverIndicator.Draw(spriteBatch, visualSlots[i].EquipButtonRect.Center.ToVector2(), color, EquippedIndicator.Origin, 0, UIScale * IndicatorScaleAdjustment);
break; break;
case GUIComponent.ComponentState.Pressed: case GUIComponent.ComponentState.Pressed:
case GUIComponent.ComponentState.Selected: case GUIComponent.ComponentState.Selected:
case GUIComponent.ComponentState.HoverSelected: case GUIComponent.ComponentState.HoverSelected:
UnequippedClickedIndicator.Draw(spriteBatch, slots[i].EquipButtonRect.Center.ToVector2(), color, EquippedIndicator.Origin, 0, UIScale * IndicatorScaleAdjustment); UnequippedClickedIndicator.Draw(spriteBatch, visualSlots[i].EquipButtonRect.Center.ToVector2(), color, EquippedIndicator.Origin, 0, UIScale * IndicatorScaleAdjustment);
break; break;
} }
} }
@@ -12,8 +12,6 @@ namespace Barotrauma.Items.Components
get { return item.Rect.Size.ToVector2(); } get { return item.Rect.Size.ToVector2(); }
} }
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1) public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
{ {
if (!IsActive || picker == null || !CanBeAttached(picker) || !picker.IsKeyDown(InputType.Aim) || picker != Character.Controlled) if (!IsActive || picker == null || !CanBeAttached(picker) || !picker.IsKeyDown(InputType.Aim) || picker != Character.Controlled)
@@ -49,7 +47,7 @@ namespace Barotrauma.Items.Components
attachPos += item.Submarine.Position; attachPos += item.Submarine.Position;
} }
Submarine.DrawGrid(spriteBatch, 14, gridPos, roundedGridPos, alpha: 0.7f); Submarine.DrawGrid(spriteBatch, 14, gridPos, roundedGridPos, alpha: 0.4f);
item.Sprite.Draw( item.Sprite.Draw(
spriteBatch, spriteBatch,
@@ -130,7 +130,7 @@ namespace Barotrauma.Items.Components
if (body.UserData is Item item) if (body.UserData is Item item)
{ {
var door = item.GetComponent<Door>(); var door = item.GetComponent<Door>();
if (door != null && door.IsOpen || door.IsBroken) continue; if (door != null && door.CanBeTraversed) { continue; }
} }
targetHull = null; targetHull = null;
@@ -239,7 +239,7 @@ namespace Barotrauma.Items.Components
{ {
if (targetSections.Count == 0) { return; } if (targetSections.Count == 0) { return; }
Item liquidItem = liquidContainer?.Inventory.Items[0]; Item liquidItem = liquidContainer?.Inventory.FirstOrDefault();
if (liquidItem == null) { return; } if (liquidItem == null) { return; }
bool isCleaning = false; bool isCleaning = false;
@@ -15,7 +15,8 @@ namespace Barotrauma.Items.Components
Random, Random,
CharacterSpecific, CharacterSpecific,
ItemSpecific, ItemSpecific,
All All,
Manual
} }
class ItemSound class ItemSound
@@ -259,6 +260,7 @@ namespace Barotrauma.Items.Components
loopingSoundChannel = null; loopingSoundChannel = null;
loopingSound = null; loopingSound = null;
} }
if (loopingSoundChannel == null || !loopingSoundChannel.IsPlaying) if (loopingSoundChannel == null || !loopingSoundChannel.IsPlaying)
{ {
loopingSoundChannel = loopingSound.RoundSound.Sound.Play( loopingSoundChannel = loopingSound.RoundSound.Sound.Play(
@@ -271,6 +273,21 @@ namespace Barotrauma.Items.Components
loopingSoundChannel.Near = loopingSound.Range * 0.4f; loopingSoundChannel.Near = loopingSound.Range * 0.4f;
loopingSoundChannel.Far = loopingSound.Range; loopingSoundChannel.Far = loopingSound.Range;
} }
// Looping sound with manual selection mode should be changed if value of ManuallySelectedSound has changed
// Otherwise the sound won't change until the sound condition (such as being active) is disabled and re-enabled
if (loopingSoundChannel != null && loopingSoundChannel.IsPlaying && soundSelectionModes[type] == SoundSelectionMode.Manual)
{
var playingIndex = sounds[type].IndexOf(loopingSound);
var shouldBePlayingIndex = Math.Clamp(ManuallySelectedSound, 0, sounds[type].Count);
if (playingIndex != shouldBePlayingIndex)
{
loopingSoundChannel.FadeOutAndDispose();
loopingSoundChannel = null;
loopingSound = null;
}
}
return; return;
} }
@@ -295,6 +312,10 @@ namespace Barotrauma.Items.Components
} }
return; return;
} }
else if (soundSelectionMode == SoundSelectionMode.Manual)
{
index = Math.Clamp(ManuallySelectedSound, 0, matchingSounds.Count);
}
else else
{ {
index = Rand.Int(matchingSounds.Count); index = Rand.Int(matchingSounds.Count);
@@ -335,7 +356,7 @@ namespace Barotrauma.Items.Components
{ {
float volume = GetSoundVolume(itemSound); float volume = GetSoundVolume(itemSound);
if (volume <= 0.0001f) { return; } if (volume <= 0.0001f) { return; }
var channel = SoundPlayer.PlaySound(itemSound.RoundSound.Sound, position, volume, itemSound.Range, itemSound.RoundSound.GetRandomFrequencyMultiplier(), item.CurrentHull); var channel = SoundPlayer.PlaySound(itemSound.RoundSound.Sound, position, volume, itemSound.Range, itemSound.RoundSound.GetRandomFrequencyMultiplier(), item.CurrentHull, ignoreMuffling: itemSound.RoundSound.IgnoreMuffling);
if (channel != null) { playingOneshotSoundChannels.Add(channel); } if (channel != null) { playingOneshotSoundChannels.Add(channel); }
} }
} }
@@ -1,4 +1,5 @@
using System; using System;
using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
@@ -32,6 +33,12 @@ namespace Barotrauma.Items.Components
private set; private set;
} }
public Sprite ContainedStateIndicatorEmpty
{
get;
private set;
}
#if DEBUG #if DEBUG
[Editable] [Editable]
#endif #endif
@@ -55,6 +62,11 @@ namespace Barotrauma.Items.Components
[Serialize(null, false, description: "An optional text displayed above the item's inventory.")] [Serialize(null, false, description: "An optional text displayed above the item's inventory.")]
public string UILabel { get; set; } public string UILabel { get; set; }
public GUIComponentStyle IndicatorStyle { get; set; }
[Serialize(null, false)]
public string ContainedStateIndicatorStyle { get; set; }
[Serialize(true, false, description: "Should an indicator displaying the state of the contained items be displayed on this item's inventory slot. "+ [Serialize(true, false, description: "Should an indicator displaying the state of the contained items be displayed on this item's inventory slot. "+
"If this item can only contain one item, the indicator will display the condition of the contained item, otherwise it will indicate how full the item is.")] "If this item can only contain one item, the indicator will display the condition of the contained item, otherwise it will indicate how full the item is.")]
public bool ShowContainedStateIndicator { get; set; } public bool ShowContainedStateIndicator { get; set; }
@@ -98,6 +110,26 @@ namespace Barotrauma.Items.Components
case "containedstateindicator": case "containedstateindicator":
ContainedStateIndicator = new Sprite(subElement); ContainedStateIndicator = new Sprite(subElement);
break; break;
case "containedstateindicatorempty":
ContainedStateIndicatorEmpty = new Sprite(subElement);
break;
}
}
if (string.IsNullOrEmpty(ContainedStateIndicatorStyle))
{
//if neither a style or a custom sprite is defined, use default style
if (ContainedStateIndicator == null)
{
IndicatorStyle = GUI.Style.GetComponentStyle("ContainedStateIndicator.Default");
}
}
else
{
IndicatorStyle = GUI.Style.GetComponentStyle("ContainedStateIndicator." + ContainedStateIndicatorStyle);
if (ContainedStateIndicator != null || ContainedStateIndicatorEmpty != null)
{
DebugConsole.AddWarning($"Item \"{item.Name}\" defines both a contained state indicator style and a custom indicator sprite. Will use the custom sprite...");
} }
} }
if (GuiFrame == null) if (GuiFrame == null)
@@ -173,15 +205,9 @@ namespace Barotrauma.Items.Components
} }
//if holding 2 different "always open" items in different hands, don't force them to stay open //if holding 2 different "always open" items in different hands, don't force them to stay open
if (character.SelectedItems[0] != null && if (character.HeldItems.Count() > 1 && character.HeldItems.All(it => it.GetComponent<ItemContainer>()?.KeepOpenWhenEquipped ?? false))
character.SelectedItems[1] != null &&
character.SelectedItems[0] != character.SelectedItems[1])
{ {
if ((character.SelectedItems[0].GetComponent<ItemContainer>()?.KeepOpenWhenEquipped ?? false) && return false;
(character.SelectedItems[1].GetComponent<ItemContainer>()?.KeepOpenWhenEquipped ?? false))
{
return false;
}
} }
return true; return true;
@@ -260,10 +286,8 @@ namespace Barotrauma.Items.Components
bool isWiringMode = SubEditorScreen.TransparentWiringMode && SubEditorScreen.IsWiringMode(); bool isWiringMode = SubEditorScreen.TransparentWiringMode && SubEditorScreen.IsWiringMode();
int i = 0; int i = 0;
foreach (Item containedItem in Inventory.Items) foreach (Item containedItem in Inventory.AllItems)
{ {
if (containedItem == null) continue;
if (AutoInteractWithContained) if (AutoInteractWithContained)
{ {
containedItem.IsHighlighted = item.IsHighlighted; containedItem.IsHighlighted = item.IsHighlighted;
@@ -313,7 +337,7 @@ namespace Barotrauma.Items.Components
public override void UpdateHUD(Character character, float deltaTime, Camera cam) public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{ {
if (item.NonInteractable) { return; } if (!item.IsInteractable(character)) { return; }
if (Inventory.RectTransform != null) if (Inventory.RectTransform != null)
{ {
guiCustomComponent.RectTransform.Parent = Inventory.RectTransform; guiCustomComponent.RectTransform.Parent = Inventory.RectTransform;
@@ -21,11 +21,17 @@ namespace Barotrauma.Items.Components
private float[] charWidths; private float[] charWidths;
private Vector4 padding;
[Serialize("0,0,0,0", true, description: "The amount of padding around the text in pixels (left,top,right,bottom).")] [Serialize("0,0,0,0", true, description: "The amount of padding around the text in pixels (left,top,right,bottom).")]
public Vector4 Padding public Vector4 Padding
{ {
get { return TextBlock.Padding; } get { return padding; }
set { TextBlock.Padding = value; } set
{
padding = value;
TextBlock.Padding = value * item.Scale;
}
} }
private string text; private string text;
@@ -43,13 +49,20 @@ namespace Barotrauma.Items.Components
} }
text = value; text = value;
DisplayText = TextManager.Get(text, returnNull: true) ?? value; SetDisplayText(value);
TextBlock.Text = DisplayText; }
if (Screen.Selected == GameMain.SubEditorScreen && Scrollable) }
{
TextBlock.Text = ToolBox.LimitString(DisplayText, textBlock.Font, item.Rect.Width); private bool ignoreLocalization;
}
SetScrollingText(); [Editable, Serialize(false, true, "Whether or not to skip localization and always display the raw value.")]
public bool IgnoreLocalization
{
get => ignoreLocalization;
set
{
ignoreLocalization = value;
SetDisplayText(Text);
} }
} }
@@ -107,13 +120,7 @@ namespace Barotrauma.Items.Components
{ {
if (textBlock == null) if (textBlock == null)
{ {
textBlock = new GUITextBlock(new RectTransform(item.Rect.Size), "", RecreateTextBlock();
textColor: textColor, font: GUI.UnscaledSmallFont, textAlignment: scrollable ? Alignment.CenterLeft : Alignment.Center, wrap: true, style: null)
{
TextDepth = item.SpriteDepth - 0.00001f,
RoundToNearestPixel = false,
TextScale = TextScale
};
} }
return textBlock; return textBlock;
} }
@@ -126,7 +133,7 @@ namespace Barotrauma.Items.Components
private void SetScrollingText() private void SetScrollingText()
{ {
if (!scrollable) return; if (!scrollable) { return; }
float totalWidth = textBlock.Font.MeasureString(DisplayText).X; float totalWidth = textBlock.Font.MeasureString(DisplayText).X;
float textAreaWidth = Math.Max(textBlock.Rect.Width - textBlock.Padding.X - textBlock.Padding.Z, 0); float textAreaWidth = Math.Max(textBlock.Rect.Width - textBlock.Padding.X - textBlock.Padding.Z, 0);
@@ -143,6 +150,7 @@ namespace Barotrauma.Items.Components
//whole text can fit in the textblock, no need to scroll //whole text can fit in the textblock, no need to scroll
needsScrolling = false; needsScrolling = false;
scrollingText = DisplayText; scrollingText = DisplayText;
scrollPadding = 0;
scrollAmount = 0.0f; scrollAmount = 0.0f;
scrollIndex = 0; scrollIndex = 0;
return; return;
@@ -161,16 +169,40 @@ namespace Barotrauma.Items.Components
scrollIndex = MathHelper.Clamp(scrollIndex, 0, DisplayText.Length); scrollIndex = MathHelper.Clamp(scrollIndex, 0, DisplayText.Length);
} }
private void SetDisplayText(string value)
{
DisplayText = IgnoreLocalization ? value : TextManager.Get(value, returnNull: true) ?? value;
TextBlock.Text = DisplayText;
if (Screen.Selected == GameMain.SubEditorScreen && Scrollable)
{
TextBlock.Text = ToolBox.LimitString(DisplayText, TextBlock.Font, item.Rect.Width);
}
SetScrollingText();
}
private void RecreateTextBlock()
{
textBlock = new GUITextBlock(new RectTransform(item.Rect.Size), "",
textColor: textColor, font: GUI.UnscaledSmallFont, textAlignment: scrollable ? Alignment.CenterLeft : Alignment.Center, wrap: !scrollable, style: null)
{
TextDepth = item.SpriteDepth - 0.00001f,
RoundToNearestPixel = false,
TextScale = TextScale,
Padding = padding * item.Scale
};
}
public override void Update(float deltaTime, Camera cam) public override void Update(float deltaTime, Camera cam)
{ {
if (!scrollable) return; if (!scrollable) { return; }
if (scrollingText == null) if (scrollingText == null)
{ {
SetScrollingText(); SetScrollingText();
} }
if (!needsScrolling) return; if (!needsScrolling) { return; }
scrollAmount -= deltaTime * ScrollSpeed; scrollAmount -= deltaTime * ScrollSpeed;
@@ -207,8 +239,30 @@ namespace Barotrauma.Items.Components
TextBlock.Text = sb.ToString(); TextBlock.Text = sb.ToString();
} }
public override void OnScaleChanged()
{
RecreateTextBlock();
SetDisplayText(Text);
prevScale = item.Scale;
prevRect = item.Rect;
}
private float prevScale;
private Rectangle prevRect;
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1) public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
{ {
if (editing)
{
if (!MathUtils.NearlyEqual(prevScale, item.Scale) || prevRect != item.Rect)
{
RecreateTextBlock();
SetDisplayText(Text);
prevScale = item.Scale;
prevRect = item.Rect;
}
}
var drawPos = new Vector2( var drawPos = new Vector2(
item.DrawPosition.X - item.Rect.Width / 2.0f, item.DrawPosition.X - item.Rect.Width / 2.0f,
-(item.DrawPosition.Y + item.Rect.Height / 2.0f)); -(item.DrawPosition.Y + item.Rect.Height / 2.0f));
@@ -223,7 +277,7 @@ namespace Barotrauma.Items.Components
} }
textBlock.TextDepth = item.SpriteDepth - 0.0001f; textBlock.TextDepth = item.SpriteDepth - 0.0001f;
textBlock.TextOffset = drawPos - textBlock.Rect.Location.ToVector2() + new Vector2(scrollAmount + scrollPadding, 0.0f); textBlock.TextOffset = drawPos - textBlock.Rect.Location.ToVector2() + (editing ? Vector2.Zero : new Vector2(scrollAmount + scrollPadding, 0.0f));
textBlock.DrawManually(spriteBatch); textBlock.DrawManually(spriteBatch);
} }
@@ -38,12 +38,6 @@ 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)
@@ -51,7 +45,7 @@ namespace Barotrauma.Items.Components
Vector2 origin = light.LightSprite.Origin; Vector2 origin = light.LightSprite.Origin;
if ((light.LightSpriteEffect & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally) { origin.X = light.LightSprite.SourceRect.Width - origin.X; } if ((light.LightSpriteEffect & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally) { origin.X = light.LightSprite.SourceRect.Width - origin.X; }
if ((light.LightSpriteEffect & SpriteEffects.FlipVertically) == SpriteEffects.FlipVertically) { origin.Y = light.LightSprite.SourceRect.Height - origin.Y; } if ((light.LightSpriteEffect & SpriteEffects.FlipVertically) == SpriteEffects.FlipVertically) { origin.Y = light.LightSprite.SourceRect.Height - origin.Y; }
light.LightSprite.Draw(spriteBatch, new Vector2(item.DrawPosition.X, -item.DrawPosition.Y), lightColor * lightBrightness, origin, -light.Rotation, item.Scale, light.LightSpriteEffect, item.SpriteDepth - 0.0001f); light.LightSprite.Draw(spriteBatch, new Vector2(item.DrawPosition.X, -item.DrawPosition.Y), lightColor * lightBrightness, origin, -light.Rotation, item.Scale, light.LightSpriteEffect, itemDepth - 0.0001f);
} }
} }
@@ -95,11 +95,11 @@ namespace Barotrauma.Items.Components
// TODO, This works fine as of now but if GUI.PreventElementOverlap ever gets fixed this block of code may become obsolete or detrimental. // TODO, This works fine as of now but if GUI.PreventElementOverlap ever gets fixed this block of code may become obsolete or detrimental.
// Only do this if there's only one linked component. If you link more containers then may // Only do this if there's only one linked component. If you link more containers then may
// GUI.PreventElementOverlap have mercy on your HUD layout // GUI.PreventElementOverlap have mercy on your HUD layout
if (GuiFrame != null && item.linkedTo.Count(entity => entity is Item item && item.DisplaySideBySideWhenLinked) == 1) if (GuiFrame != null && item.linkedTo.Count(entity => entity is Item { DisplaySideBySideWhenLinked: true }) == 1)
{ {
foreach (MapEntity linkedTo in item.linkedTo) foreach (MapEntity linkedTo in item.linkedTo)
{ {
if (!(linkedTo is Item linkedItem) || !linkedItem.DisplaySideBySideWhenLinked) { continue; } if (!(linkedTo is Item { DisplaySideBySideWhenLinked: true } linkedItem)) { continue; }
if (!linkedItem.Components.Any()) { continue; } if (!linkedItem.Components.Any()) { continue; }
var itemContainer = linkedItem.GetComponent<ItemContainer>(); var itemContainer = linkedItem.GetComponent<ItemContainer>();
@@ -108,8 +108,8 @@ namespace Barotrauma.Items.Components
// how much spacing do we want between the components // how much spacing do we want between the components
var padding = (int) (8 * GUI.Scale); var padding = (int) (8 * GUI.Scale);
// Move the linked container to the right and move the deconstructor to the left // Move the linked container to the right and move the deconstructor to the left
itemContainer.GuiFrame.RectTransform.AbsoluteOffset = new Point(100, 0); itemContainer.GuiFrame.RectTransform.AbsoluteOffset = new Point(GuiFrame.Rect.Width / -2 - padding, 0);
GuiFrame.RectTransform.AbsoluteOffset = new Point(-100, 0); GuiFrame.RectTransform.AbsoluteOffset = new Point(itemContainer.GuiFrame.Rect.Width / 2 + padding, 0);
} }
} }
return base.Select(character); return base.Select(character);
@@ -126,7 +126,7 @@ namespace Barotrauma.Items.Components
private void DrawOverLay(SpriteBatch spriteBatch, GUICustomComponent overlayComponent) private void DrawOverLay(SpriteBatch spriteBatch, GUICustomComponent overlayComponent)
{ {
overlayComponent.RectTransform.SetAsLastChild(); overlayComponent.RectTransform.SetAsLastChild();
var lastSlot = inputContainer.Inventory.slots.Last(); var lastSlot = inputContainer.Inventory.visualSlots.Last();
GUI.DrawRectangle(spriteBatch, GUI.DrawRectangle(spriteBatch,
new Rectangle( new Rectangle(
@@ -144,16 +144,15 @@ namespace Barotrauma.Items.Components
Vector2 drawPos = item.DrawPosition; Vector2 drawPos = item.DrawPosition;
drawPos += PropellerPos; drawPos += PropellerPos;
drawPos.Y = -drawPos.Y; drawPos.Y = -drawPos.Y;
propellerSprite.Draw(spriteBatch, (int)Math.Floor(spriteIndex), drawPos, Color.White, propellerSprite.Origin, 0.0f, Vector2.One); propellerSprite.Draw(spriteBatch, (int)Math.Floor(spriteIndex), drawPos, Color.White, propellerSprite.Origin, 0.0f, Vector2.One);
} }
if (editing && !GUI.DisableHUD) if (editing && !DisablePropellerDamage && propellerDamage != null && !GUI.DisableHUD)
{ {
Vector2 drawPos = item.DrawPosition; Vector2 drawPos = item.DrawPosition;
drawPos += PropellerPos; drawPos += PropellerPos * item.Scale;
drawPos.Y = -drawPos.Y; drawPos.Y = -drawPos.Y;
GUI.DrawRectangle(spriteBatch, drawPos - Vector2.One * 10, Vector2.One * 20, GUI.Style.Red); spriteBatch.DrawCircle(drawPos, propellerDamage.DamageRange * item.Scale, 16, GUI.Style.Red, thickness: 2);
} }
} }
@@ -190,7 +190,7 @@ namespace Barotrauma.Items.Components
}; };
} }
new GUITextBlock(new RectTransform(new Vector2(0.85f, 1f), container.RectTransform), fi.DisplayName) new GUITextBlock(new RectTransform(new Vector2(0.85f, 1f), container.RectTransform), GetRecipeNameAndAmount(fi))
{ {
Padding = Vector4.Zero, Padding = Vector4.Zero,
AutoScaleVertical = true, AutoScaleVertical = true,
@@ -199,6 +199,21 @@ namespace Barotrauma.Items.Components
} }
} }
private string GetRecipeNameAndAmount(FabricationRecipe fabricationRecipe)
{
if (fabricationRecipe == null) { return ""; }
if (fabricationRecipe.Amount > 1)
{
return TextManager.GetWithVariables("fabricationrecipenamewithamount",
new string[2] { "[name]", "[amount]" },
new string[2] { fabricationRecipe.DisplayName, fabricationRecipe.Amount.ToString() });
}
else
{
return fabricationRecipe.DisplayName;
}
}
partial void OnItemLoadedProjSpecific() partial void OnItemLoadedProjSpecific()
{ {
inputContainer.AllowUIOverlap = true; inputContainer.AllowUIOverlap = true;
@@ -212,11 +227,11 @@ namespace Barotrauma.Items.Components
// TODO, This works fine as of now but if GUI.PreventElementOverlap ever gets fixed this block of code may become obsolete or detrimental. // TODO, This works fine as of now but if GUI.PreventElementOverlap ever gets fixed this block of code may become obsolete or detrimental.
// Only do this if there's only one linked component. If you link more containers then may // Only do this if there's only one linked component. If you link more containers then may
// GUI.PreventElementOverlap have mercy on your HUD layout // GUI.PreventElementOverlap have mercy on your HUD layout
if (GuiFrame != null && item.linkedTo.Count(entity => entity is Item item && item.DisplaySideBySideWhenLinked) == 1) if (GuiFrame != null && item.linkedTo.Count(entity => entity is Item { DisplaySideBySideWhenLinked: true }) == 1)
{ {
foreach (MapEntity linkedTo in item.linkedTo) foreach (MapEntity linkedTo in item.linkedTo)
{ {
if (!(linkedTo is Item linkedItem) || !linkedItem.DisplaySideBySideWhenLinked) { continue; } if (!(linkedTo is Item { DisplaySideBySideWhenLinked: true } linkedItem)) { continue; }
if (!linkedItem.Components.Any()) { continue; } if (!linkedItem.Components.Any()) { continue; }
var itemContainer = linkedItem.GetComponent<ItemContainer>(); var itemContainer = linkedItem.GetComponent<ItemContainer>();
@@ -225,8 +240,8 @@ namespace Barotrauma.Items.Components
// how much spacing do we want between the components // how much spacing do we want between the components
var padding = (int) (8 * GUI.Scale); var padding = (int) (8 * GUI.Scale);
// Move the linked container to the right and move the fabricator to the left // Move the linked container to the right and move the fabricator to the left
itemContainer.GuiFrame.RectTransform.AbsoluteOffset = new Point(-100, 0); itemContainer.GuiFrame.RectTransform.AbsoluteOffset = new Point(GuiFrame.Rect.Width / -2 - padding, 0);
GuiFrame.RectTransform.AbsoluteOffset = new Point(100, 0); GuiFrame.RectTransform.AbsoluteOffset = new Point(itemContainer.GuiFrame.Rect.Width / 2 + padding, 0);
} }
} }
@@ -287,9 +302,8 @@ namespace Barotrauma.Items.Components
missingItems.Add(requiredItem); missingItems.Add(requiredItem);
} }
} }
foreach (Item item in inputContainer.Inventory.Items) foreach (Item item in inputContainer.Inventory.AllItems)
{ {
if (item == null) { continue; }
missingItems.Remove(missingItems.FirstOrDefault(mi => mi.ItemPrefabs.Contains(item.prefab))); missingItems.Remove(missingItems.FirstOrDefault(mi => mi.ItemPrefabs.Contains(item.prefab)));
} }
@@ -297,7 +311,7 @@ namespace Barotrauma.Items.Components
foreach (FabricationRecipe.RequiredItem requiredItem in missingItems) foreach (FabricationRecipe.RequiredItem requiredItem in missingItems)
{ {
while (slotIndex < inputContainer.Capacity && inputContainer.Inventory.Items[slotIndex] != null) while (slotIndex < inputContainer.Capacity && inputContainer.Inventory.GetItemAt(slotIndex) != null)
{ {
slotIndex++; slotIndex++;
} }
@@ -307,17 +321,17 @@ namespace Barotrauma.Items.Components
{ {
if (item.ParentInventory != inputContainer.Inventory && IsItemValidIngredient(item, requiredItem)) if (item.ParentInventory != inputContainer.Inventory && IsItemValidIngredient(item, requiredItem))
{ {
int availableSlotIndex = Array.IndexOf(item.ParentInventory.Items, item); int availableSlotIndex = item.ParentInventory.FindIndex(item);
//slots are null if the inventory has never been displayed //slots are null if the inventory has never been displayed
//(linked item, but the UI is not set to be displayed at the same time) //(linked item, but the UI is not set to be displayed at the same time)
if (item.ParentInventory.slots != null) if (item.ParentInventory.visualSlots != null)
{ {
if (item.ParentInventory.slots[availableSlotIndex].HighlightTimer <= 0.0f) if (item.ParentInventory.visualSlots[availableSlotIndex].HighlightTimer <= 0.0f)
{ {
item.ParentInventory.slots[availableSlotIndex].ShowBorderHighlight(GUI.Style.Green, 0.5f, 0.5f, 0.2f); item.ParentInventory.visualSlots[availableSlotIndex].ShowBorderHighlight(GUI.Style.Green, 0.5f, 0.5f, 0.2f);
if (slotIndex < inputContainer.Capacity) if (slotIndex < inputContainer.Capacity)
{ {
inputContainer.Inventory.slots[slotIndex].ShowBorderHighlight(GUI.Style.Green, 0.5f, 0.5f, 0.2f); inputContainer.Inventory.visualSlots[slotIndex].ShowBorderHighlight(GUI.Style.Green, 0.5f, 0.5f, 0.2f);
} }
} }
} }
@@ -327,7 +341,7 @@ namespace Barotrauma.Items.Components
if (slotIndex >= inputContainer.Capacity) { break; } if (slotIndex >= inputContainer.Capacity) { break; }
var itemIcon = requiredItem.ItemPrefabs.First().InventoryIcon ?? requiredItem.ItemPrefabs.First().sprite; var itemIcon = requiredItem.ItemPrefabs.First().InventoryIcon ?? requiredItem.ItemPrefabs.First().sprite;
Rectangle slotRect = inputContainer.Inventory.slots[slotIndex].Rect; Rectangle slotRect = inputContainer.Inventory.visualSlots[slotIndex].Rect;
itemIcon.Draw( itemIcon.Draw(
spriteBatch, spriteBatch,
slotRect.Center.ToVector2(), slotRect.Center.ToVector2(),
@@ -367,14 +381,12 @@ namespace Barotrauma.Items.Components
{ {
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)
{ {
var itemIcon = targetItem.TargetItem.InventoryIcon ?? targetItem.TargetItem.sprite; var itemIcon = targetItem.TargetItem.InventoryIcon ?? targetItem.TargetItem.sprite;
Rectangle slotRect = outputContainer.Inventory.slots[0].Rect; Rectangle slotRect = outputContainer.Inventory.visualSlots[0].Rect;
if (fabricatedItem != null) if (fabricatedItem != null)
{ {
@@ -449,8 +461,10 @@ namespace Barotrauma.Items.Components
Color = selectedItem.TargetItem.InventoryIconColor Color = selectedItem.TargetItem.InventoryIconColor
}; };
}*/ }*/
var nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform), var nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform),
selectedItem.TargetItem.Name, textAlignment: Alignment.CenterLeft, textColor: Color.Aqua, font: GUI.SubHeadingFont) GetRecipeNameAndAmount(selectedItem), textAlignment: Alignment.CenterLeft, textColor: Color.Aqua, font: GUI.SubHeadingFont)
{ {
AutoScaleHorizontal = true AutoScaleHorizontal = true
}; };
@@ -539,7 +553,7 @@ namespace Barotrauma.Items.Components
private bool StartButtonClicked(GUIButton button, object obj) private bool StartButtonClicked(GUIButton button, object obj)
{ {
if (selectedItem == null) { return false; } if (selectedItem == null) { return false; }
if (!outputContainer.Inventory.IsEmpty()) if (fabricatedItem == null && !outputContainer.Inventory.CanBePut(selectedItem.TargetItem))
{ {
outputSlot.Flash(GUI.Style.Red); outputSlot.Flash(GUI.Style.Red);
return false; return false;
@@ -71,10 +71,14 @@ namespace Barotrauma.Items.Components
private float showDirectionalIndicatorTimer; private float showDirectionalIndicatorTimer;
private List<LevelObject> nearbyObjects = new List<LevelObject>(); private readonly List<LevelObject> nearbyObjects = new List<LevelObject>();
private const float NearbyObjectUpdateInterval = 1.0f; private const float NearbyObjectUpdateInterval = 1.0f;
float nearbyObjectUpdateTimer; float nearbyObjectUpdateTimer;
private List<Submarine> connectedSubs = new List<Submarine>();
private const float ConnectedSubUpdateInterval = 1.0f;
float connectedSubUpdateTimer;
//Vector2 = vector from the ping source to the position of the disruption //Vector2 = vector from the ping source to the position of the disruption
//float = strength of the disruption, between 0-1 //float = strength of the disruption, between 0-1
private readonly List<Pair<Vector2, float>> disruptedDirections = new List<Pair<Vector2, float>>(); private readonly List<Pair<Vector2, float>> disruptedDirections = new List<Pair<Vector2, float>>();
@@ -135,6 +139,8 @@ namespace Barotrauma.Items.Components
private bool isConnectedToSteering; private bool isConnectedToSteering;
private static string caveLabel;
private bool AllowUsingMineralScanner => private bool AllowUsingMineralScanner =>
HasMineralScanner && !isConnectedToSteering; HasMineralScanner && !isConnectedToSteering;
@@ -143,6 +149,10 @@ namespace Barotrauma.Items.Components
System.Diagnostics.Debug.Assert(Enum.GetValues(typeof(BlipType)).Cast<BlipType>().All(t => blipColorGradient.ContainsKey(t))); System.Diagnostics.Debug.Assert(Enum.GetValues(typeof(BlipType)).Cast<BlipType>().All(t => blipColorGradient.ContainsKey(t)));
sonarBlips = new List<SonarBlip>(); sonarBlips = new List<SonarBlip>();
caveLabel =
TextManager.Get("cave", returnNull: true) ??
TextManager.Get("missiontype.nest");
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
{ {
switch (subElement.Name.ToString().ToLowerInvariant()) switch (subElement.Name.ToString().ToLowerInvariant())
@@ -413,6 +423,26 @@ namespace Barotrauma.Items.Components
networkUpdateTimer -= deltaTime; networkUpdateTimer -= deltaTime;
} }
connectedSubUpdateTimer -= deltaTime;
if (connectedSubUpdateTimer <= 0.0f)
{
connectedSubs.Clear();
if (UseTransducers)
{
foreach (var transducer in connectedTransducers)
{
if (transducer.Transducer.Item.Submarine == null) { continue; }
if (connectedSubs.Contains(transducer.Transducer.Item.Submarine)) { continue; }
connectedSubs = transducer.Transducer.Item.Submarine?.GetConnectedSubs();
}
}
else if (item.Submarine != null)
{
connectedSubs = item.Submarine?.GetConnectedSubs();
}
connectedSubUpdateTimer = ConnectedSubUpdateInterval;
}
if (sonarView.Rect.Contains(PlayerInput.MousePosition)) if (sonarView.Rect.Contains(PlayerInput.MousePosition))
{ {
float scrollSpeed = PlayerInput.ScrollWheelSpeed / 1000.0f; float scrollSpeed = PlayerInput.ScrollWheelSpeed / 1000.0f;
@@ -848,10 +878,22 @@ namespace Barotrauma.Items.Components
displayScale, center, DisplayRadius); displayScale, center, DisplayRadius);
} }
for (int i = 0; i < Level.Loaded.Caves.Count; i++)
{
var cave = Level.Loaded.Caves[i];
if (!cave.DisplayOnSonar) { continue; }
DrawMarker(spriteBatch,
caveLabel,
"cave",
"cave" + i,
cave.StartPos.ToVector2(), transducerCenter,
displayScale, center, DisplayRadius);
}
foreach (AITarget aiTarget in AITarget.List) foreach (AITarget aiTarget in AITarget.List)
{ {
if (!aiTarget.Enabled) continue; if (!aiTarget.Enabled) { continue; }
if (string.IsNullOrEmpty(aiTarget.SonarLabel) || aiTarget.SoundRange <= 0.0f) continue; if (string.IsNullOrEmpty(aiTarget.SonarLabel) || aiTarget.SoundRange <= 0.0f) { continue; }
if (Vector2.DistanceSquared(aiTarget.WorldPosition, transducerCenter) < aiTarget.SoundRange * aiTarget.SoundRange) if (Vector2.DistanceSquared(aiTarget.WorldPosition, transducerCenter) < aiTarget.SoundRange * aiTarget.SoundRange)
{ {
@@ -902,12 +944,7 @@ namespace Barotrauma.Items.Components
foreach (Submarine sub in Submarine.Loaded) foreach (Submarine sub in Submarine.Loaded)
{ {
if (!sub.ShowSonarMarker) { continue; } if (!sub.ShowSonarMarker) { continue; }
if (UseTransducers ? if (connectedSubs.Contains(sub)) { continue; }
connectedTransducers.Any(t => sub == t.Transducer.Item.Submarine || sub.DockedTo.Contains(t.Transducer.Item.Submarine)) :
(sub == item.Submarine || sub.DockedTo.Contains(item.Submarine)))
{
continue;
}
if (sub.WorldPosition.Y > Level.Loaded.Size.Y) { continue; } if (sub.WorldPosition.Y > Level.Loaded.Size.Y) { continue; }
DrawMarker(spriteBatch, DrawMarker(spriteBatch,
@@ -931,10 +968,8 @@ namespace Barotrauma.Items.Components
foreach (Submarine submarine in Submarine.Loaded) foreach (Submarine submarine in Submarine.Loaded)
{ {
if (UseTransducers ? if (!connectedSubs.Contains(submarine)) { continue; }
!connectedTransducers.Any(t => submarine == t.Transducer.Item.Submarine || submarine.DockedTo.Contains(t.Transducer.Item.Submarine)) : if (submarine.HullVertices == null) { continue; }
submarine != item.Submarine && !submarine.DockedTo.Contains(item.Submarine)) continue;
if (submarine.HullVertices == null) continue;
Vector2 offset = ConvertUnits.ToSimUnits(submarine.WorldPosition - transducerCenter); Vector2 offset = ConvertUnits.ToSimUnits(submarine.WorldPosition - transducerCenter);
@@ -1018,8 +1053,8 @@ namespace Barotrauma.Items.Components
//don't show the docking ports of the opposing team on the sonar //don't show the docking ports of the opposing team on the sonar
if (item.Submarine != null) if (item.Submarine != null)
{ {
if ((dockingPort.Item.Submarine.TeamID == Character.TeamType.Team1 && item.Submarine.TeamID == Character.TeamType.Team2) || if ((dockingPort.Item.Submarine.TeamID == CharacterTeamType.Team1 && item.Submarine.TeamID == CharacterTeamType.Team2) ||
(dockingPort.Item.Submarine.TeamID == Character.TeamType.Team2 && item.Submarine.TeamID == Character.TeamType.Team1)) (dockingPort.Item.Submarine.TeamID == CharacterTeamType.Team2 && item.Submarine.TeamID == CharacterTeamType.Team1))
{ {
continue; continue;
} }
@@ -1226,19 +1261,10 @@ namespace Barotrauma.Items.Components
foreach (Submarine submarine in Submarine.Loaded) foreach (Submarine submarine in Submarine.Loaded)
{ {
if (submarine.HullVertices == null) continue; if (submarine.HullVertices == null) { continue; }
if (!DetectSubmarineWalls) if (!DetectSubmarineWalls)
{ {
if (UseTransducers) if (connectedSubs.Contains(submarine)) { continue; }
{
if (connectedTransducers.Any(t => submarine == t.Transducer.Item.Submarine ||
submarine.DockedTo.Contains(t.Transducer.Item.Submarine))) continue;
}
else
{
if (item.Submarine == submarine) continue;
if (item.Submarine != null && item.Submarine.DockedTo.Contains(submarine)) continue;
}
} }
for (int i = 0; i < submarine.HullVertices.Count; i++) for (int i = 0; i < submarine.HullVertices.Count; i++)
@@ -34,7 +34,7 @@ namespace Barotrauma.Items.Components
private GUIComponent steerArea; private GUIComponent steerArea;
private GUITextBlock pressureWarningText; private GUITextBlock pressureWarningText, iceSpireWarningText;
private GUITextBlock tipContainer; private GUITextBlock tipContainer;
@@ -311,6 +311,7 @@ namespace Barotrauma.Items.Components
{ {
Vector2 vel = controlledSub == null ? Vector2.Zero : controlledSub.Velocity; Vector2 vel = controlledSub == null ? Vector2.Zero : controlledSub.Velocity;
var realWorldVel = ConvertUnits.ToDisplayUnits(vel.X * Physics.DisplayToRealWorldRatio) * 3.6f; var realWorldVel = ConvertUnits.ToDisplayUnits(vel.X * Physics.DisplayToRealWorldRatio) * 3.6f;
if (controlledSub != null && controlledSub.FlippedX) { realWorldVel *= -1; }
return ((int)realWorldVel).ToString(); return ((int)realWorldVel).ToString();
}; };
break; break;
@@ -440,8 +441,13 @@ namespace Barotrauma.Items.Components
(spriteBatch, guiCustomComponent) => { DrawHUD(spriteBatch, guiCustomComponent.Rect); }, null); (spriteBatch, guiCustomComponent) => { DrawHUD(spriteBatch, guiCustomComponent.Rect); }, null);
steerRadius = steerArea.Rect.Width / 2; steerRadius = steerArea.Rect.Width / 2;
pressureWarningText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.25f), steerArea.RectTransform, Anchor.Center, Pivot.TopCenter), iceSpireWarningText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.25f), steerArea.RectTransform, Anchor.Center, Pivot.TopCenter),
TextManager.Get("SteeringDepthWarning"), Color.Red, GUI.LargeFont, Alignment.Center) TextManager.Get("NavTerminalIceSpireWarning"), GUI.Style.Red, GUI.SubHeadingFont, Alignment.Center, color: Color.Black * 0.8f, wrap: true)
{
Visible = false
};
pressureWarningText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.25f), steerArea.RectTransform, Anchor.Center, Pivot.TopCenter),
TextManager.Get("SteeringDepthWarning"), GUI.Style.Red, GUI.SubHeadingFont, Alignment.Center, color: Color.Black * 0.8f)
{ {
Visible = false Visible = false
}; };
@@ -471,7 +477,11 @@ namespace Barotrauma.Items.Components
public void AttachToSonarHUD(GUICustomComponent sonarView) public void AttachToSonarHUD(GUICustomComponent sonarView)
{ {
steerArea.Visible = false; steerArea.Visible = false;
sonarView.OnDraw += (spriteBatch, guiCustomComponent) => { DrawHUD(spriteBatch, guiCustomComponent.Rect); }; sonarView.OnDraw += (spriteBatch, guiCustomComponent) =>
{
DrawHUD(spriteBatch, guiCustomComponent.Rect);
steerArea.DrawChildren(spriteBatch, recursive: true);
};
} }
public void DrawHUD(SpriteBatch spriteBatch, Rectangle rect) public void DrawHUD(SpriteBatch spriteBatch, Rectangle rect)
@@ -712,12 +722,13 @@ namespace Barotrauma.Items.Components
} }
} }
pressureWarningText.Visible = item.Submarine != null && item.Submarine.AtDamageDepth && Timing.TotalTime % 1.0f < 0.5f; pressureWarningText.Visible = item.Submarine != null && item.Submarine.AtDamageDepth && Timing.TotalTime % 1.0f < 0.8f;
iceSpireWarningText.Visible = item.Submarine != null && !pressureWarningText.Visible && showIceSpireWarning && Timing.TotalTime % 1.0f < 0.8f;
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 && !GameSession.IsTabMenuOpen && if (PlayerInput.PrimaryMouseButtonHeld() && !CrewManager.IsCommandInterfaceOpen && !GameSession.IsTabMenuOpen &&
(!GameMain.GameSession?.Campaign?.ShowCampaignUI ?? true) && !GUIMessageBox.MessageBoxes.Any()) (!GameMain.GameSession?.Campaign?.ShowCampaignUI ?? true) && !GUIMessageBox.MessageBoxes.Any(msgBox => msgBox is GUIMessageBox { MessageBoxType: GUIMessageBox.Type.Default }))
{ {
Vector2 inputPos = PlayerInput.MousePosition - steerArea.Rect.Center.ToVector2(); Vector2 inputPos = PlayerInput.MousePosition - steerArea.Rect.Center.ToVector2();
inputPos.Y = -inputPos.Y; inputPos.Y = -inputPos.Y;
@@ -113,25 +113,28 @@ namespace Barotrauma.Items.Components
} }
} }
partial void FixItemProjSpecific(Character user, float deltaTime, Item targetItem) partial void FixItemProjSpecific(Character user, float deltaTime, Item targetItem, bool showProgressBar)
{ {
float progressBarState = targetItem.ConditionPercentage / 100.0f; if (showProgressBar)
if (!MathUtils.NearlyEqual(progressBarState, prevProgressBarState) || prevProgressBarTarget != targetItem)
{ {
var door = targetItem.GetComponent<Door>(); float progressBarState = targetItem.ConditionPercentage / 100.0f;
if (door == null || door.Stuck <= 0) if (!MathUtils.NearlyEqual(progressBarState, prevProgressBarState) || prevProgressBarTarget != targetItem)
{ {
Vector2 progressBarPos = targetItem.DrawPosition; var door = targetItem.GetComponent<Door>();
var progressBar = user?.UpdateHUDProgressBar( if (door == null || door.Stuck <= 0)
targetItem, {
progressBarPos, Vector2 progressBarPos = targetItem.DrawPosition;
progressBarState, var progressBar = user?.UpdateHUDProgressBar(
GUI.Style.Red, GUI.Style.Green, targetItem,
progressBarState < prevProgressBarState ? "progressbar.cutting" : ""); progressBarPos,
if (progressBar != null) { progressBar.Size = new Vector2(60.0f, 20.0f); } progressBarState,
GUI.Style.Red, GUI.Style.Green,
progressBarState < prevProgressBarState ? "progressbar.cutting" : "");
if (progressBar != null) { progressBar.Size = new Vector2(60.0f, 20.0f); }
}
prevProgressBarState = progressBarState;
prevProgressBarTarget = targetItem;
} }
prevProgressBarState = progressBarState;
prevProgressBarTarget = targetItem;
} }
Vector2 particlePos = ConvertUnits.ToDisplayUnits(pickedPosition); Vector2 particlePos = ConvertUnits.ToDisplayUnits(pickedPosition);
@@ -53,13 +53,9 @@ namespace Barotrauma.Items.Components
{ {
//if the Character using the panel has a wire item equipped //if the Character using the panel has a wire item equipped
//and the wire hasn't been connected yet, draw it on the panel //and the wire hasn't been connected yet, draw it on the panel
for (int i = 0; i < character.SelectedItems.Length; i++) foreach (Item item in character.HeldItems)
{ {
Item selectedItem = character.SelectedItems[i]; Wire wireComponent = item.GetComponent<Wire>();
if (selectedItem == null) { continue; }
Wire wireComponent = selectedItem.GetComponent<Wire>();
if (wireComponent != null) if (wireComponent != null)
{ {
equippedWire = wireComponent; equippedWire = wireComponent;
@@ -94,7 +90,8 @@ namespace Barotrauma.Items.Components
int linkIndex = c.FindWireIndex(DraggingConnected.Item); int linkIndex = c.FindWireIndex(DraggingConnected.Item);
if (linkIndex > -1 || panel.DisconnectedWires.Contains(DraggingConnected)) if (linkIndex > -1 || panel.DisconnectedWires.Contains(DraggingConnected))
{ {
Inventory.draggingItem = DraggingConnected.Item; Inventory.DraggingItems.Clear();
Inventory.DraggingItems.Add(DraggingConnected.Item);
} }
} }
} }
@@ -182,7 +179,11 @@ namespace Barotrauma.Items.Components
new Vector2(x + width / 2, y + height), new Vector2(x + width / 2, y + height),
null, panel, ""); null, panel, "");
if (DraggingConnected == equippedWire) { Inventory.draggingItem = equippedWire.Item; } if (DraggingConnected == equippedWire)
{
Inventory.DraggingItems.Clear();
Inventory.DraggingItems.Add(equippedWire.Item);
}
} }
} }
@@ -207,7 +208,7 @@ namespace Barotrauma.Items.Components
//(so we don't drop the item when dropping the wire on a connection) //(so we don't drop the item when dropping the wire on a connection)
if (mouseInRect || (GUI.MouseOn?.UserData is ConnectionPanel && GUI.MouseOn.MouseRect.Contains(PlayerInput.MousePosition))) if (mouseInRect || (GUI.MouseOn?.UserData is ConnectionPanel && GUI.MouseOn.MouseRect.Contains(PlayerInput.MousePosition)))
{ {
Inventory.draggingItem = null; Inventory.DraggingItems.Clear();
} }
} }
@@ -236,7 +237,7 @@ namespace Barotrauma.Items.Components
{ {
float connectorSpriteScale = (35.0f / connectionSprite.SourceRect.Width) * panel.Scale; float connectorSpriteScale = (35.0f / connectionSprite.SourceRect.Width) * panel.Scale;
for (int i = 0; i < MaxLinked; i++) for (int i = 0; i < MaxWires; i++)
{ {
if (wires[i] == null || wires[i].Hidden || (DraggingConnected == wires[i] && (mouseIn || Screen.Selected == GameMain.SubEditorScreen))) { continue; } if (wires[i] == null || wires[i].Hidden || (DraggingConnected == wires[i] && (mouseIn || Screen.Selected == GameMain.SubEditorScreen))) { continue; }
if (wires[i].HiddenInGame && Screen.Selected == GameMain.GameScreen) { continue; } if (wires[i].HiddenInGame && Screen.Selected == GameMain.GameScreen) { continue; }
@@ -122,7 +122,7 @@ namespace Barotrauma.Items.Components
msg.ReadUInt16(); //user ID msg.ReadUInt16(); //user ID
foreach (Connection connection in Connections) foreach (Connection connection in Connections)
{ {
for (int i = 0; i < Connection.MaxLinked; i++) for (int i = 0; i < connection.MaxWires; i++)
{ {
msg.ReadUInt16(); msg.ReadUInt16();
} }
@@ -168,7 +168,7 @@ namespace Barotrauma.Items.Components
foreach (Connection connection in Connections) foreach (Connection connection in Connections)
{ {
for (int i = 0; i < Connection.MaxLinked; i++) for (int i = 0; i < connection.MaxWires; i++)
{ {
ushort wireId = msg.ReadUInt16(); ushort wireId = msg.ReadUInt16();
@@ -4,7 +4,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Items.Components namespace Barotrauma.Items.Components
{ {
@@ -15,7 +14,7 @@ namespace Barotrauma.Items.Components
private Point ElementMaxSize => new Point(uiElementContainer.Rect.Width, (int)(65 * GUI.yScale)); private Point ElementMaxSize => new Point(uiElementContainer.Rect.Width, (int)(65 * GUI.yScale));
partial void InitProjSpecific(XElement element) partial void InitProjSpecific()
{ {
CreateGUI(); CreateGUI();
} }
@@ -37,41 +36,70 @@ 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 (!string.IsNullOrEmpty(ciElement.PropertyName)) if (ciElement.HasPropertyName)
{ {
var layoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, elementSize), uiElementContainer.RectTransform), isHorizontal: true) 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, RelativeSpacing = 0.02f,
UserData = ciElement UserData = ciElement
}; };
//reset size restrictions set by the Style to make sure the elements can fit the interface new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), layoutGroup.RectTransform),
textBox.RectTransform.MinSize = textBox.Frame.RectTransform.MinSize = new Point(0, 0); TextManager.Get(ciElement.Label, returnNull: true) ?? ciElement.Label);
textBox.RectTransform.MaxSize = textBox.Frame.RectTransform.MaxSize = new Point(int.MaxValue, int.MaxValue); if (!ciElement.IsIntegerInput)
textBox.OnDeselected += (tb, key) =>
{ {
if (GameMain.Client == null) var textBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), layoutGroup.RectTransform), ciElement.Signal, style: "GUITextBoxNoIcon")
{ {
TextChanged(tb.UserData as CustomInterfaceElement, textBox.Text); OverflowClip = true,
} UserData = ciElement
else };
//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) =>
{ {
item.CreateClientEvent(this); if (GameMain.Client == null)
} {
}; TextChanged(tb.UserData as CustomInterfaceElement, textBox.Text);
}
else
{
item.CreateClientEvent(this);
}
};
textBox.OnEnterPressed += (tb, text) => textBox.OnEnterPressed += (tb, text) =>
{
tb.Deselect();
return true;
};
uiElements.Add(textBox);
}
else
{ {
tb.Deselect(); int.TryParse(ciElement.Signal, out int signal);
return true; var numberInput = new GUINumberInput(new RectTransform(new Vector2(0.5f, 1.0f), layoutGroup.RectTransform), GUINumberInput.NumberType.Int)
}; {
uiElements.Add(textBox); UserData = ciElement,
MinValueInt = ciElement.NumberInputMin,
MaxValueInt = ciElement.NumberInputMax,
IntValue = Math.Clamp(signal, ciElement.NumberInputMin, ciElement.NumberInputMax)
};
//reset size restrictions set by the Style to make sure the elements can fit the interface
numberInput.RectTransform.MinSize = numberInput.LayoutGroup.RectTransform.MinSize = new Point(0, 0);
numberInput.RectTransform.MaxSize = numberInput.LayoutGroup.RectTransform.MaxSize = new Point(int.MaxValue, int.MaxValue);
numberInput.OnValueChanged += (ni) =>
{
if (GameMain.Client == null)
{
ValueChanged(ni.UserData as CustomInterfaceElement, ni.IntValue);
}
else
{
item.CreateClientEvent(this);
}
};
uiElements.Add(numberInput);
}
} }
else if (ciElement.ContinuousSignal) else if (ciElement.ContinuousSignal)
{ {
@@ -175,7 +203,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() || !string.IsNullOrEmpty(element.PropertyName) || (element.Connection != null && element.Connection.Wires.Any(w => w != null)); bool visible = Screen.Selected == GameMain.SubEditorScreen || element.StatusEffects.Any() || element.HasPropertyName || (element.Connection != null && element.Connection.Wires.Any(w => w != null));
if (visible) { visibleElementCount++; } if (visible) { visibleElementCount++; }
if (uiElement.Visible != visible) if (uiElement.Visible != visible)
{ {
@@ -203,36 +231,29 @@ namespace Barotrauma.Items.Components
{ {
if (uiElements[i] is GUIButton button) if (uiElements[i] is GUIButton button)
{ {
button.Text = string.IsNullOrWhiteSpace(customInterfaceElementList[i].Label) ? button.Text = CreateLabelText(i);
TextManager.GetWithVariable("connection.signaloutx", "[num]", (i + 1).ToString()) :
customInterfaceElementList[i].Label;
button.TextBlock.Wrap = button.Text.Contains(' '); button.TextBlock.Wrap = button.Text.Contains(' ');
} }
else if (uiElements[i] is GUITickBox tickBox) else if (uiElements[i] is GUITickBox tickBox)
{ {
tickBox.Text = string.IsNullOrWhiteSpace(customInterfaceElementList[i].Label) ? tickBox.Text = CreateLabelText(i);
TextManager.GetWithVariable("connection.signaloutx", "[num]", (i + 1).ToString()) :
customInterfaceElementList[i].Label;
tickBox.TextBlock.Wrap = tickBox.Text.Contains(' '); tickBox.TextBlock.Wrap = tickBox.Text.Contains(' ');
} }
if (uiElements[i] is GUITextBox textBox) else if (uiElements[i] is GUITextBox || uiElements[i] is GUINumberInput)
{ {
var textBlock = textBox.Parent.GetChild<GUITextBlock>(); var textBlock = uiElements[i].Parent.GetChild<GUITextBlock>();
textBlock.Text = string.IsNullOrWhiteSpace(customInterfaceElementList[i].Label) ? textBlock.Text = CreateLabelText(i);
TextManager.GetWithVariable("connection.signaloutx", "[num]", (i + 1).ToString()) :
customInterfaceElementList[i].Label;
textBlock.Wrap = textBlock.Text.Contains(' '); 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;
}
}
} }
} }
string CreateLabelText(int elementIndex)
{
return string.IsNullOrWhiteSpace(customInterfaceElementList[elementIndex].Label) ?
TextManager.GetWithVariable("connection.signaloutx", "[num]", (elementIndex + 1).ToString()) :
customInterfaceElementList[elementIndex].Label;
}
uiElementContainer.Recalculate(); uiElementContainer.Recalculate();
var textBlocks = new List<GUITextBlock>(); var textBlocks = new List<GUITextBlock>();
foreach (GUIComponent element in uiElementContainer.Children) foreach (GUIComponent element in uiElementContainer.Children)
@@ -258,14 +279,40 @@ namespace Barotrauma.Items.Components
GUITextBlock.AutoScaleAndNormalize(textBlocks); GUITextBlock.AutoScaleAndNormalize(textBlocks);
} }
partial void UpdateSignalsProjSpecific()
{
for (int i = 0; i < signals.Length && i < uiElements.Count; i++)
{
if (uiElements[i] is GUITextBox tb)
{
tb.Text = customInterfaceElementList[i].Signal;
}
else if (uiElements[i] is GUINumberInput ni)
{
if (ni.InputType == GUINumberInput.NumberType.Int)
{
int.TryParse(customInterfaceElementList[i].Signal, out int value);
ni.IntValue = value;
}
}
}
}
public void ClientWrite(IWriteMessage msg, object[] extraData = null) public void ClientWrite(IWriteMessage msg, object[] extraData = null)
{ {
//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 (!string.IsNullOrEmpty(customInterfaceElementList[i].PropertyName)) if (customInterfaceElementList[i].HasPropertyName)
{ {
msg.Write(((GUITextBox)uiElements[i]).Text); if (!customInterfaceElementList[i].IsIntegerInput)
{
msg.Write(((GUITextBox)uiElements[i]).Text);
}
else
{
msg.Write(((GUINumberInput)uiElements[i]).IntValue.ToString());
}
} }
else if (customInterfaceElementList[i].ContinuousSignal) else if (customInterfaceElementList[i].ContinuousSignal)
{ {
@@ -282,9 +329,17 @@ namespace Barotrauma.Items.Components
{ {
for (int i = 0; i < customInterfaceElementList.Count; i++) for (int i = 0; i < customInterfaceElementList.Count; i++)
{ {
if (!string.IsNullOrEmpty(customInterfaceElementList[i].PropertyName)) if (customInterfaceElementList[i].HasPropertyName)
{ {
TextChanged(customInterfaceElementList[i], msg.ReadString()); if (!customInterfaceElementList[i].IsIntegerInput)
{
TextChanged(customInterfaceElementList[i], msg.ReadString());
}
else
{
int.TryParse(msg.ReadString(), out int value);
ValueChanged(customInterfaceElementList[i], value);
}
} }
else else
{ {
@@ -300,6 +355,8 @@ namespace Barotrauma.Items.Components
} }
} }
} }
UpdateSignalsProjSpecific();
} }
} }
} }
@@ -214,7 +214,10 @@ namespace Barotrauma.Items.Components
roundedGridPos += item.Submarine.Position; roundedGridPos += item.Submarine.Position;
} }
Submarine.DrawGrid(spriteBatch, 14, gridPos, roundedGridPos, alpha: 0.7f); if (!SubEditorScreen.IsSubEditor() || !SubEditorScreen.ShouldDrawGrid)
{
Submarine.DrawGrid(spriteBatch, 14, gridPos, roundedGridPos, alpha: 0.25f);
}
WireSection.Draw( WireSection.Draw(
spriteBatch, this, spriteBatch, this,
@@ -287,9 +290,7 @@ namespace Barotrauma.Items.Components
{ {
var doubleClicked = PlayerInput.DoubleClicked(); var doubleClicked = PlayerInput.DoubleClicked();
Wire equippedWire = Wire equippedWire = Character.Controlled.HeldItems.FirstOrDefault(it => it.GetComponent<Wire>() != null)?.GetComponent<Wire>();
Character.Controlled?.SelectedItems[0]?.GetComponent<Wire>() ??
Character.Controlled?.SelectedItems[1]?.GetComponent<Wire>();
if (equippedWire != null && GUI.MouseOn == null) if (equippedWire != null && GUI.MouseOn == null)
{ {
if (PlayerInput.PrimaryMouseButtonClicked() && Character.Controlled.SelectedConstruction == null) if (PlayerInput.PrimaryMouseButtonClicked() && Character.Controlled.SelectedConstruction == null)
@@ -329,6 +330,9 @@ namespace Barotrauma.Items.Components
nodeWorldPos = nodeWorldPos - sub.HiddenSubPosition - sub.Position; nodeWorldPos = nodeWorldPos - sub.HiddenSubPosition - sub.Position;
} }
if (selectedNodeIndex.HasValue && selectedNodeIndex.Value >= draggingWire.nodes.Count) { selectedNodeIndex = null; }
if (highlightedNodeIndex.HasValue && highlightedNodeIndex.Value >= draggingWire.nodes.Count) { highlightedNodeIndex = null; }
if (selectedNodeIndex.HasValue) if (selectedNodeIndex.HasValue)
{ {
if (!PlayerInput.IsShiftDown()) if (!PlayerInput.IsShiftDown())
@@ -342,14 +346,15 @@ namespace Barotrauma.Items.Components
} }
else else
{ {
if ((highlightedNodeIndex.HasValue && Vector2.DistanceSquared(nodeWorldPos, draggingWire.nodes[(int)highlightedNodeIndex]) > Submarine.GridSize.X * Submarine.GridSize.X) || float dragDistance = Submarine.GridSize.X * Submarine.GridSize.Y;
dragDistance *= 0.5f;
if ((highlightedNodeIndex.HasValue && Vector2.DistanceSquared(nodeWorldPos, draggingWire.nodes[(int)highlightedNodeIndex]) >= dragDistance) ||
PlayerInput.IsShiftDown()) PlayerInput.IsShiftDown())
{ {
selectedNodeIndex = highlightedNodeIndex; selectedNodeIndex = highlightedNodeIndex;
} }
} }
MapEntity.SelectEntity(draggingWire.item); MapEntity.SelectEntity(draggingWire.item);
} }
@@ -396,6 +401,13 @@ namespace Barotrauma.Items.Components
if (closestIndex > -1) if (closestIndex > -1)
{ {
highlightedNodeIndex = closestIndex; highlightedNodeIndex = closestIndex;
Vector2 nudge = MapEntity.GetNudgeAmount(doHold: false);
if (nudge != Vector2.Zero && closestIndex < selectedWire.nodes.Count)
{
selectedWire.MoveNode(closestIndex, nudge);
}
//start dragging the node //start dragging the node
if (PlayerInput.PrimaryMouseButtonHeld()) if (PlayerInput.PrimaryMouseButtonHeld())
{ {
@@ -148,20 +148,20 @@ namespace Barotrauma.Items.Components
List<string> texts = new List<string>(); List<string> texts = new List<string>();
List<Color> textColors = new List<Color>(); List<Color> textColors = new List<Color>();
if (target.Info != null) texts.Add(target.Info == null ? target.DisplayName : target.Info.DisplayName);
{ textColors.Add(GUI.Style.TextColor);
texts.Add(target.Name);
textColors.Add(GUI.Style.TextColor);
}
if (target.IsDead) if (target.IsDead)
{ {
texts.Add(TextManager.Get("Deceased")); texts.Add(TextManager.Get("Deceased"));
textColors.Add(GUI.Style.Red); textColors.Add(GUI.Style.Red);
texts.Add( if (target.CauseOfDeath != null)
target.CauseOfDeath.Affliction?.CauseOfDeathDescription ?? {
TextManager.AddPunctuation(':', TextManager.Get("CauseOfDeath"), TextManager.Get("CauseOfDeath." + target.CauseOfDeath.Type.ToString()))); texts.Add(
textColors.Add(GUI.Style.Red); target.CauseOfDeath.Affliction?.CauseOfDeathDescription ??
TextManager.AddPunctuation(':', TextManager.Get("CauseOfDeath"), TextManager.Get("CauseOfDeath." + target.CauseOfDeath.Type.ToString())));
textColors.Add(GUI.Style.Red);
}
} }
else else
{ {
@@ -170,6 +170,21 @@ namespace Barotrauma.Items.Components
texts.Add(target.customInteractHUDText); texts.Add(target.customInteractHUDText);
textColors.Add(GUI.Style.Green); textColors.Add(GUI.Style.Green);
} }
if (!target.IsIncapacitated && target.IsPet)
{
texts.Add(CharacterHUD.GetCachedHudText("PlayHint", GameMain.Config.KeyBindText(InputType.Use)));
textColors.Add(GUI.Style.Green);
}
if (target.CharacterHealth.UseHealthWindow && equipper?.FocusedCharacter == target && equipper.CanInteractWith(target, 160f, false))
{
texts.Add(CharacterHUD.GetCachedHudText("HealHint", GameMain.Config.KeyBindText(InputType.Health)));
textColors.Add(GUI.Style.Green);
}
if (target.CanBeDragged)
{
texts.Add(CharacterHUD.GetCachedHudText("GrabHint", GameMain.Config.KeyBindText(InputType.Grab)));
textColors.Add(GUI.Style.Green);
}
if (target.IsUnconscious) if (target.IsUnconscious)
{ {
@@ -181,14 +181,14 @@ namespace Barotrauma.Items.Components
{ {
if (moveSoundChannel == null && startMoveSound != null) if (moveSoundChannel == null && startMoveSound != null)
{ {
moveSoundChannel = SoundPlayer.PlaySound(startMoveSound.Sound, item.WorldPosition, startMoveSound.Volume, startMoveSound.Range); moveSoundChannel = SoundPlayer.PlaySound(startMoveSound.Sound, item.WorldPosition, startMoveSound.Volume, startMoveSound.Range, ignoreMuffling: startMoveSound.IgnoreMuffling);
} }
else if (moveSoundChannel == null || !moveSoundChannel.IsPlaying) else if (moveSoundChannel == null || !moveSoundChannel.IsPlaying)
{ {
if (moveSound != null) if (moveSound != null)
{ {
moveSoundChannel.FadeOutAndDispose(); moveSoundChannel.FadeOutAndDispose();
moveSoundChannel = SoundPlayer.PlaySound(moveSound.Sound, item.WorldPosition, moveSound.Volume, moveSound.Range); moveSoundChannel = SoundPlayer.PlaySound(moveSound.Sound, item.WorldPosition, moveSound.Volume, moveSound.Range, ignoreMuffling: moveSound.IgnoreMuffling);
if (moveSoundChannel != null) moveSoundChannel.Looping = true; if (moveSoundChannel != null) moveSoundChannel.Looping = true;
} }
} }
@@ -200,7 +200,7 @@ namespace Barotrauma.Items.Components
if (endMoveSound != null && moveSoundChannel.Sound != endMoveSound.Sound) if (endMoveSound != null && moveSoundChannel.Sound != endMoveSound.Sound)
{ {
moveSoundChannel.FadeOutAndDispose(); moveSoundChannel.FadeOutAndDispose();
moveSoundChannel = SoundPlayer.PlaySound(endMoveSound.Sound, item.WorldPosition, endMoveSound.Volume, endMoveSound.Range); moveSoundChannel = SoundPlayer.PlaySound(endMoveSound.Sound, item.WorldPosition, endMoveSound.Volume, endMoveSound.Range, ignoreMuffling: endMoveSound.IgnoreMuffling);
if (moveSoundChannel != null) moveSoundChannel.Looping = false; if (moveSoundChannel != null) moveSoundChannel.Looping = false;
} }
else if (!moveSoundChannel.IsPlaying) else if (!moveSoundChannel.IsPlaying)
@@ -286,40 +286,26 @@ namespace Barotrauma.Items.Components
rotation + MathHelper.PiOver2, item.Scale, rotation + MathHelper.PiOver2, item.Scale,
SpriteEffects.None, item.SpriteDepth + (barrelSprite.Depth - item.Sprite.Depth)); SpriteEffects.None, item.SpriteDepth + (barrelSprite.Depth - item.Sprite.Depth));
if (!GameMain.DebugDraw && (!editing || GUI.DisableHUD || !item.IsSelected)) { return; } if (!editing || GUI.DisableHUD || !item.IsSelected) { return; }
const float widgetRadius = 60.0f; const float widgetRadius = 60.0f;
Vector2 center = new Vector2((float)Math.Cos((maxRotation + minRotation) / 2), (float)Math.Sin((maxRotation + minRotation) / 2)); Vector2 center = new Vector2((float)Math.Cos((maxRotation + minRotation) / 2), (float)Math.Sin((maxRotation + minRotation) / 2));
GUI.DrawLine(spriteBatch, GUI.DrawLine(spriteBatch,
drawPos, drawPos,
drawPos + new Vector2((float)Math.Cos((maxRotation + minRotation) / 2), (float)Math.Sin((maxRotation + minRotation) / 2)) * widgetRadius, drawPos + center * widgetRadius,
Color.LightGreen); Color.LightGreen);
if (GameMain.DebugDraw)
{
center = new Vector2((float)Math.Cos(targetRotation), (float)Math.Sin(targetRotation));
GUI.DrawLine(spriteBatch,
drawPos,
drawPos + center * widgetRadius,
Color.Red);
for (int i = 0; i < 5; i++)
{
center = new Vector2((float)Math.Cos(rotation + (angularVelocity * 0.05f * i)), (float)Math.Sin(rotation + (angularVelocity * 0.05f * i)));
GUI.DrawLine(spriteBatch,
drawPos,
drawPos + center * widgetRadius,
Color.Lerp(Color.Black, Color.Yellow, i * 0.25f));
}
}
const float coneRadius = 300.0f; const float coneRadius = 300.0f;
float radians = maxRotation - minRotation; float radians = maxRotation - minRotation;
float circleRadius = coneRadius / Screen.Selected.Cam.Zoom * GUI.Scale; float circleRadius = coneRadius / Screen.Selected.Cam.Zoom * GUI.Scale;
float lineThickness = 1f / Screen.Selected.Cam.Zoom; float lineThickness = 1f / Screen.Selected.Cam.Zoom;
if (radians > Math.PI * 2) if (Math.Abs(minRotation - maxRotation) < 0.02f)
{
spriteBatch.DrawLine(drawPos, drawPos + center * circleRadius, GUI.Style.Green, thickness: lineThickness);
}
else if (radians > Math.PI * 2)
{ {
spriteBatch.DrawCircle(drawPos, circleRadius, 180, GUI.Style.Red, thickness: lineThickness); spriteBatch.DrawCircle(drawPos, circleRadius, 180, GUI.Style.Red, thickness: lineThickness);
} }
@@ -510,13 +496,8 @@ namespace Barotrauma.Items.Components
List<Item> availableAmmo = new List<Item>(); List<Item> availableAmmo = new List<Item>();
foreach (MapEntity e in item.linkedTo) foreach (MapEntity e in item.linkedTo)
{ {
var linkedItem = e as Item; if (!(e is Item linkedItem)) { continue; }
if (linkedItem == null) continue; availableAmmo.AddRange(linkedItem.ContainedItems);
var itemContainer = linkedItem.GetComponent<ItemContainer>();
if (itemContainer?.Inventory?.Items == null) continue;
availableAmmo.AddRange(itemContainer.Inventory.Items);
} }
float chargeRate = float chargeRate =
@@ -558,7 +539,7 @@ namespace Barotrauma.Items.Components
{ {
// TODO: Optimize? Creates multiple new objects per frame? // TODO: Optimize? Creates multiple new objects per frame?
Inventory.DrawSlot(spriteBatch, null, Inventory.DrawSlot(spriteBatch, null,
new InventorySlot(new Rectangle(invSlotPos + new Point((i % slotsPerRow) * (slotSize.X + spacing), (int)Math.Floor(i / (float)slotsPerRow) * (slotSize.Y + spacing)), slotSize)), new VisualSlot(new Rectangle(invSlotPos + new Point((i % slotsPerRow) * (slotSize.X + spacing), (int)Math.Floor(i / (float)slotsPerRow) * (slotSize.Y + spacing)), slotSize)),
availableAmmo[i], -1, true); availableAmmo[i], -1, true);
} }
if (flashNoAmmo) if (flashNoAmmo)
File diff suppressed because it is too large Load Diff
@@ -8,6 +8,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Barotrauma.Extensions; using Barotrauma.Extensions;
using Barotrauma.MapCreatures.Behavior;
using FarseerPhysics.Dynamics; using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Contacts; using FarseerPhysics.Dynamics.Contacts;
@@ -15,7 +16,7 @@ namespace Barotrauma
{ {
partial class Item : MapEntity, IDamageable, ISerializableEntity, IServerSerializable, IClientSerializable partial class Item : MapEntity, IDamageable, ISerializableEntity, IServerSerializable, IClientSerializable
{ {
public static bool ShowItems = true; public static bool ShowItems = true, ShowWires = true;
private readonly List<PosInfo> positionBuffer = new List<PosInfo>(); private readonly List<PosInfo> positionBuffer = new List<PosInfo>();
@@ -29,6 +30,7 @@ namespace Barotrauma
private bool editingHUDRefreshPending; private bool editingHUDRefreshPending;
private float editingHUDRefreshTimer; private float editingHUDRefreshTimer;
private ContainedItemSprite activeContainedSprite;
private readonly Dictionary<DecorativeSprite, DecorativeSprite.State> spriteAnimState = new Dictionary<DecorativeSprite, DecorativeSprite.State>(); private readonly Dictionary<DecorativeSprite, DecorativeSprite.State> spriteAnimState = new Dictionary<DecorativeSprite, DecorativeSprite.State>();
@@ -103,13 +105,10 @@ namespace Barotrauma
Color color = spriteColor; Color color = spriteColor;
if (Prefab.UseContainedSpriteColor && ownInventory != null) if (Prefab.UseContainedSpriteColor && ownInventory != null)
{ {
for (int i = 0; i < ownInventory.Items.Length; i++) foreach (Item item in ContainedItems)
{ {
if (ownInventory.Items[i] != null) color = item.ContainerColor;
{ break;
color = ownInventory.Items[i].ContainerColor;
break;
}
} }
} }
return color; return color;
@@ -120,13 +119,10 @@ namespace Barotrauma
Color color = InventoryIconColor; Color color = InventoryIconColor;
if (Prefab.UseContainedInventoryIconColor && ownInventory != null) if (Prefab.UseContainedInventoryIconColor && ownInventory != null)
{ {
for (int i = 0; i < ownInventory.Items.Length; i++) foreach (Item item in ContainedItems)
{ {
if (ownInventory.Items[i] != null) color = item.ContainerColor;
{ break;
color = ownInventory.Items[i].ContainerColor;
break;
}
} }
} }
return color; return color;
@@ -135,6 +131,7 @@ namespace Barotrauma
partial void SetActiveSpriteProjSpecific() partial void SetActiveSpriteProjSpecific()
{ {
activeSprite = prefab.sprite; activeSprite = prefab.sprite;
activeContainedSprite = null;
Holdable holdable = GetComponent<Holdable>(); Holdable holdable = GetComponent<Holdable>();
if (holdable != null && holdable.Attached) if (holdable != null && holdable.Attached)
{ {
@@ -142,7 +139,9 @@ namespace Barotrauma
{ {
if (containedSprite.UseWhenAttached) if (containedSprite.UseWhenAttached)
{ {
activeContainedSprite = containedSprite;
activeSprite = containedSprite.Sprite; activeSprite = containedSprite.Sprite;
UpdateSpriteStates(0.0f);
return; return;
} }
} }
@@ -154,7 +153,9 @@ namespace Barotrauma
{ {
if (containedSprite.MatchesContainer(Container)) if (containedSprite.MatchesContainer(Container))
{ {
activeContainedSprite = containedSprite;
activeSprite = containedSprite.Sprite; activeSprite = containedSprite.Sprite;
UpdateSpriteStates(0.0f);
return; return;
} }
} }
@@ -187,6 +188,7 @@ namespace Barotrauma
decorativeSprite.Sprite.EnsureLazyLoaded(); decorativeSprite.Sprite.EnsureLazyLoaded();
spriteAnimState.Add(decorativeSprite, new DecorativeSprite.State()); spriteAnimState.Add(decorativeSprite, new DecorativeSprite.State());
} }
SetActiveSprite();
UpdateSpriteStates(0.0f); UpdateSpriteStates(0.0f);
} }
@@ -240,7 +242,15 @@ 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)
{
if (isWire)
{
if (!ShowWires) { return; }
}
else if (!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);
@@ -305,7 +315,7 @@ namespace Barotrauma
foreach (var decorativeSprite in Prefab.DecorativeSprites) foreach (var decorativeSprite in Prefab.DecorativeSprites)
{ {
if (!spriteAnimState[decorativeSprite].IsActive) { continue; } if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, -rotationRad) * Scale; Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier, -rotationRad) * Scale;
if (flippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; } if (flippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; }
if (flippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; } if (flippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; }
decorativeSprite.Sprite.DrawTiled(spriteBatch, decorativeSprite.Sprite.DrawTiled(spriteBatch,
@@ -328,7 +338,7 @@ namespace Barotrauma
} }
activeSprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, color, origin, rotationRad, Scale, activeSprite.effects, depth); activeSprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, color, origin, rotationRad, Scale, activeSprite.effects, depth);
fadeInBrokenSprite?.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + fadeInBrokenSprite.Offset.ToVector2() * Scale, color * fadeInBrokenSpriteAlpha, origin, rotationRad, Scale, activeSprite.effects, depth - 0.000001f); fadeInBrokenSprite?.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + fadeInBrokenSprite.Offset.ToVector2() * Scale, color * fadeInBrokenSpriteAlpha, origin, rotationRad, Scale, activeSprite.effects, depth - 0.000001f);
if (Infector != null && Infector.ParentBallastFlora.HasBrokenThrough) if (Infector != null && (Infector.ParentBallastFlora.HasBrokenThrough || BallastFloraBehavior.AlwaysShowBallastFloraSprite))
{ {
Prefab.InfectedSprite?.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, color, Prefab.InfectedSprite.Origin, rotationRad, Scale, activeSprite.effects, depth - 0.001f); Prefab.InfectedSprite?.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, color, Prefab.InfectedSprite.Origin, rotationRad, Scale, activeSprite.effects, depth - 0.001f);
Prefab.DamagedInfectedSprite?.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, Infector.HealthColor, Prefab.DamagedInfectedSprite.Origin, rotationRad, Scale, activeSprite.effects, depth - 0.002f); Prefab.DamagedInfectedSprite?.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, Infector.HealthColor, Prefab.DamagedInfectedSprite.Origin, rotationRad, Scale, activeSprite.effects, depth - 0.002f);
@@ -336,12 +346,12 @@ namespace Barotrauma
foreach (var decorativeSprite in Prefab.DecorativeSprites) foreach (var decorativeSprite in Prefab.DecorativeSprites)
{ {
if (!spriteAnimState[decorativeSprite].IsActive) { continue; } if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
float rot = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState); float rot = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState, spriteAnimState[decorativeSprite].RandomRotationFactor);
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, -rotationRad) * Scale; Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier, -rotationRad) * Scale;
if (flippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; } if (flippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; }
if (flippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; } if (flippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; }
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + offset.X, -(DrawPosition.Y + offset.Y)), color, decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + offset.X, -(DrawPosition.Y + offset.Y)), color,
rotationRad + rot, decorativeSprite.Scale * Scale, activeSprite.effects, rotationRad + rot, decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, activeSprite.effects,
depth: Math.Min(depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth), 0.999f)); depth: Math.Min(depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth), 0.999f));
} }
} }
@@ -353,7 +363,7 @@ namespace Barotrauma
{ {
if (!back) { return; } if (!back) { return; }
float depthStep = 0.000001f; float depthStep = 0.000001f;
if (holdable.Picker.SelectedItems[0] == this) if (holdable.Picker.Inventory?.GetItemInLimbSlot(InvSlotType.RightHand) == this)
{ {
Limb holdLimb = holdable.Picker.AnimController.GetLimb(LimbType.RightHand); Limb holdLimb = holdable.Picker.AnimController.GetLimb(LimbType.RightHand);
if (holdLimb?.ActiveSprite != null) if (holdLimb?.ActiveSprite != null)
@@ -365,7 +375,7 @@ namespace Barotrauma
} }
} }
} }
else if (holdable.Picker.SelectedItems[1] == this) else if (holdable.Picker.Inventory?.GetItemInLimbSlot(InvSlotType.LeftHand) == this)
{ {
Limb holdLimb = holdable.Picker.AnimController.GetLimb(LimbType.LeftHand); Limb holdLimb = holdable.Picker.AnimController.GetLimb(LimbType.LeftHand);
if (holdLimb?.ActiveSprite != null) if (holdLimb?.ActiveSprite != null)
@@ -384,8 +394,8 @@ namespace Barotrauma
foreach (var decorativeSprite in Prefab.DecorativeSprites) foreach (var decorativeSprite in Prefab.DecorativeSprites)
{ {
if (!spriteAnimState[decorativeSprite].IsActive) { continue; } if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState); float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState, spriteAnimState[decorativeSprite].RandomRotationFactor);
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, -rotationRad) * Scale; Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier, -rotationRad) * Scale;
if (flippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; } if (flippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; }
if (flippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; } if (flippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; }
var ca = (float)Math.Cos(-body.Rotation); var ca = (float)Math.Cos(-body.Rotation);
@@ -393,7 +403,7 @@ namespace Barotrauma
Vector2 transformedOffset = new Vector2(ca * offset.X + sa * offset.Y, -sa * offset.X + ca * offset.Y); Vector2 transformedOffset = new Vector2(ca * offset.X + sa * offset.Y, -sa * offset.X + ca * offset.Y);
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + transformedOffset.X, -(DrawPosition.Y + transformedOffset.Y)), color, decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + transformedOffset.X, -(DrawPosition.Y + transformedOffset.Y)), color,
-body.Rotation + rotation, decorativeSprite.Scale * Scale, activeSprite.effects, -body.Rotation + rotation, decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, activeSprite.effects,
depth: depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth)); depth: depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth));
} }
} }
@@ -405,12 +415,12 @@ namespace Barotrauma
foreach (var decorativeSprite in upgradeSprites) foreach (var decorativeSprite in upgradeSprites)
{ {
if (!spriteAnimState[decorativeSprite].IsActive) { continue; } if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState); float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState, spriteAnimState[decorativeSprite].RandomRotationFactor);
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, -rotationRad) * Scale; Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier, -rotationRad) * Scale;
if (flippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; } if (flippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; }
if (flippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; } if (flippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; }
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + offset.X, -(DrawPosition.Y + offset.Y)), color, decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + offset.X, -(DrawPosition.Y + offset.Y)), color,
rotation, decorativeSprite.Scale * Scale, activeSprite.effects, rotation, decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, activeSprite.effects,
depth: depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth)); depth: depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth));
} }
@@ -505,12 +515,39 @@ namespace Barotrauma
public void UpdateSpriteStates(float deltaTime) public void UpdateSpriteStates(float deltaTime)
{ {
if (activeContainedSprite != null)
{
if (activeContainedSprite.DecorativeSpriteBehavior == ContainedItemSprite.DecorativeSpriteBehaviorType.HideWhenVisible)
{
foreach (DecorativeSprite decorativeSprite in Prefab.DecorativeSprites)
{
var spriteState = spriteAnimState[decorativeSprite];
spriteState.IsActive = false;
}
return;
}
}
else
{
foreach (var containedSprite in Prefab.ContainedSprites)
{
if (containedSprite.Sprite != activeSprite && containedSprite.DecorativeSpriteBehavior == ContainedItemSprite.DecorativeSpriteBehaviorType.HideWhenNotVisible)
{
foreach (DecorativeSprite decorativeSprite in Prefab.DecorativeSprites)
{
var spriteState = spriteAnimState[decorativeSprite];
spriteState.IsActive = false;
}
return;
}
}
}
DecorativeSprite.UpdateSpriteStates(Prefab.DecorativeSpriteGroups, spriteAnimState, ID, deltaTime, ConditionalMatches); DecorativeSprite.UpdateSpriteStates(Prefab.DecorativeSpriteGroups, spriteAnimState, ID, deltaTime, ConditionalMatches);
foreach (var upgrade in Upgrades) foreach (var upgrade in Upgrades)
{ {
var upgradeSprites = GetUpgradeSprites(upgrade); var upgradeSprites = GetUpgradeSprites(upgrade);
foreach (var decorativeSprite in upgradeSprites) foreach (var decorativeSprite in upgradeSprites)
{ {
var spriteState = spriteAnimState[decorativeSprite]; var spriteState = spriteAnimState[decorativeSprite];
@@ -1035,6 +1072,28 @@ namespace Barotrauma
// Always create the texts if they have not yet been created // Always create the texts if they have not yet been created
if (texts.Any() && !recreateHudTexts) { return texts; } if (texts.Any() && !recreateHudTexts) { return texts; }
texts.Clear(); texts.Clear();
string nameText = Name;
if (Prefab.Identifier == "idcard" || Tags.Contains("despawncontainer"))
{
string[] readTags = Tags.Split(',');
string idName = null;
foreach (string tag in readTags)
{
string[] s = tag.Split(':');
if (s[0] == "name")
{
idName = s[1];
break;
}
}
if (idName != null)
{
nameText += $" ({idName})";
}
}
texts.Add(new ColoredText(nameText, GUI.Style.TextColor, false, false));
foreach (ItemComponent ic in components) foreach (ItemComponent ic in components)
{ {
if (string.IsNullOrEmpty(ic.DisplayMsg)) { continue; } if (string.IsNullOrEmpty(ic.DisplayMsg)) { continue; }
@@ -1259,7 +1318,7 @@ namespace Barotrauma
{ {
if (GameMain.Client == null) { return; } if (GameMain.Client == null) { return; }
if (parentInventory != null || body == null || !body.Enabled || Removed) if (parentInventory != null || body == null || !body.Enabled || Removed || (GetComponent<Projectile>()?.IsStuckToTarget ?? false))
{ {
positionBuffer.Clear(); positionBuffer.Clear();
return; return;
@@ -1267,12 +1326,7 @@ namespace Barotrauma
isActive = true; isActive = true;
Vector2 newVelocity = body.LinearVelocity; body.CorrectPosition(positionBuffer, out Vector2 newPosition, out Vector2 newVelocity, out float newRotation, out float newAngularVelocity);
Vector2 newPosition = body.SimPosition;
float newAngularVelocity = body.AngularVelocity;
float newRotation = body.Rotation;
body.CorrectPosition(positionBuffer, out newPosition, out newVelocity, out newRotation, out newAngularVelocity);
body.LinearVelocity = newVelocity; body.LinearVelocity = newVelocity;
body.AngularVelocity = newAngularVelocity; body.AngularVelocity = newAngularVelocity;
if (Vector2.DistanceSquared(newPosition, body.SimPosition) > 0.0001f || if (Vector2.DistanceSquared(newPosition, body.SimPosition) > 0.0001f ||
@@ -1490,7 +1544,7 @@ namespace Barotrauma
foreach (WifiComponent wifiComponent in item.GetComponents<WifiComponent>()) foreach (WifiComponent wifiComponent in item.GetComponents<WifiComponent>())
{ {
wifiComponent.TeamID = (Character.TeamType)teamID; wifiComponent.TeamID = (CharacterTeamType)teamID;
} }
if (descriptionChanged) { item.Description = itemDesc; } if (descriptionChanged) { item.Description = itemDesc; }
if (tagsChanged) { item.Tags = tags; } if (tagsChanged) { item.Tags = tags; }
@@ -1521,10 +1575,10 @@ namespace Barotrauma
partial void RemoveProjSpecific() partial void RemoveProjSpecific()
{ {
if (Inventory.draggingItem == this) if (Inventory.DraggingItems.Contains(this))
{ {
Inventory.draggingItem = null; Inventory.DraggingItems.Clear();
Inventory.draggingSlot = null; Inventory.DraggingSlot = null;
} }
} }
} }
@@ -24,14 +24,14 @@ namespace Barotrauma
protected override void CalculateBackgroundFrame() protected override void CalculateBackgroundFrame()
{ {
var firstSlot = slots.FirstOrDefault(); var firstSlot = visualSlots.FirstOrDefault();
if (firstSlot == null) { return; } if (firstSlot == null) { return; }
Rectangle frame = firstSlot.Rect; Rectangle frame = firstSlot.Rect;
frame.Location += firstSlot.DrawOffset.ToPoint(); frame.Location += firstSlot.DrawOffset.ToPoint();
for (int i = 1; i < capacity; i++) for (int i = 1; i < capacity; i++)
{ {
Rectangle slotRect = slots[i].Rect; Rectangle slotRect = visualSlots[i].Rect;
slotRect.Location += slots[i].DrawOffset.ToPoint(); slotRect.Location += visualSlots[i].DrawOffset.ToPoint();
frame = Rectangle.Union(frame, slotRect); frame = Rectangle.Union(frame, slotRect);
} }
BackgroundFrame = new Rectangle( BackgroundFrame = new Rectangle(
@@ -43,7 +43,7 @@ namespace Barotrauma
public override void Draw(SpriteBatch spriteBatch, bool subInventory = false) public override void Draw(SpriteBatch spriteBatch, bool subInventory = false)
{ {
if (slots != null && slots.Length > 0) if (visualSlots != null && visualSlots.Length > 0)
{ {
CalculateBackgroundFrame(); CalculateBackgroundFrame();
if (container.InventoryBackSprite == null) if (container.InventoryBackSprite == null)
@@ -70,7 +70,7 @@ namespace Barotrauma
if (container.InventoryBottomSprite != null && !subInventory) if (container.InventoryBottomSprite != null && !subInventory)
{ {
container.InventoryBottomSprite.Draw(spriteBatch, container.InventoryBottomSprite.Draw(spriteBatch,
new Vector2(BackgroundFrame.Center.X, BackgroundFrame.Bottom) + slots[0].DrawOffset, new Vector2(BackgroundFrame.Center.X, BackgroundFrame.Bottom) + visualSlots[0].DrawOffset,
0.0f, UIScale); 0.0f, UIScale);
} }
@@ -27,8 +27,14 @@ namespace Barotrauma
class ContainedItemSprite class ContainedItemSprite
{ {
public enum DecorativeSpriteBehaviorType
{
None, HideWhenVisible, HideWhenNotVisible
}
public readonly Sprite Sprite; public readonly Sprite Sprite;
public readonly bool UseWhenAttached; public readonly bool UseWhenAttached;
public readonly DecorativeSpriteBehaviorType DecorativeSpriteBehavior;
public readonly string[] AllowedContainerIdentifiers; public readonly string[] AllowedContainerIdentifiers;
public readonly string[] AllowedContainerTags; public readonly string[] AllowedContainerTags;
@@ -36,6 +42,7 @@ namespace Barotrauma
{ {
Sprite = new Sprite(element, path, lazyLoad: lazyLoad); Sprite = new Sprite(element, path, lazyLoad: lazyLoad);
UseWhenAttached = element.GetAttributeBool("usewhenattached", false); UseWhenAttached = element.GetAttributeBool("usewhenattached", false);
Enum.TryParse(element.GetAttributeString("decorativespritebehavior", "None"), ignoreCase: true, out DecorativeSpriteBehavior);
AllowedContainerIdentifiers = element.GetAttributeStringArray("allowedcontaineridentifiers", new string[0], convertToLowerInvariant: true); AllowedContainerIdentifiers = element.GetAttributeStringArray("allowedcontaineridentifiers", new string[0], convertToLowerInvariant: true);
AllowedContainerTags = element.GetAttributeStringArray("allowedcontainertags", new string[0], convertToLowerInvariant: true); AllowedContainerTags = element.GetAttributeStringArray("allowedcontainertags", new string[0], convertToLowerInvariant: true);
} }
@@ -65,6 +65,8 @@ namespace Barotrauma.MapCreatures.Behavior
public readonly List<DamageParticle> DamageParticles = new List<DamageParticle>(); public readonly List<DamageParticle> DamageParticles = new List<DamageParticle>();
public readonly List<DamageParticle> DeathParticles = new List<DamageParticle>(); public readonly List<DamageParticle> DeathParticles = new List<DamageParticle>();
public static bool AlwaysShowBallastFloraSprite = false;
partial void LoadPrefab(XElement element) partial void LoadPrefab(XElement element)
{ {
string? branchAtlasPath = element.GetAttributeString("branchatlas", null); string? branchAtlasPath = element.GetAttributeString("branchatlas", null);
@@ -125,7 +127,7 @@ namespace Barotrauma.MapCreatures.Behavior
float particleAmount = Rand.Range(16, 32); float particleAmount = Rand.Range(16, 32);
for (int i = 0; i < particleAmount; i++) for (int i = 0; i < particleAmount; i++)
{ {
GameMain.ParticleManager.CreateParticle("shrapnel", pos, Rand.Vector(Rand.Range(-50f, 50.0f))); GameMain.ParticleManager.CreateParticle("shrapnel", pos, Rand.Vector(Rand.Range(0f, 250.0f)), Rand.Range(0f, 360.0f));
} }
} }
@@ -43,10 +43,10 @@ namespace Barotrauma
} }
} }
public void DrawFront(SpriteBatch spriteBatch, Camera cam) public void DrawDebugOverlay(SpriteBatch spriteBatch, Camera cam)
{ {
if (renderer == null) { return; } if (renderer == null) { return; }
renderer.Draw(spriteBatch, cam); renderer.DrawDebugOverlay(spriteBatch, cam);
if (GameMain.DebugDraw && Screen.Selected.Cam.Zoom > 0.1f) if (GameMain.DebugDraw && Screen.Selected.Cam.Zoom > 0.1f)
{ {
@@ -114,10 +114,13 @@ namespace Barotrauma
graphics.Clear(BackgroundColor); graphics.Clear(BackgroundColor);
if (renderer == null) return; renderer?.DrawBackground(spriteBatch, cam, LevelObjectManager, backgroundCreatureManager);
renderer.DrawBackground(spriteBatch, cam, LevelObjectManager, backgroundCreatureManager);
} }
public void DrawFront(SpriteBatch spriteBatch, Camera cam)
{
renderer?.DrawForeground(spriteBatch, cam, LevelObjectManager);
}
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{ {
bool isGlobalUpdate = msg.ReadBoolean(); bool isGlobalUpdate = msg.ReadBoolean();
@@ -241,6 +241,7 @@ namespace Barotrauma
private void UpdateDeformations(float deltaTime) private void UpdateDeformations(float deltaTime)
{ {
if (ActivePrefab.DeformableSprite == null) { return; }
foreach (SpriteDeformation deformation in spriteDeformations) foreach (SpriteDeformation deformation in spriteDeformations)
{ {
if (deformation is PositionalDeformation positionalDeformation) if (deformation is PositionalDeformation positionalDeformation)
@@ -10,6 +10,7 @@ namespace Barotrauma
partial class LevelObjectManager partial class LevelObjectManager
{ {
private readonly List<LevelObject> visibleObjectsBack = new List<LevelObject>(); private readonly List<LevelObject> visibleObjectsBack = new List<LevelObject>();
private readonly List<LevelObject> visibleObjectsMid = new List<LevelObject>();
private readonly List<LevelObject> visibleObjectsFront = new List<LevelObject>(); private readonly List<LevelObject> visibleObjectsFront = new List<LevelObject>();
private double NextRefreshTime; private double NextRefreshTime;
@@ -26,6 +27,10 @@ namespace Barotrauma
{ {
obj.Update(deltaTime); obj.Update(deltaTime);
} }
foreach (LevelObject obj in visibleObjectsMid)
{
obj.Update(deltaTime);
}
foreach (LevelObject obj in visibleObjectsFront) foreach (LevelObject obj in visibleObjectsFront)
{ {
obj.Update(deltaTime); obj.Update(deltaTime);
@@ -34,7 +39,7 @@ namespace Barotrauma
public IEnumerable<LevelObject> GetVisibleObjects() public IEnumerable<LevelObject> GetVisibleObjects()
{ {
return visibleObjectsBack.Union(visibleObjectsFront); return visibleObjectsBack.Union(visibleObjectsMid).Union(visibleObjectsFront);
} }
/// <summary> /// <summary>
@@ -43,6 +48,7 @@ namespace Barotrauma
private void RefreshVisibleObjects(Rectangle currentIndices, float zoom) private void RefreshVisibleObjects(Rectangle currentIndices, float zoom)
{ {
visibleObjectsBack.Clear(); visibleObjectsBack.Clear();
visibleObjectsMid.Clear();
visibleObjectsFront.Clear(); visibleObjectsFront.Clear();
float minSizeToDraw = MathHelper.Lerp(10.0f, 5.0f, Math.Min(zoom * 20.0f, 1.0f)); float minSizeToDraw = MathHelper.Lerp(10.0f, 5.0f, Math.Min(zoom * 20.0f, 1.0f));
@@ -70,7 +76,10 @@ namespace Barotrauma
} }
} }
var objectList = obj.Position.Z >= 0 ? visibleObjectsBack : visibleObjectsFront; var objectList =
obj.Position.Z >= 0 ?
visibleObjectsBack :
(obj.Position.Z < -1 ? visibleObjectsFront : visibleObjectsMid);
int drawOrderIndex = 0; int drawOrderIndex = 0;
for (int i = 0; i < objectList.Count; i++) for (int i = 0; i < objectList.Count; i++)
{ {
@@ -102,8 +111,31 @@ namespace Barotrauma
currentGridIndices = currentIndices; currentGridIndices = currentIndices;
} }
/// <summary>
/// Draw the objects behind the level walls
/// </summary>
public void DrawObjectsBack(SpriteBatch spriteBatch, Camera cam)
{
DrawObjects(spriteBatch, cam, visibleObjectsBack);
}
public void DrawObjects(SpriteBatch spriteBatch, Camera cam, bool drawFront) /// <summary>
/// Draw the objects in front of the level walls, but behind characters
/// </summary>
public void DrawObjectsMid(SpriteBatch spriteBatch, Camera cam)
{
DrawObjects(spriteBatch, cam, visibleObjectsMid);
}
/// <summary>
/// Draw the objects in front of the level walls and characters
/// </summary>
public void DrawObjectsFront(SpriteBatch spriteBatch, Camera cam)
{
DrawObjects(spriteBatch, cam, visibleObjectsFront);
}
private void DrawObjects(SpriteBatch spriteBatch, Camera cam, List<LevelObject> objectList)
{ {
Rectangle indices = Rectangle.Empty; Rectangle indices = Rectangle.Empty;
indices.X = (int)Math.Floor(cam.WorldView.X / (float)GridSize); indices.X = (int)Math.Floor(cam.WorldView.X / (float)GridSize);
@@ -132,7 +164,6 @@ namespace Barotrauma
} }
} }
var objectList = drawFront ? visibleObjectsFront : visibleObjectsBack;
foreach (LevelObject obj in objectList) foreach (LevelObject obj in objectList)
{ {
Vector2 camDiff = new Vector2(obj.Position.X, obj.Position.Y) - cam.WorldViewCenter; Vector2 camDiff = new Vector2(obj.Position.X, obj.Position.Y) - cam.WorldViewCenter;
@@ -220,7 +220,7 @@ namespace Barotrauma
SamplerState.LinearWrap, DepthStencilState.DepthRead, null, null, SamplerState.LinearWrap, DepthStencilState.DepthRead, null, null,
cam.Transform); cam.Transform);
backgroundSpriteManager?.DrawObjects(spriteBatch, cam, drawFront: false); backgroundSpriteManager?.DrawObjectsBack(spriteBatch, cam);
if (cam.Zoom > 0.05f) if (cam.Zoom > 0.05f)
{ {
backgroundCreatureManager?.Draw(spriteBatch, cam); backgroundCreatureManager?.Draw(spriteBatch, cam);
@@ -262,8 +262,6 @@ namespace Barotrauma
color: Color.White * alpha, textureScale: new Vector2(texScale)); color: Color.White * alpha, textureScale: new Vector2(texScale));
} }
} }
spriteBatch.End(); spriteBatch.End();
RenderWalls(GameMain.Instance.GraphicsDevice, cam); RenderWalls(GameMain.Instance.GraphicsDevice, cam);
@@ -272,11 +270,21 @@ namespace Barotrauma
BlendState.NonPremultiplied, BlendState.NonPremultiplied,
SamplerState.LinearClamp, DepthStencilState.DepthRead, null, null, SamplerState.LinearClamp, DepthStencilState.DepthRead, null, null,
cam.Transform); cam.Transform);
if (backgroundSpriteManager != null) backgroundSpriteManager.DrawObjects(spriteBatch, cam, drawFront: true); backgroundSpriteManager?.DrawObjectsMid(spriteBatch, cam);
spriteBatch.End(); spriteBatch.End();
} }
public void Draw(SpriteBatch spriteBatch, Camera cam) public void DrawForeground(SpriteBatch spriteBatch, Camera cam, LevelObjectManager backgroundSpriteManager = null)
{
spriteBatch.Begin(SpriteSortMode.Deferred,
BlendState.NonPremultiplied,
SamplerState.LinearClamp, DepthStencilState.DepthRead, null, null,
cam.Transform);
backgroundSpriteManager?.DrawObjectsFront(spriteBatch, cam);
spriteBatch.End();
}
public void DrawDebugOverlay(SpriteBatch spriteBatch, Camera cam)
{ {
if (GameMain.DebugDraw && cam.Zoom > 0.1f) if (GameMain.DebugDraw && cam.Zoom > 0.1f)
{ {
@@ -391,7 +391,7 @@ namespace Barotrauma.Lights
if (GUI.DisableItemHighlights) { return false; } if (GUI.DisableItemHighlights) { return false; }
highlightedEntities.Clear(); highlightedEntities.Clear();
if (Character.Controlled != null && (!Character.Controlled.IsKeyDown(InputType.Aim) || Character.Controlled.SelectedItems.Any(it => it?.GetComponent<Sprayer>() == null))) if (Character.Controlled != null && (!Character.Controlled.IsKeyDown(InputType.Aim) || Character.Controlled.HeldItems.Any(it => it.GetComponent<Sprayer>() == null)))
{ {
if (Character.Controlled.FocusedItem != null) if (Character.Controlled.FocusedItem != null)
{ {
@@ -222,7 +222,7 @@ namespace Barotrauma
return !tileDiscovered[MathHelper.Clamp(x, 0, tileDiscovered.Length), MathHelper.Clamp(y, 0, tileDiscovered.Length)]; return !tileDiscovered[MathHelper.Clamp(x, 0, tileDiscovered.Length), MathHelper.Clamp(y, 0, tileDiscovered.Length)];
} }
partial void ChangeLocationType(Location location, string prevName, LocationTypeChange change) partial void ChangeLocationTypeProjSpecific(Location location, string prevName, LocationTypeChange change)
{ {
if (change.Messages.Any()) if (change.Messages.Any())
{ {
@@ -383,11 +383,11 @@ namespace Barotrauma
Level.Loaded.DebugSetEndLocation(null); Level.Loaded.DebugSetEndLocation(null);
CurrentLocation.Discovered = true; CurrentLocation.Discovered = true;
CurrentLocation.CreateStore();
OnLocationChanged?.Invoke(prevLocation, CurrentLocation); OnLocationChanged?.Invoke(prevLocation, CurrentLocation);
SelectLocation(-1); SelectLocation(-1);
if (GameMain.Client == null) if (GameMain.Client == null)
{ {
CurrentLocation.CreateStore();
ProgressWorld(); ProgressWorld();
} }
else else
@@ -550,7 +550,7 @@ namespace Barotrauma
location.Type.Sprite.Draw(spriteBatch, pos, color, location.Type.Sprite.Draw(spriteBatch, pos, color,
scale: generationParams.LocationIconSize / location.Type.Sprite.size.X * iconScale * zoom); scale: generationParams.LocationIconSize / location.Type.Sprite.size.X * iconScale * zoom);
if (location.TypeChangeTimer <= 0 && !string.IsNullOrEmpty(location.LastTypeChangeMessage) && generationParams.TypeChangeIcon != null) if (location.TimeSinceLastTypeChange < 1 && !string.IsNullOrEmpty(location.LastTypeChangeMessage) && generationParams.TypeChangeIcon != null)
{ {
Vector2 typeChangeIconPos = pos + new Vector2(1.35f, -0.35f) * generationParams.LocationIconSize * 0.5f * zoom; Vector2 typeChangeIconPos = pos + new Vector2(1.35f, -0.35f) * generationParams.LocationIconSize * 0.5f * zoom;
float typeChangeIconScale = 18.0f / generationParams.TypeChangeIcon.SourceRect.Width; float typeChangeIconScale = 18.0f / generationParams.TypeChangeIcon.SourceRect.Width;
@@ -610,7 +610,7 @@ namespace Barotrauma
Vector2 nameSize = GUI.LargeFont.MeasureString(HighlightedLocation.Name); Vector2 nameSize = GUI.LargeFont.MeasureString(HighlightedLocation.Name);
Vector2 typeSize = GUI.Font.MeasureString(HighlightedLocation.Type.Name); Vector2 typeSize = GUI.Font.MeasureString(HighlightedLocation.Type.Name);
Vector2 size = new Vector2(Math.Max(nameSize.X, typeSize.X), nameSize.Y + typeSize.Y); Vector2 size = new Vector2(Math.Max(nameSize.X, typeSize.X), nameSize.Y + typeSize.Y);
bool showReputation = HighlightedLocation.Discovered && HighlightedLocation.Type.HasOutpost && HighlightedLocation.Reputation != null; bool showReputation = hudVisibility > 0.0f && HighlightedLocation.Discovered && HighlightedLocation.Type.HasOutpost && HighlightedLocation.Reputation != null;
string repLabelText = null, repValueText = null; string repLabelText = null, repValueText = null;
Vector2 repLabelSize = Vector2.Zero, repBarSize = Vector2.Zero; Vector2 repLabelSize = Vector2.Zero, repBarSize = Vector2.Zero;
if (showReputation) if (showReputation)
@@ -214,7 +214,7 @@ namespace Barotrauma
} }
else if (PlayerInput.KeyHit(Keys.V)) else if (PlayerInput.KeyHit(Keys.V))
{ {
Paste(cam.WorldViewCenter); Paste(cam.ScreenToWorld(PlayerInput.MousePosition));
} }
else if (PlayerInput.KeyHit(Keys.G)) else if (PlayerInput.KeyHit(Keys.G))
{ {
@@ -279,31 +279,10 @@ namespace Barotrauma
if (GUI.KeyboardDispatcher.Subscriber == null) if (GUI.KeyboardDispatcher.Subscriber == null)
{ {
int up = PlayerInput.KeyDown(Keys.Up) ? 1 : 0, Vector2 nudge = GetNudgeAmount();
down = PlayerInput.KeyDown(Keys.Down) ? -1 : 0, if (nudge != Vector2.Zero)
left = PlayerInput.KeyDown(Keys.Left) ? -1 : 0,
right = PlayerInput.KeyDown(Keys.Right) ? 1 : 0;
int xKeysDown = (left + right);
int yKeysDown = (up + down);
if (xKeysDown != 0 || yKeysDown != 0) { keyDelay += (float) Timing.Step; } else { keyDelay = 0; }
Vector2 nudgeAmount = Vector2.Zero;
if (keyDelay >= 0.5f)
{ {
nudgeAmount.Y = yKeysDown; foreach (MapEntity entityToNudge in selectedList) { entityToNudge.Move(nudge); }
nudgeAmount.X = xKeysDown;
}
if (PlayerInput.KeyHit(Keys.Up)) nudgeAmount.Y = 1f;
if (PlayerInput.KeyHit(Keys.Down)) nudgeAmount.Y = -1f;
if (PlayerInput.KeyHit(Keys.Left)) nudgeAmount.X = -1f;
if (PlayerInput.KeyHit(Keys.Right)) nudgeAmount.X = 1f;
if (nudgeAmount != Vector2.Zero)
{
foreach (MapEntity entityToNudge in selectedList) { entityToNudge.Move(nudgeAmount); }
} }
} }
else else
@@ -476,6 +455,8 @@ namespace Barotrauma
{ {
if (PlayerInput.PrimaryMouseButtonHeld() && if (PlayerInput.PrimaryMouseButtonHeld() &&
PlayerInput.KeyUp(Keys.Space) && PlayerInput.KeyUp(Keys.Space) &&
PlayerInput.KeyUp(Keys.LeftAlt) &&
PlayerInput.KeyUp(Keys.RightAlt) &&
(highlightedListBox == null || (GUI.MouseOn != highlightedListBox && !highlightedListBox.IsParentOf(GUI.MouseOn)))) (highlightedListBox == null || (GUI.MouseOn != highlightedListBox && !highlightedListBox.IsParentOf(GUI.MouseOn))))
{ {
//if clicking a selected entity, start moving it //if clicking a selected entity, start moving it
@@ -491,6 +472,37 @@ namespace Barotrauma
} }
} }
public static Vector2 GetNudgeAmount(bool doHold = true)
{
Vector2 nudgeAmount = Vector2.Zero;
if (doHold)
{
int up = PlayerInput.KeyDown(Keys.Up) ? 1 : 0,
down = PlayerInput.KeyDown(Keys.Down) ? -1 : 0,
left = PlayerInput.KeyDown(Keys.Left) ? -1 : 0,
right = PlayerInput.KeyDown(Keys.Right) ? 1 : 0;
int xKeysDown = (left + right);
int yKeysDown = (up + down);
if (xKeysDown != 0 || yKeysDown != 0) { keyDelay += (float) Timing.Step; } else { keyDelay = 0; }
if (keyDelay >= 0.5f)
{
nudgeAmount.Y = yKeysDown;
nudgeAmount.X = xKeysDown;
}
}
if (PlayerInput.KeyHit(Keys.Up)) nudgeAmount.Y = 1f;
if (PlayerInput.KeyHit(Keys.Down)) nudgeAmount.Y = -1f;
if (PlayerInput.KeyHit(Keys.Left)) nudgeAmount.X = -1f;
if (PlayerInput.KeyHit(Keys.Right)) nudgeAmount.X = 1f;
return nudgeAmount;
}
public MapEntity GetReplacementOrThis() public MapEntity GetReplacementOrThis()
{ {
return ReplacedBy?.GetReplacementOrThis() ?? this; return ReplacedBy?.GetReplacementOrThis() ?? this;
@@ -511,7 +523,7 @@ namespace Barotrauma
{ {
if (entities == null) if (entities == null)
{ {
if (potentialContainer.OwnInventory != null && potentialContainer.ParentInventory == null && !potentialContainer.OwnInventory.IsFull()) if (potentialContainer.OwnInventory != null && potentialContainer.ParentInventory == null && !potentialContainer.OwnInventory.IsFull(takeStacksIntoAccount: true))
{ {
targetContainer = potentialContainer; targetContainer = potentialContainer;
break; break;
@@ -45,7 +45,10 @@ namespace Barotrauma
{ {
CreateInstance(newRect); CreateInstance(newRect);
placePosition = Vector2.Zero; placePosition = Vector2.Zero;
selected = null; if (!PlayerInput.IsShiftDown())
{
selected = null;
}
} }
newRect.Y = -newRect.Y; newRect.Y = -newRect.Y;
@@ -191,10 +191,11 @@ namespace Barotrauma
Vector2 max = new Vector2(worldRect.Right, worldRect.Y); Vector2 max = new Vector2(worldRect.Right, worldRect.Y);
foreach (DecorativeSprite decorativeSprite in Prefab.DecorativeSprites) foreach (DecorativeSprite decorativeSprite in Prefab.DecorativeSprites)
{ {
min.X = Math.Min(worldPos.X - decorativeSprite.Sprite.size.X * decorativeSprite.Sprite.RelativeOrigin.X * decorativeSprite.Scale * Scale, min.X); float scale = decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale;
max.X = Math.Max(worldPos.X + decorativeSprite.Sprite.size.X * (1.0f - decorativeSprite.Sprite.RelativeOrigin.X) * decorativeSprite.Scale * Scale, max.X); min.X = Math.Min(worldPos.X - decorativeSprite.Sprite.size.X * decorativeSprite.Sprite.RelativeOrigin.X * scale, min.X);
min.Y = Math.Min(worldPos.Y - decorativeSprite.Sprite.size.Y * (1.0f - decorativeSprite.Sprite.RelativeOrigin.Y) * decorativeSprite.Scale * Scale, min.Y); max.X = Math.Max(worldPos.X + decorativeSprite.Sprite.size.X * (1.0f - decorativeSprite.Sprite.RelativeOrigin.X) * scale, max.X);
max.Y = Math.Max(worldPos.Y + decorativeSprite.Sprite.size.Y * decorativeSprite.Sprite.RelativeOrigin.Y * decorativeSprite.Scale * Scale, max.Y); min.Y = Math.Min(worldPos.Y - decorativeSprite.Sprite.size.Y * (1.0f - decorativeSprite.Sprite.RelativeOrigin.Y) * scale, min.Y);
max.Y = Math.Max(worldPos.Y + decorativeSprite.Sprite.size.Y * decorativeSprite.Sprite.RelativeOrigin.Y * scale, max.Y);
} }
if (min.X > worldView.Right || max.X < worldView.X) { return false; } if (min.X > worldView.Right || max.X < worldView.X) { return false; }
@@ -220,9 +221,14 @@ namespace Barotrauma
Draw(spriteBatch, editing, false, damageEffect); Draw(spriteBatch, editing, false, damageEffect);
} }
private float GetRealDepth()
{
return SpriteDepthOverrideIsSet ? SpriteOverrideDepth : prefab.sprite.Depth;
}
public float GetDrawDepth() public float GetDrawDepth()
{ {
return GetDrawDepth(SpriteDepthOverrideIsSet ? SpriteOverrideDepth : prefab.sprite.Depth, prefab.sprite); return GetDrawDepth(GetRealDepth(), prefab.sprite);
} }
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)
@@ -320,7 +326,7 @@ namespace Barotrauma
} }
} }
if (back == depth > 0.5f) if (back == GetRealDepth() > 0.5f)
{ {
SpriteEffects oldEffects = prefab.sprite.effects; SpriteEffects oldEffects = prefab.sprite.effects;
prefab.sprite.effects ^= SpriteEffects; prefab.sprite.effects ^= SpriteEffects;
@@ -377,10 +383,10 @@ namespace Barotrauma
foreach (var decorativeSprite in Prefab.DecorativeSprites) foreach (var decorativeSprite in Prefab.DecorativeSprites)
{ {
if (!spriteAnimState[decorativeSprite].IsActive) { continue; } if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState); float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState, spriteAnimState[decorativeSprite].RandomRotationFactor);
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState) * Scale; Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier) * Scale;
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + offset.X, -(DrawPosition.Y + offset.Y)), color, decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + offset.X, -(DrawPosition.Y + offset.Y)), color,
rotation, decorativeSprite.Scale * Scale, prefab.sprite.effects, rotation, decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, prefab.sprite.effects,
depth: Math.Min(depth + (decorativeSprite.Sprite.Depth - prefab.sprite.Depth), 0.999f)); depth: Math.Min(depth + (decorativeSprite.Sprite.Depth - prefab.sprite.Depth), 0.999f));
} }
prefab.sprite.effects = oldEffects; prefab.sprite.effects = oldEffects;
@@ -62,7 +62,11 @@ namespace Barotrauma
}; };
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(new List<MapEntity> { structure }, false)); SubEditorScreen.StoreCommand(new AddOrDeleteCommand(new List<MapEntity> { structure }, false));
selected = null; placePosition = Vector2.Zero;
if (!PlayerInput.IsShiftDown())
{
selected = null;
}
return; return;
} }
} }
@@ -22,6 +22,8 @@ namespace Barotrauma
public readonly float Range; public readonly float Range;
public readonly Vector2 FrequencyMultiplierRange; public readonly Vector2 FrequencyMultiplierRange;
public readonly bool Stream; public readonly bool Stream;
public readonly bool IgnoreMuffling;
public string Filename public string Filename
{ {
@@ -55,7 +57,7 @@ namespace Barotrauma
{ {
DebugConsole.ThrowError($"Loaded frequency range exceeds max value: {FrequencyMultiplierRange} (original string was \"{freqMultAttr}\")"); DebugConsole.ThrowError($"Loaded frequency range exceeds max value: {FrequencyMultiplierRange} (original string was \"{freqMultAttr}\")");
} }
sound.IgnoreMuffling = element.GetAttributeBool("dontmuffle", false); IgnoreMuffling = element.GetAttributeBool("dontmuffle", false);
} }
public float GetRandomFrequencyMultiplier() public float GetRandomFrequencyMultiplier()
@@ -376,22 +378,19 @@ namespace Barotrauma
public static void DrawGrid(SpriteBatch spriteBatch, int gridCells, Vector2 gridCenter, Vector2 roundedGridCenter, float alpha = 1.0f) public static void DrawGrid(SpriteBatch spriteBatch, int gridCells, Vector2 gridCenter, Vector2 roundedGridCenter, float alpha = 1.0f)
{ {
var horizontalLine = GUI.Style.GetComponentStyle("HorizontalLine").GetDefaultSprite();
var verticalLine = GUI.Style.GetComponentStyle("VerticalLine").GetDefaultSprite();
Vector2 topLeft = roundedGridCenter - Vector2.One * GridSize * gridCells / 2; Vector2 topLeft = roundedGridCenter - Vector2.One * GridSize * gridCells / 2;
Vector2 bottomRight = roundedGridCenter + Vector2.One * GridSize * gridCells / 2; Vector2 bottomRight = roundedGridCenter + Vector2.One * GridSize * gridCells / 2;
for (int i = 0; i < gridCells; i++) for (int i = 0; i < gridCells; i++)
{ {
float distFromGridX = (MathUtils.RoundTowardsClosest(gridCenter.X, GridSize.X) - gridCenter.X) / GridSize.X; float distFromGridX = (MathUtils.RoundTowardsClosest(gridCenter.X, GridSize.X) - gridCenter.X) / GridSize.X;
float distFromGridY = (MathUtils.RoundTowardsClosest(gridCenter.X, GridSize.Y) - gridCenter.X) / GridSize.Y; float distFromGridY = (MathUtils.RoundTowardsClosest(gridCenter.Y, GridSize.Y) - gridCenter.Y) / GridSize.Y;
float normalizedDistX = Math.Abs(i + distFromGridX - gridCells / 2) / (gridCells / 2); float normalizedDistX = Math.Abs(i + distFromGridX - gridCells / 2) / (gridCells / 2);
float normalizedDistY = Math.Abs(i - distFromGridY - gridCells / 2) / (gridCells / 2); float normalizedDistY = Math.Abs(i - distFromGridY - gridCells / 2) / (gridCells / 2);
float expandX = MathHelper.Lerp(30.0f, 0.0f, normalizedDistX); float expandX = MathHelper.Lerp(30.0f, 0.0f, normalizedDistY);
float expandY = MathHelper.Lerp(30.0f, 0.0f, normalizedDistY); float expandY = MathHelper.Lerp(30.0f, 0.0f, normalizedDistX);
GUI.DrawLine(spriteBatch, GUI.DrawLine(spriteBatch,
new Vector2(topLeft.X - expandX, -bottomRight.Y + i * GridSize.Y), new Vector2(topLeft.X - expandX, -bottomRight.Y + i * GridSize.Y),
@@ -420,9 +419,10 @@ namespace Barotrauma
parent.RectTransform, Anchor.Center), parent.RectTransform, Anchor.Center),
style: null); style: null);
var connectedSubs = GetConnectedSubs();
foreach (Hull hull in Hull.hullList) foreach (Hull hull in Hull.hullList)
{ {
if (hull.Submarine != this && !(DockedTo.Contains(hull.Submarine))) continue; if (hull.Submarine != this && !connectedSubs.Contains(hull.Submarine)) { continue; }
if (ignoreOutpost && !IsEntityFoundOnThisSub(hull, true)) { continue; } if (ignoreOutpost && !IsEntityFoundOnThisSub(hull, true)) { continue; }
Vector2 relativeHullPos = new Vector2( Vector2 relativeHullPos = new Vector2(
@@ -533,11 +533,11 @@ namespace Barotrauma
for (int i = 0; i < item.Connections.Count; i++) for (int i = 0; i < item.Connections.Count; i++)
{ {
int wireCount = item.Connections[i].Wires.Count(w => w != null); int wireCount = item.Connections[i].Wires.Count(w => w != null);
if (doorLinks + wireCount > Connection.MaxLinked) if (doorLinks + wireCount > item.Connections[i].MaxWires)
{ {
errorMsgs.Add(TextManager.GetWithVariables("InsufficientFreeConnectionsWarning", errorMsgs.Add(TextManager.GetWithVariables("InsufficientFreeConnectionsWarning",
new string[] { "[doorcount]", "[freeconnectioncount]" }, new string[] { "[doorcount]", "[freeconnectioncount]" },
new string[] { doorLinks.ToString(), (Connection.MaxLinked - wireCount).ToString() })); new string[] { doorLinks.ToString(), (item.Connections[i].MaxWires - wireCount).ToString() }));
break; break;
} }
} }
@@ -10,6 +10,7 @@ namespace Barotrauma.Networking
{ {
public string Name; public string Name;
public string PreferredJob; public string PreferredJob;
public CharacterTeamType PreferredTeam;
public UInt16 NameID; public UInt16 NameID;
public UInt64 SteamID; public UInt64 SteamID;
public byte ID; public byte ID;
@@ -81,7 +81,7 @@ namespace Barotrauma.Networking
private byte myID; private byte myID;
private List<Client> otherClients; private readonly List<Client> otherClients;
public readonly List<SubmarineInfo> ServerSubmarines = new List<SubmarineInfo>(); public readonly List<SubmarineInfo> ServerSubmarines = new List<SubmarineInfo>();
@@ -94,13 +94,13 @@ namespace Barotrauma.Networking
private UInt16 lastSentChatMsgID = 0; //last message this client has successfully sent private UInt16 lastSentChatMsgID = 0; //last message this client has successfully sent
private UInt16 lastQueueChatMsgID = 0; //last message added to the queue private UInt16 lastQueueChatMsgID = 0; //last message added to the queue
private List<ChatMessage> chatMsgQueue = new List<ChatMessage>(); private readonly List<ChatMessage> chatMsgQueue = new List<ChatMessage>();
public UInt16 LastSentEntityEventID; public UInt16 LastSentEntityEventID;
private ClientEntityEventManager entityEventManager; private readonly ClientEntityEventManager entityEventManager;
private FileReceiver fileReceiver; private readonly FileReceiver fileReceiver;
#if DEBUG #if DEBUG
public void PrintReceiverTransters() public void PrintReceiverTransters()
@@ -138,6 +138,12 @@ namespace Barotrauma.Networking
} }
} }
private readonly List<Client> previouslyConnectedClients = new List<Client>();
public IEnumerable<Client> PreviouslyConnectedClients
{
get { return previouslyConnectedClients; }
}
public FileReceiver FileReceiver public FileReceiver FileReceiver
{ {
get { return fileReceiver; } get { return fileReceiver; }
@@ -153,9 +159,9 @@ namespace Barotrauma.Networking
get { return entityEventManager; } get { return entityEventManager; }
} }
private object serverEndpoint; private readonly object serverEndpoint;
private int ownerKey; private readonly int ownerKey;
private bool steamP2POwner; private readonly bool steamP2POwner;
public bool IsServerOwner public bool IsServerOwner
{ {
@@ -852,7 +858,7 @@ namespace Barotrauma.Networking
endMessage = inc.ReadString(); endMessage = inc.ReadString();
bool missionSuccessful = inc.ReadBoolean(); bool missionSuccessful = inc.ReadBoolean();
Character.TeamType winningTeam = (Character.TeamType)inc.ReadByte(); CharacterTeamType winningTeam = (CharacterTeamType)inc.ReadByte();
if (missionSuccessful && GameMain.GameSession?.Mission != null) if (missionSuccessful && GameMain.GameSession?.Mission != null)
{ {
GameMain.GameSession.WinningTeam = winningTeam; GameMain.GameSession.WinningTeam = winningTeam;
@@ -1634,7 +1640,7 @@ namespace Barotrauma.Networking
{ {
if (Submarine.MainSubs[i] == null) { break; } if (Submarine.MainSubs[i] == null) { break; }
var teamID = i == 0 ? Character.TeamType.Team1 : Character.TeamType.Team2; var teamID = i == 0 ? CharacterTeamType.Team1 : CharacterTeamType.Team2;
Submarine.MainSubs[i].TeamID = teamID; Submarine.MainSubs[i].TeamID = teamID;
foreach (Item item in Item.ItemList) foreach (Item item in Item.ItemList)
{ {
@@ -1828,6 +1834,7 @@ namespace Barotrauma.Networking
UInt16 nameId = inc.ReadUInt16(); UInt16 nameId = inc.ReadUInt16();
string name = inc.ReadString(); string name = inc.ReadString();
string preferredJob = inc.ReadString(); string preferredJob = inc.ReadString();
byte preferredTeam = inc.ReadByte();
UInt16 characterID = inc.ReadUInt16(); UInt16 characterID = inc.ReadUInt16();
float karma = inc.ReadSingle(); float karma = inc.ReadSingle();
bool muted = inc.ReadBoolean(); bool muted = inc.ReadBoolean();
@@ -1844,6 +1851,7 @@ namespace Barotrauma.Networking
SteamID = steamId, SteamID = steamId,
Name = name, Name = name,
PreferredJob = preferredJob, PreferredJob = preferredJob,
PreferredTeam = (CharacterTeamType)preferredTeam,
CharacterID = characterID, CharacterID = characterID,
Karma = karma, Karma = karma,
Muted = muted, Muted = muted,
@@ -1878,6 +1886,7 @@ namespace Barotrauma.Networking
} }
existingClient.NameID = tc.NameID; existingClient.NameID = tc.NameID;
existingClient.PreferredJob = tc.PreferredJob; existingClient.PreferredJob = tc.PreferredJob;
existingClient.PreferredTeam = tc.PreferredTeam;
existingClient.Character = null; existingClient.Character = null;
existingClient.Karma = tc.Karma; existingClient.Karma = tc.Karma;
existingClient.Muted = tc.Muted; existingClient.Muted = tc.Muted;
@@ -1917,6 +1926,17 @@ namespace Barotrauma.Networking
refreshCampaignUI = true; refreshCampaignUI = true;
} }
} }
foreach (Client client in ConnectedClients)
{
if (!previouslyConnectedClients.Any(c => c.ID == client.ID))
{
while (previouslyConnectedClients.Count > 100)
{
previouslyConnectedClients.RemoveAt(0);
}
previouslyConnectedClients.Add(client);
}
}
if (updateClientListId) { LastClientListUpdateID = listId; } if (updateClientListId) { LastClientListUpdateID = listId; }
if (clientPeer is SteamP2POwnerPeer) if (clientPeer is SteamP2POwnerPeer)
@@ -2142,6 +2162,7 @@ namespace Barotrauma.Networking
} }
break; break;
case ServerNetObject.ENTITY_POSITION: case ServerNetObject.ENTITY_POSITION:
bool isItem = inc.ReadBoolean();
UInt16 id = inc.ReadUInt16(); UInt16 id = inc.ReadUInt16();
uint msgLength = inc.ReadVariableUInt32(); uint msgLength = inc.ReadVariableUInt32();
int msgEndPos = (int)(inc.BitPosition + msgLength * 8); int msgEndPos = (int)(inc.BitPosition + msgLength * 8);
@@ -2156,7 +2177,14 @@ namespace Barotrauma.Networking
entities.Add(entity); entities.Add(entity);
if (entity != null && (entity is Item || entity is Character || entity is Submarine)) if (entity != null && (entity is Item || entity is Character || entity is Submarine))
{ {
entity.ClientRead(objHeader.Value, inc, sendingTime); if (entity is Item != isItem)
{
DebugConsole.AddWarning($"Received a potentially invalid ENTITY_POSITION message. Entity type does not match (server entity is {(isItem ? "an item" : "not an item")}, client entity is {(entity?.GetType().ToString() ?? "null")}). Ignoring the message...");
}
else
{
entity.ClientRead(objHeader.Value, inc, sendingTime);
}
} }
//force to the correct position in case the entity doesn't exist //force to the correct position in case the entity doesn't exist
@@ -2230,9 +2258,9 @@ namespace Barotrauma.Networking
} }
GameAnalyticsManager.AddErrorEventOnce("GameClient.ReadInGameUpdate", GameAnalyticsSDK.Net.EGAErrorSeverity.Critical, string.Join("\n", errorLines)); GameAnalyticsManager.AddErrorEventOnce("GameClient.ReadInGameUpdate", GameAnalyticsSDK.Net.EGAErrorSeverity.Critical, string.Join("\n", errorLines));
DebugConsole.ThrowError("Writing object data to \"crashreport_object.log\", please send this file to us at http://github.com/Regalis11/Barotrauma/issues"); DebugConsole.ThrowError("Writing object data to \"networkerror_data.log\", please send this file to us at http://github.com/Regalis11/Barotrauma/issues");
using (FileStream fl = File.Open("crashreport_object.log", System.IO.FileMode.Create)) using (FileStream fl = File.Open("networkerror_data.log", System.IO.FileMode.Create))
{ {
using (System.IO.BinaryWriter bw = new System.IO.BinaryWriter(fl)) using (System.IO.BinaryWriter bw = new System.IO.BinaryWriter(fl))
using (System.IO.StreamWriter sw = new System.IO.StreamWriter(fl)) using (System.IO.StreamWriter sw = new System.IO.StreamWriter(fl))
@@ -2245,7 +2273,7 @@ namespace Barotrauma.Networking
} }
} }
} }
throw new Exception("Read error: please send us \"crashreport_object.bin\"!"); throw new Exception("Read error: please send us \"networkerror_data.log\"!");
} }
} }
@@ -2269,6 +2297,7 @@ namespace Barotrauma.Networking
{ {
outmsg.Write(""); outmsg.Write("");
} }
outmsg.Write((byte)GameMain.Config.TeamPreference);
if (!(GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign) || campaign.LastSaveID == 0) if (!(GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign) || campaign.LastSaveID == 0)
{ {
@@ -3241,12 +3270,12 @@ namespace Barotrauma.Networking
public virtual bool SelectCrewCharacter(Character character, GUIComponent frame) public virtual bool SelectCrewCharacter(Character character, GUIComponent frame)
{ {
if (character == null) return false; if (character == null) { return false; }
if (character != myCharacter) if (character != myCharacter)
{ {
var client = GameMain.NetworkMember.ConnectedClients.Find(c => c.Character == character); var client = previouslyConnectedClients.Find(c => c.Character == character);
if (client == null) return false; if (client == null) { return false; }
CreateSelectionRelatedButtons(client, frame); CreateSelectionRelatedButtons(client, frame);
} }
@@ -3256,7 +3285,7 @@ namespace Barotrauma.Networking
public virtual bool SelectCrewClient(Client client, GUIComponent frame) public virtual bool SelectCrewClient(Client client, GUIComponent frame)
{ {
if (client == null || client.ID == ID) return false; if (client == null || client.ID == ID) { return false; }
CreateSelectionRelatedButtons(client, frame); CreateSelectionRelatedButtons(client, frame);
return true; return true;
} }
@@ -38,6 +38,7 @@ namespace Barotrauma
CreateLabeledSlider(parent, 0.0f, 1.0f, 0.01f, nameof(DamageEnemyKarmaIncrease)); CreateLabeledSlider(parent, 0.0f, 1.0f, 0.01f, nameof(DamageEnemyKarmaIncrease));
CreateLabeledSlider(parent, 0.0f, 1.0f, 0.01f, nameof(ItemRepairKarmaIncrease)); CreateLabeledSlider(parent, 0.0f, 1.0f, 0.01f, nameof(ItemRepairKarmaIncrease));
CreateLabeledSlider(parent, 0.0f, 10.0f, 0.05f, nameof(ExtinguishFireKarmaIncrease)); CreateLabeledSlider(parent, 0.0f, 10.0f, 0.05f, nameof(ExtinguishFireKarmaIncrease));
CreateLabeledSlider(parent, 0.0f, 1.0f, 0.01f, nameof(BallastFloraKarmaIncrease));
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.12f), parent.RectTransform), TextManager.Get("Karma.NegativeActions"), new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.12f), parent.RectTransform), TextManager.Get("Karma.NegativeActions"),
textAlignment: Alignment.Center, font: GUI.SubHeadingFont) textAlignment: Alignment.Center, font: GUI.SubHeadingFont)
@@ -61,7 +62,6 @@ namespace Barotrauma
CreateLabeledNumberInput(parent, 0, 20, nameof(AllowedWireDisconnectionsPerMinute)); CreateLabeledNumberInput(parent, 0, 20, nameof(AllowedWireDisconnectionsPerMinute));
CreateLabeledSlider(parent, 0.0f, 20.0f, 0.5f, nameof(WireDisconnectionKarmaDecrease)); CreateLabeledSlider(parent, 0.0f, 20.0f, 0.5f, nameof(WireDisconnectionKarmaDecrease));
CreateLabeledSlider(parent, 0.0f, 30.0f, 1.0f, nameof(SpamFilterKarmaDecrease)); CreateLabeledSlider(parent, 0.0f, 30.0f, 1.0f, nameof(SpamFilterKarmaDecrease));
CreateLabeledSlider(parent, 0.0f, 1.0f, 0.01f, nameof(BallastFloraKarmaIncrease));
//hide these for now if a localized text is not available //hide these for now if a localized text is not available
if (TextManager.ContainsTag("Karma." + nameof(DangerousItemStealKarmaDecrease))) if (TextManager.ContainsTag("Karma." + nameof(DangerousItemStealKarmaDecrease)))
@@ -226,7 +226,7 @@ namespace Barotrauma.Networking
} }
else else
{ {
long msgPosition = msg.BitPosition; int msgPosition = msg.BitPosition;
if (GameSettings.VerboseLogging) if (GameSettings.VerboseLogging)
{ {
DebugConsole.NewMessage("received msg " + thisEventID + " (" + entity.ToString() + ")", DebugConsole.NewMessage("received msg " + thisEventID + " (" + entity.ToString() + ")",
@@ -242,9 +242,9 @@ namespace Barotrauma.Networking
{ {
string errorMsg = "Message byte position incorrect after reading an event for the entity \"" + entity.ToString() string errorMsg = "Message byte position incorrect after reading an event for the entity \"" + entity.ToString()
+ "\". Read " + (msg.BitPosition - msgPosition) + " bits, expected message length was " + (msgLength * 8) + " bits."; + "\". Read " + (msg.BitPosition - msgPosition) + " bits, expected message length was " + (msgLength * 8) + " bits.";
#if DEBUG
DebugConsole.ThrowError(errorMsg); DebugConsole.ThrowError(errorMsg);
#endif
GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:BitPosMismatch", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:BitPosMismatch", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
//TODO: force the BitPosition to correct place? Having some entity in a potentially incorrect state is not as bad as a desync kick //TODO: force the BitPosition to correct place? Having some entity in a potentially incorrect state is not as bad as a desync kick
@@ -168,7 +168,19 @@ namespace Barotrauma.Networking
{ {
Close(disableReconnect: true); Close(disableReconnect: true);
string missingModNames = "\n- " + string.Join("\n\n- ", missingPackages.Select(p => GetPackageStr(p))) + "\n\n"; string missingModNames = "\n";
int displayedModCount = 0;
foreach (ServerContentPackage missingPackage in missingPackages)
{
missingModNames += "\n- " + GetPackageStr(missingPackage);
displayedModCount++;
if (GUI.Font.MeasureString(missingModNames).Y > GameMain.GraphicsHeight * 0.5f)
{
missingModNames += "\n\n" + TextManager.GetWithVariable("workshopitemdownloadprompttruncated", "[number]", (missingPackages.Count - displayedModCount).ToString());
break;
}
}
missingModNames += "\n\n";
var msgBox = new GUIMessageBox( var msgBox = new GUIMessageBox(
TextManager.Get("WorkshopItemDownloadTitle"), TextManager.Get("WorkshopItemDownloadTitle"),
@@ -189,6 +201,7 @@ namespace Barotrauma.Networking
if (!contentPackageOrderReceived) if (!contentPackageOrderReceived)
{ {
GameMain.Config.BackUpModOrder();
GameMain.Config.SwapPackages(corePackage.CorePackage, regularPackages.Select(p => p.RegularPackage).ToList()); GameMain.Config.SwapPackages(corePackage.CorePackage, regularPackages.Select(p => p.RegularPackage).ToList());
contentPackageOrderReceived = true; contentPackageOrderReceived = true;
} }
@@ -195,10 +195,11 @@ namespace Barotrauma.Networking
{ {
foreach (var data in line.RichData) foreach (var data in line.RichData)
{ {
UInt64 id = 0; if (!UInt64.TryParse(data.Metadata, out ulong id)) { return; }
if (!UInt64.TryParse(data.Metadata, out id)) { return; } Client client = GameMain.Client.ConnectedClients.Find(c => c.SteamID == id)
Client client = GameMain.Client.ConnectedClients.Find(c => c.SteamID == id); ?? GameMain.Client.ConnectedClients.Find(c => c.ID == id)
client ??= GameMain.Client.ConnectedClients.Find(c => c.ID == id); ?? GameMain.Client.PreviouslyConnectedClients.FirstOrDefault(c => c.SteamID == id)
?? GameMain.Client.PreviouslyConnectedClients.FirstOrDefault(c => c.ID == id);
if (client != null && client.Karma < 40.0f) if (client != null && client.Karma < 40.0f)
{ {
textContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.0f), listBox.Content.RectTransform), textContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.0f), listBox.Content.RectTransform),
@@ -243,10 +244,11 @@ namespace Barotrauma.Networking
Data = data, Data = data,
OnClick = (component, area) => OnClick = (component, area) =>
{ {
UInt64 id = 0; if (!UInt64.TryParse(area.Data.Metadata, out UInt64 id)) { return; }
if (!UInt64.TryParse(area.Data.Metadata, out id)) { return; } Client client = GameMain.Client.ConnectedClients.Find(c => c.SteamID == id)
Client client = GameMain.Client.ConnectedClients.Find(c => c.SteamID == id); ?? GameMain.Client.ConnectedClients.Find(c => c.ID == id)
client ??= GameMain.Client.ConnectedClients.Find(c => c.ID == id); ?? GameMain.Client.PreviouslyConnectedClients.FirstOrDefault(c => c.SteamID == id)
?? GameMain.Client.PreviouslyConnectedClients.FirstOrDefault(c => c.ID == id);
if (client == null) { return; } if (client == null) { return; }
GameMain.NetLobbyScreen.SelectPlayer(client); GameMain.NetLobbyScreen.SelectPlayer(client);
} }
@@ -269,14 +269,13 @@ namespace Barotrauma.Steam
} }
} }
//TODO: find a better strategy to fetch all lobbies, this is gonna take forever if we actually have 10000 lobbies
Steamworks.Data.LobbyQuery lobbyQuery = Steamworks.SteamMatchmaking.CreateLobbyQuery().FilterDistanceWorldwide().WithMaxResults(10000);
Steamworks.Dispatch.OnDebugCallback = (callbackType, contents, isServer) => Steamworks.Dispatch.OnDebugCallback = (callbackType, contents, isServer) =>
{ {
DebugConsole.NewMessage($"{callbackType}: " + contents, Color.Yellow); DebugConsole.NewMessage($"{callbackType}: " + contents, Color.Yellow);
}; };
TaskPool.Add("LobbyQueryRequest", lobbyQuery.RequestAsync(),
TaskPool.Add("LobbyQueryRequest", LobbyQueryRequest(),
(t) => (t) =>
{ {
Steamworks.Dispatch.OnDebugCallback = null; Steamworks.Dispatch.OnDebugCallback = null;
@@ -286,7 +285,7 @@ namespace Barotrauma.Steam
taskDone(); taskDone();
return; return;
} }
var lobbies = ((Task<Steamworks.Data.Lobby[]>)t).Result; var lobbies = ((Task<List<Steamworks.Data.Lobby>>)t).Result;
if (lobbies != null) if (lobbies != null)
{ {
foreach (var lobby in lobbies) foreach (var lobby in lobbies)
@@ -373,6 +372,32 @@ namespace Barotrauma.Steam
return true; return true;
} }
public static async Task<List<Steamworks.Data.Lobby>> LobbyQueryRequest()
{
List<Steamworks.Data.Lobby> allLobbies = new List<Steamworks.Data.Lobby>();
Steamworks.Data.LobbyQuery lobbyQuery = Steamworks.SteamMatchmaking.CreateLobbyQuery()
.FilterDistanceWorldwide()
.WithMaxResults(50);
//steamworks seems to unable to retrieve more than 50
//lobbies per request; to work around this, we'll make
//up to 10 requests, asking to ignore all previous results
//in each subsequent request
for (int i = 0; i < 10; i++)
{
Steamworks.Data.Lobby[] lobbies = await lobbyQuery.RequestAsync();
if (lobbies == null) { break; }
foreach (var l in lobbies)
{
lobbyQuery = lobbyQuery
.WithoutKeyValue("lobbyowner", l.GetData("lobbyowner"));
}
allLobbies.AddRange(lobbies);
}
//make sure all returned lobbies are distinct, don't want any duplicates here
return allLobbies.Select(l => l.Id).Distinct().Select(i => allLobbies.Find(l => l.Id == i)).ToList();
}
public static void AssignLobbyDataToServerInfo(Steamworks.Data.Lobby lobby, ServerInfo serverInfo) public static void AssignLobbyDataToServerInfo(Steamworks.Data.Lobby lobby, ServerInfo serverInfo)
{ {
serverInfo.OwnerVerified = true; serverInfo.OwnerVerified = true;
@@ -1173,7 +1198,7 @@ namespace Barotrauma.Steam
foreach (ContentFile contentFile in contentPackage.Files) foreach (ContentFile contentFile in contentPackage.Files)
{ {
contentFile.Path = contentFile.Path.CleanUpPath(); contentFile.Path = contentFile.Path.CleanUpPathCrossPlatform(correctFilenameCase: true, item?.Directory);
string sourceFile = Path.Combine(item?.Directory, contentFile.Path); string sourceFile = Path.Combine(item?.Directory, contentFile.Path);
if (!File.Exists(sourceFile)) if (!File.Exists(sourceFile))
{ {
@@ -239,7 +239,7 @@ namespace Barotrauma.Networking
bool allowEnqueue = false; bool allowEnqueue = false;
if (GameMain.WindowActive) if (GameMain.WindowActive)
{ {
ForceLocal = captureTimer > 0 ? ForceLocal : false; ForceLocal = captureTimer > 0 ? ForceLocal : GameMain.Config.UseLocalVoiceByDefault;
bool pttDown = false; bool pttDown = false;
if ((PlayerInput.KeyDown(InputType.Voice) || PlayerInput.KeyDown(InputType.LocalVoice)) && if ((PlayerInput.KeyDown(InputType.Voice) || PlayerInput.KeyDown(InputType.LocalVoice)) &&
GUI.KeyboardDispatcher.Subscriber == null) GUI.KeyboardDispatcher.Subscriber == null)
@@ -101,7 +101,7 @@ namespace Barotrauma.Networking
var messageType = !client.VoipQueue.ForceLocal && ChatMessage.CanUseRadio(client.Character, out radio) ? ChatMessageType.Radio : ChatMessageType.Default; var messageType = !client.VoipQueue.ForceLocal && ChatMessage.CanUseRadio(client.Character, out radio) ? ChatMessageType.Radio : ChatMessageType.Default;
client.Character.ShowSpeechBubble(1.25f, ChatMessage.MessageColor[(int)messageType]); client.Character.ShowSpeechBubble(1.25f, ChatMessage.MessageColor[(int)messageType]);
client.VoipSound.UseRadioFilter = messageType == ChatMessageType.Radio; client.VoipSound.UseRadioFilter = messageType == ChatMessageType.Radio && !GameMain.Config.DisableVoiceChatFilters;
if (client.VoipSound.UseRadioFilter) if (client.VoipSound.UseRadioFilter)
{ {
client.VoipSound.SetRange(radio.Range * 0.8f, radio.Range); client.VoipSound.SetRange(radio.Range * 0.8f, radio.Range);
@@ -110,7 +110,7 @@ namespace Barotrauma.Networking
{ {
client.VoipSound.SetRange(ChatMessage.SpeakRange * 0.4f, ChatMessage.SpeakRange); client.VoipSound.SetRange(ChatMessage.SpeakRange * 0.4f, ChatMessage.SpeakRange);
} }
if (!client.VoipSound.UseRadioFilter && Character.Controlled != null) if (!client.VoipSound.UseRadioFilter && Character.Controlled != null && !GameMain.Config.DisableVoiceChatFilters)
{ {
client.VoipSound.UseMuffleFilter = SoundPlayer.ShouldMuffleSound(Character.Controlled, client.Character.WorldPosition, ChatMessage.SpeakRange, client.Character.CurrentHull); client.VoipSound.UseMuffleFilter = SoundPlayer.ShouldMuffleSound(Character.Controlled, client.Character.WorldPosition, ChatMessage.SpeakRange, client.Character.CurrentHull);
} }
@@ -11,15 +11,15 @@ namespace Barotrauma
{ {
class CampaignSetupUI class CampaignSetupUI
{ {
private GUIComponent newGameContainer, loadGameContainer; private readonly GUIComponent newGameContainer, loadGameContainer;
private GUIListBox subList; private GUIListBox subList;
private GUIListBox saveList; private GUIListBox saveList;
private List<GUITickBox> subTickBoxes; private List<GUITickBox> subTickBoxes;
private GUITextBox saveNameBox, seedBox; private readonly GUITextBox saveNameBox, seedBox;
private GUILayoutGroup subPreviewContainer; private readonly GUILayoutGroup subPreviewContainer;
private GUIButton loadGameButton, deleteMpSaveButton; private GUIButton loadGameButton, deleteMpSaveButton;
@@ -35,6 +35,12 @@ namespace Barotrauma
private set; private set;
} }
public GUITextBlock InitialMoneyText
{
get;
private set;
}
private readonly bool isMultiplayer; private readonly bool isMultiplayer;
public CampaignSetupUI(bool isMultiplayer, GUIComponent newGameContainer, GUIComponent loadGameContainer, IEnumerable<SubmarineInfo> submarines, IEnumerable<string> saveFiles = null) public CampaignSetupUI(bool isMultiplayer, GUIComponent newGameContainer, GUIComponent loadGameContainer, IEnumerable<SubmarineInfo> submarines, IEnumerable<string> saveFiles = null)
@@ -122,10 +128,10 @@ namespace Barotrauma
}; };
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.12f), var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.12f),
(isMultiplayer ? leftColumn : rightColumn).RectTransform) { MaxSize = new Point(int.MaxValue, 60) }, childAnchor: Anchor.TopRight); (isMultiplayer ? leftColumn : rightColumn).RectTransform) { MaxSize = new Point(int.MaxValue, 60) }, childAnchor: Anchor.BottomRight, isHorizontal: true);
if (!isMultiplayer) { buttonContainer.IgnoreLayoutGroups = true; } if (!isMultiplayer) { buttonContainer.IgnoreLayoutGroups = true; }
StartButton = new GUIButton(new RectTransform(new Vector2(0.45f, 1f), buttonContainer.RectTransform, Anchor.BottomRight) { MaxSize = new Point(350, 60) }, TextManager.Get("StartCampaignButton")) StartButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1f), buttonContainer.RectTransform, Anchor.BottomRight) { MaxSize = new Point(350, 60) }, TextManager.Get("StartCampaignButton"))
{ {
OnClicked = (GUIButton btn, object userData) => OnClicked = (GUIButton btn, object userData) =>
{ {
@@ -224,6 +230,27 @@ namespace Barotrauma
} }
}; };
InitialMoneyText = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1f), buttonContainer.RectTransform), "",
font: isMultiplayer ? GUI.Style.SmallFont : GUI.Style.Font, textColor: GUI.Style.Green)
{
TextGetter = () =>
{
int initialMoney = CampaignMode.InitialMoney;
if (isMultiplayer)
{
if (GameMain.NetLobbyScreen.SelectedSub != null)
{
initialMoney -= GameMain.NetLobbyScreen.SelectedSub.Price;
}
}
else if (subList.SelectedData is SubmarineInfo subInfo)
{
initialMoney -= subInfo.Price;
}
initialMoney = Math.Max(initialMoney, isMultiplayer ? MultiPlayerCampaign.MinimumInitialMoney : 0);
return TextManager.GetWithVariable("campaignstartingmoney", "[money]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", initialMoney));
}
};
if (!isMultiplayer) if (!isMultiplayer)
{ {
@@ -366,7 +393,7 @@ namespace Barotrauma
if (!(obj is SubmarineInfo sub)) { return true; } if (!(obj is SubmarineInfo sub)) { return true; }
#if !DEBUG #if !DEBUG
if (!isMultiplayer && sub.Price > CampaignMode.MaxInitialSubmarinePrice && !GameMain.DebugDraw) if (!isMultiplayer && sub.Price > CampaignMode.InitialMoney && !GameMain.DebugDraw)
{ {
StartButton.Enabled = false; StartButton.Enabled = false;
return false; return false;
@@ -419,13 +446,14 @@ namespace Barotrauma
} }
else else
{ {
subsToShow = submarines.Where(s => s.IsCampaignCompatibleIgnoreClass).ToList(); string downloadFolder = Path.GetFullPath(SaveUtil.SubmarineDownloadFolder);
subsToShow = submarines.Where(s => s.IsCampaignCompatibleIgnoreClass && Path.GetDirectoryName(Path.GetFullPath(s.FilePath)) != downloadFolder).ToList();
} }
subsToShow.Sort((s1, s2) => subsToShow.Sort((s1, s2) =>
{ {
int p1 = s1.Price > CampaignMode.MaxInitialSubmarinePrice ? 10 : 0; int p1 = s1.Price > CampaignMode.InitialMoney ? 10 : 0;
int p2 = s2.Price > CampaignMode.MaxInitialSubmarinePrice ? 10 : 0; int p2 = s2.Price > CampaignMode.InitialMoney ? 10 : 0;
return p1.CompareTo(p2) * 100 + s1.Name.CompareTo(s2.Name); return p1.CompareTo(p2) * 100 + s1.Name.CompareTo(s2.Name);
}); });
@@ -450,13 +478,13 @@ namespace Barotrauma
var priceText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), textBlock.RectTransform, Anchor.CenterRight), var priceText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), textBlock.RectTransform, Anchor.CenterRight),
TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", sub.Price)), textAlignment: Alignment.CenterRight, font: GUI.SmallFont) TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", sub.Price)), textAlignment: Alignment.CenterRight, font: GUI.SmallFont)
{ {
TextColor = sub.Price > CampaignMode.MaxInitialSubmarinePrice ? GUI.Style.Red : textBlock.TextColor * 0.8f, TextColor = sub.Price > CampaignMode.InitialMoney ? GUI.Style.Red : textBlock.TextColor * 0.8f,
ToolTip = textBlock.ToolTip ToolTip = textBlock.ToolTip
}; };
#if !DEBUG #if !DEBUG
if (!GameMain.DebugDraw) if (!GameMain.DebugDraw)
{ {
if (sub.Price > CampaignMode.MaxInitialSubmarinePrice || !sub.IsCampaignCompatible) if (sub.Price > CampaignMode.InitialMoney || !sub.IsCampaignCompatible)
{ {
textBlock.CanBeFocused = false; textBlock.CanBeFocused = false;
textBlock.TextColor *= 0.5f; textBlock.TextColor *= 0.5f;
@@ -466,7 +494,7 @@ namespace Barotrauma
} }
if (SubmarineInfo.SavedSubmarines.Any()) if (SubmarineInfo.SavedSubmarines.Any())
{ {
var validSubs = subsToShow.Where(s => s.IsCampaignCompatible && s.Price <= CampaignMode.MaxInitialSubmarinePrice).ToList(); var validSubs = subsToShow.Where(s => s.IsCampaignCompatible && s.Price <= CampaignMode.InitialMoney).ToList();
if (validSubs.Count > 0) if (validSubs.Count > 0)
{ {
subList.Select(validSubs[Rand.Int(validSubs.Count)]); subList.Select(validSubs[Rand.Int(validSubs.Count)]);
@@ -326,7 +326,7 @@ namespace Barotrauma
break; break;
case CampaignMode.InteractionType.Store: case CampaignMode.InteractionType.Store:
Store?.Update(); Store?.Update(deltaTime);
break; break;
} }
} }
@@ -820,6 +820,7 @@ namespace Barotrauma.CharacterEditor
} }
} }
} }
spriteBatch.End(); spriteBatch.End();
// Lights // Lights
@@ -860,10 +861,7 @@ namespace Barotrauma.CharacterEditor
} }
if (isDrawingLimb) if (isDrawingLimb)
{ {
if (spriteSheetRect.Contains(PlayerInput.MousePosition)) GUI.DrawRectangle(spriteBatch, newLimbRect, Color.Yellow);
{
GUI.DrawRectangle(spriteBatch, newLimbRect, Color.Yellow);
}
} }
if (jointCreationMode != JointCreationMode.None) if (jointCreationMode != JointCreationMode.None)
{ {
@@ -1131,32 +1129,25 @@ namespace Barotrauma.CharacterEditor
{ {
SetToggle(limbsToggle, true); SetToggle(limbsToggle, true);
} }
if (spriteSheetRect.Contains(PlayerInput.MousePosition)) if (PlayerInput.PrimaryMouseButtonHeld())
{ {
if (PlayerInput.PrimaryMouseButtonHeld()) if (newLimbRect == Rectangle.Empty)
{ {
if (newLimbRect == Rectangle.Empty) newLimbRect = new Rectangle((int)PlayerInput.MousePosition.X, (int)PlayerInput.MousePosition.Y, 0, 0);
{
newLimbRect = new Rectangle((int)PlayerInput.MousePosition.X, (int)PlayerInput.MousePosition.Y, 0, 0);
}
else
{
newLimbRect.Size = new Point((int)PlayerInput.MousePosition.X - newLimbRect.X, (int)PlayerInput.MousePosition.Y - newLimbRect.Y);
}
newLimbRect.Size = new Point(Math.Max(newLimbRect.Width, 2), Math.Max(newLimbRect.Height, 2));
} }
if (PlayerInput.PrimaryMouseButtonClicked()) else
{ {
// Take the offset and the zoom into account newLimbRect.Size = new Point((int)PlayerInput.MousePosition.X - newLimbRect.X, (int)PlayerInput.MousePosition.Y - newLimbRect.Y);
newLimbRect.Location = new Point(newLimbRect.X - spriteSheetOffsetX, newLimbRect.Y - spriteSheetOffsetY);
newLimbRect = newLimbRect.Divide(spriteSheetZoom);
CreateNewLimb(newLimbRect);
isDrawingLimb = false;
newLimbRect = Rectangle.Empty;
} }
newLimbRect.Size = new Point(Math.Max(newLimbRect.Width, 2), Math.Max(newLimbRect.Height, 2));
} }
else if (PlayerInput.PrimaryMouseButtonClicked())
{ {
// Take the offset and the zoom into account
newLimbRect.Location = new Point(newLimbRect.X - spriteSheetOffsetX, newLimbRect.Y - spriteSheetOffsetY);
newLimbRect = newLimbRect.Divide(spriteSheetZoom);
CreateNewLimb(newLimbRect);
isDrawingLimb = false;
newLimbRect = Rectangle.Empty; newLimbRect = Rectangle.Empty;
} }
} }
@@ -1449,7 +1440,11 @@ namespace Barotrauma.CharacterEditor
{ {
if (allFiles == null) if (allFiles == null)
{ {
#if DEBUG
allFiles = CharacterPrefab.ConfigFilePaths.OrderBy(p => p).ToList(); allFiles = CharacterPrefab.ConfigFilePaths.OrderBy(p => p).ToList();
#else
allFiles = CharacterPrefab.ConfigFilePaths.Where(p => !p.Contains("variant", StringComparison.OrdinalIgnoreCase)).OrderBy(p => p).ToList();
#endif
allFiles.ForEach(f => DebugConsole.NewMessage(f, Color.White)); allFiles.ForEach(f => DebugConsole.NewMessage(f, Color.White));
} }
return allFiles; return allFiles;
@@ -1780,7 +1775,7 @@ namespace Barotrauma.CharacterEditor
// Animations // Animations
AnimationParams.ClearCache(); AnimationParams.ClearCache();
string animFolder = AnimationParams.GetFolder(name, contentPackage); string animFolder = AnimationParams.GetFolder(name);
if (animations != null) if (animations != null)
{ {
if (!Directory.Exists(animFolder)) if (!Directory.Exists(animFolder))
@@ -1791,7 +1786,7 @@ namespace Barotrauma.CharacterEditor
{ {
XElement element = animation.MainElement; XElement element = animation.MainElement;
element.SetAttributeValue("type", name); element.SetAttributeValue("type", name);
string fullPath = AnimationParams.GetDefaultFile(name, animation.AnimationType, contentPackage); string fullPath = AnimationParams.GetDefaultFile(name, animation.AnimationType);
element.Name = AnimationParams.GetDefaultFileName(name, animation.AnimationType); element.Name = AnimationParams.GetDefaultFileName(name, animation.AnimationType);
#if DEBUG #if DEBUG
element.Save(fullPath); element.Save(fullPath);
@@ -1816,7 +1811,7 @@ namespace Barotrauma.CharacterEditor
default: continue; default: continue;
} }
Type type = AnimationParams.GetParamTypeFromAnimType(animType, isHumanoid); Type type = AnimationParams.GetParamTypeFromAnimType(animType, isHumanoid);
string fullPath = AnimationParams.GetDefaultFile(name, animType, contentPackage); string fullPath = AnimationParams.GetDefaultFile(name, animType);
AnimationParams.Create(fullPath, name, animType, type); AnimationParams.Create(fullPath, name, animType, type);
} }
} }
@@ -1836,9 +1831,8 @@ namespace Barotrauma.CharacterEditor
private void ShowWearables() private void ShowWearables()
{ {
if (character.Inventory == null) { return; } if (character.Inventory == null) { return; }
foreach (var item in character.Inventory.Items) foreach (var item in character.Inventory.AllItems)
{ {
if (item == null) { continue; }
// Temp condition, todo: remove // Temp condition, todo: remove
if (item.AllowedSlots.Contains(InvSlotType.Head) || item.AllowedSlots.Contains(InvSlotType.Headset)) { continue; } if (item.AllowedSlots.Contains(InvSlotType.Head) || item.AllowedSlots.Contains(InvSlotType.Headset)) { continue; }
item.Equip(character); item.Equip(character);
@@ -1847,7 +1841,7 @@ namespace Barotrauma.CharacterEditor
private void HideWearables() private void HideWearables()
{ {
character.Inventory?.Items.ForEachMod(i => i?.Unequip(character)); character.Inventory?.AllItemsMod.ForEach(i => i.Unequip(character));
} }
#endregion #endregion
@@ -2787,8 +2781,10 @@ namespace Barotrauma.CharacterEditor
} }
return true; return true;
}; };
// Spacing // Spacing
new GUIFrame(new RectTransform(buttonSize / 2, layoutGroup.RectTransform), style: null) { CanBeFocused = false }; new GUIFrame(new RectTransform(buttonSize / 2, layoutGroup.RectTransform), style: null) { CanBeFocused = false };
Vector2 messageBoxRelSize = new Vector2(0.5f, 0.7f); Vector2 messageBoxRelSize = new Vector2(0.5f, 0.7f);
var saveRagdollButton = new GUIButton(new RectTransform(buttonSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("SaveRagdoll")); var saveRagdollButton = new GUIButton(new RectTransform(buttonSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("SaveRagdoll"));
saveRagdollButton.OnClicked += (button, userData) => saveRagdollButton.OnClicked += (button, userData) =>
@@ -3573,15 +3569,33 @@ namespace Barotrauma.CharacterEditor
return rect; return rect;
} }
// TODO: refactor this so that it can be used in all cases private void UpdateSourceRect(Limb limb, Rectangle newRect, bool resize)
private void UpdateSourceRect(Limb limb, Rectangle newRect)
{ {
limb.ActiveSprite.SourceRect = newRect; Sprite activeSprite = limb.ActiveSprite;
activeSprite.SourceRect = newRect;
if (limb.DamagedSprite != null) if (limb.DamagedSprite != null)
{ {
limb.DamagedSprite.SourceRect = limb.ActiveSprite.SourceRect; limb.DamagedSprite.SourceRect = activeSprite.SourceRect;
}
Vector2 colliderSize = new Vector2(ConvertUnits.ToSimUnits(newRect.Width), ConvertUnits.ToSimUnits(newRect.Height));
if (resize)
{
if (recalculateCollider)
{
RecalculateCollider(limb, colliderSize);
}
}
var spritePos = new Vector2(spriteSheetOffsetX, GetOffsetY(activeSprite));
var originWidget = GetLimbEditWidget($"{limb.Params.ID}_origin", limb);
if (!resize && originWidget != null)
{
Vector2 newOrigin = (originWidget.DrawPos - spritePos - activeSprite.SourceRect.Location.ToVector2() * spriteSheetZoom) / spriteSheetZoom;
RecalculateOrigin(limb, newOrigin);
}
else
{
RecalculateOrigin(limb);
} }
RecalculateOrigin(limb);
TryUpdateLimbParam(limb, "sourcerect", newRect); TryUpdateLimbParam(limb, "sourcerect", newRect);
if (limbPairEditing) if (limbPairEditing)
{ {
@@ -3592,30 +3606,25 @@ namespace Barotrauma.CharacterEditor
{ {
otherLimb.DamagedSprite.SourceRect = newRect; otherLimb.DamagedSprite.SourceRect = newRect;
} }
if (resize)
{
if (recalculateCollider)
{
RecalculateCollider(otherLimb, colliderSize);
}
}
if (!resize && originWidget != null)
{
Vector2 newOrigin = (originWidget.DrawPos - spritePos - activeSprite.SourceRect.Location.ToVector2() * spriteSheetZoom) / spriteSheetZoom;
RecalculateOrigin(otherLimb, newOrigin);
}
else
{
RecalculateOrigin(otherLimb);
}
TryUpdateLimbParam(otherLimb, "sourcerect", newRect); TryUpdateLimbParam(otherLimb, "sourcerect", newRect);
RecalculateOrigin(otherLimb);
}); });
}; };
void RecalculateOrigin(Limb l)
{
// Keeps the relative origin unchanged. The absolute origin will be recalculated.
l.ActiveSprite.RelativeOrigin = l.ActiveSprite.RelativeOrigin;
// TODO:
//if (lockSpriteOrigin)
//{
// // Keeps the absolute origin unchanged. The relative origin will be recalculated.
// var spritePos = new Vector2(spriteSheetOffsetX, GetOffsetY(l));
// l.ActiveSprite.Origin = (originWidget.DrawPos - spritePos - l.ActiveSprite.SourceRect.Location.ToVector2() * spriteSheetZoom) / spriteSheetZoom;
// TryUpdateLimbParam(l, "origin", l.ActiveSprite.RelativeOrigin);
//}
//else
//{
// // Keeps the relative origin unchanged. The absolute origin will be recalculated.
// l.ActiveSprite.RelativeOrigin = l.ActiveSprite.RelativeOrigin;
//}
}
} }
private void CalculateSpritesheetZoom() private void CalculateSpritesheetZoom()
@@ -4765,7 +4774,7 @@ namespace Barotrauma.CharacterEditor
w.refresh(); w.refresh();
w.MouseHeld += dTime => w.MouseHeld += dTime =>
{ {
var spritePos = new Vector2(spriteSheetOffsetX, GetOffsetY(limb)); var spritePos = new Vector2(spriteSheetOffsetX, GetOffsetY(limb.ActiveSprite));
w.DrawPos = PlayerInput.MousePosition.Clamp(spritePos + GetTopLeft() * spriteSheetZoom, spritePos + GetBottomRight() * spriteSheetZoom); w.DrawPos = PlayerInput.MousePosition.Clamp(spritePos + GetTopLeft() * spriteSheetZoom, spritePos + GetBottomRight() * spriteSheetZoom);
sprite.Origin = (w.DrawPos - spritePos - sprite.SourceRect.Location.ToVector2() * spriteSheetZoom) / spriteSheetZoom; sprite.Origin = (w.DrawPos - spritePos - sprite.SourceRect.Location.ToVector2() * spriteSheetZoom) / spriteSheetZoom;
if (limb.DamagedSprite != null) if (limb.DamagedSprite != null)
@@ -4796,14 +4805,14 @@ namespace Barotrauma.CharacterEditor
}; };
w.PreDraw += (sb, dTime) => w.PreDraw += (sb, dTime) =>
{ {
var spritePos = new Vector2(spriteSheetOffsetX, GetOffsetY(limb)); var spritePos = new Vector2(spriteSheetOffsetX, GetOffsetY(limb.ActiveSprite));
w.DrawPos = (spritePos + (sprite.Origin + sprite.SourceRect.Location.ToVector2()) * spriteSheetZoom) w.DrawPos = (spritePos + (sprite.Origin + sprite.SourceRect.Location.ToVector2()) * spriteSheetZoom)
.Clamp(spritePos + GetTopLeft() * spriteSheetZoom, spritePos + GetBottomRight() * spriteSheetZoom); .Clamp(spritePos + GetTopLeft() * spriteSheetZoom, spritePos + GetBottomRight() * spriteSheetZoom);
w.refresh(); w.refresh();
}; };
}); });
originWidget.Draw(spriteBatch, deltaTime); originWidget.Draw(spriteBatch, deltaTime);
if (!lockSpritePosition) if (!lockSpritePosition && (limb.type != LimbType.Head || !character.IsHuman))
{ {
var positionWidget = GetLimbEditWidget($"{limb.Params.ID}_position", limb, widgetSize, Widget.Shape.Rectangle, initMethod: w => var positionWidget = GetLimbEditWidget($"{limb.Params.ID}_position", limb, widgetSize, Widget.Shape.Rectangle, initMethod: w =>
{ {
@@ -4812,17 +4821,20 @@ namespace Barotrauma.CharacterEditor
w.MouseHeld += dTime => w.MouseHeld += dTime =>
{ {
w.DrawPos = PlayerInput.MousePosition; w.DrawPos = PlayerInput.MousePosition;
var newRect = limb.ActiveSprite.SourceRect; Sprite activeSprite = limb.ActiveSprite;
var newRect = activeSprite.SourceRect;
newRect.Location = new Point( newRect.Location = new Point(
(int)((PlayerInput.MousePosition.X + halfSize - spriteSheetOffsetX) / spriteSheetZoom), (int)((PlayerInput.MousePosition.X + halfSize - spriteSheetOffsetX) / spriteSheetZoom),
(int)((PlayerInput.MousePosition.Y + halfSize - GetOffsetY(limb)) / spriteSheetZoom)); (int)((PlayerInput.MousePosition.Y + halfSize - GetOffsetY(activeSprite)) / spriteSheetZoom));
limb.ActiveSprite.SourceRect = newRect; activeSprite.SourceRect = newRect;
if (limb.DamagedSprite != null) if (limb.DamagedSprite != null)
{ {
limb.DamagedSprite.SourceRect = limb.ActiveSprite.SourceRect; limb.DamagedSprite.SourceRect = activeSprite.SourceRect;
} }
RecalculateOrigin(limb);
TryUpdateLimbParam(limb, "sourcerect", newRect); TryUpdateLimbParam(limb, "sourcerect", newRect);
var spritePos = new Vector2(spriteSheetOffsetX, GetOffsetY(activeSprite));
Vector2 newOrigin = (originWidget.DrawPos - spritePos - activeSprite.SourceRect.Location.ToVector2() * spriteSheetZoom) / spriteSheetZoom;
RecalculateOrigin(limb, newOrigin);
if (limbPairEditing) if (limbPairEditing)
{ {
UpdateOtherLimbs(limb, otherLimb => UpdateOtherLimbs(limb, otherLimb =>
@@ -4833,24 +4845,9 @@ namespace Barotrauma.CharacterEditor
otherLimb.DamagedSprite.SourceRect = newRect; otherLimb.DamagedSprite.SourceRect = newRect;
} }
TryUpdateLimbParam(otherLimb, "sourcerect", newRect); TryUpdateLimbParam(otherLimb, "sourcerect", newRect);
RecalculateOrigin(otherLimb); RecalculateOrigin(otherLimb, newOrigin);
}); });
}; };
void RecalculateOrigin(Limb l)
{
if (lockSpriteOrigin)
{
// Keeps the absolute origin unchanged. The relative origin will be recalculated.
var spritePos = new Vector2(spriteSheetOffsetX, GetOffsetY(l));
l.ActiveSprite.Origin = (originWidget.DrawPos - spritePos - l.ActiveSprite.SourceRect.Location.ToVector2() * spriteSheetZoom) / spriteSheetZoom;
TryUpdateLimbParam(l, "origin", l.ActiveSprite.RelativeOrigin);
}
else
{
// Keeps the relative origin unchanged. The absolute origin will be recalculated.
l.ActiveSprite.RelativeOrigin = l.ActiveSprite.RelativeOrigin;
}
}
}; };
w.PreDraw += (sb, dTime) => w.refresh(); w.PreDraw += (sb, dTime) => w.refresh();
}); });
@@ -4860,7 +4857,7 @@ namespace Barotrauma.CharacterEditor
} }
positionWidget.Draw(spriteBatch, deltaTime); positionWidget.Draw(spriteBatch, deltaTime);
} }
if (!lockSpriteSize) if (!lockSpriteSize && (limb.type != LimbType.Head || !character.IsHuman))
{ {
var sizeWidget = GetLimbEditWidget($"{limb.Params.ID}_size", limb, widgetSize, Widget.Shape.Rectangle, initMethod: w => var sizeWidget = GetLimbEditWidget($"{limb.Params.ID}_size", limb, widgetSize, Widget.Shape.Rectangle, initMethod: w =>
{ {
@@ -4869,22 +4866,24 @@ namespace Barotrauma.CharacterEditor
w.MouseHeld += dTime => w.MouseHeld += dTime =>
{ {
w.DrawPos = PlayerInput.MousePosition; w.DrawPos = PlayerInput.MousePosition;
var newRect = limb.ActiveSprite.SourceRect; Sprite activeSprite = limb.ActiveSprite;
float offset_y = limb.ActiveSprite.SourceRect.Y * spriteSheetZoom + GetOffsetY(limb); Rectangle newRect = activeSprite.SourceRect;
float offset_x = limb.ActiveSprite.SourceRect.X * spriteSheetZoom + spriteSheetOffsetX; float offset_y = activeSprite.SourceRect.Y * spriteSheetZoom + GetOffsetY(activeSprite);
float offset_x = activeSprite.SourceRect.X * spriteSheetZoom + spriteSheetOffsetX;
int width = (int)((PlayerInput.MousePosition.X - halfSize - offset_x) / spriteSheetZoom); int width = (int)((PlayerInput.MousePosition.X - halfSize - offset_x) / spriteSheetZoom);
int height = (int)((PlayerInput.MousePosition.Y - halfSize - offset_y) / spriteSheetZoom); int height = (int)((PlayerInput.MousePosition.Y - halfSize - offset_y) / spriteSheetZoom);
newRect.Size = new Point(width, height); newRect.Size = new Point(width, height);
limb.ActiveSprite.SourceRect = newRect; activeSprite.SourceRect = newRect;
limb.ActiveSprite.size = new Vector2(width, height); activeSprite.size = new Vector2(width, height);
Vector2 colliderSize = new Vector2(ConvertUnits.ToSimUnits(width), ConvertUnits.ToSimUnits(height));
if (recalculateCollider) if (recalculateCollider)
{ {
RecalculateCollider(limb); RecalculateCollider(limb, colliderSize);
} }
RecalculateOrigin(limb); RecalculateOrigin(limb);
if (limb.DamagedSprite != null) if (limb.DamagedSprite != null)
{ {
limb.DamagedSprite.SourceRect = limb.ActiveSprite.SourceRect; limb.DamagedSprite.SourceRect = activeSprite.SourceRect;
} }
TryUpdateLimbParam(limb, "sourcerect", newRect); TryUpdateLimbParam(limb, "sourcerect", newRect);
if (limbPairEditing) if (limbPairEditing)
@@ -4895,7 +4894,7 @@ namespace Barotrauma.CharacterEditor
RecalculateOrigin(otherLimb); RecalculateOrigin(otherLimb);
if (recalculateCollider) if (recalculateCollider)
{ {
RecalculateCollider(otherLimb); RecalculateCollider(otherLimb, colliderSize);
} }
if (otherLimb.DamagedSprite != null) if (otherLimb.DamagedSprite != null)
{ {
@@ -4904,29 +4903,6 @@ namespace Barotrauma.CharacterEditor
TryUpdateLimbParam(otherLimb, "sourcerect", newRect); TryUpdateLimbParam(otherLimb, "sourcerect", newRect);
}); });
}; };
void RecalculateCollider(Limb l)
{
// 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.9f;
l.body.SetSize(new Vector2(ConvertUnits.ToSimUnits(width), ConvertUnits.ToSimUnits(height)) * l.Scale * RagdollParams.TextureScale * multiplier);
TryUpdateLimbParam(l, "radius", ConvertUnits.ToDisplayUnits(l.body.radius / l.Params.Scale / 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 / l.Params.Scale / RagdollParams.LimbScale / RagdollParams.TextureScale));
}
void RecalculateOrigin(Limb l)
{
if (lockSpriteOrigin)
{
// Keeps the absolute origin unchanged. The relative origin will be recalculated.
l.ActiveSprite.Origin = l.ActiveSprite.Origin;
TryUpdateLimbParam(l, "origin", l.ActiveSprite.RelativeOrigin);
}
else
{
// Keeps the relative origin unchanged. The absolute origin will be recalculated.
l.ActiveSprite.RelativeOrigin = l.ActiveSprite.RelativeOrigin;
}
}
}; };
w.PreDraw += (sb, dTime) => w.refresh(); w.PreDraw += (sb, dTime) => w.refresh();
}); });
@@ -4955,22 +4931,48 @@ namespace Barotrauma.CharacterEditor
} }
offsetY += (int)(texture.Height * spriteSheetZoom); offsetY += (int)(texture.Height * spriteSheetZoom);
} }
}
int GetTextureHeight(Limb limb) private int GetTextureHeight(Sprite sprite)
{
int textureIndex = Textures.IndexOf(sprite.Texture);
int height = 0;
foreach (var t in Textures)
{ {
int textureIndex = Textures.IndexOf(limb.ActiveSprite.Texture); if (Textures.IndexOf(t) < textureIndex)
int height = 0;
foreach (var t in Textures)
{ {
if (Textures.IndexOf(t) < textureIndex) height += t.Height;
{
height += t.Height;
}
} }
return (int)(height * spriteSheetZoom);
} }
return (int)(height * spriteSheetZoom);
}
int GetOffsetY(Limb limb) => spriteSheetOffsetY + GetTextureHeight(limb); private int GetOffsetY(Sprite sprite) => spriteSheetOffsetY + GetTextureHeight(sprite);
private void RecalculateCollider(Limb l, Vector2 size)
{
// 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.9f;
l.body.SetSize(new Vector2(size.X, size.Y) * l.Scale * RagdollParams.TextureScale * multiplier);
TryUpdateLimbParam(l, "radius", ConvertUnits.ToDisplayUnits(l.body.radius / l.Params.Scale / 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 / l.Params.Scale / RagdollParams.LimbScale / RagdollParams.TextureScale));
}
private void RecalculateOrigin(Limb l, Vector2? newOrigin = null)
{
Sprite activeSprite = l.ActiveSprite;
if (lockSpriteOrigin)
{
// Keeps the absolute origin unchanged. The relative origin will be recalculated.
activeSprite.Origin = newOrigin ?? activeSprite.Origin;
TryUpdateLimbParam(l, "origin", activeSprite.RelativeOrigin);
}
else
{
// Keeps the relative origin unchanged. The absolute origin will be recalculated.
activeSprite.RelativeOrigin = activeSprite.RelativeOrigin;
}
} }
private void DrawSpritesheetJointEditor(SpriteBatch spriteBatch, float deltaTime, Limb limb, Vector2 limbScreenPos, float spriteRotation = 0) private void DrawSpritesheetJointEditor(SpriteBatch spriteBatch, float deltaTime, Limb limb, Vector2 limbScreenPos, float spriteRotation = 0)
@@ -5175,61 +5177,81 @@ namespace Barotrauma.CharacterEditor
case Keys.Left: case Keys.Left:
foreach (var limb in selectedLimbs) foreach (var limb in selectedLimbs)
{ {
// Can't edit human heads
if (limb.type == LimbType.Head && character.IsHuman) { continue; }
var newRect = limb.ActiveSprite.SourceRect; var newRect = limb.ActiveSprite.SourceRect;
if (PlayerInput.KeyDown(Keys.LeftControl)) bool resize = PlayerInput.KeyDown(Keys.LeftControl);
if (resize)
{ {
if (lockSpriteSize) { return; }
newRect.Width--; newRect.Width--;
} }
else else
{ {
if (lockSpritePosition) { return; }
newRect.X--; newRect.X--;
} }
UpdateSourceRect(limb, newRect); UpdateSourceRect(limb, newRect, resize);
} }
break; break;
case Keys.Right: case Keys.Right:
foreach (var limb in selectedLimbs) foreach (var limb in selectedLimbs)
{ {
// Can't edit human heads
if (limb.type == LimbType.Head && character.IsHuman) { continue; }
var newRect = limb.ActiveSprite.SourceRect; var newRect = limb.ActiveSprite.SourceRect;
if (PlayerInput.KeyDown(Keys.LeftControl)) bool resize = PlayerInput.KeyDown(Keys.LeftControl);
if (resize)
{ {
if (lockSpriteSize) { return; }
newRect.Width++; newRect.Width++;
} }
else else
{ {
if (lockSpritePosition) { return; }
newRect.X++; newRect.X++;
} }
UpdateSourceRect(limb, newRect); UpdateSourceRect(limb, newRect, resize);
} }
break; break;
case Keys.Down: case Keys.Down:
foreach (var limb in selectedLimbs) foreach (var limb in selectedLimbs)
{ {
// Can't edit human heads
if (limb.type == LimbType.Head && character.IsHuman) { continue; }
var newRect = limb.ActiveSprite.SourceRect; var newRect = limb.ActiveSprite.SourceRect;
if (PlayerInput.KeyDown(Keys.LeftControl)) bool resize = PlayerInput.KeyDown(Keys.LeftControl);
if (resize)
{ {
if (lockSpriteSize) { return; }
newRect.Height++; newRect.Height++;
} }
else else
{ {
if (lockSpritePosition) { return; }
newRect.Y++; newRect.Y++;
} }
UpdateSourceRect(limb, newRect); UpdateSourceRect(limb, newRect, resize);
} }
break; break;
case Keys.Up: case Keys.Up:
foreach (var limb in selectedLimbs) foreach (var limb in selectedLimbs)
{ {
// Can't edit human heads
if (limb.type == LimbType.Head && character.IsHuman) { continue; }
var newRect = limb.ActiveSprite.SourceRect; var newRect = limb.ActiveSprite.SourceRect;
if (PlayerInput.KeyDown(Keys.LeftControl)) bool resize = PlayerInput.KeyDown(Keys.LeftControl);
if (resize)
{ {
if (lockSpriteSize) { return; }
newRect.Height--; newRect.Height--;
} }
else else
{ {
if (lockSpritePosition) { return; }
newRect.Y--; newRect.Y--;
} }
UpdateSourceRect(limb, newRect); UpdateSourceRect(limb, newRect, resize);
} }
break; break;
} }
@@ -0,0 +1,571 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace Barotrauma
{
class EditorImageManager
{
private struct EditorImageContainer
{
public float Rotation;
public float Scale;
public Vector2 Position;
public string Path;
public float Opacity;
public EditorImage.DrawTargetType DrawTarget;
public EditorImage CreateImage()
{
return new EditorImage(Path, Position)
{
Position = Position,
Scale = Scale,
Opacity = Opacity,
Rotation = Rotation,
DrawTarget = DrawTarget
};
}
public static EditorImageContainer? Load(XElement element)
{
string path = element.GetAttributeString("path", "");
if (string.IsNullOrWhiteSpace(path)) { return null; }
Vector2 pos = element.GetAttributeVector2("pos", Vector2.Zero);
float scale = element.GetAttributeFloat("scale", 1f);
float rotation = element.GetAttributeFloat("rotation", 0f);
float opacity = element.GetAttributeFloat("opacity", 1f);
string drawTargetString = element.GetAttributeString("drawtarget", "");
if (!Enum.TryParse<EditorImage.DrawTargetType>(drawTargetString, out var drawTarget))
{
drawTarget = EditorImage.DrawTargetType.World;
}
return new EditorImageContainer
{
Path = path,
Rotation = rotation,
Opacity = opacity,
Position = pos,
Scale = scale,
DrawTarget = drawTarget
};
}
public static EditorImageContainer ImageToContainer(EditorImage img)
{
return new EditorImageContainer
{
Path = img.ImagePath,
Rotation = img.Rotation,
Position = img.Position,
Opacity = img.Opacity,
Scale = img.Scale,
DrawTarget = img.DrawTarget
};
}
public static XElement SerializeImage(EditorImageContainer image)
{
return new XElement("image",
new XAttribute("pos", XMLExtensions.Vector2ToString(image.Position)),
new XAttribute("rotation", image.Rotation),
new XAttribute("opacity", image.Opacity),
new XAttribute("path", image.Path),
new XAttribute("scale", image.Scale),
new XAttribute("drawtarget", image.DrawTarget.ToString()));
}
}
private readonly List<EditorImageContainer> PendingImages = new List<EditorImageContainer>();
public readonly List<EditorImage> Images = new List<EditorImage>();
private readonly List<EditorImage> screenImages = new List<EditorImage>(),
worldImages = new List<EditorImage>();
public bool EditorMode;
private string editModeText = "";
private Vector2 textSize = Vector2.Zero;
public void Save(XElement element)
{
XElement saveElement = new XElement("editorimages");
foreach (EditorImage image in Images)
{
EditorImageContainer container = EditorImageContainer.ImageToContainer(image);
saveElement.Add(EditorImageContainer.SerializeImage(container));
}
foreach (EditorImageContainer container in PendingImages)
{
saveElement.Add(EditorImageContainer.SerializeImage(container));
}
element.Add(saveElement);
}
public void Load(XElement element)
{
Clear(alsoPending: true);
foreach (XElement subElement in element.Elements())
{
EditorImageContainer? tempImage = EditorImageContainer.Load(subElement);
if (tempImage != null)
{
PendingImages.Add(tempImage.Value);
}
}
}
public void OnEditorSelected()
{
editModeText = TextManager.Get("SubEditor.ImageEditingMode");
textSize = GUI.LargeFont.MeasureString(editModeText);
TryLoadPendingImages();
}
private void TryLoadPendingImages()
{
if (PendingImages.Count == 0) { return; }
Clear(alsoPending: false);
foreach (EditorImageContainer pendingImage in PendingImages)
{
EditorImage img = pendingImage.CreateImage();
if (img.Image == null) { continue; }
Images.Add(img);
img.UpdateRectangle();
}
UpdateImageCategories();
PendingImages.Clear();
}
public void Clear(bool alsoPending = false)
{
foreach (EditorImage img in Images)
{
img.Image?.Dispose();
}
Images.Clear();
screenImages.Clear();
worldImages.Clear();
if (alsoPending)
{
PendingImages.Clear();
}
}
public void Update(float deltaTime)
{
if (!EditorMode) { return; }
foreach (EditorImage image in Images)
{
image.Update(deltaTime);
}
if (PlayerInput.PrimaryMouseButtonDown())
{
EditorImage? hover = Images.FirstOrDefault(img => img.IsMouseOn());
if (hover != null)
{
foreach (EditorImage image in Images)
{
image.Selected = false;
}
hover.Selected = true;
}
}
if (PlayerInput.KeyHit(Keys.Delete) || (PlayerInput.IsCtrlDown() && PlayerInput.KeyHit(Keys.D)))
{
Images.RemoveAll(img => img.Selected);
UpdateImageCategories();
}
if (PlayerInput.KeyHit(Keys.Space))
{
foreach (EditorImage image in Images)
{
if (image.Selected)
{
if (image.DrawTarget == EditorImage.DrawTargetType.World)
{
Vector2 pos = image.Position;
pos.Y = -pos.Y;
pos = Screen.Selected.Cam.WorldToScreen(pos);
if (PlayerInput.IsShiftDown())
{
pos = new Vector2(GameMain.GraphicsWidth / 2f, GameMain.GraphicsHeight / 2f);
}
image.Position = pos;
image.DrawTarget = EditorImage.DrawTargetType.Camera;
image.Scale *= Screen.Selected.Cam.Zoom;
image.UpdateRectangle();
}
else
{
Vector2 pos = Screen.Selected.Cam.ScreenToWorld(image.Position);
pos.Y = -pos.Y;
image.Position = pos;
image.DrawTarget = EditorImage.DrawTargetType.World;
image.Scale /= Screen.Selected.Cam.Zoom;
image.UpdateRectangle();
}
}
}
UpdateImageCategories();
}
MapEntity.DisableSelect = true;
}
private void UpdateImageCategories()
{
screenImages.Clear();
worldImages.Clear();
foreach (EditorImage image in Images)
{
switch (image.DrawTarget)
{
case EditorImage.DrawTargetType.World:
worldImages.Add(image);
break;
default:
screenImages.Add(image);
break;
}
}
}
public void CreateImageWizard(Vector2 positon)
{
string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
if (!Directory.Exists(home)) { return; }
FileSelection.OnFileSelected = file =>
{
Vector2 pos = Screen.Selected.Cam.ScreenToWorld(positon);
pos.Y = -pos.Y;
Images.Add(new EditorImage(file, pos) { DrawTarget = EditorImage.DrawTargetType.World });
UpdateImageCategories();
GameMain.Config.SaveNewPlayerConfig();
};
FileSelection.ClearFileTypeFilters();
FileSelection.AddFileTypeFilter("PNG", "*.png");
FileSelection.AddFileTypeFilter("JPEG", "*.jpg, *.jpeg");
FileSelection.AddFileTypeFilter("All files", "*.*");
FileSelection.SelectFileTypeFilter("*.png");
FileSelection.CurrentDirectory = home;
FileSelection.Open = true;
}
public void DrawEditing(SpriteBatch spriteBatch, Camera cam)
{
if (!EditorMode) { return; }
DrawImages(spriteBatch, cam);
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState);
Vector2 textPos = new Vector2(GameMain.GraphicsWidth / 2f - (textSize.X / 2f), GameMain.GraphicsHeight / 10f - (textSize.Y / 2f));
GUI.DrawString(spriteBatch, textPos, editModeText, GUI.Style.Yellow, Color.Black * 0.4f, 8, GUI.LargeFont);
spriteBatch.End();
}
public void Draw(SpriteBatch spriteBatch, Camera cam)
{
if (EditorMode) { return; }
DrawImages(spriteBatch, cam);
}
private void DrawImages(SpriteBatch spriteBatch, Camera cam)
{
if (screenImages.Count > 0)
{
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState);
foreach (EditorImage image in screenImages)
{
image.Draw(spriteBatch);
if (EditorMode) { image.DrawEditing(spriteBatch, cam); }
}
spriteBatch.End();
}
if (worldImages.Count > 0)
{
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, transformMatrix: cam.Transform);
foreach (EditorImage image in worldImages)
{
image.Draw(spriteBatch);
if (EditorMode) { image.DrawEditing(spriteBatch, cam); }
}
spriteBatch.End();
}
}
}
class EditorImage
{
public enum DrawTargetType
{
Camera,
World
}
public Texture2D? Image;
public string ImagePath;
public Vector2 Position;
public float Rotation;
public float Opacity = 1f;
public float Scale = 1f;
public DrawTargetType DrawTarget;
public bool Selected;
public Rectangle Bounds;
private float prevAngle;
private bool disableMove;
private bool isDragging;
private readonly Dictionary<string, Widget> widgets = new Dictionary<string, Widget>();
public EditorImage(string path, Vector2 pos)
{
Image = Sprite.LoadTexture(path, out Sprite _, compress: false);
ImagePath = path;
Position = pos;
UpdateRectangle();
}
public bool IsMouseOn() => Bounds.Contains(GetMousePos());
public Vector2 GetMousePos()
{
switch (DrawTarget)
{
case DrawTargetType.Camera:
return PlayerInput.MousePosition;
case DrawTargetType.World:
Vector2 pos = Screen.Selected.Cam.ScreenToWorld(PlayerInput.MousePosition);
pos.Y = -pos.Y;
return pos;
default:
return PlayerInput.MousePosition;
}
}
public void Update(float deltaTime)
{
if (!Selected) { return; }
if (widgets.Values.Any(w => w.IsSelected)) { return; }
if (PlayerInput.PrimaryMouseButtonDown() && !disableMove && IsMouseOn())
{
isDragging = true;
}
if (isDragging)
{
Camera cam = Screen.Selected.Cam;
if (PlayerInput.MouseSpeed != Vector2.Zero)
{
Vector2 mouseSpeed = PlayerInput.MouseSpeed;
if (DrawTarget == DrawTargetType.World)
{
mouseSpeed /= cam.Zoom;
}
Position += mouseSpeed;
UpdateRectangle();
}
}
if (PlayerInput.KeyDown(Keys.OemPlus) || PlayerInput.KeyDown(Keys.Up))
{
Opacity += 0.01f;
}
if (PlayerInput.KeyDown(Keys.OemMinus) || PlayerInput.KeyDown(Keys.Down))
{
Opacity -= 0.01f;
}
if (PlayerInput.KeyHit(Keys.D0))
{
Opacity = 1f;
}
Opacity = Math.Clamp(Opacity, 0, 1f);
if (!PlayerInput.PrimaryMouseButtonHeld())
{
isDragging = false;
}
}
private void DrawWidgets(SpriteBatch spriteBatch)
{
float widgetSize = Image == null ? 100f : Math.Max(Image.Width, Image.Height) / 2f;
int width = 3;
int size = 32;
if (DrawTarget == DrawTargetType.World)
{
width = Math.Max(width, (int) (width / Screen.Selected.Cam.Zoom));
}
Widget currentWidget = GetWidget("transform", size, width, widget =>
{
widget.MouseDown += () =>
{
widget.color = GUI.Style.Green;
prevAngle = Rotation;
disableMove = true;
};
widget.Deselected += () =>
{
widget.color = Color.Yellow;
disableMove = false;
};
widget.MouseHeld += (deltaTime) =>
{
Rotation = GetRotationAngle(Position) + (float) Math.PI / 2f;
float distance = Vector2.Distance(Position, GetMousePos());
Scale = Math.Abs(distance) / widgetSize;
if (PlayerInput.IsShiftDown())
{
const float rotationStep = (float) (Math.PI / 4f);
Rotation = (float) Math.Round(Rotation / rotationStep) * rotationStep;
}
if (PlayerInput.IsCtrlDown())
{
const float scaleStep = 0.1f;
Scale = (float) Math.Round(Scale / scaleStep) * scaleStep;
}
UpdateRectangle();
};
widget.PreUpdate += (deltaTime) =>
{
if (DrawTarget != DrawTargetType.World) { return; }
widget.DrawPos = new Vector2(widget.DrawPos.X, -widget.DrawPos.Y);
widget.DrawPos = Screen.Selected.Cam.WorldToScreen(widget.DrawPos);
};
widget.PostUpdate += (deltaTime) =>
{
if (DrawTarget != DrawTargetType.World) { return; }
widget.DrawPos = Screen.Selected.Cam.ScreenToWorld(widget.DrawPos);
widget.DrawPos = new Vector2(widget.DrawPos.X, -widget.DrawPos.Y);
};
widget.PreDraw += (sprtBtch, deltaTime) =>
{
widget.tooltip = $"Scale: {Math.Round(Scale, 2)}\n" +
$"Rotation: {(int) MathHelper.ToDegrees(Rotation)}";
float rotation = Rotation - (float) Math.PI / 2f;
widget.DrawPos = Position + new Vector2((float) Math.Cos(rotation), (float) Math.Sin(rotation)) * (Scale * widgetSize);
widget.Update(deltaTime);
};
});
currentWidget.Draw(spriteBatch, (float) Timing.Step);
GUI.DrawLine(spriteBatch, Position, currentWidget.DrawPos, GUI.Style.Green, width: width);
}
private float GetRotationAngle(Vector2 drawPosition)
{
Vector2 rotationVector = GetMousePos() - drawPosition;
rotationVector.Normalize();
double angle = Math.Atan2(MathHelper.ToRadians(rotationVector.Y), MathHelper.ToRadians(rotationVector.X));
if (angle < 0)
{
angle = Math.Abs(angle - prevAngle) < Math.Abs((angle + Math.PI * 2) - prevAngle) ? angle : angle + Math.PI * 2;
}
else if (angle > 0)
{
angle = Math.Abs(angle - prevAngle) < Math.Abs((angle - Math.PI * 2) - prevAngle) ? angle : angle - Math.PI * 2;
}
angle = MathHelper.Clamp((float) angle, -((float) Math.PI * 2), (float) Math.PI * 2);
prevAngle = (float) angle;
return (float) angle;
}
private Widget GetWidget(string id, int size, float thickness = 1f, Action<Widget>? initMethod = null)
{
if (!widgets.TryGetValue(id, out Widget? widget))
{
widget = new Widget(id, size, Widget.Shape.Rectangle)
{
color = Color.Yellow,
RequireMouseOn = false
};
widgets.Add(id, widget);
initMethod?.Invoke(widget);
}
widget.size = size;
widget.thickness = thickness;
return widget;
}
public void UpdateRectangle()
{
if (Image == null)
{
Bounds = new Rectangle((int) Position.X, (int) Position.Y, 512, 512);
return;
}
Vector2 size = new Vector2(Image.Width * Scale, Image.Height * Scale);
Bounds = new Rectangle((Position - size / 2f).ToPoint(), size.ToPoint());
}
public void Draw(SpriteBatch spriteBatch)
{
if (Image == null) { return; }
spriteBatch.Draw(Image, Position, null, Color.White * Opacity, Rotation, new Vector2(Image.Width / 2f, Image.Height / 2f), scale: Scale, SpriteEffects.None, 0f);
}
public void DrawEditing(SpriteBatch spriteBatch, Camera cam)
{
Rectangle bounds = Bounds;
int width = 4;
if (DrawTarget == DrawTargetType.World)
{
width = (int) (width / cam.Zoom);
}
GUI.DrawRectangle(spriteBatch, bounds, Selected ? GUI.Style.Red : GUI.Style.Green, thickness: width);
if (Selected)
{
DrawWidgets(spriteBatch);
}
}
}
}
@@ -399,7 +399,7 @@ namespace Barotrauma
else else
{ {
newNode = new CustomNode(subElement.Name.ToString()) { Position = new Vector2(ident, 0), ID = CreateID() }; newNode = new CustomNode(subElement.Name.ToString()) { Position = new Vector2(ident, 0), ID = CreateID() };
foreach (XAttribute attribute in subElement.Attributes()) foreach (XAttribute attribute in subElement.Attributes().Where(attribute => !attribute.ToString().StartsWith("_")))
{ {
newNode.Connections.Add(new NodeConnection(newNode, NodeConnectionType.Value, attribute.Name.ToString(), typeof(string))); newNode.Connections.Add(new NodeConnection(newNode, NodeConnectionType.Value, attribute.Name.ToString(), typeof(string)));
} }
@@ -525,6 +525,7 @@ namespace Barotrauma
public override void Select() public override void Select()
{ {
GUI.PreventPauseMenuToggle = false;
projectName = TextManager.Get("EventEditor.Unnamed"); projectName = TextManager.Get("EventEditor.Unnamed");
base.Select(); base.Select();
} }
@@ -77,9 +77,8 @@ namespace Barotrauma
} }
if (Character.Controlled?.Inventory != null) if (Character.Controlled?.Inventory != null)
{ {
foreach (Item item in Character.Controlled.Inventory.Items) foreach (Item item in Character.Controlled.Inventory.AllItems)
{ {
if (item == null) { continue; }
if (Character.Controlled.HasEquippedItem(item)) if (Character.Controlled.HasEquippedItem(item))
{ {
item.AddToGUIUpdateList(); item.AddToGUIUpdateList();
@@ -249,6 +248,8 @@ namespace Barotrauma
} }
spriteBatch.End(); spriteBatch.End();
Level.Loaded?.DrawFront(spriteBatch, cam);
//draw the rendertarget and particles that are only supposed to be drawn in water into renderTargetWater //draw the rendertarget and particles that are only supposed to be drawn in water into renderTargetWater
graphics.SetRenderTarget(renderTargetWater); graphics.SetRenderTarget(renderTargetWater);
@@ -317,10 +318,8 @@ namespace Barotrauma
{ {
c.DrawFront(spriteBatch, cam); c.DrawFront(spriteBatch, cam);
} }
if (Level.Loaded != null)
{ Level.Loaded?.DrawDebugOverlay(spriteBatch, cam);
Level.Loaded.DrawFront(spriteBatch, cam);
}
if (GameMain.DebugDraw) if (GameMain.DebugDraw)
{ {
MapEntity.mapEntityList.ForEach(me => me.AiTarget?.Draw(spriteBatch)); MapEntity.mapEntityList.ForEach(me => me.AiTarget?.Draw(spriteBatch));
@@ -374,7 +373,10 @@ namespace Barotrauma
{ {
BlurStrength = Character.Controlled.BlurStrength * 0.005f; BlurStrength = Character.Controlled.BlurStrength * 0.005f;
DistortStrength = Character.Controlled.DistortStrength; DistortStrength = Character.Controlled.DistortStrength;
chromaticAberrationStrength -= Vector3.One * Character.Controlled.RadialDistortStrength; if (GameMain.Config.EnableRadialDistortion)
{
chromaticAberrationStrength -= Vector3.One * Character.Controlled.RadialDistortStrength;
}
chromaticAberrationStrength += new Vector3(-0.03f, -0.015f, 0.0f) * Character.Controlled.ChromaticAberrationStrength; chromaticAberrationStrength += new Vector3(-0.03f, -0.015f, 0.0f) * Character.Controlled.ChromaticAberrationStrength;
} }
else else
@@ -438,8 +440,8 @@ namespace Barotrauma
if (!PlayerInput.PrimaryMouseButtonHeld()) if (!PlayerInput.PrimaryMouseButtonHeld())
{ {
Inventory.draggingSlot = null; Inventory.DraggingSlot = null;
Inventory.draggingItem = null; Inventory.DraggingItems.Clear();
} }
} }
} }
@@ -714,8 +714,9 @@ namespace Barotrauma
if (Level.Loaded != null) if (Level.Loaded != null)
{ {
Level.Loaded.DrawBack(graphics, spriteBatch, cam); Level.Loaded.DrawBack(graphics, spriteBatch, cam);
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, SamplerState.LinearWrap, DepthStencilState.DepthRead, transformMatrix: cam.Transform);
Level.Loaded.DrawFront(spriteBatch, cam); Level.Loaded.DrawFront(spriteBatch, cam);
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, SamplerState.LinearWrap, DepthStencilState.DepthRead, transformMatrix: cam.Transform);
Level.Loaded.DrawDebugOverlay(spriteBatch, cam);
Submarine.Draw(spriteBatch, false); Submarine.Draw(spriteBatch, false);
Submarine.DrawFront(spriteBatch); Submarine.DrawFront(spriteBatch);
Submarine.DrawDamageable(spriteBatch, null); Submarine.DrawDamageable(spriteBatch, null);
@@ -817,6 +818,13 @@ namespace Barotrauma
public override void Update(double deltaTime) public override void Update(double deltaTime)
{ {
if (lightingEnabled.Selected)
{
foreach (Item item in Item.ItemList)
{
item?.GetComponent<Items.Components.LightComponent>()?.Update((float)deltaTime, cam);
}
}
GameMain.LightManager?.Update((float)deltaTime); GameMain.LightManager?.Update((float)deltaTime);
pointerLightSource.Position = cam.ScreenToWorld(PlayerInput.MousePosition); pointerLightSource.Position = cam.ScreenToWorld(PlayerInput.MousePosition);
@@ -886,16 +894,16 @@ namespace Barotrauma
{ {
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
{ {
string id = element.GetAttributeString("identifier", null) ?? element.Name.ToString(); string id = subElement.GetAttributeString("identifier", null) ?? subElement.Name.ToString();
if (!id.Equals(genParams.Name, StringComparison.OrdinalIgnoreCase)) { continue; } if (!id.Equals(genParams.Name, StringComparison.OrdinalIgnoreCase)) { continue; }
SerializableProperty.SerializeProperties(genParams, element, true); genParams.Save(subElement);
} }
} }
else else
{ {
string id = element.GetAttributeString("identifier", null) ?? element.Name.ToString(); string id = element.GetAttributeString("identifier", null) ?? element.Name.ToString();
if (!id.Equals(genParams.Name, StringComparison.OrdinalIgnoreCase)) { continue; } if (!id.Equals(genParams.Name, StringComparison.OrdinalIgnoreCase)) { continue; }
SerializableProperty.SerializeProperties(genParams, element, true); genParams.Save(element);
} }
break; break;
} }
@@ -679,7 +679,7 @@ namespace Barotrauma
} }
#endregion #endregion
public void QuickStart(bool fixedSeed = false) public void QuickStart(bool fixedSeed = false, string sub = null)
{ {
if (fixedSeed) if (fixedSeed)
{ {
@@ -688,7 +688,7 @@ namespace Barotrauma
} }
SubmarineInfo selectedSub = null; SubmarineInfo selectedSub = null;
string subName = GameMain.Config.QuickStartSubmarineName; string subName = sub ?? 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);
@@ -834,6 +834,39 @@ namespace Barotrauma
return true; return true;
} }
private void TryStartServer()
{
if (SubmarineInfo.SavedSubmarines.Any(s => s.CalculatingHash))
{
var waitBox = new GUIMessageBox(TextManager.Get("pleasewait"), TextManager.Get("waitforsubmarinehashcalculations"), new string[] { TextManager.Get("cancel") });
var waitCoroutine = CoroutineManager.StartCoroutine(WaitForSubmarineHashCalculations(waitBox), "WaitForSubmarineHashCalculations");
waitBox.Buttons[0].OnClicked += (btn, userdata) =>
{
CoroutineManager.StopCoroutines(waitCoroutine);
return true;
};
}
else
{
StartServer();
}
}
private IEnumerable<object> WaitForSubmarineHashCalculations(GUIMessageBox messageBox)
{
string originalText = messageBox.Text.Text;
int doneCount = 0;
do
{
doneCount = SubmarineInfo.SavedSubmarines.Count(s => !s.CalculatingHash);
messageBox.Text.Text = originalText + $" ({doneCount}/{SubmarineInfo.SavedSubmarines.Count()})";
yield return CoroutineStatus.Running;
} while (doneCount < SubmarineInfo.SavedSubmarines.Count());
messageBox.Close();
StartServer();
yield return CoroutineStatus.Success;
}
private void StartServer() private void StartServer()
{ {
string name = serverNameBox.Text; string name = serverNameBox.Text;
@@ -1095,12 +1128,13 @@ namespace Barotrauma
StartNewGame = StartGame StartNewGame = StartGame
}; };
var startButtonContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), innerNewGame.RectTransform, Anchor.Center), style: null); var startButtonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), innerNewGame.RectTransform, Anchor.Center), isHorizontal: true, childAnchor: Anchor.BottomRight);
campaignSetupUI.StartButton.RectTransform.Parent = startButtonContainer.RectTransform; campaignSetupUI.StartButton.RectTransform.Parent = startButtonContainer.RectTransform;
campaignSetupUI.StartButton.RectTransform.MinSize = new Point( campaignSetupUI.StartButton.RectTransform.MinSize = new Point(
(int)(campaignSetupUI.StartButton.TextBlock.TextSize.X * 1.5f), (int)(campaignSetupUI.StartButton.TextBlock.TextSize.X * 1.5f),
campaignSetupUI.StartButton.RectTransform.MinSize.Y); campaignSetupUI.StartButton.RectTransform.MinSize.Y);
startButtonContainer.RectTransform.MinSize = new Point(0, campaignSetupUI.StartButton.RectTransform.MinSize.Y); startButtonContainer.RectTransform.MinSize = new Point(0, campaignSetupUI.StartButton.RectTransform.MinSize.Y);
campaignSetupUI.InitialMoneyText.RectTransform.Parent = startButtonContainer.RectTransform;
} }
private void CreateHostServerFields() private void CreateHostServerFields()
@@ -1340,7 +1374,7 @@ namespace Barotrauma
new string[] { TextManager.Get("yes"), TextManager.Get("no") }); new string[] { TextManager.Get("yes"), TextManager.Get("no") });
msgBox.Buttons[0].OnClicked += (_, __) => msgBox.Buttons[0].OnClicked += (_, __) =>
{ {
StartServer(); TryStartServer();
msgBox.Close(); msgBox.Close();
return true; return true;
}; };
@@ -1348,7 +1382,7 @@ namespace Barotrauma
} }
else else
{ {
StartServer(); TryStartServer();
} }
return true; return true;

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