v0.19.0.0 (unstable)

This commit is contained in:
Regalis11
2022-07-20 18:47:07 +03:00
parent 2e2663a175
commit 6b55adcdd9
170 changed files with 2769 additions and 1634 deletions
@@ -159,7 +159,7 @@ namespace Barotrauma
GameMain.LightManager.LosEnabled = true; GameMain.LightManager.LosEnabled = true;
} }
#endif #endif
timer += CoroutineManager.UnscaledDeltaTime; timer += CoroutineManager.DeltaTime;
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
} }
@@ -73,6 +73,11 @@ namespace Barotrauma
} }
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 80.0f, State.ToString(), stateColor, Color.Black); GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 80.0f, State.ToString(), stateColor, Color.Black);
if (State == AIState.Attack && selectedTargetingParams != null && selectedTargetingParams.AttackPattern == AttackPattern.Circle)
{
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 100.0f, CirclePhase.ToString(), stateColor, Color.Black);
}
if (LatchOntoAI != null && (State == AIState.Idle || LatchOntoAI.IsAttachedToSub)) if (LatchOntoAI != null && (State == AIState.Idle || LatchOntoAI.IsAttachedToSub))
{ {
foreach (Joint attachJoint in LatchOntoAI.AttachJoints) foreach (Joint attachJoint in LatchOntoAI.AttachJoints)
@@ -1,15 +1,14 @@
using Barotrauma.Items.Components; using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using Barotrauma.Particles;
using Barotrauma.SpriteDeformations; using Barotrauma.SpriteDeformations;
using Barotrauma.Extensions;
using FarseerPhysics; using FarseerPhysics;
using FarseerPhysics.Dynamics; using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Joints;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using Barotrauma.Particles; using System.Linq;
namespace Barotrauma namespace Barotrauma
{ {
@@ -55,21 +54,34 @@ namespace Barotrauma
if (character.MemState[0].SelectedItem == null || character.MemState[0].SelectedItem.Removed) if (character.MemState[0].SelectedItem == null || character.MemState[0].SelectedItem.Removed)
{ {
character.SelectedConstruction = null; character.SelectedItem = null;
} }
else else if (character.SelectedItem != character.MemState[0].SelectedItem)
{ {
if (character.SelectedConstruction != character.MemState[0].SelectedItem) foreach (var ic in character.MemState[0].SelectedItem.Components)
{ {
foreach (var ic in character.MemState[0].SelectedItem.Components) if (ic.CanBeSelected)
{ {
if (ic.CanBeSelected) ic.Select(character);
{
ic.Select(character);
}
} }
} }
character.SelectedConstruction = character.MemState[0].SelectedItem; character.SelectedItem = character.MemState[0].SelectedItem;
}
if (character.MemState[0].SelectedSecondaryItem == null || character.MemState[0].SelectedSecondaryItem.Removed)
{
character.SelectedSecondaryItem = null;
}
else if (character.SelectedSecondaryItem != character.MemState[0].SelectedSecondaryItem)
{
foreach (var ic in character.MemState[0].SelectedSecondaryItem.Components)
{
if (ic.CanBeSelected)
{
ic.Select(character);
}
}
character.SelectedSecondaryItem = character.MemState[0].SelectedSecondaryItem;
} }
if (character.MemState[0].Animation == AnimController.Animation.CPR) if (character.MemState[0].Animation == AnimController.Animation.CPR)
@@ -201,15 +213,24 @@ namespace Barotrauma
{ {
if (serverPos.SelectedItem == null || serverPos.SelectedItem.Removed) if (serverPos.SelectedItem == null || serverPos.SelectedItem.Removed)
{ {
character.SelectedConstruction = null; character.SelectedItem = null;
} }
else if (serverPos.SelectedItem != null) else if (character.SelectedItem != serverPos.SelectedItem)
{ {
if (character.SelectedConstruction != serverPos.SelectedItem) serverPos.SelectedItem.TryInteract(character, ignoreRequiredItems: true, forceSelectKey: true);
{ character.SelectedItem = serverPos.SelectedItem;
serverPos.SelectedItem.TryInteract(character, ignoreRequiredItems: true, forceSelectKey: true); }
} }
character.SelectedConstruction = serverPos.SelectedItem; if (localPos.SelectedSecondaryItem != serverPos.SelectedSecondaryItem)
{
if (serverPos.SelectedSecondaryItem == null || serverPos.SelectedSecondaryItem.Removed)
{
character.SelectedSecondaryItem = null;
}
else if (character.SelectedSecondaryItem != serverPos.SelectedSecondaryItem)
{
serverPos.SelectedSecondaryItem.TryInteract(character, ignoreRequiredItems: true, forceSelectKey: true);
character.SelectedSecondaryItem = serverPos.SelectedSecondaryItem;
} }
} }
@@ -496,12 +517,11 @@ namespace Barotrauma
float maxDepth = 0.0f; float maxDepth = 0.0f;
float minDepth = 1.0f; float minDepth = 1.0f;
float depthOffset = 0.0f; float depthOffset = 0.0f;
var ladder = character.SelectedConstruction?.GetComponent<Ladder>();
if (ladder != null) if (character.SelectedSecondaryItem?.GetComponent<Ladder>() is Ladder ladder)
{ {
CalculateLimbDepths(); CalculateLimbDepths();
if (character.WorldPosition.X < character.SelectedConstruction.WorldPosition.X) if (character.WorldPosition.X < character.SelectedSecondaryItem.WorldPosition.X)
{ {
//at the left side of the ladder, needs to be drawn in front of the rungs //at the left side of the ladder, needs to be drawn in front of the rungs
if (maxDepth > ladder.BackgroundSpriteDepth) if (maxDepth > ladder.BackgroundSpriteDepth)
@@ -522,16 +542,21 @@ namespace Barotrauma
else else
{ {
CalculateLimbDepths(); CalculateLimbDepths();
var controller = character.SelectedConstruction?.GetComponent<Controller>(); AdjustDepthOffset(character.SelectedItem);
if (controller != null && controller.ControlCharacterPose && controller.User == character && controller.UserInCorrectPosition) AdjustDepthOffset(character.SelectedSecondaryItem);
void AdjustDepthOffset(Item item)
{ {
if (controller.Item.SpriteDepth <= maxDepth || controller.DrawUserBehind) if (item?.GetComponent<Controller>() is { ControlCharacterPose: true, UserInCorrectPosition: true } controller && controller.User == character)
{ {
depthOffset = Math.Max(controller.Item.GetDrawDepth() + 0.0001f - minDepth, -minDepth); if (controller.Item.SpriteDepth <= maxDepth || controller.DrawUserBehind)
} {
else depthOffset = Math.Max(controller.Item.GetDrawDepth() + 0.0001f - minDepth, -minDepth);
{ }
depthOffset = Math.Max(controller.Item.GetDrawDepth() - 0.0001f - maxDepth, 0.0f); else
{
depthOffset = Math.Max(controller.Item.GetDrawDepth() - 0.0001f - maxDepth, 0.0f);
}
} }
} }
} }
@@ -9,7 +9,6 @@ using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Xml.Linq;
using Barotrauma.Extensions; using Barotrauma.Extensions;
namespace Barotrauma namespace Barotrauma
@@ -323,8 +322,8 @@ namespace Barotrauma
{ {
cam.OffsetAmount = targetOffsetAmount = item.Prefab.OffsetOnSelected * item.OffsetOnSelectedMultiplier; cam.OffsetAmount = targetOffsetAmount = item.Prefab.OffsetOnSelected * item.OffsetOnSelectedMultiplier;
} }
else if (SelectedConstruction != null && ViewTarget == null && else if (SelectedItem != null && ViewTarget == null &&
SelectedConstruction.Components.Any(ic => ic?.GuiFrame != null && ic.ShouldDrawHUD(this))) SelectedItem.Components.Any(ic => ic?.GuiFrame != null && ic.ShouldDrawHUD(this)))
{ {
cam.OffsetAmount = targetOffsetAmount = 0.0f; cam.OffsetAmount = targetOffsetAmount = 0.0f;
cursorPosition = cursorPosition =
@@ -368,21 +367,20 @@ namespace Barotrauma
if (!GUI.InputBlockingMenuOpen) if (!GUI.InputBlockingMenuOpen)
{ {
if (SelectedConstruction != null && if (SelectedItem != null &&
(SelectedConstruction.ActiveHUDs.Any(ic => ic.GuiFrame != null && HUD.CloseHUD(ic.GuiFrame.Rect)) || (SelectedItem.ActiveHUDs.Any(ic => ic.GuiFrame != null && HUD.CloseHUD(ic.GuiFrame.Rect)) ||
((ViewTarget as Item)?.Prefab.FocusOnSelected ?? false) && PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape))) ((ViewTarget as Item)?.Prefab.FocusOnSelected ?? false) && PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape)))
{ {
if (GameMain.Client != null) if (GameMain.Client != null)
{ {
//emulate a Select input to get the character to deselect the item server-side //emulate a Deselect input to get the character to deselect the item server-side
//keys[(int)InputType.Select].Hit = true; EmulateInput(InputType.Deselect);
keys[(int)InputType.Deselect].Hit = true;
} }
//reset focus to prevent us from accidentally interacting with another entity //reset focus to prevent us from accidentally interacting with another entity
focusedItem = null; focusedItem = null;
FocusedCharacter = null; FocusedCharacter = null;
findFocusedTimer = 0.2f; findFocusedTimer = 0.2f;
SelectedConstruction = null; SelectedItem = null;
} }
} }
@@ -426,6 +424,11 @@ namespace Barotrauma
} }
} }
public void EmulateInput(InputType input)
{
keys[(int)input].Hit = true;
}
partial void OnAttackedProjSpecific(Character attacker, AttackResult attackResult, float stun) partial void OnAttackedProjSpecific(Character attacker, AttackResult attackResult, float stun)
{ {
if (IsDead) { return; } if (IsDead) { return; }
@@ -518,7 +521,7 @@ namespace Barotrauma
//reduce the amount of aim assist if an item has been selected //reduce the amount of aim assist if an item has been selected
//= can't switch selection to another item without deselecting the current one first UNLESS the cursor is directly on the item //= can't switch selection to another item without deselecting the current one first UNLESS the cursor is directly on the item
//otherwise it would be too easy to accidentally switch the selected item when rewiring items //otherwise it would be too easy to accidentally switch the selected item when rewiring items
float aimAssistAmount = SelectedConstruction == null ? 100.0f * aimAssistModifier : 1.0f; float aimAssistAmount = SelectedItem == null ? 100.0f * aimAssistModifier : 1.0f;
Vector2 displayPosition = ConvertUnits.ToDisplayUnits(simPosition); Vector2 displayPosition = ConvertUnits.ToDisplayUnits(simPosition);
@@ -623,12 +626,12 @@ namespace Barotrauma
{ {
if (this != controlled) { return false; } if (this != controlled) { return false; }
if (GameMain.GameSession?.Campaign != null && GameMain.GameSession.Campaign.ShowCampaignUI) { return true; } if (GameMain.GameSession?.Campaign != null && GameMain.GameSession.Campaign.ShowCampaignUI) { return true; }
var controller = SelectedConstruction?.GetComponent<Controller>(); var controller = SelectedItem?.GetComponent<Controller>();
//lock if using a controller, except if we're also using a connection panel in the same item //lock if using a controller, except if we're also using a connection panel in the same item
return return
SelectedConstruction != null && SelectedItem != null &&
controller?.User == this && controller.HideHUD && controller?.User == this && controller.HideHUD &&
SelectedConstruction?.GetComponent<ConnectionPanel>()?.User != this; SelectedItem?.GetComponent<ConnectionPanel>()?.User != this;
} }
@@ -900,7 +903,14 @@ namespace Barotrauma
if (info != null) if (info != null)
{ {
LocalizedString name = Info.DisplayName; LocalizedString name = Info.DisplayName;
if (controlled == null && name != Info.Name) { name += " " + TextManager.Get("Disguised"); } if (controlled == null && name != Info.Name)
{
name += " " + TextManager.Get("Disguised");
}
else if (Info.Title != null)
{
name += '\n' + Info.Title;
}
Vector2 nameSize = GUIStyle.Font.MeasureString(name); Vector2 nameSize = GUIStyle.Font.MeasureString(name);
Vector2 namePos = new Vector2(pos.X, pos.Y - 10.0f - (5.0f / cam.Zoom)) - nameSize * 0.5f / cam.Zoom; Vector2 namePos = new Vector2(pos.X, pos.Y - 10.0f - (5.0f / cam.Zoom)) - nameSize * 0.5f / cam.Zoom;
@@ -108,7 +108,7 @@ namespace Barotrauma
private static bool ShouldDrawInventory(Character character) private static bool ShouldDrawInventory(Character character)
{ {
var controller = character.SelectedConstruction?.GetComponent<Controller>(); var controller = character.SelectedItem?.GetComponent<Controller>();
return return
character?.Inventory != null && character?.Inventory != null &&
@@ -417,13 +417,17 @@ namespace Barotrauma
visibleRange = new Range<float>(-100f, 500f); visibleRange = new Range<float>(-100f, 500f);
} }
float dist = Vector2.Distance(character.WorldPosition, npc.WorldPosition);
float distFactor = 1.0f - MathUtils.InverseLerp(1000.0f, 3000.0f, dist);
float alpha = MathHelper.Lerp(0.3f, 1.0f, distFactor);
GUI.DrawIndicator( GUI.DrawIndicator(
spriteBatch, spriteBatch,
npc.WorldPosition, npc.WorldPosition,
cam, cam,
visibleRange, visibleRange,
iconStyle.GetDefaultSprite(), iconStyle.GetDefaultSprite(),
iconStyle.Color); iconStyle.Color * alpha,
label: npc.Info?.Title);
} }
foreach (Item item in Item.ItemList) foreach (Item item in Item.ItemList)
@@ -436,10 +440,10 @@ namespace Barotrauma
} }
} }
if (character.SelectedConstruction != null && if (character.SelectedItem != null &&
(character.CanInteractWith(character.SelectedConstruction) || Screen.Selected == GameMain.SubEditorScreen)) (character.CanInteractWith(character.SelectedItem) || Screen.Selected == GameMain.SubEditorScreen))
{ {
character.SelectedConstruction.DrawHUD(spriteBatch, cam, character); character.SelectedItem.DrawHUD(spriteBatch, cam, character);
} }
if (character.Inventory != null) if (character.Inventory != null)
{ {
@@ -561,9 +565,15 @@ namespace Barotrauma
Color nameColor = character.FocusedCharacter.GetNameColor(); Color nameColor = character.FocusedCharacter.GetNameColor();
GUI.DrawString(spriteBatch, textPos, focusName, nameColor, Color.Black * 0.7f, 2, GUIStyle.SubHeadingFont, ForceUpperCase.No); GUI.DrawString(spriteBatch, textPos, focusName, nameColor, Color.Black * 0.7f, 2, GUIStyle.SubHeadingFont, ForceUpperCase.No);
textPos.X += 10.0f * GUI.Scale;
textPos.Y += GUIStyle.SubHeadingFont.MeasureString(focusName).Y; textPos.Y += GUIStyle.SubHeadingFont.MeasureString(focusName).Y;
if (character.FocusedCharacter.Info?.Title != null && !character.FocusedCharacter.Info.Title.IsNullOrEmpty())
{
GUI.DrawString(spriteBatch, textPos, character.FocusedCharacter.Info.Title, nameColor, Color.Black * 0.7f, 2, GUIStyle.SubHeadingFont, ForceUpperCase.No);
textPos.Y += GUIStyle.SubHeadingFont.MeasureString(character.FocusedCharacter.Info.Title.Value).Y;
}
textPos.X += 10.0f * GUI.Scale;
if (!character.FocusedCharacter.IsIncapacitated && character.FocusedCharacter.IsPet) if (!character.FocusedCharacter.IsIncapacitated && character.FocusedCharacter.IsPet)
{ {
GUI.DrawString(spriteBatch, textPos, GetCachedHudText("PlayHint", InputType.Use), GUI.DrawString(spriteBatch, textPos, GetCachedHudText("PlayHint", InputType.Use),
@@ -102,7 +102,7 @@ namespace Barotrauma
if (PersonalityTrait != null) if (PersonalityTrait != null)
{ {
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), headerTextArea.RectTransform), new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), headerTextArea.RectTransform),
TextManager.AddPunctuation(':', TextManager.Get("PersonalityTrait"), TextManager.Get("personalitytrait." + PersonalityTrait.Name.Replace(" ".ToIdentifier(), "".ToIdentifier()))), TextManager.AddPunctuation(':', TextManager.Get("PersonalityTrait"), PersonalityTrait.DisplayName),
font: font) font: font)
{ {
Padding = Vector4.Zero Padding = Vector4.Zero
@@ -523,20 +523,18 @@ namespace Barotrauma
Color facialHairColor = inc.ReadColorR8G8B8(); Color facialHairColor = inc.ReadColorR8G8B8();
string ragdollFile = inc.ReadString(); string ragdollFile = inc.ReadString();
string jobIdentifier = inc.ReadString(); uint jobIdentifier = inc.ReadUInt32();
int variant = inc.ReadByte(); int variant = inc.ReadByte();
JobPrefab jobPrefab = null; JobPrefab jobPrefab = null;
Dictionary<Identifier, float> skillLevels = new Dictionary<Identifier, float>(); Dictionary<Identifier, float> skillLevels = new Dictionary<Identifier, float>();
if (!string.IsNullOrEmpty(jobIdentifier)) if (jobIdentifier > 0)
{ {
jobPrefab = JobPrefab.Get(jobIdentifier); jobPrefab = JobPrefab.Prefabs.Find(jp => jp.UintIdentifier == jobIdentifier);
byte skillCount = inc.ReadByte(); foreach (SkillPrefab skillPrefab in jobPrefab.Skills.OrderBy(s => s.Identifier))
for (int i = 0; i < skillCount; i++)
{ {
Identifier skillIdentifier = inc.ReadIdentifier();
float skillLevel = inc.ReadSingle(); float skillLevel = inc.ReadSingle();
skillLevels.Add(skillIdentifier, skillLevel); skillLevels.Add(skillPrefab.Identifier, skillLevel);
} }
} }
@@ -777,7 +775,21 @@ namespace Barotrauma
createColorSelector($"Customization.{nameof(info.Head.SkinColor)}".ToIdentifier(), info.SkinColors, () => info.Head.SkinColor, createColorSelector($"Customization.{nameof(info.Head.SkinColor)}".ToIdentifier(), info.SkinColors, () => info.Head.SkinColor,
(color) => info.Head.SkinColor = color); (color) => info.Head.SkinColor = color);
#if DEBUG
new GUIButton(new RectTransform(Vector2.One * 0.12f,
parentComponent.RectTransform,
anchor: Anchor.BottomRight, scaleBasis: ScaleBasis.Smallest)
{ RelativeOffset = new Vector2(0.01f, 0.005f) }, style: "SaveButton", color: Color.Magenta)
{
ToolTip = "DEBUG ONLY: copy the character info XML to clipboard",
OnClicked = (button, o) =>
{
XElement element = info.Save(null);
Clipboard.SetText(element.ToString());
return false;
}
};
#endif
RandomizeButton = new GUIButton(new RectTransform(Vector2.One * 0.12f, RandomizeButton = new GUIButton(new RectTransform(Vector2.One * 0.12f,
parentComponent.RectTransform, parentComponent.RectTransform,
anchor: Anchor.BottomRight, scaleBasis: ScaleBasis.Smallest) anchor: Anchor.BottomRight, scaleBasis: ScaleBasis.Smallest)
@@ -1,5 +1,4 @@
using Barotrauma.Extensions; using Barotrauma.Items.Components;
using Barotrauma.Items.Components;
using Barotrauma.Networking; using Barotrauma.Networking;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
@@ -44,7 +43,8 @@ namespace Barotrauma
LastNetworkUpdateID, LastNetworkUpdateID,
AnimController.TargetDir, AnimController.TargetDir,
SelectedCharacter, SelectedCharacter,
SelectedConstruction, SelectedItem,
SelectedSecondaryItem,
AnimController.Anim); AnimController.Anim);
memLocalState.Add(posInfo); memLocalState.Add(posInfo);
@@ -219,15 +219,17 @@ namespace Barotrauma
bool entitySelected = msg.ReadBoolean(); bool entitySelected = msg.ReadBoolean();
Character selectedCharacter = null; Character selectedCharacter = null;
Item selectedItem = null; Item selectedItem = null, selectedSecondaryItem = null;
AnimController.Animation animation = AnimController.Animation.None; AnimController.Animation animation = AnimController.Animation.None;
if (entitySelected) if (entitySelected)
{ {
ushort characterID = msg.ReadUInt16(); ushort characterID = msg.ReadUInt16();
ushort itemID = msg.ReadUInt16(); ushort itemID = msg.ReadUInt16();
ushort secondaryItemID = msg.ReadUInt16();
selectedCharacter = FindEntityByID(characterID) as Character; selectedCharacter = FindEntityByID(characterID) as Character;
selectedItem = FindEntityByID(itemID) as Item; selectedItem = FindEntityByID(itemID) as Item;
selectedSecondaryItem = FindEntityByID(secondaryItemID) as Item;
if (characterID != NullEntityID) if (characterID != NullEntityID)
{ {
bool doingCpr = msg.ReadBoolean(); bool doingCpr = msg.ReadBoolean();
@@ -274,7 +276,7 @@ namespace Barotrauma
pos, rotation, pos, rotation,
networkUpdateID, networkUpdateID,
facingRight ? Direction.Right : Direction.Left, facingRight ? Direction.Right : Direction.Left,
selectedCharacter, selectedItem, animation); selectedCharacter, selectedItem, selectedSecondaryItem, animation);
while (index < memState.Count && NetIdUtils.IdMoreRecent(posInfo.ID, memState[index].ID)) while (index < memState.Count && NetIdUtils.IdMoreRecent(posInfo.ID, memState[index].ID))
index++; index++;
@@ -286,7 +288,7 @@ namespace Barotrauma
pos, rotation, pos, rotation,
linearVelocity, angularVelocity, linearVelocity, angularVelocity,
sendingTime, facingRight ? Direction.Right : Direction.Left, sendingTime, facingRight ? Direction.Right : Direction.Left,
selectedCharacter, selectedItem, animation); selectedCharacter, selectedItem, selectedSecondaryItem, animation);
while (index < memState.Count && posInfo.Timestamp > memState[index].Timestamp) while (index < memState.Count && posInfo.Timestamp > memState[index].Timestamp)
index++; index++;
@@ -375,9 +377,15 @@ namespace Barotrauma
if (attackLimbIndex == 255 || Removed) { break; } if (attackLimbIndex == 255 || Removed) { break; }
if (attackLimbIndex >= AnimController.Limbs.Length) if (attackLimbIndex >= AnimController.Limbs.Length)
{ {
string errorMsg = $"Received invalid {(eventType == EventType.SetAttackTarget ? "SetAttackTarget" : "ExecuteAttack")} message. Limb index out of bounds (character: {Name}, limb index: {attackLimbIndex}, limb count: {AnimController.Limbs.Length})"; //it's possible to get these errors when mid-round syncing, as the client may not
DebugConsole.ThrowError(errorMsg); //yet know about afflictions that have given the character extra limbs (e.g. spineling genes)
GameAnalyticsManager.AddErrorEventOnce("Character.ClientEventRead:AttackLimbOutOfBounds", GameAnalyticsManager.ErrorSeverity.Error, errorMsg); //ignoring the error should be safe though, not executing the attack should not cause any further issues
if (!GameMain.Client.MidRoundSyncing)
{
string errorMsg = $"Received invalid {(eventType == EventType.SetAttackTarget ? "SetAttackTarget" : "ExecuteAttack")} message. Limb index out of bounds (character: {Name}, limb index: {attackLimbIndex}, limb count: {AnimController.Limbs.Length})";
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("Character.ClientEventRead:AttackLimbOutOfBounds", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
}
break; break;
} }
Limb attackLimb = AnimController.Limbs[attackLimbIndex]; Limb attackLimb = AnimController.Limbs[attackLimbIndex];
@@ -673,16 +681,17 @@ namespace Barotrauma
AfflictionPrefab causeOfDeathAffliction = null; AfflictionPrefab causeOfDeathAffliction = null;
if (causeOfDeathType == CauseOfDeathType.Affliction) if (causeOfDeathType == CauseOfDeathType.Affliction)
{ {
string afflictionName = msg.ReadString(); uint afflictionId = msg.ReadUInt32();
if (!AfflictionPrefab.Prefabs.ContainsKey(afflictionName)) AfflictionPrefab afflictionPrefab = AfflictionPrefab.Prefabs.Find(p => p.UintIdentifier == afflictionId);
if (afflictionPrefab == null)
{ {
string errorMsg = $"Error in CharacterNetworking.ReadStatus: affliction not found ({afflictionName})"; string errorMsg = $"Error in CharacterNetworking.ReadStatus: affliction not found (id {afflictionId})";
causeOfDeathType = CauseOfDeathType.Unknown; causeOfDeathType = CauseOfDeathType.Unknown;
GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ReadStatus:AfflictionIndexOutOfBounts", GameAnalyticsManager.ErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ReadStatus:AfflictionNotFound", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
} }
else else
{ {
causeOfDeathAffliction = AfflictionPrefab.Prefabs[afflictionName]; causeOfDeathAffliction = afflictionPrefab;
} }
} }
bool containsAfflictionData = msg.ReadBoolean(); bool containsAfflictionData = msg.ReadBoolean();
@@ -162,11 +162,7 @@ namespace Barotrauma
openHealthWindow.characterName.Text = value.Character.Info.DisplayName; openHealthWindow.characterName.Text = value.Character.Info.DisplayName;
value.Character.Info.CheckDisguiseStatus(false); value.Character.Info.CheckDisguiseStatus(false);
} }
Character.Controlled.SelectedItem = null;
if (Character.Controlled.SelectedConstruction != null && Character.Controlled.SelectedConstruction.GetComponent<Ladder>() == null)
{
Character.Controlled.SelectedConstruction = null;
}
} }
HintManager.OnShowHealthInterface(); HintManager.OnShowHealthInterface();
@@ -724,7 +720,7 @@ namespace Barotrauma
//emulate a Health input to get the character to deselect the item server-side //emulate a Health input to get the character to deselect the item server-side
if (GameMain.Client != null) if (GameMain.Client != null)
{ {
Character.Controlled.Keys[(int)InputType.Health].Hit = true; Character.Controlled.EmulateInput(InputType.Health);
} }
OpenHealthWindow = null; OpenHealthWindow = null;
} }
@@ -2014,7 +2010,7 @@ namespace Barotrauma
FaceTint = DefaultFaceTint; FaceTint = DefaultFaceTint;
BodyTint = Color.TransparentBlack; BodyTint = Color.TransparentBlack;
if (!(Character?.Params?.Health.ApplyAfflictionColors ?? false)) { return; } if (!Character.Params.Health.ApplyAfflictionColors) { return; }
foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictions) foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictions)
{ {
@@ -2031,15 +2027,21 @@ namespace Barotrauma
foreach (Limb limb in Character.AnimController.Limbs) foreach (Limb limb in Character.AnimController.Limbs)
{ {
if (limb.HealthIndex < 0 || limb.HealthIndex >= limbHealths.Count) { continue; } if (limb.HealthIndex < 0 || limb.HealthIndex >= limbHealths.Count) { continue; }
limb.BurnOverlayStrength = 0.0f; limb.BurnOverlayStrength = 0.0f;
limb.DamageOverlayStrength = 0.0f; limb.DamageOverlayStrength = 0.0f;
foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictions) foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictions)
{ {
if (kvp.Value != limbHealths[limb.HealthIndex]) { continue; }
var affliction = kvp.Key; var affliction = kvp.Key;
limb.BurnOverlayStrength += affliction.Strength / Math.Min(affliction.Prefab.MaxStrength, 100) * affliction.Prefab.BurnOverlayAlpha; float burnStrength = affliction.Strength / Math.Min(affliction.Prefab.MaxStrength, 100) * affliction.Prefab.BurnOverlayAlpha;
limb.DamageOverlayStrength += affliction.Strength / Math.Min(affliction.Prefab.MaxStrength, 100) * affliction.Prefab.DamageOverlayAlpha; if (kvp.Value == limbHealths[limb.HealthIndex])
{
limb.BurnOverlayStrength += burnStrength;
limb.DamageOverlayStrength += affliction.Strength / Math.Min(affliction.Prefab.MaxStrength, 100) * affliction.Prefab.DamageOverlayAlpha;
}
else
{
limb.BurnOverlayStrength += burnStrength / 2;
}
} }
} }
} }
@@ -481,7 +481,7 @@ namespace Barotrauma
{ {
ContentPath texturePath = ContentPath texturePath =
character.Params.VariantFile?.Root?.GetAttributeContentPath("texture", character.Prefab.ContentPackage) character.Params.VariantFile?.Root?.GetAttributeContentPath("texture", character.Prefab.ContentPackage)
?? ContentPath.FromRaw(character.Prefab.ContentPackage, spriteParams.GetTexturePath()); ?? ContentPath.FromRaw(spriteParams.Element.ContentPackage ?? character.Prefab.ContentPackage, spriteParams.GetTexturePath());
path = GetSpritePath(texturePath); path = GetSpritePath(texturePath);
} }
else else
@@ -1,21 +1,18 @@
using Barotrauma.Items.Components; using Barotrauma.ClientSource.Settings;
using Barotrauma.Extensions;
using Barotrauma.IO;
using Barotrauma.Items.Components;
using Barotrauma.MapCreatures.Behavior;
using Barotrauma.Networking; using Barotrauma.Networking;
using Barotrauma.Steam;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Input;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Barotrauma.IO; using System.Globalization;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Xml.Linq; using System.Xml.Linq;
using System.Globalization;
using FarseerPhysics;
using Barotrauma.Extensions;
using Barotrauma.Steam;
using System.Threading.Tasks;
using Barotrauma.ClientSource.Settings;
using Barotrauma.MapCreatures.Behavior;
using static Barotrauma.FabricationRecipe; using static Barotrauma.FabricationRecipe;
namespace Barotrauma namespace Barotrauma
@@ -1299,7 +1296,7 @@ namespace Barotrauma
int? fabricationCost = null; int? fabricationCost = null;
int? deconstructProductCost = null; int? deconstructProductCost = null;
var fabricationRecipe = fabricableItems.Find(f => f.TargetItem == itemPrefab); var fabricationRecipe = fabricableItems.Find(f => f.TargetItem == itemPrefab && f.RequiredItems.Any());
if (fabricationRecipe != null) if (fabricationRecipe != null)
{ {
foreach (var ingredient in fabricationRecipe.RequiredItems) foreach (var ingredient in fabricationRecipe.RequiredItems)
@@ -1334,6 +1331,21 @@ namespace Barotrauma
if (fabricationRecipe != null) if (fabricationRecipe != null)
{ {
var ingredient = fabricationRecipe.RequiredItems.Find(r => r.ItemPrefabs.Contains(targetItem)); var ingredient = fabricationRecipe.RequiredItems.Find(r => r.ItemPrefabs.Contains(targetItem));
if (ingredient == null)
{
foreach (var requiredItem in fabricationRecipe.RequiredItems)
{
foreach (var itemPrefab2 in requiredItem.ItemPrefabs)
{
foreach (var recipe in itemPrefab2.FabricationRecipes.Values)
{
ingredient ??= recipe.RequiredItems.Find(r => r.ItemPrefabs.Contains(targetItem));
}
}
}
}
if (ingredient == null) if (ingredient == null)
{ {
NewMessage("Deconstructing \"" + itemPrefab.Name + "\" produces \"" + deconstructItem.ItemIdentifier + "\", which isn't required in the fabrication recipe of the item.", Color.Red); NewMessage("Deconstructing \"" + itemPrefab.Name + "\" produces \"" + deconstructItem.ItemIdentifier + "\", which isn't required in the fabrication recipe of the item.", Color.Red);
@@ -2077,7 +2089,17 @@ namespace Barotrauma
var prefab = MapEntityPrefab.Find(null, args[0]); var prefab = MapEntityPrefab.Find(null, args[0]);
if (prefab != null) if (prefab != null)
{ {
DebugConsole.NewMessage(prefab.Name + " " + prefab.Identifier + " " + prefab.GetType().ToString()); NewMessage(prefab.Name + " " + prefab.Identifier + " " + prefab.GetType().ToString());
}
}));
commands.Add(new Command("copycharacterinfotoclipboard", "", (string[] args) =>
{
if (Character.Controlled?.Info != null)
{
XElement element = Character.Controlled?.Info.Save(null);
Clipboard.SetText(element.ToString());
DebugConsole.NewMessage($"Copied the characterinfo of {Character.Controlled.Name} to clipboard.");
} }
})); }));
@@ -2507,7 +2529,7 @@ namespace Barotrauma
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) =>
{ {
if (args.Length != 1) return; if (args.Length != 1) { return; }
TextManager.CheckForDuplicates(args[0].ToIdentifier().ToLanguageIdentifier()); TextManager.CheckForDuplicates(args[0].ToIdentifier().ToLanguageIdentifier());
})); }));
@@ -2517,9 +2539,20 @@ namespace Barotrauma
NPCConversation.WriteToCSV(); NPCConversation.WriteToCSV();
})); }));
commands.Add(new Command("csvtoxml", "csvtoxml [language] -> Converts .csv localization files in Content/NPCConversations & Content/Texts to .xml for use in-game.", (string[] args) => commands.Add(new Command("csvtoxml", "csvtoxml -> Converts .csv localization files Content/Texts/Texts.csv and Content/Texts/NPCConversations.csv to .xml for use in-game.", (string[] args) =>
{ {
LocalizationCSVtoXML.Convert(); ShowQuestionPrompt("Do you want to save the text files to the project folder (../../../BarotraumaShared/Content/Texts/)? If not, they are saved in the current working directory. Y/N",
(option1) =>
{
ShowQuestionPrompt("Do you want to convert the NPC conversations as well? Y/N",
(option2) =>
{
LocalizationCSVtoXML.ConvertMasterLocalizationKit(
option1.ToLowerInvariant() == "y" ? "../../../BarotraumaShared/Content/Texts/" : "Content/Texts",
option1.ToLowerInvariant() == "y" ? "../../../BarotraumaShared/Content/NPCConversations/" : "Content/NPCConversations",
convertConversations: option2.ToLowerInvariant() == "y");
});
});
})); }));
commands.Add(new Command("printproperties", "Goes through the currently collected property list for missing localizations and writes them to a file.", (string[] args) => commands.Add(new Command("printproperties", "Goes through the currently collected property list for missing localizations and writes them to a file.", (string[] args) =>
@@ -32,12 +32,12 @@ namespace Barotrauma
private static bool shouldFadeToBlack; private static bool shouldFadeToBlack;
private bool IsBlockedByAnotherConversation(IEnumerable<Entity> _) private bool IsBlockedByAnotherConversation(IEnumerable<Entity> _, float duration)
{ {
return return
lastActiveAction != null && lastActiveAction != null &&
lastActiveAction.ParentEvent != ParentEvent && lastActiveAction.ParentEvent != ParentEvent &&
Timing.TotalTime < lastActiveAction.lastActiveTime + BlockOtherConversationsDuration; Timing.TotalTime < lastActiveAction.lastActiveTime + duration;
} }
partial void ShowDialog(Character speaker, Character targetCharacter) partial void ShowDialog(Character speaker, Character targetCharacter)
@@ -528,7 +528,7 @@ namespace Barotrauma
if (characterInfo.PersonalityTrait is NPCPersonalityTrait trait) if (characterInfo.PersonalityTrait is NPCPersonalityTrait trait)
{ {
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("PersonalityTrait")); new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("PersonalityTrait"));
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), TextManager.Get("personalitytrait." + trait.Name.Replace(" ".ToIdentifier(), Identifier.Empty))); new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), trait.DisplayName);
} }
infoLabelGroup.Recalculate(); infoLabelGroup.Recalculate();
infoValueGroup.Recalculate(); infoValueGroup.Recalculate();
@@ -958,7 +958,7 @@ namespace Barotrauma
// Wire cursors // Wire cursors
if (Character.Controlled != null) if (Character.Controlled != null)
{ {
if (Character.Controlled.SelectedConstruction?.GetComponent<ConnectionPanel>() != null) if (Character.Controlled.SelectedItem?.GetComponent<ConnectionPanel>() != null)
{ {
if (Connection.DraggingConnected != null) if (Connection.DraggingConnected != null)
{ {
@@ -1344,7 +1344,7 @@ namespace Barotrauma
/// <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>
/// <param name="overrideAlpha">Override the distance-based alpha value with the specified alpha value</param> /// <param name="overrideAlpha">Override the distance-based alpha value with the specified alpha value</param>
public static void DrawIndicator(SpriteBatch spriteBatch, in Vector2 worldPosition, Camera cam, in Range<float> visibleRange, Sprite sprite, in Color color, public static void DrawIndicator(SpriteBatch spriteBatch, in Vector2 worldPosition, Camera cam, in Range<float> visibleRange, Sprite sprite, in Color color,
bool createOffset = true, float scaleMultiplier = 1.0f, float? overrideAlpha = null) bool createOffset = true, float scaleMultiplier = 1.0f, float? overrideAlpha = null, LocalizedString label = null)
{ {
Vector2 diff = worldPosition - cam.WorldViewCenter; Vector2 diff = worldPosition - cam.WorldViewCenter;
float dist = diff.Length(); float dist = diff.Length();
@@ -1394,10 +1394,6 @@ namespace Barotrauma
angle = MathHelper.Lerp(originalAngle, angle, MathHelper.Clamp(((screenDist + 10f) - iconDiff.Length()) / 10f, 0f, 1f)); angle = MathHelper.Lerp(originalAngle, angle, MathHelper.Clamp(((screenDist + 10f) - iconDiff.Length()) / 10f, 0f, 1f));
/*Vector2 unclampedDiff = new Vector2(
(float)Math.Cos(angle) * screenDist,
(float)-Math.Sin(angle) * screenDist);*/
iconDiff = new Vector2( iconDiff = new Vector2(
(float)Math.Cos(angle) * Math.Min(GameMain.GraphicsWidth * 0.4f, screenDist), (float)Math.Cos(angle) * Math.Min(GameMain.GraphicsWidth * 0.4f, screenDist),
(float)-Math.Sin(angle) * Math.Min(GameMain.GraphicsHeight * 0.4f, screenDist)); (float)-Math.Sin(angle) * Math.Min(GameMain.GraphicsHeight * 0.4f, screenDist));
@@ -1405,7 +1401,20 @@ namespace Barotrauma
Vector2 iconPos = cam.WorldToScreen(cam.WorldViewCenter) + iconDiff; Vector2 iconPos = cam.WorldToScreen(cam.WorldViewCenter) + iconDiff;
sprite.Draw(spriteBatch, iconPos, color * alpha, rotate: 0.0f, scale: symbolScale); sprite.Draw(spriteBatch, iconPos, color * alpha, rotate: 0.0f, scale: symbolScale);
if (/*unclampedDiff.Length()*/ screenDist - 10 > iconDiff.Length()) if (label != null)
{
float cursorDist = Vector2.Distance(PlayerInput.MousePosition, iconPos);
if (cursorDist < sprite.size.X * symbolScale)
{
Vector2 textSize = GUIStyle.Font.MeasureString(label);
Vector2 textPos = iconPos + new Vector2(sprite.size.X * symbolScale * 0.7f * Math.Sign(-iconDiff.X), -textSize.Y / 2);
if (iconDiff.X > 0) { textPos.X -= textSize.X; }
DrawString(spriteBatch, textPos + Vector2.One, label, Color.Black);
DrawString(spriteBatch, textPos, label, color);
}
}
if (screenDist - 10 > iconDiff.Length())
{ {
Vector2 normalizedDiff = Vector2.Normalize(targetScreenPos - iconPos); Vector2 normalizedDiff = Vector2.Normalize(targetScreenPos - iconPos);
Vector2 arrowOffset = normalizedDiff * sprite.size.X * symbolScale * 0.7f; Vector2 arrowOffset = normalizedDiff * sprite.size.X * symbolScale * 0.7f;
@@ -1465,9 +1474,9 @@ namespace Barotrauma
depth); depth);
} }
public static void DrawString(SpriteBatch sb, Vector2 pos, LocalizedString text, Color color, Color? backgroundColor = null, int backgroundPadding = 0, GUIFont font = null) public static void DrawString(SpriteBatch sb, Vector2 pos, LocalizedString text, Color color, Color? backgroundColor = null, int backgroundPadding = 0, GUIFont font = null, ForceUpperCase forceUpperCase = ForceUpperCase.Inherit)
{ {
DrawString(sb, pos, text.Value, color, backgroundColor, backgroundPadding, font); DrawString(sb, pos, text.Value, color, backgroundColor, backgroundPadding, font, forceUpperCase);
} }
public static void DrawString(SpriteBatch sb, Vector2 pos, string text, Color color, Color? backgroundColor = null, int backgroundPadding = 0, GUIFont font = null, ForceUpperCase forceUpperCase = ForceUpperCase.Inherit) public static void DrawString(SpriteBatch sb, Vector2 pos, string text, Color color, Color? backgroundColor = null, int backgroundPadding = 0, GUIFont font = null, ForceUpperCase forceUpperCase = ForceUpperCase.Inherit)
@@ -62,7 +62,7 @@ namespace Barotrauma
GUIFont headerFont = GUIStyle.SubHeadingFont; GUIFont headerFont = GUIStyle.SubHeadingFont;
GUIFont font = GUIStyle.SmallFont; // font the context menu options use GUIFont font = GUIStyle.SmallFont; // font the context menu options use
Vector4 padding = new Vector4(4), headerPadding = new Vector4(8); Vector4 padding = new Vector4(4), headerPadding = new Vector4(8);
int horizontalPadding = (int) (padding.X + padding.Z), verticalPadding = (int) (padding.Y + padding.W); int horizontalPadding = (int)(padding.X + padding.Z), verticalPadding = (int)(padding.Y + padding.W);
bool hasHeader = !header.IsNullOrWhiteSpace(); bool hasHeader = !header.IsNullOrWhiteSpace();
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@@ -131,9 +131,15 @@ namespace Barotrauma
optionElement.ToolTip = option.Tooltip; optionElement.ToolTip = option.Tooltip;
} }
if (!option.IsEnabled) //option doesn't do anything, make it a label
if (option.OnSelected == null)
{ {
optionElement.TextColor *= 0.5f; optionElement.TextAlignment = Alignment.BottomLeft;
optionElement.TextColor = optionElement.DisabledTextColor = GUIStyle.Green;
}
else if (!option.IsEnabled)
{
optionElement.TextColor *= 0.5f;
} }
} }
@@ -146,7 +152,10 @@ namespace Barotrauma
// Resize all children to the size of their text // Resize all children to the size of their text
foreach (GUITextBlock block in children.Where(c => c is GUITextBlock).Cast<GUITextBlock>()) 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)); bool isLabel = block.UserData is ContextMenuOption option && option.OnSelected == null;
block.RectTransform.NonScaledSize = new Point(
(int)(block.TextSize.X + (block.Padding.X + block.Padding.Z)),
(int)Math.Max(block.TextSize.Y * 1.2f, 18 * GUI.Scale));
} }
int largestWidth = children.Max(c => c.Rect.Width + horizontalPadding); int largestWidth = children.Max(c => c.Rect.Width + horizontalPadding);
@@ -155,7 +164,7 @@ namespace Barotrauma
if (HeaderLabel != null) if (HeaderLabel != null)
{ {
RectTransform headerTransform = HeaderLabel.RectTransform; RectTransform headerTransform = HeaderLabel.RectTransform;
headerTransform.MinSize = new Point((int) (HeaderLabel.TextSize.X + (headerPadding.X + headerPadding.Z)), headerTransform.NonScaledSize.Y); headerTransform.MinSize = new Point((int)(HeaderLabel.TextSize.X + (headerPadding.X + headerPadding.Z)), headerTransform.NonScaledSize.Y);
if (largestWidth < headerTransform.MinSize.X) if (largestWidth < headerTransform.MinSize.X)
{ {
largestWidth = headerTransform.MinSize.X; largestWidth = headerTransform.MinSize.X;
@@ -171,7 +180,7 @@ namespace Barotrauma
// the cropped size of the option list // the cropped size of the option list
Point newSize = new Point(largestWidth, children.Sum(c => c.Rect.Height) + verticalPadding); 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 // 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)); RectTransform.NonScaledSize = new Point(newSize.X, (int)(newSize.Y / optionList.RectTransform.RelativeSize.Y));
optionList.RectTransform.NonScaledSize = newSize; optionList.RectTransform.NonScaledSize = newSize;
// move the context menu if it would go outside of screen // move the context menu if it would go outside of screen
@@ -227,8 +236,8 @@ namespace Barotrauma
private Vector2 InflateSize(ref Point size, LocalizedString label, ScalableFont font) private Vector2 InflateSize(ref Point size, LocalizedString label, ScalableFont font)
{ {
Vector2 textSize = font.MeasureString(label); Vector2 textSize = font.MeasureString(label);
size.X = Math.Max((int) Math.Ceiling(textSize.X), size.X); size.X = Math.Max((int)Math.Ceiling(textSize.X), size.X);
size.Y += (int) Math.Ceiling(textSize.Y); size.Y += (int)Math.Ceiling(textSize.Y);
return textSize; return textSize;
} }
@@ -0,0 +1,75 @@
using Microsoft.Xna.Framework;
using System;
namespace Barotrauma
{
public class GUIDragHandle : GUIComponent
{
private readonly RectTransform elementToMove;
private Vector2 dragStart;
private bool dragStarted;
public Rectangle DragArea;
public GUIDragHandle(RectTransform rectT, RectTransform elementToMove, string style = "GUIDragIndicator")
: base(style, rectT)
{
this.elementToMove = elementToMove;
DragArea = new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight);
}
protected override void Update(float deltaTime)
{
if (!Visible) return;
base.Update(deltaTime);
Enabled = true;
if (dragStarted)
{
Point moveAmount = (PlayerInput.MousePosition - dragStart).ToPoint() - elementToMove.ScreenSpaceOffset;
Rectangle rect = elementToMove.Rect;
rect.Location += moveAmount;
moveAmount.X += Math.Max(DragArea.X - rect.X, 0);
moveAmount.X -= Math.Max(rect.Right - DragArea.Right, 0);
moveAmount.Y += Math.Max(DragArea.Y - rect.Y, 0);
moveAmount.Y -= Math.Max(rect.Bottom - DragArea.Bottom, 0);
if (moveAmount != Point.Zero)
{
elementToMove.ScreenSpaceOffset += moveAmount;
}
if (!PlayerInput.PrimaryMouseButtonHeld())
{
dragStarted = false;
}
}
else if (Rect.Contains(PlayerInput.MousePosition) && CanBeFocused && Enabled && GUI.IsMouseOn(this))
{
State = Selected ? ComponentState.HoverSelected : ComponentState.Hover;
if (PlayerInput.PrimaryMouseButtonDown())
{
dragStart = PlayerInput.MousePosition - elementToMove.ScreenSpaceOffset.ToVector2();
dragStarted = true;
}
}
else
{
if (!ExternalHighlight)
{
State = Selected ? ComponentState.Selected : ComponentState.None;
}
else
{
State = ComponentState.Hover;
}
}
foreach (GUIComponent child in Children)
{
child.State = State;
}
}
}
}
@@ -49,7 +49,7 @@ namespace Barotrauma
//scaling the bar linearly with the resolution tends to make them too large on large resolutions //scaling the bar linearly with the resolution tends to make them too large on large resolutions
float desiredSize = 25.0f; float desiredSize = 25.0f;
float scaledSize = desiredSize * GUI.Scale; float scaledSize = desiredSize * GUI.Scale;
return (int)((desiredSize + scaledSize) / 2.0f); return (int)Math.Min((desiredSize + scaledSize) / 2.0f, Rect.Height / 3);
} }
} }
@@ -73,6 +73,8 @@ namespace Barotrauma
public bool HideChildrenOutsideFrame = true; public bool HideChildrenOutsideFrame = true;
public bool ResizeContentToMakeSpaceForScrollBar = true;
private bool useGridLayout; private bool useGridLayout;
private GUIComponent scrollToElement; private GUIComponent scrollToElement;
@@ -419,7 +421,7 @@ namespace Barotrauma
{ {
dimensionsNeedsRecalculation = false; dimensionsNeedsRecalculation = false;
ContentBackground.RectTransform.Resize(Rect.Size); ContentBackground.RectTransform.Resize(Rect.Size);
bool reduceScrollbarSize = KeepSpaceForScrollBar ? ScrollBarEnabled : ScrollBarVisible; bool reduceScrollbarSize = ResizeContentToMakeSpaceForScrollBar && (KeepSpaceForScrollBar ? ScrollBarEnabled : ScrollBarVisible);
Point contentSize = reduceScrollbarSize ? CalculateFrameSize(ScrollBar.IsHorizontal, ScrollBarSize) : Rect.Size; Point contentSize = reduceScrollbarSize ? CalculateFrameSize(ScrollBar.IsHorizontal, ScrollBarSize) : Rect.Size;
Content.RectTransform.Resize(new Point((int)(contentSize.X - Padding.X - Padding.Z), (int)(contentSize.Y - Padding.Y - Padding.W))); Content.RectTransform.Resize(new Point((int)(contentSize.X - Padding.X - Padding.Z), (int)(contentSize.Y - Padding.Y - Padding.W)));
if (!IsScrollBarOnDefaultSide) { Content.RectTransform.SetPosition(Anchor.BottomRight); } if (!IsScrollBarOnDefaultSide) { Content.RectTransform.SetPosition(Anchor.BottomRight); }
@@ -471,6 +471,7 @@ namespace Barotrauma
public void SetBackgroundIcon(Sprite icon) public void SetBackgroundIcon(Sprite icon)
{ {
if (icon == null) { return; } if (icon == null) { return; }
if (icon == BackgroundIcon.Sprite) { return; }
GUIImage newIcon = new GUIImage(new RectTransform(icon.size.ToPoint(), RectTransform), icon) GUIImage newIcon = new GUIImage(new RectTransform(icon.size.ToPoint(), RectTransform), icon)
{ {
IgnoreLayoutGroups = true, IgnoreLayoutGroups = true,
@@ -593,7 +594,7 @@ namespace Barotrauma
{ {
newBackgroundIcon.SetAsFirstChild(); newBackgroundIcon.SetAsFirstChild();
newBackgroundIcon.RectTransform.AbsoluteOffset = new Point(InnerFrame.Rect.Location.X - (int)(newBackgroundIcon.Rect.Size.X / 1.25f), (int)defaultPos.Y - newBackgroundIcon.Rect.Size.Y / 2); newBackgroundIcon.RectTransform.AbsoluteOffset = new Point(InnerFrame.Rect.Location.X - (int)(newBackgroundIcon.Rect.Size.X / 1.25f), (int)defaultPos.Y - newBackgroundIcon.Rect.Size.Y / 2);
newBackgroundIcon.Color = ToolBox.GradientLerp(iconState, Color.Transparent, Color.White); newBackgroundIcon.Color = Color.Lerp(Color.Transparent, Color.White, iconState);
if (newBackgroundIcon.Color.A == 255) if (newBackgroundIcon.Color.A == 255)
{ {
BackgroundIcon = newBackgroundIcon; BackgroundIcon = newBackgroundIcon;
@@ -81,6 +81,11 @@ namespace Barotrauma
get; private set; get; private set;
} }
public static Rectangle ItemHUDArea
{
get; private set;
}
public static int Padding public static int Padding
{ {
get; private set; get; private set;
@@ -168,6 +173,8 @@ namespace Barotrauma
// Height is based on text content // Height is based on text content
VotingArea = new Rectangle(votingAreaX, votingAreaY, votingAreaWidth, 0); VotingArea = new Rectangle(votingAreaX, votingAreaY, votingAreaWidth, 0);
ItemHUDArea = new Rectangle(0, ButtonAreaTop.Bottom, GameMain.GraphicsWidth, GameMain.GraphicsHeight - ButtonAreaTop.Bottom - InventoryAreaLower.Height);
} }
public static void Draw(SpriteBatch spriteBatch) public static void Draw(SpriteBatch spriteBatch)
@@ -181,6 +188,7 @@ namespace Barotrauma
GUI.DrawRectangle(spriteBatch, InventoryAreaLower, Color.Yellow * 0.5f); GUI.DrawRectangle(spriteBatch, InventoryAreaLower, Color.Yellow * 0.5f);
GUI.DrawRectangle(spriteBatch, HealthWindowAreaLeft, Color.Red * 0.5f); GUI.DrawRectangle(spriteBatch, HealthWindowAreaLeft, Color.Red * 0.5f);
GUI.DrawRectangle(spriteBatch, BottomRightInfoArea, Color.Green * 0.5f); GUI.DrawRectangle(spriteBatch, BottomRightInfoArea, Color.Green * 0.5f);
GUI.DrawRectangle(spriteBatch, ItemHUDArea, Color.Magenta * 0.3f);
} }
} }
@@ -403,7 +403,7 @@ namespace Barotrauma
CreateSubmarineInfo(infoFrameHolder, Submarine.MainSub); CreateSubmarineInfo(infoFrameHolder, Submarine.MainSub);
break; break;
case InfoFrameTab.Talents: case InfoFrameTab.Talents:
CreateTalentInfo(infoFrameHolder); CreateCharacterInfo(infoFrameHolder);
break; break;
} }
} }
@@ -1803,161 +1803,121 @@ namespace Barotrauma
{ TalentTree.TalentTreeStageState.Highlighted, new Color(50,47,33,255) }, { TalentTree.TalentTreeStageState.Highlighted, new Color(50,47,33,255) },
}.ToImmutableDictionary(); }.ToImmutableDictionary();
private void CreateTalentInfo(GUIFrame infoFrame) private void CreateCharacterInfo(GUIFrame infoFrame)
{ {
infoFrame.ClearChildren(); infoFrame.ClearChildren();
talentButtons.Clear(); talentButtons.Clear();
talentCornerIcons.Clear(); talentCornerIcons.Clear();
GUIFrame talentFrameBackground = new GUIFrame(new RectTransform(Vector2.One, infoFrame.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox"); GUIFrame background = new GUIFrame(new RectTransform(Vector2.One, infoFrame.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox");
int padding = GUI.IntScale(15); int padding = GUI.IntScale(15);
GUIFrame talentFrameContent = new GUIFrame(new RectTransform(new Point(talentFrameBackground.Rect.Width - padding, talentFrameBackground.Rect.Height - padding), infoFrame.RectTransform, Anchor.Center), style: null); GUIFrame frame = new GUIFrame(new RectTransform(new Point(background.Rect.Width - padding, background.Rect.Height - padding), infoFrame.RectTransform, Anchor.Center), style: null);
GUIFrame paddedTalentFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.9f), talentFrameContent.RectTransform, Anchor.Center), style: null); GUIFrame content = new GUIFrame(new RectTransform(new Vector2(0.98f), frame.RectTransform, Anchor.Center), style: null);
GUIFrame talentFrameMain = new GUIFrame(new RectTransform(Vector2.One, paddedTalentFrame.RectTransform), style: null);
GUIFrame characterSettingsFrame = null; GUIFrame characterSettingsFrame = null;
GUILayoutGroup characterLayout = null; GUILayoutGroup characterLayout = null;
if (!(GameMain.NetworkMember is null)) if (!(GameMain.NetworkMember is null))
{ {
characterSettingsFrame = new GUIFrame(new RectTransform(Vector2.One, talentFrameContent.RectTransform), style: null) { Visible = false }; characterSettingsFrame = new GUIFrame(new RectTransform(Vector2.One, frame.RectTransform), style: null) { Visible = false };
characterLayout = new GUILayoutGroup(new RectTransform(Vector2.One, characterSettingsFrame.RectTransform)); characterLayout = new GUILayoutGroup(new RectTransform(Vector2.One, characterSettingsFrame.RectTransform));
GUIFrame containerFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.9f), characterLayout.RectTransform), style: null); GUIFrame containerFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.9f), characterLayout.RectTransform), style: null);
GUIFrame playerFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.7f), containerFrame.RectTransform, Anchor.Center), style: null); GUIFrame playerFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.7f), containerFrame.RectTransform, Anchor.Center), style: null);
GameMain.NetLobbyScreen.CreatePlayerFrame(playerFrame, alwaysAllowEditing: true, createPendingText: false); GameMain.NetLobbyScreen.CreatePlayerFrame(playerFrame, alwaysAllowEditing: true, createPendingText: false);
} }
/*Character controlledCharacter = Character.Controlled;
if (controlledCharacter == null) { return; }
if (controlledCharacter.Info is null)
{
DebugConsole.ThrowError("No character info found for talent UI");
return;
}*/
Character controlledCharacter = Character.Controlled; Character controlledCharacter = Character.Controlled;
CharacterInfo info = controlledCharacter?.Info ?? GameMain.Client?.CharacterInfo; CharacterInfo info = controlledCharacter?.Info ?? GameMain.Client?.CharacterInfo;
if (info == null) { return; } if (info == null) { return; }
Job job = info.Job; Job job = info.Job;
GUILayoutGroup talentFrameLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), talentFrameMain.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter) GUILayoutGroup contentLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), content.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter)
{ {
AbsoluteSpacing = GUI.IntScale(5) AbsoluteSpacing = GUI.IntScale(10),
Stretch = true
}; };
GUILayoutGroup talentInfoLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), talentFrameLayoutGroup.RectTransform, Anchor.Center), isHorizontal: true); GUILayoutGroup topLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.3f), contentLayout.RectTransform, Anchor.Center), isHorizontal: true);
new GUICustomComponent(new RectTransform(new Vector2(0.25f, 1f), topLayout.RectTransform), onDraw: (batch, component) =>
new GUICustomComponent(new RectTransform(new Vector2(0.25f, 1f), talentInfoLayoutGroup.RectTransform), onDraw: (batch, component) =>
{ {
float posY = component.Rect.Center.Y - component.Rect.Width / 2; float posY = component.Rect.Center.Y - component.Rect.Width / 2;
info.DrawPortrait(batch, new Vector2(component.Rect.X, posY), Vector2.Zero, component.Rect.Width, false, false); info.DrawPortrait(batch, new Vector2(component.Rect.X, posY), Vector2.Zero, component.Rect.Width, false, false);
}); });
GUILayoutGroup nameLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.3f, 1f), talentInfoLayoutGroup.RectTransform)) { RelativeSpacing = 0.05f }; GUILayoutGroup nameLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.3f, 1f), topLayout.RectTransform))
{
AbsoluteSpacing = GUI.IntScale(5),
CanBeFocused = true
};
Vector2 nameSize = GUIStyle.SubHeadingFont.MeasureString(info.Name); GUITextBlock nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), nameLayout.RectTransform), info.Name, font: GUIStyle.SubHeadingFont);
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(Vector2.One, nameLayout.RectTransform), info.Name, font: GUIStyle.SubHeadingFont);
nameBlock.RectTransform.NonScaledSize = nameSize.Pad(nameBlock.Padding).ToPoint();
if (!info.OmitJobInMenus) if (!info.OmitJobInMenus)
{ {
nameBlock.TextColor = job.Prefab.UIColor; nameBlock.TextColor = job.Prefab.UIColor;
Vector2 jobSize = GUIStyle.SmallFont.MeasureString(job.Name); GUITextBlock jobBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), nameLayout.RectTransform), job.Name, font: GUIStyle.SmallFont) { TextColor = job.Prefab.UIColor };
GUITextBlock jobBlock = new GUITextBlock(new RectTransform(Vector2.One, nameLayout.RectTransform), job.Name, font: GUIStyle.SmallFont) { TextColor = job.Prefab.UIColor };
jobBlock.RectTransform.NonScaledSize = jobSize.Pad(jobBlock.Padding).ToPoint();
} }
LocalizedString traitString = TextManager.AddPunctuation(':', TextManager.Get("PersonalityTrait"), TextManager.Get("personalitytrait." + info.PersonalityTrait.Name.Replace(" ", ""))); LocalizedString traitString = TextManager.AddPunctuation(':', TextManager.Get("PersonalityTrait"), info.PersonalityTrait.DisplayName);
Vector2 traitSize = GUIStyle.SmallFont.MeasureString(traitString); Vector2 traitSize = GUIStyle.SmallFont.MeasureString(traitString);
GUITextBlock traitBlock = new GUITextBlock(new RectTransform(Vector2.One, nameLayout.RectTransform), traitString, font: GUIStyle.SmallFont); GUITextBlock traitBlock = new GUITextBlock(new RectTransform(Vector2.One, nameLayout.RectTransform), traitString, font: GUIStyle.SmallFont);
traitBlock.RectTransform.NonScaledSize = traitSize.Pad(traitBlock.Padding).ToPoint(); traitBlock.RectTransform.NonScaledSize = traitSize.Pad(traitBlock.Padding).ToPoint();
GUIFrame talentsOutsideTreeFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.35f), nameLayout.RectTransform, Anchor.BottomCenter), style: null); IEnumerable<TalentPrefab> talentsOutsideTree = info.GetUnlockedTalentsOutsideTree().Select(e => TalentPrefab.TalentPrefabs.Find(c => c.Identifier == e));
if (talentsOutsideTree.Count() > 0)
if (!(GameMain.NetworkMember is null))
{ {
GUIButton newCharacterBox = new GUIButton(new RectTransform(new Vector2(0.675f, 1f), talentsOutsideTreeFrame.RectTransform, Anchor.TopLeft), //spacing
text: GameMain.NetLobbyScreen.CampaignCharacterDiscarded ? TextManager.Get("settings") : TextManager.Get("createnew")) new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), nameLayout.RectTransform), style: null);
GUILayoutGroup extraTalentLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.3f), nameLayout.RectTransform), childAnchor: Anchor.TopCenter);
talentPointText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), extraTalentLayout.RectTransform, anchor: Anchor.Center), TextManager.Get("talentmenu.extratalents"), font: GUIStyle.SubHeadingFont);
talentPointText.RectTransform.MaxSize = new Point(int.MaxValue, (int)talentPointText.TextSize.Y);
var extraTalentList = new GUIListBox(new RectTransform(new Vector2(0.9f, 0.8f), extraTalentLayout.RectTransform, anchor: Anchor.Center), isHorizontal: true)
{ {
IgnoreLayoutGroups = true AutoHideScrollBar = false,
ResizeContentToMakeSpaceForScrollBar = false
}; };
newCharacterBox.TextBlock.AutoScaleHorizontal = true; extraTalentList.ScrollBar.RectTransform.SetPosition(Anchor.BottomCenter, Pivot.TopCenter);
extraTalentList.RectTransform.MinSize = new Point(0, GUI.IntScale(65));
extraTalentLayout.Recalculate();
extraTalentList.ForceLayoutRecalculation();
newCharacterBox.OnClicked = (button, o) => foreach (var extraTalent in talentsOutsideTree)
{ {
if (!GameMain.NetLobbyScreen.CampaignCharacterDiscarded) var img = new GUIImage(new RectTransform(new Point(extraTalentList.Content.Rect.Height), extraTalentList.Content.RectTransform), sprite: extraTalent.Icon, scaleToFit: true)
{ {
GameMain.NetLobbyScreen.TryDiscardCampaignCharacter(() => ToolTip = RichString.Rich($"‖color:{Color.White.ToStringHex()}‖{extraTalent.DisplayName}‖color:end‖" + "\n\n" + extraTalent.Description),
{ Color = GUIStyle.Green
newCharacterBox.Text = TextManager.Get("settings");
if (pendingChangesFrame != null)
{
NetLobbyScreen.CreateChangesPendingFrame(pendingChangesFrame);
}
OpenMenu();
});
return true;
}
OpenMenu();
return true;
void OpenMenu()
{
characterSettingsFrame!.Visible = true;
talentFrameMain.Visible = false;
}
};
if (!(characterLayout is null))
{
GUILayoutGroup characterCloseButtonLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), characterLayout.RectTransform), childAnchor: Anchor.BottomRight);
new GUIButton(new RectTransform(new Vector2(0.4f, 1f), characterCloseButtonLayout.RectTransform), TextManager.Get("ApplySettingsButton")) //TODO: Is this text appropriate for this circumstance for all languages?
{
OnClicked = (button, o) =>
{
GameMain.Client?.SendCharacterInfo(GameMain.Client.PendingName);
characterSettingsFrame!.Visible = false;
talentFrameMain.Visible = true;
return true;
}
}; };
img.RectTransform.SizeChanged += () =>
{
img.RectTransform.MaxSize = new Point(img.Rect.Height);
};
} }
} }
IEnumerable<TalentPrefab> talentsOutsideTree = info.GetUnlockedTalentsOutsideTree().Select(e => TalentPrefab.TalentPrefabs.Find(c => c.Identifier == e)); GUILayoutGroup skillLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.45f, 1f), topLayout.RectTransform), childAnchor: Anchor.TopRight)
if (talentsOutsideTree.Count() > 0)
{ {
//TODO: replace with something more generic AbsoluteSpacing = GUI.IntScale(5),
GUIImage endocrineIcon = new GUIImage(new RectTransform(new Vector2(0.275f, 1f), talentsOutsideTreeFrame.RectTransform, anchor: Anchor.TopRight, scaleBasis: ScaleBasis.Normal), style: "EndocrineReminderIcon") Stretch = true
{ };
ToolTip = $"{TextManager.Get("afflictionname.endocrineboost")}\n\n{string.Join(", ", talentsOutsideTree.Select(e => e.DisplayName))}"
};
}
GUILayoutGroup skillLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.45f, 1f), talentInfoLayoutGroup.RectTransform)) { Stretch = true }; GUITextBlock skillBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), skillLayout.RectTransform), TextManager.Get("skills"), font: GUIStyle.SubHeadingFont);
LocalizedString skillString = TextManager.Get("skills");
Vector2 skillSize = GUIStyle.SubHeadingFont.MeasureString(skillString);
GUITextBlock skillBlock = new GUITextBlock(new RectTransform(Vector2.One, skillLayout.RectTransform), skillString, font: GUIStyle.SubHeadingFont);
skillBlock.RectTransform.NonScaledSize = skillSize.Pad(skillBlock.Padding).ToPoint();
skillListBox = new GUIListBox(new RectTransform(new Vector2(1f, 1f - skillBlock.RectTransform.RelativeSize.Y), skillLayout.RectTransform), style: null); skillListBox = new GUIListBox(new RectTransform(new Vector2(1f, 1f - skillBlock.RectTransform.RelativeSize.Y), skillLayout.RectTransform), style: null);
CreateTalentSkillList(controlledCharacter, info, skillListBox); CreateSkillList(controlledCharacter, info, skillListBox);
if (controlledCharacter != null) if (controlledCharacter != null)
{ {
if (!TalentTree.JobTalentTrees.TryGet(info.Job.Prefab.Identifier, out TalentTree talentTree)) { return; } if (!TalentTree.JobTalentTrees.TryGet(info.Job.Prefab.Identifier, out TalentTree talentTree)) { return; }
new GUIFrame(new RectTransform(new Vector2(1f, 1f), talentFrameLayoutGroup.RectTransform), style: "HorizontalLine"); new GUIFrame(new RectTransform(new Vector2(1f, 1f), contentLayout.RectTransform), style: "HorizontalLine");
GUIListBox talentTreeListBox = new GUIListBox(new RectTransform(new Vector2(1f, 0.7f), talentFrameLayoutGroup.RectTransform, Anchor.TopCenter), isHorizontal: true, style: null); GUIListBox talentTreeListBox = new GUIListBox(new RectTransform(new Vector2(1f, 0.6f), contentLayout.RectTransform, Anchor.TopCenter), isHorizontal: true, style: null);
selectedTalents = info.GetUnlockedTalentsInTree().ToList(); selectedTalents = info.GetUnlockedTalentsInTree().ToList();
@@ -2008,10 +1968,9 @@ namespace Barotrauma
}; };
GUIFrame croppedTalentFrame = new GUIFrame(new RectTransform(Vector2.One, talentFrame.RectTransform, anchor: Anchor.Center, scaleBasis: ScaleBasis.BothHeight), style: null); GUIFrame croppedTalentFrame = new GUIFrame(new RectTransform(Vector2.One, talentFrame.RectTransform, anchor: Anchor.Center, scaleBasis: ScaleBasis.BothHeight), style: null);
GUIButton talentButton = new GUIButton(new RectTransform(Vector2.One, croppedTalentFrame.RectTransform, anchor: Anchor.Center), style: null) GUIButton talentButton = new GUIButton(new RectTransform(Vector2.One, croppedTalentFrame.RectTransform, anchor: Anchor.Center), style: null)
{ {
ToolTip = RichString.Rich(talent.DisplayName + "\n\n" + talent.Description), ToolTip = RichString.Rich($"‖color:{Color.White.ToStringHex()}‖{talent.DisplayName}‖color:end‖" + "\n\n" + talent.Description),
UserData = talent.Identifier, UserData = talent.Identifier,
PressedColor = pressedColor, PressedColor = pressedColor,
Enabled = controlledCharacter != null, Enabled = controlledCharacter != null,
@@ -2078,9 +2037,13 @@ namespace Barotrauma
} }
GUITextBlock.AutoScaleAndNormalize(subTreeNames); GUITextBlock.AutoScaleAndNormalize(subTreeNames);
GUILayoutGroup talentBottomFrame = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.07f), talentFrameLayoutGroup.RectTransform, Anchor.TopCenter), isHorizontal: true) { RelativeSpacing = 0.01f }; GUILayoutGroup bottomLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.07f), contentLayout.RectTransform, Anchor.TopCenter), isHorizontal: true)
{
RelativeSpacing = 0.01f,
Stretch = true
};
GUILayoutGroup experienceLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.59f, 1f), talentBottomFrame.RectTransform)); GUILayoutGroup experienceLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.59f, 1f), bottomLayout.RectTransform));
GUIFrame experienceBarFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.5f), experienceLayout.RectTransform), style: null); GUIFrame experienceBarFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.5f), experienceLayout.RectTransform), style: null);
experienceBar = new GUIProgressBar(new RectTransform(new Vector2(1f, 1f), experienceBarFrame.RectTransform, Anchor.CenterLeft), experienceBar = new GUIProgressBar(new RectTransform(new Vector2(1f, 1f), experienceBarFrame.RectTransform, Anchor.CenterLeft),
@@ -2097,30 +2060,84 @@ namespace Barotrauma
talentPointText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), experienceLayout.RectTransform, anchor: Anchor.Center), "", font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterRight) { AutoScaleVertical = true }; talentPointText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), experienceLayout.RectTransform, anchor: Anchor.Center), "", font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterRight) { AutoScaleVertical = true };
talentResetButton = new GUIButton(new RectTransform(new Vector2(0.19f, 1f), talentBottomFrame.RectTransform), text: TextManager.Get("reset"), style: "GUIButtonFreeScale") talentResetButton = new GUIButton(new RectTransform(new Vector2(0.19f, 1f), bottomLayout.RectTransform), text: TextManager.Get("reset"), style: "GUIButtonFreeScale")
{ {
OnClicked = ResetTalentSelection OnClicked = ResetTalentSelection
}; };
talentApplyButton = new GUIButton(new RectTransform(new Vector2(0.19f, 1f), talentBottomFrame.RectTransform), text: TextManager.Get("applysettingsbutton"), style: "GUIButtonFreeScale") talentApplyButton = new GUIButton(new RectTransform(new Vector2(0.19f, 1f), bottomLayout.RectTransform), text: TextManager.Get("applysettingsbutton"), style: "GUIButtonFreeScale")
{ {
OnClicked = ApplyTalentSelection, OnClicked = ApplyTalentSelection,
}; };
GUITextBlock.AutoScaleAndNormalize(talentResetButton.TextBlock, talentApplyButton.TextBlock); GUITextBlock.AutoScaleAndNormalize(talentResetButton.TextBlock, talentApplyButton.TextBlock);
} }
if (!(GameMain.NetworkMember is null))
{
GUIButton newCharacterBox = new GUIButton(new RectTransform(new Vector2(0.5f, 0.2f), skillLayout.RectTransform, Anchor.BottomRight),
text: GameMain.NetLobbyScreen.CampaignCharacterDiscarded ? TextManager.Get("settings") : TextManager.Get("createnew"), style: "GUIButtonSmall")
{
IgnoreLayoutGroups = false
};
newCharacterBox.TextBlock.AutoScaleHorizontal = true;
newCharacterBox.OnClicked = (button, o) =>
{
if (!GameMain.NetLobbyScreen.CampaignCharacterDiscarded)
{
GameMain.NetLobbyScreen.TryDiscardCampaignCharacter(() =>
{
newCharacterBox.Text = TextManager.Get("settings");
if (pendingChangesFrame != null)
{
NetLobbyScreen.CreateChangesPendingFrame(pendingChangesFrame);
}
OpenMenu();
});
return true;
}
OpenMenu();
return true;
void OpenMenu()
{
characterSettingsFrame!.Visible = true;
content.Visible = false;
}
};
if (!(characterLayout is null))
{
GUILayoutGroup characterCloseButtonLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), characterLayout.RectTransform), childAnchor: Anchor.BottomCenter);
new GUIButton(new RectTransform(new Vector2(0.4f, 1f), characterCloseButtonLayout.RectTransform), TextManager.Get("ApplySettingsButton")) //TODO: Is this text appropriate for this circumstance for all languages?
{
OnClicked = (button, o) =>
{
GameMain.Client?.SendCharacterInfo(GameMain.Client.PendingName);
characterSettingsFrame!.Visible = false;
content.Visible = true;
return true;
}
};
}
}
UpdateTalentInfo(); UpdateTalentInfo();
} }
private void CreateTalentSkillList(Character character, CharacterInfo info, GUIListBox parent) private void CreateSkillList(Character character, CharacterInfo info, GUIListBox parent)
{ {
parent.Content.ClearChildren(); parent.Content.ClearChildren();
List<GUITextBlock> skillNames = new List<GUITextBlock>(); List<GUITextBlock> skillNames = new List<GUITextBlock>();
foreach (Skill skill in info.Job.GetSkills()) foreach (Skill skill in info.Job.GetSkills())
{ {
GUILayoutGroup skillContainer = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.2f), parent.Content.RectTransform), isHorizontal: true) { CanBeFocused = false }; GUILayoutGroup skillContainer = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.0f), parent.Content.RectTransform), isHorizontal: true) { CanBeFocused = true };
var skillName = new GUITextBlock(new RectTransform(new Vector2(0.7f, 0.0f), skillContainer.RectTransform), TextManager.Get($"skillname.{skill.Identifier}").Fallback(skill.Identifier.Value));
skillNames.Add(skillName);
skillName.RectTransform.MinSize = new Point(0, skillName.Rect.Height);
skillContainer.RectTransform.MinSize = new Point(0, skillName.Rect.Height);
skillNames.Add(new GUITextBlock(new RectTransform(new Vector2(0.7f, 1f), skillContainer.RectTransform), TextManager.Get($"skillname.{skill.Identifier}").Fallback(skill.Identifier.Value))); new GUITextBlock(new RectTransform(new Vector2(0.15f, 1.0f), skillContainer.RectTransform), Math.Floor(skill.Level).ToString("F0"), textAlignment: Alignment.TopRight);
new GUITextBlock(new RectTransform(new Vector2(0.15f, 1.0f), skillContainer.RectTransform), Math.Floor(skill.Level).ToString("F0"), textAlignment: Alignment.CenterRight) { Padding = new Vector4(0, 0, 4, 0) };
float modifiedSkillLevel = character?.GetSkillLevel(skill.Identifier) ?? skill.Level; float modifiedSkillLevel = character?.GetSkillLevel(skill.Identifier) ?? skill.Level;
if (!MathUtils.NearlyEqual(MathF.Floor(modifiedSkillLevel), MathF.Floor(skill.Level))) if (!MathUtils.NearlyEqual(MathF.Floor(modifiedSkillLevel), MathF.Floor(skill.Level)))
@@ -2129,15 +2146,15 @@ namespace Barotrauma
//TODO: if/when we upgrade to C# 9, do neater pattern matching here //TODO: if/when we upgrade to C# 9, do neater pattern matching here
string stringColor = true switch string stringColor = true switch
{ {
true when skillChange > 0 => XMLExtensions.ColorToString(GUIStyle.Green), true when skillChange > 0 => XMLExtensions.ToStringHex(GUIStyle.Green),
true when skillChange < 0 => XMLExtensions.ColorToString(GUIStyle.Red), true when skillChange < 0 => XMLExtensions.ToStringHex(GUIStyle.Red),
_ => XMLExtensions.ColorToString(GUIStyle.TextColorNormal) _ => XMLExtensions.ToStringHex(GUIStyle.TextColorNormal)
}; };
RichString changeText = RichString.Rich($"(‖color:{stringColor}‖{(skillChange > 0 ? "+" : string.Empty) + skillChange}‖color:end‖)"); RichString changeText = RichString.Rich($"(‖color:{stringColor}‖{(skillChange > 0 ? "+" : string.Empty) + skillChange}‖color:end‖)");
new GUITextBlock(new RectTransform(new Vector2(0.15f, 1.0f), skillContainer.RectTransform), changeText) { Padding = Vector4.Zero }; new GUITextBlock(new RectTransform(new Vector2(0.15f, 1.0f), skillContainer.RectTransform), changeText) { Padding = Vector4.Zero };
} }
skillContainer.Recalculate(); //skillContainer.Recalculate();
} }
parent.RecalculateChildren(); parent.RecalculateChildren();
@@ -2216,7 +2233,7 @@ namespace Barotrauma
talentButton.icon.HoverColor = hoverColor; talentButton.icon.HoverColor = hoverColor;
} }
CreateTalentSkillList(controlledCharacter, controlledCharacter.Info, skillListBox); CreateSkillList(controlledCharacter, controlledCharacter.Info, skillListBox);
} }
private void ApplyTalents(Character controlledCharacter) private void ApplyTalents(Character controlledCharacter)
@@ -432,13 +432,21 @@ namespace Barotrauma
}; };
Location location = Campaign.Map.CurrentLocation; Location location = Campaign.Map.CurrentLocation;
int hullRepairCost = location?.GetAdjustedMechanicalCost(CampaignMode.HullRepairCost) ?? CampaignMode.HullRepairCost;
int itemRepairCost = location?.GetAdjustedMechanicalCost(CampaignMode.ItemRepairCost) ?? CampaignMode.ItemRepairCost; int hullRepairCost = Campaign.GetHullRepairCost();
int shuttleRetrieveCost = location?.GetAdjustedMechanicalCost(CampaignMode.ShuttleReplaceCost) ?? CampaignMode.ShuttleReplaceCost; int itemRepairCost = Campaign.GetItemRepairCost();
int shuttleRetrieveCost = CampaignMode.ShuttleReplaceCost;
if (location != null)
{
hullRepairCost = location.GetAdjustedMechanicalCost(hullRepairCost);
itemRepairCost = location.GetAdjustedMechanicalCost(itemRepairCost);
shuttleRetrieveCost = location.GetAdjustedMechanicalCost(shuttleRetrieveCost);
}
CreateRepairEntry(currentStoreLayout.Content, TextManager.Get("repairallwalls"), "RepairHullButton", hullRepairCost, (button, o) => CreateRepairEntry(currentStoreLayout.Content, TextManager.Get("repairallwalls"), "RepairHullButton", hullRepairCost, (button, o) =>
{ {
if (Campaign.PurchasedHullRepairs) //cost is zero = nothing to repair
if (Campaign.PurchasedHullRepairs || hullRepairCost <= 0)
{ {
button.Enabled = false; button.Enabled = false;
return false; return false;
@@ -471,7 +479,7 @@ namespace Barotrauma
return false; return false;
} }
return true; return true;
}, Campaign.PurchasedHullRepairs || !HasPermission, isHovered => }, Campaign.PurchasedHullRepairs || !HasPermission || hullRepairCost <= 0, isHovered =>
{ {
highlightWalls = isHovered; highlightWalls = isHovered;
return true; return true;
@@ -479,7 +487,8 @@ namespace Barotrauma
CreateRepairEntry(currentStoreLayout.Content, TextManager.Get("repairallitems"), "RepairItemsButton", itemRepairCost, (button, o) => CreateRepairEntry(currentStoreLayout.Content, TextManager.Get("repairallitems"), "RepairItemsButton", itemRepairCost, (button, o) =>
{ {
if (PlayerBalance >= itemRepairCost && !Campaign.PurchasedItemRepairs) //cost is zero = nothing to repair
if (PlayerBalance >= itemRepairCost && !Campaign.PurchasedItemRepairs && itemRepairCost > 0)
{ {
LocalizedString body = TextManager.GetWithVariable("ItemRepairs.PurchasePromptBody", "[amount]", itemRepairCost.ToString()); LocalizedString body = TextManager.GetWithVariable("ItemRepairs.PurchasePromptBody", "[amount]", itemRepairCost.ToString());
currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get("Upgrades.PurchasePromptTitle"), body, () => currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get("Upgrades.PurchasePromptTitle"), body, () =>
@@ -505,9 +514,8 @@ namespace Barotrauma
button.Enabled = false; button.Enabled = false;
return false; return false;
} }
return true; return true;
}, Campaign.PurchasedItemRepairs || !HasPermission, isHovered => }, Campaign.PurchasedItemRepairs || !HasPermission || itemRepairCost <= 0, isHovered =>
{ {
foreach (var (item, itemFrame) in itemPreviews) foreach (var (item, itemFrame) in itemPreviews)
{ {
@@ -814,7 +814,7 @@ namespace Barotrauma
else if ((Character.Controlled == null || !itemHudActive()) else if ((Character.Controlled == null || !itemHudActive())
&& CharacterHealth.OpenHealthWindow == null && CharacterHealth.OpenHealthWindow == null
&& !CrewManager.IsCommandInterfaceOpen && !CrewManager.IsCommandInterfaceOpen
&& !(Screen.Selected is SubEditorScreen editor && !editor.WiringMode && Character.Controlled?.SelectedConstruction != null)) && !(Screen.Selected is SubEditorScreen editor && !editor.WiringMode && Character.Controlled?.SelectedItem != null))
{ {
// Otherwise toggle pausing, unless another window/interface is open. // Otherwise toggle pausing, unless another window/interface is open.
GUI.TogglePauseMenu(); GUI.TogglePauseMenu();
@@ -822,9 +822,9 @@ namespace Barotrauma
static bool itemHudActive() static bool itemHudActive()
{ {
if (Character.Controlled?.SelectedConstruction == null) { return false; } if (Character.Controlled?.SelectedItem == null) { return false; }
return return
Character.Controlled.SelectedConstruction.ActiveHUDs.Any(ic => ic.GuiFrame != null) || Character.Controlled.SelectedItem.ActiveHUDs.Any(ic => ic.GuiFrame != null) ||
((Character.Controlled.ViewTarget as Item)?.Prefab?.FocusOnSelected ?? false); ((Character.Controlled.ViewTarget as Item)?.Prefab?.FocusOnSelected ?? false);
} }
} }
@@ -919,7 +919,7 @@ namespace Barotrauma
#endif #endif
} }
CoroutineManager.Update((float)Timing.Step, Paused ? 0.0f : (float)Timing.Step); CoroutineManager.Update(Paused, (float)Timing.Step);
SteamManager.Update((float)Timing.Step); SteamManager.Update((float)Timing.Step);
@@ -1098,7 +1098,7 @@ namespace Barotrauma
GameAnalyticsManager.AddDesignEvent(eventId + "EventManager:CurrentIntensity", GameSession.EventManager.CurrentIntensity); GameAnalyticsManager.AddDesignEvent(eventId + "EventManager:CurrentIntensity", GameSession.EventManager.CurrentIntensity);
foreach (var activeEvent in GameSession.EventManager.ActiveEvents) foreach (var activeEvent in GameSession.EventManager.ActiveEvents)
{ {
GameAnalyticsManager.AddDesignEvent(eventId + "EventManager:ActiveEvents:" + activeEvent.ToString()); GameAnalyticsManager.AddDesignEvent(eventId + "EventManager:ActiveEvents:" + activeEvent.Prefab.Identifier);
} }
GameSession.LogEndRoundStats(eventId); GameSession.LogEndRoundStats(eventId);
if (GameSession.GameMode is TutorialMode tutorialMode) if (GameSession.GameMode is TutorialMode tutorialMode)
@@ -2412,7 +2412,7 @@ namespace Barotrauma
float reactorOutput = -reactor.CurrPowerConsumption; float reactorOutput = -reactor.CurrPowerConsumption;
// If player is not an engineer AND the reactor is not powered up AND nobody is using the reactor // If player is not an engineer AND the reactor is not powered up AND nobody is using the reactor
// --> Create shortcut node for "Operate Reactor" order's "Power Up" option // --> Create shortcut node for "Operate Reactor" order's "Power Up" option
if (ShouldDelegateOrder("operatereactor") && reactorOutput < float.Epsilon && characters.None(c => c.SelectedConstruction == reactor.Item)) if (ShouldDelegateOrder("operatereactor") && reactorOutput < float.Epsilon && characters.None(c => c.SelectedItem == reactor.Item))
{ {
var orderPrefab = OrderPrefab.Prefabs["operatereactor"]; var orderPrefab = OrderPrefab.Prefabs["operatereactor"];
var order = new Order(orderPrefab, orderPrefab.Options[0], reactor.Item, reactor); var order = new Order(orderPrefab, orderPrefab.Options[0], reactor.Item, reactor);
@@ -2426,7 +2426,7 @@ namespace Barotrauma
// If player is not a captain AND nobody is using the nav terminal AND the nav terminal is powered up // If player is not a captain AND nobody is using the nav terminal AND the nav terminal is powered up
// --> Create shortcut node for Steer order // --> Create shortcut node for Steer order
if (CanFitMoreNodes() && ShouldDelegateOrder("steer") && IsNonDuplicateOrderPrefab(OrderPrefab.Prefabs["steer"]) && if (CanFitMoreNodes() && ShouldDelegateOrder("steer") && IsNonDuplicateOrderPrefab(OrderPrefab.Prefabs["steer"]) &&
subItems.Find(i => i.HasTag("navterminal") && i.IsPlayerTeamInteractable) is Item nav && characters.None(c => c.SelectedConstruction == nav) && subItems.Find(i => i.HasTag("navterminal") && i.IsPlayerTeamInteractable) is Item nav && characters.None(c => c.SelectedItem == nav) &&
nav.GetComponent<Steering>() is Steering steering && steering.Voltage > steering.MinVoltage) nav.GetComponent<Steering>() is Steering steering && steering.Voltage > steering.MinVoltage)
{ {
var order = new Order(OrderPrefab.Prefabs["steer"], steering.Item, steering); var order = new Order(OrderPrefab.Prefabs["steer"], steering.Item, steering);
@@ -3,8 +3,8 @@ namespace Barotrauma
{ {
partial class GameMode partial class GameMode
{ {
public virtual void Draw(SpriteBatch spriteBatch) public virtual void HUDScaleChanged() { }
{
} public virtual void Draw(SpriteBatch spriteBatch) { }
} }
} }
@@ -115,12 +115,16 @@ namespace Barotrauma
partial void InitProjSpecific() partial void InitProjSpecific()
{ {
var buttonContainer = new GUILayoutGroup(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.ButtonAreaTop, GUI.Canvas), CreateButtons();
isHorizontal: true, childAnchor: Anchor.CenterRight) }
{
CanBeFocused = false
};
public override void HUDScaleChanged()
{
CreateButtons();
}
private void CreateButtons()
{
int buttonHeight = (int) (GUI.Scale * 40), int buttonHeight = (int) (GUI.Scale * 40),
buttonWidth = GUI.IntScale(450), buttonWidth = GUI.IntScale(450),
buttonCenter = buttonHeight / 2, buttonCenter = buttonHeight / 2,
@@ -166,8 +170,6 @@ namespace Barotrauma
}, },
UserData = "ReadyCheckButton" UserData = "ReadyCheckButton"
}; };
buttonContainer.Recalculate();
} }
private void InitCampaignUI() private void InitCampaignUI()
@@ -311,7 +313,7 @@ namespace Barotrauma
if (prevControlled != null) if (prevControlled != null)
{ {
prevControlled.SelectedConstruction = null; prevControlled.SelectedItem = prevControlled.SelectedSecondaryItem = null;
if (prevControlled.AIController != null) if (prevControlled.AIController != null)
{ {
prevControlled.AIController.Enabled = true; prevControlled.AIController.Enabled = true;
@@ -362,7 +364,7 @@ namespace Barotrauma
float t = 0.0f; float t = 0.0f;
while (t < fadeOutDuration || endTransition.Running) while (t < fadeOutDuration || endTransition.Running)
{ {
t += CoroutineManager.UnscaledDeltaTime; t += CoroutineManager.DeltaTime;
overlayColor = Color.Lerp(Color.Transparent, Color.White, t / fadeOutDuration); overlayColor = Color.Lerp(Color.Transparent, Color.White, t / fadeOutDuration);
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
} }
@@ -469,7 +471,6 @@ namespace Barotrauma
{ {
base.End(transitionType); base.End(transitionType);
ForceMapUI = ShowCampaignUI = false; ForceMapUI = ShowCampaignUI = false;
UpgradeManager.CanUpgrade = true;
// remove all event dialogue boxes // remove all event dialogue boxes
GUIMessageBox.MessageBoxes.ForEachMod(mb => GUIMessageBox.MessageBoxes.ForEachMod(mb =>
@@ -169,10 +169,20 @@ namespace Barotrauma
public static SinglePlayerCampaign Load(XElement element) => new SinglePlayerCampaign(element); public static SinglePlayerCampaign Load(XElement element) => new SinglePlayerCampaign(element);
private void InitUI() private void InitUI()
{
CreateEndRoundButton();
campaignUIContainer = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), style: "InnerGlow", color: Color.Black);
CampaignUI = new CampaignUI(this, campaignUIContainer)
{
StartRound = () => { TryEndRound(); }
};
}
private void CreateEndRoundButton()
{ {
int buttonHeight = (int)(GUI.Scale * 40); int buttonHeight = (int)(GUI.Scale * 40);
int buttonWidth = GUI.IntScale(450); int buttonWidth = GUI.IntScale(450);
endRoundButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle((GameMain.GraphicsWidth / 2) - (buttonWidth / 2), HUDLayoutSettings.ButtonAreaTop.Center.Y - (buttonHeight / 2), buttonWidth, buttonHeight), GUI.Canvas), endRoundButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle((GameMain.GraphicsWidth / 2) - (buttonWidth / 2), HUDLayoutSettings.ButtonAreaTop.Center.Y - (buttonHeight / 2), buttonWidth, buttonHeight), GUI.Canvas),
TextManager.Get("EndRound"), textAlignment: Alignment.Center, style: "EndRoundButton") TextManager.Get("EndRound"), textAlignment: Alignment.Center, style: "EndRoundButton")
{ {
@@ -190,12 +200,11 @@ namespace Barotrauma
return true; return true;
} }
}; };
}
campaignUIContainer = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), style: "InnerGlow", color: Color.Black); public override void HUDScaleChanged()
CampaignUI = new CampaignUI(this, campaignUIContainer) {
{ CreateEndRoundButton();
StartRound = () => { TryEndRound(); }
};
} }
#endregion #endregion
@@ -292,7 +301,7 @@ namespace Barotrauma
yield return CoroutineStatus.Success; yield return CoroutineStatus.Success;
} }
overlayTextColor = Color.Lerp(Color.Transparent, Color.White, (timer - 1.0f) / fadeInDuration); overlayTextColor = Color.Lerp(Color.Transparent, Color.White, (timer - 1.0f) / fadeInDuration);
timer = Math.Min(timer + CoroutineManager.UnscaledDeltaTime, textDuration); timer = Math.Min(timer + CoroutineManager.DeltaTime, textDuration);
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
} }
var outpost = GameMain.GameSession.Level.StartOutpost; var outpost = GameMain.GameSession.Level.StartOutpost;
@@ -320,7 +329,7 @@ namespace Barotrauma
while (timer < fadeInDuration) while (timer < fadeInDuration)
{ {
overlayColor = Color.Lerp(Color.LightGray, Color.Transparent, timer / fadeInDuration); overlayColor = Color.Lerp(Color.LightGray, Color.Transparent, timer / fadeInDuration);
timer += CoroutineManager.UnscaledDeltaTime; timer += CoroutineManager.DeltaTime;
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
} }
overlayColor = Color.Transparent; overlayColor = Color.Transparent;
@@ -353,7 +362,7 @@ namespace Barotrauma
if (prevControlled != null) if (prevControlled != null)
{ {
prevControlled.SelectedConstruction = null; prevControlled.SelectedItem = prevControlled.SelectedSecondaryItem = null;
if (prevControlled.AIController != null) if (prevControlled.AIController != null)
{ {
prevControlled.AIController.Enabled = true; prevControlled.AIController.Enabled = true;
@@ -424,7 +433,7 @@ namespace Barotrauma
float t = 0.0f; float t = 0.0f;
while (t < fadeOutDuration || endTransition.Running) while (t < fadeOutDuration || endTransition.Running)
{ {
t += CoroutineManager.UnscaledDeltaTime; t += CoroutineManager.DeltaTime;
overlayColor = Color.Lerp(Color.Transparent, Color.White, t / fadeOutDuration); overlayColor = Color.Lerp(Color.Transparent, Color.White, t / fadeOutDuration);
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
} }
@@ -436,6 +445,7 @@ namespace Barotrauma
if (success) if (success)
{ {
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine); GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
GameMain.GameSession.EventManager.RegisterEventHistory();
SaveUtil.SaveGame(GameMain.GameSession.SavePath); SaveUtil.SaveGame(GameMain.GameSession.SavePath);
} }
else else
@@ -124,7 +124,7 @@ namespace Barotrauma.Tutorials
captain_medicSpawnPos = Item.ItemList.Find(i => i.HasTag("captain_medicspawnpos")).WorldPosition; captain_medicSpawnPos = Item.ItemList.Find(i => i.HasTag("captain_medicspawnpos")).WorldPosition;
tutorial_submarineDoor = Item.ItemList.Find(i => i.HasTag("tutorial_submarinedoor")).GetComponent<Door>(); tutorial_submarineDoor = Item.ItemList.Find(i => i.HasTag("tutorial_submarinedoor")).GetComponent<Door>();
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, jobOrJobPrefab: JobPrefab.Get("medicaldoctor")) var medicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: JobPrefab.Get("medicaldoctor".ToIdentifier()))
{ {
TeamID = CharacterTeamType.Team1 TeamID = CharacterTeamType.Team1
}; };
@@ -148,21 +148,21 @@ namespace Barotrauma.Tutorials
SetDoorAccess(tutorial_lockedDoor_1, null, false); SetDoorAccess(tutorial_lockedDoor_1, null, false);
SetDoorAccess(tutorial_lockedDoor_2, null, false); SetDoorAccess(tutorial_lockedDoor_2, null, false);
var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: JobPrefab.Get("mechanic")) var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: JobPrefab.Get("mechanic".ToIdentifier()))
{ {
TeamID = CharacterTeamType.Team1 TeamID = CharacterTeamType.Team1
}; };
captain_mechanic = Character.Create(mechanicInfo, WayPoint.GetRandom(SpawnType.Human, mechanicInfo.Job?.Prefab, Submarine.MainSub).WorldPosition, "mechanic"); captain_mechanic = Character.Create(mechanicInfo, WayPoint.GetRandom(SpawnType.Human, mechanicInfo.Job?.Prefab, Submarine.MainSub).WorldPosition, "mechanic");
captain_mechanic.GiveJobItems(); captain_mechanic.GiveJobItems();
var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: JobPrefab.Get("securityofficer")) var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: JobPrefab.Get("securityofficer".ToIdentifier()))
{ {
TeamID = CharacterTeamType.Team1 TeamID = CharacterTeamType.Team1
}; };
captain_security = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job?.Prefab, Submarine.MainSub).WorldPosition, "securityofficer"); captain_security = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job?.Prefab, Submarine.MainSub).WorldPosition, "securityofficer");
captain_security.GiveJobItems(); captain_security.GiveJobItems();
var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: JobPrefab.Get("engineer")) var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: JobPrefab.Get("engineer".ToIdentifier()))
{ {
TeamID = CharacterTeamType.Team1 TeamID = CharacterTeamType.Team1
}; };
@@ -339,8 +339,8 @@ namespace Barotrauma.Tutorials
private bool IsSelectedItem(Item item) private bool IsSelectedItem(Item item)
{ {
return return
captain?.SelectedConstruction == item || captain?.SelectedItem == item ||
(captain?.SelectedConstruction?.linkedTo?.Contains(item) ?? false); (captain?.SelectedItem?.linkedTo?.Contains(item) ?? false);
} }
} }
} }
@@ -119,7 +119,7 @@ namespace Barotrauma.Tutorials
var patientHull2 = WayPoint.WayPointList.Find(wp => wp.IdCardDesc == "airlock").CurrentHull; var patientHull2 = WayPoint.WayPointList.Find(wp => wp.IdCardDesc == "airlock").CurrentHull;
medBay = WayPoint.WayPointList.Find(wp => wp.IdCardDesc == "medbay").CurrentHull; medBay = WayPoint.WayPointList.Find(wp => wp.IdCardDesc == "medbay").CurrentHull;
var assistantInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: JobPrefab.Get("assistant")) var assistantInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: JobPrefab.Get("assistant".ToIdentifier()))
{ {
TeamID = CharacterTeamType.Team1 TeamID = CharacterTeamType.Team1
}; };
@@ -130,7 +130,7 @@ namespace Barotrauma.Tutorials
patient1.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 15.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, jobOrJobPrefab: JobPrefab.Get("assistant")) assistantInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: JobPrefab.Get("assistant".ToIdentifier()))
{ {
TeamID = CharacterTeamType.Team1 TeamID = CharacterTeamType.Team1
}; };
@@ -139,7 +139,7 @@ namespace Barotrauma.Tutorials
patient2.CanSpeak = false; patient2.CanSpeak = false;
patient2.AIController.Enabled = false; patient2.AIController.Enabled = false;
var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: JobPrefab.Get("engineer")) var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: JobPrefab.Get("engineer".ToIdentifier()))
{ {
TeamID = CharacterTeamType.Team1 TeamID = CharacterTeamType.Team1
}; };
@@ -148,13 +148,13 @@ namespace Barotrauma.Tutorials
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, jobOrJobPrefab: JobPrefab.Get("securityofficer")); var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: JobPrefab.Get("securityofficer".ToIdentifier()));
var subPatient2 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job?.Prefab, Submarine.MainSub).WorldPosition, "3"); var subPatient2 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job?.Prefab, Submarine.MainSub).WorldPosition, "3");
subPatient2.TeamID = CharacterTeamType.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, jobOrJobPrefab: JobPrefab.Get("engineer")) var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: JobPrefab.Get("engineer".ToIdentifier()))
{ {
TeamID = CharacterTeamType.Team1 TeamID = CharacterTeamType.Team1
}; };
@@ -262,7 +262,7 @@ namespace Barotrauma.Tutorials
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.SelectedItem == doctor_suppliesCabinet.Item)
{ {
for (int i = 0; i < doctor.Inventory.Capacity; i++) for (int i = 0; i < doctor.Inventory.Capacity; i++)
{ {
@@ -373,7 +373,7 @@ namespace Barotrauma.Tutorials
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.SelectedItem == doctor_medBayCabinet.Item)
{ {
for (int i = 0; i < doctor.Inventory.Capacity; i++) for (int i = 0; i < doctor.Inventory.Capacity; i++)
{ {
@@ -400,7 +400,7 @@ namespace Barotrauma.Tutorials
wait -= 0.1f; wait -= 0.1f;
engineer_reactor.AutoTemp = true; engineer_reactor.AutoTemp = true;
} while (wait > 0.0f); } while (wait > 0.0f);
engineer.SelectedConstruction = null; engineer.SelectedItem = null;
engineer_reactor.CanBeSelected = false; engineer_reactor.CanBeSelected = false;
RemoveCompletedObjective(2); RemoveCompletedObjective(2);
GameAnalyticsManager.AddDesignEvent("Tutorial:EngineerTutorial:Objective2"); GameAnalyticsManager.AddDesignEvent("Tutorial:EngineerTutorial:Objective2");
@@ -513,7 +513,7 @@ namespace Barotrauma.Tutorials
private bool IsSelectedItem(Item item) private bool IsSelectedItem(Item item)
{ {
return engineer?.SelectedConstruction == item; return engineer?.SelectedItem == item;
} }
private IEnumerable<CoroutineStatus> ReactorOperatedProperly() private IEnumerable<CoroutineStatus> ReactorOperatedProperly()
@@ -568,7 +568,7 @@ namespace Barotrauma.Tutorials
private void HandleJunctionBoxWiringHighlights() private void HandleJunctionBoxWiringHighlights()
{ {
Item selected = engineer.SelectedConstruction; Item selected = engineer.SelectedItem;
if (!engineer.HasEquippedItem("screwdriver".ToIdentifier())) if (!engineer.HasEquippedItem("screwdriver".ToIdentifier()))
{ {
@@ -440,7 +440,7 @@ namespace Barotrauma.Tutorials
bool gotSodium = false; bool gotSodium = false;
do do
{ {
if (mechanic.SelectedConstruction == mechanic_craftingCabinet.Item) if (mechanic.SelectedItem == mechanic_craftingCabinet.Item)
{ {
for (int i = 0; i < mechanic.Inventory.Capacity; i++) for (int i = 0; i < mechanic.Inventory.Capacity; i++)
{ {
@@ -702,7 +702,7 @@ namespace Barotrauma.Tutorials
private bool IsSelectedItem(Item item) private bool IsSelectedItem(Item item)
{ {
return mechanic?.SelectedConstruction == item; return mechanic?.SelectedItem == item;
} }
private bool WallHasDamagedSections(Structure wall) private bool WallHasDamagedSections(Structure wall)
@@ -510,7 +510,7 @@ namespace Barotrauma.Tutorials
private bool IsSelectedItem(Item item) private bool IsSelectedItem(Item item)
{ {
return officer?.SelectedConstruction == item; return officer?.SelectedItem == item;
} }
private Character SpawnMonster(string speciesName, Vector2 pos) private Character SpawnMonster(string speciesName, Vector2 pos)
@@ -14,6 +14,7 @@ namespace Barotrauma
public static bool IsTabMenuOpen => GameMain.GameSession?.tabMenu != null; public static bool IsTabMenuOpen => GameMain.GameSession?.tabMenu != null;
public static TabMenu TabMenuInstance => GameMain.GameSession?.tabMenu; public static TabMenu TabMenuInstance => GameMain.GameSession?.tabMenu;
private float prevHudScale;
private TabMenu tabMenu; private TabMenu tabMenu;
@@ -119,6 +120,7 @@ namespace Barotrauma
return true; return true;
} }
}; };
prevHudScale = GameSettings.CurrentConfig.Graphics.HUDScale;
} }
public void AddToGUIUpdateList() public void AddToGUIUpdateList()
@@ -178,6 +180,12 @@ namespace Barotrauma
} }
} }
public void HUDScaleChanged()
{
CreateTopLeftButtons();
GameMode?.HUDScaleChanged();
}
partial void UpdateProjSpecific(float deltaTime) partial void UpdateProjSpecific(float deltaTime)
{ {
if (GUI.DisableHUD) { return; } if (GUI.DisableHUD) { return; }
@@ -112,19 +112,19 @@ namespace Barotrauma
CheckReminders(); CheckReminders();
} }
public static void OnSetSelectedConstruction(Character character, Item oldConstruction, Item newConstruction) public static void OnSetSelectedItem(Character character, Item oldItem, Item newItem)
{ {
if (oldConstruction == newConstruction) { return; } if (oldItem == newItem) { return; }
if (Character.Controlled != null && Character.Controlled == character && oldConstruction != null && oldConstruction.GetComponent<Ladder>() == null) if (Character.Controlled != null && Character.Controlled == character && oldItem != null && !oldItem.IsLadder)
{ {
TimeStoppedInteracting = Timing.TotalTime; TimeStoppedInteracting = Timing.TotalTime;
} }
if (newConstruction == null) { return; } if (newItem == null) { return; }
if (newConstruction.GetComponent<Ladder>() != null) { return; } if (newItem.IsLadder) { return; }
if (newConstruction.GetComponent<ConnectionPanel>() is ConnectionPanel cp && cp.User == character) { return; } if (newItem.GetComponent<ConnectionPanel>() is ConnectionPanel cp && cp.User == character) { return; }
OnStartedInteracting(character, newConstruction); OnStartedInteracting(character, newItem);
} }
private static void OnStartedInteracting(Character character, Item item) private static void OnStartedInteracting(Character character, Item item)
@@ -177,10 +177,10 @@ namespace Barotrauma
private static void CheckIsInteracting() private static void CheckIsInteracting()
{ {
if (!CanDisplayHints()) { return; } if (!CanDisplayHints()) { return; }
if (Character.Controlled?.SelectedConstruction == null) { return; } if (Character.Controlled?.SelectedItem == null) { return; }
if (Character.Controlled.SelectedConstruction.GetComponent<Reactor>() is Reactor reactor && reactor.PowerOn && if (Character.Controlled.SelectedItem.GetComponent<Reactor>() is Reactor reactor && reactor.PowerOn &&
Character.Controlled.SelectedConstruction.OwnInventory?.AllItems is IEnumerable<Item> containedItems && Character.Controlled.SelectedItem.OwnInventory?.AllItems is IEnumerable<Item> containedItems &&
containedItems.Count(i => i.HasTag("reactorfuel")) > 1) containedItems.Count(i => i.HasTag("reactorfuel")) > 1)
{ {
if (DisplayHint("onisinteracting.reactorwithextrarods".ToIdentifier())) { return; } if (DisplayHint("onisinteracting.reactorwithextrarods".ToIdentifier())) { return; }
@@ -272,7 +272,7 @@ namespace Barotrauma
if (!CanDisplayHints()) { return; } if (!CanDisplayHints()) { return; }
if (sonar == null || sonar.Removed) { return; } if (sonar == null || sonar.Removed) { return; }
if (spottedCharacter == null || spottedCharacter.Removed || spottedCharacter.IsDead) { return; } if (spottedCharacter == null || spottedCharacter.Removed || spottedCharacter.IsDead) { return; }
if (Character.Controlled.SelectedConstruction != sonar) { return; } if (Character.Controlled.SelectedItem != sonar) { return; }
if (HumanAIController.IsFriendly(Character.Controlled, spottedCharacter)) { return; } if (HumanAIController.IsFriendly(Character.Controlled, spottedCharacter)) { return; }
DisplayHint("onsonarspottedenemy".ToIdentifier()); DisplayHint("onsonarspottedenemy".ToIdentifier());
} }
@@ -305,7 +305,7 @@ namespace Barotrauma
{ {
if (!CanDisplayHints()) { return; } if (!CanDisplayHints()) { return; }
if (character != Character.Controlled) { return; } if (character != Character.Controlled) { return; }
if (character.SelectedConstruction != null || character.FocusedItem != null) { return; } if (character.HasSelectedAnyItem || character.FocusedItem != null) { return; }
if (item == null || !item.IsShootable || !item.RequireAimToUse) { return; } if (item == null || !item.IsShootable || !item.RequireAimToUse) { return; }
if (TimeStoppedInteracting + 1 > Timing.TotalTime) { return; } if (TimeStoppedInteracting + 1 > Timing.TotalTime) { return; }
if (GUI.MouseOn != null) { return; } if (GUI.MouseOn != null) { return; }
@@ -317,7 +317,7 @@ namespace Barotrauma
variables: new[] { ("[key]".ToIdentifier(), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Aim)) }, variables: new[] { ("[key]".ToIdentifier(), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Aim)) },
onUpdate: () => onUpdate: () =>
{ {
if (character.SelectedConstruction == null && GUI.MouseOn == null && PlayerInput.KeyDown(InputType.Aim)) if (character.SelectedItem == null && GUI.MouseOn == null && PlayerInput.KeyDown(InputType.Aim))
{ {
ActiveHintMessageBox.Close(); ActiveHintMessageBox.Close();
} }
@@ -562,7 +562,7 @@ namespace Barotrauma
break; break;
} }
//if putting an item to a container with a max stack size of 1, only put one item from the stack //if putting an item to a container with a max stack size of 1, only put one item from the stack
if (quickUseAction == QuickUseAction.PutToContainer && (character.SelectedConstruction?.GetComponent<ItemContainer>()?.MaxStackSize ?? 0) <= 1) if (quickUseAction == QuickUseAction.PutToContainer && (character.SelectedItem?.GetComponent<ItemContainer>()?.MaxStackSize ?? 0) <= 1)
{ {
break; break;
} }
@@ -595,14 +595,14 @@ namespace Barotrauma
if (rootInventory != null && if (rootInventory != null &&
rootInventory.Owner != Character.Controlled && rootInventory.Owner != Character.Controlled &&
rootInventory.Owner != Character.Controlled.SelectedConstruction && rootInventory.Owner != Character.Controlled.SelectedItem &&
rootInventory.Owner != Character.Controlled.SelectedCharacter) rootInventory.Owner != Character.Controlled.SelectedCharacter)
{ {
//allow interacting if the container is linked to the item the character is interacting with //allow interacting if the container is linked to the item the character is interacting with
if (!(rootContainer != null && if (!(rootContainer != null &&
rootContainer.DisplaySideBySideWhenLinked && rootContainer.DisplaySideBySideWhenLinked &&
Character.Controlled.SelectedConstruction != null && Character.Controlled.SelectedItem != null &&
rootContainer.linkedTo.Contains(Character.Controlled.SelectedConstruction))) rootContainer.linkedTo.Contains(Character.Controlled.SelectedItem)))
{ {
DraggingItems.Clear(); DraggingItems.Clear();
} }
@@ -756,7 +756,7 @@ namespace Barotrauma
} }
else else
{ {
var selectedContainer = character.SelectedConstruction?.GetComponent<ItemContainer>(); var selectedContainer = character.SelectedItem?.GetComponent<ItemContainer>();
if (selectedContainer != null && if (selectedContainer != null &&
selectedContainer.Inventory != null && selectedContainer.Inventory != null &&
!selectedContainer.Inventory.Locked) !selectedContainer.Inventory.Locked)
@@ -775,7 +775,8 @@ namespace Barotrauma
else else
{ {
bool isEquippable = item.AllowedSlots.Any(s => s != InvSlotType.Any); bool isEquippable = item.AllowedSlots.Any(s => s != InvSlotType.Any);
var selectedContainer = character.SelectedConstruction?.GetComponent<ItemContainer>(); var selectedContainer = character.SelectedItem?.GetComponent<ItemContainer>();
if (selectedContainer != null && if (selectedContainer != null &&
selectedContainer.Inventory != null && selectedContainer.Inventory != null &&
!selectedContainer.Inventory.Locked && !selectedContainer.Inventory.Locked &&
@@ -930,7 +931,7 @@ namespace Barotrauma
} }
break; break;
case QuickUseAction.PutToContainer: case QuickUseAction.PutToContainer:
var selectedContainer = character.SelectedConstruction?.GetComponent<ItemContainer>(); var selectedContainer = character.SelectedItem?.GetComponent<ItemContainer>();
if (selectedContainer != null && selectedContainer.Inventory != null) if (selectedContainer != null && selectedContainer.Inventory != null)
{ {
//player has selected the inventory of another item -> attempt to move the item there //player has selected the inventory of another item -> attempt to move the item there
@@ -965,8 +966,8 @@ namespace Barotrauma
} }
break; break;
case QuickUseAction.PutToEquippedItem: case QuickUseAction.PutToEquippedItem:
//order by the condition of the contained item to prefer putting into the item with the emptiest ammo/battery/tank
foreach (Item heldItem in character.HeldItems) foreach (Item heldItem in character.HeldItems.OrderBy(it => it.ContainedItems.FirstOrDefault()?.Condition ?? 0.0f))
{ {
if (heldItem.OwnInventory == null) { continue; } if (heldItem.OwnInventory == null) { continue; }
//don't allow swapping if we're moving items into an item with 1 slot holding a stack of items //don't allow swapping if we're moving items into an item with 1 slot holding a stack of items
@@ -24,10 +24,9 @@ namespace Barotrauma.Items.Components
public void ExtractJobPrefab(IReadOnlyDictionary<Identifier, string> tags) public void ExtractJobPrefab(IReadOnlyDictionary<Identifier, string> tags)
{ {
if (!tags.TryGetValue("jobid".ToIdentifier(), out string jobId)) { return; } if (!tags.TryGetValue("jobid".ToIdentifier(), out string jobId)) { return; }
if (!jobId.IsNullOrEmpty()) if (!jobId.IsNullOrEmpty())
{ {
JobPrefab = JobPrefab.Get(jobId); JobPrefab = JobPrefab.Get(jobId.ToIdentifier());
} }
} }
@@ -522,13 +522,11 @@ namespace Barotrauma.Items.Components
if (soundSelectionModes == null) soundSelectionModes = new Dictionary<ActionType, SoundSelectionMode>(); if (soundSelectionModes == null) soundSelectionModes = new Dictionary<ActionType, SoundSelectionMode>();
if (!soundSelectionModes.ContainsKey(type) || soundSelectionModes[type] == SoundSelectionMode.Random) if (!soundSelectionModes.ContainsKey(type) || soundSelectionModes[type] == SoundSelectionMode.Random)
{ {
SoundSelectionMode selectionMode = SoundSelectionMode.Random; Enum.TryParse(subElement.GetAttributeString("selectionmode", "Random"), out SoundSelectionMode selectionMode);
Enum.TryParse(subElement.GetAttributeString("selectionmode", "Random"), out selectionMode);
soundSelectionModes[type] = selectionMode; soundSelectionModes[type] = selectionMode;
} }
List<ItemSound> soundList = null; if (!sounds.TryGetValue(itemSound.Type, out List<ItemSound> soundList))
if (!sounds.TryGetValue(itemSound.Type, out soundList))
{ {
soundList = new List<ItemSound>(); soundList = new List<ItemSound>();
sounds.Add(itemSound.Type, soundList); sounds.Add(itemSound.Type, soundList);
@@ -566,6 +564,9 @@ namespace Barotrauma.Items.Components
} }
string style = GuiFrameSource.Attribute("style") == null ? null : GuiFrameSource.GetAttributeString("style", ""); string style = GuiFrameSource.Attribute("style") == null ? null : GuiFrameSource.GetAttributeString("style", "");
GuiFrame = new GUIFrame(RectTransform.Load(GuiFrameSource, GUI.Canvas, Anchor.Center), style, color); GuiFrame = new GUIFrame(RectTransform.Load(GuiFrameSource, GUI.Canvas, Anchor.Center), style, color);
TryCreateDragHandle();
DefaultLayout = GUILayoutSettings.Load(GuiFrameSource); DefaultLayout = GUILayoutSettings.Load(GuiFrameSource);
if (GuiFrame != null) if (GuiFrame != null)
{ {
@@ -574,6 +575,22 @@ namespace Barotrauma.Items.Components
GameMain.Instance.ResolutionChanged += OnResolutionChanged; GameMain.Instance.ResolutionChanged += OnResolutionChanged;
} }
protected void TryCreateDragHandle()
{
if (GuiFrame != null && GuiFrameSource.GetAttributeBool("draggable", true))
{
var handle = new GUIDragHandle(new RectTransform(Vector2.One, GuiFrame.RectTransform, Anchor.Center),
GuiFrame.RectTransform, style: null)
{
DragArea = HUDLayoutSettings.ItemHUDArea
};
int iconHeight = GUIStyle.ItemFrameMargin.Y / 4;
new GUIImage(new RectTransform(new Point(GuiFrame.Rect.Width, iconHeight), handle.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, iconHeight / 2) },
style: "GUIDragIndicatorHorizontal");
}
}
/// <summary> /// <summary>
/// Overload this method and implement. The method is automatically called when the resolution changes. /// Overload this method and implement. The method is automatically called when the resolution changes.
/// </summary> /// </summary>
@@ -189,7 +189,7 @@ namespace Barotrauma.Items.Components
onDraw: (SpriteBatch spriteBatch, GUICustomComponent component) => { Inventory.Draw(spriteBatch); }, onDraw: (SpriteBatch spriteBatch, GUICustomComponent component) => { Inventory.Draw(spriteBatch); },
onUpdate: null) onUpdate: null)
{ {
CanBeFocused = false CanBeFocused = true
}; };
// Expand the frame vertically if it's too small to fit the text // Expand the frame vertically if it's too small to fit the text
@@ -381,9 +381,14 @@ namespace Barotrauma.Items.Components
guiCustomComponent.RectTransform.Parent = Inventory.RectTransform; guiCustomComponent.RectTransform.Parent = Inventory.RectTransform;
} }
if (item.ParentInventory?.Owner == character && character.SelectedItem == item)
{
character.SelectedItem = null;
}
//if the item is in the character's inventory, no need to update the item's inventory //if the item is in the character's inventory, no need to update the item's inventory
//because the player can see it by hovering the cursor over the item //because the player can see it by hovering the cursor over the item
guiCustomComponent.Visible = item.ParentInventory?.Owner != character && DrawInventory; guiCustomComponent.Visible = DrawInventory && item.ParentInventory?.Owner != character;
if (!guiCustomComponent.Visible) { return; } if (!guiCustomComponent.Visible) { return; }
Inventory.Update(deltaTime, cam); Inventory.Update(deltaTime, cam);
@@ -1,4 +1,5 @@
using Barotrauma.Extensions; using System.Collections.Generic;
using System.Collections.Immutable;
using Barotrauma.Networking; using Barotrauma.Networking;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
@@ -15,6 +16,7 @@ namespace Barotrauma.Items.Components
} }
private GUIButton activateButton; private GUIButton activateButton;
private GUIComponent inputInventoryHolder, outputInventoryHolder; private GUIComponent inputInventoryHolder, outputInventoryHolder;
private GUIListBox outputDisplayListBox;
private GUIComponent inSufficientPowerWarning; private GUIComponent inSufficientPowerWarning;
@@ -44,29 +46,40 @@ namespace Barotrauma.Items.Components
protected override void CreateGUI() protected override void CreateGUI()
{ {
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.90f, 0.80f), GuiFrame.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter) var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.88f), GuiFrame.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter)
{ {
Stretch = true, Stretch = true,
RelativeSpacing = 0.08f RelativeSpacing = 0.08f
}; };
new GUITextBlock(new RectTransform(new Vector2(1f, 0.07f), paddedFrame.RectTransform), item.Name, font: GUIStyle.SubHeadingFont) new GUITextBlock(new RectTransform(new Vector2(1f, 0.07f), paddedFrame.RectTransform) { MinSize = new Point(0, GUI.IntScale(25)) }, item.Name, font: GUIStyle.SubHeadingFont)
{ {
TextAlignment = Alignment.Center, TextAlignment = Alignment.Center,
AutoScaleHorizontal = true AutoScaleHorizontal = true
}; };
var topFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.5f), paddedFrame.RectTransform), style: null); var topFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.375f), paddedFrame.RectTransform), style: null);
// === INPUT LABEL === // // === INPUT LABEL === //
var inputLabelArea = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.15f), topFrame.RectTransform, Anchor.TopCenter), childAnchor: Anchor.CenterLeft, isHorizontal: true) var inputLabelArea = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.15f), topFrame.RectTransform, Anchor.TopCenter), childAnchor: Anchor.CenterLeft, isHorizontal: true);
{
Stretch = true, var queueLabelLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.43f, 1f), inputLabelArea.RectTransform), childAnchor: Anchor.CenterLeft, isHorizontal: true)
RelativeSpacing = 0.05f {
}; Stretch = true,
var inputLabel = new GUITextBlock(new RectTransform(Vector2.One, inputLabelArea.RectTransform), TextManager.Get("deconstructor.input", "uilabel.input"), font: GUIStyle.SubHeadingFont) { Padding = Vector4.Zero }; RelativeSpacing = 0.05f
inputLabel.RectTransform.Resize(new Point((int) inputLabel.Font.MeasureString(inputLabel.Text).X, inputLabel.RectTransform.Rect.Height)); };
new GUIFrame(new RectTransform(Vector2.One, inputLabelArea.RectTransform), style: "HorizontalLine"); var queueLabel = new GUITextBlock(new RectTransform(Vector2.One, queueLabelLayout.RectTransform), TextManager.Get("deconstructor.inputqueue"), font: GUIStyle.SubHeadingFont) { Padding = Vector4.Zero };
queueLabel.RectTransform.Resize(new Point((int) queueLabel.Font.MeasureString(queueLabel.Text).X, queueLabel.RectTransform.Rect.Height));
new GUIFrame(new RectTransform(Vector2.One, queueLabelLayout.RectTransform), style: "HorizontalLine");
var inputLabelLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.57f, 1f), inputLabelArea.RectTransform), childAnchor: Anchor.CenterLeft, isHorizontal: true)
{
Stretch = true,
RelativeSpacing = 0.05f
};
var inputLabel = new GUITextBlock(new RectTransform(Vector2.One, inputLabelLayout.RectTransform), TextManager.Get("deconstructor.input", "uilabel.input"), font: GUIStyle.SubHeadingFont) { Padding = Vector4.Zero };
inputLabel.RectTransform.Resize(new Point((int) inputLabel.Font.MeasureString(inputLabel.Text).X, inputLabel.RectTransform.Rect.Height));
new GUIFrame(new RectTransform(Vector2.One, inputLabelLayout.RectTransform), style: "HorizontalLine");
var inputArea = new GUILayoutGroup(new RectTransform(new Vector2(1f, 1f), topFrame.RectTransform, Anchor.CenterLeft), childAnchor: Anchor.BottomLeft, isHorizontal: true) { Stretch = true, RelativeSpacing = 0.05f }; var inputArea = new GUILayoutGroup(new RectTransform(new Vector2(1f, 1f), topFrame.RectTransform, Anchor.CenterLeft), childAnchor: Anchor.BottomLeft, isHorizontal: true) { Stretch = true, RelativeSpacing = 0.05f };
@@ -92,7 +105,7 @@ namespace Barotrauma.Items.Components
}; };
// === OUTPUT AREA === // // === OUTPUT AREA === //
var bottomFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.5f), paddedFrame.RectTransform), style: null); var bottomFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.375f), paddedFrame.RectTransform), style: null);
// === OUTPUT LABEL === // // === OUTPUT LABEL === //
var outputLabelArea = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.15f), bottomFrame.RectTransform, Anchor.TopCenter), childAnchor: Anchor.CenterLeft, isHorizontal: true) var outputLabelArea = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.15f), bottomFrame.RectTransform, Anchor.TopCenter), childAnchor: Anchor.CenterLeft, isHorizontal: true)
@@ -104,10 +117,16 @@ namespace Barotrauma.Items.Components
outputLabel.RectTransform.Resize(new Point((int) outputLabel.Font.MeasureString(outputLabel.Text).X, outputLabel.RectTransform.Rect.Height)); outputLabel.RectTransform.Resize(new Point((int) outputLabel.Font.MeasureString(outputLabel.Text).X, outputLabel.RectTransform.Rect.Height));
new GUIFrame(new RectTransform(Vector2.One, outputLabelArea.RectTransform), style: "HorizontalLine"); new GUIFrame(new RectTransform(Vector2.One, outputLabelArea.RectTransform), style: "HorizontalLine");
var outputArea = new GUILayoutGroup(new RectTransform(new Vector2(1f, 1f), bottomFrame.RectTransform, Anchor.CenterLeft), childAnchor: Anchor.BottomLeft, isHorizontal: true) { Stretch = true, RelativeSpacing = 0.05f }; var outputArea = new GUILayoutGroup(new RectTransform(new Vector2(1f, 1f), bottomFrame.RectTransform, Anchor.CenterLeft), childAnchor: Anchor.BottomLeft, isHorizontal: true) { Stretch = true, RelativeSpacing = 0.05f };
// === OUTPUT SLOTS === // // === OUTPUT SLOTS === //
outputInventoryHolder = new GUIFrame(new RectTransform(new Vector2(1f - InfoAreaWidth, 1f), outputArea.RectTransform, Anchor.CenterLeft), style: null); outputInventoryHolder = new GUIFrame(new RectTransform(new Vector2(1f - InfoAreaWidth, 1f), outputArea.RectTransform, Anchor.CenterLeft), style: null);
GUILayoutGroup outputDisplayLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.25f), paddedFrame.RectTransform), childAnchor: Anchor.TopCenter);
GUILayoutGroup outDisplayTopGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.2f), outputDisplayLayout.RectTransform), isHorizontal: true);
GUITextBlock outDisplayBlock = new GUITextBlock(new RectTransform(Vector2.One, outDisplayTopGroup.RectTransform), TextManager.Get("deconstructor.output"), font: GUIStyle.SubHeadingFont) { Padding = Vector4.Zero };
GUILayoutGroup outDisplayBottomGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.975f, 0.8f), outputDisplayLayout.RectTransform), isHorizontal: true);
outputDisplayListBox = new GUIListBox(new RectTransform(new Vector2(1f, 1f), outDisplayBottomGroup.RectTransform), isHorizontal: true, style: null);
if (InfoAreaWidth >= 0.0f) if (InfoAreaWidth >= 0.0f)
{ {
@@ -195,6 +214,170 @@ namespace Barotrauma.Items.Components
return base.Select(character); return base.Select(character);
} }
partial void OnItemSlotsChanged(ItemContainer container)
{
if (container.Inventory is null) { return; }
RefreshOutputDisplay(container.Inventory.AllItems.ToImmutableArray());
}
private void RefreshOutputDisplay(ImmutableArray<Item> items)
{
const string outputItemCountUserData = "OutputItemCount";
const string questionMarkUserData = "UnknownItemOutput";
if (outputDisplayListBox is null || inputContainer.Inventory is null) { return; }
Dictionary<Identifier, int> itemCounts = new Dictionary<Identifier, int>();
Dictionary<Identifier, GUIComponent> children = new Dictionary<Identifier, GUIComponent>();
bool addQuestionMark = false;
foreach (GUIComponent child in outputDisplayListBox.Content.Children)
{
if (child.UserData is Identifier it)
{
children.Add(it, child);
}
}
if (outputDisplayListBox.Content.FindChild(questionMarkUserData) is { } foundChild)
{
outputDisplayListBox.RemoveChild(foundChild);
}
foreach (Item it in items)
{
if (it.Prefab.RandomDeconstructionOutput)
{
addQuestionMark = true;
continue;
}
foreach (DeconstructItem deconstructItem in it.Prefab.DeconstructItems)
{
RegisterItem(deconstructItem.ItemIdentifier);
}
if (it.OwnInventory is { } inventory)
{
foreach (Item inventoryItems in inventory.AllItems)
{
RegisterItem(inventoryItems.Prefab.Identifier);
}
}
void RegisterItem(Identifier identifier)
{
if (itemCounts.ContainsKey(identifier))
{
itemCounts[identifier]++;
return;
}
itemCounts.Add(identifier, 1);
}
}
foreach (var (it, child) in children)
{
if (!itemCounts.ContainsKey(it))
{
outputDisplayListBox.RemoveChild(child);
}
}
foreach (var (it, amount) in itemCounts)
{
if (!children.TryGetValue(it, out GUIComponent child))
{
child = CreateOutputDisplayItem(it, outputDisplayListBox.Content);
}
if (child is null) { continue; }
UpdateOutputDisplayItemCount(child, amount);
}
if (addQuestionMark)
{
CreateQuestionMark(outputDisplayListBox.Content);
}
static void CreateQuestionMark(GUIComponent parent)
{
GUIFrame itemFrame = new GUIFrame(new RectTransform(new Vector2(0.1f, 1f), parent.RectTransform), style: null)
{
UserData = questionMarkUserData,
ToolTip = TextManager.Get("deconstructor.unknownitemsoutput")
};
GUIFrame questionMarkFrame = new GUIFrame(new RectTransform(Vector2.One, itemFrame.RectTransform, scaleBasis: ScaleBasis.Smallest, anchor: Anchor.Center), style: "GUIFrameListBox")
{
CanBeFocused = false,
};
// question mark text
new GUITextBlock(new RectTransform(Vector2.One, questionMarkFrame.RectTransform, anchor: Anchor.Center), text: "?", textAlignment: Alignment.Center, font: GUIStyle.LargeFont)
{
CanBeFocused = false
};
}
static GUIComponent CreateOutputDisplayItem(Identifier identifier, GUIComponent parent)
{
ItemPrefab prefab = ItemPrefab.Find(null, identifier);
if (prefab is null) { return null; }
GUIFrame itemFrame = new GUIFrame(new RectTransform(new Vector2(0.1f, 1f), parent.RectTransform), style: null)
{
UserData = identifier,
ToolTip = GetTooltip(prefab)
};
Sprite icon = prefab.InventoryIcon ?? prefab.Sprite;
Color iconColor = prefab.InventoryIcon is null ? prefab.SpriteColor : prefab.InventoryIconColor;
GUIImage itemIcon = new GUIImage(new RectTransform(Vector2.One, itemFrame.RectTransform, scaleBasis: ScaleBasis.Smallest, anchor: Anchor.Center), sprite: icon, scaleToFit: true)
{
Color = iconColor,
CanBeFocused = false
};
// item count text
new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.5f), itemIcon.RectTransform, anchor: Anchor.BottomRight), "", font: GUIStyle.Font, textAlignment: Alignment.BottomRight)
{
UserData = outputItemCountUserData,
Shadow = true,
CanBeFocused = false,
Padding = Vector4.Zero,
TextColor = Color.White,
};
return itemFrame;
}
static void UpdateOutputDisplayItemCount(GUIComponent component, int count)
{
if (!(component.FindChild(outputItemCountUserData, recursive: true) is GUITextBlock textBlock)) { return; }
textBlock.Text = TextManager.GetWithVariable("campaignstore.quantity", "[amount]", count.ToString());
}
static RichString GetTooltip(ItemPrefab prefab)
{
LocalizedString toolTip = $"‖color:{Color.White.ToStringHex()}‖{prefab.Name}‖color:end‖";
LocalizedString description = prefab.Description;
if (!description.IsNullOrEmpty()) { toolTip += '\n' + description; }
if (prefab.ContentPackage != GameMain.VanillaContent && prefab.ContentPackage != null)
{
toolTip += $"\n‖color:{Color.MediumPurple.ToStringHex()}‖{prefab.ContentPackage.Name}‖color:end‖";
}
return RichString.Rich(toolTip);
}
}
partial void OnItemLoadedProjSpecific() partial void OnItemLoadedProjSpecific()
{ {
inputContainer.AllowUIOverlap = true; inputContainer.AllowUIOverlap = true;
@@ -246,6 +246,7 @@ namespace Barotrauma.Items.Components
protected override void CreateGUI() protected override void CreateGUI()
{ {
GuiFrame.ClearChildren(); GuiFrame.ClearChildren();
TryCreateDragHandle();
GuiFrame.RectTransform.RelativeOffset = new Vector2(0.05f, 0.0f); GuiFrame.RectTransform.RelativeOffset = new Vector2(0.05f, 0.0f);
GuiFrame.CanBeFocused = true; GuiFrame.CanBeFocused = true;
@@ -25,7 +25,7 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam) public override void Update(float deltaTime, Camera cam)
{ {
if (Character.Controlled?.SelectedConstruction != item) if (Character.Controlled?.SelectedItem != item)
{ {
IsActive = false; IsActive = false;
return; return;
@@ -871,13 +871,11 @@ namespace Barotrauma.Items.Components
posToMaintain = item.Submarine.WorldPosition; posToMaintain = item.Submarine.WorldPosition;
} }
MaintainPos = true; MaintainPos = true;
if (userdata is Vector2) if (userdata is Vector2 nudgeAmount)
{ {
Sonar sonar = item.GetComponent<Sonar>(); if (item.GetComponent<Sonar>() is Sonar sonar)
Vector2 nudgeAmount = (Vector2)userdata;
if (sonar != null)
{ {
nudgeAmount *= sonar == null ? 500.0f : 500.0f / sonar.Zoom; nudgeAmount *= 500.0f / sonar.Zoom;
} }
PosToMaintain += nudgeAmount; PosToMaintain += nudgeAmount;
} }
@@ -59,7 +59,7 @@ namespace Barotrauma.Items.Components
public override bool ShouldDrawHUD(Character character) public override bool ShouldDrawHUD(Character character)
{ {
if (item.HiddenInGame) { return false; } if (item.HiddenInGame) { return false; }
if (!HasRequiredItems(character, false) || character.SelectedConstruction != item) { return false; } if (!HasRequiredItems(character, false) || character.SelectedItem != item) { return false; }
if (character.IsTraitor && item.ConditionPercentage > MinSabotageCondition) { return true; } if (character.IsTraitor && item.ConditionPercentage > MinSabotageCondition) { return true; }
float defaultMaxCondition = item.MaxCondition / item.MaxRepairConditionMultiplier; float defaultMaxCondition = item.MaxCondition / item.MaxRepairConditionMultiplier;
@@ -110,6 +110,7 @@ namespace Barotrauma.Items.Components
if (GuiFrame != null) if (GuiFrame != null)
{ {
GuiFrame.ClearChildren(); GuiFrame.ClearChildren();
TryCreateDragHandle();
CreateGUI(); CreateGUI();
} }
} }
@@ -265,7 +266,7 @@ namespace Barotrauma.Items.Components
} }
} }
if (CurrentFixer != null && CurrentFixer.SelectedConstruction == item) if (CurrentFixer != null && CurrentFixer.SelectedItem == item)
{ {
if (repairSoundChannel == null || !repairSoundChannel.IsPlaying) if (repairSoundChannel == null || !repairSoundChannel.IsPlaying)
{ {
@@ -33,10 +33,16 @@ namespace Barotrauma.Items.Components
originalMaxSize = GuiFrame.RectTransform.MaxSize; originalMaxSize = GuiFrame.RectTransform.MaxSize;
originalRelativeSize = GuiFrame.RectTransform.RelativeSize; originalRelativeSize = GuiFrame.RectTransform.RelativeSize;
CheckForLabelOverlap(); CheckForLabelOverlap();
new GUICustomComponent(new RectTransform(Vector2.One, GuiFrame.RectTransform), DrawConnections, null) var content = new GUICustomComponent(new RectTransform(Vector2.One, GuiFrame.RectTransform), DrawConnections, null)
{ {
UserData = this UserData = this
}; };
content.RectTransform.SetAsFirstChild();
//prevents inputs from going through the GUICustomComponent to the drag handle
var blocker = new GUIFrame(new RectTransform(GuiFrame.Rect.Size - GUIStyle.ItemFrameMargin, GuiFrame.RectTransform, Anchor.Center)
{ AbsoluteOffset = GUIStyle.ItemFrameOffset },
style: null);
} }
public void TriggerRewiringSound() public void TriggerRewiringSound()
@@ -62,7 +68,7 @@ namespace Barotrauma.Items.Components
} }
rewireSoundTimer -= deltaTime; rewireSoundTimer -= deltaTime;
if (user != null && user.SelectedConstruction == item && rewireSoundTimer > 0.0f) if (user != null && user.SelectedItem == item && rewireSoundTimer > 0.0f)
{ {
if (rewireSoundChannel == null || !rewireSoundChannel.IsPlaying) if (rewireSoundChannel == null || !rewireSoundChannel.IsPlaying)
{ {
@@ -85,12 +91,12 @@ namespace Barotrauma.Items.Components
public override bool ShouldDrawHUD(Character character) public override bool ShouldDrawHUD(Character character)
{ {
return character == Character.Controlled && character == user && character.SelectedConstruction == item; return character == Character.Controlled && character == user && character.SelectedItem == item;
} }
public override void UpdateHUD(Character character, float deltaTime, Camera cam) public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{ {
if (character != Character.Controlled || character != user || character.SelectedConstruction != item) { return; } if (character != Character.Controlled || character != user || character.SelectedItem != item) { return; }
if (HighlightedWire != null) if (HighlightedWire != null)
{ {
@@ -311,7 +311,7 @@ namespace Barotrauma.Items.Components
Wire equippedWire = Character.Controlled.HeldItems.FirstOrDefault(it => it.GetComponent<Wire>() != null)?.GetComponent<Wire>(); Wire equippedWire = Character.Controlled.HeldItems.FirstOrDefault(it => it.GetComponent<Wire>() != null)?.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.SelectedItem == null)
{ {
equippedWire.Use(1.0f, Character.Controlled); equippedWire.Use(1.0f, Character.Controlled);
} }
@@ -364,7 +364,7 @@ namespace Barotrauma
get get
{ {
return Character.Controlled != null && return Character.Controlled != null &&
Character.Controlled.SelectedConstruction == null && !Character.Controlled.HasSelectedAnyItem &&
CharacterHealth.OpenHealthWindow == null && CharacterHealth.OpenHealthWindow == null &&
DraggingItems.Any(); DraggingItems.Any();
} }
@@ -924,9 +924,9 @@ namespace Barotrauma
} }
} }
if (Character.Controlled.SelectedConstruction != null) if (Character.Controlled.SelectedItem != null)
{ {
foreach (var ic in Character.Controlled.SelectedConstruction.ActiveHUDs) foreach (var ic in Character.Controlled.SelectedItem.ActiveHUDs)
{ {
var itemContainer = ic as ItemContainer; var itemContainer = ic as ItemContainer;
if (itemContainer?.Inventory?.visualSlots == null) { continue; } if (itemContainer?.Inventory?.visualSlots == null) { continue; }
@@ -1003,9 +1003,9 @@ namespace Barotrauma
} }
} }
if (character.SelectedConstruction != null) if (character.SelectedItem != null)
{ {
foreach (var ic in character.SelectedConstruction.ActiveHUDs) foreach (var ic in character.SelectedItem.ActiveHUDs)
{ {
var itemContainer = ic as ItemContainer; var itemContainer = ic as ItemContainer;
if (itemContainer?.Inventory?.visualSlots == null) { continue; } if (itemContainer?.Inventory?.visualSlots == null) { continue; }
@@ -1341,27 +1341,29 @@ namespace Barotrauma
} }
else else
{ {
var rootOwner = (selectedSlot.ParentInventory?.Owner as Item)?.GetRootInventoryOwner(); static bool OwnerInaccessible(Entity owner) =>
if (selectedSlot.ParentInventory?.Owner != Character.Controlled && owner != Character.Controlled &&
selectedSlot.ParentInventory?.Owner != Character.Controlled.SelectedCharacter && owner != Character.Controlled.SelectedCharacter &&
selectedSlot.ParentInventory?.Owner != Character.Controlled.SelectedConstruction && owner != Character.Controlled.SelectedItem &&
!(Character.Controlled.SelectedConstruction?.linkedTo.Contains(selectedSlot.ParentInventory?.Owner) ?? false) && (Character.Controlled.SelectedItem == null || !Character.Controlled.SelectedItem.linkedTo.Contains(owner));
rootOwner != Character.Controlled &&
rootOwner != Character.Controlled.SelectedCharacter && Entity owner = selectedSlot.ParentInventory?.Owner;
rootOwner != Character.Controlled.SelectedConstruction && Entity rootOwner = (owner as Item)?.GetRootInventoryOwner();
!(Character.Controlled.SelectedConstruction?.linkedTo.Contains(rootOwner) ?? false)) if (OwnerInaccessible(owner) && (rootOwner == owner || OwnerInaccessible(rootOwner)))
{ {
return false; return false;
} }
var parentItem = (selectedSlot?.ParentInventory?.Owner as Item) ?? selectedSlot?.Item; Item parentItem = (owner as Item) ?? selectedSlot?.Item;
if ((parentItem?.GetRootInventoryOwner() is Character ownerCharacter) && if (parentItem?.GetRootInventoryOwner() is Character ownerCharacter)
ownerCharacter == Character.Controlled &&
CharacterHealth.OpenHealthWindow?.Character != ownerCharacter &&
ownerCharacter.Inventory.IsInLimbSlot(parentItem, InvSlotType.HealthInterface) &&
Screen.Selected != GameMain.SubEditorScreen)
{ {
highlightedSubInventorySlots.RemoveWhere(s => s.Item == parentItem); if (ownerCharacter == Character.Controlled &&
return false; CharacterHealth.OpenHealthWindow?.Character != ownerCharacter &&
ownerCharacter.Inventory.IsInLimbSlot(parentItem, InvSlotType.HealthInterface) &&
Screen.Selected != GameMain.SubEditorScreen)
{
highlightedSubInventorySlots.RemoveWhere(s => s.Item == parentItem);
return false;
}
} }
} }
return true; return true;
@@ -1725,7 +1727,8 @@ namespace Barotrauma
if (inventory != null && if (inventory != null &&
!inventory.Locked && !inventory.Locked &&
Character.Controlled?.Inventory == inventory && Character.Controlled?.Inventory == inventory &&
slot.InventoryKeyIndex != -1) slot.InventoryKeyIndex != -1 &&
slot.InventoryKeyIndex < GameSettings.CurrentConfig.InventoryKeyMap.Bindings.Length)
{ {
spriteBatch.Draw(slotHotkeySprite.Texture, rect.ScaleSize(1.15f), slotHotkeySprite.SourceRect, slotColor); spriteBatch.Draw(slotHotkeySprite.Texture, rect.ScaleSize(1.15f), slotHotkeySprite.SourceRect, slotColor);
GUI.DrawString(spriteBatch, rect.Location.ToVector2() + new Vector2((int)(4.25f * UIScale), (int)Math.Ceiling(-1.5f * UIScale)), GameSettings.CurrentConfig.InventoryKeyMap.Bindings[slot.InventoryKeyIndex].Name, Color.Black, font: GUIStyle.HotkeyFont); GUI.DrawString(spriteBatch, rect.Location.ToVector2() + new Vector2((int)(4.25f * UIScale), (int)Math.Ceiling(-1.5f * UIScale)), GameSettings.CurrentConfig.InventoryKeyMap.Bindings[slot.InventoryKeyIndex].Name, Color.Black, font: GUIStyle.HotkeyFont);
@@ -982,7 +982,7 @@ namespace Barotrauma
List<GUIComponent> elementsToMove = new List<GUIComponent>(); List<GUIComponent> elementsToMove = new List<GUIComponent>();
if (editingHUD != null && editingHUD.UserData == this && if (editingHUD != null && editingHUD.UserData == this &&
((HasInGameEditableProperties && Character.Controlled?.SelectedConstruction == this) || Screen.Selected == GameMain.SubEditorScreen)) ((HasInGameEditableProperties && Character.Controlled?.SelectedItem == this) || Screen.Selected == GameMain.SubEditorScreen))
{ {
elementsToMove.Add(editingHUD); elementsToMove.Add(editingHUD);
} }
@@ -1042,7 +1042,7 @@ namespace Barotrauma
public void UpdateHUD(Camera cam, Character character, float deltaTime) public void UpdateHUD(Camera cam, Character character, float deltaTime)
{ {
bool editingHUDCreated = false; bool editingHUDCreated = false;
if ((HasInGameEditableProperties && (character.SelectedConstruction == this || EditableWhenEquipped)) || if ((HasInGameEditableProperties && (character.SelectedItem == this || EditableWhenEquipped)) ||
Screen.Selected == GameMain.SubEditorScreen) Screen.Selected == GameMain.SubEditorScreen)
{ {
GUIComponent prevEditingHUD = editingHUD; GUIComponent prevEditingHUD = editingHUD;
@@ -1126,7 +1126,7 @@ namespace Barotrauma
foreach (Character otherCharacter in Character.CharacterList) foreach (Character otherCharacter in Character.CharacterList)
{ {
if (otherCharacter != character && if (otherCharacter != character &&
otherCharacter.SelectedConstruction == this) otherCharacter.SelectedItem == this)
{ {
ItemInUseWarning.Visible = true; ItemInUseWarning.Visible = true;
if (mergedHUDRect.Width > GameMain.GraphicsWidth / 2) { mergedHUDRect.Inflate(-GameMain.GraphicsWidth / 4, 0); } if (mergedHUDRect.Width > GameMain.GraphicsWidth / 2) { mergedHUDRect.Inflate(-GameMain.GraphicsWidth / 4, 0); }
@@ -1145,7 +1145,7 @@ namespace Barotrauma
public void DrawHUD(SpriteBatch spriteBatch, Camera cam, Character character) public void DrawHUD(SpriteBatch spriteBatch, Camera cam, Character character)
{ {
if (HasInGameEditableProperties && (character.SelectedConstruction == this || EditableWhenEquipped)) if (HasInGameEditableProperties && (character.SelectedItem == this || EditableWhenEquipped))
{ {
DrawEditing(spriteBatch, cam); DrawEditing(spriteBatch, cam);
} }
@@ -1215,6 +1215,7 @@ namespace Barotrauma
if (ic.DisplayMsg.IsNullOrEmpty()) { continue; } if (ic.DisplayMsg.IsNullOrEmpty()) { continue; }
if (!ic.CanBePicked && !ic.CanBeSelected) { continue; } if (!ic.CanBePicked && !ic.CanBeSelected) { continue; }
if (ic is Holdable holdable && !holdable.CanBeDeattached()) { continue; } if (ic is Holdable holdable && !holdable.CanBeDeattached()) { continue; }
if (ic is ConnectionPanel connectionPanel && !connectionPanel.CanRewire()) { continue; }
Color color = Color.Gray; Color color = Color.Gray;
if (ic.HasRequiredItems(character, false)) if (ic.HasRequiredItems(character, false))
@@ -1246,15 +1247,15 @@ namespace Barotrauma
} }
else else
{ {
if (HasInGameEditableProperties && Character.Controlled != null && (Character.Controlled.SelectedConstruction == this || EditableWhenEquipped)) if (HasInGameEditableProperties && Character.Controlled != null && (Character.Controlled.SelectedItem == this || EditableWhenEquipped))
{ {
if (editingHUD != null && editingHUD.UserData == this) { editingHUD.AddToGUIUpdateList(); } if (editingHUD != null && editingHUD.UserData == this) { editingHUD.AddToGUIUpdateList(); }
} }
} }
if (Character.Controlled != null && Character.Controlled.SelectedConstruction != this && GetComponent<RemoteController>() == null) if (Character.Controlled != null && Character.Controlled.SelectedItem != this && GetComponent<RemoteController>() == null)
{ {
if (Character.Controlled.SelectedConstruction?.GetComponent<RemoteController>()?.TargetItem != this && if (Character.Controlled.SelectedItem?.GetComponent<RemoteController>()?.TargetItem != this &&
!Character.Controlled.HeldItems.Any(it => it.GetComponent<RemoteController>()?.TargetItem == this)) !Character.Controlled.HeldItems.Any(it => it.GetComponent<RemoteController>()?.TargetItem == this))
{ {
return; return;
@@ -1,5 +1,4 @@
using System; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System.Linq; using System.Linq;
@@ -17,7 +16,7 @@ namespace Barotrauma
{ {
if (PlayerInput.KeyHit(InputType.Select)) if (PlayerInput.KeyHit(InputType.Select))
{ {
Character.Controlled.SelectedConstruction = null; Character.Controlled.SelectedItem = null;
} }
}*/ }*/
} }
@@ -232,8 +232,7 @@ namespace Barotrauma
{ {
foreach (MapEntity e in mapEntityList) foreach (MapEntity e in mapEntityList)
{ {
if (!e.SelectableInEditor) continue; if (!e.SelectableInEditor) { continue; }
if (e.IsMouseOn(position)) if (e.IsMouseOn(position))
{ {
int i = 0; int i = 0;
@@ -243,9 +242,7 @@ namespace Barotrauma
{ {
i++; i++;
} }
highlightedEntities.Insert(i, e); highlightedEntities.Insert(i, e);
if (i == 0) highLightedEntity = e; if (i == 0) highLightedEntity = e;
} }
} }
@@ -741,7 +738,14 @@ namespace Barotrauma
/// </summary> /// </summary>
public static void DrawSelecting(SpriteBatch spriteBatch, Camera cam) public static void DrawSelecting(SpriteBatch spriteBatch, Camera cam)
{ {
if (GUI.MouseOn != null) return; if (Screen.Selected is SubEditorScreen subEditor)
{
if (subEditor.IsMouseOnEditorGUI()) { return; }
}
else if (GUI.MouseOn != null)
{
return;
}
Vector2 position = PlayerInput.MousePosition; Vector2 position = PlayerInput.MousePosition;
position = cam.ScreenToWorld(position); position = cam.ScreenToWorld(position);
@@ -1093,6 +1097,10 @@ namespace Barotrauma
resizeDirY = y; resizeDirY = y;
resizing = true; resizing = true;
startMovingPos = Vector2.Zero; startMovingPos = Vector2.Zero;
foreach (var mapEntity in mapEntityList)
{
if (mapEntity != this) { mapEntity.isHighlighted = false; }
}
} }
} }
} }
@@ -1202,6 +1202,8 @@ namespace Barotrauma.Networking
connected = false; connected = false;
var prevContentPackages = clientPeer.ServerContentPackages; var prevContentPackages = clientPeer.ServerContentPackages;
//decrement lobby update ID to make sure we update the lobby when we reconnect
GameMain.NetLobbyScreen.LastUpdateID--;
ConnectToServer(serverEndpoint, serverName); ConnectToServer(serverEndpoint, serverName);
if (clientPeer != null) if (clientPeer != null)
{ {
@@ -3271,8 +3273,8 @@ namespace Barotrauma.Networking
if (gameStarted && Screen.Selected == GameMain.GameScreen) if (gameStarted && Screen.Selected == GameMain.GameScreen)
{ {
var controller = Character.Controlled?.SelectedConstruction?.GetComponent<Controller>(); bool disableButtons = Character.Controlled?.SelectedItem?.GetComponent<Controller>() is Controller c1 && c1.HideHUD ||
bool disableButtons = Character.Controlled != null && (controller != null && controller.HideHUD); Character.Controlled?.SelectedSecondaryItem?.GetComponent<Controller>() is Controller c2 && c2.HideHUD;
buttonContainer.Visible = !disableButtons; buttonContainer.Visible = !disableButtons;
if (!GUI.DisableHUD && !GUI.DisableUpperHUD) if (!GUI.DisableHUD && !GUI.DisableUpperHUD)
@@ -227,10 +227,13 @@ namespace Barotrauma.Networking
bool allowEnqueue = overrideSound != null; bool allowEnqueue = overrideSound != null;
if (GameMain.WindowActive && SettingsMenu.Instance is null) if (GameMain.WindowActive && SettingsMenu.Instance is null)
{ {
bool pttDown = PlayerInput.KeyDown(InputType.Voice) && GUI.KeyboardDispatcher.Subscriber == null; bool usingActiveMode = PlayerInput.KeyDown(InputType.Voice);
bool usingLocalMode = PlayerInput.KeyDown(InputType.LocalVoice);
bool usingRadioMode = PlayerInput.KeyDown(InputType.RadioVoice);
bool pttDown = (usingActiveMode || usingLocalMode || usingRadioMode) && GUI.KeyboardDispatcher.Subscriber == null;
if (pttDown || captureTimer <= 0) if (pttDown || captureTimer <= 0)
{ {
ForceLocal = GameMain.ActiveChatMode == ChatMode.Local; ForceLocal = (usingActiveMode && GameMain.ActiveChatMode == ChatMode.Local) || usingLocalMode;
} }
if (GameSettings.CurrentConfig.Audio.VoiceSetting == VoiceMode.Activity) if (GameSettings.CurrentConfig.Audio.VoiceSetting == VoiceMode.Activity)
{ {
@@ -121,10 +121,9 @@ namespace Barotrauma.Networking
{ {
client.VoipSound.SetRange(ChatMessage.SpeakRange * 0.4f, ChatMessage.SpeakRange); client.VoipSound.SetRange(ChatMessage.SpeakRange * 0.4f, ChatMessage.SpeakRange);
} }
if (messageType != ChatMessageType.Radio && Character.Controlled != null && !GameSettings.CurrentConfig.Audio.DisableVoiceChatFilters) client.VoipSound.UseMuffleFilter =
{ messageType != ChatMessageType.Radio && Character.Controlled != null && !GameSettings.CurrentConfig.Audio.DisableVoiceChatFilters &&
client.VoipSound.UseMuffleFilter = SoundPlayer.ShouldMuffleSound(Character.Controlled, client.Character.WorldPosition, ChatMessage.SpeakRange, client.Character.CurrentHull); SoundPlayer.ShouldMuffleSound(Character.Controlled, client.Character.WorldPosition, ChatMessage.SpeakRange, client.Character.CurrentHull);
}
} }
GameMain.NetLobbyScreen?.SetPlayerSpeaking(client); GameMain.NetLobbyScreen?.SetPlayerSpeaking(client);
@@ -3,7 +3,6 @@ using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Particles namespace Barotrauma.Particles
{ {
@@ -217,6 +216,11 @@ namespace Barotrauma.Particles
} }
} }
public void ClearParticles()
{
particleCount = 0;
}
public void RemoveByPrefab(ParticlePrefab prefab) public void RemoveByPrefab(ParticlePrefab prefab)
{ {
if (particles == null) { return; } if (particles == null) { return; }
@@ -132,11 +132,11 @@ namespace Barotrauma
} }
else if (a.MouseButton != MouseButton.None) else if (a.MouseButton != MouseButton.None)
{ {
return a.MouseButton == b.MouseButton; return !(b is null) && a.MouseButton == b.MouseButton;
} }
else else
{ {
return a.Key.Equals(b.Key); return !(b is null) && a.Key.Equals(b.Key);
} }
} }
@@ -27,8 +27,6 @@ namespace Barotrauma
private bool hasMaxMissions; private bool hasMaxMissions;
private GUIButton repairHullsButton, replaceShuttlesButton, repairItemsButton;
private SubmarineSelection submarineSelection; private SubmarineSelection submarineSelection;
private Location selectedLocation; private Location selectedLocation;
@@ -101,170 +99,6 @@ namespace Barotrauma
tabs[(int)CampaignMode.InteractionType.Store] = storeTab; tabs[(int)CampaignMode.InteractionType.Store] = storeTab;
Store = new Store(this, storeTab); Store = new Store(this, storeTab);
// repair tab -------------------------------------------------------------------------
tabs[(int)CampaignMode.InteractionType.Repair] = CreateDefaultTabContainer(container, new Vector2(0.7f));
var repairFrame = new GUIFrame(new RectTransform(Vector2.One, GetTabContainer(CampaignMode.InteractionType.Repair).RectTransform, Anchor.TopLeft), color: Color.Black * 0.9f);
new GUIFrame(new RectTransform(new Vector2(1.25f, 1.25f), repairFrame.RectTransform, Anchor.Center), style: "OuterGlow", color: Color.Black * 0.7f)
{
UserData = "outerglow",
CanBeFocused = false
};
var repairContent = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.85f), repairFrame.RectTransform, Anchor.Center))
{
RelativeSpacing = 0.05f,
Stretch = true
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), repairContent.RectTransform), "", font: GUIStyle.LargeFont)
{
TextGetter = GetMoney
};
// repair hulls -----------------------------------------------
var repairHullsHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), repairContent.RectTransform), childAnchor: Anchor.TopRight)
{
RelativeSpacing = 0.05f,
Stretch = true
};
new GUIImage(new RectTransform(new Vector2(0.3f, 1.0f), repairHullsHolder.RectTransform, Anchor.CenterLeft), "RepairHullButton")
{
IgnoreLayoutGroups = true,
CanBeFocused = false
};
var repairHullsLabel = new GUITextBlock(new RectTransform(new Vector2(0.7f, 0.3f), repairHullsHolder.RectTransform), TextManager.Get("RepairAllWalls"), textAlignment: Alignment.Right, font: GUIStyle.SubHeadingFont)
{
ForceUpperCase = ForceUpperCase.Yes
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), repairHullsHolder.RectTransform), CampaignMode.HullRepairCost.ToString(), textAlignment: Alignment.Right, font: GUIStyle.SubHeadingFont);
repairHullsButton = new GUIButton(new RectTransform(new Vector2(0.4f, 0.3f), repairHullsHolder.RectTransform) { MinSize = new Point(140, 0) }, TextManager.Get("Repair"))
{
OnClicked = (btn, userdata) =>
{
if (Campaign.PurchasedHullRepairs)
{
Campaign.Wallet.Refund(CampaignMode.HullRepairCost);
Campaign.PurchasedHullRepairs = false;
}
else
{
if (Campaign.TryPurchase(null, CampaignMode.HullRepairCost))
{
GameAnalyticsManager.AddMoneySpentEvent(CampaignMode.HullRepairCost, GameAnalyticsManager.MoneySink.Service, "hullrepairs");
Campaign.PurchasedHullRepairs = true;
}
}
GameMain.Client?.SendCampaignState();
btn.GetChild<GUITickBox>().Selected = Campaign.PurchasedHullRepairs;
return true;
}
};
new GUITickBox(new RectTransform(new Vector2(0.65f), repairHullsButton.RectTransform, Anchor.CenterLeft) { AbsoluteOffset = new Point(10, 0) }, "")
{
CanBeFocused = false
};
// repair items -------------------------------------------
var repairItemsHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), repairContent.RectTransform), childAnchor: Anchor.TopRight)
{
RelativeSpacing = 0.05f,
Stretch = true
};
new GUIImage(new RectTransform(new Vector2(0.3f, 1.0f), repairItemsHolder.RectTransform, Anchor.CenterLeft), "RepairItemsButton")
{
IgnoreLayoutGroups = true,
CanBeFocused = false
};
var repairItemsLabel = new GUITextBlock(new RectTransform(new Vector2(0.7f, 0.3f), repairItemsHolder.RectTransform), TextManager.Get("RepairAllItems"), textAlignment: Alignment.Right, font: GUIStyle.SubHeadingFont)
{
ForceUpperCase = ForceUpperCase.Yes
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), repairItemsHolder.RectTransform), CampaignMode.ItemRepairCost.ToString(), textAlignment: Alignment.Right, font: GUIStyle.SubHeadingFont);
repairItemsButton = new GUIButton(new RectTransform(new Vector2(0.4f, 0.3f), repairItemsHolder.RectTransform) { MinSize = new Point(140, 0) }, TextManager.Get("Repair"))
{
OnClicked = (btn, userdata) =>
{
if (Campaign.PurchasedItemRepairs)
{
Campaign.Wallet.Refund(CampaignMode.ItemRepairCost);
Campaign.PurchasedItemRepairs = false;
}
else
{
if (Campaign.TryPurchase(null, CampaignMode.ItemRepairCost))
{
GameAnalyticsManager.AddMoneySpentEvent(CampaignMode.ItemRepairCost, GameAnalyticsManager.MoneySink.Service, "devicerepairs");
Campaign.PurchasedItemRepairs = true;
}
}
GameMain.Client?.SendCampaignState();
btn.GetChild<GUITickBox>().Selected = Campaign.PurchasedItemRepairs;
return true;
}
};
new GUITickBox(new RectTransform(new Vector2(0.65f), repairItemsButton.RectTransform, Anchor.CenterLeft) { AbsoluteOffset = new Point(10, 0) }, "")
{
CanBeFocused = false
};
// replace lost shuttles -------------------------------------------
var replaceShuttlesHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), repairContent.RectTransform), childAnchor: Anchor.TopRight)
{
RelativeSpacing = 0.05f,
Stretch = true
};
new GUIImage(new RectTransform(new Vector2(0.3f, 1.0f), replaceShuttlesHolder.RectTransform, Anchor.CenterLeft), "ReplaceShuttlesButton")
{
IgnoreLayoutGroups = true,
CanBeFocused = false
};
var replaceShuttlesLabel = new GUITextBlock(new RectTransform(new Vector2(0.7f, 0.3f), replaceShuttlesHolder.RectTransform), TextManager.Get("ReplaceLostShuttles"), textAlignment: Alignment.Right, font: GUIStyle.SubHeadingFont)
{
ForceUpperCase = ForceUpperCase.Yes
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), replaceShuttlesHolder.RectTransform), CampaignMode.ShuttleReplaceCost.ToString(), textAlignment: Alignment.Right, font: GUIStyle.SubHeadingFont);
replaceShuttlesButton = new GUIButton(new RectTransform(new Vector2(0.4f, 0.3f), replaceShuttlesHolder.RectTransform) { MinSize = new Point(140, 0) }, TextManager.Get("ReplaceShuttles"))
{
OnClicked = (btn, userdata) =>
{
if (GameMain.GameSession?.SubmarineInfo != null &&
GameMain.GameSession.SubmarineInfo.LeftBehindSubDockingPortOccupied)
{
new GUIMessageBox("", TextManager.Get("ReplaceShuttleDockingPortOccupied"));
return true;
}
if (Campaign.PurchasedLostShuttles)
{
Campaign.Wallet.Refund(CampaignMode.ShuttleReplaceCost);
Campaign.PurchasedLostShuttles = false;
}
else
{
if (Campaign.TryPurchase(null, CampaignMode.ShuttleReplaceCost))
{
GameAnalyticsManager.AddMoneySpentEvent(CampaignMode.ShuttleReplaceCost, GameAnalyticsManager.MoneySink.Service, "retrieveshuttle");
Campaign.PurchasedLostShuttles = true;
}
}
GameMain.Client?.SendCampaignState();
btn.GetChild<GUITickBox>().Selected = Campaign.PurchasedLostShuttles;
return true;
}
};
new GUITickBox(new RectTransform(new Vector2(0.65f), replaceShuttlesButton.RectTransform, Anchor.CenterLeft) { AbsoluteOffset = new Point(10, 0) }, "")
{
CanBeFocused = false
};
GUITextBlock.AutoScaleAndNormalize(repairHullsLabel, repairItemsLabel, replaceShuttlesLabel);
GUITextBlock.AutoScaleAndNormalize(repairHullsButton.GetChild<GUITickBox>().TextBlock, repairItemsButton.GetChild<GUITickBox>().TextBlock, replaceShuttlesButton.GetChild<GUITickBox>().TextBlock);
// upgrade tab ------------------------------------------------------------------------- // upgrade tab -------------------------------------------------------------------------
tabs[(int)CampaignMode.InteractionType.Upgrade] = new GUIFrame(new RectTransform(Vector2.One, container.RectTransform), color: Color.Black * 0.9f); tabs[(int)CampaignMode.InteractionType.Upgrade] = new GUIFrame(new RectTransform(Vector2.One, container.RectTransform), color: Color.Black * 0.9f);
@@ -701,26 +535,6 @@ namespace Barotrauma
switch (selectedTab) switch (selectedTab)
{ {
case CampaignMode.InteractionType.Repair:
repairHullsButton.Enabled =
(Campaign.PurchasedHullRepairs || Campaign.Wallet.CanAfford(CampaignMode.HullRepairCost));
repairHullsButton.GetChild<GUITickBox>().Selected = Campaign.PurchasedHullRepairs;
repairItemsButton.Enabled =
(Campaign.PurchasedItemRepairs || Campaign.Wallet.CanAfford(CampaignMode.ItemRepairCost));
repairItemsButton.GetChild<GUITickBox>().Selected = Campaign.PurchasedItemRepairs;
if (GameMain.GameSession?.SubmarineInfo == null || !GameMain.GameSession.SubmarineInfo.SubsLeftBehind)
{
replaceShuttlesButton.Enabled = false;
replaceShuttlesButton.GetChild<GUITickBox>().Selected = false;
}
else
{
replaceShuttlesButton.Enabled =
(Campaign.PurchasedLostShuttles || Campaign.Wallet.CanAfford(CampaignMode.ShuttleReplaceCost));
replaceShuttlesButton.GetChild<GUITickBox>().Selected = Campaign.PurchasedLostShuttles;
}
break;
case CampaignMode.InteractionType.Store: case CampaignMode.InteractionType.Store:
Store.SelectStore(storeIdentifier); Store.SelectStore(storeIdentifier);
break; break;
@@ -2606,8 +2606,8 @@ namespace Barotrauma.CharacterEditor
animationControls = new GUIFrame(new RectTransform(Vector2.One, centerArea.RectTransform), style: null) { CanBeFocused = false }; animationControls = new GUIFrame(new RectTransform(Vector2.One, centerArea.RectTransform), style: null) { CanBeFocused = false };
var layoutGroupAnimation = new GUILayoutGroup(new RectTransform(Vector2.One, animationControls.RectTransform), childAnchor: Anchor.TopLeft) { CanBeFocused = false }; var layoutGroupAnimation = new GUILayoutGroup(new RectTransform(Vector2.One, animationControls.RectTransform), childAnchor: Anchor.TopLeft) { CanBeFocused = false };
var animationSelectionElement = new GUIFrame(new RectTransform(new Point(elementSize.X * 2 - (int)(5 * GUI.xScale), elementSize.Y), layoutGroupAnimation.RectTransform), style: null); var animationSelectionElement = new GUIFrame(new RectTransform(new Point(elementSize.X * 2 - (int)(5 * GUI.xScale), elementSize.Y), layoutGroupAnimation.RectTransform), style: null);
var animationSelectionText = new GUITextBlock(new RectTransform(new Point(elementSize.X, elementSize.Y), animationSelectionElement.RectTransform), GetCharacterEditorTranslation("SelectedAnimation") + ": ", Color.WhiteSmoke, textAlignment: Alignment.Center); var animationSelectionText = new GUITextBlock(new RectTransform(new Point(elementSize.X, elementSize.Y), animationSelectionElement.RectTransform), GetCharacterEditorTranslation("SelectedAnimation"), Color.WhiteSmoke, textAlignment: Alignment.CenterRight);
animSelection = new GUIDropDown(new RectTransform(new Point((int)(100 * GUI.xScale), elementSize.Y), animationSelectionElement.RectTransform, Anchor.TopRight), elementCount: 5); animSelection = new GUIDropDown(new RectTransform(new Point((int)(150 * GUI.xScale), elementSize.Y), animationSelectionElement.RectTransform, Anchor.Center, Pivot.CenterLeft), elementCount: 5);
if (character.AnimController.CanWalk) if (character.AnimController.CanWalk)
{ {
animSelection.AddItem(AnimationType.Walk.ToString(), AnimationType.Walk); animSelection.AddItem(AnimationType.Walk.ToString(), AnimationType.Walk);
@@ -1,11 +1,10 @@
using Barotrauma.Extensions;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using FarseerPhysics;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using Barotrauma.Extensions;
namespace Barotrauma namespace Barotrauma
{ {
@@ -79,21 +78,27 @@ namespace Barotrauma
public override void AddToGUIUpdateList() public override void AddToGUIUpdateList()
{ {
if (Character.Controlled != null && Character.Controlled.SelectedConstruction != null && Character.Controlled.CanInteractWith(Character.Controlled.SelectedConstruction)) if (Character.Controlled != null)
{ {
Character.Controlled.SelectedConstruction.AddToGUIUpdateList(); if (Character.Controlled.SelectedItem is { } selectedItem && Character.Controlled.CanInteractWith(selectedItem))
}
if (Character.Controlled?.Inventory != null)
{
foreach (Item item in Character.Controlled.Inventory.AllItems)
{ {
if (Character.Controlled.HasEquippedItem(item)) selectedItem.AddToGUIUpdateList();
}
if (Character.Controlled.SelectedSecondaryItem is { } selectedSecondaryItem && Character.Controlled.CanInteractWith(selectedSecondaryItem))
{
selectedSecondaryItem.AddToGUIUpdateList();
}
if (Character.Controlled.Inventory != null)
{
foreach (Item item in Character.Controlled.Inventory.AllItems)
{ {
item.AddToGUIUpdateList(); if (Character.Controlled.HasEquippedItem(item))
{
item.AddToGUIUpdateList();
}
} }
} }
} }
GameMain.GameSession?.AddToGUIUpdateList(); GameMain.GameSession?.AddToGUIUpdateList();
Character.AddAllToGUIUpdateList(); Character.AddAllToGUIUpdateList();
} }
@@ -260,11 +265,7 @@ namespace Barotrauma
//Draw the rest of the structures, characters and front structures //Draw the rest of the structures, characters and front structures
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform); spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform);
Submarine.DrawBack(spriteBatch, false, e => !(e is Structure) || e.SpriteDepth < 0.9f); Submarine.DrawBack(spriteBatch, false, e => !(e is Structure) || e.SpriteDepth < 0.9f);
foreach (Character c in Character.CharacterList) DrawCharacters(deformed: false, firstPass: true);
{
if (!c.IsVisible || c.AnimController.Limbs.Any(l => l.DeformSprite != null)) { continue; }
c.Draw(spriteBatch, Cam);
}
spriteBatch.End(); spriteBatch.End();
sw.Stop(); sw.Stop();
@@ -272,11 +273,12 @@ namespace Barotrauma
sw.Restart(); sw.Restart();
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform); spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform);
DrawDeformed(firstPass: true); DrawCharacters(deformed: true, firstPass: true);
DrawDeformed(firstPass: false); DrawCharacters(deformed: true, firstPass: false);
DrawCharacters(deformed: false, firstPass: false);
spriteBatch.End(); spriteBatch.End();
void DrawDeformed(bool firstPass) void DrawCharacters(bool deformed, bool firstPass)
{ {
//backwards order to render the most recently spawned characters in front (characters spawned later have a larger sprite depth) //backwards order to render the most recently spawned characters in front (characters spawned later have a larger sprite depth)
for (int i = Character.CharacterList.Count - 1; i >= 0; i--) for (int i = Character.CharacterList.Count - 1; i >= 0; i--)
@@ -284,7 +286,14 @@ namespace Barotrauma
Character c = Character.CharacterList[i]; Character c = Character.CharacterList[i];
if (!c.IsVisible) { continue; } if (!c.IsVisible) { continue; }
if (c.Params.DrawLast == firstPass) { continue; } if (c.Params.DrawLast == firstPass) { continue; }
if (c.AnimController.Limbs.All(l => l.DeformSprite == null)) { continue; } if (deformed)
{
if (c.AnimController.Limbs.All(l => l.DeformSprite == null)) { continue; }
}
else
{
if (c.AnimController.Limbs.Any(l => l.DeformSprite != null)) { continue; }
}
c.Draw(spriteBatch, Cam); c.Draw(spriteBatch, Cam);
} }
} }
@@ -695,8 +695,13 @@ namespace Barotrauma
gamesession.StartRound(fixedSeed ? "abcd" : ToolBox.RandomSeed(8), difficulty, levelGenerationParams); gamesession.StartRound(fixedSeed ? "abcd" : ToolBox.RandomSeed(8), difficulty, levelGenerationParams);
GameMain.GameScreen.Select(); GameMain.GameScreen.Select();
// TODO: modding support // TODO: modding support
string[] jobIdentifiers = new string[] { "captain", "engineer", "mechanic", "securityofficer", "medicaldoctor" }; Identifier[] jobIdentifiers = new Identifier[] {
foreach (string job in jobIdentifiers) "captain".ToIdentifier(),
"engineer".ToIdentifier(),
"mechanic".ToIdentifier(),
"securityofficer".ToIdentifier(),
"medicaldoctor".ToIdentifier() };
foreach (Identifier job in jobIdentifiers)
{ {
var jobPrefab = JobPrefab.Get(job); var jobPrefab = JobPrefab.Get(job);
var variant = Rand.Range(0, jobPrefab.Variants); var variant = Rand.Range(0, jobPrefab.Variants);
@@ -55,9 +55,7 @@ namespace Barotrauma
while (timer < duration) while (timer < duration)
{ {
GUI.ScreenOverlayColor = Color.Lerp(from, to, Math.Min(timer / duration, 1.0f)); GUI.ScreenOverlayColor = Color.Lerp(from, to, Math.Min(timer / duration, 1.0f));
timer += CoroutineManager.DeltaTime;
timer += CoroutineManager.UnscaledDeltaTime;
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
} }
@@ -813,8 +813,13 @@ namespace Barotrauma
var itemCount = new GUITextBlock(new RectTransform(new Vector2(0.33f, 1.0f), itemCountText.RectTransform, Anchor.TopRight, Pivot.TopLeft), "", textAlignment: Alignment.CenterRight); var itemCount = new GUITextBlock(new RectTransform(new Vector2(0.33f, 1.0f), itemCountText.RectTransform, Anchor.TopRight, Pivot.TopLeft), "", textAlignment: Alignment.CenterRight);
itemCount.TextGetter = () => itemCount.TextGetter = () =>
{ {
itemCount.TextColor = Item.ItemList.Count > MaxItems ? GUIStyle.Red : Color.Lerp(GUIStyle.Green, GUIStyle.Orange, Item.ItemList.Count / (float)MaxItems); int count = Item.ItemList.Count;
return Item.ItemList.Count.ToString(); if (dummyCharacter?.Inventory != null)
{
count -= dummyCharacter.Inventory.AllItems.Count();
}
itemCount.TextColor = count > MaxItems ? GUIStyle.Red : Color.Lerp(GUIStyle.Green, GUIStyle.Orange, count / (float)MaxItems);
return count.ToString();
}; };
var structureCountText = new GUITextBlock(new RectTransform(new Vector2(0.75f, 0.0f), paddedEntityCountPanel.RectTransform), TextManager.Get("Structures"), var structureCountText = new GUITextBlock(new RectTransform(new Vector2(0.75f, 0.0f), paddedEntityCountPanel.RectTransform), TextManager.Get("Structures"),
@@ -1497,7 +1502,7 @@ namespace Barotrauma
/// <returns></returns> /// <returns></returns>
private static IEnumerable<CoroutineStatus> AutoSaveCoroutine() private static IEnumerable<CoroutineStatus> AutoSaveCoroutine()
{ {
DateTime target = DateTime.Now.AddMinutes(GameSettings.CurrentConfig.AutoSaveIntervalSeconds); DateTime target = DateTime.Now.AddSeconds(GameSettings.CurrentConfig.AutoSaveIntervalSeconds);
DateTime tempTarget = DateTime.Now; DateTime tempTarget = DateTime.Now;
bool wasPaused = false; bool wasPaused = false;
@@ -1549,7 +1554,9 @@ namespace Barotrauma
MapEntity.DeselectAll(); MapEntity.DeselectAll();
ClearUndoBuffer(); ClearUndoBuffer();
#if !DEBUG
DebugConsole.DeactivateCheats(); DebugConsole.DeactivateCheats();
#endif
SetMode(Mode.Default); SetMode(Mode.Default);
@@ -3337,23 +3344,21 @@ namespace Barotrauma
{ {
if (!(userData is XElement element)) { return; } if (!(userData is XElement element)) { return; }
#warning TODO: revise #warning TODO: revise
string filePath = element.GetAttributeStringUnrestricted("file", ""); string filePath = element.GetAttributeStringUnrestricted("file", "");
if (string.IsNullOrWhiteSpace(filePath)) { return; } if (string.IsNullOrWhiteSpace(filePath)) { return; }
var loadedSub = Submarine.Load(new SubmarineInfo(filePath), true); var loadedSub = Submarine.Load(new SubmarineInfo(filePath), true);
// set the submarine file path to the "default" value
var unspecifiedFileName = TextManager.Get("UnspecifiedSubFileName");
loadedSub.Info.FilePath = Path.Combine(ContentPackage.LocalModsDir, unspecifiedFileName.Value, $"{unspecifiedFileName}.sub");
loadedSub.Info.Name = unspecifiedFileName.Value;
try try
{ {
loadedSub.Info.Name = loadedSub.Info.SubmarineElement.GetAttributeString("name", loadedSub.Info.Name); loadedSub.Info.Name = loadedSub.Info.SubmarineElement.GetAttributeString("name", loadedSub.Info.Name);
} }
catch (Exception e) catch (Exception e)
{ {
DebugConsole.ThrowError("Failed to find a name for the submarine.", e); DebugConsole.ThrowError("Failed to find a name for the submarine.", e);
var unspecifiedFileName = TextManager.Get("UnspecifiedSubFileName");
loadedSub.Info.Name = unspecifiedFileName.Value;
} }
MainSub = loadedSub; MainSub = loadedSub;
MainSub.SetPrevTransform(MainSub.Position); MainSub.SetPrevTransform(MainSub.Position);
@@ -3726,7 +3731,6 @@ namespace Barotrauma
} }
else else
{ {
List<ContextMenuOption> availableLayerOptions = new List<ContextMenuOption> List<ContextMenuOption> availableLayerOptions = new List<ContextMenuOption>
{ {
new ContextMenuOption("editor.layer.nolayer", true, onSelected: () => { MoveToLayer(null, targets); }) new ContextMenuOption("editor.layer.nolayer", true, onSelected: () => { MoveToLayer(null, targets); })
@@ -3769,7 +3773,8 @@ namespace Barotrauma
{ {
if (!me.Removed) { me.Remove(); } if (!me.Removed) { me.Remove(); }
} }
})); }),
new ContextMenuOption(TextManager.Get("editortip.shiftforextraoptions") + '\n' + TextManager.Get("editortip.altforruler"), isEnabled: false, onSelected: null));
} }
} }
@@ -4257,7 +4262,7 @@ namespace Barotrauma
MapEntity.SelectedList.Clear(); MapEntity.SelectedList.Clear();
MapEntity.FilteredSelectedList.Clear(); MapEntity.FilteredSelectedList.Clear();
MapEntity.SelectEntity(itemContainer); MapEntity.SelectEntity(itemContainer);
dummyCharacter.SelectedConstruction = itemContainer; dummyCharacter.SelectedItem = itemContainer;
FilterEntities(entityFilterBox.Text); FilterEntities(entityFilterBox.Text);
} }
@@ -4268,9 +4273,9 @@ namespace Barotrauma
{ {
if (dummyCharacter == null) { return; } if (dummyCharacter == null) { return; }
//nothing to close -> return //nothing to close -> return
if (DraggedItemPrefab == null && dummyCharacter?.SelectedConstruction == null && OpenedItem == null) { return; } if (DraggedItemPrefab == null && dummyCharacter?.SelectedItem == null && OpenedItem == null) { return; }
DraggedItemPrefab = null; DraggedItemPrefab = null;
dummyCharacter.SelectedConstruction = null; dummyCharacter.SelectedItem = null;
OpenedItem?.Drop(dummyCharacter); OpenedItem?.Drop(dummyCharacter);
OpenedItem?.SetTransform(oldItemPosition, 0f); OpenedItem?.SetTransform(oldItemPosition, 0f);
OpenedItem = null; OpenedItem = null;
@@ -4347,9 +4352,9 @@ namespace Barotrauma
} }
} }
if (dummyCharacter?.SelectedConstruction != null) if (dummyCharacter?.SelectedItem != null)
{ {
var inv = dummyCharacter?.SelectedConstruction?.OwnInventory; var inv = dummyCharacter?.SelectedItem?.OwnInventory;
if (inv != null) if (inv != null)
{ {
switch (obj) switch (obj)
@@ -4779,9 +4784,9 @@ namespace Barotrauma
if (dummyCharacter != null) if (dummyCharacter != null)
{ {
CharacterHUD.AddToGUIUpdateList(dummyCharacter); CharacterHUD.AddToGUIUpdateList(dummyCharacter);
if (dummyCharacter.SelectedConstruction != null) if (dummyCharacter.SelectedItem != null)
{ {
dummyCharacter.SelectedConstruction.AddToGUIUpdateList(); dummyCharacter.SelectedItem.AddToGUIUpdateList();
} }
else if (WiringMode && MapEntity.SelectedList.FirstOrDefault() is Item item && item.GetComponent<Wire>() != null) else if (WiringMode && MapEntity.SelectedList.FirstOrDefault() is Item item && item.GetComponent<Wire>() != null)
{ {
@@ -4801,7 +4806,7 @@ namespace Barotrauma
/// <summary> /// <summary>
/// GUI.MouseOn doesn't get updated while holding primary mouse and we need it to /// GUI.MouseOn doesn't get updated while holding primary mouse and we need it to
/// </summary> /// </summary>
private bool IsMouseOnEditorGUI() public bool IsMouseOnEditorGUI()
{ {
if (GUI.MouseOn == null) { return false; } if (GUI.MouseOn == null) { return false; }
@@ -5143,7 +5148,7 @@ namespace Barotrauma
{ {
if (dummyCharacter != null) if (dummyCharacter != null)
{ {
if (dummyCharacter.SelectedConstruction == null) if (dummyCharacter.SelectedItem == null)
{ {
foreach (var entity in MapEntity.mapEntityList) foreach (var entity in MapEntity.mapEntityList)
{ {
@@ -5285,7 +5290,7 @@ namespace Barotrauma
me.IsHighlighted = false; me.IsHighlighted = false;
} }
if (dummyCharacter.SelectedConstruction == null) if (dummyCharacter.SelectedItem == null)
{ {
List<Wire> wires = new List<Wire>(); List<Wire> wires = new List<Wire>();
foreach (Item item in Item.ItemList) foreach (Item item in Item.ItemList)
@@ -5308,8 +5313,8 @@ namespace Barotrauma
}); });
} }
if (dummyCharacter.SelectedConstruction == null || if (dummyCharacter.SelectedItem == null ||
dummyCharacter.SelectedConstruction.GetComponent<Pickable>() != null) dummyCharacter.SelectedItem.GetComponent<Pickable>() != null)
{ {
if (WiringMode && PlayerInput.IsShiftDown()) if (WiringMode && PlayerInput.IsShiftDown())
{ {
@@ -5341,7 +5346,7 @@ namespace Barotrauma
TeleportDummyCharacter(oldItemPosition); TeleportDummyCharacter(oldItemPosition);
} }
if (WiringMode && dummyCharacter?.SelectedConstruction == null) if (WiringMode && dummyCharacter?.SelectedItem == null)
{ {
TeleportDummyCharacter(FarseerPhysics.ConvertUnits.ToSimUnits(dummyCharacter.CursorPosition)); TeleportDummyCharacter(FarseerPhysics.ConvertUnits.ToSimUnits(dummyCharacter.CursorPosition));
} }
@@ -5358,7 +5363,7 @@ namespace Barotrauma
} }
// Deposit item from our "infinite stack" into inventory slots // Deposit item from our "infinite stack" into inventory slots
var inv = dummyCharacter?.SelectedConstruction?.OwnInventory; var inv = dummyCharacter?.SelectedItem?.OwnInventory;
if (inv?.visualSlots != null && !PlayerInput.IsCtrlDown()) if (inv?.visualSlots != null && !PlayerInput.IsCtrlDown())
{ {
var dragginMouse = MouseDragStart != Vector2.Zero && Vector2.Distance(PlayerInput.MousePosition, MouseDragStart) >= GUI.Scale * 20; var dragginMouse = MouseDragStart != Vector2.Zero && Vector2.Distance(PlayerInput.MousePosition, MouseDragStart) >= GUI.Scale * 20;
@@ -5524,7 +5529,7 @@ namespace Barotrauma
} }
if (!saveAssemblyFrame.Rect.Contains(PlayerInput.MousePosition) && !snapToGridFrame.Rect.Contains(PlayerInput.MousePosition) && if (!saveAssemblyFrame.Rect.Contains(PlayerInput.MousePosition) && !snapToGridFrame.Rect.Contains(PlayerInput.MousePosition) &&
dummyCharacter?.SelectedConstruction == null && !WiringMode && GUI.MouseOn == null) dummyCharacter?.SelectedItem == null && !WiringMode && GUI.MouseOn == null)
{ {
if (layerList is { Visible: true } && GUI.KeyboardDispatcher.Subscriber == layerList) if (layerList is { Visible: true } && GUI.KeyboardDispatcher.Subscriber == layerList)
{ {
@@ -5549,7 +5554,7 @@ namespace Barotrauma
if (!WiringMode) if (!WiringMode)
{ {
bool shouldCloseHud = dummyCharacter?.SelectedConstruction != null && HUD.CloseHUD(dummyCharacter.SelectedConstruction.Rect) && DraggedItemPrefab == null; bool shouldCloseHud = dummyCharacter?.SelectedItem != null && HUD.CloseHUD(dummyCharacter.SelectedItem.Rect) && DraggedItemPrefab == null;
if (MapEntityPrefab.Selected != null && GUI.MouseOn == null) if (MapEntityPrefab.Selected != null && GUI.MouseOn == null)
{ {
@@ -5565,7 +5570,7 @@ namespace Barotrauma
} }
else else
{ {
if (dummyCharacter?.SelectedConstruction == null) if (dummyCharacter?.SelectedItem == null)
{ {
CreateContextMenu(); CreateContextMenu();
} }
@@ -5622,11 +5627,11 @@ namespace Barotrauma
wire?.Update((float)deltaTime, cam); wire?.Update((float)deltaTime, cam);
} }
if (dummyCharacter.SelectedConstruction != null) if (dummyCharacter.SelectedItem != null)
{ {
if (MapEntity.SelectedList.Contains(dummyCharacter.SelectedConstruction) || WiringMode) if (MapEntity.SelectedList.Contains(dummyCharacter.SelectedItem) || WiringMode)
{ {
dummyCharacter.SelectedConstruction?.UpdateHUD(cam, dummyCharacter, (float)deltaTime); dummyCharacter.SelectedItem?.UpdateHUD(cam, dummyCharacter, (float)deltaTime);
} }
else else
{ {
@@ -1,5 +1,4 @@
#nullable enable #nullable enable
using System;
using System.Linq; using System.Linq;
using Barotrauma.Extensions; using Barotrauma.Extensions;
using Barotrauma.Items.Components; using Barotrauma.Items.Components;
@@ -14,18 +13,14 @@ using Microsoft.Xna.Framework.Graphics;
*/ */
namespace Barotrauma namespace Barotrauma
{ {
class TestScreen : EditorScreen internal sealed class TestScreen : EditorScreen
{ {
public override Camera Cam { get; } public override Camera Cam { get; }
private Item? miniMapItem; private Item? miniMapItem;
private Submarine? submarine;
public static Character? dummyCharacter; public static Character? dummyCharacter;
public static Effect? BlueprintEffect; public static Effect? BlueprintEffect;
private GUIFrame? container;
private TabMenu? tabMenu;
public TestScreen() public TestScreen()
{ {
@@ -43,14 +38,11 @@ namespace Barotrauma
return true; return true;
} }
}; };
} }
public override void Select() public override void Select()
{ {
base.Select(); base.Select();
container = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), style: "InnerGlow", color: Color.Black);
var tab = new GUIFrame(new RectTransform(Vector2.One, container.RectTransform), color: Color.Black * 0.9f);
if (dummyCharacter is { Removed: false }) if (dummyCharacter is { Removed: false })
{ {
dummyCharacter?.Remove(); dummyCharacter?.Remove();
@@ -61,30 +53,50 @@ namespace Barotrauma
dummyCharacter.Info.Name = "Galldren"; dummyCharacter.Info.Name = "Galldren";
dummyCharacter.Inventory.CreateSlots(); dummyCharacter.Inventory.CreateSlots();
miniMapItem = new Item(ItemPrefab.Find(null, "deconstructor".ToIdentifier()), Vector2.Zero, null, 1337, false);
foreach (ItemComponent component in miniMapItem.Components)
{
component.OnItemLoaded();
}
Character.Controlled = dummyCharacter; Character.Controlled = dummyCharacter;
GameMain.World.ProcessChanges(); GameMain.World.ProcessChanges();
tabMenu = new TabMenu();
} }
public override void AddToGUIUpdateList() public override void AddToGUIUpdateList()
{ {
Frame.AddToGUIUpdateList(); Frame.AddToGUIUpdateList();
container?.AddToGUIUpdateList(); CharacterHUD.AddToGUIUpdateList(dummyCharacter);
tabMenu?.AddToGUIUpdateList(); dummyCharacter?.SelectedItem?.AddToGUIUpdateList();
// CharacterHUD.AddToGUIUpdateList(dummyCharacter);
// dummyCharacter?.SelectedConstruction?.AddToGUIUpdateList();
} }
public override void Update(double deltaTime) public override void Update(double deltaTime)
{ {
base.Update(deltaTime); base.Update(deltaTime);
if (dummyCharacter is { } dummy) if (dummyCharacter is { } dummy && miniMapItem is { } item)
{ {
if (dummy.SelectedItem != item)
{
dummy.SelectedItem = item;
}
dummy.SelectedItem?.UpdateHUD(Cam, dummy, (float)deltaTime);
Vector2 pos = FarseerPhysics.ConvertUnits.ToSimUnits(item.Position);
foreach (Limb limb in dummy.AnimController.Limbs)
{
limb.body.SetTransform(pos, 0.0f);
}
if (dummy.AnimController?.Collider is { } collider)
{
collider.SetTransform(pos, 0);
}
dummy.ControlLocalPlayer((float)deltaTime, Cam, false); dummy.ControlLocalPlayer((float)deltaTime, Cam, false);
dummy.Control((float)deltaTime, Cam); dummy.Control((float)deltaTime, Cam);
} }
tabMenu?.Update((float)deltaTime);
} }
public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch) public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
@@ -93,12 +105,13 @@ namespace Barotrauma
graphics.Clear(BackgroundColor); graphics.Clear(BackgroundColor);
spriteBatch.Begin(SpriteSortMode.BackToFront, transformMatrix: Cam.Transform); spriteBatch.Begin(SpriteSortMode.BackToFront, transformMatrix: Cam.Transform);
// miniMapItem?.Draw(spriteBatch, false); miniMapItem?.Draw(spriteBatch, false);
// if (dummyCharacter is { } dummy) if (dummyCharacter is { } dummy)
// { {
// dummyCharacter.DrawFront(spriteBatch, Cam); dummyCharacter.DrawFront(spriteBatch, Cam);
// dummyCharacter.Draw(spriteBatch, Cam); dummyCharacter.Draw(spriteBatch, Cam);
// } }
spriteBatch.End(); spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState); spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState);
@@ -1448,7 +1448,9 @@ namespace Barotrauma
var component = otherComponents[componentIndex]; var component = otherComponents[componentIndex];
Debug.Assert(component.GetType() == parentObject.GetType()); Debug.Assert(component.GetType() == parentObject.GetType());
SafeAdd(component, property); SafeAdd(component, property);
if (value is string stringValue && Enum.TryParse(property.PropertyType, stringValue, out var enumValue)) if (value is string stringValue &&
property.PropertyType.IsEnum &&
Enum.TryParse(property.PropertyType, stringValue, out var enumValue))
{ {
property.PropertyInfo.SetValue(component, enumValue); property.PropertyInfo.SetValue(component, enumValue);
} }
@@ -27,6 +27,8 @@ namespace Barotrauma
Mods Mods
} }
public Tab CurrentTab { get; private set; }
private GameSettings.Config unsavedConfig; private GameSettings.Config unsavedConfig;
private readonly GUIFrame mainFrame; private readonly GUIFrame mainFrame;
@@ -37,7 +39,13 @@ namespace Barotrauma
public readonly WorkshopMenu WorkshopMenu; public readonly WorkshopMenu WorkshopMenu;
private static readonly ImmutableHashSet<InputType> LegacyInputTypes = new List<InputType>() { InputType.Chat, InputType.RadioChat }.ToImmutableHashSet(); private static readonly ImmutableHashSet<InputType> LegacyInputTypes = new List<InputType>()
{
InputType.Chat,
InputType.RadioChat,
InputType.LocalVoice,
InputType.RadioVoice,
}.ToImmutableHashSet();
public static SettingsMenu Create(RectTransform mainParent) public static SettingsMenu Create(RectTransform mainParent)
{ {
@@ -97,6 +105,7 @@ namespace Barotrauma
public void SelectTab(Tab tab) public void SelectTab(Tab tab)
{ {
CurrentTab = tab;
SwitchContent(tabContents[tab].Content); SwitchContent(tabContents[tab].Content);
tabber.Children.ForEach(c => tabber.Children.ForEach(c =>
{ {
@@ -764,27 +773,35 @@ namespace Barotrauma
private void CreateBottomButtons() private void CreateBottomButtons()
{ {
GUIButton cancelButton = new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), bottom.RectTransform), text: TextManager.Get("Cancel"))
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), bottom.RectTransform), text: TextManager.Get("Cancel")) {
OnClicked = (btn, obj) =>
{ {
OnClicked = (btn, obj) => Close();
{ return false;
Close(); }
return false; };
} new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), bottom.RectTransform), text: TextManager.Get("applysettingsbutton"))
}; {
GUIButton applyButton = OnClicked = (btn, obj) =>
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), bottom.RectTransform), text: TextManager.Get("applysettingsbutton"))
{ {
OnClicked = (btn, obj) => GameSettings.SetCurrentConfig(unsavedConfig);
if (WorkshopMenu is MutableWorkshopMenu mutableWorkshopMenu &&
mutableWorkshopMenu.CurrentTab == MutableWorkshopMenu.Tab.InstalledMods)
{ {
GameSettings.SetCurrentConfig(unsavedConfig); mutableWorkshopMenu.Apply();
if (WorkshopMenu is MutableWorkshopMenu mutableWorkshopMenu) { mutableWorkshopMenu.Apply(); }
GameSettings.SaveCurrentConfig();
mainFrame.Flash(color: GUIStyle.Green);
return false;
} }
}; GameSettings.SaveCurrentConfig();
mainFrame.Flash(color: GUIStyle.Green);
return false;
},
OnAddedToGUIUpdateList = (GUIComponent component) =>
{
component.Enabled =
CurrentTab != Tab.Mods ||
(WorkshopMenu is MutableWorkshopMenu mutableWorkshopMenu && mutableWorkshopMenu.CurrentTab == MutableWorkshopMenu.Tab.InstalledMods && !mutableWorkshopMenu.ViewingItemDetails);
}
};
} }
public void Close() public void Close()
@@ -368,7 +368,7 @@ namespace Barotrauma.Sounds
string filePath = overrideFilePath ?? element.GetAttributeContentPath("file")?.Value ?? ""; string filePath = overrideFilePath ?? element.GetAttributeContentPath("file")?.Value ?? "";
if (!File.Exists(filePath)) if (!File.Exists(filePath))
{ {
throw new System.IO.FileNotFoundException("Sound file \"" + filePath + "\" doesn't exist!"); throw new System.IO.FileNotFoundException($"Sound file \"{filePath}\" doesn't exist! Content package \"{(element.ContentPackage?.Name ?? "Unknown")}\".");
} }
var newSound = new OggSound(this, filePath, stream, xElement: element); var newSound = new OggSound(this, filePath, stream, xElement: element);
@@ -253,12 +253,18 @@ namespace Barotrauma
if (flipHorizontal) if (flipHorizontal)
{ {
float diff = targetSize.X % (sourceRect.Width * scale.X); float diff = targetSize.X % (sourceRect.Width * scale.X);
flippedDrawOffset.X = (int)((sourceRect.Width * scale.X - diff) / scale.X); flippedDrawOffset.X = (sourceRect.Width * scale.X - diff) / scale.X;
flippedDrawOffset.X =
MathUtils.NearlyEqual(flippedDrawOffset.X, MathF.Round(flippedDrawOffset.X)) ?
MathF.Round(flippedDrawOffset.X) : flippedDrawOffset.X;
} }
if (flipVertical) if (flipVertical)
{ {
float diff = targetSize.Y % (sourceRect.Height * scale.Y); float diff = targetSize.Y % (sourceRect.Height * scale.Y);
flippedDrawOffset.Y = (int)((sourceRect.Height * scale.Y - diff) / scale.Y); flippedDrawOffset.Y = (sourceRect.Height * scale.Y - diff) / scale.Y;
flippedDrawOffset.Y =
MathUtils.NearlyEqual(flippedDrawOffset.Y, MathF.Round(flippedDrawOffset.Y)) ?
MathF.Round(flippedDrawOffset.Y) : flippedDrawOffset.Y;
} }
drawOffset += flippedDrawOffset; drawOffset += flippedDrawOffset;
@@ -395,8 +395,8 @@ namespace Barotrauma.Steam
if (rules.ContainsKey("allowspectating")) { serverInfo.AllowSpectating = rules["allowspectating"] == "True"; } if (rules.ContainsKey("allowspectating")) { serverInfo.AllowSpectating = rules["allowspectating"] == "True"; }
if (rules.ContainsKey("allowrespawn")) { serverInfo.AllowRespawn = rules["allowrespawn"] == "True"; } if (rules.ContainsKey("allowrespawn")) { serverInfo.AllowRespawn = rules["allowrespawn"] == "True"; }
if (rules.ContainsKey("voicechatenabled")) { serverInfo.VoipEnabled = rules["voicechatenabled"] == "True"; } if (rules.ContainsKey("voicechatenabled")) { serverInfo.VoipEnabled = rules["voicechatenabled"] == "True"; }
if (rules.ContainsKey("friendlyfireenabled")) { serverInfo.AllowRespawn = rules["friendlyfireenabled"] == "True"; } if (rules.ContainsKey("friendlyfireenabled")) { serverInfo.FriendlyFireEnabled = rules["friendlyfireenabled"] == "True"; }
if (rules.ContainsKey("karmaenabled")) { serverInfo.VoipEnabled = rules["karmaenabled"] == "True"; } if (rules.ContainsKey("karmaenabled")) { serverInfo.KarmaEnabled = rules["karmaenabled"] == "True"; }
if (rules.ContainsKey("traitors")) if (rules.ContainsKey("traitors"))
{ {
if (Enum.TryParse(rules["traitors"], out YesNoMaybe traitorsEnabled)) { serverInfo.TraitorsEnabled = traitorsEnabled; } if (Enum.TryParse(rules["traitors"], out YesNoMaybe traitorsEnabled)) { serverInfo.TraitorsEnabled = traitorsEnabled; }
@@ -13,6 +13,8 @@ namespace Barotrauma.Steam
{ {
private CorePackage EnabledCorePackage => enabledCoreDropdown.SelectedData as CorePackage ?? throw new Exception("Valid core package not selected"); private CorePackage EnabledCorePackage => enabledCoreDropdown.SelectedData as CorePackage ?? throw new Exception("Valid core package not selected");
public bool ViewingItemDetails { get; private set; }
private readonly GUIDropDown enabledCoreDropdown; private readonly GUIDropDown enabledCoreDropdown;
private readonly GUIListBox enabledRegularModsList; private readonly GUIListBox enabledRegularModsList;
private readonly GUIListBox disabledRegularModsList; private readonly GUIListBox disabledRegularModsList;
@@ -523,6 +525,7 @@ namespace Barotrauma.Steam
public void PopulateInstalledModLists(bool forceRefreshEnabled = false, bool refreshDisabled = true) public void PopulateInstalledModLists(bool forceRefreshEnabled = false, bool refreshDisabled = true)
{ {
ViewingItemDetails = false;
bulkUpdateButton.Enabled = false; bulkUpdateButton.Enabled = false;
bulkUpdateButton.ToolTip = ""; bulkUpdateButton.ToolTip = "";
ContentPackageManager.UpdateContentPackageList(); ContentPackageManager.UpdateContentPackageList();
@@ -283,6 +283,7 @@ namespace Barotrauma.Steam
{ {
CanBeFocused = false CanBeFocused = false
}; };
unpublishedLayout.Recalculate();
} }
if (publishedGuiComponents.Any()) if (publishedGuiComponents.Any())
@@ -456,6 +457,7 @@ namespace Barotrauma.Steam
{ {
CreateSubscribeButton(workshopItem, new RectTransform(Vector2.One, itemLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight), spriteScale: 0.4f); CreateSubscribeButton(workshopItem, new RectTransform(Vector2.One, itemLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight), spriteScale: 0.4f);
} }
itemLayout.Recalculate();
} }
onFill?.Invoke(workshopItems); onFill?.Invoke(workshopItems);
}); });
@@ -550,6 +552,7 @@ namespace Barotrauma.Steam
private void PopulateFrameWithItemInfo(Steamworks.Ugc.Item workshopItem, GUIFrame parentFrame) private void PopulateFrameWithItemInfo(Steamworks.Ugc.Item workshopItem, GUIFrame parentFrame)
{ {
ViewingItemDetails = true;
taskCancelSrc = taskCancelSrc.IsCancellationRequested ? new CancellationTokenSource() : taskCancelSrc; taskCancelSrc = taskCancelSrc.IsCancellationRequested ? new CancellationTokenSource() : taskCancelSrc;
var contentPackage var contentPackage
@@ -1,12 +1,9 @@
#nullable enable #nullable enable
using Barotrauma.Extensions; using Barotrauma.Extensions;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using ItemOrPackage = Barotrauma.Either<Steamworks.Ugc.Item, Barotrauma.ContentPackage>;
namespace Barotrauma.Steam namespace Barotrauma.Steam
{ {
@@ -29,6 +26,8 @@ namespace Barotrauma.Steam
ShowOnlyItemAssemblies ShowOnlyItemAssemblies
} }
public Tab CurrentTab { get; private set; }
private readonly GUILayoutGroup tabber; private readonly GUILayoutGroup tabber;
private readonly Dictionary<Tab, (GUIButton Button, GUIFrame Content)> tabContents; private readonly Dictionary<Tab, (GUIButton Button, GUIFrame Content)> tabContents;
@@ -78,6 +77,7 @@ namespace Barotrauma.Steam
public void SelectTab(Tab tab) public void SelectTab(Tab tab)
{ {
CurrentTab = tab;
SwitchContent(tabContents[tab].Content); SwitchContent(tabContents[tab].Content);
tabber.Children.ForEach(c => tabber.Children.ForEach(c =>
{ {
@@ -1,30 +1,75 @@
#if DEBUG #if DEBUG
using Barotrauma.IO;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Barotrauma.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Linq; using System.Linq;
using System.Globalization; using System.Text;
namespace Barotrauma namespace Barotrauma
{ {
class LocalizationCSVtoXML class LocalizationCSVtoXML
{ {
private static Regex csvSplit = new Regex("(?:^|,)(\"(?:[^\"])*\"|[^,]*)", RegexOptions.Compiled); // Handling commas inside data fields surrounded by "" private static readonly List<int> conversationClosingIndent = new List<int>();
private static List<int> conversationClosingIndent = new List<int>(); private static readonly char[] separator = new char[1] { '|' };
private static char[] separator = new char[1] { '|' };
private const string conversationsPath = "Content/NPCConversations"; private const string conversationsPath = "Content/NPCConversations";
private const string infoTextPath = "Content/Texts"; private const string infoTextPath = "Content/Texts";
private const string xmlHeader = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"; private const string xmlHeader = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
private static string[,] translatedLanguageNames = new string[13, 2] { { "English", "English" }, { "French", "Français" }, { "German", "Deutsch" }, private static readonly string[,] translatedLanguageNames = new string[13, 2] { { "English", "English" }, { "French", "Français" }, { "German", "Deutsch" },
{ "Russian", "Русский" }, { "Brazilian Portuguese", "Português brasileiro" }, { "Simplified Chinese", "中文(简体)" }, { "Traditional Chinese", "中文(繁體)" }, { "Russian", "Русский" }, { "Brazilian Portuguese", "Português brasileiro" }, { "Simplified Chinese", "中文(简体)" }, { "Traditional Chinese", "中文(繁體)" },
{ "Castilian Spanish", "Castellano" }, { "Latinamerican Spanish", "Español Latinoamericano" }, { "Polish", "Polski" }, { "Turkish", "Türkçe" }, { "Castilian Spanish", "Castellano" }, { "Latinamerican Spanish", "Español Latinoamericano" }, { "Polish", "Polski" }, { "Turkish", "Türkçe" },
{ "Japanese", "日本語" }, { "Korean", "한국어" } }; { "Japanese", "日本語" }, { "Korean", "한국어" } };
public static void Convert() public static void ConvertMasterLocalizationKit(string outputTextsDirectory, string outputConversationsDirectory, bool convertConversations)
{
string textFilePath = Path.Combine(infoTextPath, "Texts.csv");
string conversationFilePath = Path.Combine(infoTextPath, "NPCConversations.csv");
Dictionary<string, List<string>> xmlContent;
try
{
xmlContent = ConvertInfoTextToXML(File.ReadAllLines(textFilePath, Encoding.UTF8));
}
catch (Exception e)
{
DebugConsole.ThrowError("InfoText Localization .csv to .xml conversion failed for: " + textFilePath, e);
return;
}
if (xmlContent == null)
{
DebugConsole.ThrowError("InfoText Localization .csv to .xml conversion failed for: " + textFilePath);
return;
}
foreach (string language in xmlContent.Keys)
{
string languageNoWhitespace = language.Replace(" ", "");
string xmlFileFullPath = Path.Combine(outputTextsDirectory, $"{languageNoWhitespace}/{languageNoWhitespace}Vanilla.xml");
File.WriteAllLines(xmlFileFullPath, xmlContent[language], Encoding.UTF8);
DebugConsole.NewMessage("InfoText localization .xml file successfully created at: " + xmlFileFullPath);
}
if (convertConversations)
{
var conversationLinesAll = File.ReadAllLines(conversationFilePath, Encoding.UTF8);
foreach (string language in xmlContent.Keys)
{
List<string> convXmlContent = ConvertConversationsToXML(conversationLinesAll, language);
if (convXmlContent == null)
{
DebugConsole.ThrowError("NPCConversation Localization .csv to .xml conversion failed for: " + language);
continue;
}
string languageNoWhitespace = language.Replace(" ", "");
string xmlFileFullPath = Path.Combine(outputTextsDirectory, $"NpcConversations_{languageNoWhitespace}.xml");
File.WriteAllLines(xmlFileFullPath, convXmlContent, Encoding.UTF8);
DebugConsole.NewMessage("Conversation localization .xml file successfully created at: " + xmlFileFullPath);
}
}
}
[Obsolete]
public static void ConvertIndividualFiles()
{ {
if (GameSettings.CurrentConfig.Language != TextManager.DefaultLanguage) if (GameSettings.CurrentConfig.Language != TextManager.DefaultLanguage)
{ {
@@ -89,8 +134,7 @@ namespace Barotrauma
for (int j = 0; j < infoTextFiles.Count; j++) for (int j = 0; j < infoTextFiles.Count; j++)
{ {
List<string> xmlContent;
List<string> xmlContent = null;
try try
{ {
xmlContent = ConvertInfoTextToXML(File.ReadAllLines(infoTextFiles[j], Encoding.UTF8), language); xmlContent = ConvertInfoTextToXML(File.ReadAllLines(infoTextFiles[j], Encoding.UTF8), language);
@@ -121,6 +165,109 @@ namespace Barotrauma
} }
} }
private static Dictionary<string, List<string>> ConvertInfoTextToXML(string[] csvContent)
{
Dictionary<string, List<string>> xmlContentByLanguage = new Dictionary<string, List<string>>();
//get all the languages from the header row
string headerRow = csvContent[0];
var headerContent = headerRow.Split(separator);
for (int i = 0; i < headerContent.Length; i++)
{
string languageName = headerContent[i];
if (languageName.Equals("tag", StringComparison.OrdinalIgnoreCase) ||
languageName.Equals("comments", StringComparison.OrdinalIgnoreCase))
{
continue;
}
string translatedName = GetTranslatedName(languageName);
bool nowhitespace = TextManager.IsCJK(translatedName);
List<string> xmlContent = new List<string>()
{
xmlHeader,
$"<infotexts language=\"{languageName}\" nowhitespace=\"{nowhitespace.ToString().ToLower()}\" translatedname=\"{translatedName}\">"
};
xmlContentByLanguage.Add(headerContent[i], xmlContent);
}
for (int row = 1; row < csvContent.Length; row++) // Start at one to ignore header
{
if (!xmlContentByLanguage.Values.All(values => values.Count == xmlContentByLanguage["English"].Count))
{
throw new Exception($"Error while converting csv to xml: mismatching number of texts on line {row-1} ({csvContent[row - 1]}). Check that there's no extra newlines, separators or missing lines in the csv file.");
}
if (csvContent[row].Length == 0)
{
AddToAllLanguages(string.Empty);
}
else
{
string[] split = csvContent[row].Split(separator);
if (split.Length < xmlContentByLanguage.Count)
{
throw new Exception($"Error while converting csv to xml: not enough values on line {row} ({csvContent[row]}). Check that there's no extra newlines, separators or missing lines in the csv file.");
}
if (split.Length > 1) // Localization data
{
//all values empty = an empty line
if (split.All(s => s.IsNullOrEmpty()))
{
AddToAllLanguages(string.Empty);
}
//value is empty in all languages
else if (!split[0].IsNullOrEmpty() && split.Skip(2).All(s => s.IsNullOrEmpty()))
{
//first line is all lower-case and contains dot, assume it's an empty value
if (split[0].Contains(".") && !split[0].Any(char.IsUpper))
{
AddToAllLanguages($"<{split[0]}></{split[0]}>");
}
//otherwise assume it's a comment
else
{
AddToAllLanguages($"<!-- {split[0]} -->");
}
}
else
{
for (int j = 0; j < split.Length; j++)
{
string languageName = headerContent[j];
if (languageName.Equals("tag", StringComparison.OrdinalIgnoreCase) ||
languageName.Equals("comments", StringComparison.OrdinalIgnoreCase))
{
continue;
}
split[j] = split[j].Replace(" & ", " &amp; ");
xmlContentByLanguage[languageName].Add($"<{split[0]}>{split[j]}</{split[0]}>");
}
}
}
else // A header/comment
{
AddToAllLanguages($"<!-- {split[0]} -->");
}
}
}
AddToAllLanguages(string.Empty);
AddToAllLanguages("</infotexts>");
void AddToAllLanguages(string str)
{
foreach (var xmlContent in xmlContentByLanguage.Values)
{
xmlContent.Add(str);
}
}
return xmlContentByLanguage;
}
[Obsolete]
private static List<string> ConvertInfoTextToXML(string[] csvContent, string language) private static List<string> ConvertInfoTextToXML(string[] csvContent, string language)
{ {
List<string> xmlContent = new List<string> List<string> xmlContent = new List<string>
@@ -147,12 +294,6 @@ namespace Barotrauma
if (split.Length >= 2) // Localization data if (split.Length >= 2) // Localization data
{ {
if (split.Length > 2 && !split[0].All(char.IsLower)) // Invalid header in line with localization data
{
split[0] = split[1];
split[1] = split[2];
split[2] = string.Empty;
}
split[1] = split[1].Replace(" & ", " &amp; "); split[1] = split[1].Replace(" & ", " &amp; ");
xmlContent.Add($"<{split[0]}>{split[1]}</{split[0]}>"); xmlContent.Add($"<{split[0]}>{split[1]}</{split[0]}>");
} }
@@ -186,68 +327,39 @@ namespace Barotrauma
private static List<string> ConvertConversationsToXML(string[] csvContent, string language) private static List<string> ConvertConversationsToXML(string[] csvContent, string language)
{ {
List<string> xmlContent = new List<string>(); List<string> xmlContent = new List<string>
xmlContent.Add(xmlHeader); {
xmlHeader
};
string translatedName = GetTranslatedName(language); string translatedName = GetTranslatedName(language);
bool nowhitespace = TextManager.IsCJK(translatedName); bool nowhitespace = TextManager.IsCJK(translatedName);
int languageColumn = -1;
string[] headerSplit = csvContent[0].Split(separator);
for (int i = 0; i < headerSplit.Length; i++)
{
if (headerSplit[i] == language)
{
languageColumn = i;
break;
}
}
xmlContent.Add($"<Conversations identifier=\"vanillaconversations\" Language=\"{language}\" nowhitespace=\"{nowhitespace}\">"); xmlContent.Add($"<Conversations identifier=\"vanillaconversations\" Language=\"{language}\" nowhitespace=\"{nowhitespace}\">");
xmlContent.Add(string.Empty);
xmlContent.Add("<!-- Personality traits -->");
int traitStart = -1; int conversationStart = 1;
for (int i = 0; i < csvContent.Length; i++)
{
if (csvContent[i].StartsWith("Personality"))
{
traitStart = i + 1;
break;
}
}
int conversationStart = -1;
for (int i = 0; i < csvContent.Length; i++)
{
if (csvContent[i].StartsWith("Generic"))
{
conversationStart = i;
break;
}
}
if (traitStart == -1)
{
DebugConsole.ThrowError("Invalid formatting of NPCConversations, no traits found!");
return null;
}
//DebugConsole.NewMessage("Count: " + NPCPersonalityTrait.List.Count);
var traits = NPCPersonalityTrait.GetAll(language.ToLanguageIdentifier()).ToArray();
for (int i = 0; i < traits.Length; i++) // Traits
{
//string[] split = SplitCSV(csvContent[traitStart + i].Trim(separator));
string[] split = csvContent[traitStart + i].Split(separator);
xmlContent.Add(
$"<PersonalityTrait " +
$"{GetVariable("name", split[1])}" +
$"{GetVariable("alloweddialogtags", string.Join(",", traits[i].AllowedDialogTags))}" +
$"{GetVariable("commonness", traits[i].Commonness.ToString(CultureInfo.InvariantCulture))}/>");
}
xmlContent.Add(string.Empty); xmlContent.Add(string.Empty);
for (int i = conversationStart; i < csvContent.Length; i++) // Conversations for (int i = conversationStart; i < csvContent.Length; i++) // Conversations
{ {
string[] split = csvContent[i].Split(separator); string[] split = csvContent[i].Split(separator);
int emptyFields = 0; int emptyFields = 0;
for (int j = 0; j < split.Length; j++) for (int j = 0; j < split.Length; j++)
{ {
if (split[j] == string.Empty) emptyFields++; if (split[j] == string.Empty) { emptyFields++; }
} }
if (emptyFields == split.Length) // Empty line with only commas, indicates the end of the previous conversation if (emptyFields == split.Length) // Empty line with only commas, indicates the end of the previous conversation
{ {
HandleClosingElements(xmlContent, 0); HandleClosingElements(xmlContent, 0);
@@ -260,10 +372,10 @@ namespace Barotrauma
continue; continue;
} }
string speaker = split[1]; string line = split[languageColumn].Replace("\"", "");
int depthIndex = int.Parse(split[2]); string speaker = split[2];
int depthIndex = int.Parse(split[3]);
// 3 = original line // 3 = original line
string line = split[3].Replace("\"", "");
string flags = split[4].Replace("\"", ""); string flags = split[4].Replace("\"", "");
string allowedJobs = split[5].Replace("\"", ""); string allowedJobs = split[5].Replace("\"", "");
string speakerTags = split[6].Replace("\"", ""); string speakerTags = split[6].Replace("\"", "");
@@ -332,24 +444,6 @@ namespace Barotrauma
} }
} }
private static string[] SplitCSV(string input) // Splits the .csv with regex, leaving commas inside quotation marks intact
{
List<string> list = new List<string>();
string curr = null;
foreach (Match match in csvSplit.Matches(input))
{
curr = match.Value;
if (0 == curr.Length)
{
list.Add("");
}
list.Add(curr.TrimStart(separator));
}
return list.ToArray();
}
private static string GetIndenting(int depthIndex) private static string GetIndenting(int depthIndex)
{ {
string indenting = string.Empty; string indenting = string.Empty;
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.18.15.0</Version> <Version>0.19.0.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright> <Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
@@ -65,7 +65,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition="'$(Configuration)'!='Debug'"> <ItemGroup Condition="'$(Configuration)'!='Debug'">
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" Exclude="..\BarotraumaShared\Data\Saves\*.save" /> <Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" Exclude="..\BarotraumaShared\Data\Saves\*.save;..\BarotraumaShared\ModLists\*.xml" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(Configuration)'=='Debug'"> <ItemGroup Condition="'$(Configuration)'=='Debug'">
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" /> <Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" />
+2 -2
View File
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.18.15.0</Version> <Version>0.19.0.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright> <Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
@@ -56,7 +56,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition="'$(Configuration)'!='Debug'"> <ItemGroup Condition="'$(Configuration)'!='Debug'">
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" Exclude="..\BarotraumaShared\Data\Saves\*.save" /> <Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" Exclude="..\BarotraumaShared\Data\Saves\*.save;..\BarotraumaShared\ModLists\*.xml" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(Configuration)'=='Debug'"> <ItemGroup Condition="'$(Configuration)'=='Debug'">
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" /> <Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" />
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.18.15.0</Version> <Version>0.19.0.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright> <Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
@@ -62,7 +62,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition="'$(Configuration)'!='Debug'"> <ItemGroup Condition="'$(Configuration)'!='Debug'">
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" Exclude="..\BarotraumaShared\Data\Saves\*.save" /> <Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" Exclude="..\BarotraumaShared\Data\Saves\*.save;..\BarotraumaShared\ModLists\*.xml" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(Configuration)'=='Debug'"> <ItemGroup Condition="'$(Configuration)'=='Debug'">
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" /> <Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" />
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.18.15.0</Version> <Version>0.19.0.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright> <Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
@@ -65,7 +65,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" Exclude="..\BarotraumaShared\Data\Saves\*.save" /> <Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" Exclude="..\BarotraumaShared\Data\Saves\*.save;..\BarotraumaShared\ModLists\*.xml" />
<Content Remove="..\BarotraumaShared\**\*.cs" /> <Content Remove="..\BarotraumaShared\**\*.cs" />
<Content Remove="..\BarotraumaShared\**\*.props" /> <Content Remove="..\BarotraumaShared\**\*.props" />
<Compile Include="..\BarotraumaShared\**\*.cs" /> <Compile Include="..\BarotraumaShared\**\*.cs" />
+2 -2
View File
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.18.15.0</Version> <Version>0.19.0.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright> <Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
@@ -58,7 +58,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" Exclude="..\BarotraumaShared\Data\Saves\*.save" /> <Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" Exclude="..\BarotraumaShared\Data\Saves\*.save;..\BarotraumaShared\ModLists\*.xml" />
<Content Remove="..\BarotraumaShared\**\*.cs" /> <Content Remove="..\BarotraumaShared\**\*.cs" />
<Content Remove="..\BarotraumaShared\**\*.props" /> <Content Remove="..\BarotraumaShared\**\*.props" />
<Compile Include="..\BarotraumaShared\**\*.cs" /> <Compile Include="..\BarotraumaShared\**\*.cs" />
@@ -1,5 +1,4 @@
using Barotrauma.Networking; using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -66,19 +65,16 @@ namespace Barotrauma
if (Job != null) if (Job != null)
{ {
msg.Write(Job.Prefab.Identifier); msg.Write(Job.Prefab.UintIdentifier);
msg.Write((byte)Job.Variant); msg.Write((byte)Job.Variant);
var skills = Job.GetSkills(); foreach (SkillPrefab skillPrefab in Job.Prefab.Skills.OrderBy(s => s.Identifier))
msg.Write((byte)skills.Count());
foreach (Skill skill in skills)
{ {
msg.Write(skill.Identifier); msg.Write(Job.GetSkill(skillPrefab.Identifier).Level);
msg.Write(skill.Level);
} }
} }
else else
{ {
msg.Write(""); msg.Write((uint)0);
msg.Write((byte)0); msg.Write((byte)0);
} }
@@ -359,11 +359,12 @@ namespace Barotrauma
tempBuffer.Write(AnimController.Dir > 0.0f); tempBuffer.Write(AnimController.Dir > 0.0f);
} }
if (SelectedCharacter != null || SelectedConstruction != null) if (SelectedCharacter != null || HasSelectedAnyItem)
{ {
tempBuffer.Write(true); tempBuffer.Write(true);
tempBuffer.Write(SelectedCharacter != null ? SelectedCharacter.ID : NullEntityID); tempBuffer.Write(SelectedCharacter != null ? SelectedCharacter.ID : NullEntityID);
tempBuffer.Write(SelectedConstruction != null ? SelectedConstruction.ID : NullEntityID); tempBuffer.Write(SelectedItem != null ? SelectedItem.ID : NullEntityID);
tempBuffer.Write(SelectedSecondaryItem != null ? SelectedSecondaryItem.ID : NullEntityID);
if (SelectedCharacter != null) if (SelectedCharacter != null)
{ {
tempBuffer.Write(AnimController.Anim == AnimController.Animation.CPR); tempBuffer.Write(AnimController.Anim == AnimController.Animation.CPR);
@@ -424,8 +425,8 @@ namespace Barotrauma
msg.Write(owner == c && owner.Character == this); msg.Write(owner == c && owner.Character == this);
msg.Write(owner != null && owner.Character == this && GameMain.Server.ConnectedClients.Contains(owner) ? owner.ID : (byte)0); msg.Write(owner != null && owner.Character == this && GameMain.Server.ConnectedClients.Contains(owner) ? owner.ID : (byte)0);
break; break;
case CharacterStatusEventData _: case CharacterStatusEventData statusEventData:
WriteStatus(msg); WriteStatus(msg, statusEventData.ForceAfflictionData);
break; break;
case UpdateSkillsEventData _: case UpdateSkillsEventData _:
if (Info?.Job == null) if (Info?.Job == null)
@@ -573,7 +574,7 @@ namespace Barotrauma
msg.WriteRangedInteger((int)CauseOfDeath.Type, 0, Enum.GetValues(typeof(CauseOfDeathType)).Length - 1); msg.WriteRangedInteger((int)CauseOfDeath.Type, 0, Enum.GetValues(typeof(CauseOfDeathType)).Length - 1);
if (CauseOfDeath.Type == CauseOfDeathType.Affliction) if (CauseOfDeath.Type == CauseOfDeathType.Affliction)
{ {
msg.Write(CauseOfDeath.Affliction.Identifier); msg.Write(CauseOfDeath.Affliction.UintIdentifier);
} }
msg.Write(forceAfflictionData); msg.Write(forceAfflictionData);
if (forceAfflictionData) if (forceAfflictionData)
@@ -58,7 +58,7 @@ namespace Barotrauma
Reset(); Reset();
} }
private bool IsBlockedByAnotherConversation(IEnumerable<Entity> targets) private bool IsBlockedByAnotherConversation(IEnumerable<Entity> targets, float duration)
{ {
foreach (Entity e in targets) foreach (Entity e in targets)
{ {
@@ -68,7 +68,7 @@ namespace Barotrauma
{ {
if (lastActiveAction.ContainsKey(targetClient) && if (lastActiveAction.ContainsKey(targetClient) &&
lastActiveAction[targetClient].ParentEvent != ParentEvent && lastActiveAction[targetClient].ParentEvent != ParentEvent &&
Timing.TotalTime < lastActiveAction[targetClient].lastActiveTime + BlockOtherConversationsDuration) Timing.TotalTime < lastActiveAction[targetClient].lastActiveTime + duration)
{ {
return true; return true;
} }
@@ -91,7 +91,7 @@ namespace Barotrauma
{ {
targetClients.Add(targetClient); targetClients.Add(targetClient);
lastActiveAction[targetClient] = this; lastActiveAction[targetClient] = this;
ServerWrite(speaker, targetClient); ServerWrite(speaker, targetClient, interrupt);
} }
} }
} }
@@ -105,14 +105,14 @@ namespace Barotrauma
{ {
targetClients.Add(c); targetClients.Add(c);
lastActiveAction[c] = this; lastActiveAction[c] = this;
ServerWrite(speaker, c); ServerWrite(speaker, c, interrupt);
} }
} }
} }
} }
} }
private void ServerWrite(Character speaker, Client client) public void ServerWrite(Character speaker, Client client, bool interrupt)
{ {
IWriteMessage outmsg = new WriteOnlyMessage(); IWriteMessage outmsg = new WriteOnlyMessage();
outmsg.Write((byte)ServerPacketHeader.EVENTACTION); outmsg.Write((byte)ServerPacketHeader.EVENTACTION);
@@ -28,13 +28,21 @@ namespace Barotrauma
continue; continue;
} }
if (selectedOption == byte.MaxValue) if (convAction.SelectedOption > -1)
{ {
convAction.IgnoreClient(sender, 3f); //someone else already chose an option for this conversation: interrupt for this client
convAction.ServerWrite(convAction.speaker, sender, interrupt: true);
} }
else else
{ {
convAction.SelectedOption = selectedOption; if (selectedOption == byte.MaxValue)
{
convAction.IgnoreClient(sender, 3f);
}
else
{
convAction.SelectedOption = selectedOption;
}
} }
return; return;
} }
@@ -329,7 +329,7 @@ namespace Barotrauma
if (Server == null) { break; } if (Server == null) { break; }
SteamManager.Update((float)Timing.Step); SteamManager.Update((float)Timing.Step);
TaskPool.Update(); TaskPool.Update();
CoroutineManager.Update((float)Timing.Step, (float)Timing.Step); CoroutineManager.Update(paused: false, (float)Timing.Step);
Timing.Accumulator -= Timing.Step; Timing.Accumulator -= Timing.Step;
updateCount++; updateCount++;
@@ -6,6 +6,8 @@ namespace Barotrauma
{ {
private readonly Queue<WalletChangedData> transactions = new Queue<WalletChangedData>(); private readonly Queue<WalletChangedData> transactions = new Queue<WalletChangedData>();
public bool ShouldForceUpdate;
partial void SettingsChanged(Option<int> balanceChanged, Option<int> rewardChanged) partial void SettingsChanged(Option<int> balanceChanged, Option<int> rewardChanged)
{ {
transactions.Enqueue(new WalletChangedData transactions.Enqueue(new WalletChangedData
@@ -15,6 +17,15 @@ namespace Barotrauma
}); });
} }
/// <summary>
/// Forces the server to sync the state of the wallet regardless if the balance/reward has changed
/// </summary>
public void ForceUpdate()
{
SettingsChanged(balanceChanged: Option<int>.Some(0), rewardChanged: Option<int>.None());
ShouldForceUpdate = true;
}
public bool HasTransactions() => transactions.Count > 0; public bool HasTransactions() => transactions.Count > 0;
public NetWalletTransaction DequeueAndMergeTransactions(ushort id) public NetWalletTransaction DequeueAndMergeTransactions(ushort id)
@@ -354,6 +354,7 @@ namespace Barotrauma
LeaveUnconnectedSubs(leavingSub); LeaveUnconnectedSubs(leavingSub);
NextLevel = newLevel; NextLevel = newLevel;
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine); GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
GameMain.GameSession.EventManager.RegisterEventHistory();
SaveUtil.SaveGame(GameMain.GameSession.SavePath); SaveUtil.SaveGame(GameMain.GameSession.SavePath);
} }
else else
@@ -530,8 +531,9 @@ namespace Barotrauma
if (wallet.HasTransactions()) if (wallet.HasTransactions())
{ {
NetWalletTransaction transaction = wallet.DequeueAndMergeTransactions(id); NetWalletTransaction transaction = wallet.DequeueAndMergeTransactions(id);
if (transaction.ChangedData.BalanceChanged.IsNone() && transaction.ChangedData.RewardDistributionChanged.IsNone()) { continue; } if (!wallet.ShouldForceUpdate && transaction.ChangedData.BalanceChanged.IsNone() && transaction.ChangedData.RewardDistributionChanged.IsNone()) { continue; }
transactions.Add(transaction); transactions.Add(transaction);
wallet.ShouldForceUpdate = false;
} }
} }
@@ -790,11 +792,19 @@ namespace Barotrauma
purchasedItemSwaps.Add(new PurchasedItemSwap(itemToRemove, itemToInstall)); purchasedItemSwaps.Add(new PurchasedItemSwap(itemToRemove, itemToInstall));
} }
int hullRepairCost = GetHullRepairCost();
int itemRepairCost = GetItemRepairCost();
int shuttleRetrieveCost = CampaignMode.ShuttleReplaceCost;
Location location = Map.CurrentLocation; Location location = Map.CurrentLocation;
int hullRepairCost = location?.GetAdjustedMechanicalCost(HullRepairCost) ?? HullRepairCost; if (location != null)
int itemRepairCost = location?.GetAdjustedMechanicalCost(ItemRepairCost) ?? ItemRepairCost; {
int shuttleRetrieveCost = location?.GetAdjustedMechanicalCost(ShuttleReplaceCost) ?? ShuttleReplaceCost; hullRepairCost = location.GetAdjustedMechanicalCost(hullRepairCost);
itemRepairCost = location.GetAdjustedMechanicalCost(itemRepairCost);
shuttleRetrieveCost = location.GetAdjustedMechanicalCost(shuttleRetrieveCost);
}
Wallet personalWallet = GetWallet(sender); Wallet personalWallet = GetWallet(sender);
personalWallet?.ForceUpdate();
if (purchasedHullRepairs != PurchasedHullRepairs) if (purchasedHullRepairs != PurchasedHullRepairs)
{ {
@@ -875,6 +885,9 @@ namespace Barotrauma
{ {
foreach (var item in store.Value.ToList()) foreach (var item in store.Value.ToList())
{ {
if (map?.CurrentLocation?.Stores == null || !map.CurrentLocation.Stores.ContainsKey(store.Key)) { continue; }
item.Quantity = Math.Min(map.CurrentLocation.Stores[store.Key].Stock.Find(s => s.ItemPrefab == item.ItemPrefab)?.Quantity ?? 0, item.Quantity);
if (item.Quantity <= 0) { continue; }
CargoManager.ModifyItemQuantityInBuyCrate(store.Key, item.ItemPrefab, item.Quantity, sender); CargoManager.ModifyItemQuantityInBuyCrate(store.Key, item.ItemPrefab, item.Quantity, sender);
} }
} }
@@ -62,6 +62,8 @@ namespace Barotrauma.Networking
public float DeleteDisconnectedTimer; public float DeleteDisconnectedTimer;
public DateTime JoinTime;
private CharacterInfo characterInfo; private CharacterInfo characterInfo;
public CharacterInfo CharacterInfo public CharacterInfo CharacterInfo
{ {
@@ -114,6 +116,8 @@ namespace Barotrauma.Networking
//initialize to infinity, gets set to a proper value when initializing midround syncing //initialize to infinity, gets set to a proper value when initializing midround syncing
MidRoundSyncTimeOut = double.PositiveInfinity; MidRoundSyncTimeOut = double.PositiveInfinity;
JoinTime = DateTime.Now;
} }
partial void DisposeProjSpecific() partial void DisposeProjSpecific()
@@ -2081,7 +2081,7 @@ namespace Barotrauma.Networking
float waitForResponseTimer = 5.0f; float waitForResponseTimer = 5.0f;
while (connectedClients.Any(c => !c.ReadyToStart) && waitForResponseTimer > 0.0f) while (connectedClients.Any(c => !c.ReadyToStart) && waitForResponseTimer > 0.0f)
{ {
waitForResponseTimer -= CoroutineManager.UnscaledDeltaTime; waitForResponseTimer -= CoroutineManager.DeltaTime;
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
} }
@@ -2090,7 +2090,7 @@ namespace Barotrauma.Networking
float waitForTransfersTimer = 20.0f; float waitForTransfersTimer = 20.0f;
while (FileSender.ActiveTransfers.Count > 0 && waitForTransfersTimer > 0.0f) while (FileSender.ActiveTransfers.Count > 0 && waitForTransfersTimer > 0.0f)
{ {
waitForTransfersTimer -= CoroutineManager.UnscaledDeltaTime; waitForTransfersTimer -= CoroutineManager.DeltaTime;
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
} }
} }
@@ -3278,7 +3278,8 @@ namespace Barotrauma.Networking
Client.UpdateKickVotes(connectedClients); Client.UpdateKickVotes(connectedClients);
int minimumKickVotes = Math.Max(1, (int)(connectedClients.Count * serverSettings.KickVoteRequiredRatio)); var kickVoteEligibleClients = connectedClients.Where(c => (DateTime.Now - c.JoinTime).TotalSeconds > ServerSettings.DisallowKickVoteTime);
int minimumKickVotes = Math.Max(2, (int)(kickVoteEligibleClients.Count() * serverSettings.KickVoteRequiredRatio));
var clientsToKick = connectedClients.FindAll(c => var clientsToKick = connectedClients.FindAll(c =>
c.Connection != OwnerConnection && c.Connection != OwnerConnection &&
!c.HasPermission(ClientPermissions.Kick) && !c.HasPermission(ClientPermissions.Kick) &&
@@ -3581,14 +3582,14 @@ namespace Barotrauma.Networking
List<JobVariant> jobPreferences = new List<JobVariant>(); List<JobVariant> jobPreferences = new List<JobVariant>();
int count = message.ReadByte(); int count = message.ReadByte();
// TODO: modding support?
for (int i = 0; i < Math.Min(count, 3); i++) for (int i = 0; i < Math.Min(count, 3); i++)
{ {
string jobIdentifier = message.ReadString(); string jobIdentifier = message.ReadString();
int variant = message.ReadByte(); int variant = message.ReadByte();
if (JobPrefab.Prefabs.ContainsKey(jobIdentifier)) if (JobPrefab.Prefabs.TryGet(jobIdentifier, out JobPrefab jobPrefab))
{ {
jobPreferences.Add(new JobVariant(JobPrefab.Prefabs[jobIdentifier], variant)); if (jobPrefab.HiddenJob) { continue; }
jobPreferences.Add(new JobVariant(jobPrefab, variant));
} }
} }
@@ -188,9 +188,9 @@ namespace Barotrauma
} }
} }
if (client.Character?.Info?.Job.Prefab.Identifier == "captain" && client.Character.SelectedConstruction != null) if (client.Character?.Info?.Job.Prefab.Identifier == "captain" && client.Character.SelectedItem != null)
{ {
if (client.Character.SelectedConstruction.GetComponent<Steering>() != null) if (client.Character.SelectedItem.GetComponent<Steering>() != null)
{ {
AdjustKarma(client.Character, SteerSubKarmaIncrease * deltaTime, "Steering the sub"); AdjustKarma(client.Character, SteerSubKarmaIncrease * deltaTime, "Steering the sub");
} }
@@ -125,7 +125,7 @@ namespace Barotrauma.Networking
return characterToRespawnCount >= GetMinCharactersToRespawn(); return characterToRespawnCount >= GetMinCharactersToRespawn();
} }
partial void UpdateWaiting(float deltaTime) partial void UpdateWaiting(float _)
{ {
if (RespawnShuttle != null) if (RespawnShuttle != null)
{ {
@@ -487,6 +487,20 @@ namespace Barotrauma.Networking
{ {
AutoItemPlacer.RegenerateLoot(RespawnShuttle, respawnContainer); AutoItemPlacer.RegenerateLoot(RespawnShuttle, respawnContainer);
} }
//try to put the items in containers in the shuttle
foreach (var respawnItem in respawnItems)
{
foreach (Item shuttleItem in RespawnShuttle.GetItems(alsoFromConnectedSubs: false))
{
if (shuttleItem.NonInteractable || shuttleItem.NonPlayerTeamInteractable) { continue; }
var container = shuttleItem.GetComponent<ItemContainer>();
if (container != null && container.Inventory.TryPutItem(respawnItem, user: null))
{
break;
}
}
}
} }
var characterData = campaign?.GetClientCharacterData(clients[i]); var characterData = campaign?.GetClientCharacterData(clients[i]);
@@ -254,15 +254,21 @@ namespace Barotrauma
break; break;
case VoteType.Kick: case VoteType.Kick:
byte kickedClientID = inc.ReadByte(); byte kickedClientID = inc.ReadByte();
if ((DateTime.Now - sender.JoinTime).TotalSeconds > GameMain.Server.ServerSettings.DisallowKickVoteTime)
Client kicked = GameMain.Server.ConnectedClients.Find(c => c.ID == kickedClientID);
if (kicked != null && kicked.Connection != GameMain.Server.OwnerConnection && !kicked.HasKickVoteFrom(sender))
{ {
kicked.AddKickVote(sender); GameMain.Server.SendDirectChatMessage($"ServerMessage.kickvotedisallowed", sender);
Client.UpdateKickVotes(GameMain.Server.ConnectedClients);
GameMain.Server.SendChatMessage($"ServerMessage.HasVotedToKick~[initiator]={sender.Name}~[target]={kicked.Name}", ChatMessageType.Server, null);
}
}
else
{
Client kicked = GameMain.Server.ConnectedClients.Find(c => c.ID == kickedClientID);
if (kicked != null && kicked.Connection != GameMain.Server.OwnerConnection && !kicked.HasKickVoteFrom(sender))
{
kicked.AddKickVote(sender);
Client.UpdateKickVotes(GameMain.Server.ConnectedClients);
GameMain.Server.SendChatMessage($"ServerMessage.HasVotedToKick~[initiator]={sender.Name}~[target]={kicked.Name}", ChatMessageType.Server, null);
}
}
break; break;
case VoteType.StartRound: case VoteType.StartRound:
bool ready = inc.ReadBoolean(); bool ready = inc.ReadBoolean();
@@ -55,6 +55,10 @@ namespace Barotrauma
#if LINUX #if LINUX
setLinuxEnv(); setLinuxEnv();
AppDomain.CurrentDomain.ProcessExit += (s, e) =>
{
GameMain.ShouldRun = false;
};
#endif #endif
Console.WriteLine("Barotrauma Dedicated Server " + GameMain.Version + Console.WriteLine("Barotrauma Dedicated Server " + GameMain.Version +
" (" + AssemblyInfo.BuildString + ", branch " + AssemblyInfo.GitBranch + ", revision " + AssemblyInfo.GitRevision + ")"); " (" + AssemblyInfo.BuildString + ", branch " + AssemblyInfo.GitBranch + ", revision " + AssemblyInfo.GitRevision + ")");
@@ -93,7 +97,7 @@ namespace Barotrauma
private static void CrashHandler(object sender, UnhandledExceptionEventArgs args) private static void CrashHandler(object sender, UnhandledExceptionEventArgs args)
{ {
void swallowExceptions(Action action) static void swallowExceptions(Action action)
{ {
try try
{ {
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.18.15.0</Version> <Version>0.19.0.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright> <Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
@@ -60,7 +60,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" Exclude="..\BarotraumaShared\Data\Saves\*.save" /> <Content Include="..\BarotraumaShared\**\*" CopyToOutputDirectory="PreserveNewest" Exclude="..\BarotraumaShared\Data\Saves\*.save;..\BarotraumaShared\ModLists\*.xml" />
<Content Remove="..\BarotraumaShared\**\*.cs" /> <Content Remove="..\BarotraumaShared\**\*.cs" />
<Content Remove="..\BarotraumaShared\**\*.props" /> <Content Remove="..\BarotraumaShared\**\*.props" />
<Compile Include="..\BarotraumaShared\**\*.cs" /> <Compile Include="..\BarotraumaShared\**\*.cs" />
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<mods name="Release checklist mods">
<Vanilla />
<Workshop name="FrithsBiomes" id="2020332046" />
<Workshop name="Improved Husks" id="2085783214" />
<Workshop name="Meaningful Upgrades" id="2183524355" />
<Workshop name="Community Conversation Pack" id="2435017882" />
<Workshop name="Stations from beyond" id="2585543390" />
<Workshop name="FrithsMissionTweak" id="2788861460" />
<Workshop name="New Wrecks For Barotrauma (With sellable wrecks)" id="2184257427" />
<Workshop name="DynamicEuropa" id="2532991202" />
<Workshop name="32x Stack" id="2683570256" />
<Workshop name="Transparent Diving Helmets" id="2670466527" />
<Workshop name="Better Vanilla Weapons" id="2655476933" />
<Workshop name="AnimEuropa" id="2723467015" />
</mods>
@@ -511,9 +511,9 @@ namespace Barotrauma
{ {
newDir = Direction.Left; newDir = Direction.Left;
} }
if (Character.SelectedConstruction != null) if (Character.SelectedItem != null)
{ {
Character.SelectedConstruction.SecondaryUse(deltaTime, Character); Character.SelectedItem.SecondaryUse(deltaTime, Character);
} }
} }
else if (AutoFaceMovement && Math.Abs(Character.AnimController.TargetMovement.X) > 0.1f && !Character.AnimController.InWater) else if (AutoFaceMovement && Math.Abs(Character.AnimController.TargetMovement.X) > 0.1f && !Character.AnimController.InWater)
@@ -2148,7 +2148,7 @@ namespace Barotrauma
if (c.Removed) { continue; } if (c.Removed) { continue; }
if (c.TeamID != team) { continue; } if (c.TeamID != team) { continue; }
if (c.IsIncapacitated) { continue; } if (c.IsIncapacitated) { continue; }
if (c.SelectedConstruction == target.Item) if (c.SelectedItem == target.Item)
{ {
operatingCharacter = c; operatingCharacter = c;
return true; return true;
@@ -2185,7 +2185,7 @@ namespace Barotrauma
if (c.IsIncapacitated) { continue; } if (c.IsIncapacitated) { continue; }
if (c.IsPlayer) if (c.IsPlayer)
{ {
if (c.SelectedConstruction == target.Item) if (c.SelectedItem == target.Item)
{ {
// If the other character is player, don't try to operate // If the other character is player, don't try to operate
other = c; other = c;
@@ -79,7 +79,8 @@ namespace Barotrauma
{ {
pathFinder = new PathFinder(WayPoint.WayPointList.FindAll(wp => wp.SpawnType == SpawnType.Path), true) pathFinder = new PathFinder(WayPoint.WayPointList.FindAll(wp => wp.SpawnType == SpawnType.Path), true)
{ {
GetNodePenalty = GetNodePenalty GetNodePenalty = GetNodePenalty,
GetSingleNodePenalty = GetSingleNodePenalty
}; };
this.canOpenDoors = canOpenDoors; this.canOpenDoors = canOpenDoors;
@@ -360,7 +361,7 @@ namespace Barotrauma
Ladder nextLadder = GetNextLadder(); Ladder nextLadder = GetNextLadder();
var ladders = currentLadder ?? nextLadder; var ladders = currentLadder ?? nextLadder;
bool useLadders = canClimb && ladders != null && steering.LengthSquared() > 0.1f && (!isDiving || steering.Y > 1); bool useLadders = canClimb && ladders != null && steering.LengthSquared() > 0.1f && (!isDiving || steering.Y > 1);
if (useLadders && character.SelectedConstruction != ladders.Item) if (useLadders && character.SelectedSecondaryItem != ladders.Item)
{ {
if (character.CanInteractWith(ladders.Item)) if (character.CanInteractWith(ladders.Item))
{ {
@@ -372,7 +373,7 @@ namespace Barotrauma
// Try to select the previous ladder, unless it's already selected, unless the previous ladder is not adjacent to the current ladder. // Try to select the previous ladder, unless it's already selected, unless the previous ladder is not adjacent to the current ladder.
// The intention of this code is to prevent the bots from dropping from the "double ladders". // The intention of this code is to prevent the bots from dropping from the "double ladders".
var previousLadders = currentPath.PrevNode?.Ladders; var previousLadders = currentPath.PrevNode?.Ladders;
if (previousLadders != null && previousLadders != ladders && character.SelectedConstruction != previousLadders.Item && if (previousLadders != null && previousLadders != ladders && character.SelectedSecondaryItem != previousLadders.Item &&
character.CanInteractWith(previousLadders.Item) && Math.Abs(previousLadders.Item.WorldPosition.X - ladders.Item.WorldPosition.X) < 5) character.CanInteractWith(previousLadders.Item) && Math.Abs(previousLadders.Item.WorldPosition.X - ladders.Item.WorldPosition.X) < 5)
{ {
previousLadders.Item.TryInteract(character, forceSelectKey: true); previousLadders.Item.TryInteract(character, forceSelectKey: true);
@@ -382,8 +383,7 @@ namespace Barotrauma
var collider = character.AnimController.Collider; var collider = character.AnimController.Collider;
if (character.IsClimbing && !useLadders) if (character.IsClimbing && !useLadders)
{ {
character.AnimController.Anim = AnimController.Animation.None; character.StopClimbing();
character.SelectedConstruction = null;
} }
if (character.IsClimbing && useLadders) if (character.IsClimbing && useLadders)
{ {
@@ -402,15 +402,14 @@ namespace Barotrauma
// We need some margin, because if a hatch has closed, it's possible that the height from floor is slightly negative. // We need some margin, because if a hatch has closed, it's possible that the height from floor is slightly negative.
bool isAboveFloor = heightFromFloor > -0.1f; bool isAboveFloor = heightFromFloor > -0.1f;
// If the next waypoint is horizontally far, we don't want to keep holding the ladders // If the next waypoint is horizontally far, we don't want to keep holding the ladders
if (isAboveFloor && (nextLadder == null || Math.Abs(currentPath.CurrentNode.WorldPosition.X - currentPath.NextNode.WorldPosition.X) > 50)) if (isAboveFloor && !currentPath.IsAtEndNode && (nextLadder == null || Math.Abs(currentPath.CurrentNode.WorldPosition.X - currentPath.NextNode.WorldPosition.X) > 50))
{ {
character.AnimController.Anim = AnimController.Animation.None; character.StopClimbing();
character.SelectedConstruction = null;
} }
else if (nextLadder != null && !nextLadderSameAsCurrent) else if (nextLadder != null && !nextLadderSameAsCurrent)
{ {
// Try to change the ladder (hatches between two submarines) // Try to change the ladder (hatches between two submarines)
if (character.SelectedConstruction != nextLadder.Item && character.CanInteractWith(nextLadder.Item)) if (character.SelectedSecondaryItem != nextLadder.Item && character.CanInteractWith(nextLadder.Item))
{ {
if (nextLadder.Item.TryInteract(character, forceSelectKey: true)) if (nextLadder.Item.TryInteract(character, forceSelectKey: true))
{ {
@@ -418,7 +417,7 @@ namespace Barotrauma
} }
} }
} }
if (isAboveFloor || nextLadderSameAsCurrent || nextLadder == null && Math.Abs(diff.Y) < 10) if (!currentPath.IsAtEndNode && (isAboveFloor || nextLadderSameAsCurrent || nextLadder == null && Math.Abs(diff.Y) < 10))
{ {
NextNode(!doorsChecked); NextNode(!doorsChecked);
} }
@@ -756,42 +755,8 @@ namespace Barotrauma
private float? GetNodePenalty(PathNode node, PathNode nextNode) private float? GetNodePenalty(PathNode node, PathNode nextNode)
{ {
if (character == null) { return 0.0f; } if (character == null) { return 0.0f; }
if (nextNode.Waypoint.isObstructed) { return null; } float? penalty = GetSingleNodePenalty(nextNode);
float penalty = 0.0f; if (penalty == null) { return null; }
if (nextNode.Waypoint.ConnectedGap != null && nextNode.Waypoint.ConnectedGap.Open < 0.9f)
{
var door = nextNode.Waypoint.ConnectedDoor;
if (door == null)
{
penalty = 100.0f;
}
else
{
if (!CanAccessDoor(door, button =>
{
// Ignore buttons that are on the wrong side of the door
if (door.IsHorizontal)
{
if (Math.Sign(button.Item.WorldPosition.Y - door.Item.WorldPosition.Y) != Math.Sign(character.WorldPosition.Y - door.Item.WorldPosition.Y))
{
return false;
}
}
else
{
if (Math.Sign(button.Item.WorldPosition.X - door.Item.WorldPosition.X) != Math.Sign(character.WorldPosition.X - door.Item.WorldPosition.X))
{
return false;
}
}
return true;
}))
{
return null;
}
}
}
bool nextNodeAboveWaterLevel = nextNode.Waypoint.CurrentHull != null && nextNode.Waypoint.CurrentHull.Surface < nextNode.Waypoint.Position.Y; bool nextNodeAboveWaterLevel = nextNode.Waypoint.CurrentHull != null && nextNode.Waypoint.CurrentHull.Surface < nextNode.Waypoint.Position.Y;
//non-humanoids can't climb up ladders //non-humanoids can't climb up ladders
if (!(character.AnimController is HumanoidAnimController)) if (!(character.AnimController is HumanoidAnimController))
@@ -839,6 +804,47 @@ namespace Barotrauma
return penalty; return penalty;
} }
private float? GetSingleNodePenalty(PathNode node)
{
if (node.Waypoint.isObstructed) { return null; }
if (node.IsBlocked()) { return null; }
float penalty = 0.0f;
if (node.Waypoint.ConnectedGap != null && node.Waypoint.ConnectedGap.Open < 0.9f)
{
var door = node.Waypoint.ConnectedDoor;
if (door == null)
{
penalty = 100.0f;
}
else
{
if (!CanAccessDoor(door, button =>
{
// Ignore buttons that are on the wrong side of the door
if (door.IsHorizontal)
{
if (Math.Sign(button.Item.WorldPosition.Y - door.Item.WorldPosition.Y) != Math.Sign(character.WorldPosition.Y - door.Item.WorldPosition.Y))
{
return false;
}
}
else
{
if (Math.Sign(button.Item.WorldPosition.X - door.Item.WorldPosition.X) != Math.Sign(character.WorldPosition.X - door.Item.WorldPosition.X))
{
return false;
}
}
return true;
}))
{
return null;
}
}
}
return penalty;
}
public static float smallRoomSize = 500; public static float smallRoomSize = 500;
public void Wander(float deltaTime, float wallAvoidDistance = 150, bool stayStillInTightSpace = true) public void Wander(float deltaTime, float wallAvoidDistance = 150, bool stayStillInTightSpace = true)
{ {
@@ -14,13 +14,11 @@ namespace Barotrauma
public readonly LanguageIdentifier Language; public readonly LanguageIdentifier Language;
public readonly List<NPCConversation> Conversations; public readonly List<NPCConversation> Conversations;
public readonly Dictionary<Identifier, NPCPersonalityTrait> PersonalityTraits;
public NPCConversationCollection(NPCConversationsFile file, ContentXElement element) : base(file, element.GetAttributeIdentifier("identifier", "")) public NPCConversationCollection(NPCConversationsFile file, ContentXElement element) : base(file, element.GetAttributeIdentifier("identifier", ""))
{ {
Language = element.GetAttributeIdentifier("language", "English").ToLanguageIdentifier(); Language = element.GetAttributeIdentifier("language", "English").ToLanguageIdentifier();
Conversations = new List<NPCConversation>(); Conversations = new List<NPCConversation>();
PersonalityTraits = new Dictionary<Identifier, NPCPersonalityTrait>();
foreach (var subElement in element.Elements()) foreach (var subElement in element.Elements())
{ {
Identifier elemName = new Identifier(subElement.Name.LocalName); Identifier elemName = new Identifier(subElement.Name.LocalName);
@@ -28,11 +26,6 @@ namespace Barotrauma
{ {
Conversations.Add(new NPCConversation(subElement)); Conversations.Add(new NPCConversation(subElement));
} }
else if (elemName == "PersonalityTrait")
{
var personalityTrait = new NPCPersonalityTrait(subElement);
PersonalityTraits.Add(personalityTrait.Name, personalityTrait);
}
} }
} }
@@ -361,10 +354,13 @@ namespace Barotrauma
private static float GetConversationProbability(NPCConversation conversation) private static float GetConversationProbability(NPCConversation conversation)
{ {
int index = previousConversations.IndexOf(conversation); //prefer choosing conversations with more flags (= for more specific situations) when possible
if (index < 0) return 10.0f; float baseProbability = MathF.Pow(conversation.Flags.Count + 1, 2);
return 1.0f - 1.0f / (index + 1); int index = previousConversations.IndexOf(conversation);
if (index < 0) { return baseProbability * 10.0f; }
return baseProbability + 1.0f - 1.0f / (index + 1);
} }
#if DEBUG #if DEBUG
@@ -512,6 +512,23 @@ namespace Barotrauma
foreach (var weapon in weaponList) foreach (var weapon in weaponList)
{ {
float priority = weapon.CombatPriority; float priority = weapon.CombatPriority;
if (weapon is RepairTool repairTool)
{
switch (repairTool.UsableIn)
{
case RepairTool.UseEnvironment.Air:
if (character.InWater) { continue; }
break;
case RepairTool.UseEnvironment.Water:
if (!character.InWater) { continue; }
break;
case RepairTool.UseEnvironment.None:
continue;
case RepairTool.UseEnvironment.Both:
default:
break;
}
}
if (prioritizeMelee) if (prioritizeMelee)
{ {
if (weapon is MeleeWeapon) if (weapon is MeleeWeapon)
@@ -196,10 +196,7 @@ namespace Barotrauma
character.AIController.SteeringManager.Reset(); character.AIController.SteeringManager.Reset();
return; return;
} }
if (!character.IsClimbing) character.SelectedItem = null;
{
character.SelectedConstruction = null;
}
if (Target is Entity e) if (Target is Entity e)
{ {
if (e.Removed) if (e.Removed)
@@ -647,7 +644,7 @@ namespace Barotrauma
{ {
if (character.IsClimbing) if (character.IsClimbing)
{ {
if (SteeringManager == PathSteering && PathSteering.CurrentPath != null && !PathSteering.CurrentPath.Finished && PathSteering.IsCurrentNodeLadder) if (SteeringManager == PathSteering && PathSteering.CurrentPath != null && !PathSteering.CurrentPath.Finished && PathSteering.IsCurrentNodeLadder && !PathSteering.CurrentPath.IsAtEndNode)
{ {
if (Target.WorldPosition.Y > character.WorldPosition.Y) if (Target.WorldPosition.Y > character.WorldPosition.Y)
{ {
@@ -694,7 +691,7 @@ namespace Barotrauma
{ {
if (Target is Item item) if (Target is Item item)
{ {
if (!character.IsClimbing && character.CanInteractWith(item, out _, checkLinked: false)) { IsCompleted = true; } if (character.CanInteractWith(item, out _, checkLinked: false)) { IsCompleted = true; }
} }
else if (Target is Character targetCharacter) else if (Target is Character targetCharacter)
{ {
@@ -161,10 +161,7 @@ namespace Barotrauma
character.DeselectCharacter(); character.DeselectCharacter();
} }
if (!character.IsClimbing) character.SelectedItem = null;
{
character.SelectedConstruction = null;
}
CleanupItems(deltaTime); CleanupItems(deltaTime);
@@ -310,7 +307,7 @@ namespace Barotrauma
if (character.AnimController.GetHeightFromFloor() < 0.1f) if (character.AnimController.GetHeightFromFloor() < 0.1f)
{ {
character.AnimController.Anim = AnimController.Animation.None; character.AnimController.Anim = AnimController.Animation.None;
character.SelectedConstruction = null; character.SelectedSecondaryItem = null;
} }
return; return;
} }
@@ -375,7 +372,7 @@ namespace Barotrauma
} }
chairCheckTimer -= deltaTime; chairCheckTimer -= deltaTime;
if (chairCheckTimer <= 0.0f && character.SelectedConstruction == null) if (chairCheckTimer <= 0.0f && character.SelectedSecondaryItem == null)
{ {
foreach (Item item in Item.ItemList) foreach (Item item in Item.ItemList)
{ {
@@ -205,7 +205,7 @@ namespace Barotrauma
if (!character.IsClimbing && character.CanInteractWith(target.Item, out _, checkLinked: false)) if (!character.IsClimbing && character.CanInteractWith(target.Item, out _, checkLinked: false))
{ {
HumanAIController.FaceTarget(target.Item); HumanAIController.FaceTarget(target.Item);
if (character.SelectedConstruction != target.Item) if (character.SelectedItem != target.Item)
{ {
target.Item.TryInteract(character, forceSelectKey: true); target.Item.TryInteract(character, forceSelectKey: true);
} }
@@ -26,7 +26,7 @@ namespace Barotrauma
private bool IsRepairing() => IsRepairing(character, Item); private bool IsRepairing() => IsRepairing(character, Item);
private readonly bool isPriority; private readonly bool isPriority;
public static bool IsRepairing(Character character, Item item) => character.SelectedConstruction == item && item.Repairables.Any(r => r.CurrentFixer == character); public static bool IsRepairing(Character character, Item item) => character.SelectedItem == item && item.Repairables.Any(r => r.CurrentFixer == character);
public AIObjectiveRepairItem(Character character, Item item, AIObjectiveManager objectiveManager, float priorityModifier = 1, bool isPriority = false) public AIObjectiveRepairItem(Character character, Item item, AIObjectiveManager objectiveManager, float priorityModifier = 1, bool isPriority = false)
: base(character, objectiveManager, priorityModifier) : base(character, objectiveManager, priorityModifier)
@@ -165,7 +165,7 @@ namespace Barotrauma
return; return;
} }
} }
if (!character.IsClimbing && character.CanInteractWith(Item, out _, checkLinked: false)) if (character.CanInteractWith(Item, out _, checkLinked: false))
{ {
waitTimer += deltaTime; waitTimer += deltaTime;
if (waitTimer < WaitTimeBeforeRepair) { return; } if (waitTimer < WaitTimeBeforeRepair) { return; }
@@ -184,12 +184,12 @@ namespace Barotrauma
} }
if (!Abandon) if (!Abandon)
{ {
if (character.SelectedConstruction != Item) if (character.SelectedItem != Item)
{ {
if (Item.TryInteract(character, ignoreRequiredItems: true, forceSelectKey: true) || if (Item.TryInteract(character, ignoreRequiredItems: true, forceSelectKey: true) ||
Item.TryInteract(character, ignoreRequiredItems: true, forceUseKey: true)) Item.TryInteract(character, ignoreRequiredItems: true, forceUseKey: true))
{ {
character.SelectedConstruction = Item; character.SelectedItem = Item;
} }
else else
{ {
@@ -232,8 +232,6 @@ namespace Barotrauma
previousCondition = -1; previousCondition = -1;
var objective = new AIObjectiveGoTo(Item, character, objectiveManager) var objective = new AIObjectiveGoTo(Item, character, objectiveManager)
{ {
// Don't stop in ladders, because we can't interact with other items while holding the ladders.
endNodeFilter = node => node.Waypoint.Ladders == null,
TargetName = Item.Name TargetName = Item.Name
}; };
if (repairTool != null) if (repairTool != null)
@@ -67,7 +67,7 @@ namespace Barotrauma
if (!ViableForRepair(item, character, HumanAIController)) { return false; }; if (!ViableForRepair(item, character, HumanAIController)) { return false; };
if (!Objectives.ContainsKey(item)) if (!Objectives.ContainsKey(item))
{ {
if (item != character.SelectedConstruction) if (item != character.SelectedItem)
{ {
if (NearlyFullCondition(item)) { return false; } if (NearlyFullCondition(item)) { return false; }
} }
@@ -96,7 +96,7 @@ namespace Barotrauma
protected override float TargetEvaluation() protected override float TargetEvaluation()
{ {
var selectedItem = character.SelectedConstruction; var selectedItem = character.SelectedItem;
if (selectedItem != null && AIObjectiveRepairItem.IsRepairing(character, selectedItem) && selectedItem.ConditionPercentage < 100) if (selectedItem != null && AIObjectiveRepairItem.IsRepairing(character, selectedItem) && selectedItem.ConditionPercentage < 100)
{ {
// Don't stop fixing until completely done // Don't stop fixing until completely done
@@ -109,6 +109,8 @@ namespace Barotrauma
{ {
public delegate float? GetNodePenaltyHandler(PathNode node, PathNode prevNode); public delegate float? GetNodePenaltyHandler(PathNode node, PathNode prevNode);
public GetNodePenaltyHandler GetNodePenalty; public GetNodePenaltyHandler GetNodePenalty;
public delegate float? GetSingleNodePenaltyHandler(PathNode node);
public GetSingleNodePenaltyHandler GetSingleNodePenalty;
private readonly List<PathNode> nodes; private readonly List<PathNode> nodes;
private readonly bool isCharacter; private readonly bool isCharacter;
@@ -282,8 +284,6 @@ namespace Barotrauma
} }
//avoid stopping at a doorway //avoid stopping at a doorway
if (node.Waypoint.ConnectedDoor != null) { node.TempDistance *= 10.0f; } if (node.Waypoint.ConnectedDoor != null) { node.TempDistance *= 10.0f; }
//avoid stopping at a ladder
if (node.Waypoint.Ladders != null) { node.TempDistance *= 10.0f; }
} }
//optimization: node extremely far (> 100m / 800 m) from the end position, don't try to use it as an end node //optimization: node extremely far (> 100m / 800 m) from the end position, don't try to use it as an end node
if (node.TempDistance > (InsideSubmarine ? 100.0f * 100.0f : 800.0f * 800.0f)) if (node.TempDistance > (InsideSubmarine ? 100.0f * 100.0f : 800.0f * 800.0f))
@@ -325,15 +325,24 @@ namespace Barotrauma
#endif #endif
return new SteeringPath(true); return new SteeringPath(true);
} }
var path = FindPath(startNode, endNode, nodeFilter, errorMsgStr, minGapSize); return FindPath(startNode, endNode, nodeFilter, errorMsgStr, minGapSize);
return path;
bool IsWaypointVisible(PathNode node, Vector2 rayStart, bool checkVisibility = true) bool IsValidStartNode(PathNode node) => IsValidNode(node, (isCharacter, start), startNodeFilter);
bool IsValidEndNode(PathNode node) => IsValidNode(node, (isCharacter && checkVisibility, end), endNodeFilter);
bool IsValidNode(PathNode node, (bool check, Vector2 start) visibilityCheck, Func<PathNode, bool> extraFilter)
{ {
//if searching for a path inside the sub, make sure the waypoint is visible if (nodeFilter != null && !nodeFilter(node)) { return false; }
if (checkVisibility && isCharacter) if (extraFilter != null && !extraFilter(node)) { return false; }
if (GetSingleNodePenalty != null && GetSingleNodePenalty(node) == null) { return false; }
if (node.Waypoint.ConnectedGap != null)
{ {
var body = Submarine.PickBody(rayStart, node.TempPosition, if (!CanFitThroughGap(node.Waypoint.ConnectedGap, minGapSize)) { return false; }
}
if (visibilityCheck.check)
{
var body = Submarine.PickBody(visibilityCheck.start, node.TempPosition,
collisionCategory: Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionStairs); collisionCategory: Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionStairs);
if (body != null) if (body != null)
{ {
@@ -344,36 +353,6 @@ namespace Barotrauma
} }
return true; return true;
} }
bool IsValidStartNode(PathNode node)
{
if (nodeFilter != null && !nodeFilter(node)) { return false; }
if (startNodeFilter != null && !startNodeFilter(node)) { return false; }
if (node.Waypoint.isObstructed) { return false; }
// Always check the visibility for the start node
if (!IsWaypointVisible(node, start)) { return false; }
if (node.IsBlocked()) { return false; }
if (node.Waypoint.ConnectedGap != null)
{
if (!CanFitThroughGap(node.Waypoint.ConnectedGap, minGapSize)) { return false; }
}
return true;
}
bool IsValidEndNode(PathNode node)
{
if (nodeFilter != null && !nodeFilter(node)) { return false; }
if (endNodeFilter != null && !endNodeFilter(node)) { return false; }
if (node.Waypoint.isObstructed) { return false; }
// Only check the visibility for the end node when allowed (fix leaks)
if (!IsWaypointVisible(node, end, checkVisibility: checkVisibility)) { return false; }
if (node.IsBlocked()) { return false; }
if (node.Waypoint.ConnectedGap != null)
{
if (!CanFitThroughGap(node.Waypoint.ConnectedGap, minGapSize)) { return false; }
}
return true;
}
} }
private SteeringPath FindPath(PathNode start, PathNode end, Func<PathNode, bool> filter = null, string errorMsgStr = "", float minGapSize = 0) private SteeringPath FindPath(PathNode start, PathNode end, Func<PathNode, bool> filter = null, string errorMsgStr = "", float minGapSize = 0)
@@ -402,9 +381,7 @@ namespace Barotrauma
foreach (PathNode node in nodes) foreach (PathNode node in nodes)
{ {
if (node.state != 1 || node.F > dist) { continue; } if (node.state != 1 || node.F > dist) { continue; }
if (isCharacter && node.Waypoint.isObstructed) { continue; }
if (filter != null && !filter(node)) { continue; } if (filter != null && !filter(node)) { continue; }
if (node.IsBlocked()) { continue; }
if (node.Waypoint.ConnectedGap != null) if (node.Waypoint.ConnectedGap != null)
{ {
if (!CanFitThroughGap(node.Waypoint.ConnectedGap, minGapSize)) { continue; } if (!CanFitThroughGap(node.Waypoint.ConnectedGap, minGapSize)) { continue; }
@@ -516,6 +493,3 @@ namespace Barotrauma
private bool CanFitThroughGap(Gap gap, float minWidth) => gap.IsHorizontal ? gap.RectHeight > minWidth : gap.RectWidth > minWidth; private bool CanFitThroughGap(Gap gap, float minWidth) => gap.IsHorizontal ? gap.RectHeight > minWidth : gap.RectWidth > minWidth;
} }
} }
@@ -115,6 +115,8 @@ namespace Barotrauma
} }
} }
public bool IsAtEndNode => currentIndex >= nodes.Count - 1;
public List<WayPoint> Nodes public List<WayPoint> Nodes
{ {
get { return nodes; } get { return nodes; }
@@ -101,7 +101,7 @@ namespace Barotrauma
{ {
if (InWater || !CanWalk) if (InWater || !CanWalk)
{ {
return TargetMovement.LengthSquared() > MathUtils.Pow2(SwimSlowParams.MovementSpeed); return TargetMovement.LengthSquared() > MathUtils.Pow2(SwimSlowParams.MovementSpeed + 0.0001f);
} }
else else
{ {
@@ -134,9 +134,12 @@ namespace Barotrauma
} }
} }
public enum Animation { None, Climbing, UsingConstruction, Struggle, CPR }; public enum Animation { None, Climbing, UsingItem, Struggle, CPR, UsingItemWhileClimbing };
public Animation Anim; public Animation Anim;
public bool IsUsingItem => Anim == Animation.UsingItem || Anim == Animation.UsingItemWhileClimbing;
public bool IsClimbing => Anim == Animation.Climbing || Anim == Animation.UsingItemWhileClimbing;
public Vector2 AimSourceWorldPos public Vector2 AimSourceWorldPos
{ {
get get
@@ -280,7 +283,7 @@ namespace Barotrauma
public void UpdateUseItem(bool allowMovement, Vector2 handWorldPos) public void UpdateUseItem(bool allowMovement, Vector2 handWorldPos)
{ {
useItemTimer = 0.5f; useItemTimer = 0.5f;
Anim = Animation.UsingConstruction; StartUsingItem();
if (!allowMovement) if (!allowMovement)
{ {
@@ -359,8 +362,13 @@ namespace Barotrauma
Vector2 itemPos = aim ? aimPos : holdPos; Vector2 itemPos = aim ? aimPos : holdPos;
var controller = character.SelectedConstruction?.GetComponent<Controller>(); var controller = character.SelectedItem?.GetComponent<Controller>();
bool usingController = controller != null && !controller.AllowAiming; bool usingController = controller != null && !controller.AllowAiming;
if (!usingController)
{
controller = character.SelectedSecondaryItem?.GetComponent<Controller>();
usingController = controller != null && !controller.AllowAiming;
}
bool isClimbing = character.IsClimbing && Math.Abs(character.AnimController.TargetMovement.Y) > 0.01f; bool isClimbing = character.IsClimbing && Math.Abs(character.AnimController.TargetMovement.Y) > 0.01f;
float itemAngle; float itemAngle;
Holdable holdable = item.GetComponent<Holdable>(); Holdable holdable = item.GetComponent<Holdable>();
@@ -722,5 +730,45 @@ namespace Barotrauma
CalculateArmLengths(); CalculateArmLengths();
} }
} }
private void StartAnimation(Animation animation)
{
if (animation == Animation.UsingItem)
{
Anim = IsClimbing ? Animation.UsingItemWhileClimbing : Animation.UsingItem;
}
else if (animation == Animation.Climbing)
{
Anim = IsUsingItem ? Animation.UsingItemWhileClimbing : Animation.Climbing;
}
else
{
Anim = animation;
}
}
private void StopAnimation(Animation animation)
{
if (animation == Animation.UsingItem)
{
Anim = IsClimbing ? Animation.Climbing : Animation.None;
}
else if (animation == Animation.Climbing)
{
Anim = IsUsingItem ? Animation.UsingItem : Animation.None;
}
else
{
Anim = Animation.None;
}
}
public void StartUsingItem() => StartAnimation(Animation.UsingItem);
public void StartClimbing() => StartAnimation(Animation.Climbing);
public void StopUsingItem() => StopAnimation(Animation.UsingItem);
public void StopClimbing() => StopAnimation(Animation.Climbing);
} }
} }

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