Unstable v0.1300.0.0 (February 19th 2021)

This commit is contained in:
Joonas Rikkonen
2021-02-25 13:44:23 +02:00
parent b772654326
commit 24cbef485a
441 changed files with 21343 additions and 8562 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)
@@ -11,6 +11,9 @@ namespace Barotrauma
{ {
if (!ShowAITargets) { return; } if (!ShowAITargets) { return; }
var pos = new Vector2(WorldPosition.X, -WorldPosition.Y); var pos = new Vector2(WorldPosition.X, -WorldPosition.Y);
float thickness = 1 / Screen.Selected.Cam.Zoom;
float offset = MathUtils.VectorToAngle(new Vector2(sectorDir.X, -sectorDir.Y)) - (sectorRad / 2f);
if (soundRange > 0.0f) if (soundRange > 0.0f)
{ {
Color color; Color color;
@@ -26,8 +29,16 @@ namespace Barotrauma
{ {
color = Color.OrangeRed; color = Color.OrangeRed;
} }
ShapeExtensions.DrawCircle(spriteBatch, pos, SoundRange, 100, color, thickness: 1 / Screen.Selected.Cam.Zoom);
ShapeExtensions.DrawCircle(spriteBatch, pos, 3, 8, color, thickness: 2 / Screen.Selected.Cam.Zoom); if (sectorRad < MathHelper.TwoPi)
{
spriteBatch.DrawSector(pos, SoundRange, sectorRad, 100, color, offset: offset, thickness: thickness);
}
else
{
spriteBatch.DrawCircle(pos, SoundRange, 100, color, thickness: thickness);
}
spriteBatch.DrawCircle(pos, 3, 8, color, thickness: 2 / Screen.Selected.Cam.Zoom);
GUI.DrawLine(spriteBatch, pos, pos + Vector2.UnitY * SoundRange, color, width: (int)(1 / Screen.Selected.Cam.Zoom) + 1); GUI.DrawLine(spriteBatch, pos, pos + Vector2.UnitY * SoundRange, color, width: (int)(1 / Screen.Selected.Cam.Zoom) + 1);
} }
if (sightRange > 0.0f) if (sightRange > 0.0f)
@@ -47,7 +58,14 @@ namespace Barotrauma
// disable the indicators for structures and hulls, because they clutter the debug view // disable the indicators for structures and hulls, because they clutter the debug view
return; return;
} }
ShapeExtensions.DrawCircle(spriteBatch, pos, SightRange, 100, color, thickness: 1 / Screen.Selected.Cam.Zoom); if (sectorRad < MathHelper.TwoPi)
{
spriteBatch.DrawSector(pos, SightRange, sectorRad, 100, color, offset: offset, thickness: thickness);
}
else
{
spriteBatch.DrawCircle(pos, SightRange, 100, color, thickness: thickness);
}
ShapeExtensions.DrawCircle(spriteBatch, pos, 6, 8, color, thickness: 2 / Screen.Selected.Cam.Zoom); ShapeExtensions.DrawCircle(spriteBatch, pos, 6, 8, color, thickness: 2 / Screen.Selected.Cam.Zoom);
GUI.DrawLine(spriteBatch, pos, pos + Vector2.UnitY * SightRange, color, width: (int)(1 / Screen.Selected.Cam.Zoom) + 1); GUI.DrawLine(spriteBatch, pos, pos + Vector2.UnitY * SightRange, color, width: (int)(1 / Screen.Selected.Cam.Zoom) + 1);
} }
@@ -1,22 +1,12 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using FarseerPhysics; using FarseerPhysics;
using System;
using System.Linq;
namespace Barotrauma namespace Barotrauma
{ {
partial class HumanAIController : AIController partial class HumanAIController : AIController
{ {
public static bool debugai;
partial void InitProjSpecific()
{
/*if (GameMain.GameSession != null && GameMain.GameSession.CrewManager != null)
{
CurrentOrder = Order.GetPrefab("dismissed");
objectiveManager.SetOrder(CurrentOrder, "", null);
GameMain.GameSession.CrewManager.SetCharacterOrder(Character, CurrentOrder, null, null);
}*/
}
public override void DebugDraw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch) public override void DebugDraw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch)
{ {
if (Character == Character.Controlled) { return; } if (Character == Character.Controlled) { return; }
@@ -24,6 +14,7 @@ namespace Barotrauma
Vector2 pos = Character.WorldPosition; Vector2 pos = Character.WorldPosition;
pos.Y = -pos.Y; pos.Y = -pos.Y;
Vector2 textOffset = new Vector2(-40, -160); Vector2 textOffset = new Vector2(-40, -160);
textOffset.Y -= Math.Max(ObjectiveManager.CurrentOrders.Count - 1, 0) * 20;
if (SelectedAiTarget?.Entity != null) if (SelectedAiTarget?.Entity != null)
{ {
@@ -31,61 +22,57 @@ namespace Barotrauma
//GUI.DrawString(spriteBatch, pos + textOffset, $"AI TARGET: {SelectedAiTarget.Entity.ToString()}", Color.White, Color.Black); //GUI.DrawString(spriteBatch, pos + textOffset, $"AI TARGET: {SelectedAiTarget.Entity.ToString()}", Color.White, Color.Black);
} }
GUI.DrawString(spriteBatch, pos + textOffset, Character.Name, Color.White, Color.Black); Vector2 stringDrawPos = pos + textOffset;
GUI.DrawString(spriteBatch, stringDrawPos, Character.Name, Color.White, Color.Black);
if (ObjectiveManager != null) var currentOrder = ObjectiveManager.CurrentOrder;
if (ObjectiveManager.CurrentOrders.Any())
{ {
var currentOrder = ObjectiveManager.CurrentOrder; var currentOrders = ObjectiveManager.CurrentOrders;
if (currentOrder != null) currentOrders.Sort((x, y) => y.ManualPriority.CompareTo(x.ManualPriority));
for (int i = 0; i < currentOrders.Count; i++)
{ {
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 20), $"ORDER: {currentOrder.DebugTag} ({currentOrder.Priority.FormatZeroDecimal()})", Color.White, Color.Black); stringDrawPos += new Vector2(0, 20);
var order = currentOrders[i];
GUI.DrawString(spriteBatch, stringDrawPos, $"ORDER {i + 1}: {order.Objective.DebugTag} ({order.Objective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
} }
else if (ObjectiveManager.WaitTimer > 0) }
else if (ObjectiveManager.WaitTimer > 0)
{
stringDrawPos += new Vector2(0, 20);
GUI.DrawString(spriteBatch, stringDrawPos - textOffset, $"Waiting... {ObjectiveManager.WaitTimer.FormatZeroDecimal()}", Color.White, Color.Black);
}
var currentObjective = ObjectiveManager.CurrentObjective;
if (currentObjective != null)
{
int offset = currentOrder != null ? 20 + ((ObjectiveManager.CurrentOrders.Count - 1) * 20) : 0;
if (currentOrder == null || currentOrder.Priority <= 0)
{ {
GUI.DrawString(spriteBatch, pos + new Vector2(0, 20), $"Waiting... {ObjectiveManager.WaitTimer.FormatZeroDecimal()}", Color.White, Color.Black); stringDrawPos += new Vector2(0, 20);
GUI.DrawString(spriteBatch, stringDrawPos, $"MAIN OBJECTIVE: {currentObjective.DebugTag} ({currentObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
} }
var currentObjective = ObjectiveManager.CurrentObjective; var subObjective = currentObjective.CurrentSubObjective;
if (currentObjective != null) if (subObjective != null)
{ {
int offset = currentOrder != null ? 20 : 0; stringDrawPos += new Vector2(0, 20);
if (currentOrder == null || currentOrder.Priority <= 0) GUI.DrawString(spriteBatch, stringDrawPos, $"SUBOBJECTIVE: {subObjective.DebugTag} ({subObjective.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);
}
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++) var activeObjective = ObjectiveManager.GetActiveObjective();
if (activeObjective != null)
{ {
var objective = ObjectiveManager.Objectives[i]; stringDrawPos += new Vector2(0, 20);
int offsetMultiplier; GUI.DrawString(spriteBatch, stringDrawPos, $"ACTIVE OBJECTIVE: {activeObjective.DebugTag} ({activeObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
if (ObjectiveManager.CurrentOrder == null)
{
if (i == 0)
{
continue;
}
else
{
offsetMultiplier = i - 1;
}
}
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);
} }
} }
Vector2 objectiveStringDrawPos = stringDrawPos + new Vector2(120, 40);
for (int i = 0; i < ObjectiveManager.Objectives.Count; i++)
{
var objective = ObjectiveManager.Objectives[i];
GUI.DrawString(spriteBatch, objectiveStringDrawPos, $"{objective.DebugTag} ({objective.Priority.FormatZeroDecimal()})", Color.White, Color.Black * 0.5f);
objectiveStringDrawPos += new Vector2(0, 18);
}
if (steeringManager is IndoorsSteeringManager pathSteering) if (steeringManager is IndoorsSteeringManager pathSteering)
{ {
var path = pathSteering.CurrentPath; var path = pathSteering.CurrentPath;
@@ -111,13 +98,21 @@ namespace Barotrauma
new Vector2(path.CurrentNode.DrawPosition.X, -path.CurrentNode.DrawPosition.Y), new Vector2(path.CurrentNode.DrawPosition.X, -path.CurrentNode.DrawPosition.Y),
Color.BlueViolet, 0, 3); Color.BlueViolet, 0, 3);
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 100), "Path cost: " + path.Cost.FormatZeroDecimal(), Color.White, Color.Black * 0.5f); GUI.DrawString(spriteBatch, stringDrawPos + new Vector2(0, 40), "Path cost: " + path.Cost.FormatZeroDecimal(), Color.White, Color.Black * 0.5f);
} }
} }
} }
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;
@@ -520,7 +520,7 @@ namespace Barotrauma
foreach (Character c in CharacterList) foreach (Character c in CharacterList)
{ {
if (!CanInteractWith(c, checkVisibility: false)) continue; if (!CanInteractWith(c, checkVisibility: false) || (c.AnimController?.SimplePhysicsEnabled ?? true)) { continue; }
float dist = Vector2.DistanceSquared(mouseSimPos, c.SimPosition); float dist = Vector2.DistanceSquared(mouseSimPos, c.SimPosition);
if (dist < maxDist * maxDist && (closestCharacter == null || dist < closestDist)) if (dist < maxDist * maxDist && (closestCharacter == null || dist < closestDist))
@@ -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);
@@ -634,9 +634,9 @@ namespace Barotrauma
} }
} }
partial void SetOrderProjSpecific(Order order, string orderOption) partial void SetOrderProjSpecific(Order order, string orderOption, int priority)
{ {
GameMain.GameSession?.CrewManager?.AddCurrentOrderIcon(this, order, orderOption); GameMain.GameSession?.CrewManager?.AddCurrentOrderIcon(this, order, orderOption, priority);
} }
public static void AddAllToGUIUpdateList() public static void AddAllToGUIUpdateList()
@@ -686,7 +686,7 @@ namespace Barotrauma
public virtual void DrawFront(SpriteBatch spriteBatch, Camera cam) public virtual void DrawFront(SpriteBatch spriteBatch, Camera cam)
{ {
if (!Enabled || InvisibleTimer > 0.0f) { return; } if (!Enabled || InvisibleTimer > 0.0f || (AnimController?.SimplePhysicsEnabled ?? true)) { return; }
if (GameMain.DebugDraw) if (GameMain.DebugDraw)
{ {
@@ -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)
{ {
@@ -815,7 +815,7 @@ namespace Barotrauma
iconPos.Y = -iconPos.Y; iconPos.Y = -iconPos.Y;
nameColor = iconStyle.Color; nameColor = iconStyle.Color;
var icon = iconStyle.Sprites[GUIComponent.ComponentState.None].First(); var icon = iconStyle.Sprites[GUIComponent.ComponentState.None].First();
float iconScale = 30.0f / icon.Sprite.size.X / cam.Zoom; float iconScale = (30.0f / icon.Sprite.size.X / cam.Zoom) * GUI.Scale;
icon.Sprite.Draw(spriteBatch, iconPos + new Vector2(-35.0f, -25.0f), iconStyle.Color * hudInfoAlpha, scale: iconScale); icon.Sprite.Draw(spriteBatch, iconPos + new Vector2(-35.0f, -25.0f), iconStyle.Color * hudInfoAlpha, scale: iconScale);
} }
} }
@@ -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;
} }
@@ -1,4 +1,5 @@
using Barotrauma.Items.Components; using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using FarseerPhysics; using FarseerPhysics;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
@@ -57,7 +58,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 +77,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)
{ {
@@ -130,17 +131,6 @@ namespace Barotrauma
{ {
character.Inventory.ClearSubInventories(); character.Inventory.ClearSubInventories();
} }
for (int i = 0; i < character.Inventory.Items.Length - 1; i++)
{
var item = character.Inventory.Items[i];
if (item == null || character.Inventory.SlotTypes[i] == InvSlotType.Any) continue;
foreach (ItemComponent ic in item.Components)
{
if (ic.DrawHudWhenEquipped) ic.UpdateHUD(character, deltaTime, cam);
}
}
} }
if (character.IsHumanoid && character.SelectedCharacter != null && character.SelectedCharacter.Inventory != null) if (character.IsHumanoid && character.SelectedCharacter != null && character.SelectedCharacter.Inventory != null)
@@ -206,22 +196,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 (character.GetCurrentOrderWithTopPriority()?.Order is Order currentOrder && DrawIcon(currentOrder))
{ {
DrawOrderIndicator(spriteBatch, cam, character, character.CurrentOrder, 1.0f); DrawOrderIndicator(spriteBatch, cam, 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 +230,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 +243,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.None(it => it?.GetComponent<Sprayer>() != null)))
{ {
if (character.FocusedCharacter != null && character.FocusedCharacter.CanBeSelected) if (character.FocusedCharacter != null && character.FocusedCharacter.CanBeSelected)
{ {
@@ -281,7 +280,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 +290,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 +306,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 +340,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 +353,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 +430,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 +456,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 +495,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 +518,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,48 +152,37 @@ 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);
} }
} }
private void GetDisguisedSprites(IdCard idCard) private void GetDisguisedSprites(IdCard idCard)
{ {
if (idCard.Item.Tags == string.Empty) return;
if (idCard.StoredJobPrefab == null || idCard.StoredPortrait == null) if (idCard.StoredJobPrefab == null || idCard.StoredPortrait == null)
{ {
string[] readTags = idCard.Item.Tags.Split(','); string[] readTags = idCard.Item.Tags.Split(',');
if (readTags.Length == 0) return;
if (idCard.StoredJobPrefab == null) if (idCard.StoredJobPrefab == null)
{ {
string jobIdTag = readTags.First(s => s.StartsWith("jobid:")); string jobIdTag = readTags.FirstOrDefault(s => s.StartsWith("jobid:"));
if (jobIdTag != string.Empty && jobIdTag.Length > 6) if (jobIdTag != null && jobIdTag.Length > 6)
{ {
string jobId = jobIdTag.Substring(6); string jobId = jobIdTag.Substring(6);
if (jobId != string.Empty) if (jobId != string.Empty)
@@ -292,7 +292,7 @@ namespace Barotrauma
break; break;
case ServerNetObject.ENTITY_EVENT: case ServerNetObject.ENTITY_EVENT:
int eventType = msg.ReadRangedInteger(0, 5); int eventType = msg.ReadRangedInteger(0, 6);
switch (eventType) switch (eventType)
{ {
case 0: //NetEntityEvent.Type.InventoryState case 0: //NetEntityEvent.Type.InventoryState
@@ -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
@@ -390,6 +390,19 @@ namespace Barotrauma
byte campaignInteractionType = msg.ReadByte(); byte campaignInteractionType = msg.ReadByte();
(GameMain.GameSession?.GameMode as CampaignMode)?.AssignNPCMenuInteraction(this, (CampaignMode.InteractionType)campaignInteractionType); (GameMain.GameSession?.GameMode as CampaignMode)?.AssignNPCMenuInteraction(this, (CampaignMode.InteractionType)campaignInteractionType);
break; break;
case 6: //NetEntityEvent.Type.ObjectiveManagerOrderState
bool properData = msg.ReadBoolean();
if (!properData) { break; }
int orderIndex = msg.ReadRangedInteger(0, Order.PrefabList.Count);
var orderPrefab = Order.PrefabList[orderIndex];
string option = null;
if (orderPrefab.HasOptions)
{
int optionIndex = msg.ReadRangedInteger(0, orderPrefab.Options.Length);
option = orderPrefab.Options[optionIndex];
}
GameMain.GameSession.CrewManager.SetHighlightedOrderIcon(this, orderPrefab.Identifier, option);
break;
} }
msg.ReadPadBits(); msg.ReadPadBits();
break; break;
@@ -434,20 +447,22 @@ 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)
{ {
(GameMain.GameSession.GameMode as CampaignMode)?.AssignNPCMenuInteraction(character, character.CampaignInteractionType); (GameMain.GameSession.GameMode as CampaignMode)?.AssignNPCMenuInteraction(character, character.CampaignInteractionType);
} }
// Check if the character has a current order // Check if the character has current orders
if (inc.ReadBoolean()) int orderCount = inc.ReadByte();
for (int i = 0; i < orderCount; i++)
{ {
int orderPrefabIndex = inc.ReadByte(); int orderPrefabIndex = inc.ReadByte();
Entity targetEntity = FindEntityByID(inc.ReadUInt16()); Entity targetEntity = FindEntityByID(inc.ReadUInt16());
Character orderGiver = inc.ReadBoolean() ? FindEntityByID(inc.ReadUInt16()) as Character : null; Character orderGiver = inc.ReadBoolean() ? FindEntityByID(inc.ReadUInt16()) as Character : null;
int orderOptionIndex = inc.ReadByte(); int orderOptionIndex = inc.ReadByte();
int orderPriority = inc.ReadByte();
OrderTarget targetPosition = null; OrderTarget targetPosition = null;
if (inc.ReadBoolean()) if (inc.ReadBoolean())
{ {
@@ -468,7 +483,7 @@ namespace Barotrauma
new Order(orderPrefab, targetPosition, orderGiver: orderGiver); new Order(orderPrefab, targetPosition, orderGiver: orderGiver);
character.SetOrder(order, character.SetOrder(order,
orderOptionIndex >= 0 && orderOptionIndex < orderPrefab.Options.Length ? orderPrefab.Options[orderOptionIndex] : null, orderOptionIndex >= 0 && orderOptionIndex < orderPrefab.Options.Length ? orderPrefab.Options[orderOptionIndex] : null,
orderGiver, speak: false); orderPriority, orderGiver, speak: false);
} }
else else
{ {
@@ -487,7 +502,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.TeamID != CharacterTeamType.None && !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:
@@ -668,12 +668,17 @@ namespace Barotrauma
bloodParticleTimer -= deltaTime * (affliction.Strength / 10.0f); bloodParticleTimer -= deltaTime * (affliction.Strength / 10.0f);
if (bloodParticleTimer <= 0.0f) if (bloodParticleTimer <= 0.0f)
{ {
var emitter = Character.BloodEmitters.FirstOrDefault();
float particleMinScale = emitter != null ? emitter.Prefab.ScaleMin : 0.5f;
float particleMaxScale = emitter != null ? emitter.Prefab.ScaleMax : 1;
float severity = Math.Min(affliction.Strength / affliction.Prefab.MaxStrength * Character.Params.BleedParticleMultiplier, 1);
float bloodParticleSize = MathHelper.Lerp(particleMinScale, particleMaxScale, severity);
bool inWater = Character.AnimController.InWater; bool inWater = Character.AnimController.InWater;
float bloodParticleSize = MathHelper.Lerp(0.5f, 1.0f, affliction.Strength / 100.0f);
if (!inWater) if (!inWater)
{ {
bloodParticleSize *= 2.0f; bloodParticleSize *= 2.0f;
} }
var blood = GameMain.ParticleManager.CreateParticle( var blood = GameMain.ParticleManager.CreateParticle(
inWater ? Character.Params.BleedParticleWater : Character.Params.BleedParticleAir, inWater ? Character.Params.BleedParticleWater : Character.Params.BleedParticleAir,
targetLimb.WorldPosition, Rand.Vector(affliction.Strength), 0.0f, Character.AnimController.CurrentHull); targetLimb.WorldPosition, Rand.Vector(affliction.Strength), 0.0f, Character.AnimController.CurrentHull);
@@ -682,7 +687,7 @@ namespace Barotrauma
{ {
blood.Size *= bloodParticleSize; blood.Size *= bloodParticleSize;
} }
bloodParticleTimer = 1.0f; bloodParticleTimer = MathHelper.Lerp(2, 0.5f, severity);
} }
} }
@@ -912,7 +917,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 +1637,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 +1657,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 +1801,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);
}
} }
} }
@@ -274,7 +299,12 @@ namespace Barotrauma
{ {
var textContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.0f), listBox.Content.RectTransform), style: "InnerFrame", color: Color.White) var textContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.0f), listBox.Content.RectTransform), style: "InnerFrame", color: Color.White)
{ {
CanBeFocused = false CanBeFocused = true,
OnSecondaryClicked = (component, data) =>
{
GUIContextMenu.CreateContextMenu(new ContextMenuOption("editor.copytoclipboard", true, () => { Clipboard.SetText(msg.Text); }));
return true;
}
}; };
var textBlock = new GUITextBlock(new RectTransform(new Point(listBox.Content.Rect.Width - 5, 0), textContainer.RectTransform, Anchor.TopLeft) { AbsoluteOffset = new Point(2, 2) }, var textBlock = new GUITextBlock(new RectTransform(new Point(listBox.Content.Rect.Width - 5, 0), textContainer.RectTransform, Anchor.TopLeft) { AbsoluteOffset = new Point(2, 2) },
msg.Text, textAlignment: Alignment.TopLeft, font: GUI.SmallFont, wrap: true) msg.Text, textAlignment: Alignment.TopLeft, font: GUI.SmallFont, wrap: true)
@@ -455,7 +485,7 @@ namespace Barotrauma
var subInfo = new SubmarineInfo(string.Join(" ", args)); var subInfo = new SubmarineInfo(string.Join(" ", args));
Submarine.MainSub = Submarine.Load(subInfo, true); Submarine.MainSub = Submarine.Load(subInfo, true);
} }
GameMain.SubEditorScreen.Select(); GameMain.SubEditorScreen.Select(enableAutoSave: Screen.Selected != GameMain.GameScreen);
}, isCheat: true)); }, isCheat: true));
commands.Add(new Command("editparticles|particleeditor", "editparticles/particleeditor: Switch to the Particle Editor to edit particle effects.", (string[] args) => commands.Add(new Command("editparticles|particleeditor", "editparticles/particleeditor: Switch to the Particle Editor to edit particle effects.", (string[] args) =>
@@ -487,6 +517,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;
@@ -497,6 +545,102 @@ 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("spreadsheetexport", "Export items in format recognized by the spreadsheet importer.", (string[] args) =>
{
SpreadsheetExport.Export();
}));
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);
@@ -510,11 +654,19 @@ 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);
AssignRelayToServer("spreadsheetexport", 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) => { }));
@@ -552,14 +704,15 @@ namespace Barotrauma
AssignOnExecute("explosion", (string[] args) => AssignOnExecute("explosion", (string[] args) =>
{ {
Vector2 explosionPos = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition); Vector2 explosionPos = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition);
float range = 500, force = 10, damage = 50, structureDamage = 10, itemDamage = 100, empStrength = 0.0f; float range = 500, force = 10, damage = 50, structureDamage = 10, itemDamage = 100, empStrength = 0.0f, ballastFloraStrength = 50f;
if (args.Length > 0) float.TryParse(args[0], out range); if (args.Length > 0) float.TryParse(args[0], out range);
if (args.Length > 1) float.TryParse(args[1], out force); if (args.Length > 1) float.TryParse(args[1], out force);
if (args.Length > 2) float.TryParse(args[2], out damage); if (args.Length > 2) float.TryParse(args[2], out damage);
if (args.Length > 3) float.TryParse(args[3], out structureDamage); if (args.Length > 3) float.TryParse(args[3], out structureDamage);
if (args.Length > 4) float.TryParse(args[4], out itemDamage); if (args.Length > 4) float.TryParse(args[4], out itemDamage);
if (args.Length > 5) float.TryParse(args[5], out empStrength); if (args.Length > 5) float.TryParse(args[5], out empStrength);
new Explosion(range, force, damage, structureDamage, itemDamage, empStrength).Explode(explosionPos, null); if (args.Length > 6) float.TryParse(args[6], out ballastFloraStrength);
new Explosion(range, force, damage, structureDamage, itemDamage, empStrength, ballastFloraStrength).Explode(explosionPos, null);
}); });
AssignOnExecute("teleportcharacter|teleport", (string[] args) => AssignOnExecute("teleportcharacter|teleport", (string[] args) =>
@@ -997,6 +1150,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;
@@ -1382,6 +1546,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) =>
{ {
@@ -1416,6 +1590,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();
@@ -1831,15 +2011,22 @@ namespace Barotrauma
ToolBox.OpenFileWithShell(Path.GetFullPath(filePath)); ToolBox.OpenFileWithShell(Path.GetFullPath(filePath));
})); }));
#if DEBUG #if DEBUG
commands.Add(new Command("playovervc", "Plays a sound over voice chat.", (args) =>
{
VoipCapture.Instance?.SetOverrideSound(args.Length > 0 ? args[0] : null);
}));
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) =>
@@ -2236,6 +2423,37 @@ namespace Barotrauma
} }
); );
#if DEBUG
commands.Add(new Command("setcurrentlocationtype", "setcurrentlocationtype [location type]: Change the type of the current location.", (string[] args) =>
{
var character = Character.Controlled;
if (GameMain.GameSession?.Campaign == null)
{
ThrowError("Campaign not active!");
return;
}
if (args.Length == 0)
{
ThrowError("Please give the location type after the command.");
return;
}
var locationType = LocationType.List.Find(lt => lt.Identifier.Equals(args[0], StringComparison.OrdinalIgnoreCase));
if (locationType == null)
{
ThrowError($"Could not find the location type \"{args[0]}\".");
return;
}
GameMain.GameSession.Campaign.Map.CurrentLocation.ChangeType(locationType);
},
() =>
{
return new string[][]
{
LocationType.List.Select(lt => lt.Identifier).ToArray()
};
}));
#endif
commands.Add(new Command("limbscale", "Define the limbscale for the controlled character. Provide id or name if you want to target another character. Note: the changes are not saved!", (string[] args) => commands.Add(new Command("limbscale", "Define the limbscale for the controlled character. Provide id or name if you want to target another character. Note: the changes are not saved!", (string[] args) =>
{ {
var character = Character.Controlled; var character = Character.Controlled;
@@ -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));
} }
} }
@@ -0,0 +1,30 @@
using Barotrauma.Networking;
namespace Barotrauma
{
partial class AbandonedOutpostMission : Mission
{
public override void ClientReadInitial(IReadMessage msg)
{
byte characterCount = msg.ReadByte();
for (int i = 0; i < characterCount; i++)
{
characters.Add(Character.ReadSpawnData(msg));
ushort itemCount = msg.ReadUInt16();
for (int j = 0; j < itemCount; j++)
{
Item.ReadSpawnData(msg);
}
}
if (characters.Contains(null))
{
throw new System.Exception("Error in AbandonedOutpostMission.ClientReadInitial: character list contains null (mission: " + Prefab.Identifier + ")");
}
if (characters.Count != characterCount)
{
throw new System.Exception("Error in AbandonedOutpostMission.ClientReadInitial: character count does not match the server count (" + characters + " != " + characters.Count + "mission: " + Prefab.Identifier + ")");
}
}
}
}
@@ -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();
@@ -1,19 +1,17 @@
using Microsoft.Xna.Framework; namespace Barotrauma
using System;
namespace Barotrauma
{ {
abstract partial class MissionMode : GameMode abstract partial class MissionMode : GameMode
{ {
public override void ShowStartMessage() public override void ShowStartMessage()
{ {
if (mission == null) return; foreach (Mission mission in missions)
new GUIMessageBox(mission.Name, mission.Description, new string[0], type: GUIMessageBox.Type.InGame, icon: mission.Prefab.Icon)
{ {
IconColor = mission.Prefab.IconColor, new GUIMessageBox(mission.Name, mission.Description, new string[0], type: GUIMessageBox.Type.InGame, icon: mission.Prefab.Icon)
UserData = "missionstartmessage" {
}; IconColor = mission.Prefab.IconColor,
UserData = "missionstartmessage"
};
}
} }
} }
} }
@@ -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++)
{ {
@@ -91,9 +91,10 @@ namespace Barotrauma
UserData = "container" UserData = "container"
}; };
int panelMaxWidth = (int)(GUI.xScale * (GUI.HorizontalAspectRatio < 1.4f ? 650 : 560));
var availableMainGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.4f, 1.0f), campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).RectTransform) var availableMainGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.4f, 1.0f), campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).RectTransform)
{ {
MaxSize = new Point(560, campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Rect.Height) MaxSize = new Point(panelMaxWidth, campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Rect.Height)
}) })
{ {
Stretch = true, Stretch = true,
@@ -149,7 +150,7 @@ namespace Barotrauma
var pendingAndCrewMainGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.4f, 1.0f), campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).RectTransform, anchor: Anchor.TopRight) var pendingAndCrewMainGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.4f, 1.0f), campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).RectTransform, anchor: Anchor.TopRight)
{ {
MaxSize = new Point(560, campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Rect.Height) MaxSize = new Point(panelMaxWidth, campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Rect.Height)
}) })
{ {
Stretch = true, Stretch = true,
@@ -177,7 +178,7 @@ namespace Barotrauma
var pendingAndCrewGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.95f), anchor: Anchor.Center, var pendingAndCrewGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.95f), anchor: Anchor.Center,
parent: new GUIFrame(new RectTransform(new Vector2(1.0f, 13.25f / 14.0f), pendingAndCrewMainGroup.RectTransform) parent: new GUIFrame(new RectTransform(new Vector2(1.0f, 13.25f / 14.0f), pendingAndCrewMainGroup.RectTransform)
{ {
MaxSize = new Point(560, campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Rect.Height) MaxSize = new Point(panelMaxWidth, campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Rect.Height)
}).RectTransform)); }).RectTransform));
float height = 0.05f; float height = 0.05f;
@@ -335,7 +336,7 @@ namespace Barotrauma
jobColor = characterInfo.Job.Prefab.UIColor; jobColor = characterInfo.Job.Prefab.UIColor;
} }
GUIFrame frame = new GUIFrame(new RectTransform(new Point(listBox.Content.Rect.Width, 55), parent: listBox.Content.RectTransform), "ListBoxElement") GUIFrame frame = new GUIFrame(new RectTransform(new Point(listBox.Content.Rect.Width, (int)(GUI.yScale * 55)), parent: listBox.Content.RectTransform), "ListBoxElement")
{ {
UserData = new Tuple<CharacterInfo, float>(characterInfo, skill != null ? skill.Level : 0.0f) UserData = new Tuple<CharacterInfo, float>(characterInfo, skill != null ? skill.Level : 0.0f)
}; };
@@ -366,7 +367,7 @@ namespace Barotrauma
jobBlock.Text = ToolBox.LimitString(jobBlock.Text, jobBlock.Font, jobBlock.Rect.Width); jobBlock.Text = ToolBox.LimitString(jobBlock.Text, jobBlock.Font, jobBlock.Rect.Width);
float width = 0.6f / 3; float width = 0.6f / 3;
if (characterInfo.Job != null) if (characterInfo.Job != null && skill != null)
{ {
GUILayoutGroup skillGroup = new GUILayoutGroup(new RectTransform(new Vector2(width, 0.6f), mainGroup.RectTransform), isHorizontal: true); GUILayoutGroup skillGroup = new GUILayoutGroup(new RectTransform(new Vector2(width, 0.6f), mainGroup.RectTransform), isHorizontal: true);
float iconWidth = (float)skillGroup.Rect.Height / skillGroup.Rect.Width; float iconWidth = (float)skillGroup.Rect.Height / skillGroup.Rect.Width;
@@ -287,6 +287,7 @@ namespace Barotrauma
{ {
lock (mutex) lock (mutex)
{ {
usedIndicatorAngles.Clear();
if (ScreenChanged) if (ScreenChanged)
{ {
@@ -854,6 +855,7 @@ namespace Barotrauma
lock (mutex) lock (mutex)
{ {
GUIMessageBox.AddActiveToGUIUpdateList(); GUIMessageBox.AddActiveToGUIUpdateList();
GUIContextMenu.AddActiveToGUIUpdateList();
if (pauseMenuOpen) if (pauseMenuOpen)
{ {
@@ -920,6 +922,7 @@ namespace Barotrauma
if ((!PlayerInput.PrimaryMouseButtonHeld() && !PlayerInput.PrimaryMouseButtonClicked()) || c == prevMouseOn) if ((!PlayerInput.PrimaryMouseButtonHeld() && !PlayerInput.PrimaryMouseButtonClicked()) || c == prevMouseOn)
{ {
MouseOn = c; MouseOn = c;
var sakdjfnsjkd = c.MouseRect;
} }
break; break;
} }
@@ -1117,15 +1120,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 +1233,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 +1258,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 +1270,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 * Scale;
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 +1296,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 +1528,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 +1546,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 +1647,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 +2119,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 +2328,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);
}
}
@@ -0,0 +1,292 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
namespace Barotrauma
{
struct ContextMenuOption
{
public string Label;
public Action OnSelected;
public ContextMenuOption[]? SubOptions;
public bool IsEnabled;
public string Tooltip;
// Creates a regular context menu
public ContextMenuOption(string label, bool isEnabled, Action onSelected)
{
Label = TextManager.Get(label, returnNull: true) ?? label;
OnSelected = onSelected;
IsEnabled = isEnabled;
SubOptions = null;
Tooltip = string.Empty;
}
// Creates a option with a sub context menu
public ContextMenuOption(string label, bool isEnabled, params ContextMenuOption[] options): this(label, isEnabled, () => { })
{
SubOptions = options;
}
}
internal class GUIContextMenu : GUIComponent
{
public static GUIContextMenu? CurrentContextMenu;
private readonly Dictionary<ContextMenuOption, GUITextBlock> Options = new Dictionary<ContextMenuOption, GUITextBlock>();
private GUIContextMenu? SubMenu;
public readonly GUITextBlock? HeaderLabel;
public GUITextBlock? ParentOption;
/// <summary>
/// Creates a context menu. This constructor does not make the context menu active.
/// Use <see cref="CreateContextMenu(Barotrauma.ContextMenuOption[])"/> to make right click context menus.
/// </summary>
/// <param name="position">Position at which to create the context menu</param>
/// <param name="header">Header text</param>
/// <param name="style">Background style</param>
/// <param name="options">list of context menu options</param>
public GUIContextMenu(Vector2? position, string header, string style, params ContextMenuOption[] options) : base(style, new RectTransform(Point.Zero, GUI.Canvas))
{
Vector2 pos = position ?? PlayerInput.MousePosition;
ScalableFont headerFont = GUI.SubHeadingFont;
ScalableFont font = GUI.SmallFont; // font the context menu options use
Vector4 padding = new Vector4(4), headerPadding = new Vector4(8);
int horizontalPadding = (int) (padding.X + padding.Z), verticalPadding = (int) (padding.Y + padding.W);
bool hasHeader = !string.IsNullOrWhiteSpace(header);
//----------------------------------------------------------------------------------
// Estimate the size of the context menu
//----------------------------------------------------------------------------------
Dictionary<ContextMenuOption, Vector2> optionsAndSizes = new Dictionary<ContextMenuOption, Vector2>();
// estimate how big the context menu needs to be
Point estimatedSize = new Point(horizontalPadding, verticalPadding);
if (hasHeader)
{
InflateSize(ref estimatedSize, header, headerFont);
}
foreach (ContextMenuOption option in options)
{
Vector2 optionSize = InflateSize(ref estimatedSize, option.Label, font);
optionsAndSizes.Add(option, optionSize);
}
// it's better to overestimate the size since it's going to be cropped anyways
estimatedSize = estimatedSize.Multiply(1.2f);
RectTransform.NonScaledSize = estimatedSize;
RectTransform.AbsoluteOffset = pos.ToPoint();
//----------------------------------------------------------------------------------
// Construct the GUI elements
//----------------------------------------------------------------------------------
GUILayoutGroup background = new GUILayoutGroup(new RectTransform(Vector2.One, RectTransform, Anchor.Center));
if (hasHeader)
{
HeaderLabel = new GUITextBlock(new RectTransform(new Vector2(1f, 0.2f), background.RectTransform), header, font: headerFont) { Padding = headerPadding };
}
GUIListBox optionList = new GUIListBox(new RectTransform(new Vector2(1f, hasHeader ? 0.8f : 1f), background.RectTransform), style: null)
{
AutoHideScrollBar = false,
ScrollBarVisible = false,
Padding = hasHeader ? new Vector4(4, 0, 4, 4) : padding
};
foreach (var (option, size) in optionsAndSizes)
{
GUITextBlock optionElement = new GUITextBlock(new RectTransform(size.ToPoint(), optionList.Content.RectTransform), option.Label, font: font)
{
UserData = option,
Enabled = option.IsEnabled
};
Options.Add(option, optionElement);
if (!string.IsNullOrWhiteSpace(option.Tooltip) && optionElement.Enabled)
{
optionElement.ToolTip = option.Tooltip;
}
if (!option.IsEnabled)
{
optionElement.TextColor *= 0.5f;
}
}
//----------------------------------------------------------------------------------
// Positioning and cropping the context menu
//----------------------------------------------------------------------------------
List<GUIComponent> children = optionList.Content.Children.ToList();
// Resize all children to the size of their text
foreach (GUITextBlock block in children.Where(c => c is GUITextBlock).Cast<GUITextBlock>())
{
block.RectTransform.NonScaledSize = new Point((int) (block.TextSize.X + (block.Padding.X + block.Padding.Z)), (int) (18 * GUI.Scale));
}
int largestWidth = children.Max(c => c.Rect.Width + horizontalPadding);
// if the header is bigger than any of the options then overwrite
if (HeaderLabel != null)
{
RectTransform headerTransform = HeaderLabel.RectTransform;
headerTransform.MinSize = new Point((int) (HeaderLabel.TextSize.X + (headerPadding.X + headerPadding.Z)), headerTransform.NonScaledSize.Y);
if (largestWidth < headerTransform.MinSize.X)
{
largestWidth = headerTransform.MinSize.X;
}
}
// resize all children to the size of the longest element
foreach (GUIComponent c in children)
{
c.RectTransform.MinSize = new Point(largestWidth, c.Rect.Height);
}
// the cropped size of the option list
Point newSize = new Point(largestWidth, children.Sum(c => c.Rect.Height) + verticalPadding);
// resize the menu itself taking into account the option menus relative Y size
RectTransform.NonScaledSize = new Point(newSize.X, (int) (newSize.Y / optionList.RectTransform.RelativeSize.Y));
optionList.RectTransform.NonScaledSize = newSize;
// move the context menu if it would go outside of screen
if (RectTransform.Rect.Bottom > GameMain.GraphicsHeight)
{
Rectangle rect = RectTransform.Rect;
RectTransform.AbsoluteOffset = new Point(rect.X, rect.Y - rect.Height);
}
if (RectTransform.Rect.Right > GameMain.GraphicsWidth)
{
Rectangle rect = RectTransform.Rect;
RectTransform.AbsoluteOffset = new Point(rect.X - rect.Width, rect.Y);
}
background.Recalculate();
optionList.OnSelected = OnSelected;
}
public static GUIContextMenu CreateContextMenu(params ContextMenuOption[] options) => CreateContextMenu(PlayerInput.MousePosition, string.Empty, null, options);
public static GUIContextMenu CreateContextMenu(Vector2? pos, string header, Color? headerColor, params ContextMenuOption[] options)
{
GUIContextMenu menu = new GUIContextMenu(pos,header, "GUIToolTip", options);
if (headerColor != null)
{
menu.HeaderLabel?.OverrideTextColor(headerColor.Value);
}
CurrentContextMenu = menu;
return menu;
}
private bool OnSelected(GUIComponent _, object data)
{
if (data is ContextMenuOption option && option.IsEnabled)
{
CurrentContextMenu = null;
option.OnSelected();
return true;
}
return false;
}
/// <summary>
/// Inflates a point by the size of the text
/// </summary>
/// <param name="size">Pint to resize</param>
/// <param name="label">String whose size to inflate by</param>
/// <param name="font">What font to use</param>
/// <returns>The size of the text</returns>
private Vector2 InflateSize(ref Point size, string label, ScalableFont font)
{
Vector2 textSize = font.MeasureString(label);
size.X = Math.Max((int) Math.Ceiling(textSize.X), size.X);
size.Y += (int) Math.Ceiling(textSize.Y);
return textSize;
}
protected override void Update(float deltaTime)
{
base.Update(deltaTime);
// keep the parent highlighted
if (ParentOption != null)
{
ParentOption.State = ComponentState.Hover;
}
if (SubMenu != null && !SubMenu.IsMouseOver())
{
SubMenu = null;
return;
}
foreach (var (option, textBlock) in Options)
{
// Create a new sub context menu if hovering over an option with sub options
if (GUI.MouseOn != textBlock) { continue; }
if (option.IsEnabled && option.SubOptions is { } subOptions && subOptions.Any())
{
Vector2 subMenuPos = new Vector2(textBlock.MouseRect.Right + 4, textBlock.MouseRect.Y);
SubMenu = new GUIContextMenu(subMenuPos, "", "GUIToolTip", subOptions)
{
ParentOption = textBlock
};
}
}
}
/// <summary>
/// Checks if the mouse cursor is over this context menu or any of its sub menus
/// </summary>
/// <returns></returns>
private bool IsMouseOver()
{
Rectangle expandedRect = Rect;
expandedRect.Inflate(20, 20);
bool isMouseOn = expandedRect.Contains(PlayerInput.MousePosition);
if (ParentOption != null)
{
isMouseOn |= GUI.MouseOn == ParentOption;
}
// Recursively check sub context menus
if (!isMouseOn && SubMenu != null)
{
isMouseOn = SubMenu.IsMouseOver();
}
return isMouseOn;
}
public override void AddToGUIUpdateList(bool ignoreChildren = false, int order = 0)
{
base.AddToGUIUpdateList(ignoreChildren, order);
SubMenu?.AddToGUIUpdateList();
}
public static void AddActiveToGUIUpdateList()
{
if (CurrentContextMenu != null && !CurrentContextMenu.IsMouseOver())
{
CurrentContextMenu = null;
}
CurrentContextMenu?.AddToGUIUpdateList();
}
}
}
@@ -214,7 +214,22 @@ namespace Barotrauma
public bool AutoHideScrollBar { get; set; } = true; public bool AutoHideScrollBar { get; set; } = true;
private bool IsScrollBarOnDefaultSide { get; set; } private bool IsScrollBarOnDefaultSide { get; set; }
public bool CanDragElements { get; set; } = false; public bool CanDragElements
{
get
{
return canDragElements;
}
set
{
if (value == false && canDragElements && draggedElement != null)
{
draggedElement = null;
}
canDragElements = value;
}
}
private bool canDragElements = false;
private GUIComponent draggedElement; private GUIComponent draggedElement;
private Rectangle draggedReferenceRectangle; private Rectangle draggedReferenceRectangle;
private Point draggedReferenceOffset; private Point draggedReferenceOffset;
@@ -223,9 +238,12 @@ namespace Barotrauma
private bool scheduledScroll = false; private bool scheduledScroll = false;
private readonly bool isHorizontal;
/// <param name="isScrollBarOnDefaultSide">For horizontal listbox, default side is on the bottom. For vertical, it's on the right.</param> /// <param name="isScrollBarOnDefaultSide">For horizontal listbox, default side is on the bottom. For vertical, it's on the right.</param>
public GUIListBox(RectTransform rectT, bool isHorizontal = false, Color? color = null, string style = "", bool isScrollBarOnDefaultSide = true, bool useMouseDownToSelect = false) : base(style, rectT) public GUIListBox(RectTransform rectT, bool isHorizontal = false, Color? color = null, string style = "", bool isScrollBarOnDefaultSide = true, bool useMouseDownToSelect = false) : base(style, rectT)
{ {
this.isHorizontal = isHorizontal;
HoverCursor = CursorState.Hand; HoverCursor = CursorState.Hand;
CanBeFocused = true; CanBeFocused = true;
selected = new List<GUIComponent>(); selected = new List<GUIComponent>();
@@ -454,22 +472,42 @@ namespace Barotrauma
} }
else else
{ {
draggedElement.RectTransform.AbsoluteOffset = draggedReferenceOffset + new Point(0, (int)PlayerInput.MousePosition.Y - draggedReferenceRectangle.Center.Y); draggedElement.RectTransform.AbsoluteOffset = isHorizontal ?
draggedReferenceOffset + new Point((int)PlayerInput.MousePosition.X - draggedReferenceRectangle.Center.X, 0) :
draggedReferenceOffset + new Point(0, (int)PlayerInput.MousePosition.Y - draggedReferenceRectangle.Center.Y);
int index = Content.RectTransform.GetChildIndex(draggedElement.RectTransform); int index = Content.RectTransform.GetChildIndex(draggedElement.RectTransform);
int currIndex = index; int currIndex = index;
while (currIndex > 0 && PlayerInput.MousePosition.Y < draggedReferenceRectangle.Top) if (isHorizontal)
{ {
currIndex--; while (currIndex > 0 && PlayerInput.MousePosition.X < draggedReferenceRectangle.Left)
draggedReferenceRectangle.Y -= draggedReferenceRectangle.Height; {
draggedReferenceOffset.Y -= draggedReferenceRectangle.Height; currIndex--;
draggedReferenceRectangle.X -= draggedReferenceRectangle.Width;
draggedReferenceOffset.X -= draggedReferenceRectangle.Width;
}
while (currIndex < Content.CountChildren - 1 && PlayerInput.MousePosition.X > draggedReferenceRectangle.Right)
{
currIndex++;
draggedReferenceRectangle.X += draggedReferenceRectangle.Width;
draggedReferenceOffset.X += draggedReferenceRectangle.Width;
}
} }
while (currIndex < Content.CountChildren - 1 && PlayerInput.MousePosition.Y > draggedReferenceRectangle.Bottom) else
{ {
currIndex++; while (currIndex > 0 && PlayerInput.MousePosition.Y < draggedReferenceRectangle.Top)
draggedReferenceRectangle.Y += draggedReferenceRectangle.Height; {
draggedReferenceOffset.Y += draggedReferenceRectangle.Height; currIndex--;
draggedReferenceRectangle.Y -= draggedReferenceRectangle.Height;
draggedReferenceOffset.Y -= draggedReferenceRectangle.Height;
}
while (currIndex < Content.CountChildren - 1 && PlayerInput.MousePosition.Y > draggedReferenceRectangle.Bottom)
{
currIndex++;
draggedReferenceRectangle.Y += draggedReferenceRectangle.Height;
draggedReferenceOffset.Y += draggedReferenceRectangle.Height;
}
} }
if (currIndex != index) if (currIndex != index)
@@ -510,10 +548,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;
@@ -943,9 +981,10 @@ namespace Barotrauma
public override void RemoveChild(GUIComponent child) public override void RemoveChild(GUIComponent child)
{ {
if (child == null) { return; } if (child == null) { return; }
child.RectTransform.Parent = null; child.RectTransform.Parent = null;
if (selected.Contains(child)) selected.Remove(child); if (selected.Contains(child)) { selected.Remove(child); }
if (draggedElement == child) { draggedElement = null; }
UpdateScrollBarSize(); UpdateScrollBarSize();
} }
@@ -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())
@@ -81,10 +81,13 @@ namespace Barotrauma
private float floatValue; private float floatValue;
public float FloatValue public float FloatValue
{ {
get { return floatValue; } get
{
return floatValue;
}
set set
{ {
if (MathUtils.NearlyEqual(value, floatValue)) return; if (MathUtils.NearlyEqual(value, floatValue)) { return; }
floatValue = value; floatValue = value;
ClampFloatValue(); ClampFloatValue();
float newValue = floatValue; float newValue = floatValue;
@@ -129,10 +132,13 @@ namespace Barotrauma
private int intValue; private int intValue;
public int IntValue public int IntValue
{ {
get { return intValue; } get
{
return intValue;
}
set set
{ {
if (value == intValue) return; if (value == intValue) { return; }
intValue = value; intValue = value;
UpdateText(); UpdateText();
} }
@@ -192,6 +198,29 @@ namespace Barotrauma
}; };
TextBox.CaretColor = TextBox.TextColor; TextBox.CaretColor = TextBox.TextColor;
TextBox.OnTextChanged += TextChanged; TextBox.OnTextChanged += TextChanged;
TextBox.OnDeselected += (sender, key) =>
{
if (inputType == NumberType.Int)
{
ClampIntValue();
}
else
{
ClampFloatValue();
}
};
TextBox.OnEnterPressed += (textBox, text) =>
{
if (inputType == NumberType.Int)
{
ClampIntValue();
}
else
{
ClampFloatValue();
}
return true;
};
var buttonArea = new GUIFrame(new RectTransform(new Vector2(_relativeButtonAreaWidth, 1.0f), LayoutGroup.RectTransform, Anchor.CenterRight), style: null); var buttonArea = new GUIFrame(new RectTransform(new Vector2(_relativeButtonAreaWidth, 1.0f), LayoutGroup.RectTransform, Anchor.CenterRight), style: null);
PlusButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.5f), buttonArea.RectTransform), style: null); PlusButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.5f), buttonArea.RectTransform), style: null);
@@ -299,10 +328,12 @@ namespace Barotrauma
if (inputType == NumberType.Int) if (inputType == NumberType.Int)
{ {
IntValue -= valueStep > 0 ? (int)valueStep : 1; IntValue -= valueStep > 0 ? (int)valueStep : 1;
ClampIntValue();
} }
else if (maxValueFloat.HasValue && minValueFloat.HasValue) else if (maxValueFloat.HasValue && minValueFloat.HasValue)
{ {
FloatValue -= valueStep > 0 ? valueStep : Round(); FloatValue -= valueStep > 0 ? valueStep : Round();
ClampFloatValue();
} }
} }
@@ -311,10 +342,12 @@ namespace Barotrauma
if (inputType == NumberType.Int) if (inputType == NumberType.Int)
{ {
IntValue += valueStep > 0 ? (int)valueStep : 1; IntValue += valueStep > 0 ? (int)valueStep : 1;
ClampIntValue();
} }
else if (inputType == NumberType.Float) else if (inputType == NumberType.Float)
{ {
FloatValue += valueStep > 0 ? valueStep : Round(); FloatValue += valueStep > 0 ? valueStep : Round();
ClampFloatValue();
} }
} }
@@ -325,7 +358,7 @@ namespace Barotrauma
/// </summary> /// </summary>
private float Round() private float Round()
{ {
if (!maxValueFloat.HasValue || !minValueFloat.HasValue) return 0; if (!maxValueFloat.HasValue || !minValueFloat.HasValue) { return 0; }
float onePercent = MathHelper.Lerp(minValueFloat.Value, maxValueFloat.Value, 0.01f); float onePercent = MathHelper.Lerp(minValueFloat.Value, maxValueFloat.Value, 0.01f);
float diff = maxValueFloat.Value - minValueFloat.Value; float diff = maxValueFloat.Value - minValueFloat.Value;
int decimals = (int)MathHelper.Lerp(3, 0, MathUtils.InverseLerp(10, 1000, diff)); int decimals = (int)MathHelper.Lerp(3, 0, MathUtils.InverseLerp(10, 1000, diff));
@@ -337,28 +370,24 @@ namespace Barotrauma
switch (InputType) switch (InputType)
{ {
case NumberType.Int: case NumberType.Int:
int newIntValue = IntValue;
if (string.IsNullOrWhiteSpace(text) || text == "-") if (string.IsNullOrWhiteSpace(text) || text == "-")
{ {
intValue = 0; intValue = 0;
} }
else if (int.TryParse(text, out newIntValue)) else if (int.TryParse(text, out int newIntValue))
{ {
intValue = newIntValue; intValue = newIntValue;
} }
ClampIntValue();
break; break;
case NumberType.Float: case NumberType.Float:
float newFloatValue = FloatValue;
if (string.IsNullOrWhiteSpace(text) || text == "-") if (string.IsNullOrWhiteSpace(text) || text == "-")
{ {
floatValue = 0; floatValue = 0;
} }
else if (float.TryParse(text, NumberStyles.Any, CultureInfo.InvariantCulture, out newFloatValue)) else if (float.TryParse(text, NumberStyles.Any, CultureInfo.InvariantCulture, out float newFloatValue))
{ {
floatValue = newFloatValue; floatValue = newFloatValue;
} }
ClampFloatValue();
break; break;
} }
OnValueChanged?.Invoke(this); OnValueChanged?.Invoke(this);
@@ -34,6 +34,8 @@ namespace Barotrauma
public readonly Sprite[] CursorSprite = new Sprite[7]; public readonly Sprite[] CursorSprite = new Sprite[7];
public UISprite RadiationSprite { get; private set; }
public UISprite UIGlow { get; private set; } public UISprite UIGlow { get; private set; }
public UISprite UIGlowCircular { get; private set; } public UISprite UIGlowCircular { get; private set; }
@@ -70,6 +72,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 +153,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;
@@ -205,6 +211,9 @@ namespace Barotrauma
case "uiglow": case "uiglow":
UIGlow = new UISprite(subElement); UIGlow = new UISprite(subElement);
break; break;
case "radiation":
RadiationSprite = new UISprite(subElement);
break;
case "uiglowcircular": case "uiglowcircular":
UIGlowCircular = new UISprite(subElement); UIGlowCircular = new UISprite(subElement);
break; break;
@@ -344,7 +353,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 +363,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,72 @@ 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); SetPriceGetters(itemFrame, true);
} }
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 +700,73 @@ 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); SetPriceGetters(itemFrame, false);
}
SetItemFrameStatus(itemFrame, hasPermissions && itemQuantity > 0);
if (itemQuantity < 1 && !isRequestedGood)
{
itemFrame.Visible = false;
} }
if (quantity < 1) { 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);
@@ -604,6 +774,37 @@ namespace Barotrauma
shoppingCrateSellList.BarScroll = prevShoppingCrateScroll; shoppingCrateSellList.BarScroll = prevShoppingCrateScroll;
} }
private void SetPriceGetters(GUIComponent itemFrame, bool buying)
{
if (itemFrame == null || !(itemFrame.UserData is PurchasedItem pi)) { return; }
if (itemFrame.FindChild("undiscountedprice", recursive: true) is GUITextBlock undiscountedPriceBlock)
{
if (buying)
{
undiscountedPriceBlock.TextGetter = () => GetCurrencyFormatted(
CurrentLocation?.GetAdjustedItemBuyPrice(pi.ItemPrefab, considerDailySpecials: false) ?? 0);
}
else
{
undiscountedPriceBlock.TextGetter = () => GetCurrencyFormatted(
CurrentLocation?.GetAdjustedItemSellPrice(pi.ItemPrefab, considerRequestedGoods: false) ?? 0);
}
}
if (itemFrame.FindChild("price", recursive: true) is GUITextBlock priceBlock)
{
if (buying)
{
priceBlock.TextGetter = () => GetCurrencyFormatted(CurrentLocation?.GetAdjustedItemBuyPrice(pi.ItemPrefab) ?? 0);
}
else
{
priceBlock.TextGetter = () => GetCurrencyFormatted(CurrentLocation?.GetAdjustedItemSellPrice(pi.ItemPrefab) ?? 0);
}
}
}
public void RefreshItemsToSell() public void RefreshItemsToSell()
{ {
itemsToSell.Clear(); itemsToSell.Clear();
@@ -673,13 +874,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 +909,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 +1054,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 +1062,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 +1106,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 +1168,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 +1199,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 +1208,36 @@ 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 (locationHasDealOnItem)
{ {
priceBlock.TextGetter = () => GetCurrencyFormatted(CurrentLocation?.GetAdjustedItemSellPrice(priceInfo) ?? 0); var undiscounterPriceBlock = new GUITextBlock(
} new RectTransform(new Vector2(1.0f, 0.25f), priceFrame.RectTransform, anchor: Anchor.Center)
else {
{ AbsoluteOffset = new Point(0, priceBlock.RectTransform.ScaledSize.Y)
priceBlock.TextGetter = () => GetCurrencyFormatted(CurrentLocation?.GetAdjustedItemBuyPrice(priceInfo) ?? 0); }, "", font: GUI.SmallFont, textAlignment: Alignment.Center)
{
CanBeFocused = false,
Strikethrough = new GUITextBlock.StrikethroughSettings(color: priceBlock.TextColor, expand: 1),
TextColor = priceBlock.TextColor,
UserData = "undiscountedprice"
};
} }
SetPriceGetters(frame, !isSellingRelatedList);
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 +1260,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 +1288,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 +1347,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 +1479,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 +1513,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 +1575,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) })
{ {
@@ -858,54 +857,60 @@ namespace Barotrauma
int locationInfoYOffset = locationNameText.Rect.Height + locationTypeText.Rect.Height + padding * 2; int locationInfoYOffset = locationNameText.Rect.Height + locationTypeText.Rect.Height + padding * 2;
GUIFrame missionDescriptionHolder; GUIListBox missionList;
if (hasPortrait) if (hasPortrait)
{ {
GUIFrame missionImageHolder = new GUIFrame(new RectTransform(new Point(contentWidth, (int)(missionFrame.Rect.Height * 0.588f)), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, locationInfoYOffset) }); GUIFrame portraitHolder = new GUIFrame(new RectTransform(new Point(contentWidth, (int)(missionFrame.Rect.Height * 0.588f)), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, locationInfoYOffset) });
float portraitAspectRatio = portrait.SourceRect.Width / portrait.SourceRect.Height; float portraitAspectRatio = portrait.SourceRect.Width / portrait.SourceRect.Height;
GUIImage portraitImage = new GUIImage(new RectTransform(new Vector2(1.0f, 1f), missionImageHolder.RectTransform), portrait, scaleToFit: true); GUIImage portraitImage = new GUIImage(new RectTransform(new Vector2(1.0f, 1f), portraitHolder.RectTransform), portrait, scaleToFit: true);
missionImageHolder.RectTransform.NonScaledSize = new Point(portraitImage.Rect.Size.X, (int)(portraitImage.Rect.Size.X / portraitAspectRatio)); portraitHolder.RectTransform.NonScaledSize = new Point(portraitImage.Rect.Size.X, (int)(portraitImage.Rect.Size.X / portraitAspectRatio));
missionDescriptionHolder = new GUIFrame(new RectTransform(new Point(contentWidth, 0), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, missionImageHolder.RectTransform.AbsoluteOffset.Y + missionImageHolder.Rect.Height + padding) }, style: null);
missionList = new GUIListBox(new RectTransform(new Point(contentWidth, missionFrame.Rect.Bottom - portraitHolder.Rect.Bottom - padding), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, portraitHolder.RectTransform.AbsoluteOffset.Y + portraitHolder.Rect.Height + padding) });
} }
else else
{ {
missionDescriptionHolder = new GUIFrame(new RectTransform(new Point(contentWidth, 0), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, locationInfoYOffset) }, style: null); missionList = new GUIListBox(new RectTransform(new Point(contentWidth, missionFrame.Rect.Height - locationInfoYOffset - padding), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, locationInfoYOffset) });
} }
missionList.ContentBackground.Color = Color.Transparent;
missionList.Spacing = GUI.IntScale(15);
Mission mission = GameMain.GameSession?.Mission; if (GameMain.GameSession?.Missions != null)
if (mission != null)
{ {
GUILayoutGroup missionTextGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.744f, 0f), missionDescriptionHolder.RectTransform, Anchor.CenterLeft) { RelativeOffset = new Vector2(0.225f, 0f) }, false, childAnchor: Anchor.TopLeft); foreach (Mission mission in GameMain.GameSession.Missions)
string missionNameString = ToolBox.WrapText(mission.Name, missionTextGroup.Rect.Width, GUI.LargeFont);
string missionDescriptionString = ToolBox.WrapText(mission.Description, missionTextGroup.Rect.Width, GUI.Font);
string rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", mission.Reward));
string missionRewardString = ToolBox.WrapText(TextManager.GetWithVariable("MissionReward", "[reward]", rewardText), missionTextGroup.Rect.Width, GUI.Font);
Vector2 missionNameSize = GUI.LargeFont.MeasureString(missionNameString);
Vector2 missionDescriptionSize = GUI.Font.MeasureString(missionDescriptionString);
Vector2 missionRewardSize = GUI.Font.MeasureString(missionRewardString);
missionDescriptionHolder.RectTransform.NonScaledSize = new Point(missionDescriptionHolder.RectTransform.NonScaledSize.X, (int)(missionNameSize.Y + missionDescriptionSize.Y + missionRewardSize.Y));
missionTextGroup.RectTransform.NonScaledSize = new Point(missionTextGroup.RectTransform.NonScaledSize.X, missionDescriptionHolder.RectTransform.NonScaledSize.Y);
if (mission.Prefab.Icon != null)
{ {
float iconAspectRatio = mission.Prefab.Icon.SourceRect.Width / mission.Prefab.Icon.SourceRect.Height; GUIFrame missionDescriptionHolder = new GUIFrame(new RectTransform(Vector2.One, missionList.Content.RectTransform), style: null);
int iconWidth = (int)(0.225f * missionDescriptionHolder.RectTransform.NonScaledSize.X); GUILayoutGroup missionTextGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.744f, 0f), missionDescriptionHolder.RectTransform, Anchor.CenterLeft) { RelativeOffset = new Vector2(0.225f, 0f) }, false, childAnchor: Anchor.TopLeft);
int iconHeight = Math.Max(missionTextGroup.RectTransform.NonScaledSize.Y, (int)(iconWidth * iconAspectRatio));
Point iconSize = new Point(iconWidth, iconHeight);
new GUIImage(new RectTransform(iconSize, missionDescriptionHolder.RectTransform), mission.Prefab.Icon, null, true) { Color = mission.Prefab.IconColor }; string missionNameString = ToolBox.WrapText(mission.Name, missionTextGroup.Rect.Width, GUI.LargeFont);
} string missionDescriptionString = ToolBox.WrapText(mission.Description, missionTextGroup.Rect.Width, GUI.Font);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionNameString, font: GUI.LargeFont); string rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", mission.Reward));
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionRewardString); string missionRewardString = ToolBox.WrapText(TextManager.GetWithVariable("MissionReward", "[reward]", rewardText), missionTextGroup.Rect.Width, GUI.Font);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionDescriptionString);
Vector2 missionNameSize = GUI.LargeFont.MeasureString(missionNameString);
Vector2 missionDescriptionSize = GUI.Font.MeasureString(missionDescriptionString);
Vector2 missionRewardSize = GUI.Font.MeasureString(missionRewardString);
missionDescriptionHolder.RectTransform.NonScaledSize = new Point(missionDescriptionHolder.RectTransform.NonScaledSize.X, (int)(missionNameSize.Y + missionDescriptionSize.Y + missionRewardSize.Y));
missionTextGroup.RectTransform.NonScaledSize = new Point(missionTextGroup.RectTransform.NonScaledSize.X, missionDescriptionHolder.RectTransform.NonScaledSize.Y);
if (mission.Prefab.Icon != null)
{
float iconAspectRatio = mission.Prefab.Icon.SourceRect.Width / mission.Prefab.Icon.SourceRect.Height;
int iconWidth = (int)(0.225f * missionDescriptionHolder.RectTransform.NonScaledSize.X);
int iconHeight = Math.Max(missionTextGroup.RectTransform.NonScaledSize.Y, (int)(iconWidth * iconAspectRatio));
Point iconSize = new Point(iconWidth, iconHeight);
new GUIImage(new RectTransform(iconSize, missionDescriptionHolder.RectTransform), mission.Prefab.Icon, null, true) { Color = mission.Prefab.IconColor, HoverColor = mission.Prefab.IconColor };
}
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionNameString, font: GUI.LargeFont);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionRewardString);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionDescriptionString);
}
} }
else else
{ {
GUILayoutGroup missionTextGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0f), missionDescriptionHolder.RectTransform, Anchor.CenterLeft), false, childAnchor: Anchor.TopLeft); GUILayoutGroup missionTextGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0f), missionList.RectTransform, Anchor.CenterLeft), false, childAnchor: Anchor.TopLeft);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), TextManager.Get("NoMission"), font: GUI.LargeFont); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), TextManager.Get("NoMission"), font: GUI.LargeFont);
} }
} }
@@ -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);
} }
@@ -32,6 +32,7 @@ namespace Barotrauma
public Vector2 DrawPos { get; set; } public Vector2 DrawPos { get; set; }
public int size = 10; public int size = 10;
public float thickness = 1f;
/// <summary> /// <summary>
/// Used only for circles. /// Used only for circles.
/// </summary> /// </summary>
@@ -157,7 +158,7 @@ namespace Barotrauma
{ {
GUI.DrawRectangle(spriteBatch, drawRect, secondaryColor.Value, isFilled, thickness: 2); GUI.DrawRectangle(spriteBatch, drawRect, secondaryColor.Value, isFilled, thickness: 2);
} }
GUI.DrawRectangle(spriteBatch, drawRect, color, isFilled, thickness: IsSelected ? 3 : 1); GUI.DrawRectangle(spriteBatch, drawRect, color, isFilled, thickness: IsSelected ? (int)(thickness * 3) : (int)thickness);
break; break;
case Shape.Circle: case Shape.Circle:
if (secondaryColor.HasValue) if (secondaryColor.HasValue)
@@ -182,7 +183,7 @@ namespace Barotrauma
{ {
if (showTooltip && !string.IsNullOrEmpty(tooltip)) if (showTooltip && !string.IsNullOrEmpty(tooltip))
{ {
var offset = tooltipOffset ?? new Vector2(size, -size / 2); var offset = tooltipOffset ?? new Vector2(size, -size / 2f);
GUI.DrawString(spriteBatch, DrawPos + offset, tooltip, textColor, textBackgroundColor); GUI.DrawString(spriteBatch, DrawPos + offset, tooltip, textColor, textBackgroundColor);
} }
} }
@@ -557,6 +557,7 @@ namespace Barotrauma
GameModePreset.Init(); GameModePreset.Init();
SaveUtil.DeleteDownloadedSubs();
SubmarineInfo.RefreshSavedSubs(); SubmarineInfo.RefreshSavedSubs();
TitleScreen.LoadState = 65.0f; TitleScreen.LoadState = 65.0f;
@@ -634,6 +635,7 @@ namespace Barotrauma
/// </summary> /// </summary>
protected override void UnloadContent() protected override void UnloadContent()
{ {
TextureLoader.CancelAll();
CoroutineManager.StopCoroutines("Load"); CoroutineManager.StopCoroutines("Load");
Video.Close(); Video.Close();
VoipCapture.Instance?.Dispose(); VoipCapture.Instance?.Dispose();
@@ -682,7 +684,7 @@ namespace Barotrauma
} }
public void OnLobbyJoinRequested(Steamworks.Data.Lobby lobby, Steamworks.SteamId friendId) public void OnLobbyJoinRequested(Steamworks.Data.Lobby lobby, Steamworks.SteamId friendId)
{ {
SteamManager.JoinLobby(lobby.Id, true); SteamManager.JoinLobby(lobby.Id, true);
} }
@@ -902,7 +904,9 @@ namespace Barotrauma
} }
#if !DEBUG #if !DEBUG
if (NetworkMember == null && !WindowActive && !Paused && true && Screen.Selected != MainMenuScreen && Config.PauseOnFocusLost) if (NetworkMember == null && !WindowActive && !Paused && true && Config.PauseOnFocusLost &&
Screen.Selected != MainMenuScreen && Screen.Selected != ServerListScreen && Screen.Selected != NetLobbyScreen &&
Screen.Selected != SubEditorScreen && Screen.Selected != LevelEditorScreen)
{ {
GUI.TogglePauseMenu(); GUI.TogglePauseMenu();
Paused = true; Paused = true;
@@ -1072,13 +1076,6 @@ namespace Barotrauma
{ {
((TutorialMode)GameSession.GameMode).Tutorial?.Stop(); ((TutorialMode)GameSession.GameMode).Tutorial?.Stop();
} }
if (GameSettings.SendUserStatistics)
{
Mission mission = GameSession.Mission;
GameAnalyticsManager.AddDesignEvent("QuitRound:" + (save ? "Save" : "NoSave"));
GameAnalyticsManager.AddDesignEvent("EndRound:" + (mission == null ? "NoMission" : (mission.Completed ? "MissionCompleted" : "MissionFailed")));
}
} }
GUIMessageBox.CloseAll(); GUIMessageBox.CloseAll();
MainMenuScreen.Select(); MainMenuScreen.Select();
@@ -1112,7 +1109,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); }
File diff suppressed because it is too large Load Diff
@@ -59,13 +59,16 @@ namespace Barotrauma
public override void ShowStartMessage() public override void ShowStartMessage()
{ {
if (Mission == null) return; foreach (Mission mission in Missions)
new GUIMessageBox(Mission.Name, Mission.Description, new string[0], type: GUIMessageBox.Type.InGame, icon: Mission.Prefab.Icon)
{ {
IconColor = Mission.Prefab.IconColor, new GUIMessageBox(
UserData = "missionstartmessage" mission.Prefab.IsSideObjective ? TextManager.AddPunctuation(':', TextManager.Get("sideobjective"), mission.Name) : mission.Name,
}; mission.Description, new string[0], type: GUIMessageBox.Type.InGame, icon: mission.Prefab.Icon)
{
IconColor = mission.Prefab.IconColor,
UserData = "missionstartmessage"
};
}
} }
/// <summary> /// <summary>
@@ -158,7 +161,8 @@ namespace Barotrauma
case TransitionType.ProgressToNextEmptyLocation: case TransitionType.ProgressToNextEmptyLocation:
if (Level.Loaded.EndOutpost == null || !Level.Loaded.EndOutpost.DockedTo.Contains(leavingSub)) if (Level.Loaded.EndOutpost == null || !Level.Loaded.EndOutpost.DockedTo.Contains(leavingSub))
{ {
buttonText = TextManager.GetWithVariable("EnterLocation", "[locationname]", Level.Loaded.EndLocation?.Name ?? "[ERROR]"); string textTag = availableTransition == TransitionType.ProgressToNextLocation ? "EnterLocation" : "EnterEmptyLocation";
buttonText = TextManager.GetWithVariable(textTag, "[locationname]", Level.Loaded.EndLocation?.Name ?? "[ERROR]");
endRoundButton.Visible = !ForceMapUI && !ShowCampaignUI; endRoundButton.Visible = !ForceMapUI && !ShowCampaignUI;
} }
break; break;
@@ -170,7 +174,8 @@ namespace Barotrauma
case TransitionType.ReturnToPreviousEmptyLocation: case TransitionType.ReturnToPreviousEmptyLocation:
if (Level.Loaded.StartOutpost == null || !Level.Loaded.StartOutpost.DockedTo.Contains(leavingSub)) if (Level.Loaded.StartOutpost == null || !Level.Loaded.StartOutpost.DockedTo.Contains(leavingSub))
{ {
buttonText = TextManager.GetWithVariable("EnterLocation", "[locationname]", Level.Loaded.StartLocation?.Name ?? "[ERROR]"); string textTag = availableTransition == TransitionType.ReturnToPreviousLocation ? "EnterLocation" : "EnterEmptyLocation";
buttonText = TextManager.GetWithVariable(textTag, "[locationname]", Level.Loaded.StartLocation?.Name ?? "[ERROR]");
endRoundButton.Visible = !ForceMapUI && !ShowCampaignUI; endRoundButton.Visible = !ForceMapUI && !ShowCampaignUI;
} }
@@ -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();
} }
@@ -711,13 +711,20 @@ namespace Barotrauma
DebugConsole.ThrowError($"Error when receiving campaign data from the server: mission prefab \"{availableMission.First}\" not found."); DebugConsole.ThrowError($"Error when receiving campaign data from the server: mission prefab \"{availableMission.First}\" not found.");
continue; continue;
} }
if (availableMission.Second < 0 || availableMission.Second >= campaign.Map.CurrentLocation.Connections.Count) if (availableMission.Second == 255)
{ {
DebugConsole.ThrowError($"Error when receiving campaign data from the server: connection index for mission \"{availableMission.First}\" out of range (index: {availableMission.Second}, current location: {campaign.Map.CurrentLocation.Name}, connections: {campaign.Map.CurrentLocation.Connections.Count})."); campaign.Map.CurrentLocation.UnlockMission(missionPrefab);
continue; }
else
{
if (availableMission.Second < 0 || availableMission.Second >= campaign.Map.CurrentLocation.Connections.Count)
{
DebugConsole.ThrowError($"Error when receiving campaign data from the server: connection index for mission \"{availableMission.First}\" out of range (index: {availableMission.Second}, current location: {campaign.Map.CurrentLocation.Name}, connections: {campaign.Map.CurrentLocation.Connections.Count}).");
continue;
}
LocationConnection connection = campaign.Map.CurrentLocation.Connections[availableMission.Second];
campaign.Map.CurrentLocation.UnlockMission(missionPrefab, connection);
} }
LocationConnection connection = campaign.Map.CurrentLocation.Connections[availableMission.Second];
campaign.Map.CurrentLocation.UnlockMission(missionPrefab, connection);
} }
GameMain.NetLobbyScreen.ToggleCampaignMode(true); GameMain.NetLobbyScreen.ToggleCampaignMode(true);
@@ -812,8 +819,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; }
@@ -20,7 +22,7 @@ namespace Barotrauma
{ {
if (CoroutineManager.IsCoroutineRunning("LevelTransition") || CoroutineManager.IsCoroutineRunning("SubmarineTransition") || gameOver) { return; } if (CoroutineManager.IsCoroutineRunning("LevelTransition") || CoroutineManager.IsCoroutineRunning("SubmarineTransition") || gameOver) { return; }
if (PlayerInput.RightButtonClicked() || if (PlayerInput.SecondaryMouseButtonClicked() ||
PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape)) PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape))
{ {
ShowCampaignUI = false; ShowCampaignUI = false;
@@ -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;
@@ -368,9 +372,6 @@ namespace Barotrauma
SoundPlayer.OverrideMusicDuration = 18.0f; SoundPlayer.OverrideMusicDuration = 18.0f;
crewDead = false; crewDead = false;
LevelData lvlData = GameMain.GameSession.LevelData;
bool beaconActive = GameMain.GameSession.Level.CheckBeaconActive();
GameMain.GameSession.EndRound("", traitorResults, transitionType); GameMain.GameSession.EndRound("", traitorResults, transitionType);
var continueButton = GameMain.GameSession.RoundSummary?.ContinueButton; var continueButton = GameMain.GameSession.RoundSummary?.ContinueButton;
RoundSummary roundSummary = null; RoundSummary roundSummary = null;
@@ -455,8 +456,6 @@ namespace Barotrauma
} }
} }
lvlData.IsBeaconActive = beaconActive;
SaveUtil.SaveGame(GameMain.GameSession.SavePath); SaveUtil.SaveGame(GameMain.GameSession.SavePath);
} }
else else
@@ -526,6 +525,8 @@ namespace Barotrauma
if (CoroutineManager.IsCoroutineRunning("LevelTransition") || CoroutineManager.IsCoroutineRunning("SubmarineTransition") || gameOver) { return; } if (CoroutineManager.IsCoroutineRunning("LevelTransition") || CoroutineManager.IsCoroutineRunning("SubmarineTransition") || gameOver) { return; }
base.Update(deltaTime); base.Update(deltaTime);
Map?.Radiation.UpdateRadiation(deltaTime);
if (PlayerInput.SecondaryMouseButtonClicked() || if (PlayerInput.SecondaryMouseButtonClicked() ||
PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape)) PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape))
@@ -699,6 +700,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
@@ -39,6 +39,10 @@ namespace Barotrauma
base.Start(); base.Start();
CrewManager.InitSinglePlayerRound(); CrewManager.InitSinglePlayerRound();
foreach (Submarine submarine in Submarine.Loaded)
{
submarine.NeutralizeBallast();
}
if (SpawnOutpost) if (SpawnOutpost)
{ {
@@ -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;
@@ -283,7 +283,7 @@ namespace Barotrauma.Tutorials
doctor.RemoveActiveObjectiveEntity(patient1); doctor.RemoveActiveObjectiveEntity(patient1);
TriggerTutorialSegment(3, GameMain.Config.KeyBindText(InputType.Command)); // Get the patient to medbay TriggerTutorialSegment(3, GameMain.Config.KeyBindText(InputType.Command)); // Get the patient to medbay
while (patient1.CurrentOrder == null || patient1.CurrentOrder.Identifier != "follow") while (patient1.GetCurrentOrderWithTopPriority()?.Order?.Identifier != "follow")
{ {
// TODO: Rework order highlighting for new command UI // TODO: Rework order highlighting for new command UI
// GameMain.GameSession.CrewManager.HighlightOrderButton(patient1, "follow", highlightColor, new Vector2(5, 5)); // GameMain.GameSession.CrewManager.HighlightOrderButton(patient1, "follow", highlightColor, new Vector2(5, 5));
@@ -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;
}
}
mechanic_oxygenContainer = Item.ItemList.Find(i => i.HasTag("mechanic_oxygencontainer")).GetComponent<ItemContainer>();
for (int i = 0; i < mechanic_oxygenContainer.Inventory.Items.Length; i++)
{
foreach (ItemComponent ic in mechanic_oxygenContainer.Inventory.Items[i].Components)
{ {
ic.CanBePicked = true; ic.CanBePicked = true;
} }
} }
mechanic_oxygenContainer = Item.ItemList.Find(i => i.HasTag("mechanic_oxygencontainer")).GetComponent<ItemContainer>();
foreach (Item item in mechanic_oxygenContainer.Inventory.AllItems)
{
foreach (ItemComponent ic in item.Components)
{
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,14 +420,10 @@ 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);
}
}
} }
if (mechanic_deconstructor.InputContainer.Inventory.FindItemByIdentifier("oxygentank") != null && !mechanic_deconstructor.IsActive) if (mechanic_deconstructor.InputContainer.Inventory.FindItemByIdentifier("oxygentank") != null && !mechanic_deconstructor.IsActive)
@@ -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);
@@ -317,7 +317,8 @@ namespace Barotrauma.Tutorials
yield return new WaitForSeconds(2f, false); yield return new WaitForSeconds(2f, false);
TriggerTutorialSegment(4, GameMain.Config.KeyBindText(InputType.Select), GameMain.Config.KeyBindText(InputType.Shoot), GameMain.Config.KeyBindText(InputType.Deselect)); // Kill hammerhead TriggerTutorialSegment(4, GameMain.Config.KeyBindText(InputType.Select), GameMain.Config.KeyBindText(InputType.Shoot), GameMain.Config.KeyBindText(InputType.Deselect)); // Kill hammerhead
officer_hammerhead = SpawnMonster("hammerhead", officer_hammerheadSpawnPos); officer_hammerhead = SpawnMonster("hammerhead", officer_hammerheadSpawnPos);
((EnemyAIController)officer_hammerhead.AIController).StayInsideLevel = false; officer_hammerhead.Params.AI.AvoidAbyss = false;
officer_hammerhead.Params.AI.StayInAbyss = false;
officer_hammerhead.AIController.SelectTarget(officer.AiTarget); officer_hammerhead.AIController.SelectTarget(officer.AiTarget);
SetHighlight(officer_coilgunPeriscope, true); SetHighlight(officer_coilgunPeriscope, true);
float originalDistance = Vector2.Distance(officer_coilgunPeriscope.WorldPosition, officer_hammerheadSpawnPos); float originalDistance = Vector2.Distance(officer_coilgunPeriscope.WorldPosition, officer_hammerheadSpawnPos);
@@ -371,12 +372,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 +384,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 +397,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 +424,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);
@@ -62,7 +62,7 @@ namespace Barotrauma.Tutorials
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
GameMain.GameSession = new GameSession(subInfo, GameModePreset.Tutorial, missionPrefab: null); GameMain.GameSession = new GameSession(subInfo, GameModePreset.Tutorial, missionPrefabs: null);
(GameMain.GameSession.GameMode as TutorialMode).Tutorial = this; (GameMain.GameSession.GameMode as TutorialMode).Tutorial = this;
if (generationParams != null) if (generationParams != null)
@@ -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);
@@ -182,7 +182,8 @@ namespace Barotrauma.Tutorials
protected bool HasOrder(Character character, string identifier, string option = null) protected bool HasOrder(Character character, string identifier, string option = null)
{ {
if (character.CurrentOrder?.Identifier == identifier) var currentOrderInfo = character.GetCurrentOrderWithTopPriority();
if (currentOrderInfo?.Order?.Identifier == identifier)
{ {
if (option == null) if (option == null)
{ {
@@ -190,8 +191,7 @@ namespace Barotrauma.Tutorials
} }
else else
{ {
HumanAIController humanAI = character.AIController as HumanAIController; return currentOrderInfo?.OrderOption == option;
return humanAI.CurrentOrderOption == option;
} }
} }
@@ -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);
@@ -46,6 +48,7 @@ namespace Barotrauma
msgBox.Buttons[0].OnClicked = delegate msgBox.Buttons[0].OnClicked = delegate
{ {
msgBox.Close(); msgBox.Close();
if (GameMain.Client == null) { return true; }
SendState(ReadyStatus.Yes); SendState(ReadyStatus.Yes);
CreateResultsMessage(); CreateResultsMessage();
return true; return true;
@@ -55,6 +58,7 @@ namespace Barotrauma
msgBox.Buttons[1].OnClicked = delegate msgBox.Buttons[1].OnClicked = delegate
{ {
msgBox.Close(); msgBox.Close();
if (GameMain.Client == null) { return true; }
SendState(ReadyStatus.No); SendState(ReadyStatus.No);
CreateResultsMessage(); CreateResultsMessage();
return true; return true;
@@ -63,6 +67,8 @@ namespace Barotrauma
private void CreateResultsMessage() private void CreateResultsMessage()
{ {
if (GameMain.Client == null) { return; }
Vector2 relativeSize = new Vector2(0.2f, 0.3f); Vector2 relativeSize = new Vector2(0.2f, 0.3f);
Point minSize = new Point(300, 400); Point minSize = new Point(300, 400);
resultsBox = new GUIMessageBox(readyCheckHeader, string.Empty, new[] { closeButton }, relativeSize, minSize, type: GUIMessageBox.Type.Vote) { UserData = ResultData, Draggable = true }; resultsBox = new GUIMessageBox(readyCheckHeader, string.Empty, new[] { closeButton }, relativeSize, minSize, type: GUIMessageBox.Type.Vote) { UserData = ResultData, Draggable = true };
@@ -73,7 +79,7 @@ namespace Barotrauma
GUIListBox listBox = new GUIListBox(new RectTransform(new Vector2(1f, 0.8f), resultsBox.Content.RectTransform)) { UserData = UserListData }; GUIListBox listBox = new GUIListBox(new RectTransform(new Vector2(1f, 0.8f), resultsBox.Content.RectTransform)) { UserData = UserListData };
foreach (var (id, status) in Clients) foreach (var (id, _) in Clients)
{ {
Client? client = GameMain.Client.ConnectedClients.FirstOrDefault(c => c.ID == id); Client? client = GameMain.Client.ConnectedClients.FirstOrDefault(c => c.ID == id);
GUIFrame container = new GUIFrame(new RectTransform(new Vector2(1f, 0.15f), listBox.Content.RectTransform), style: "ListBoxElement") { UserData = id }; GUIFrame container = new GUIFrame(new RectTransform(new Vector2(1f, 0.15f), listBox.Content.RectTransform), style: "ListBoxElement") { UserData = id };
@@ -120,7 +126,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;
} }
} }
@@ -130,12 +139,20 @@ namespace Barotrauma
ReadyCheckState state = (ReadyCheckState) inc.ReadByte(); ReadyCheckState state = (ReadyCheckState) inc.ReadByte();
CrewManager? crewManager = GameMain.GameSession?.CrewManager; CrewManager? crewManager = GameMain.GameSession?.CrewManager;
List<Client> otherClients = GameMain.Client.ConnectedClients; List<Client> otherClients = GameMain.Client.ConnectedClients;
if (crewManager == null || otherClients == null) { return; } if (crewManager == null || otherClients == null)
{
if (state == ReadyCheckState.Start)
{
SendState(ReadyStatus.No);
}
return;
}
switch (state) switch (state)
{ {
case ReadyCheckState.Start: case ReadyCheckState.Start:
bool isOwn = false; bool isOwn = false;
byte authorId = 0;
float duration = inc.ReadSingle(); float duration = inc.ReadSingle();
string author = inc.ReadString(); string author = inc.ReadString();
@@ -143,7 +160,8 @@ namespace Barotrauma
if (hasAuthor) if (hasAuthor)
{ {
isOwn = inc.ReadByte() == GameMain.Client.ID; authorId = inc.ReadByte();
isOwn = authorId == GameMain.Client.ID;
} }
ushort clientCount = inc.ReadUInt16(); ushort clientCount = inc.ReadUInt16();
@@ -165,12 +183,21 @@ namespace Barotrauma
{ {
rCheck.CreateMessageBox(author); rCheck.CreateMessageBox(author);
} }
if (hasAuthor && rCheck.Clients.ContainsKey(authorId))
{
rCheck.Clients[authorId] = ReadyStatus.Yes;
}
break; break;
case ReadyCheckState.Update: case ReadyCheckState.Update:
crewManager.ActiveReadyCheck.time = inc.ReadSingle(); float time = inc.ReadSingle();
ReadyStatus newState = (ReadyStatus) inc.ReadByte(); ReadyStatus newState = (ReadyStatus) inc.ReadByte();
byte targetId = inc.ReadByte(); byte targetId = inc.ReadByte();
crewManager.ActiveReadyCheck?.UpdateState(targetId, newState); if (crewManager.ActiveReadyCheck != null)
{
crewManager.ActiveReadyCheck.time = time;
crewManager.ActiveReadyCheck?.UpdateState(targetId, newState);
}
break; break;
case ReadyCheckState.End: case ReadyCheckState.End:
ushort count = inc.ReadUInt16(); ushort count = inc.ReadUInt16();
@@ -190,6 +217,9 @@ namespace Barotrauma
partial void EndReadyCheck() partial void EndReadyCheck()
{ {
if (IsFinished) { return; }
IsFinished = true;
int readyCount = Clients.Count(pair => pair.Value == ReadyStatus.Yes); int readyCount = Clients.Count(pair => pair.Value == ReadyStatus.Yes);
int totalCount = Clients.Count; int totalCount = Clients.Count;
GameMain.Client.AddChatMessage(ChatMessage.Create(string.Empty, readyCheckStatus(readyCount, totalCount), ChatMessageType.Server, null)); GameMain.Client.AddChatMessage(ChatMessage.Create(string.Empty, readyCheckStatus(readyCount, totalCount), ChatMessageType.Server, null));
@@ -16,7 +16,7 @@ namespace Barotrauma
private int jobColumnWidth, characterColumnWidth, statusColumnWidth; private int jobColumnWidth, characterColumnWidth, statusColumnWidth;
private readonly SubmarineInfo sub; private readonly SubmarineInfo sub;
private readonly Mission selectedMission; private readonly List<Mission> selectedMissions;
private readonly Location startLocation, endLocation; private readonly Location startLocation, endLocation;
private readonly GameMode gameMode; private readonly GameMode gameMode;
@@ -32,11 +32,11 @@ namespace Barotrauma
public RoundSummary(SubmarineInfo sub, GameMode gameMode, Mission selectedMission, Location startLocation, Location endLocation) public RoundSummary(SubmarineInfo sub, GameMode gameMode, IEnumerable<Mission> selectedMissions, Location startLocation, Location endLocation)
{ {
this.sub = sub; this.sub = sub;
this.gameMode = gameMode; this.gameMode = gameMode;
this.selectedMission = selectedMission; this.selectedMissions = selectedMissions.ToList();
this.startLocation = startLocation; this.startLocation = startLocation;
this.endLocation = endLocation; this.endLocation = endLocation;
initialLocationReputation = startLocation?.Reputation?.Value ?? 0.0f; initialLocationReputation = startLocation?.Reputation?.Value ?? 0.0f;
@@ -75,7 +75,7 @@ namespace Barotrauma
//crew panel ------------------------------------------------------------------------------- //crew panel -------------------------------------------------------------------------------
GUIFrame crewFrame = new GUIFrame(new RectTransform(new Vector2(0.35f, 0.55f), background.RectTransform, Anchor.TopCenter, minSize: new Point(minWidth, minHeight))); GUIFrame crewFrame = new GUIFrame(new RectTransform(new Vector2(0.35f, 0.45f), background.RectTransform, Anchor.TopCenter, minSize: new Point(minWidth, minHeight)));
GUIFrame crewFrameInner = new GUIFrame(new RectTransform(new Point(crewFrame.Rect.Width - padding * 2, crewFrame.Rect.Height - padding * 2), crewFrame.RectTransform, Anchor.Center), style: "InnerFrame"); GUIFrame crewFrameInner = new GUIFrame(new RectTransform(new Point(crewFrame.Rect.Width - padding * 2, crewFrame.Rect.Height - padding * 2), crewFrame.RectTransform, Anchor.Center), style: "InnerFrame");
var crewContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), crewFrameInner.RectTransform, Anchor.Center)) var crewContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), crewFrameInner.RectTransform, Anchor.Center))
@@ -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.Missions.Any(m => m 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 -------------------------------------------------------------------------------
@@ -200,7 +200,7 @@ namespace Barotrauma
GUIListBox reputationList = new GUIListBox(new RectTransform(Vector2.One, reputationContent.RectTransform)) GUIListBox reputationList = new GUIListBox(new RectTransform(Vector2.One, reputationContent.RectTransform))
{ {
Padding = new Vector4(2, 5, 0, 0) Padding = new Vector4(4, 10, 0, 0) * GUI.Scale
}; };
reputationList.ContentBackground.Color = Color.Transparent; reputationList.ContentBackground.Color = Color.Transparent;
@@ -253,103 +253,122 @@ namespace Barotrauma
//mission panel ------------------------------------------------------------------------------- //mission panel -------------------------------------------------------------------------------
GUIFrame missionframe = new GUIFrame(new RectTransform(new Vector2(0.39f, 0.22f), background.RectTransform, Anchor.TopCenter, minSize: new Point(minWidth, minHeight / 4))); GUIFrame missionframe = new GUIFrame(new RectTransform(new Vector2(0.39f, 0.3f), background.RectTransform, Anchor.TopCenter, minSize: new Point(minWidth, minHeight / 4)));
GUIFrame missionframeInner = new GUIFrame(new RectTransform(new Point(missionframe.Rect.Width - padding * 2, missionframe.Rect.Height - padding * 2), missionframe.RectTransform, Anchor.Center), style: "InnerFrame"); GUILayoutGroup missionFrameContent = new GUILayoutGroup(new RectTransform(new Point(missionframe.Rect.Width - padding * 2, missionframe.Rect.Height - padding * 2), missionframe.RectTransform, Anchor.Center))
{
var missionContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), missionframeInner.RectTransform, Anchor.Center)) Stretch = true,
RelativeSpacing = 0.05f
};
GUIFrame missionframeInner = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.9f), missionFrameContent.RectTransform, Anchor.Center), style: "InnerFrame");
var missionContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.93f), missionframeInner.RectTransform, Anchor.Center))
{ {
RelativeSpacing = 0.05f,
Stretch = true Stretch = true
}; };
List<Mission> missionsToDisplay = new List<Mission>(selectedMissions);
if (!selectedMissions.Any() && startLocation?.SelectedMission != null) { missionsToDisplay.Add(startLocation.SelectedMission); }
if (missionsToDisplay.Any())
{
var missionHeader = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionContent.RectTransform),
TextManager.Get(missionsToDisplay.Count > 1 ? "Missions" : "Mission"), textAlignment: Alignment.TopLeft, font: GUI.SubHeadingFont);
missionHeader.RectTransform.MinSize = new Point(0, (int)(missionHeader.Rect.Height * 1.2f));
}
GUIListBox missionList = new GUIListBox(new RectTransform(Vector2.One, missionContent.RectTransform, Anchor.Center))
{
Padding = new Vector4(4, 10, 0, 0) * GUI.Scale
};
missionList.ContentBackground.Color = Color.Transparent;
ButtonArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), missionFrameContent.RectTransform, Anchor.BottomCenter), isHorizontal: true, childAnchor: Anchor.BottomRight)
{
RelativeSpacing = 0.025f
};
missionFrameContent.Recalculate();
missionContent.Recalculate();
if (!string.IsNullOrWhiteSpace(endMessage)) if (!string.IsNullOrWhiteSpace(endMessage))
{ {
var endText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionContent.RectTransform), var endText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionList.Content.RectTransform),
TextManager.GetServerMessage(endMessage), wrap: true); TextManager.GetServerMessage(endMessage), wrap: true)
{
CanBeFocused = false
};
endText.RectTransform.MinSize = new Point(0, endText.Rect.Height); endText.RectTransform.MinSize = new Point(0, endText.Rect.Height);
var line = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.1f), missionContent.RectTransform), style: "HorizontalLine"); var line = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.1f), missionList.Content.RectTransform), style: "HorizontalLine");
line.RectTransform.NonScaledSize = new Point(line.Rect.Width, GUI.IntScale(5.0f)); line.RectTransform.NonScaledSize = new Point(line.Rect.Width, GUI.IntScale(5.0f));
} }
var missionContentHorizontal = new GUILayoutGroup(new RectTransform(Vector2.One, missionContent.RectTransform), childAnchor: Anchor.TopLeft, isHorizontal: true) foreach (Mission displayedMission in missionsToDisplay)
{ {
RelativeSpacing = 0.025f, var missionContentHorizontal = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.8f), missionList.Content.RectTransform), childAnchor: Anchor.CenterLeft, isHorizontal: true)
Stretch = true {
}; RelativeSpacing = 0.025f,
Stretch = true
};
Mission displayedMission = selectedMission ?? startLocation.SelectedMission; string missionMessage =
string missionMessage = ""; selectedMissions.Contains(displayedMission) ?
GUIImage missionIcon; displayedMission.Completed ? displayedMission.SuccessMessage : displayedMission.FailureMessage :
if (displayedMission != null)
{
missionMessage =
displayedMission == selectedMission ?
displayedMission.Completed ? displayedMission.SuccessMessage : displayedMission.FailureMessage :
displayedMission.Description; displayedMission.Description;
missionIcon = new GUIImage(new RectTransform(new Point(missionContentHorizontal.Rect.Height), missionContentHorizontal.RectTransform), displayedMission.Prefab.Icon, scaleToFit: true) GUIImage missionIcon = new GUIImage(new RectTransform(new Point((int)(missionContentHorizontal.Rect.Height)), missionContentHorizontal.RectTransform), displayedMission.Prefab.Icon, scaleToFit: true)
{ {
Color = displayedMission.Prefab.IconColor Color = displayedMission.Prefab.IconColor
}; };
if (displayedMission == selectedMission) missionIcon.RectTransform.MinSize = new Point((int)(missionContentHorizontal.Rect.Height * 0.9f));
if (selectedMissions.Contains(displayedMission))
{ {
new GUIImage(new RectTransform(Vector2.One, missionIcon.RectTransform), displayedMission.Completed ? "MissionCompletedIcon" : "MissionFailedIcon", scaleToFit: true); new GUIImage(new RectTransform(Vector2.One, missionIcon.RectTransform), displayedMission.Completed ? "MissionCompletedIcon" : "MissionFailedIcon", scaleToFit: true);
} }
}
else
{
missionIcon = new GUIImage(new RectTransform(new Point(missionContentHorizontal.Rect.Height), missionContentHorizontal.RectTransform), style: "NoMissionIcon", scaleToFit: true);
}
var missionTextContent = new GUILayoutGroup(new RectTransform(Vector2.One, missionContentHorizontal.RectTransform))
{
RelativeSpacing = 0.05f
};
missionContentHorizontal.Recalculate();
missionContent.Recalculate();
missionIcon.RectTransform.MinSize = new Point(0, missionContentHorizontal.Rect.Height);
missionTextContent.RectTransform.MaxSize = new Point(int.MaxValue, missionIcon.Rect.Width);
GUITextBlock missionDescription = null; var missionTextContent = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 0.8f), missionContentHorizontal.RectTransform))
if (displayedMission == null) {
{ RelativeSpacing = 0.05f
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
TextManager.Get("nomission"), font: GUI.LargeFont); displayedMission.Name, font: GUI.SubHeadingFont);
}
else
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
TextManager.AddPunctuation(':', TextManager.Get("Mission"), displayedMission.Name), font: GUI.SubHeadingFont);
missionDescription = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
missionMessage, wrap: true); missionMessage, wrap: true);
if (displayedMission == selectedMission && displayedMission.Completed) if (selectedMissions.Contains(displayedMission) && displayedMission.Completed && displayedMission.Reward > 0)
{ {
string rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", displayedMission.Reward)); string rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", displayedMission.Reward));
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
TextManager.GetWithVariable("MissionReward", "[reward]", rewardText)); TextManager.GetWithVariable("MissionReward", "[reward]", rewardText));
} }
if (displayedMission != missionsToDisplay.Last())
{
var spacing = new GUIFrame(new RectTransform(new Vector2(1.0f, 1.0f), missionList.Content.RectTransform) { MaxSize = new Point(int.MaxValue, GUI.IntScale(15)) }, style: null);
new GUIFrame(new RectTransform(new Vector2(0.8f, 1.0f), spacing.RectTransform, Anchor.Center) { RelativeOffset = new Vector2(0.1f, 0.0f) }, "HorizontalLine");
}
} }
ButtonArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), missionContent.RectTransform, Anchor.BottomCenter), isHorizontal: true, childAnchor: Anchor.BottomRight) if (!missionsToDisplay.Any())
{ {
IgnoreLayoutGroups = true, var missionContentHorizontal = new GUILayoutGroup(new RectTransform(Vector2.One, missionList.Content.RectTransform), childAnchor: Anchor.TopLeft, isHorizontal: true)
RelativeSpacing = 0.025f {
}; RelativeSpacing = 0.025f,
Stretch = true
};
GUIImage missionIcon = new GUIImage(new RectTransform(new Point((int)(missionContentHorizontal.Rect.Height * 0.7f)), missionContentHorizontal.RectTransform), style: "NoMissionIcon", scaleToFit: true);
missionIcon.RectTransform.MinSize = new Point((int)(missionContentHorizontal.Rect.Height * 0.7f));
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionContentHorizontal.RectTransform),
TextManager.Get("nomission"), font: GUI.LargeFont);
}
/*missionContentHorizontal.Recalculate();
missionContent.Recalculate();
missionIcon.RectTransform.MinSize = new Point(0, missionContentHorizontal.Rect.Height);
missionTextContent.RectTransform.MaxSize = new Point(int.MaxValue, missionIcon.Rect.Width);*/
ContinueButton = new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), ButtonArea.RectTransform), TextManager.Get("Close")); ContinueButton = new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), ButtonArea.RectTransform), TextManager.Get("Close"));
ButtonArea.RectTransform.NonScaledSize = new Point(ButtonArea.Rect.Width, ContinueButton.Rect.Height); ButtonArea.RectTransform.NonScaledSize = new Point(ButtonArea.Rect.Width, ContinueButton.Rect.Height);
ButtonArea.RectTransform.IsFixedSize = true; ButtonArea.RectTransform.IsFixedSize = true;
missionContent.Recalculate(); missionFrameContent.Recalculate();
//description overlapping with the buttons -> switch to small font
if (missionDescription != null && missionDescription.Rect.Y + missionDescription.TextSize.Y > ButtonArea.Rect.Y)
{
missionDescription.Font = GUI.Style.SmallFont;
//still overlapping -> shorten the text
if (missionDescription.Rect.Y + missionDescription.TextSize.Y > ButtonArea.Rect.Y && missionDescription.WrappedText.Contains('\n'))
{
missionDescription.ToolTip = missionDescription.Text;
missionDescription.Text = missionDescription.WrappedText.Split('\n').First() + "...";
}
}
// set layout ------------------------------------------------------------------- // set layout -------------------------------------------------------------------
@@ -396,15 +415,21 @@ namespace Barotrauma
textTag = "RoundSummaryLeaving"; textTag = "RoundSummaryLeaving";
break; break;
case CampaignMode.TransitionType.ProgressToNextLocation: case CampaignMode.TransitionType.ProgressToNextLocation:
case CampaignMode.TransitionType.ProgressToNextEmptyLocation:
locationName = endLocation?.Name; locationName = endLocation?.Name;
textTag = "RoundSummaryProgress"; textTag = "RoundSummaryProgress";
break; break;
case CampaignMode.TransitionType.ProgressToNextEmptyLocation:
locationName = endLocation?.Name;
textTag = "RoundSummaryProgressToEmptyLocation";
break;
case CampaignMode.TransitionType.ReturnToPreviousLocation: case CampaignMode.TransitionType.ReturnToPreviousLocation:
case CampaignMode.TransitionType.ReturnToPreviousEmptyLocation:
locationName = startLocation?.Name; locationName = startLocation?.Name;
textTag = "RoundSummaryReturn"; textTag = "RoundSummaryReturn";
break; break;
case CampaignMode.TransitionType.ReturnToPreviousEmptyLocation:
locationName = startLocation?.Name;
textTag = "RoundSummaryReturnToEmptyLocation";
break;
default: default:
textTag = Submarine.MainSub.AtEndPosition ? "RoundSummaryProgress" : "RoundSummaryReturn"; textTag = Submarine.MainSub.AtEndPosition ? "RoundSummaryProgress" : "RoundSummaryReturn";
break; break;
@@ -456,7 +481,7 @@ namespace Barotrauma
GUIListBox crewList = new GUIListBox(new RectTransform(Vector2.One, parent.RectTransform)) GUIListBox crewList = new GUIListBox(new RectTransform(Vector2.One, parent.RectTransform))
{ {
Padding = new Vector4(2, 5, 0, 0), Padding = new Vector4(4, 10, 0, 0) * GUI.Scale,
AutoHideScrollBar = false AutoHideScrollBar = false
}; };
crewList.ContentBackground.Color = Color.Transparent; crewList.ContentBackground.Color = Color.Transparent;
@@ -503,7 +528,7 @@ namespace Barotrauma
Character character = characterInfo.Character; Character character = characterInfo.Character;
if (character == null || character.IsDead) if (character == null || character.IsDead)
{ {
if (character == null && characterInfo.IsNewHire) if (character == null && characterInfo.IsNewHire && characterInfo.CauseOfDeath == null)
{ {
statusText = TextManager.Get("CampaignCrew.NewHire"); statusText = TextManager.Get("CampaignCrew.NewHire");
statusColor = GUI.Style.Blue; statusColor = GUI.Style.Blue;
@@ -551,7 +576,7 @@ namespace Barotrauma
string name, float reputation, float normalizedReputation, float initialReputation, string name, float reputation, float normalizedReputation, float initialReputation,
string shortDescription, string fullDescription, Sprite icon, Sprite backgroundPortrait, Color iconColor) string shortDescription, string fullDescription, Sprite icon, Sprite backgroundPortrait, Color iconColor)
{ {
var factionFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.3f), parent.RectTransform), style: null); var factionFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.35f), parent.RectTransform), style: null);
if (backgroundPortrait != null) if (backgroundPortrait != null)
{ {
@@ -65,10 +65,11 @@ namespace Barotrauma
keyMapping[(int)InputType.Voice] = new KeyOrMouse(Keys.V); keyMapping[(int)InputType.Voice] = new KeyOrMouse(Keys.V);
keyMapping[(int)InputType.LocalVoice] = new KeyOrMouse(Keys.B); keyMapping[(int)InputType.LocalVoice] = new KeyOrMouse(Keys.B);
keyMapping[(int)InputType.Command] = new KeyOrMouse(MouseButton.MiddleMouse); keyMapping[(int)InputType.Command] = new KeyOrMouse(MouseButton.MiddleMouse);
#if DEBUG
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);
#endif
keyMapping[(int)InputType.TakeHalfFromInventorySlot] = new KeyOrMouse(Keys.LeftShift);
keyMapping[(int)InputType.TakeOneFromInventorySlot] = new KeyOrMouse(Keys.LeftControl);
if (Language == "French") if (Language == "French")
{ {
@@ -175,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))
@@ -225,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)
@@ -244,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;
@@ -263,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;
} }
@@ -707,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"),
@@ -727,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);
@@ -745,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)
@@ -811,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);
@@ -827,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);
@@ -837,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) =>
@@ -1016,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,
@@ -1098,13 +1177,14 @@ namespace Barotrauma
style: "GUISlider", barSize: 0.05f) style: "GUISlider", barSize: 0.05f)
{ {
UserData = micVolumeText, UserData = micVolumeText,
Range = new Vector2(0,540), Range = new Vector2(0, ((float)VoipConfig.BUFFER_SIZE / (float)VoipConfig.FREQUENCY) * 1000.0f * 25.0f),
Step = 1.0f / 9.0f Step = 1.0f / 25.0f
}; };
cutoffPreventionSlider.BarScrollValue = VoiceChatCutoffPrevention; cutoffPreventionSlider.BarScrollValue = VoiceChatCutoffPrevention;
cutoffPreventionSlider.OnMoved = (scrollBar, scroll) => cutoffPreventionSlider.OnMoved = (scrollBar, scroll) =>
{ {
VoiceChatCutoffPrevention = (int)scrollBar.BarScrollValue; int bufferMsLength = (int)(((float)VoipConfig.BUFFER_SIZE / (float)VoipConfig.FREQUENCY) * 1000.0f);
VoiceChatCutoffPrevention = (int)Math.Round(scrollBar.BarScrollValue / bufferMsLength) * bufferMsLength;
cutoffPreventionText.Text = TextManager.Get("CutoffPrevention") + cutoffPreventionText.Text = TextManager.Get("CutoffPrevention") +
" " + TextManager.GetWithVariable("timeformatmilliseconds", "[milliseconds]", VoiceChatCutoffPrevention.ToString()); " " + TextManager.GetWithVariable("timeformatmilliseconds", "[milliseconds]", VoiceChatCutoffPrevention.ToString());
return true; return true;
@@ -1142,6 +1222,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;
@@ -1185,7 +1266,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);
@@ -1201,19 +1282,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 };
@@ -1228,14 +1311,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 };
@@ -1246,11 +1332,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),
@@ -1599,7 +1694,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;
} }
@@ -1618,7 +1713,9 @@ 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;
} }
@@ -1709,22 +1806,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 || !IsInLimbSlot(doubleClickedItem, InvSlotType.Any))
{
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);
@@ -931,8 +948,8 @@ namespace Barotrauma
item.Remove(); item.Remove();
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;
} }
} }
@@ -86,12 +86,12 @@ namespace Barotrauma.Items.Components
float scale = VineScale * vine.VineStep; float scale = VineScale * vine.VineStep;
if (VineAtlas != null) if (VineAtlas != null && VineAtlas.Loaded)
{ {
spriteBatch.Draw(VineAtlas.Texture, pos + vine.offset, vineSprite.SourceRect, color, 0f, vineSprite.AbsoluteOrigin, scale, SpriteEffects.None, layer3); spriteBatch.Draw(VineAtlas.Texture, pos + vine.offset, vineSprite.SourceRect, color, 0f, vineSprite.AbsoluteOrigin, scale, SpriteEffects.None, layer3);
} }
if (DecayAtlas != null) if (DecayAtlas != null && DecayAtlas.Loaded)
{ {
spriteBatch.Draw(DecayAtlas.Texture, pos, vineSprite.SourceRect, vine.HealthColor, 0f, vineSprite.AbsoluteOrigin, scale, SpriteEffects.None, layer2); spriteBatch.Draw(DecayAtlas.Texture, pos, vineSprite.SourceRect, vine.HealthColor, 0f, vineSprite.AbsoluteOrigin, scale, SpriteEffects.None, layer2);
} }
@@ -2,9 +2,6 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Barotrauma.Items.Components namespace Barotrauma.Items.Components
{ {
@@ -17,7 +14,11 @@ namespace Barotrauma.Items.Components
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) { return; } if (!IsActive || picker == null || !CanBeAttached(picker) || !picker.IsKeyDown(InputType.Aim) || picker != Character.Controlled)
{
Drawable = false;
return;
}
Vector2 gridPos = picker.Position; Vector2 gridPos = picker.Position;
Vector2 roundedGridPos = new Vector2( Vector2 roundedGridPos = new Vector2(
@@ -46,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,
@@ -55,13 +55,8 @@ namespace Barotrauma.Items.Components
{ {
if (character == null || !character.IsKeyDown(InputType.Aim)) return; if (character == null || !character.IsKeyDown(InputType.Aim)) return;
#if DEBUG
if (PlayerInput.KeyHit(InputType.PreviousFireMode)) if (PlayerInput.KeyHit(InputType.PreviousFireMode))
#else
if (PlayerInput.MouseWheelDownClicked())
#endif
{ {
if (spraySetting > 0) if (spraySetting > 0)
{ {
spraySetting--; spraySetting--;
@@ -74,11 +69,7 @@ namespace Barotrauma.Items.Components
targetSections.Clear(); targetSections.Clear();
} }
#if DEBUG
if (PlayerInput.KeyHit(InputType.NextFireMode)) if (PlayerInput.KeyHit(InputType.NextFireMode))
#else
if (PlayerInput.MouseWheelUpClicked())
#endif
{ {
if (spraySetting < 2) if (spraySetting < 2)
{ {
@@ -139,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;
@@ -248,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)
@@ -147,6 +179,23 @@ namespace Barotrauma.Items.Components
{ {
CanBeFocused = false CanBeFocused = false
}; };
// Expand the frame vertically if it's too small to fit the text
if (label != null && label.RectTransform.RelativeSize.Y > 0.5f)
{
int newHeight = (int)(GuiFrame.Rect.Height + (2 * (label.RectTransform.RelativeSize.Y - 0.5f) * content.Rect.Height));
if (newHeight > GuiFrame.RectTransform.MaxSize.Y)
{
Point newMaxSize = GuiFrame.RectTransform.MaxSize;
newMaxSize.Y = newHeight;
GuiFrame.RectTransform.MaxSize = newMaxSize;
}
GuiFrame.RectTransform.Resize(new Point(GuiFrame.Rect.Width, newHeight));
content.RectTransform.Resize(GuiFrame.Rect.Size - GUIStyle.ItemFrameMargin);
label.CalculateHeightFromText();
guiCustomComponent.RectTransform.Resize(new Vector2(1.0f, Math.Max(1.0f - label.RectTransform.RelativeSize.Y, minInventoryAreaSize)));
}
Inventory.RectTransform = guiCustomComponent.RectTransform; Inventory.RectTransform = guiCustomComponent.RectTransform;
} }
@@ -173,15 +222,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;
@@ -257,13 +300,11 @@ namespace Barotrauma.Items.Components
spriteEffects |= MathUtils.NearlyEqual(ItemRotation % 180, 90.0f) ? SpriteEffects.FlipHorizontally : SpriteEffects.FlipVertically; spriteEffects |= MathUtils.NearlyEqual(ItemRotation % 180, 90.0f) ? SpriteEffects.FlipHorizontally : SpriteEffects.FlipVertically;
} }
bool isWiringMode = 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 +354,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;
@@ -41,15 +47,22 @@ namespace Barotrauma.Items.Components
{ {
textBlock = null; textBlock = null;
} }
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;
@@ -204,11 +236,33 @@ 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;
@@ -21,18 +21,14 @@ namespace Barotrauma.Items.Components
private readonly List<Submarine> displayedSubs = new List<Submarine>(); private readonly List<Submarine> displayedSubs = new List<Submarine>();
private Point prevResolution;
partial void InitProjSpecific(XElement element) partial void InitProjSpecific(XElement element)
{ {
noPowerTip = TextManager.Get("SteeringNoPowerTip"); noPowerTip = TextManager.Get("SteeringNoPowerTip");
CreateGUI(); CreateGUI();
} }
protected override void OnResolutionChanged()
{
base.OnResolutionChanged();
CreateHUD();
}
protected override void CreateGUI() protected override void CreateGUI()
{ {
GuiFrame.RectTransform.RelativeOffset = new Vector2(0.05f, 0.0f); GuiFrame.RectTransform.RelativeOffset = new Vector2(0.05f, 0.0f);
@@ -76,15 +72,10 @@ namespace Barotrauma.Items.Components
hullInfoFrame.AddToGUIUpdateList(order: 1); hullInfoFrame.AddToGUIUpdateList(order: 1);
} }
public override void OnMapLoaded()
{
base.OnMapLoaded();
CreateHUD();
}
private void CreateHUD() private void CreateHUD()
{ {
submarineContainer.ClearChildren(); prevResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
submarineContainer?.ClearChildren();
if (item.Submarine == null) { return; } if (item.Submarine == null) { return; }
@@ -94,19 +85,15 @@ namespace Barotrauma.Items.Components
displayedSubs.AddRange(item.Submarine.DockedTo); displayedSubs.AddRange(item.Submarine.DockedTo);
} }
public override void FlipX(bool relativeToSub)
{
CreateHUD();
}
public override void UpdateHUD(Character character, float deltaTime, Camera cam) public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{ {
//recreate HUD if the subs we should display have changed //recreate HUD if the subs we should display have changed
if ((item.Submarine == null && displayedSubs.Count > 0) || //item not inside a sub anymore, but display is still showing subs if ((item.Submarine == null && displayedSubs.Count > 0) || //item not inside a sub anymore, but display is still showing subs
!displayedSubs.Contains(item.Submarine) || //current sub not displayer !displayedSubs.Contains(item.Submarine) || //current sub not displayer
item.Submarine.DockedTo.Any(s => !displayedSubs.Contains(s)) || //some of the docked subs not diplayed prevResolution.X != GameMain.GraphicsWidth || prevResolution.Y != GameMain.GraphicsHeight || //resolution changed
!submarineContainer.Children.Any() || // We lack a GUI item.Submarine.DockedTo.Any(s => !displayedSubs.Contains(s)) || //some of the docked subs not diplayed
displayedSubs.Any(s => s != item.Submarine && !item.Submarine.DockedTo.Contains(s))) //displaying a sub that shouldn't be displayed !submarineContainer.Children.Any() || // We lack a GUI
displayedSubs.Any(s => s != item.Submarine && !item.Submarine.DockedTo.Contains(s))) //displaying a sub that shouldn't be displayed
{ {
CreateHUD(); CreateHUD();
} }
@@ -215,15 +215,33 @@ namespace Barotrauma.Items.Components
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{ {
int msgStartPos = msg.BitPosition;
float flowPercentage = msg.ReadRangedInteger(-10, 10) * 10.0f;
bool isActive = msg.ReadBoolean();
bool hijacked = msg.ReadBoolean();
float? targetLevel;
if (msg.ReadBoolean())
{
targetLevel = msg.ReadSingle();
}
else
{
targetLevel = null;
}
if (correctionTimer > 0.0f) if (correctionTimer > 0.0f)
{ {
StartDelayedCorrection(type, msg.ExtractBits(5 + 1), sendingTime); int msgLength = msg.BitPosition - msgStartPos;
msg.BitPosition = msgStartPos;
StartDelayedCorrection(type, msg.ExtractBits(msgLength), sendingTime);
return; return;
} }
FlowPercentage = msg.ReadRangedInteger(-10, 10) * 10.0f; FlowPercentage = flowPercentage;
IsActive = msg.ReadBoolean(); IsActive = isActive;
Hijacked = msg.ReadBoolean(); Hijacked = hijacked;
TargetLevel = targetLevel;
} }
} }
} }
@@ -502,8 +502,16 @@ namespace Barotrauma.Items.Components
{ {
if (item.Removed) { return; } if (item.Removed) { return; }
Vector2 clampedOptimalTurbineOutput = optimalTurbineOutput;
Vector2 clampedAllowedTurbineOutput = allowedTurbineOutput;
if (clampedOptimalTurbineOutput.X > 100.0f)
{
clampedOptimalTurbineOutput = new Vector2(92.0f, 110.0f);
clampedAllowedTurbineOutput = new Vector2(85.0f, 110.0f);
}
DrawMeter(spriteBatch, container.Rect, DrawMeter(spriteBatch, container.Rect,
turbineOutputMeter, TurbineOutput, new Vector2(0.0f, 100.0f), optimalTurbineOutput, allowedTurbineOutput); turbineOutputMeter, TurbineOutput, new Vector2(0.0f, 100.0f), clampedOptimalTurbineOutput, clampedAllowedTurbineOutput);
} }
public override void UpdateHUD(Character character, float deltaTime, Camera cam) public override void UpdateHUD(Character character, float deltaTime, Camera cam)
@@ -15,7 +15,8 @@ namespace Barotrauma.Items.Components
public enum BlipType public enum BlipType
{ {
Default, Default,
Disruption Disruption,
Destructible
} }
private PathFinder pathFinder; private PathFinder pathFinder;
@@ -54,7 +55,7 @@ namespace Barotrauma.Items.Components
private Sprite sonarBlip; private Sprite sonarBlip;
private Sprite lineSprite; private Sprite lineSprite;
private readonly Dictionary<string, Sprite> targetIcons = new Dictionary<string, Sprite>(); private readonly Dictionary<string, Tuple<Sprite, Color>> targetIcons = new Dictionary<string, Tuple<Sprite, Color>>();
private float displayBorderSize; private float displayBorderSize;
@@ -70,6 +71,14 @@ namespace Barotrauma.Items.Components
private float showDirectionalIndicatorTimer; private float showDirectionalIndicatorTimer;
private readonly List<LevelObject> nearbyObjects = new List<LevelObject>();
private const float NearbyObjectUpdateInterval = 1.0f;
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>>();
@@ -109,6 +118,10 @@ namespace Barotrauma.Items.Components
{ {
BlipType.Disruption, BlipType.Disruption,
new Color[] { Color.TransparentBlack, new Color(254, 68, 19), new Color(255, 220, 62), new Color(255, 255, 255) } new Color[] { Color.TransparentBlack, new Color(254, 68, 19), new Color(255, 220, 62), new Color(255, 255, 255) }
},
{
BlipType.Destructible,
new Color[] { Color.TransparentBlack, new Color(74, 113, 75) * 0.8f, new Color(151, 236, 172) * 0.8f, new Color(153, 217, 234) * 0.8f }
} }
}; };
@@ -124,11 +137,22 @@ namespace Barotrauma.Items.Components
private readonly List<GUITextBlock> textBlocksToScaleAndNormalize = new List<GUITextBlock>(); private readonly List<GUITextBlock> textBlocksToScaleAndNormalize = new List<GUITextBlock>();
private bool isConnectedToSteering;
private static string caveLabel;
private bool AllowUsingMineralScanner =>
HasMineralScanner && !isConnectedToSteering;
partial void InitProjSpecific(XElement element) partial void InitProjSpecific(XElement element)
{ {
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())
@@ -161,7 +185,9 @@ namespace Barotrauma.Items.Components
break; break;
case "icon": case "icon":
var targetIconSprite = new Sprite(subElement); var targetIconSprite = new Sprite(subElement);
targetIcons.Add(subElement.GetAttributeString("identifier", ""), targetIconSprite); var color = subElement.GetAttributeColor("color", Color.White);
targetIcons.Add(subElement.GetAttributeString("identifier", ""),
new Tuple<Sprite, Color>(targetIconSprite, color));
break; break;
} }
} }
@@ -176,16 +202,20 @@ namespace Barotrauma.Items.Components
protected override void CreateGUI() protected override void CreateGUI()
{ {
bool isConnectedToSteering = item.GetComponent<Steering>() != null; isConnectedToSteering = item.GetComponent<Steering>() != null;
Vector2 size = isConnectedToSteering ? controlBoxSize : new Vector2(controlBoxSize.X * 2.0f, controlBoxSize.Y); Vector2 size = isConnectedToSteering ? controlBoxSize : new Vector2(0.46f, 0.4f);
controlContainer = new GUIFrame(new RectTransform(size, GuiFrame.RectTransform, Anchor.BottomRight, Pivot.BottomLeft), "ItemUI"); controlContainer = new GUIFrame(new RectTransform(size, GuiFrame.RectTransform, Anchor.BottomLeft), "ItemUI");
if (!isConnectedToSteering && !GUI.IsFourByThree())
{
controlContainer.RectTransform.MaxSize = new Point((int)(380 * GUI.xScale), (int)(300 * GUI.yScale));
}
var paddedControlContainer = new GUIFrame(new RectTransform(controlContainer.Rect.Size - GUIStyle.ItemFrameMargin, controlContainer.RectTransform, Anchor.Center) var paddedControlContainer = new GUIFrame(new RectTransform(controlContainer.Rect.Size - GUIStyle.ItemFrameMargin, controlContainer.RectTransform, Anchor.Center)
{ {
AbsoluteOffset = GUIStyle.ItemFrameOffset AbsoluteOffset = GUIStyle.ItemFrameOffset
}, style: null); }, style: null);
// Based on the height difference to the steering control box so that the elements keep the same size // Based on the height difference to the steering control box so that the elements keep the same size
float extraHeight = 0.03f; float extraHeight = 0.0694f;
var sonarModeArea = new GUIFrame(new RectTransform(new Vector2(1, 0.4f + extraHeight), paddedControlContainer.RectTransform, Anchor.TopCenter), style: null); var sonarModeArea = new GUIFrame(new RectTransform(new Vector2(1, 0.4f + extraHeight), paddedControlContainer.RectTransform, Anchor.TopCenter), style: null);
SonarModeSwitch = new GUIButton(new RectTransform(new Vector2(0.2f, 1), sonarModeArea.RectTransform), string.Empty, style: "SwitchVertical") SonarModeSwitch = new GUIButton(new RectTransform(new Vector2(0.2f, 1), sonarModeArea.RectTransform), string.Empty, style: "SwitchVertical")
{ {
@@ -224,6 +254,8 @@ namespace Barotrauma.Items.Components
}; };
passiveTickBox.TextBlock.OverrideTextColor(GUI.Style.TextColor); passiveTickBox.TextBlock.OverrideTextColor(GUI.Style.TextColor);
activeTickBox.TextBlock.OverrideTextColor(GUI.Style.TextColor); activeTickBox.TextBlock.OverrideTextColor(GUI.Style.TextColor);
textBlocksToScaleAndNormalize.Clear();
textBlocksToScaleAndNormalize.Add(passiveTickBox.TextBlock); textBlocksToScaleAndNormalize.Add(passiveTickBox.TextBlock);
textBlocksToScaleAndNormalize.Add(activeTickBox.TextBlock); textBlocksToScaleAndNormalize.Add(activeTickBox.TextBlock);
@@ -249,7 +281,8 @@ namespace Barotrauma.Items.Components
} }
}; };
new GUIFrame(new RectTransform(new Vector2(0.8f, 0.01f), paddedControlContainer.RectTransform, Anchor.Center), style: "HorizontalLine"); new GUIFrame(new RectTransform(new Vector2(0.8f, 0.01f), paddedControlContainer.RectTransform, Anchor.Center), style: "HorizontalLine")
{ UserData = "horizontalline" };
var directionalModeFrame = new GUIFrame(new RectTransform(new Vector2(1, 0.45f), lowerAreaFrame.RectTransform, Anchor.BottomCenter), style: null); var directionalModeFrame = new GUIFrame(new RectTransform(new Vector2(1, 0.45f), lowerAreaFrame.RectTransform, Anchor.BottomCenter), style: null);
directionalModeSwitch = new GUIButton(new RectTransform(new Vector2(0.3f, 0.8f), directionalModeFrame.RectTransform, Anchor.CenterLeft), string.Empty, style: "SwitchHorizontal") directionalModeSwitch = new GUIButton(new RectTransform(new Vector2(0.3f, 0.8f), directionalModeFrame.RectTransform, Anchor.CenterLeft), string.Empty, style: "SwitchHorizontal")
@@ -270,6 +303,15 @@ namespace Barotrauma.Items.Components
TextManager.Get("SonarDirectionalPing"), GUI.Style.TextColor, GUI.SubHeadingFont, Alignment.CenterLeft); TextManager.Get("SonarDirectionalPing"), GUI.Style.TextColor, GUI.SubHeadingFont, Alignment.CenterLeft);
textBlocksToScaleAndNormalize.Add(directionalModeSwitchText); textBlocksToScaleAndNormalize.Add(directionalModeSwitchText);
if (AllowUsingMineralScanner)
{
AddMineralScannerSwitchToGUI();
}
else
{
mineralScannerSwitch = null;
}
GuiFrame.CanBeFocused = false; GuiFrame.CanBeFocused = false;
GUITextBlock.AutoScaleAndNormalize(textBlocksToScaleAndNormalize); GUITextBlock.AutoScaleAndNormalize(textBlocksToScaleAndNormalize);
@@ -284,12 +326,17 @@ namespace Barotrauma.Items.Components
if (isConnectedToSteering) if (isConnectedToSteering)
{ {
controlContainer.RectTransform.RelativeOffset = controlBoxOffset; controlContainer.RectTransform.RelativeOffset = controlBoxOffset;
controlContainer.RectTransform.SetPosition(Anchor.TopLeft); controlContainer.RectTransform.SetPosition(Anchor.TopRight);
sonarView.RectTransform.ScaleBasis = ScaleBasis.Smallest; sonarView.RectTransform.ScaleBasis = ScaleBasis.Smallest;
sonarView.RectTransform.SetPosition(Anchor.CenterRight); sonarView.RectTransform.SetPosition(Anchor.CenterLeft);
sonarView.RectTransform.Resize(GUISizeCalculation); sonarView.RectTransform.Resize(GUISizeCalculation);
GUITextBlock.AutoScaleAndNormalize(textBlocksToScaleAndNormalize); GUITextBlock.AutoScaleAndNormalize(textBlocksToScaleAndNormalize);
} }
else if (GUI.RelativeHorizontalAspectRatio > 0.75f)
{
sonarView.RectTransform.RelativeOffset = new Vector2(0.13f * GUI.RelativeHorizontalAspectRatio, 0);
sonarView.RectTransform.SetPosition(Anchor.BottomRight);
}
} }
private void SetPingDirection(Vector2 direction) private void SetPingDirection(Vector2 direction)
@@ -306,20 +353,39 @@ namespace Barotrauma.Items.Components
{ {
base.OnItemLoaded(); base.OnItemLoaded();
zoomSlider.BarScroll = MathUtils.InverseLerp(MinZoom, MaxZoom, zoom); zoomSlider.BarScroll = MathUtils.InverseLerp(MinZoom, MaxZoom, zoom);
if (HasMineralScanner) { AddMineralScannerSwitchToGUI(); } if (AllowUsingMineralScanner && mineralScannerSwitch == null)
{
AddMineralScannerSwitchToGUI();
GUITextBlock.AutoScaleAndNormalize(textBlocksToScaleAndNormalize);
}
//make the sonarView customcomponent render the steering view so it gets drawn in front of the sonar //make the sonarView customcomponent render the steering view so it gets drawn in front of the sonar
item.GetComponent<Steering>()?.AttachToSonarHUD(sonarView); item.GetComponent<Steering>()?.AttachToSonarHUD(sonarView);
} }
private void AddMineralScannerSwitchToGUI() private void AddMineralScannerSwitchToGUI()
{ {
// First adjust other elements of the lower area // First adjust other elements to make room for the additional switch
zoomSlider.Parent.RectTransform.RelativeSize = new Vector2(1.0f, 0.3f); controlContainer.RectTransform.RelativeSize = new Vector2(
directionalModeSwitch.Parent.RectTransform.RelativeSize = new Vector2(1.0f, 0.3f); controlContainer.RectTransform.RelativeSize.X,
controlContainer.RectTransform.RelativeSize.Y * 1.25f);
SonarModeSwitch.Parent.RectTransform.RelativeSize = new Vector2(
SonarModeSwitch.Parent.RectTransform.RelativeSize.X,
SonarModeSwitch.Parent.RectTransform.RelativeSize.Y * 0.8f);
lowerAreaFrame.Parent.GetChildByUserData("horizontalline").RectTransform.RelativeOffset =
new Vector2(0.0f, -0.1f);
lowerAreaFrame.RectTransform.RelativeSize = new Vector2(
lowerAreaFrame.RectTransform.RelativeSize.X,
lowerAreaFrame.RectTransform.RelativeSize.Y * 1.2f);
zoomSlider.Parent.RectTransform.RelativeSize = new Vector2(
zoomSlider.Parent.RectTransform.RelativeSize.X,
zoomSlider.Parent.RectTransform.RelativeSize.Y * (2.0f / 3.0f));
directionalModeSwitch.Parent.RectTransform.RelativeSize = new Vector2(
directionalModeSwitch.Parent.RectTransform.RelativeSize.X,
zoomSlider.Parent.RectTransform.RelativeSize.Y);
directionalModeSwitch.Parent.RectTransform.SetPosition(Anchor.Center); directionalModeSwitch.Parent.RectTransform.SetPosition(Anchor.Center);
// Then add the scanner switch // Then add the scanner switch
var mineralScannerFrame = new GUIFrame(new RectTransform(new Vector2(1, 0.3f), lowerAreaFrame.RectTransform, Anchor.BottomCenter), style: null); var mineralScannerFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, zoomSlider.Parent.RectTransform.RelativeSize.Y), lowerAreaFrame.RectTransform, Anchor.BottomCenter), style: null);
mineralScannerSwitch = new GUIButton(new RectTransform(new Vector2(0.3f, 0.8f), mineralScannerFrame.RectTransform, Anchor.CenterLeft), string.Empty, style: "SwitchHorizontal") mineralScannerSwitch = new GUIButton(new RectTransform(new Vector2(0.3f, 0.8f), mineralScannerFrame.RectTransform, Anchor.CenterLeft), string.Empty, style: "SwitchHorizontal")
{ {
OnClicked = (button, data) => OnClicked = (button, data) =>
@@ -337,7 +403,6 @@ namespace Barotrauma.Items.Components
var mineralScannerSwitchText = new GUITextBlock(new RectTransform(new Vector2(0.7f, 1), mineralScannerFrame.RectTransform, Anchor.CenterRight), var mineralScannerSwitchText = new GUITextBlock(new RectTransform(new Vector2(0.7f, 1), mineralScannerFrame.RectTransform, Anchor.CenterRight),
TextManager.Get("SonarMineralScanner"), GUI.Style.TextColor, GUI.SubHeadingFont, Alignment.CenterLeft); TextManager.Get("SonarMineralScanner"), GUI.Style.TextColor, GUI.SubHeadingFont, Alignment.CenterLeft);
textBlocksToScaleAndNormalize.Add(mineralScannerSwitchText); textBlocksToScaleAndNormalize.Add(mineralScannerSwitchText);
GUITextBlock.AutoScaleAndNormalize(textBlocksToScaleAndNormalize);
} }
public override void UpdateHUD(Character character, float deltaTime, Camera cam) public override void UpdateHUD(Character character, float deltaTime, Camera cam)
@@ -358,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;
@@ -382,7 +467,7 @@ namespace Barotrauma.Items.Components
Vector2.DistanceSquared(sonarView.Rect.Center.ToVector2(), PlayerInput.MousePosition) < Vector2.DistanceSquared(sonarView.Rect.Center.ToVector2(), PlayerInput.MousePosition) <
(sonarView.Rect.Width / 2 * sonarView.Rect.Width / 2); (sonarView.Rect.Width / 2 * sonarView.Rect.Width / 2);
if (HasMineralScanner && Level.Loaded != null && !Level.Loaded.Generating) if (AllowUsingMineralScanner && Level.Loaded != null && !Level.Loaded.Generating)
{ {
if (MineralClusters == null) if (MineralClusters == null)
{ {
@@ -417,26 +502,42 @@ namespace Barotrauma.Items.Components
if (Level.Loaded != null) if (Level.Loaded != null)
{ {
nearbyObjectUpdateTimer -= deltaTime;
if (nearbyObjectUpdateTimer <= 0.0f)
{
nearbyObjects.Clear();
foreach (var nearbyObject in Level.Loaded.LevelObjectManager.GetAllObjects(transducerCenter, range * zoom))
{
if (!nearbyObject.VisibleOnSonar) { continue; }
float objectRange = range + nearbyObject.SonarRadius;
if (Vector2.DistanceSquared(transducerCenter, nearbyObject.WorldPosition) < objectRange * objectRange)
{
nearbyObjects.Add(nearbyObject);
}
}
nearbyObjectUpdateTimer = NearbyObjectUpdateInterval;
}
List<LevelTrigger> ballastFloraSpores = new List<LevelTrigger>(); List<LevelTrigger> ballastFloraSpores = new List<LevelTrigger>();
Dictionary<LevelTrigger, Vector2> levelTriggerFlows = new Dictionary<LevelTrigger, Vector2>(); Dictionary<LevelTrigger, Vector2> levelTriggerFlows = new Dictionary<LevelTrigger, Vector2>();
for (var pingIndex = 0; pingIndex < activePingsCount; ++pingIndex) for (var pingIndex = 0; pingIndex < activePingsCount; ++pingIndex)
{ {
var activePing = activePings[pingIndex]; var activePing = activePings[pingIndex];
LevelObjectManager objManager = Level.Loaded.LevelObjectManager;
float pingRange = range * activePing.State / zoom; float pingRange = range * activePing.State / zoom;
foreach (LevelObject levelObject in objManager.GetAllObjects(transducerCenter, pingRange)) foreach (LevelObject levelObject in nearbyObjects)
{ {
if (levelObject.Triggers == null) { continue; } if (levelObject.Triggers == null) { continue; }
//gather all nearby triggers that are causing the water to flow into the dictionary //gather all nearby triggers that are causing the water to flow into the dictionary
foreach (LevelTrigger trigger in levelObject.Triggers) foreach (LevelTrigger trigger in levelObject.Triggers)
{ {
Vector2 flow = trigger.GetWaterFlowVelocity(); Vector2 flow = trigger.GetWaterFlowVelocity();
//ignore ones that are barely doing anything (flow^2 < 1) //ignore ones that are barely doing anything (flow^2 <= 1)
if (flow.LengthSquared() > 1.0f && !levelTriggerFlows.ContainsKey(trigger)) if (flow.LengthSquared() >= 1.0f && !levelTriggerFlows.ContainsKey(trigger))
{ {
levelTriggerFlows.Add(trigger, flow); levelTriggerFlows.Add(trigger, flow);
} }
if (!string.IsNullOrWhiteSpace(trigger.InfectIdentifier) && Vector2.DistanceSquared(transducerCenter, trigger.WorldPosition) < pingRange / 2 * pingRange / 2) if (!string.IsNullOrWhiteSpace(trigger.InfectIdentifier) &&
Vector2.DistanceSquared(transducerCenter, trigger.WorldPosition) < pingRange / 2 * pingRange / 2)
{ {
ballastFloraSpores.Add(trigger); ballastFloraSpores.Add(trigger);
} }
@@ -727,8 +828,8 @@ namespace Barotrauma.Items.Components
float directionalPingVisibility = useDirectionalPing && currentMode == Mode.Active ? 1.0f : showDirectionalIndicatorTimer; float directionalPingVisibility = useDirectionalPing && currentMode == Mode.Active ? 1.0f : showDirectionalIndicatorTimer;
if (directionalPingVisibility > 0.0f) if (directionalPingVisibility > 0.0f)
{ {
Vector2 sector1 = MathUtils.RotatePointAroundTarget(pingDirection * DisplayRadius, Vector2.Zero, DirectionalPingSector * 0.5f); Vector2 sector1 = MathUtils.RotatePointAroundTarget(pingDirection * DisplayRadius, Vector2.Zero, MathHelper.ToRadians(DirectionalPingSector * 0.5f));
Vector2 sector2 = MathUtils.RotatePointAroundTarget(pingDirection * DisplayRadius, Vector2.Zero, -DirectionalPingSector * 0.5f); Vector2 sector2 = MathUtils.RotatePointAroundTarget(pingDirection * DisplayRadius, Vector2.Zero, MathHelper.ToRadians(-DirectionalPingSector * 0.5f));
DrawLine(spriteBatch, Vector2.Zero, sector1, Color.LightCyan * 0.2f * directionalPingVisibility, width: 3); DrawLine(spriteBatch, Vector2.Zero, sector1, Color.LightCyan * 0.2f * directionalPingVisibility, width: 3);
DrawLine(spriteBatch, Vector2.Zero, sector2, Color.LightCyan * 0.2f * directionalPingVisibility, width: 3); DrawLine(spriteBatch, Vector2.Zero, sector2, Color.LightCyan * 0.2f * directionalPingVisibility, width: 3);
} }
@@ -761,7 +862,7 @@ namespace Barotrauma.Items.Components
{ {
DrawMarker(spriteBatch, DrawMarker(spriteBatch,
Level.Loaded.StartLocation.Name, Level.Loaded.StartLocation.Name,
"outpost", Level.Loaded.StartOutpost != null ? "outpost" : "location",
Level.Loaded.StartLocation.Name, Level.Loaded.StartLocation.Name,
Level.Loaded.StartPosition, transducerCenter, Level.Loaded.StartPosition, transducerCenter,
displayScale, center, DisplayRadius); displayScale, center, DisplayRadius);
@@ -771,16 +872,28 @@ namespace Barotrauma.Items.Components
{ {
DrawMarker(spriteBatch, DrawMarker(spriteBatch,
Level.Loaded.EndLocation.Name, Level.Loaded.EndLocation.Name,
"outpost", Level.Loaded.EndOutpost != null ? "outpost" : "location",
Level.Loaded.EndLocation.Name, Level.Loaded.EndLocation.Name,
Level.Loaded.EndPosition, transducerCenter, Level.Loaded.EndPosition, transducerCenter,
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)
{ {
@@ -793,10 +906,8 @@ namespace Barotrauma.Items.Components
} }
} }
if (GameMain.GameSession.Mission != null) foreach (Mission mission in GameMain.GameSession.Missions)
{ {
var mission = GameMain.GameSession.Mission;
if (!string.IsNullOrWhiteSpace(mission.SonarLabel)) if (!string.IsNullOrWhiteSpace(mission.SonarLabel))
{ {
foreach (Vector2 sonarPosition in mission.SonarPositions) foreach (Vector2 sonarPosition in mission.SonarPositions)
@@ -811,18 +922,17 @@ namespace Barotrauma.Items.Components
} }
} }
if (HasMineralScanner && useMineralScanner && CurrentMode == Mode.Active && MineralClusters != null) if (AllowUsingMineralScanner && useMineralScanner && CurrentMode == Mode.Active && MineralClusters != null)
{ {
var maxMineralScanRangeSquared = Range * Range;
foreach (var t in MineralClusters) foreach (var t in MineralClusters)
{ {
var unobtainedMinerals = t.Item2.Where(i => i != null && i.GetRootInventoryOwner() == i); var unobtainedMinerals = t.Item2.Where(i => i != null && i.GetRootInventoryOwner() == i);
if (unobtainedMinerals.None()) { continue; } if (unobtainedMinerals.None()) { continue; }
if (Vector2.DistanceSquared(transducerCenter, t.Item1) > maxMineralScanRangeSquared) { continue; } if (!CheckResourceMarkerVisibility(t.Item1, transducerCenter)) { continue; }
var i = unobtainedMinerals.FirstOrDefault(); var i = unobtainedMinerals.FirstOrDefault();
if (i == null) { continue; } if (i == null) { continue; }
DrawMarker(spriteBatch, DrawMarker(spriteBatch,
i.Name, null, i, i.Name, "mineral", i,
t.Item1, transducerCenter, t.Item1, transducerCenter,
displayScale, center, DisplayRadius * 0.95f, displayScale, center, DisplayRadius * 0.95f,
onlyShowTextOnMouseOver: true); onlyShowTextOnMouseOver: true);
@@ -832,16 +942,11 @@ 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,
sub.Info.DisplayName, sub.Info.DisplayName,
sub.Info.HasTag(SubmarineTag.Shuttle) ? "shuttle" : "submarine", sub.Info.HasTag(SubmarineTag.Shuttle) ? "shuttle" : "submarine",
sub, sub,
sub.WorldPosition, transducerCenter, sub.WorldPosition, transducerCenter,
@@ -861,10 +966,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);
@@ -948,8 +1051,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;
} }
@@ -1075,8 +1178,7 @@ namespace Barotrauma.Items.Components
for (var pingIndex = 0; pingIndex < activePingsCount; ++pingIndex) for (var pingIndex = 0; pingIndex < activePingsCount; ++pingIndex)
{ {
var activePing = activePings[pingIndex]; foreach (LevelObject levelObject in nearbyObjects)
foreach (LevelObject levelObject in Level.Loaded.LevelObjectManager.GetAllObjects(pingSource, range * activePing.State))
{ {
if (levelObject.ActivePrefab?.SonarDisruption <= 0.0f) { continue; } if (levelObject.ActivePrefab?.SonarDisruption <= 0.0f) { continue; }
@@ -1157,19 +1259,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++)
@@ -1209,9 +1302,9 @@ namespace Barotrauma.Items.Components
{ {
foreach (Voronoi2.GraphEdge edge in cell.Edges) foreach (Voronoi2.GraphEdge edge in cell.Edges)
{ {
if (!edge.IsSolid) continue; if (!edge.IsSolid) { continue; }
float cellDot = Vector2.Dot(cell.Center - pingSource, (edge.Center + cell.Translation) - cell.Center); float cellDot = Vector2.Dot(cell.Center - pingSource, (edge.Center + cell.Translation) - cell.Center);
if (cellDot > 0) continue; if (cellDot > 0) { continue; }
float facingDot = Vector2.Dot( float facingDot = Vector2.Dot(
Vector2.Normalize(edge.Point1 - edge.Point2), Vector2.Normalize(edge.Point1 - edge.Point2),
@@ -1222,7 +1315,8 @@ namespace Barotrauma.Items.Components
edge.Point2 + cell.Translation, edge.Point2 + cell.Translation,
pingSource, transducerPos, pingSource, transducerPos,
pingRadius, prevPingRadius, pingRadius, prevPingRadius,
350.0f, 3.0f * (Math.Abs(facingDot) + 1.0f), range, pingStrength, passive); 350.0f, 3.0f * (Math.Abs(facingDot) + 1.0f), range, pingStrength, passive,
blipType : cell.IsDestructible ? BlipType.Destructible : BlipType.Default);
} }
} }
@@ -1311,7 +1405,7 @@ namespace Barotrauma.Items.Components
} }
private void CreateBlipsForLine(Vector2 point1, Vector2 point2, Vector2 pingSource, Vector2 transducerPos, float pingRadius, float prevPingRadius, private void CreateBlipsForLine(Vector2 point1, Vector2 point2, Vector2 pingSource, Vector2 transducerPos, float pingRadius, float prevPingRadius,
float lineStep, float zStep, float range, float pingStrength, bool passive) float lineStep, float zStep, float range, float pingStrength, bool passive, BlipType blipType = BlipType.Default)
{ {
lineStep /= zoom; lineStep /= zoom;
zStep /= zoom; zStep /= zoom;
@@ -1327,13 +1421,13 @@ namespace Barotrauma.Items.Components
//ignore if outside the display //ignore if outside the display
Vector2 transducerDiff = point - transducerPos; Vector2 transducerDiff = point - transducerPos;
Vector2 transducerDisplayDiff = transducerDiff * displayScale; Vector2 transducerDisplayDiff = transducerDiff * displayScale;
if (transducerDisplayDiff.LengthSquared() > DisplayRadius * DisplayRadius) continue; if (transducerDisplayDiff.LengthSquared() > DisplayRadius * DisplayRadius) { continue; }
//ignore if the point is not within the ping //ignore if the point is not within the ping
Vector2 pointDiff = point - pingSource; Vector2 pointDiff = point - pingSource;
Vector2 displayPointDiff = pointDiff * displayScale; Vector2 displayPointDiff = pointDiff * displayScale;
float displayPointDistSqr = displayPointDiff.LengthSquared(); float displayPointDistSqr = displayPointDiff.LengthSquared();
if (displayPointDistSqr < prevPingRadius * prevPingRadius || displayPointDistSqr > pingRadius * pingRadius) continue; if (displayPointDistSqr < prevPingRadius * prevPingRadius || displayPointDistSqr > pingRadius * pingRadius) { continue; }
//ignore if direction is disrupted //ignore if direction is disrupted
float transducerDist = transducerDiff.Length(); float transducerDist = transducerDiff.Length();
@@ -1348,7 +1442,7 @@ namespace Barotrauma.Items.Components
break; break;
} }
} }
if (disrupted) continue; if (disrupted) { continue; }
float displayPointDist = (float)Math.Sqrt(displayPointDistSqr); float displayPointDist = (float)Math.Sqrt(displayPointDistSqr);
float alpha = pingStrength * Rand.Range(1.5f, 2.0f); float alpha = pingStrength * Rand.Range(1.5f, 2.0f);
@@ -1360,8 +1454,8 @@ namespace Barotrauma.Items.Components
int minDist = (int)(200 / zoom); int minDist = (int)(200 / zoom);
sonarBlips.RemoveAll(b => b.FadeTimer < fadeTimer && Math.Abs(pos.X - b.Position.X) < minDist && Math.Abs(pos.Y - b.Position.Y) < minDist); sonarBlips.RemoveAll(b => b.FadeTimer < fadeTimer && Math.Abs(pos.X - b.Position.X) < minDist && Math.Abs(pos.Y - b.Position.Y) < minDist);
var blip = new SonarBlip(pos, fadeTimer, 1.0f + ((displayPointDist + z) / DisplayRadius)); var blip = new SonarBlip(pos, fadeTimer, 1.0f + ((displayPointDist + z) / DisplayRadius), blipType);
if (!passive && !CheckBlipVisibility(blip, transducerPos)) continue; if (!passive && !CheckBlipVisibility(blip, transducerPos)) { continue; }
sonarBlips.Add(blip); sonarBlips.Add(blip);
zStep += 0.5f / zoom; zStep += 0.5f / zoom;
@@ -1375,7 +1469,7 @@ namespace Barotrauma.Items.Components
alpha -= 0.1f; alpha -= 0.1f;
} }
if (alpha < 0) break; if (alpha < 0) { break; }
} }
} }
} }
@@ -1404,6 +1498,30 @@ namespace Barotrauma.Items.Components
return true; return true;
} }
/// <summary>
/// Based largely on existing CheckBlipVisibility() code
/// </summary>
private bool CheckResourceMarkerVisibility(Vector2 resourcePos, Vector2 transducerPos)
{
var distSquared = Vector2.DistanceSquared(transducerPos, resourcePos);
if (distSquared > Range * Range)
{
return false;
}
if (currentPingIndex != -1 && activePings[currentPingIndex].IsDirectional)
{
var pos = (resourcePos - transducerPos) * displayScale * zoom;
pos.Y = -pos.Y;
var length = pos.Length();
var dir = pos / length;
if (Vector2.Dot(activePings[currentPingIndex].Direction, dir) < DirectionalPingDotProduct)
{
return false;
}
}
return true;
}
private void DrawBlip(SpriteBatch spriteBatch, SonarBlip blip, Vector2 transducerPos, Vector2 center, float strength, float blipScale) private void DrawBlip(SpriteBatch spriteBatch, SonarBlip blip, Vector2 transducerPos, Vector2 center, float strength, float blipScale)
{ {
strength = MathHelper.Clamp(strength, 0.0f, 1.0f); strength = MathHelper.Clamp(strength, 0.0f, 1.0f);
@@ -1524,13 +1642,14 @@ namespace Barotrauma.Items.Components
} }
} }
if (string.IsNullOrEmpty(iconIdentifier) || !targetIcons.ContainsKey(iconIdentifier)) if (iconIdentifier == null || !targetIcons.ContainsKey(iconIdentifier))
{ {
GUI.DrawRectangle(spriteBatch, new Rectangle((int)markerPos.X - 3, (int)markerPos.Y - 3, 6, 6), markerColor, thickness: 2); GUI.DrawRectangle(spriteBatch, new Rectangle((int)markerPos.X - 3, (int)markerPos.Y - 3, 6, 6), markerColor, thickness: 2);
} }
else else
{ {
targetIcons[iconIdentifier].Draw(spriteBatch, markerPos); var iconInfo = targetIcons[iconIdentifier];
iconInfo.Item1.Draw(spriteBatch, markerPos, iconInfo.Item2);
} }
if (alpha <= 0.0f) { return; } if (alpha <= 0.0f) { return; }
@@ -1561,9 +1680,9 @@ namespace Barotrauma.Items.Components
screenBackground?.Remove(); screenBackground?.Remove();
lineSprite?.Remove(); lineSprite?.Remove();
foreach (Sprite sprite in targetIcons.Values) foreach (var t in targetIcons.Values)
{ {
sprite.Remove(); t.Item1.Remove();
} }
targetIcons.Clear(); targetIcons.Clear();
@@ -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;
@@ -112,7 +112,7 @@ namespace Barotrauma.Items.Components
protected override void CreateGUI() protected override void CreateGUI()
{ {
controlContainer = new GUIFrame(new RectTransform(new Vector2(Sonar.controlBoxSize.X, 1 - Sonar.controlBoxSize.Y * 2), GuiFrame.RectTransform, Anchor.CenterLeft), "ItemUI"); controlContainer = new GUIFrame(new RectTransform(new Vector2(Sonar.controlBoxSize.X, 1 - Sonar.controlBoxSize.Y * 2), GuiFrame.RectTransform, Anchor.CenterRight), "ItemUI");
var paddedControlContainer = new GUIFrame(new RectTransform(controlContainer.Rect.Size - GUIStyle.ItemFrameMargin, controlContainer.RectTransform, Anchor.Center) var paddedControlContainer = new GUIFrame(new RectTransform(controlContainer.Rect.Size - GUIStyle.ItemFrameMargin, controlContainer.RectTransform, Anchor.Center)
{ {
AbsoluteOffset = GUIStyle.ItemFrameOffset AbsoluteOffset = GUIStyle.ItemFrameOffset
@@ -265,7 +265,7 @@ namespace Barotrauma.Items.Components
levelStartSelected ? Destination.LevelStart : Destination.LevelEnd); levelStartSelected ? Destination.LevelStart : Destination.LevelEnd);
// Status -> // Status ->
statusContainer = new GUIFrame(new RectTransform(Sonar.controlBoxSize, GuiFrame.RectTransform, Anchor.BottomLeft) statusContainer = new GUIFrame(new RectTransform(Sonar.controlBoxSize, GuiFrame.RectTransform, Anchor.BottomRight)
{ {
RelativeOffset = Sonar.controlBoxOffset RelativeOffset = Sonar.controlBoxOffset
}, "ItemUI"); }, "ItemUI");
@@ -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;
@@ -339,9 +340,9 @@ namespace Barotrauma.Items.Components
//docking interface ---------------------------------------------------- //docking interface ----------------------------------------------------
float dockingButtonSize = 1.1f; float dockingButtonSize = 1.1f;
float elementScale = 0.6f; float elementScale = 0.6f;
dockingContainer = new GUIFrame(new RectTransform(Sonar.controlBoxSize, GuiFrame.RectTransform, Anchor.BottomLeft, scaleBasis: ScaleBasis.Smallest) dockingContainer = new GUIFrame(new RectTransform(Sonar.controlBoxSize, GuiFrame.RectTransform, Anchor.BottomRight, scaleBasis: ScaleBasis.Smallest)
{ {
RelativeOffset = new Vector2(Sonar.controlBoxOffset.X + 0.05f, Sonar.controlBoxOffset.Y) RelativeOffset = new Vector2(Sonar.controlBoxOffset.X + 0.05f, -0.05f)
}, style: null); }, style: null);
dockText = TextManager.Get("label.navterminaldock", fallBackTag: "captain.dock"); dockText = TextManager.Get("label.navterminaldock", fallBackTag: "captain.dock");
@@ -436,12 +437,17 @@ namespace Barotrauma.Items.Components
}; };
// Sonar area // Sonar area
steerArea = new GUICustomComponent(new RectTransform(Sonar.GUISizeCalculation, GuiFrame.RectTransform, Anchor.CenterRight, scaleBasis: ScaleBasis.Smallest), steerArea = new GUICustomComponent(new RectTransform(Sonar.GUISizeCalculation, GuiFrame.RectTransform, Anchor.CenterLeft, scaleBasis: ScaleBasis.Smallest),
(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;
@@ -66,7 +66,11 @@ namespace Barotrauma.Items.Components
{ {
foreach (ParticleEmitter particleEmitter in particleEmitters) foreach (ParticleEmitter particleEmitter in particleEmitters)
{ {
float particleAngle = item.body.Rotation + MathHelper.ToRadians(BarrelRotation) + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi); float particleAngle = MathHelper.ToRadians(BarrelRotation);
if (item.body != null)
{
particleAngle += item.body.Rotation + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
}
particleEmitter.Emit( particleEmitter.Emit(
deltaTime, ConvertUnits.ToDisplayUnits(raystart), deltaTime, ConvertUnits.ToDisplayUnits(raystart),
item.CurrentHull, particleAngle, particleEmitter.Prefab.CopyEntityAngle ? -particleAngle : 0); item.CurrentHull, particleAngle, particleEmitter.Prefab.CopyEntityAngle ? -particleAngle : 0);
@@ -109,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);
@@ -48,18 +48,14 @@ namespace Barotrauma.Items.Components
Wire equippedWire = null; Wire equippedWire = null;
bool allowRewiring = GameMain.NetworkMember?.ServerSettings == null || GameMain.NetworkMember.ServerSettings.AllowRewiring; bool allowRewiring = GameMain.NetworkMember?.ServerSettings == null || GameMain.NetworkMember.ServerSettings.AllowRewiring || panel.AlwaysAllowRewiring;
if (allowRewiring && (!panel.Locked || Screen.Selected == GameMain.SubEditorScreen)) if (allowRewiring && (!panel.Locked || Screen.Selected == GameMain.SubEditorScreen))
{ {
//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; }
@@ -380,7 +381,7 @@ namespace Barotrauma.Items.Components
{ {
ConnectionPanel.HighlightedWire = wire; ConnectionPanel.HighlightedWire = wire;
bool allowRewiring = GameMain.NetworkMember?.ServerSettings == null || GameMain.NetworkMember.ServerSettings.AllowRewiring; bool allowRewiring = GameMain.NetworkMember?.ServerSettings == null || GameMain.NetworkMember.ServerSettings.AllowRewiring || panel.AlwaysAllowRewiring;
if (allowRewiring && (!wire.Locked && !panel.Locked || Screen.Selected == GameMain.SubEditorScreen)) if (allowRewiring && (!wire.Locked && !panel.Locked || Screen.Selected == GameMain.SubEditorScreen))
{ {
//start dragging the wire //start dragging the wire
@@ -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();
} }
} }
} }
@@ -117,9 +117,9 @@ namespace Barotrauma.Items.Components
{ {
if (defaultWireSprite == null) if (defaultWireSprite == null)
{ {
defaultWireSprite = new Sprite("Content/Items/wireHorizontal.png", new Vector2(0.5f, 0.5f)) defaultWireSprite = new Sprite("Content/Items/Electricity/signalcomp.png", new Rectangle(970, 47, 14, 16), new Vector2(0.5f, 0.5f))
{ {
Depth = 0.85f Depth = 0.855f
}; };
} }
@@ -156,7 +156,7 @@ namespace Barotrauma.Items.Components
drawOffset = sub.DrawPosition + sub.HiddenSubPosition; drawOffset = sub.DrawPosition + sub.HiddenSubPosition;
} }
float depth = item.IsSelected ? 0.0f : SubEditorScreen.IsWiringMode() ? 0.02f : wireSprite.Depth + ((item.ID % 100) * 0.00001f); float depth = item.IsSelected ? 0.0f : SubEditorScreen.IsWiringMode() ? 0.02f : wireSprite.Depth + (item.ID % 100) * 0.000001f;// item.GetDrawDepth(wireSprite.Depth, wireSprite);
if (item.IsHighlighted) if (item.IsHighlighted)
{ {
@@ -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,
@@ -286,10 +289,8 @@ namespace Barotrauma.Items.Components
public static void UpdateEditing(List<Wire> wires) public static void UpdateEditing(List<Wire> wires)
{ {
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,7 +196,7 @@ namespace Barotrauma.Items.Components
texts.Add(TextManager.Get("Stunned")); texts.Add(TextManager.Get("Stunned"));
textColors.Add(GUI.Style.Orange); textColors.Add(GUI.Style.Orange);
} }
int oxygenTextIndex = MathHelper.Clamp((int)Math.Floor((1.0f - (target.Oxygen / 100.0f)) * OxygenTexts.Length), 0, OxygenTexts.Length - 1); int oxygenTextIndex = MathHelper.Clamp((int)Math.Floor((1.0f - (target.Oxygen / 100.0f)) * OxygenTexts.Length), 0, OxygenTexts.Length - 1);
texts.Add(OxygenTexts[oxygenTextIndex]); texts.Add(OxygenTexts[oxygenTextIndex]);
textColors.Add(Color.Lerp(GUI.Style.Red, GUI.Style.Green, target.Oxygen / 100.0f)); textColors.Add(Color.Lerp(GUI.Style.Red, GUI.Style.Green, target.Oxygen / 100.0f));
@@ -210,7 +225,7 @@ namespace Barotrauma.Items.Components
foreach (AfflictionPrefab affliction in combinedAfflictionStrengths.Keys) foreach (AfflictionPrefab affliction in combinedAfflictionStrengths.Keys)
{ {
texts.Add(TextManager.AddPunctuation(':', affliction.Name, ((int)combinedAfflictionStrengths[affliction]).ToString() + " %")); texts.Add(TextManager.AddPunctuation(':', affliction.Name, Math.Max(((int)combinedAfflictionStrengths[affliction]), 1).ToString() + " %"));
textColors.Add(Color.Lerp(GUI.Style.Orange, GUI.Style.Red, combinedAfflictionStrengths[affliction] / affliction.MaxStrength)); textColors.Add(Color.Lerp(GUI.Style.Orange, GUI.Style.Red, combinedAfflictionStrengths[affliction] / affliction.MaxStrength));
} }
} }
@@ -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)
@@ -247,7 +247,7 @@ namespace Barotrauma.Items.Components
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1) public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
{ {
if (!MathUtils.NearlyEqual(item.Rotation, prevBaseRotation)) if (!MathUtils.NearlyEqual(item.Rotation, prevBaseRotation) || !MathUtils.NearlyEqual(item.Scale, prevScale))
{ {
UpdateTransformedBarrelPos(); UpdateTransformedBarrelPos();
} }
@@ -290,9 +290,10 @@ namespace Barotrauma.Items.Components
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));
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);
const float coneRadius = 300.0f; const float coneRadius = 300.0f;
@@ -300,7 +301,11 @@ namespace Barotrauma.Items.Components
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);
} }
@@ -309,12 +314,15 @@ namespace Barotrauma.Items.Components
spriteBatch.DrawSector(drawPos, circleRadius, radians, (int)Math.Abs(90 * radians), GUI.Style.Green, offset: minRotation, thickness: lineThickness); spriteBatch.DrawSector(drawPos, circleRadius, radians, (int)Math.Abs(90 * radians), GUI.Style.Green, offset: minRotation, thickness: lineThickness);
} }
Widget minRotationWidget = GetWidget("minrotation", spriteBatch, size: 10, initMethod: (widget) => int baseWidgetScale = GUI.IntScale(16);
int widgetSize = (int) (Math.Max(baseWidgetScale, baseWidgetScale / Screen.Selected.Cam.Zoom));
float widgetThickness = Math.Max(1f, lineThickness);
Widget minRotationWidget = GetWidget("minrotation", spriteBatch, size: widgetSize, thickness: widgetThickness, initMethod: (widget) =>
{ {
widget.Selected += () => widget.Selected += () =>
{ {
oldRotation = RotationLimits; oldRotation = RotationLimits;
}; };
widget.MouseDown += () => widget.MouseDown += () =>
{ {
widget.color = GUI.Style.Green; widget.color = GUI.Style.Green;
@@ -324,6 +332,7 @@ namespace Barotrauma.Items.Components
{ {
widget.color = Color.Yellow; widget.color = Color.Yellow;
item.CreateEditingHUD(); item.CreateEditingHUD();
RotationLimits = RotationLimits;
if (SubEditorScreen.IsSubEditor()) if (SubEditorScreen.IsSubEditor())
{ {
SubEditorScreen.StoreCommand(new PropertyCommand(this, "RotationLimits", RotationLimits, oldRotation)); SubEditorScreen.StoreCommand(new PropertyCommand(this, "RotationLimits", RotationLimits, oldRotation));
@@ -332,13 +341,7 @@ namespace Barotrauma.Items.Components
widget.MouseHeld += (deltaTime) => widget.MouseHeld += (deltaTime) =>
{ {
minRotation = GetRotationAngle(GetDrawPos()); minRotation = GetRotationAngle(GetDrawPos());
if (minRotation > maxRotation) UpdateBarrel();
{
float temp = minRotation;
minRotation = maxRotation;
maxRotation = temp;
}
RotationLimits = RotationLimits;
MapEntity.DisableSelect = true; MapEntity.DisableSelect = true;
}; };
widget.PreUpdate += (deltaTime) => widget.PreUpdate += (deltaTime) =>
@@ -359,7 +362,7 @@ namespace Barotrauma.Items.Components
}; };
}); });
Widget maxRotationWidget = GetWidget("maxrotation", spriteBatch, size: 10, initMethod: (widget) => Widget maxRotationWidget = GetWidget("maxrotation", spriteBatch, size: widgetSize, thickness: widgetThickness, initMethod: (widget) =>
{ {
widget.Selected += () => widget.Selected += () =>
{ {
@@ -368,12 +371,13 @@ namespace Barotrauma.Items.Components
widget.MouseDown += () => widget.MouseDown += () =>
{ {
widget.color = GUI.Style.Green; widget.color = GUI.Style.Green;
prevAngle = minRotation; prevAngle = maxRotation;
}; };
widget.Deselected += () => widget.Deselected += () =>
{ {
widget.color = Color.Yellow; widget.color = Color.Yellow;
item.CreateEditingHUD(); item.CreateEditingHUD();
RotationLimits = RotationLimits;
if (SubEditorScreen.IsSubEditor()) if (SubEditorScreen.IsSubEditor())
{ {
SubEditorScreen.StoreCommand(new PropertyCommand(this, "RotationLimits", RotationLimits, oldRotation)); SubEditorScreen.StoreCommand(new PropertyCommand(this, "RotationLimits", RotationLimits, oldRotation));
@@ -382,13 +386,7 @@ namespace Barotrauma.Items.Components
widget.MouseHeld += (deltaTime) => widget.MouseHeld += (deltaTime) =>
{ {
maxRotation = GetRotationAngle(GetDrawPos()); maxRotation = GetRotationAngle(GetDrawPos());
if (minRotation > maxRotation) UpdateBarrel();
{
float temp = minRotation;
minRotation = maxRotation;
maxRotation = temp;
}
RotationLimits = RotationLimits;
MapEntity.DisableSelect = true; MapEntity.DisableSelect = true;
}; };
widget.PreUpdate += (deltaTime) => widget.PreUpdate += (deltaTime) =>
@@ -418,22 +416,32 @@ namespace Barotrauma.Items.Components
drawPos.Y = -drawPos.Y; drawPos.Y = -drawPos.Y;
return drawPos; return drawPos;
} }
void UpdateBarrel()
{
rotation = (minRotation + maxRotation) / 2;
}
} }
private Widget GetWidget(string id, SpriteBatch spriteBatch, int size = 5, Action<Widget> initMethod = null) private Widget GetWidget(string id, SpriteBatch spriteBatch, int size = 5, float thickness = 1f, Action<Widget> initMethod = null)
{ {
Vector2 offset = new Vector2(size / 2 + 5, -10);
if (!widgets.TryGetValue(id, out Widget widget)) if (!widgets.TryGetValue(id, out Widget widget))
{ {
widget = new Widget(id, size, Widget.Shape.Rectangle) widget = new Widget(id, size, Widget.Shape.Rectangle)
{ {
color = Color.Yellow, color = Color.Yellow,
tooltipOffset = new Vector2(size / 2 + 5, -10), tooltipOffset = offset,
inputAreaMargin = 20, inputAreaMargin = 20,
RequireMouseOn = false RequireMouseOn = false
}; };
widgets.Add(id, widget); widgets.Add(id, widget);
initMethod?.Invoke(widget); initMethod?.Invoke(widget);
} }
widget.size = size;
widget.tooltipOffset = offset;
widget.thickness = thickness;
return widget; return widget;
} }
@@ -488,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 =
@@ -536,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)
@@ -165,7 +165,14 @@ namespace Barotrauma.Items.Components
if (isLocked) if (isLocked)
{ {
Lock(isNetworkMessage: true, forcePosition: true); if (DockingTarget.joint != null)
{
DockingTarget.Lock(isNetworkMessage: true, forcePosition: true);
}
else
{
Lock(isNetworkMessage: true, forcePosition: true);
}
} }
} }
else else
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>();
@@ -85,7 +87,7 @@ namespace Barotrauma
{ {
get get
{ {
if (!GameMain.SubEditorScreen.ShowThalamus && prefab.Category.HasFlag(MapEntityCategory.Thalamus)) if (GameMain.SubEditorScreen.IsSubcategoryHidden(prefab.Subcategory))
{ {
return false; return false;
} }
@@ -93,18 +95,20 @@ namespace Barotrauma
} }
} }
public float GetDrawDepth()
{
return GetDrawDepth(SpriteDepth, Sprite);
}
public Color GetSpriteColor() public Color GetSpriteColor()
{ {
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;
@@ -115,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;
@@ -130,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)
{ {
@@ -137,7 +139,9 @@ namespace Barotrauma
{ {
if (containedSprite.UseWhenAttached) if (containedSprite.UseWhenAttached)
{ {
activeSprite = containedSprite.Sprite; activeContainedSprite = containedSprite;
activeSprite = containedSprite.Sprite;
UpdateSpriteStates(0.0f);
return; return;
} }
} }
@@ -149,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;
} }
} }
@@ -182,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);
} }
@@ -235,12 +242,20 @@ 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);
bool isWiringMode = editing && SubEditorScreen.IsWiringMode() && !isWire && parentInventory == null; bool isWiringMode = editing && SubEditorScreen.TransparentWiringMode && SubEditorScreen.IsWiringMode() && !isWire && parentInventory == null;
bool renderTransparent = isWiringMode && GetComponent<ConnectionPanel>() == null; bool renderTransparent = isWiringMode && GetComponent<ConnectionPanel>() == null;
if (renderTransparent) { color *= 0.15f; } if (renderTransparent) { color *= 0.15f; }
@@ -300,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,
@@ -323,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);
@@ -331,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));
} }
} }
@@ -348,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)
@@ -360,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)
@@ -379,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);
@@ -388,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));
} }
} }
@@ -400,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));
} }
@@ -500,12 +515,39 @@ namespace Barotrauma
public void UpdateSpriteStates(float deltaTime) public void UpdateSpriteStates(float deltaTime)
{ {
DecorativeSprite.UpdateSpriteStates(Prefab.DecorativeSpriteGroups, spriteAnimState, ID, deltaTime, ConditionalMatches); 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);
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];
@@ -1030,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; }
@@ -1254,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;
@@ -1262,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 ||
@@ -1485,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; }
@@ -1516,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);
} }
@@ -5,6 +5,8 @@ using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
using Barotrauma.Items.Components; using Barotrauma.Items.Components;
using Barotrauma.Networking; using Barotrauma.Networking;
using Barotrauma.Particles;
using Barotrauma.Sounds;
using FarseerPhysics; using FarseerPhysics;
using FarseerPhysics.Dynamics; using FarseerPhysics.Dynamics;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
@@ -33,12 +35,19 @@ namespace Barotrauma.MapCreatures.Behavior
[Serialize(defaultValue: 0f, isSaveable: false)] [Serialize(defaultValue: 0f, isSaveable: false)]
public float MaxVelocity { get; set; } public float MaxVelocity { get; set; }
[Serialize(defaultValue: "255,255,255,255", isSaveable: false)]
public Color ColorMultiplier { get; set; }
private float RandRotation() => Rand.Range(MinRotation, MaxRotation); private float RandRotation() => Rand.Range(MinRotation, MaxRotation);
private float RandVelocity() => Rand.Range(MinVelocity, MaxVelocity); private float RandVelocity() => Rand.Range(MinVelocity, MaxVelocity);
public void Emit(Vector2 pos) public void Emit(Vector2 pos)
{ {
GameMain.ParticleManager.CreateParticle(Identifier, pos, RandRotation(), RandVelocity()); Particle particle = GameMain.ParticleManager.CreateParticle(Identifier, pos, RandRotation(), RandVelocity());
if (particle != null)
{
particle.ColorMultiplier = ColorMultiplier.ToVector4();
}
} }
public DamageParticle(XElement element) public DamageParticle(XElement element)
@@ -54,6 +63,9 @@ namespace Barotrauma.MapCreatures.Behavior
public readonly List<Sprite> LeafSprites = new List<Sprite>(), DamagedLeafSprites = new List<Sprite>(); public readonly List<Sprite> LeafSprites = new List<Sprite>(), DamagedLeafSprites = new List<Sprite>();
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 static bool AlwaysShowBallastFloraSprite = false;
partial void LoadPrefab(XElement element) partial void LoadPrefab(XElement element)
{ {
@@ -97,6 +109,9 @@ namespace Barotrauma.MapCreatures.Behavior
case "damageparticle": case "damageparticle":
DamageParticles.Add(new DamageParticle(subElement)); DamageParticles.Add(new DamageParticle(subElement));
break; break;
case "deathparticle":
DeathParticles.Add(new DamageParticle(subElement));
break;
case "targets": case "targets":
LoadTargets(subElement); LoadTargets(subElement);
break; break;
@@ -112,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));
} }
} }
@@ -129,11 +144,22 @@ namespace Barotrauma.MapCreatures.Behavior
} }
} }
private static readonly Color DarkColor = new Color(25, 25, 25); private void CreateDeathParticle(BallastFloraBranch branch)
{
Vector2 pos = GetWorldPosition() + branch.Position;
int amount = (int)Math.Clamp(branch.MaxHealth / 10f, 1, 10);
for (int i = 0; i < amount; i++)
{
foreach (DamageParticle particle in DeathParticles)
{
particle.Emit(pos);
}
}
}
public void Draw(SpriteBatch spriteBatch) public void Draw(SpriteBatch spriteBatch)
{ {
const float zStep = 0.00001f; const float zStep = 0.000001f;
float leafDepth = zStep; float leafDepth = zStep;
float flowerDepth = zStep; float flowerDepth = zStep;
@@ -217,12 +243,12 @@ namespace Barotrauma.MapCreatures.Behavior
if (HasBrokenThrough) if (HasBrokenThrough)
{ {
if (branchAtlas != null) if (branchAtlas != null && branchAtlas.Loaded)
{ {
spriteBatch.Draw(branchAtlas.Texture, pos + branch.offset, branchSprite.SourceRect, branchColor, 0f, branchSprite.AbsoluteOrigin, BaseBranchScale * branch.VineStep, SpriteEffects.None, layer2); spriteBatch.Draw(branchAtlas.Texture, pos + branch.offset, branchSprite.SourceRect, branchColor, 0f, branchSprite.AbsoluteOrigin, BaseBranchScale * branch.VineStep, SpriteEffects.None, layer2);
} }
if (decayAtlas != null && isDamaged) if (decayAtlas != null && isDamaged && decayAtlas.Loaded)
{ {
spriteBatch.Draw(decayAtlas.Texture, pos + branch.offset, branchSprite.SourceRect, branch.HealthColor, 0f, branchSprite.AbsoluteOrigin, BaseBranchScale * branch.VineStep, SpriteEffects.None, layer2 - zStep); spriteBatch.Draw(decayAtlas.Texture, pos + branch.offset, branchSprite.SourceRect, branch.HealthColor, 0f, branchSprite.AbsoluteOrigin, BaseBranchScale * branch.VineStep, SpriteEffects.None, layer2 - zStep);
} }
@@ -242,6 +268,10 @@ namespace Barotrauma.MapCreatures.Behavior
DamagedFlowerSprites[variant].Draw(spriteBatch, pos, branch.HealthColor, flowerSprite.Origin, scale: flowerScale, rotate: branch.FlowerConfig.Rotation, depth: layer1 - flowerDepth - zStep); DamagedFlowerSprites[variant].Draw(spriteBatch, pos, branch.HealthColor, flowerSprite.Origin, scale: flowerScale, rotate: branch.FlowerConfig.Rotation, depth: layer1 - flowerDepth - zStep);
} }
flowerDepth -= zStep; flowerDepth -= zStep;
if (flowerDepth > 0.01f)
{
flowerDepth = zStep;
}
} }
if (branch.LeafConfig.Variant >= 0 && HasBrokenThrough) if (branch.LeafConfig.Variant >= 0 && HasBrokenThrough)
@@ -254,6 +284,10 @@ namespace Barotrauma.MapCreatures.Behavior
DamagedLeafSprites[variant].Draw(spriteBatch, pos, branch.HealthColor, leafSprite.Origin, scale: BaseLeafScale * branch.LeafConfig.Scale * branch.FlowerStep, rotate: branch.LeafConfig.Rotation, depth: layer3 + leafDepth - zStep); DamagedLeafSprites[variant].Draw(spriteBatch, pos, branch.HealthColor, leafSprite.Origin, scale: BaseLeafScale * branch.LeafConfig.Scale * branch.FlowerStep, rotate: branch.LeafConfig.Rotation, depth: layer3 + leafDepth - zStep);
} }
leafDepth += zStep; leafDepth += zStep;
if (leafDepth > 0.01f)
{
flowerDepth = zStep;
}
} }
} }
} }
@@ -264,25 +298,42 @@ namespace Barotrauma.MapCreatures.Behavior
switch (header) switch (header)
{ {
case NetworkHeader.Infect: case NetworkHeader.Infect:
int infectBranch = -1;
ushort itemId = msg.ReadUInt16(); ushort itemId = msg.ReadUInt16();
bool infect = msg.ReadBoolean(); bool infect = msg.ReadBoolean();
if (Entity.FindEntityByID(itemId) is Item item) if (infect)
{
infectBranch = msg.ReadInt32();
}
Entity? entity = Entity.FindEntityByID(itemId);
if (entity is Item item)
{ {
if (infect) if (infect)
{ {
ClaimTarget(item, null); ClaimTarget(item, Branches.FirstOrDefault(b => b.ID == infectBranch));
} }
else else
{ {
RemoveClaim(itemId); RemoveClaim(itemId);
} }
} }
else
{
DebugConsole.AddWarning($"Received Infect.{infect} Network Header with invalid item ID: {itemId}, which belongs to {entity?.ToString() ?? "null!"}");
}
break; break;
case NetworkHeader.BranchCreate: case NetworkHeader.BranchCreate:
int parentId = msg.ReadInt32(); int parentId = msg.ReadInt32();
BallastFloraBranch branch = ReadBranch(msg); BallastFloraBranch branch = ReadBranch(msg);
BallastFloraBranch? parent = Branches.FirstOrDefault(b => b.ID == parentId);
UpdateConnections(branch, Branches.FirstOrDefault(b => b.ID == parentId)); if (parent == null)
{
DebugConsole.AddWarning($"Received BranchCreate with an invalid parent ID: {parentId}, Maximum ID is {Branches.Max(b => b.ID)}");
}
UpdateConnections(branch, parent);
Branches.Add(branch); Branches.Add(branch);
OnBranchGrowthSuccess(branch); OnBranchGrowthSuccess(branch);
break; break;
@@ -290,7 +341,15 @@ namespace Barotrauma.MapCreatures.Behavior
int removedBranchId = msg.ReadInt32(); int removedBranchId = msg.ReadInt32();
BallastFloraBranch removedBranch = Branches.FirstOrDefault(b => b.ID == removedBranchId); BallastFloraBranch removedBranch = Branches.FirstOrDefault(b => b.ID == removedBranchId);
if (removedBranch != null) { RemoveBranch(removedBranch); } if (removedBranch != null)
{
RemoveBranch(removedBranch);
}
else
{
DebugConsole.AddWarning($"Received BranchRemove for a branch that doesn't exist. ID: {removedBranchId}, Maximum ID is {Branches.Max(b => b.ID)}");
}
break; break;
case NetworkHeader.BranchDamage: case NetworkHeader.BranchDamage:
@@ -303,6 +362,10 @@ namespace Barotrauma.MapCreatures.Behavior
CreateDamageParticle(damagedBranch, damage); CreateDamageParticle(damagedBranch, damage);
damagedBranch.Health = health; damagedBranch.Health = health;
} }
else
{
DebugConsole.AddWarning($"Received BranchDamage for a branch that doesn't exist. ID: {damageBranchId}, Maximum ID is {Branches.Max(b => b.ID)}");
}
break; break;
case NetworkHeader.Kill: case NetworkHeader.Kill:
Kill(); Kill();
@@ -326,6 +389,7 @@ namespace Barotrauma.MapCreatures.Behavior
return new BallastFloraBranch(this, pos, (VineTileType)type, FoliageConfig.Deserialize(flowerConfig), FoliageConfig.Deserialize(leafConfig)) return new BallastFloraBranch(this, pos, (VineTileType)type, FoliageConfig.Deserialize(flowerConfig), FoliageConfig.Deserialize(leafConfig))
{ {
ID = id, ID = id,
MaxHealth = maxHealth,
Sides = (TileSide) sides Sides = (TileSide) sides
}; };
} }
@@ -22,47 +22,47 @@ namespace Barotrauma
var underwaterExplosion = GameMain.ParticleManager.CreateParticle("underwaterexplosion", worldPosition, Vector2.Zero, 0.0f, hull); var underwaterExplosion = GameMain.ParticleManager.CreateParticle("underwaterexplosion", worldPosition, Vector2.Zero, 0.0f, hull);
if (underwaterExplosion != null) if (underwaterExplosion != null)
{ {
underwaterExplosion.Size *= MathHelper.Clamp(attack.Range / 150.0f, 0.5f, 10.0f); underwaterExplosion.Size *= MathHelper.Clamp(Attack.Range / 150.0f, 0.5f, 10.0f);
underwaterExplosion.StartDelay = 0.0f; underwaterExplosion.StartDelay = 0.0f;
} }
} }
for (int i = 0; i < attack.Range * 0.1f; i++) for (int i = 0; i < Attack.Range * 0.1f; i++)
{ {
if (!underwater) if (!underwater)
{ {
float particleSpeed = Rand.Range(0.0f, 1.0f); float particleSpeed = Rand.Range(0.0f, 1.0f);
particleSpeed = particleSpeed * particleSpeed * attack.Range; particleSpeed = particleSpeed * particleSpeed * Attack.Range;
if (flames) if (flames)
{ {
float particleScale = MathHelper.Clamp(attack.Range * 0.0025f, 0.5f, 2.0f); float particleScale = MathHelper.Clamp(Attack.Range * 0.0025f, 0.5f, 2.0f);
var flameParticle = GameMain.ParticleManager.CreateParticle("explosionfire", var flameParticle = GameMain.ParticleManager.CreateParticle("explosionfire",
ClampParticlePos(worldPosition + Rand.Vector((float)System.Math.Sqrt(Rand.Range(0.0f, attack.Range))), hull), ClampParticlePos(worldPosition + Rand.Vector((float)System.Math.Sqrt(Rand.Range(0.0f, Attack.Range))), hull),
Rand.Vector(Rand.Range(0.0f, particleSpeed)), 0.0f, hull); Rand.Vector(Rand.Range(0.0f, particleSpeed)), 0.0f, hull);
if (flameParticle != null) flameParticle.Size *= particleScale; if (flameParticle != null) flameParticle.Size *= particleScale;
} }
if (smoke) if (smoke)
{ {
GameMain.ParticleManager.CreateParticle(Rand.Range(0.0f, 1.0f) < 0.5f ? "explosionsmoke" : "smoke", GameMain.ParticleManager.CreateParticle(Rand.Range(0.0f, 1.0f) < 0.5f ? "explosionsmoke" : "smoke",
ClampParticlePos(worldPosition + Rand.Vector((float)System.Math.Sqrt(Rand.Range(0.0f, attack.Range))), hull), ClampParticlePos(worldPosition + Rand.Vector((float)System.Math.Sqrt(Rand.Range(0.0f, Attack.Range))), hull),
Rand.Vector(Rand.Range(0.0f, particleSpeed)), 0.0f, hull); Rand.Vector(Rand.Range(0.0f, particleSpeed)), 0.0f, hull);
} }
} }
else if (underwaterBubble) else if (underwaterBubble)
{ {
Vector2 bubblePos = Rand.Vector(Rand.Range(0.0f, attack.Range * 0.5f)); Vector2 bubblePos = Rand.Vector(Rand.Range(0.0f, Attack.Range * 0.5f));
GameMain.ParticleManager.CreateParticle("risingbubbles", worldPosition + bubblePos, GameMain.ParticleManager.CreateParticle("risingbubbles", worldPosition + bubblePos,
Vector2.Zero, 0.0f, hull); Vector2.Zero, 0.0f, hull);
if (i < attack.Range * 0.02f) if (i < Attack.Range * 0.02f)
{ {
var underwaterExplosion = GameMain.ParticleManager.CreateParticle("underwaterexplosion", worldPosition + bubblePos, var underwaterExplosion = GameMain.ParticleManager.CreateParticle("underwaterexplosion", worldPosition + bubblePos,
Vector2.Zero, 0.0f, hull); Vector2.Zero, 0.0f, hull);
if (underwaterExplosion != null) if (underwaterExplosion != null)
{ {
underwaterExplosion.Size *= MathHelper.Clamp(attack.Range / 300.0f, 0.5f, 2.0f) * Rand.Range(0.8f, 1.2f); underwaterExplosion.Size *= MathHelper.Clamp(Attack.Range / 300.0f, 0.5f, 2.0f) * Rand.Range(0.8f, 1.2f);
} }
} }
@@ -77,7 +77,7 @@ namespace Barotrauma
if (flash) if (flash)
{ {
float displayRange = flashRange.HasValue ? flashRange.Value : attack.Range; float displayRange = flashRange.HasValue ? flashRange.Value : Attack.Range;
if (displayRange < 0.1f) { return; } if (displayRange < 0.1f) { return; }
var light = new LightSource(worldPosition, displayRange, Color.LightYellow, null); var light = new LightSource(worldPosition, displayRange, Color.LightYellow, null);
CoroutineManager.StartCoroutine(DimLight(light)); CoroutineManager.StartCoroutine(DimLight(light));
@@ -336,6 +336,7 @@ namespace Barotrauma
public void DrawSectionColors(SpriteBatch spriteBatch) public void DrawSectionColors(SpriteBatch spriteBatch)
{ {
if (BackgroundSections == null || BackgroundSections.Count == 0) { return; }
Vector2 drawOffset = Submarine == null ? Vector2.Zero : Submarine.DrawPosition; Vector2 drawOffset = Submarine == null ? Vector2.Zero : Submarine.DrawPosition;
Point sectionSize = BackgroundSections[0].Rect.Size; Point sectionSize = BackgroundSections[0].Rect.Size;
Vector2 drawPos = drawOffset + new Vector2(rect.Location.X + sectionSize.X / 2, rect.Location.Y - sectionSize.Y / 2); Vector2 drawPos = drawOffset + new Vector2(rect.Location.X + sectionSize.X / 2, rect.Location.Y - sectionSize.Y / 2);
@@ -31,11 +31,20 @@ namespace Barotrauma
List<VertexPositionTexture> vertices = new List<VertexPositionTexture>(); List<VertexPositionTexture> vertices = new List<VertexPositionTexture>();
foreach (VoronoiCell cell in cells) foreach (VoronoiCell cell in cells)
{ {
Vector2 minVert = cell.Edges[0].Point1;
Vector2 maxVert = cell.Edges[0].Point1;
float circumference = 0.0f; float circumference = 0.0f;
foreach (GraphEdge edge in cell.Edges) foreach (GraphEdge edge in cell.Edges)
{ {
circumference += Vector2.Distance(edge.Point1, edge.Point2); circumference += Vector2.Distance(edge.Point1, edge.Point2);
minVert = new Vector2(
Math.Min(minVert.X, edge.Point1.X),
Math.Min(minVert.Y, edge.Point1.Y));
maxVert = new Vector2(
Math.Max(maxVert.X, edge.Point1.X),
Math.Max(maxVert.Y, edge.Point1.Y));
} }
Vector2 center = (minVert + maxVert) / 2;
foreach (GraphEdge edge in cell.Edges) foreach (GraphEdge edge in cell.Edges)
{ {
if (!edge.IsSolid) { continue; } if (!edge.IsSolid) { continue; }
@@ -130,8 +139,8 @@ namespace Barotrauma
break; break;
} }
float point1UV = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(edge.Point1 - cell.Center)); float point1UV = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(edge.Point1 - center));
float point2UV = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(edge.Point2 - cell.Center)); float point2UV = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(edge.Point2 - center));
//handle wrapping around 0/360 //handle wrapping around 0/360
if (point1UV - point2UV > MathHelper.Pi) if (point1UV - point2UV > MathHelper.Pi)
{ {
@@ -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();
@@ -3,9 +3,11 @@ using Barotrauma.Networking;
using Barotrauma.Particles; using Barotrauma.Particles;
using Barotrauma.Sounds; using Barotrauma.Sounds;
using Barotrauma.SpriteDeformations; using Barotrauma.SpriteDeformations;
using FarseerPhysics;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
namespace Barotrauma namespace Barotrauma
@@ -72,6 +74,18 @@ namespace Barotrauma
private set; private set;
} }
public bool VisibleOnSonar
{
get;
private set;
}
public float SonarRadius
{
get;
private set;
}
partial void InitProjSpecific() partial void InitProjSpecific()
{ {
Sprite?.EnsureLazyLoaded(); Sprite?.EnsureLazyLoaded();
@@ -135,6 +149,13 @@ namespace Barotrauma
} }
} }
} }
VisibleOnSonar = Prefab.SonarDisruption > 0.0f || Prefab.OverrideProperties.Any(p => p != null && p.SonarDisruption > 0.0f) ||
(Triggers != null && Triggers.Any(t => !MathUtils.NearlyEqual(t.Force, Vector2.Zero) && t.ForceMode != LevelTrigger.TriggerForceMode.LimitVelocity || !string.IsNullOrWhiteSpace(t.InfectIdentifier)));
if (VisibleOnSonar && Triggers.Any())
{
SonarRadius = Triggers.Select(t => t.ColliderRadius * 1.5f).Max();
}
} }
public void Update(float deltaTime) public void Update(float deltaTime)
@@ -220,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)
{ {
@@ -294,7 +302,7 @@ namespace Barotrauma
{ {
GUI.DrawLine(spriteBatch, new Vector2(edge.Point1.X + cell.Translation.X, -(edge.Point1.Y + cell.Translation.Y)), GUI.DrawLine(spriteBatch, new Vector2(edge.Point1.X + cell.Translation.X, -(edge.Point1.Y + cell.Translation.Y)),
new Vector2(edge.Point2.X + cell.Translation.X, -(edge.Point2.Y + cell.Translation.Y)), edge.NextToCave ? Color.Red : (cell.Body == null ? Color.Cyan * 0.5f : (edge.IsSolid ? Color.White : Color.Gray)), new Vector2(edge.Point2.X + cell.Translation.X, -(edge.Point2.Y + cell.Translation.Y)), edge.NextToCave ? Color.Red : (cell.Body == null ? Color.Cyan * 0.5f : (edge.IsSolid ? Color.White : Color.Gray)),
width: edge.NextToCave ? 8 :1); width: edge.NextToCave ? 8 : 1);
} }
foreach (Vector2 point in cell.BodyVertices) foreach (Vector2 point in cell.BodyVertices)
@@ -314,6 +322,11 @@ namespace Barotrauma
} }
}*/ }*/
foreach (var abyssIsland in level.AbyssIslands)
{
GUI.DrawRectangle(spriteBatch, new Vector2(abyssIsland.Area.X, -abyssIsland.Area.Y - abyssIsland.Area.Height), abyssIsland.Area.Size.ToVector2(), Color.Cyan, thickness: 5);
}
foreach (var ruin in level.Ruins) foreach (var ruin in level.Ruins)
{ {
ruin.DebugDraw(spriteBatch); ruin.DebugDraw(spriteBatch);
@@ -395,20 +408,20 @@ namespace Barotrauma
graphicsDevice.SetVertexBuffer(wall.WallBuffer); graphicsDevice.SetVertexBuffer(wall.WallBuffer);
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(wall.WallBuffer.VertexCount / 3.0f)); graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(wall.WallBuffer.VertexCount / 3.0f));
if (destructibleWall.Damage > 0.0f)
{
wallCenterEffect.Texture = level.GenerationParams.WallSpriteDestroyed.Texture;
wallCenterEffect.Alpha = MathHelper.Lerp(0.2f, 1.0f, destructibleWall.Damage / destructibleWall.MaxHealth) * wall.Alpha;
wallCenterEffect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(wall.WallEdgeBuffer.VertexCount / 3.0f));
}
wallEdgeEffect.Texture = level.GenerationParams.DestructibleWallEdgeSprite?.Texture ?? level.GenerationParams.WallEdgeSprite.Texture; wallEdgeEffect.Texture = level.GenerationParams.DestructibleWallEdgeSprite?.Texture ?? level.GenerationParams.WallEdgeSprite.Texture;
wallEdgeEffect.World = wall.GetTransform() * transformMatrix; wallEdgeEffect.World = wall.GetTransform() * transformMatrix;
wallEdgeEffect.Alpha = wall.Alpha; wallEdgeEffect.Alpha = wall.Alpha;
wallEdgeEffect.CurrentTechnique.Passes[0].Apply(); wallEdgeEffect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.SetVertexBuffer(wall.WallEdgeBuffer); graphicsDevice.SetVertexBuffer(wall.WallEdgeBuffer);
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(wall.WallEdgeBuffer.VertexCount / 3.0f)); graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(wall.WallEdgeBuffer.VertexCount / 3.0f));
if (destructibleWall.Damage <= 0.0f) { continue; }
wallEdgeEffect.Texture = level.GenerationParams.WallSpriteDestroyed.Texture;
wallEdgeEffect.Alpha = MathHelper.Lerp(0.2f, 1.0f, destructibleWall.Damage / destructibleWall.MaxHealth) * wall.Alpha;
wallEdgeEffect.World = wall.GetTransform() * transformMatrix;
wallEdgeEffect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.SetVertexBuffer(wall.WallEdgeBuffer);
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(wall.WallEdgeBuffer.VertexCount / 3.0f));
} }
} }
@@ -132,7 +132,7 @@ namespace Barotrauma.Lights
public void AddLight(LightSource light) public void AddLight(LightSource light)
{ {
if (!lights.Contains(light)) lights.Add(light); if (!lights.Contains(light)) { lights.Add(light); }
} }
public void RemoveLight(LightSource light) public void RemoveLight(LightSource light)
@@ -153,7 +153,7 @@ namespace Barotrauma.Lights
public void Update(float deltaTime) public void Update(float deltaTime)
{ {
foreach (LightSource light in lights) foreach (LightSource light in activeLights)
{ {
if (!light.Enabled) { continue; } if (!light.Enabled) { continue; }
light.Update(deltaTime); light.Update(deltaTime);
@@ -183,7 +183,7 @@ namespace Barotrauma.Lights
foreach (LightSource light in lights) foreach (LightSource light in lights)
{ {
if (!light.Enabled) { continue; } if (!light.Enabled) { continue; }
if ((light.Color.A < 1 || light.Range < 1.0f || light.CurrentBrightness <= 0.0f) && !light.LightSourceParams.OverrideLightSpriteAlpha.HasValue) { continue; } if ((light.Color.A < 1 || light.Range < 1.0f) && !light.LightSourceParams.OverrideLightSpriteAlpha.HasValue) { continue; }
if (light.ParentBody != null) if (light.ParentBody != null)
{ {
light.Position = light.ParentBody.DrawPosition; light.Position = light.ParentBody.DrawPosition;
@@ -212,7 +212,7 @@ namespace Barotrauma.Lights
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, transformMatrix: spriteBatchTransform); spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, transformMatrix: spriteBatchTransform);
foreach (LightSource light in activeLights) foreach (LightSource light in activeLights)
{ {
if (light.IsBackground) { continue; } if (light.IsBackground || light.CurrentBrightness <= 0.0f) { continue; }
//draw limb lights at this point, because they were skipped over previously to prevent them from being obstructed //draw limb lights at this point, because they were skipped over previously to prevent them from being obstructed
if (light.ParentBody?.UserData is Limb limb && !limb.Hide) { light.DrawSprite(spriteBatch, cam); } if (light.ParentBody?.UserData is Limb limb && !limb.Hide) { light.DrawSprite(spriteBatch, cam); }
} }
@@ -227,7 +227,7 @@ namespace Barotrauma.Lights
Level.Loaded?.BackgroundCreatureManager?.DrawLights(spriteBatch, cam); Level.Loaded?.BackgroundCreatureManager?.DrawLights(spriteBatch, cam);
foreach (LightSource light in activeLights) foreach (LightSource light in activeLights)
{ {
if (!light.IsBackground) { continue; } if (!light.IsBackground || light.CurrentBrightness <= 0.0f) { continue; }
light.DrawSprite(spriteBatch, cam); light.DrawSprite(spriteBatch, cam);
light.DrawLightVolume(spriteBatch, lightEffect, transform); light.DrawLightVolume(spriteBatch, lightEffect, transform);
} }
@@ -272,7 +272,7 @@ namespace Barotrauma.Lights
foreach (LightSource light in activeLights) foreach (LightSource light in activeLights)
{ {
//don't draw limb lights at this point, they need to be drawn after lights have been obstructed by characters //don't draw limb lights at this point, they need to be drawn after lights have been obstructed by characters
if (light.IsBackground || light.ParentBody?.UserData is Limb) { continue; } if (light.IsBackground || light.ParentBody?.UserData is Limb || light.CurrentBrightness <= 0.0f) { continue; }
light.DrawSprite(spriteBatch, cam); light.DrawSprite(spriteBatch, cam);
} }
spriteBatch.End(); spriteBatch.End();
@@ -337,7 +337,7 @@ namespace Barotrauma.Lights
foreach (LightSource light in activeLights) foreach (LightSource light in activeLights)
{ {
if (light.IsBackground) { continue; } if (light.IsBackground || light.CurrentBrightness <= 0.0f) { continue; }
light.DrawLightVolume(spriteBatch, lightEffect, transform); light.DrawLightVolume(spriteBatch, lightEffect, transform);
} }
@@ -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)
{ {
@@ -470,37 +470,29 @@ namespace Barotrauma.Lights
public void Update(float deltaTime) public void Update(float deltaTime)
{ {
float brightness = 1.0f;
if (lightSourceParams.BlinkFrequency > 0.0f) if (lightSourceParams.BlinkFrequency > 0.0f)
{ {
blinkTimer = (blinkTimer + deltaTime * lightSourceParams.BlinkFrequency) % 1.0f; blinkTimer = (blinkTimer + deltaTime * lightSourceParams.BlinkFrequency) % 1.0f;
if (blinkTimer > 0.5f)
{
CurrentBrightness = 0.0f;
return;
}
} }
if (lightSourceParams.PulseFrequency > 0.0f && lightSourceParams.PulseAmount > 0.0f)
if (lightSourceParams.PulseFrequency > 0.0f)
{ {
pulseState = (pulseState + deltaTime * lightSourceParams.PulseFrequency) % 1.0f; pulseState = (pulseState + deltaTime * lightSourceParams.PulseFrequency) % 1.0f;
//oscillate between 0-1
brightness *= 1.0f - (float)(Math.Sin(pulseState * MathHelper.TwoPi) + 1.0f) / 2.0f * lightSourceParams.PulseAmount;
} }
if (lightSourceParams.Flicker > 0.0f)
if (blinkTimer > 0.5f)
{ {
CurrentBrightness = 0.0f; flickerState += deltaTime * lightSourceParams.FlickerSpeed;
} flickerState %= 255;
else brightness *= 1.0f - PerlinNoise.GetPerlin(flickerState, flickerState * 0.5f) * lightSourceParams.Flicker;
{
float flicker = 0.0f;
float pulse = 0.0f;
if (lightSourceParams.Flicker > 0.0f)
{
flickerState += deltaTime * lightSourceParams.FlickerSpeed;
flickerState %= 255;
flicker = PerlinNoise.GetPerlin(flickerState, flickerState * 0.5f) * lightSourceParams.Flicker;
}
if (lightSourceParams.PulseFrequency > 0.0f && lightSourceParams.PulseAmount > 0.0f)
{
//oscillate between 0-1
pulse = (float)(Math.Sin(pulseState * MathHelper.TwoPi) + 1.0f) / 2.0f * lightSourceParams.PulseAmount;
}
CurrentBrightness = (1.0f - flicker) * (1.0f - pulse);
} }
CurrentBrightness = brightness;
} }
/// <summary> /// <summary>
@@ -2,8 +2,8 @@
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using Microsoft.Xna.Framework.Input;
namespace Barotrauma namespace Barotrauma
{ {
@@ -63,6 +63,8 @@ namespace Barotrauma
private Sprite[,] mapTiles; private Sprite[,] mapTiles;
private bool[,] tileDiscovered; private bool[,] tileDiscovered;
private float connectionHighlightState;
private Pair<Rectangle, string> connectionTooltip; private Pair<Rectangle, string> connectionTooltip;
#if DEBUG #if DEBUG
@@ -120,7 +122,6 @@ namespace Barotrauma
DrawOffset = -CurrentLocation.MapPosition; DrawOffset = -CurrentLocation.MapPosition;
} }
Vector2 tileSize = generationParams.MapTiles.Values.First().First().size * generationParams.MapTileScale; Vector2 tileSize = generationParams.MapTiles.Values.First().First().size * generationParams.MapTileScale;
int tilesX = (int)Math.Ceiling(Width / tileSize.X); int tilesX = (int)Math.Ceiling(Width / tileSize.X);
int tilesY = (int)Math.Ceiling(Height / tileSize.Y); int tilesY = (int)Math.Ceiling(Height / tileSize.Y);
@@ -222,7 +223,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())
{ {
@@ -267,15 +268,37 @@ namespace Barotrauma
} }
} }
currLocationIndicatorPos = Vector2.Lerp(currLocationIndicatorPos, CurrentDisplayLocation.MapPosition, deltaTime); Vector2 currentPosition = CurrentDisplayLocation.MapPosition;
if (Level.Loaded?.Type == LevelData.LevelType.LocationConnection && Level.Loaded.StartLocation != null && Level.Loaded.EndLocation != null)
{
Vector2 startPos = CurrentDisplayLocation == Level.Loaded.StartLocation ? Level.Loaded.StartLocation.MapPosition : Level.Loaded.EndLocation.MapPosition;
int moveDir = CurrentDisplayLocation == Level.Loaded.StartLocation ? 1 : -1;
Vector2 diff = Level.Loaded.EndLocation.MapPosition - Level.Loaded.StartLocation.MapPosition;
currentPosition = startPos +
Vector2.Normalize(diff) * Math.Min(100, diff.Length() * 0.2f) * moveDir;
}
else
{
currentPosition += Vector2.UnitY * 35;
}
currLocationIndicatorPos = Vector2.Lerp(currLocationIndicatorPos, currentPosition, deltaTime);
#if DEBUG #if DEBUG
if (GameMain.DebugDraw) if (GameMain.DebugDraw)
{ {
if (editor == null) CreateEditor(); if (editor == null) CreateEditor();
editor.AddToGUIUpdateList(order: 1); editor.AddToGUIUpdateList(order: 1);
} }
if (PlayerInput.KeyHit(Keys.Space))
{
Radiation?.OnStep();
}
#endif #endif
Radiation?.MapUpdate(deltaTime);
if (mapAnimQueue.Count > 0) if (mapAnimQueue.Count > 0)
{ {
hudVisibility = Math.Max(hudVisibility - deltaTime, 0.0f); hudVisibility = Math.Max(hudVisibility - deltaTime, 0.0f);
@@ -324,6 +347,15 @@ namespace Barotrauma
} }
} }
if (SelectedConnection != null)
{
connectionHighlightState = Math.Min(connectionHighlightState + deltaTime, 1.0f);
}
else
{
connectionHighlightState = 0.0f;
}
if (GUI.KeyboardDispatcher.Subscriber == null) if (GUI.KeyboardDispatcher.Subscriber == null)
{ {
float moveSpeed = 1000.0f; float moveSpeed = 1000.0f;
@@ -336,7 +368,7 @@ namespace Barotrauma
} }
targetZoom = MathHelper.Clamp(targetZoom, generationParams.MinZoom, generationParams.MaxZoom); targetZoom = MathHelper.Clamp(targetZoom, generationParams.MinZoom, generationParams.MaxZoom);
zoom = MathHelper.Lerp(zoom, targetZoom, 0.1f); zoom = MathHelper.Lerp(zoom, targetZoom * GUI.Scale, 0.1f);
if (GUI.MouseOn == mapContainer) if (GUI.MouseOn == mapContainer)
{ {
@@ -351,6 +383,7 @@ namespace Barotrauma
//clients aren't allowed to select the location without a permission //clients aren't allowed to select the location without a permission
if ((GameMain.GameSession?.GameMode as CampaignMode)?.AllowedToManageCampaign() ?? false) if ((GameMain.GameSession?.GameMode as CampaignMode)?.AllowedToManageCampaign() ?? false)
{ {
connectionHighlightState = 0.0f;
SelectedConnection = connection; SelectedConnection = connection;
SelectedLocation = HighlightedLocation; SelectedLocation = HighlightedLocation;
@@ -383,12 +416,13 @@ 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();
Radiation.OnStep(1);
} }
else else
{ {
@@ -483,6 +517,8 @@ namespace Barotrauma
float rawNoiseScale = 1.0f + PerlinNoise.GetPerlin((int)(Timing.TotalTime * 1 - 1), (int)(Timing.TotalTime * 1 - 1)); float rawNoiseScale = 1.0f + PerlinNoise.GetPerlin((int)(Timing.TotalTime * 1 - 1), (int)(Timing.TotalTime * 1 - 1));
cameraNoiseStrength = PerlinNoise.GetPerlin((int)(Timing.TotalTime * 1 - 1), (int)(Timing.TotalTime * 1 - 1)); cameraNoiseStrength = PerlinNoise.GetPerlin((int)(Timing.TotalTime * 1 - 1), (int)(Timing.TotalTime * 1 - 1));
Radiation.Draw(spriteBatch, rect, zoom);
noiseOverlay.DrawTiled(spriteBatch, rect.Location.ToVector2(), rect.Size.ToVector2(), noiseOverlay.DrawTiled(spriteBatch, rect.Location.ToVector2(), rect.Size.ToVector2(),
startOffset: new Vector2(Rand.Range(0.0f, noiseOverlay.SourceRect.Width), Rand.Range(0.0f, noiseOverlay.SourceRect.Height)), startOffset: new Vector2(Rand.Range(0.0f, noiseOverlay.SourceRect.Width), Rand.Range(0.0f, noiseOverlay.SourceRect.Height)),
color : Color.White * cameraNoiseStrength * 0.1f, color : Color.White * cameraNoiseStrength * 0.1f,
@@ -519,22 +555,6 @@ namespace Barotrauma
if (!rect.Intersects(drawRect)) { continue; } if (!rect.Intersects(drawRect)) { continue; }
if (location == CurrentDisplayLocation )
{
generationParams.CurrentLocationIndicator.Draw(spriteBatch,
rectCenter + (currLocationIndicatorPos + viewOffset) * zoom,
generationParams.IndicatorColor,
generationParams.CurrentLocationIndicator.Origin, 0, Vector2.One * (generationParams.LocationIconSize / generationParams.CurrentLocationIndicator.size.X) * 1.7f * zoom);
}
if (location == SelectedLocation)
{
generationParams.SelectedLocationIndicator.Draw(spriteBatch,
rectCenter + (location.MapPosition + viewOffset) * zoom,
generationParams.IndicatorColor,
generationParams.SelectedLocationIndicator.Origin, 0, Vector2.One * (generationParams.LocationIconSize / generationParams.SelectedLocationIndicator.size.X) * 1.7f * zoom);
}
Color color = location.Type.SpriteColor; Color color = location.Type.SpriteColor;
if (!location.Discovered) { color = Color.White; } if (!location.Discovered) { color = Color.White; }
if (location.Connections.Find(c => c.Locations.Contains(CurrentDisplayLocation)) == null) if (location.Connections.Find(c => c.Locations.Contains(CurrentDisplayLocation)) == null)
@@ -542,6 +562,12 @@ namespace Barotrauma
color *= 0.5f; color *= 0.5f;
} }
// TODO proper visualization of this
if (location.Type.HasOutpost && !location.HasOutpost())
{
color = GUI.Style.Red;
}
float iconScale = location == CurrentDisplayLocation ? 1.2f : 1.0f; float iconScale = location == CurrentDisplayLocation ? 1.2f : 1.0f;
if (location == HighlightedLocation) if (location == HighlightedLocation)
{ {
@@ -550,7 +576,35 @@ 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 == CurrentDisplayLocation)
{
if (SelectedLocation != null)
{
Vector2 dir = Vector2.Normalize(SelectedLocation.MapPosition - currLocationIndicatorPos);
GUI.Arrow.Draw(spriteBatch,
rectCenter + (currLocationIndicatorPos + viewOffset) * zoom + dir * generationParams.LocationIconSize * 0.6f * zoom,
generationParams.IndicatorColor,
GUI.Arrow.Origin,
rotate: MathUtils.VectorToAngle(dir) + MathHelper.PiOver2,
new Vector2(0.5f, 1.0f) * zoom);
}
generationParams.CurrentLocationIndicator.Draw(spriteBatch,
rectCenter + (currLocationIndicatorPos + viewOffset) * zoom,
generationParams.IndicatorColor,
generationParams.CurrentLocationIndicator.Origin, 0, Vector2.One * (generationParams.LocationIconSize / generationParams.CurrentLocationIndicator.size.X) * 0.8f * zoom);
}
if (location == SelectedLocation)
{
generationParams.SelectedLocationIndicator.Draw(spriteBatch,
rectCenter + (location.MapPosition + viewOffset) * zoom,
generationParams.IndicatorColor,
generationParams.SelectedLocationIndicator.Origin, 0, Vector2.One * (generationParams.LocationIconSize / generationParams.SelectedLocationIndicator.size.X) * 1.7f * zoom);
}
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;
@@ -576,7 +630,7 @@ namespace Barotrauma
} }
} }
if (GameMain.DebugDraw && location == HighlightedLocation && (!location.Discovered || !location.Type.HasOutpost)) if (GameMain.DebugDraw && location == HighlightedLocation && (!location.Discovered || !location.HasOutpost()))
{ {
if (location.Reputation != null) if (location.Reputation != null)
{ {
@@ -608,9 +662,9 @@ namespace Barotrauma
Vector2 pos = rectCenter + (HighlightedLocation.MapPosition + viewOffset) * zoom; Vector2 pos = rectCenter + (HighlightedLocation.MapPosition + viewOffset) * zoom;
pos.X += 50 * zoom; pos.X += 50 * zoom;
Vector2 nameSize = GUI.LargeFont.MeasureString(HighlightedLocation.Name); Vector2 nameSize = GUI.LargeFont.MeasureString(HighlightedLocation.Name);
Vector2 typeSize = GUI.Font.MeasureString(HighlightedLocation.Type.Name); Vector2 typeSize = string.IsNullOrEmpty(HighlightedLocation.Type.Name) ? Vector2.Zero : 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)
@@ -674,19 +728,22 @@ namespace Barotrauma
int width = (int)(generationParams.LocationConnectionWidth * zoom); int width = (int)(generationParams.LocationConnectionWidth * zoom);
//current level
if (Level.Loaded?.LevelData == connection.LevelData) if (Level.Loaded?.LevelData == connection.LevelData)
{ {
connectionColor = generationParams.HighlightedConnectionColor; connectionColor = generationParams.HighlightedConnectionColor;
width = (int)(width * 1.5f); width = (int)(width * 1.5f);
} }
//selected connection
if (SelectedLocation != CurrentDisplayLocation && if (SelectedLocation != CurrentDisplayLocation &&
(connection.Locations.Contains(SelectedLocation) && connection.Locations.Contains(CurrentDisplayLocation))) connection.Locations.Contains(SelectedLocation) && connection.Locations.Contains(CurrentDisplayLocation))
{ {
connectionColor = generationParams.HighlightedConnectionColor; connectionColor = generationParams.HighlightedConnectionColor;
width *= 2; width *= 2;
} }
//highlighted connection
else if (HighlightedLocation != CurrentDisplayLocation && else if (HighlightedLocation != CurrentDisplayLocation &&
(connection.Locations.Contains(HighlightedLocation) && connection.Locations.Contains(CurrentDisplayLocation))) connection.Locations.Contains(HighlightedLocation) && connection.Locations.Contains(CurrentDisplayLocation))
{ {
connectionColor = generationParams.HighlightedConnectionColor; connectionColor = generationParams.HighlightedConnectionColor;
width *= 2; width *= 2;
@@ -741,47 +798,90 @@ namespace Barotrauma
} }
float dist = Vector2.Distance(start, end); float dist = Vector2.Distance(start, end);
var connectionSprite = connection.Passed ? generationParams.PassedConnectionSprite : generationParams.ConnectionSprite; var connectionSprite = connection.Passed ? generationParams.PassedConnectionSprite : generationParams.ConnectionSprite;
Color segmentColor = connectionColor;
int segmentWidth = width;
if (connection == SelectedConnection)
{
float t = (i - startIndex) / (float)(endIndex - startIndex - 1);
if (CurrentDisplayLocation == connection.Locations[1]) { t = 1.0f - t; }
if (t > connectionHighlightState)
{
segmentWidth /= 2;
segmentColor = connection.Passed ? generationParams.ConnectionColor : generationParams.UnvisitedConnectionColor;
}
else
{
}
}
spriteBatch.Draw(connectionSprite.Texture, spriteBatch.Draw(connectionSprite.Texture,
new Rectangle((int)start.X, (int)start.Y, (int)(dist - 1 * zoom), width), new Rectangle((int)start.X, (int)start.Y, (int)(dist - 1 * zoom), segmentWidth),
connectionSprite.SourceRect, connectionColor * a, MathUtils.VectorToAngle(end - start), connectionSprite.SourceRect, segmentColor * a,
MathUtils.VectorToAngle(end - start),
new Vector2(0, connectionSprite.size.Y / 2), SpriteEffects.None, 0.01f); new Vector2(0, connectionSprite.size.Y / 2), SpriteEffects.None, 0.01f);
} }
int iconCount = 0, iconIndex = 0;
if (connectionStart.HasValue && connectionEnd.HasValue) if (connectionStart.HasValue && connectionEnd.HasValue)
{ {
GUIComponentStyle crushDepthWarningIconStyle = null; if (connection.LevelData.HasBeaconStation) { iconCount++; }
if (connection.LevelData.HasHuntingGrounds) { iconCount++; }
string tooltip = null; string tooltip = null;
if (connection.LevelData.InitialDepth * Physics.DisplayToRealWorldRatio > connection.LevelData.RealWorldCrushDepth) var subCrushDepth = Submarine.MainSub?.RealWorldCrushDepth ?? Level.DefaultRealWorldCrushDepth;
if (GameMain.GameSession?.Campaign?.UpgradeManager != null)
{ {
crushDepthWarningIconStyle = GUI.Style.GetComponentStyle("CrushDepthWarningHighIcon"); var hullUpgradePrefab = UpgradePrefab.Find("increasewallhealth");
if (hullUpgradePrefab != null)
{
int pendingLevel = GameMain.GameSession.Campaign.UpgradeManager.GetUpgradeLevel(hullUpgradePrefab, hullUpgradePrefab.UpgradeCategories.First());
int currentLevel = GameMain.GameSession.Campaign.UpgradeManager.GetRealUpgradeLevel(hullUpgradePrefab, hullUpgradePrefab.UpgradeCategories.First());
if (pendingLevel > currentLevel)
{
string updateValueStr = hullUpgradePrefab.SourceElement?.Element("Structure")?.GetAttributeString("crushdepth", null);
if (!string.IsNullOrEmpty(updateValueStr))
{
subCrushDepth = PropertyReference.CalculateUpgrade(subCrushDepth, pendingLevel - currentLevel, updateValueStr);
}
}
}
}
string crushDepthWarningIconStyle = null;
if (connection.LevelData.InitialDepth * Physics.DisplayToRealWorldRatio > subCrushDepth)
{
iconCount++;
crushDepthWarningIconStyle = "CrushDepthWarningHighIcon";
tooltip = "crushdepthwarninghigh"; tooltip = "crushdepthwarninghigh";
} }
else if ((connection.LevelData.InitialDepth + connection.LevelData.Size.Y) * Physics.DisplayToRealWorldRatio > connection.LevelData.RealWorldCrushDepth) else if ((connection.LevelData.InitialDepth + connection.LevelData.Size.Y) * Physics.DisplayToRealWorldRatio > subCrushDepth)
{ {
crushDepthWarningIconStyle = GUI.Style.GetComponentStyle("CrushDepthWarningLowIcon"); iconCount++;
crushDepthWarningIconStyle = "CrushDepthWarningLowIcon";
tooltip = "crushdepthwarninglow"; tooltip = "crushdepthwarninglow";
} }
if (connection.LevelData.HasBeaconStation)
{
var beaconStationIconStyle = connection.LevelData.IsBeaconActive ? "BeaconStationActive" : "BeaconStationInactive";
DrawIcon(beaconStationIconStyle, (int)(28 * zoom), TextManager.Get(connection.LevelData.IsBeaconActive ? "BeaconStationActiveTooltip" : "BeaconStationInactiveTooltip"));
}
if (connection.LevelData.HasHuntingGrounds)
{
DrawIcon("HuntingGrounds", (int)(28 * zoom), TextManager.Get("HuntingGroundsTooltip"));
}
if (crushDepthWarningIconStyle != null) if (crushDepthWarningIconStyle != null)
{ {
Vector2 iconPos = (connectionStart.Value + connectionEnd.Value) / 2; DrawIcon(crushDepthWarningIconStyle, (int)(32 * zoom),
float iconSize = 32.0f * GUI.Scale; TextManager.Get(tooltip)
bool mouseOn = HighlightedLocation == null && Vector2.DistanceSquared(iconPos, PlayerInput.MousePosition) < iconSize * iconSize;
Sprite crushDepthWarningIcon = crushDepthWarningIconStyle.GetDefaultSprite();
crushDepthWarningIcon.Draw(spriteBatch, iconPos,
mouseOn ? crushDepthWarningIconStyle.HoverColor : crushDepthWarningIconStyle.Color,
scale: iconSize / crushDepthWarningIcon.size.X);
if (mouseOn)
{
connectionTooltip = new Pair<Rectangle, string>(
new Rectangle(iconPos.ToPoint(), new Point((int)iconSize)),
TextManager.Get(tooltip)
.Replace("[initialdepth]", ((int)(connection.LevelData.InitialDepth * Physics.DisplayToRealWorldRatio)).ToString()) .Replace("[initialdepth]", ((int)(connection.LevelData.InitialDepth * Physics.DisplayToRealWorldRatio)).ToString())
.Replace("[submarinecrushdepth]", ((int)(Submarine.MainSub?.RealWorldCrushDepth ?? Level.DefaultRealWorldCrushDepth)).ToString())); .Replace("[submarinecrushdepth]", ((int)subCrushDepth).ToString()));
}
} }
} }
if (GameMain.DebugDraw && zoom > 1.0f && generationParams.ShowLevelTypeNames) if (GameMain.DebugDraw && zoom > (1.0f * GUI.Scale) && generationParams.ShowLevelTypeNames)
{ {
Vector2 center = rectCenter + (connection.CenterPos + viewOffset) * zoom; Vector2 center = rectCenter + (connection.CenterPos + viewOffset) * zoom;
if (viewArea.Contains(center) && connection.Biome != null) if (viewArea.Contains(center) && connection.Biome != null)
@@ -789,6 +889,25 @@ namespace Barotrauma
GUI.DrawString(spriteBatch, center, connection.Biome.Identifier + " (" + connection.Difficulty + ")", Color.White); GUI.DrawString(spriteBatch, center, connection.Biome.Identifier + " (" + connection.Difficulty + ")", Color.White);
} }
} }
void DrawIcon(string iconStyle, int iconSize, string tooltip)
{
Vector2 iconPos = (connectionStart.Value + connectionEnd.Value) / 2;
Vector2 iconDiff = Vector2.Normalize(connectionEnd.Value - connectionStart.Value) * iconSize;
iconPos += (iconDiff * -(iconCount - 1) / 2.0f) + iconDiff * iconIndex;
var style = GUI.Style.GetComponentStyle(iconStyle);
bool mouseOn = HighlightedLocation == null && Vector2.DistanceSquared(iconPos, PlayerInput.MousePosition) < iconSize * iconSize;
Sprite iconSprite = style.GetDefaultSprite();
iconSprite.Draw(spriteBatch, iconPos, (mouseOn ? style.HoverColor : style.Color) * 0.7f,
scale: iconSize / iconSprite.size.X);
if (mouseOn)
{
connectionTooltip = new Pair<Rectangle, string>(new Rectangle(iconPos.ToPoint(), new Point(iconSize)), tooltip);
}
iconIndex++;
}
} }
private float hudVisibility; private float hudVisibility;
@@ -819,8 +938,9 @@ namespace Barotrauma
return; return;
} }
if (anim.StartZoom == null) { anim.StartZoom = MathUtils.InverseLerp(generationParams.MinZoom, generationParams.MaxZoom, zoom); } float unscaledZoom = zoom / GUI.Scale;
if (anim.EndZoom == null) { anim.EndZoom = MathUtils.InverseLerp(generationParams.MinZoom, generationParams.MaxZoom, zoom); } if (anim.StartZoom == null) { anim.StartZoom = MathUtils.InverseLerp(generationParams.MinZoom, generationParams.MaxZoom, unscaledZoom); }
if (anim.EndZoom == null) { anim.EndZoom = MathUtils.InverseLerp(generationParams.MinZoom, generationParams.MaxZoom, unscaledZoom); }
anim.StartPos = (anim.StartLocation == null) ? -DrawOffset : anim.StartLocation.MapPosition; anim.StartPos = (anim.StartLocation == null) ? -DrawOffset : anim.StartLocation.MapPosition;
@@ -833,7 +953,8 @@ namespace Barotrauma
zoom = zoom =
MathHelper.Lerp(generationParams.MinZoom, generationParams.MaxZoom, MathHelper.Lerp(generationParams.MinZoom, generationParams.MaxZoom,
MathHelper.SmoothStep(anim.StartZoom.Value, anim.EndZoom.Value, t)); MathHelper.SmoothStep(anim.StartZoom.Value, anim.EndZoom.Value, t))
* GUI.Scale;
if (anim.Timer >= anim.Duration) if (anim.Timer >= anim.Duration)
{ {
@@ -0,0 +1,38 @@
#nullable enable
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma
{
internal partial class Radiation
{
public void Draw(SpriteBatch spriteBatch, Rectangle container, float zoom)
{
if (!Enabled) { return; }
UISprite uiSprite = GUI.Style.RadiationSprite;
var (offsetX, offsetY) = Map.DrawOffset * zoom;
var (centerX, centerY) = container.Center.ToVector2();
var (halfSizeX, halfSizeY) = new Vector2(container.Width / 2f, container.Height / 2f) * zoom;
float viewBottom = centerY + Map.Height * zoom;
Vector2 topLeft = new Vector2(centerX + offsetX - halfSizeX, centerY + offsetY - halfSizeY);
Vector2 size = new Vector2((Amount - increasedAmount) * zoom + halfSizeX, viewBottom - topLeft.Y);
if (size.X < 0) { return; }
uiSprite.Sprite.DrawTiled(spriteBatch, topLeft, size, GUI.Style.Red * 0.33f, Vector2.Zero, textureScale: new Vector2(zoom));
if (container.Contains(PlayerInput.MousePosition) && PlayerInput.MousePosition.X < topLeft.X + size.X)
{
// TODO tooltip?
}
}
public void MapUpdate(float deltaTime)
{
if (increasedAmount > 0)
{
increasedAmount -= (lastIncrease / Params.AnimationSpeed) * deltaTime;
}
}
}
}
@@ -114,6 +114,18 @@ namespace Barotrauma
public MapEntity ReplacedBy; public MapEntity ReplacedBy;
public virtual void Draw(SpriteBatch spriteBatch, bool editing, bool back = true) { } public virtual void Draw(SpriteBatch spriteBatch, bool editing, bool back = true) { }
/// <summary>
/// A method that modifies the draw depth to prevent z-fighting between entities with the same sprite depth
/// </summary>
public float GetDrawDepth(float baseDepth, Sprite sprite)
{
float depth = baseDepth
//take texture into account to get entities with (roughly) the same base depth and texture to render consecutively to minimize texture swaps
+ (sprite?.Texture?.SortingKey ?? 0) % 100 * 0.00001f
+ ID % 100 * 0.000001f;
return Math.Min(depth, 1.0f);
}
/// <summary> /// <summary>
/// Update the selection logic in submarine editor /// Update the selection logic in submarine editor
@@ -202,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))
{ {
@@ -267,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
@@ -464,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
@@ -479,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;
@@ -499,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;
@@ -24,7 +24,7 @@ namespace Barotrauma
{ {
get get
{ {
if (!GameMain.SubEditorScreen.ShowThalamus && prefab.Category.HasFlag(MapEntityCategory.Thalamus)) if (GameMain.SubEditorScreen.IsSubcategoryHidden(prefab.Subcategory))
{ {
return false; return false;
} }
@@ -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,11 +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()
{ {
float depth = SpriteDepthOverrideIsSet ? SpriteOverrideDepth : prefab.sprite.Depth; return GetDrawDepth(GetRealDepth(), prefab.sprite);
depth -= (ID % 255) * 0.000001f;
return depth;
} }
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)
@@ -254,7 +258,7 @@ namespace Barotrauma
thickness: Math.Max(1, (int)(2 / Screen.Selected.Cam.Zoom))); thickness: Math.Max(1, (int)(2 / Screen.Selected.Cam.Zoom)));
} }
bool isWiringMode = editing && SubEditorScreen.IsWiringMode(); bool isWiringMode = editing && SubEditorScreen.TransparentWiringMode && SubEditorScreen.IsWiringMode();
if (isWiringMode) { color *= 0.15f; } if (isWiringMode) { color *= 0.15f; }
@@ -263,8 +267,8 @@ namespace Barotrauma
float depth = GetDrawDepth(); float depth = GetDrawDepth();
Vector2 textureOffset = this.textureOffset; Vector2 textureOffset = this.textureOffset;
if (FlippedX) textureOffset.X = -textureOffset.X; if (FlippedX) { textureOffset.X = -textureOffset.X; }
if (FlippedY) textureOffset.Y = -textureOffset.Y; if (FlippedY) { textureOffset.Y = -textureOffset.Y; }
if (back && damageEffect == null && !isWiringMode) if (back && damageEffect == null && !isWiringMode)
{ {
@@ -304,7 +308,7 @@ namespace Barotrauma
color: Prefab.BackgroundSpriteColor, color: Prefab.BackgroundSpriteColor,
textureScale: TextureScale * Scale, textureScale: TextureScale * Scale,
startOffset: backGroundOffset, startOffset: backGroundOffset,
depth: Math.Max(Prefab.BackgroundSprite.Depth + (ID % 255) * 0.000001f, depth + 0.000001f)); depth: Math.Max(GetDrawDepth(Prefab.BackgroundSprite.Depth, Prefab.BackgroundSprite), depth + 0.000001f));
if (UseDropShadow) if (UseDropShadow)
{ {
@@ -322,13 +326,14 @@ 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;
for (int i = 0; i < Sections.Length; i++) for (int i = 0; i < Sections.Length; i++)
{ {
Rectangle drawSection = Sections[i].rect;
if (damageEffect != null) if (damageEffect != null)
{ {
float newCutoff = MathHelper.Lerp(0.0f, 0.65f, Sections[i].damage / MaxHealth); float newCutoff = MathHelper.Lerp(0.0f, 0.65f, Sections[i].damage / MaxHealth);
@@ -345,21 +350,30 @@ namespace Barotrauma
Submarine.DamageEffectColor = color; Submarine.DamageEffectColor = color;
} }
} }
if (!HasDamage && i == 0)
{
drawSection = new Rectangle(
drawSection.X,
drawSection.Y,
Sections[Sections.Length -1 ].rect.Right - drawSection.X,
drawSection.Y - (Sections[Sections.Length - 1].rect.Y - Sections[Sections.Length - 1].rect.Height));
i = Sections.Length;
}
Vector2 sectionOffset = new Vector2( Vector2 sectionOffset = new Vector2(
Math.Abs(rect.Location.X - Sections[i].rect.Location.X), Math.Abs(rect.Location.X - drawSection.Location.X),
Math.Abs(rect.Location.Y - Sections[i].rect.Location.Y)); Math.Abs(rect.Location.Y - drawSection.Location.Y));
if (FlippedX && IsHorizontal) sectionOffset.X = Sections[i].rect.Right - rect.Right; if (FlippedX && IsHorizontal) { sectionOffset.X = drawSection.Right - rect.Right; }
if (FlippedY && !IsHorizontal) sectionOffset.Y = (rect.Y - rect.Height) - (Sections[i].rect.Y - Sections[i].rect.Height); if (FlippedY && !IsHorizontal) { sectionOffset.Y = (rect.Y - rect.Height) - (drawSection.Y - drawSection.Height); }
sectionOffset.X += MathUtils.PositiveModulo((int)-textureOffset.X, prefab.sprite.SourceRect.Width); sectionOffset.X += MathUtils.PositiveModulo((int)-textureOffset.X, prefab.sprite.SourceRect.Width);
sectionOffset.Y += MathUtils.PositiveModulo((int)-textureOffset.Y, prefab.sprite.SourceRect.Height); sectionOffset.Y += MathUtils.PositiveModulo((int)-textureOffset.Y, prefab.sprite.SourceRect.Height);
prefab.sprite.DrawTiled( prefab.sprite.DrawTiled(
spriteBatch, spriteBatch,
new Vector2(Sections[i].rect.X + drawOffset.X, -(Sections[i].rect.Y + drawOffset.Y)), new Vector2(drawSection.X + drawOffset.X, -(drawSection.Y + drawOffset.Y)),
new Vector2(Sections[i].rect.Width, Sections[i].rect.Height), new Vector2(drawSection.Width, drawSection.Height),
color: color, color: color,
startOffset: sectionOffset, startOffset: sectionOffset,
depth: depth, depth: depth,
@@ -369,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;

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