v0.14.6.0

This commit is contained in:
Joonas Rikkonen
2021-06-17 17:54:52 +03:00
parent 3f324b14e8
commit c27e2ea5ab
348 changed files with 13156 additions and 4266 deletions
@@ -1,4 +1,5 @@
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using System.Collections.Generic;
using System.Linq;
@@ -42,10 +43,7 @@ namespace Barotrauma
public IEnumerable<Item> GetSellableItems(Character character)
{
if (character == null) { return new List<Item>(); }
// Only consider items which have been:
// a) sold in singleplayer or confirmed by server (SellStatus.Confirmed); or
// b) sold locally in multiplayer (SellStatus.Local), but the client has not received a campaing state update yet after selling them
var confirmedSoldEntities = SoldEntities.Where(se => se.Status != SoldEntity.SellStatus.Unconfirmed);
var confirmedSoldEntities = GetConfirmedSoldEntities();
// The bag slot is intentionally left out since we want to be able to sell items from there
var equipmentSlots = new List<InvSlotType>() { InvSlotType.Head, InvSlotType.InnerClothes, InvSlotType.OuterClothes, InvSlotType.Headset, InvSlotType.Card };
return character.Inventory.FindAllItems(item =>
@@ -72,6 +70,43 @@ namespace Barotrauma
}
}
public IEnumerable<Item> GetSellableItemsFromSub()
{
if (Submarine.MainSub == null) { return new List<Item>(); }
var confirmedSoldEntities = GetConfirmedSoldEntities();
return Submarine.MainSub.GetItems(true).FindAll(item =>
{
if (!item.Prefab.CanBeSold) { return false; }
if (item.SpawnedInOutpost) { return false; }
if (!item.Prefab.AllowSellingWhenBroken && item.ConditionPercentage < 90.0f) { return false; }
if (!item.Components.All(c => !(c is Holdable h) || !h.Attachable || !h.Attached)) { return false; }
if (!item.Components.All(c => !(c is Wire w) || w.Connections.All(c => c == null))) { return false; }
if (!ItemAndAllContainersInteractable(item)) { return false; }
if (confirmedSoldEntities.Any(it => it.Item == item)) { return false; }
// There must be no contained items or the contained items must be confirmed as sold
if (!item.ContainedItems.All(it => confirmedSoldEntities.Any(se => se.Item == it))) { return false; }
return true;
}).Distinct();
static bool ItemAndAllContainersInteractable(Item item)
{
do
{
if (!item.IsPlayerTeamInteractable) { return false; }
item = item.Container;
} while (item != null);
return true;
}
}
private IEnumerable<SoldEntity> GetConfirmedSoldEntities()
{
// Only consider items which have been:
// a) sold in singleplayer or confirmed by server (SellStatus.Confirmed); or
// b) sold locally in multiplayer (SellStatus.Local), but the client has not received a campaing state update yet after selling them
return SoldEntities.Where(se => se.Status != SoldEntity.SellStatus.Unconfirmed);
}
public void SetItemsInBuyCrate(List<PurchasedItem> items)
{
ItemsInBuyCrate.Clear();
@@ -119,10 +154,34 @@ namespace Barotrauma
OnItemsInSellCrateChanged?.Invoke();
}
public void SellItems(List<PurchasedItem> itemsToSell)
public void ModifyItemQuantityInSellFromSubCrate(ItemPrefab itemPrefab, int changeInQuantity)
{
var itemsInInventory = GetSellableItems(Character.Controlled);
var canAddToRemoveQueue = campaign.IsSinglePlayer && Entity.Spawner != null;
var itemToSell = ItemsInSellFromSubCrate.Find(i => i.ItemPrefab == itemPrefab);
if (itemToSell != null)
{
itemToSell.Quantity += changeInQuantity;
if (itemToSell.Quantity < 1)
{
ItemsInSellFromSubCrate.Remove(itemToSell);
}
}
else if (changeInQuantity > 0)
{
itemToSell = new PurchasedItem(itemPrefab, changeInQuantity);
ItemsInSellFromSubCrate.Add(itemToSell);
}
OnItemsInSellFromSubCrateChanged?.Invoke();
}
public void SellItems(List<PurchasedItem> itemsToSell, Store.StoreTab sellingMode)
{
var sellableItems = sellingMode switch
{
Store.StoreTab.Sell => GetSellableItems(Character.Controlled),
Store.StoreTab.SellFromSub => GetSellableItemsFromSub(),
_ => throw new System.NotImplementedException(),
};
bool canAddToRemoveQueue = campaign.IsSinglePlayer && Entity.Spawner != null;
var sellerId = GameMain.Client?.ID ?? 0;
// Check all the prices before starting the transaction
@@ -137,7 +196,7 @@ namespace Barotrauma
if (Location.StoreCurrentBalance < itemValue) { continue; }
// TODO: Write logic for prioritizing certain items over others (e.g. lone Battery Cell should be preferred over one inside a Stun Baton)
var matchingItems = itemsInInventory.Where(i => i.Prefab == item.ItemPrefab);
var matchingItems = sellableItems.Where(i => i.Prefab == item.ItemPrefab);
if (matchingItems.Count() <= item.Quantity)
{
foreach (Item i in matchingItems)
@@ -163,12 +222,21 @@ namespace Barotrauma
campaign.Money += itemValue;
// Remove from the sell crate
if (ItemsInSellCrate.Find(pi => pi.ItemPrefab == item.ItemPrefab) is { } itemToSell)
// TODO: Simplify duplicate logic?
if (sellingMode == Store.StoreTab.Sell && ItemsInSellCrate.Find(pi => pi.ItemPrefab == item.ItemPrefab) is { } inventoryItem)
{
itemToSell.Quantity -= item.Quantity;
if (itemToSell.Quantity < 1)
inventoryItem.Quantity -= item.Quantity;
if (inventoryItem.Quantity < 1)
{
ItemsInSellCrate.Remove(itemToSell);
ItemsInSellCrate.Remove(inventoryItem);
}
}
else if(sellingMode == Store.StoreTab.SellFromSub && ItemsInSellFromSubCrate.Find(pi => pi.ItemPrefab == item.ItemPrefab) is { } subItem)
{
subItem.Quantity -= item.Quantity;
if (subItem.Quantity < 1)
{
ItemsInSellFromSubCrate.Remove(subItem);
}
}
}
@@ -71,6 +71,7 @@ namespace Barotrauma
: this(isSinglePlayer)
{
AddCharacterElements(element);
ActiveOrdersElement = element.GetChildElement("activeorders");
}
partial void InitProjectSpecific()
@@ -192,7 +193,8 @@ namespace Barotrauma
{
AbsoluteSpacing = (int)(5 * GUI.Scale),
UserData = "reportbuttons",
CanBeFocused = false
CanBeFocused = false,
Visible = false
};
ReportButtonFrame.RectTransform.AbsoluteOffset = new Point(0, -chatBox.ToggleButton.Rect.Height);
@@ -433,13 +435,20 @@ namespace Barotrauma
Stretch = false
};
var soundIcons = new GUIFrame(new RectTransform(new Vector2(0.8f * iconRelativeWidth, 0.8f), layoutGroup.RectTransform), style: null)
var extraIconFrame = new GUIFrame(new RectTransform(new Vector2(0.8f * iconRelativeWidth, 0.8f), layoutGroup.RectTransform), style: null)
{
CanBeFocused = false,
UserData = "soundicons"
UserData = "extraicons"
};
var soundIconParent = new GUIFrame(new RectTransform(Vector2.One, extraIconFrame.RectTransform), style: null)
{
CanBeFocused = false,
UserData = "soundicons",
Visible = character.IsPlayer
};
new GUIImage(
new RectTransform(Vector2.One, soundIcons.RectTransform),
new RectTransform(Vector2.One, soundIconParent.RectTransform),
GUI.Style.GetComponentStyle("GUISoundIcon").GetDefaultSprite(),
scaleToFit: true)
{
@@ -448,7 +457,7 @@ namespace Barotrauma
Visible = true
};
new GUIImage(
new RectTransform(Vector2.One, soundIcons.RectTransform),
new RectTransform(Vector2.One, soundIconParent.RectTransform),
"GUISoundIconDisabled",
scaleToFit: true)
{
@@ -457,6 +466,16 @@ namespace Barotrauma
Visible = false
};
if (character.IsBot)
{
new GUIFrame(new RectTransform(Vector2.One, extraIconFrame.RectTransform), style: null)
{
CanBeFocused = false,
UserData = "objectiveicon",
Visible = false
};
}
new GUIButton(new RectTransform(new Point((int)commandButtonAbsoluteHeight), background.RectTransform), style: "CrewListCommandButton")
{
ToolTip = TextManager.Get("inputtype.command"),
@@ -624,9 +643,7 @@ namespace Barotrauma
{
if (client?.Character == null) { return; }
if (crewList.Content.GetChildByUserData(client.Character)?
.FindChild(c => c is GUILayoutGroup)?
.GetChildByUserData("soundicons") is GUIComponent soundIcons)
if (GetSoundIconParent(client.Character) is GUIComponent soundIcons)
{
var soundIcon = soundIcons.FindChild(c => c.UserData is Pair<string, float> pair && pair.First == "soundicon");
var soundIconDisabled = soundIcons.FindChild("soundicondisabled");
@@ -638,15 +655,17 @@ namespace Barotrauma
public void SetClientSpeaking(Client client)
{
if (client?.Character != null) { SetCharacterSpeaking(client.Character); }
if (client?.Character != null)
{
SetCharacterSpeaking(client.Character);
}
}
public void SetCharacterSpeaking(Character character)
{
if (crewList.Content.GetChildByUserData(character)?
.FindChild(c => c is GUILayoutGroup)?
.GetChildByUserData("soundicons")?
.FindChild(c => c.UserData is Pair<string, float> pair && pair.First == "soundicon") is GUIComponent soundIcon)
if (character == null || character.IsBot) { return; }
if (GetSoundIconParent(character)?.FindChild(c => c.UserData is Pair<string, float> pair && pair.First == "soundicon") is GUIComponent soundIcon)
{
soundIcon.Color = Color.White;
Pair<string, float> userdata = soundIcon.UserData as Pair<string, float>;
@@ -654,6 +673,19 @@ namespace Barotrauma
}
}
private GUIComponent GetSoundIconParent(GUIComponent characterComponent)
{
return characterComponent?
.FindChild(c => c is GUILayoutGroup)?
.GetChildByUserData("extraicons")?
.GetChildByUserData("soundicons");
}
private GUIComponent GetSoundIconParent(Character character)
{
return GetSoundIconParent(crewList?.Content.GetChildByUserData(character));
}
#endregion
#region Crew List Order Displayment
@@ -770,6 +802,7 @@ namespace Barotrauma
if (icon is GUIImage image)
{
image.Sprite = GetOrderIconSprite(order, option);
image.ToolTip = CreateOrderTooltip(order, option);
}
updatedExistingIcon = true;
}
@@ -835,7 +868,7 @@ namespace Barotrauma
Visible = false
};
int hierarchyIndex = GetOrderIconHierarchyIndex(priority);
int hierarchyIndex = Math.Clamp(CharacterInfo.HighestManualOrderPriority - priority, 0, Math.Max(currentOrderIconList.Content.CountChildren - 1, 0));
if (hierarchyIndex != currentOrderIconList.Content.GetChildIndex(nodeIcon))
{
nodeIcon.RectTransform.RepositionChildInHierarchy(hierarchyIndex);
@@ -878,11 +911,6 @@ namespace Barotrauma
}
}
}
static int GetOrderIconHierarchyIndex(int priority)
{
return CharacterInfo.HighestManualOrderPriority - priority;
}
}
public void AddCurrentOrderIcon(Character character, OrderInfo? orderInfo)
@@ -1020,29 +1048,36 @@ namespace Barotrauma
SetCharacterOrder(character, orderInfo.Order, orderInfo.OrderOption, priority, Character.Controlled);
}
private string CreateOrderTooltip(Order order, string option)
private string CreateOrderTooltip(Order orderPrefab, string option, Entity targetEntity)
{
if (order == null) { return ""; }
if (orderPrefab == null) { return ""; }
if (!string.IsNullOrEmpty(option))
{
return TextManager.GetWithVariables("crewlistordericontooltip",
new string[2] { "[ordername]", "[orderoption]" },
new string[2] { order.Name, order.GetOptionName(option) });
new string[2] { orderPrefab.Name, orderPrefab.GetOptionName(option) });
}
else if (order.TargetEntity is Item targetItem && order.MinimapIcons.ContainsKey(targetItem.Prefab.Identifier))
else if (targetEntity is Item targetItem && targetItem.Prefab.MinimapIcon != null)
{
return TextManager.GetWithVariables("crewlistordericontooltip",
new string[2] { "[ordername]", "[orderoption]" },
new string[2] { order.Name, targetItem.Name });
new string[2] { orderPrefab.Name, targetItem.Name });
}
else
{
return order.Name;
return orderPrefab.Name;
}
}
private string CreateOrderTooltip(OrderInfo orderInfo) =>
CreateOrderTooltip(orderInfo.Order, orderInfo.OrderOption);
private string CreateOrderTooltip(Order order, string option)
{
return CreateOrderTooltip(order?.Prefab ?? order, option, order?.TargetEntity);
}
private string CreateOrderTooltip(OrderInfo orderInfo)
{
return CreateOrderTooltip(orderInfo.Order?.Prefab ?? orderInfo.Order, orderInfo.OrderOption, orderInfo.Order?.TargetEntity);
}
private Sprite GetOrderIconSprite(Order order, string option)
{
@@ -1052,9 +1087,9 @@ namespace Barotrauma
{
order.Prefab.OptionSprites.TryGetValue(option, out sprite);
}
if (sprite == null && order.TargetEntity is Item targetItem && order.MinimapIcons.Any())
if (sprite == null && order.TargetEntity is Item targetItem && targetItem.Prefab.MinimapIcon != null)
{
order.MinimapIcons.TryGetValue(targetItem.Prefab.Identifier, out sprite);
sprite = targetItem.Prefab.MinimapIcon;
}
return sprite ?? order.SymbolSprite;
}
@@ -1244,10 +1279,7 @@ namespace Barotrauma
//make the previously selected character wait in place for some time
//(so they don't immediately start idling and walking away from their station)
var aiController = Character.Controlled?.AIController;
if (aiController != null)
{
aiController.Reset();
}
aiController?.Reset();
DisableCommandUI();
Character.Controlled = character;
HintManager.OnChangeCharacter();
@@ -1255,24 +1287,34 @@ namespace Barotrauma
private int TryAdjustIndex(int amount)
{
int index = Character.Controlled == null ? 0 :
crewList.Content.GetChildIndex(crewList.Content.GetChildByUserData(Character.Controlled)) + amount;
if (Character.Controlled == null) { return 0; }
int currentIndex = crewList.Content.GetChildIndex(crewList.Content.GetChildByUserData(Character.Controlled));
if (currentIndex == -1) { return 0; }
int lastIndex = crewList.Content.CountChildren - 1;
if (index > lastIndex)
int index = currentIndex + amount;
for (int i = 0; i < crewList.Content.CountChildren; i++)
{
index = 0;
if (index > lastIndex) { index = 0; }
if (index < 0) { index = lastIndex; }
if ((crewList.Content.GetChild(index)?.UserData as Character)?.IsOnPlayerTeam ?? false)
{
return index;
}
index += amount;
}
if (index < 0)
{
index = lastIndex;
}
return index;
return 0;
}
partial void UpdateProjectSpecific(float deltaTime)
{
// Quick selection
if (!GameMain.IsMultiplayer && GUI.KeyboardDispatcher.Subscriber == null)
if (GameMain.IsSingleplayer && GUI.KeyboardDispatcher.Subscriber == null)
{
if (PlayerInput.KeyHit(InputType.SelectNextCharacter))
{
@@ -1294,7 +1336,7 @@ namespace Barotrauma
(GUI.KeyboardDispatcher.Subscriber == null || (GUI.KeyboardDispatcher.Subscriber is GUIComponent component && (component == crewList || component.IsChildOf(crewList)))) &&
commandFrame == null && !clicklessSelectionActive && CanIssueOrders && !(GameMain.GameSession?.Campaign?.ShowCampaignUI ?? false))
{
if (PlayerInput.KeyDown(Keys.LeftShift) || PlayerInput.KeyDown(Keys.RightShift))
if (PlayerInput.IsShiftDown())
{
CreateCommandUI(FindEntityContext(), true);
}
@@ -1521,19 +1563,44 @@ namespace Barotrauma
{
crewList.Select(character, force: true);
}
if (character.AIController is HumanAIController controller)
if (GameMain.IsSingleplayer && character.IsBot && character.AIController is HumanAIController controller &&
controller.ObjectiveManager is AIObjectiveManager objectiveManager)
{
OrderInfo? currentOrderInfo = controller.ObjectiveManager?.GetCurrentOrderInfo();
if (currentOrderInfo.HasValue)
// In multiplayer, these are set through character networking (the server lets the clients now when these are updated)
if (objectiveManager.CurrentObjective is AIObjective currentObjective)
{
SetHighlightedOrderIcon(characterComponent, currentOrderInfo.Value.Order?.Identifier, currentOrderInfo.Value.OrderOption);
if (objectiveManager.IsOrder(currentObjective))
{
var orderInfo = objectiveManager.CurrentOrders.FirstOrDefault(o => o.Objective == currentObjective);
SetOrderHighlight(characterComponent, orderInfo.Order?.Identifier, orderInfo.OrderOption);
}
else
{
CreateObjectiveIcon(characterComponent, currentObjective);
}
}
}
if (characterComponent.GetChild<GUILayoutGroup>().GetChildByUserData("soundicons") is GUIComponent soundIconParent)
// Order highlighting and objective icons are intended to communicate bot behavior so they should be disabled for player characters
if (character.IsPlayer)
{
DisableOrderHighlight(characterComponent);
RemoveObjectiveIcon(characterComponent);
}
if (GetSoundIconParent(characterComponent) is GUIComponent soundIconParent)
{
if (soundIconParent.FindChild(c => c.UserData is Pair<string, float> pair && pair.First == "soundicon") is GUIImage soundIcon)
{
VoipClient.UpdateVoiceIndicator(soundIcon, 0.0f, deltaTime);
if (character.IsPlayer)
{
soundIconParent.Visible = true;
VoipClient.UpdateVoiceIndicator(soundIcon, 0.0f, deltaTime);
}
else if(soundIcon.Visible)
{
var userdata = soundIcon.UserData as Pair<string, float>;
userdata.Second = 0.0f;
soundIconParent.Visible = soundIcon.Visible = false;
}
}
}
}
@@ -1559,31 +1626,127 @@ namespace Barotrauma
UpdateReports();
}
private void SetHighlightedOrderIcon(GUIComponent characterComponent, string orderIdentifier, string orderOption)
private void SetOrderHighlight(GUIComponent characterComponent, string orderIdentifier, string orderOption)
{
var currentOrderIconList = GetCurrentOrderIconList(characterComponent);
if (currentOrderIconList == null) { return; }
bool foundMatch = false;
foreach (var orderIcon in currentOrderIconList.Content.Children)
if (characterComponent == null) { return; }
RemoveObjectiveIcon(characterComponent);
if (GetCurrentOrderIconList(characterComponent) is GUIListBox currentOrderIconList)
{
var glowComponent = orderIcon.GetChildByUserData("glow");
if (glowComponent == null) { continue; }
if (foundMatch)
bool foundMatch = false;
foreach (var orderIcon in currentOrderIconList.Content.Children)
{
glowComponent.Visible = false;
continue;
var glowComponent = orderIcon.GetChildByUserData("glow");
if (glowComponent == null) { continue; }
if (foundMatch)
{
glowComponent.Visible = false;
continue;
}
var orderInfo = (OrderInfo)orderIcon.UserData;
foundMatch = orderInfo.MatchesOrder(orderIdentifier, orderOption);
glowComponent.Visible = foundMatch;
}
var orderInfo = (OrderInfo)orderIcon.UserData;
foundMatch = orderInfo.MatchesOrder(orderIdentifier, orderOption);
glowComponent.Visible = foundMatch;
}
}
public void SetHighlightedOrderIcon(Character character, string orderIdentifier, string orderOption)
public void SetOrderHighlight(Character character, string orderIdentifier, string orderOption)
{
if (crewList == null) { return; }
var characterComponent = crewList.Content.GetChildByUserData(character);
SetHighlightedOrderIcon(characterComponent, orderIdentifier, orderOption);
SetOrderHighlight(characterComponent, orderIdentifier, orderOption);
}
private void DisableOrderHighlight(GUIComponent characterComponent)
{
if (GetCurrentOrderIconList(characterComponent) is GUIListBox currentOrderIconList)
{
foreach (var orderIcon in currentOrderIconList.Content.Children)
{
var glowComponent = orderIcon.GetChildByUserData("glow");
if (glowComponent == null) { continue; }
glowComponent.Visible = false;
}
}
}
private void CreateObjectiveIcon(GUIComponent characterComponent, Sprite sprite, string tooltip)
{
if (characterComponent == null || !(characterComponent.UserData is Character character) || character.IsPlayer) { return; }
DisableOrderHighlight(characterComponent);
if (GetObjectiveIconParent(characterComponent) is GUIFrame objectiveIconFrame)
{
var existingObjectiveIcon = objectiveIconFrame.GetChild<GUIImage>();
if (existingObjectiveIcon == null || existingObjectiveIcon.Sprite != sprite || existingObjectiveIcon.ToolTip != tooltip)
{
objectiveIconFrame.ClearChildren();
if (sprite != null)
{
var objectiveIcon = CreateNodeIcon(Vector2.One, objectiveIconFrame.RectTransform, sprite, Color.LightGray, tooltip: tooltip);
new GUIFrame(new RectTransform(new Vector2(1.5f), objectiveIcon.RectTransform, anchor: Anchor.Center), style: "OuterGlowCircular")
{
CanBeFocused = false,
Color = Color.LightGray
};
objectiveIconFrame.Visible = true;
}
else
{
objectiveIconFrame.Visible = false;
}
}
}
}
public void CreateObjectiveIcon(Character character, string identifier, string option, Entity targetEntity)
{
CreateObjectiveIcon(crewList?.Content.GetChildByUserData(character),
AIObjective.GetSprite(identifier, option, targetEntity),
GetObjectiveIconTooltip(identifier, option, targetEntity));
}
private void CreateObjectiveIcon(GUIComponent characterComponent, AIObjective objective)
{
CreateObjectiveIcon(characterComponent,
objective?.GetSprite(),
GetObjectiveIconTooltip(objective));
}
private string GetObjectiveIconTooltip(string identifier, string option, Entity targetEntity)
{
string variableValue;
identifier = identifier.RemoveWhitespace();
if (Order.Prefabs.TryGetValue(identifier, out Order orderPrefab))
{
variableValue = CreateOrderTooltip(orderPrefab, option, targetEntity);
}
else
{
variableValue = TextManager.Get($"objective.{identifier}", returnNull: true) ?? "";
}
return string.IsNullOrEmpty(variableValue) ? variableValue : TextManager.GetWithVariable("crewlistobjectivetooltip", "[objective]", variableValue);
}
private string GetObjectiveIconTooltip(AIObjective objective)
{
return objective == null ? "" :
GetObjectiveIconTooltip(objective.Identifier, objective.Option, (objective as AIObjectiveOperateItem)?.OperateTarget);
}
private GUIComponent GetObjectiveIconParent(GUIComponent characterComponent)
{
return characterComponent?
.GetChild<GUILayoutGroup>()?
.GetChildByUserData("extraicons")?
.GetChildByUserData("objectiveicon");
}
private void RemoveObjectiveIcon(GUIComponent characterComponent)
{
if (GetObjectiveIconParent(characterComponent) is GUIFrame objectiveIconFrame)
{
objectiveIconFrame.ClearChildren();
objectiveIconFrame.Visible = false;
}
}
#endregion
@@ -1853,7 +2016,10 @@ namespace Barotrauma
{
nodeConnectors = new GUICustomComponent(
new RectTransform(Vector2.One, commandFrame.RectTransform),
onDraw: DrawNodeConnectors);
onDraw: DrawNodeConnectors)
{
CanBeFocused = false
};
nodeConnectors.SetAsFirstChild();
background.SetAsFirstChild();
}
@@ -1862,10 +2028,27 @@ namespace Barotrauma
{
if (centerNode == null || optionNodes == null) { return; }
var startNodePos = centerNode.Rect.Center.ToVector2();
// Don't draw connectors for mini map options or assignment nodes
if ((targetFrame == null || !targetFrame.Visible) && !(optionNodes.FirstOrDefault()?.Item1.UserData is Character))
// Don't draw connectors for assignment nodes
if (!(optionNodes.FirstOrDefault()?.Item1.UserData is Character))
{
optionNodes.ForEach(n => DrawNodeConnector(startNodePos, centerNodeMargin, n.Item1, optionNodeMargin, spriteBatch));
// Regular option nodes
if (targetFrame == null || !targetFrame.Visible)
{
optionNodes.ForEach(n => DrawNodeConnector(startNodePos, centerNodeMargin, n.Item1, optionNodeMargin, spriteBatch));
}
// Minimap item nodes for single-option orders
else if(optionNodes.FirstOrDefault()?.Item1?.UserData is Tuple<Order, string> userData && string.IsNullOrEmpty(userData.Item2))
{
foreach (var node in optionNodes)
{
float iconRadius = 0.5f * optionNodeMargin;
Vector2 itemPosition = node.Item1.Parent.Rect.Center.ToVector2();
if (Vector2.Distance(node.Item1.Center, itemPosition) <= iconRadius) { continue; }
DrawNodeConnector(itemPosition, 0.0f, node.Item1, iconRadius, spriteBatch, widthMultiplier: 0.5f);
GUI.DrawFilledRectangle(spriteBatch, itemPosition - Vector2.One, new Vector2(3),
node.Item1.GetChildByUserData("colorsource")?.Color ?? Color.White);
}
}
}
DrawNodeConnector(startNodePos, centerNodeMargin, returnNode, returnNodeMargin, spriteBatch);
if (shortcutCenterNode == null || !shortcutCenterNode.Visible) { return; }
@@ -1874,7 +2057,7 @@ namespace Barotrauma
shortcutNodes.ForEach(n => DrawNodeConnector(startNodePos, shortcutCenterNodeMargin, n, shortcutNodeMargin, spriteBatch));
}
private void DrawNodeConnector(Vector2 startNodePos, float startNodeMargin, GUIComponent endNode, float endNodeMargin, SpriteBatch spriteBatch)
private void DrawNodeConnector(Vector2 startNodePos, float startNodeMargin, GUIComponent endNode, float endNodeMargin, SpriteBatch spriteBatch, float widthMultiplier = 1.0f)
{
if (endNode == null || !endNode.Visible) { return; }
var endNodePos = endNode.Rect.Center.ToVector2();
@@ -1885,11 +2068,11 @@ namespace Barotrauma
if ((selectedNode == null && endNode != shortcutCenterNode && GUI.IsMouseOn(endNode)) ||
(isSelectionHighlighted && (endNode == selectedNode || (endNode == shortcutCenterNode && shortcutNodes.Any(n => GUI.IsMouseOn(n))))))
{
GUI.DrawLine(spriteBatch, start, end, colorSource != null ? colorSource.HoverColor : Color.White, width: 4);
GUI.DrawLine(spriteBatch, start, end, colorSource?.HoverColor ?? Color.White, width: Math.Max(widthMultiplier * 4.0f, 1.0f));
}
else
{
GUI.DrawLine(spriteBatch, start, end, colorSource != null ? colorSource.Color : Color.White * nodeColorMultiplier, width: 2);
GUI.DrawLine(spriteBatch, start, end, colorSource?.Color ?? Color.White * nodeColorMultiplier, width: Math.Max(widthMultiplier * 2.0f, 1.0f));
}
}
@@ -1966,7 +2149,12 @@ namespace Barotrauma
{
if (commandFrame == null) { return false; }
RemoveOptionNodes();
if (targetFrame != null) { targetFrame.Visible = false; }
if (targetFrame != null)
{
targetFrame.Visible = false;
nodeConnectors.RectTransform.Parent = commandFrame.RectTransform;
nodeConnectors.RectTransform.RepositionChildInHierarchy(1);
}
// TODO: Center node could move to option node instead of being removed
commandFrame.RemoveChild(centerNode);
SetCenterNode(node);
@@ -2126,6 +2314,8 @@ namespace Barotrauma
private void CreateShortcutNodes()
{
bool HasAppropriateJob(Character c, string jobId) => c.Info?.Job != null && c.Info.Job.Prefab.AppropriateOrders.Contains(jobId);
Submarine sub = GetTargetSubmarine();
if (sub == null) { return; }
@@ -2138,7 +2328,7 @@ namespace Barotrauma
var reactorOutput = -reactor.CurrPowerConsumption;
// 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
if ((Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("engineer")) &&
if ((Character.Controlled == null || !HasAppropriateJob(Character.Controlled, "operatereactor")) &&
reactorOutput < float.Epsilon && characters.None(c => c.SelectedConstruction == reactor.Item))
{
var order = new Order(Order.GetPrefab("operatereactor"), reactor.Item, reactor, Character.Controlled);
@@ -2151,7 +2341,7 @@ namespace Barotrauma
// TODO: Reconsider the conditions as bot captain can have the nav term selected without operating it
// 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
if (shortcutNodes.Count < maxShortCutNodeCount && (Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("captain")) &&
if (shortcutNodes.Count < maxShortCutNodeCount && (Character.Controlled == null || !HasAppropriateJob(Character.Controlled, "steer")) &&
sub.GetItems(false).Find(i => i.HasTag("navterminal") && i.IsPlayerTeamInteractable) is Item nav && characters.None(c => c.SelectedConstruction == nav) &&
nav.GetComponent<Steering>() is Steering steering && steering.Voltage > steering.MinVoltage)
{
@@ -2161,8 +2351,8 @@ namespace Barotrauma
// If player is not a security officer AND invaders are reported
// --> Create shorcut node for Fight Intruders order
if (shortcutNodes.Count < maxShortCutNodeCount && (Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("securityofficer")) &&
(Order.GetPrefab("reportintruders") is Order reportIntruders && ActiveOrders.Any(o => o.First.Prefab == reportIntruders)))
if (shortcutNodes.Count < maxShortCutNodeCount && (Character.Controlled == null || !HasAppropriateJob(Character.Controlled, "fightintruders")) &&
Order.GetPrefab("reportintruders") is Order reportIntruders && ActiveOrders.Any(o => o.First.Prefab == reportIntruders))
{
shortcutNodes.Add(
CreateOrderNode(shortcutNodeSize, null, Point.Zero, Order.GetPrefab("fightintruders"), -1));
@@ -2170,25 +2360,43 @@ namespace Barotrauma
// If player is not a mechanic AND a breach has been reported
// --> Create shorcut node for Fix Leaks order
if (shortcutNodes.Count < maxShortCutNodeCount && (Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("mechanic")) &&
(Order.GetPrefab("reportbreach") is Order reportBreach && ActiveOrders.Any(o => o.First.Prefab == reportBreach)))
if (shortcutNodes.Count < maxShortCutNodeCount && (Character.Controlled == null || !HasAppropriateJob(Character.Controlled, "fixleaks")) &&
Order.GetPrefab("reportbreach") is Order reportBreach && ActiveOrders.Any(o => o.First.Prefab == reportBreach))
{
shortcutNodes.Add(
CreateOrderNode(shortcutNodeSize, null, Point.Zero, Order.GetPrefab("fixleaks"), -1));
}
// If player is not an engineer AND broken devices have been reported
// --> Create shortcut node for Repair Damaged Systems order
if (shortcutNodes.Count < maxShortCutNodeCount && (Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("engineer")) &&
(Order.GetPrefab("reportbrokendevices") is Order reportBrokenDevices && ActiveOrders.Any(o => o.First.Prefab == reportBrokenDevices)))
// --> Create shortcut nodes for the Repair orders
if (shortcutNodes.Count < maxShortCutNodeCount && Order.GetPrefab("reportbrokendevices") is Order reportBrokenDevices && ActiveOrders.Any(o => o.First.Prefab == reportBrokenDevices))
{
shortcutNodes.Add(
CreateOrderNode(shortcutNodeSize, null, Point.Zero, Order.GetPrefab("repairsystems"), -1));
// TODO: Doesn't work for player issued reports, because they don't have a target.
int repairNodes = 0;
string tag = "repairelectrical";
if (shortcutNodes.Count < maxShortCutNodeCount && (Character.Controlled == null || !HasAppropriateJob(Character.Controlled, tag)) && ActiveOrders.Any(o => o.First.Prefab == reportBrokenDevices && o.First.TargetItemComponent is Repairable r && r.requiredSkills.Any(s => s.Identifier == "electrical")))
{
shortcutNodes.Add(CreateOrderNode(shortcutNodeSize, null, Point.Zero, Order.GetPrefab(tag), -1));
repairNodes++;
}
tag = "repairmechanical";
if (shortcutNodes.Count < maxShortCutNodeCount && (Character.Controlled == null || !HasAppropriateJob(Character.Controlled, tag)) && ActiveOrders.Any(o => o.First.Prefab == reportBrokenDevices && o.First.TargetItemComponent is Repairable r && r.requiredSkills.Any(s => s.Identifier == "mechanical")))
{
shortcutNodes.Add(CreateOrderNode(shortcutNodeSize, null, Point.Zero, Order.GetPrefab(tag), -1));
repairNodes++;
}
if (repairNodes == 0 && shortcutNodes.Count < maxShortCutNodeCount)
{
tag = "repairsystems";
if (Character.Controlled == null || !HasAppropriateJob(Character.Controlled, tag))
{
shortcutNodes.Add(CreateOrderNode(shortcutNodeSize, null, Point.Zero, Order.GetPrefab(tag), -1));
}
}
}
// If fire is reported
// --> Create shortcut node for Extinguish Fires order
if (shortcutNodes.Count < maxShortCutNodeCount && ActiveOrders.Any(o=> o.First.Prefab == Order.GetPrefab("reportfire")))
if (shortcutNodes.Count < maxShortCutNodeCount && ActiveOrders.Any(o => o.First.Prefab == Order.GetPrefab("reportfire")))
{
shortcutNodes.Add(
CreateOrderNode(shortcutNodeSize, null, Point.Zero, Order.GetPrefab("extinguishfires"), -1));
@@ -2555,20 +2763,22 @@ namespace Barotrauma
if (itemTargetFrame == null) { continue; }
var anchor = Anchor.TopLeft;
if (itemTargetFrame.RectTransform.RelativeOffset.X < 0.5f && itemTargetFrame.RectTransform.RelativeOffset.Y < 0.5f)
if (itemTargetFrame.RectTransform.RelativeOffset.X < 0.5f)
{
anchor = Anchor.BottomRight;
if (itemTargetFrame.RectTransform.RelativeOffset.Y < 0.5f)
{
anchor = Anchor.BottomRight;
}
else
{
anchor = Anchor.TopRight;
}
}
else if (itemTargetFrame.RectTransform.RelativeOffset.X > 0.5f && itemTargetFrame.RectTransform.RelativeOffset.Y < 0.5f)
else if (itemTargetFrame.RectTransform.RelativeOffset.Y < 0.5f)
{
anchor = Anchor.BottomLeft;
}
else if (itemTargetFrame.RectTransform.RelativeOffset.X < 0.5f && itemTargetFrame.RectTransform.RelativeOffset.Y > 0.5f)
{
anchor = Anchor.TopRight;
}
GUIComponent optionElement;
if (order.Options.Length > 1)
{
@@ -2631,7 +2841,6 @@ namespace Barotrauma
{
UserData = userData,
Font = GUI.SmallFont,
ToolTip = item?.Name ?? GetOrderNameBasedOnContextuality(order),
OnClicked = (_, userData) =>
{
if (!CanIssueOrders) { return false; }
@@ -2645,21 +2854,31 @@ namespace Barotrauma
{
optionElement.OnSecondaryClicked = (button, _) => CreateAssignmentNodes(button);
}
Sprite icon = null;
order.MinimapIcons?.TryGetValue(item.Prefab.Identifier, out icon);
if (item.Prefab.MinimapIcon != null)
{
icon = item.Prefab.MinimapIcon;
}
var colorMultiplier = characters.Any(c => c.CurrentOrders.Any(o => o.Order != null &&
o.Order.Identifier == userData.Item1.Identifier &&
o.Order.TargetEntity == userData.Item1.TargetEntity)) ? 0.5f : 1f;
CreateNodeIcon(Vector2.One, optionElement.RectTransform, icon ?? order.SymbolSprite, order.Color * colorMultiplier);
CreateNodeIcon(Vector2.One, optionElement.RectTransform, item.Prefab.MinimapIcon ?? order.SymbolSprite, order.Color * colorMultiplier, tooltip: item.Name);
optionNodes.Add(new Tuple<GUIComponent, Keys>(optionElement, Keys.None));
}
optionElements.Add(optionElement);
}
GUI.PreventElementOverlap(optionElements, clampArea: new Rectangle(10, 10, GameMain.GraphicsWidth - 20, GameMain.GraphicsHeight - 20));
Rectangle clampArea = new Rectangle(10, 10, GameMain.GraphicsWidth - 20, GameMain.GraphicsHeight - 20);
if (order.Identifier == "operateweapons")
{
Rectangle disallowedArea = targetFrame.GetChild<GUIFrame>().Rect;
Point originalSize = disallowedArea.Size;
disallowedArea.Size = disallowedArea.MultiplySize(0.9f);
disallowedArea.X += (originalSize.X - disallowedArea.Size.X) / 2;
disallowedArea.Y += (originalSize.Y - disallowedArea.Size.Y) / 2;
GUI.PreventElementOverlap(optionElements, new List<Rectangle>() { disallowedArea }, clampArea);
nodeConnectors.RectTransform.Parent = targetFrame.RectTransform;
nodeConnectors.RectTransform.SetAsFirstChild();
}
else
{
GUI.PreventElementOverlap(optionElements, clampArea: clampArea);
}
var shadow = new GUIFrame(
new RectTransform(targetFrame.Rect.Size + new Point((int)(200 * GUI.Scale)), targetFrame.RectTransform, anchor: Anchor.Center),
@@ -2731,6 +2950,12 @@ namespace Barotrauma
private bool CreateAssignmentNodes(GUIComponent node)
{
if (centerNode == null)
{
DisableCommandUI();
return false;
}
var order = (node.UserData is Order) ?
new Tuple<Order, string>(node.UserData as Order, null) :
node.UserData as Tuple<Order, string>;
@@ -2777,6 +3002,8 @@ namespace Barotrauma
node = null;
}
targetFrame.Visible = false;
nodeConnectors.RectTransform.Parent = commandFrame.RectTransform;
nodeConnectors.RectTransform.RepositionChildInHierarchy(1);
}
if (shortcutCenterNode != null)
{
@@ -3182,16 +3409,7 @@ namespace Barotrauma
private Character GetCharacterForQuickAssignment(Order order)
{
var controllingCharacter = Character.Controlled != null;
#if !DEBUG
if (!controllingCharacter) { return null; }
#endif
if (order.Category == OrderCategory.Operate && HumanAIController.IsItemOperatedByAnother(null, order.TargetItemComponent, out Character operatingCharacter) &&
(!controllingCharacter || operatingCharacter.CanHearCharacter(Character.Controlled)))
{
return operatingCharacter;
}
return GetCharactersSortedForOrder(order, false).FirstOrDefault(c => !controllingCharacter || c.CanHearCharacter(Character.Controlled)) ?? Character.Controlled;
return GetCharacterForQuickAssignment(order, Character.Controlled, characters);
}
private List<Character> GetCharactersForManualAssignment(Order order)
@@ -3203,34 +3421,15 @@ namespace Barotrauma
{
return characters.Union(GetOrderableFriendlyNPCs()).Where(c => !c.IsDismissed).OrderBy(c => c.Info.DisplayName).ToList();
}
return GetCharactersSortedForOrder(order, order.Identifier != "follow").ToList();
}
private IEnumerable<Character> GetCharactersSortedForOrder(Order order, bool includeSelf)
{
return characters.Where(c => Character.Controlled == null || ((includeSelf || c != Character.Controlled) && c.TeamID == Character.Controlled.TeamID)).Union(GetOrderableFriendlyNPCs())
// 1. Prioritize those who are on the same submarine than the controlled character
.OrderByDescending(c => Character.Controlled == null || c.Submarine == Character.Controlled.Submarine)
// 2. Prioritize those who have been given the same maintenance or operate order as now issued
.ThenByDescending(c => c.CurrentOrders.Any(o =>
o.Order != null && o.Order.Identifier == order.Identifier &&
(order.Category == OrderCategory.Maintenance || order.Category == OrderCategory.Operate)))
// 3. Prioritize those with the appropriate job for the order
.ThenByDescending(c => order.HasAppropriateJob(c))
// 4. Prioritize bots over player controlled characters
.ThenByDescending(c => c.IsBot)
// 5. Use the priority value of the current objective
.ThenBy(c => c.AIController is HumanAIController humanAI ? humanAI.ObjectiveManager.CurrentObjective?.Priority : 0)
// 6. Prioritize those with the best skill for the order
.ThenByDescending(c => c.GetSkillLevel(order.AppropriateSkill));
return GetCharactersSortedForOrder(order, characters, Character.Controlled, order.Identifier != "follow", extraCharacters: GetOrderableFriendlyNPCs()).ToList();
}
private IEnumerable<Character> GetOrderableFriendlyNPCs()
{
// TODO: change this so that we can get the data without having to rely on ui elements.
return crewList.Content.Children.Where(c => c.UserData is Character character && character.TeamID == CharacterTeamType.FriendlyNPC).Select(c => (Character)c.UserData);
}
#endregion
#endregion
@@ -3325,15 +3524,76 @@ namespace Barotrauma
public void Save(XElement parentElement)
{
XElement element = new XElement("crew");
foreach (CharacterInfo ci in characterInfos)
{
var infoElement = ci.Save(element);
if (ci.InventoryData != null) { infoElement.Add(ci.InventoryData); }
if (ci.HealthData != null) { infoElement.Add(ci.HealthData); }
if (ci.OrderData != null) { infoElement.Add(ci.OrderData); }
if (ci.LastControlled) { infoElement.Add(new XAttribute("lastcontrolled", true)); }
}
SaveActiveOrders(element);
parentElement.Add(element);
}
public static void ClientReadActiveOrders(IReadMessage inc)
{
ushort count = inc.ReadUInt16();
if (count < 1) { return; }
var activeOrders = new List<(Order, float?)>();
for (ushort i = 0; i < count; i++)
{
var orderMessageInfo = OrderChatMessage.ReadOrder(inc);
Character orderGiver = null;
if (inc.ReadBoolean())
{
ushort orderGiverId = inc.ReadUInt16();
orderGiver = orderGiverId != Entity.NullEntityID ? Entity.FindEntityByID(orderGiverId) as Character : null;
}
if (orderMessageInfo.OrderIndex < 0 || orderMessageInfo.OrderIndex >= Order.PrefabList.Count)
{
DebugConsole.ThrowError("Invalid active order - order index out of bounds.");
continue;
}
Order orderPrefab = orderMessageInfo.OrderPrefab ?? Order.PrefabList[orderMessageInfo.OrderIndex];
Order order = orderMessageInfo.TargetType switch
{
Order.OrderTargetType.Entity =>
new Order(orderPrefab, orderMessageInfo.TargetEntity, orderPrefab.GetTargetItemComponent(orderMessageInfo.TargetEntity as Item), orderGiver: orderGiver),
Order.OrderTargetType.Position =>
new Order(orderPrefab, orderMessageInfo.TargetPosition, orderGiver: orderGiver),
Order.OrderTargetType.WallSection =>
new Order(orderPrefab, orderMessageInfo.TargetEntity as Structure, orderMessageInfo.WallSectionIndex, orderGiver: orderGiver),
_ => throw new NotImplementedException()
};
if (order != null && order.TargetAllCharacters)
{
var fadeOutTime = !orderPrefab.IsIgnoreOrder ? (float?)orderPrefab.FadeOutTime : null;
activeOrders.Add((order, fadeOutTime));
}
}
foreach (var (order, fadeOutTime) in activeOrders)
{
if (order.IsIgnoreOrder)
{
switch (order.TargetType)
{
case Order.OrderTargetType.Entity:
if (!(order.TargetEntity is IIgnorable ignorableEntity)) { break; }
ignorableEntity.OrderedToBeIgnored = order.Identifier == "ignorethis";
break;
case Order.OrderTargetType.Position:
throw new NotImplementedException();
case Order.OrderTargetType.WallSection:
if (!order.WallSectionIndex.HasValue) { break; }
if (!(order.TargetEntity is Structure s)) { break; }
if (!(s.GetSection(order.WallSectionIndex.Value) is IIgnorable ignorableWall)) { break; }
ignorableWall.OrderedToBeIgnored = order.Identifier == "ignorethis";
break;
}
}
GameMain.GameSession?.CrewManager?.AddOrder(order, fadeOutTime);
}
}
}
}
@@ -189,7 +189,7 @@ namespace Barotrauma
case TransitionType.None:
default:
if (Level.Loaded.Type == LevelData.LevelType.Outpost &&
(Character.Controlled?.Submarine?.Info.Type == SubmarineType.Player || (Character.Controlled?.CurrentHull?.OutpostModuleTags?.Contains("airlock") ?? false)))
(Character.Controlled?.Submarine?.Info.Type == SubmarineType.Player || (Character.Controlled?.CurrentHull?.OutpostModuleTags.Contains("airlock") ?? false)))
{
buttonText = TextManager.GetWithVariable("LeaveLocation", "[locationname]", Level.Loaded.StartLocation?.Name ?? "[ERROR]");
endRoundButton.Visible = !ForceMapUI && !ShowCampaignUI;
@@ -287,6 +287,7 @@ namespace Barotrauma
{
case InteractionType.None:
case InteractionType.Talk:
case InteractionType.Examine:
return;
case InteractionType.Upgrade when !UpgradeManager.CanUpgradeSub():
UpgradeManager.CreateUpgradeErrorMessage(TextManager.Get("Dialog.CantUpgrade"), IsSinglePlayer, npc);
@@ -211,10 +211,7 @@ namespace Barotrauma
{
Character.Controlled = null;
if (prevControlled != null)
{
prevControlled.ClearInputs();
}
prevControlled?.ClearInputs();
overlayColor = Color.LightGray;
overlaySprite = Map.CurrentLocation.Type.GetPortrait(Map.CurrentLocation.PortraitId);
@@ -326,7 +323,6 @@ namespace Barotrauma
Level prevLevel = Level.Loaded;
bool success = CrewManager.GetCharacters().Any(c => !c.IsDead);
GUI.SetSavingIndicatorState(success);
crewDead = false;
var continueButton = GameMain.GameSession.RoundSummary?.ContinueButton;
@@ -484,6 +480,8 @@ namespace Barotrauma
{
IsFirstRound = false;
CoroutineManager.StartCoroutine(DoLevelTransition(), "LevelTransition");
bool success = CrewManager.GetCharacters().Any(c => !c.IsDead);
GUI.SetSavingIndicatorState(success && (Level.IsLoadedOutpost || transitionType != TransitionType.None));
}
}
@@ -534,7 +532,13 @@ namespace Barotrauma
msg.Write(map.CurrentLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.CurrentLocationIndex);
msg.Write(map.SelectedLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.SelectedLocationIndex);
msg.Write(map.SelectedMissionIndex == -1 ? byte.MaxValue : (byte)map.SelectedMissionIndex);
var selectedMissionIndices = map.GetSelectedMissionIndices();
msg.Write((byte)selectedMissionIndices.Count());
foreach (int selectedMissionIndex in selectedMissionIndices)
{
msg.Write((byte)selectedMissionIndex);
}
msg.Write(PurchasedHullRepairs);
msg.Write(PurchasedItemRepairs);
msg.Write(PurchasedLostShuttles);
@@ -569,6 +573,13 @@ namespace Barotrauma
msg.Write(category.Identifier);
msg.Write((byte)level);
}
msg.Write((ushort)UpgradeManager.PurchasedItemSwaps.Count);
foreach (var itemSwap in UpgradeManager.PurchasedItemSwaps)
{
msg.Write(itemSwap.ItemToRemove.ID);
msg.Write(itemSwap.ItemToInstall?.Identifier ?? string.Empty);
}
}
//static because we may need to instantiate the campaign if it hasn't been done yet
@@ -581,8 +592,15 @@ namespace Barotrauma
string mapSeed = msg.ReadString();
UInt16 currentLocIndex = msg.ReadUInt16();
UInt16 selectedLocIndex = msg.ReadUInt16();
byte selectedMissionIndex = msg.ReadByte();
bool allowDebugTeleport = msg.ReadBoolean();
byte selectedMissionCount = msg.ReadByte();
List<int> selectedMissionIndices = new List<int>();
for (int i = 0; i < selectedMissionCount; i++)
{
selectedMissionIndices.Add(msg.ReadByte());
}
bool allowDebugTeleport = msg.ReadBoolean();
float? reputation = null;
if (msg.ReadBoolean()) { reputation = msg.ReadSingle(); }
@@ -657,6 +675,21 @@ namespace Barotrauma
pendingUpgrades.Add(new PurchasedUpgrade(prefab, category, upgradeLevel));
}
ushort purchasedItemSwapCount = msg.ReadUInt16();
List<PurchasedItemSwap> purchasedItemSwaps = new List<PurchasedItemSwap>();
for (int i = 0; i < purchasedItemSwapCount; i++)
{
UInt16 itemToRemoveID = msg.ReadUInt16();
Item itemToRemove = Entity.FindEntityByID(itemToRemoveID) as Item;
string itemToInstallIdentifier = msg.ReadString();
ItemPrefab itemToInstall = string.IsNullOrEmpty(itemToInstallIdentifier) ? null : ItemPrefab.Find(string.Empty, itemToInstallIdentifier);
if (itemToRemove == null) { continue; }
purchasedItemSwaps.Add(new PurchasedItemSwap(itemToRemove, itemToInstall));
}
bool hasCharacterData = msg.ReadBoolean();
CharacterInfo myCharacterInfo = null;
if (hasCharacterData)
@@ -694,7 +727,7 @@ namespace Barotrauma
campaign.Map.SetLocation(currentLocIndex == UInt16.MaxValue ? -1 : currentLocIndex);
campaign.Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex);
campaign.Map.SelectMission(selectedMissionIndex);
campaign.Map.SelectMission(selectedMissionIndices);
campaign.Map.AllowDebugTeleport = allowDebugTeleport;
campaign.CargoManager.SetItemsInBuyCrate(buyCrateItems);
campaign.CargoManager.SetPurchasedItems(purchasedItems);
@@ -703,6 +736,26 @@ namespace Barotrauma
campaign.UpgradeManager.SetPendingUpgrades(pendingUpgrades);
campaign.UpgradeManager.PurchasedUpgrades.Clear();
campaign.UpgradeManager.PurchasedUpgrades.Clear();
foreach (var purchasedItemSwap in purchasedItemSwaps)
{
if (purchasedItemSwap.ItemToInstall == null)
{
campaign.UpgradeManager.CancelItemSwap(purchasedItemSwap.ItemToRemove, force: true);
}
else
{
campaign.UpgradeManager.PurchaseItemSwap(purchasedItemSwap.ItemToRemove, purchasedItemSwap.ItemToInstall, force: true);
}
}
foreach (Item item in Item.ItemList)
{
if (item.PendingItemSwap != null && !purchasedItemSwaps.Any(it => it.ItemToRemove == item))
{
item.PendingItemSwap = null;
}
}
foreach (var (identifier, rep) in factionReps)
{
Faction faction = campaign.Factions.FirstOrDefault(f => f.Prefab.Identifier.Equals(identifier, StringComparison.OrdinalIgnoreCase));
@@ -101,7 +101,8 @@ namespace Barotrauma
case "cargo":
CargoManager.LoadPurchasedItems(subElement);
break;
case "pendingupgrades":
case "pendingupgrades": //backwards compatibility
case "upgrademanager":
UpgradeManager = new UpgradeManager(this, subElement, isSingleplayer: true);
break;
case "pets":
@@ -229,6 +230,7 @@ namespace Barotrauma
{
PetBehavior.LoadPets(petsElement);
}
CrewManager.LoadActiveOrders();
GUI.DisableSavingIndicatorDelayed();
}
@@ -264,10 +266,7 @@ namespace Barotrauma
prevControlled.AIController.Enabled = false;
}
Character.Controlled = null;
if (prevControlled != null)
{
prevControlled.ClearInputs();
}
prevControlled?.ClearInputs();
GUI.DisableHUD = true;
while (GameMain.Instance.LoadingScreenOpen)
@@ -303,7 +302,7 @@ namespace Barotrauma
yield return CoroutineStatus.Success;
}
overlayTextColor = Color.Lerp(Color.Transparent, Color.White, (timer - 1.0f) / fadeInDuration);
timer = Math.Min(timer + CoroutineManager.DeltaTime, textDuration);
timer = Math.Min(timer + CoroutineManager.UnscaledDeltaTime, textDuration);
yield return CoroutineStatus.Running;
}
var outpost = GameMain.GameSession.Level.StartOutpost;
@@ -331,7 +330,7 @@ namespace Barotrauma
while (timer < fadeInDuration)
{
overlayColor = Color.Lerp(Color.LightGray, Color.Transparent, timer / fadeInDuration);
timer += CoroutineManager.DeltaTime;
timer += CoroutineManager.UnscaledDeltaTime;
yield return CoroutineStatus.Running;
}
overlayColor = Color.Transparent;
@@ -446,9 +445,13 @@ namespace Barotrauma
{
Submarine.MainSub = leavingSub;
GameMain.GameSession.Submarine = leavingSub;
GameMain.GameSession.SubmarineInfo = leavingSub.Info;
leavingSub.Info.FilePath = System.IO.Path.Combine(SaveUtil.TempPath, leavingSub.Info.Name + ".sub");
var subsToLeaveBehind = GetSubsToLeaveBehind(leavingSub);
GameMain.GameSession.OwnedSubmarines.Add(leavingSub.Info);
foreach (Submarine sub in subsToLeaveBehind)
{
GameMain.GameSession.OwnedSubmarines.RemoveAll(s => s != leavingSub.Info && s.Name == sub.Info.Name);
MapEntity.mapEntityList.RemoveAll(e => e.Submarine == sub && e is LinkedSubmarine);
LinkedSubmarine.CreateDummy(leavingSub, sub);
}
@@ -480,6 +483,8 @@ namespace Barotrauma
EnableRoundSummaryGameOverState();
}
CrewManager?.ClearCurrentOrders();
//--------------------------------------
SelectSummaryScreen(roundSummary, newLevel, mirror, () =>
@@ -558,7 +563,7 @@ namespace Barotrauma
}
#if DEBUG
if (PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.R))
if (GUI.KeyboardDispatcher.Subscriber == null && PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.M))
{
if (GUIMessageBox.MessageBoxes.Any()) { GUIMessageBox.MessageBoxes.Remove(GUIMessageBox.MessageBoxes.Last()); }
@@ -735,9 +740,10 @@ namespace Barotrauma
if (c.Inventory != null)
{
c.Info.InventoryData = new XElement("inventory");
c.SaveInventory(c.Inventory, c.Info.InventoryData);
c.SaveInventory();
c.Inventory?.DeleteAllItems();
}
c.Info.SaveOrderData();
}
petsElement = new XElement("pets");
@@ -748,7 +754,7 @@ namespace Barotrauma
CampaignMetadata.Save(modeElement);
Map.Save(modeElement);
CargoManager?.SavePurchasedItems(modeElement);
UpgradeManager?.SavePendingUpgrades(modeElement, UpgradeManager?.PendingUpgrades);
UpgradeManager?.Save(modeElement);
element.Add(modeElement);
}
}
@@ -123,17 +123,17 @@ namespace Barotrauma.Tutorials
SetDoorAccess(tutorial_lockedDoor_2, null, false);
var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("mechanic"));
captain_mechanic = Character.Create(mechanicInfo, WayPoint.GetRandom(SpawnType.Human, mechanicInfo.Job, Submarine.MainSub).WorldPosition, "mechanic");
captain_mechanic = Character.Create(mechanicInfo, WayPoint.GetRandom(SpawnType.Human, mechanicInfo.Job?.Prefab, Submarine.MainSub).WorldPosition, "mechanic");
captain_mechanic.TeamID = CharacterTeamType.Team1;
captain_mechanic.GiveJobItems();
var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("securityofficer"));
captain_security = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job, Submarine.MainSub).WorldPosition, "securityofficer");
captain_security = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job?.Prefab, Submarine.MainSub).WorldPosition, "securityofficer");
captain_security.TeamID = CharacterTeamType.Team1;
captain_security.GiveJobItems();
var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("engineer"));
captain_engineer = Character.Create(engineerInfo, WayPoint.GetRandom(SpawnType.Human, engineerInfo.Job, Submarine.MainSub).WorldPosition, "engineer");
captain_engineer = Character.Create(engineerInfo, WayPoint.GetRandom(SpawnType.Human, engineerInfo.Job?.Prefab, Submarine.MainSub).WorldPosition, "engineer");
captain_engineer.TeamID = CharacterTeamType.Team1;
captain_engineer.GiveJobItems();
@@ -94,19 +94,19 @@ namespace Barotrauma.Tutorials
patient2.AIController.Enabled = false;
var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("engineer"));
var subPatient1 = Character.Create(mechanicInfo, WayPoint.GetRandom(SpawnType.Human, mechanicInfo.Job, Submarine.MainSub).WorldPosition, "3");
var subPatient1 = Character.Create(mechanicInfo, WayPoint.GetRandom(SpawnType.Human, mechanicInfo.Job?.Prefab, Submarine.MainSub).WorldPosition, "3");
subPatient1.TeamID = CharacterTeamType.Team1;
subPatient1.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 40.0f) }, stun: 0, playSound: false);
subPatients.Add(subPatient1);
var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("securityofficer"));
var subPatient2 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job, Submarine.MainSub).WorldPosition, "3");
var subPatient2 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job?.Prefab, Submarine.MainSub).WorldPosition, "3");
subPatient2.TeamID = CharacterTeamType.Team1;
subPatient2.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.InternalDamage, 40.0f) }, stun: 0, playSound: false);
subPatients.Add(subPatient2);
var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("engineer"));
var subPatient3 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, engineerInfo.Job, Submarine.MainSub).WorldPosition, "3");
var subPatient3 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, engineerInfo.Job?.Prefab, Submarine.MainSub).WorldPosition, "3");
subPatient3.TeamID = CharacterTeamType.Team1;
subPatient3.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 20.0f) }, stun: 0, playSound: false);
subPatients.Add(subPatient3);
@@ -531,7 +531,7 @@ namespace Barotrauma.Tutorials
}
}
yield return null;
} while (!mechanic.HasEquippedItem("divingsuit"));
} while (!mechanic.HasEquippedItem("divingsuit", slotType: InvSlotType.OuterClothes));
SetHighlight(mechanic_divingSuitContainer.Item, false);
RemoveCompletedObjective(segments[8]);
SetDoorAccess(tutorial_mechanicFinalDoor, tutorial_mechanicFinalDoorLight, true);
@@ -177,7 +177,7 @@ namespace Barotrauma.Tutorials
}
}
return WayPoint.GetRandom(spawnPointType, charInfo.Job, spawnSub);
return WayPoint.GetRandom(spawnPointType, charInfo.Job?.Prefab, spawnSub);
}
protected bool HasOrder(Character character, string identifier, string option = null)
@@ -224,10 +224,7 @@ namespace Barotrauma.Tutorials
public virtual void Update(float deltaTime)
{
if (videoPlayer != null)
{
videoPlayer.Update();
}
videoPlayer?.Update();
if (activeObjectives != null)
{
@@ -524,7 +524,7 @@ namespace Barotrauma
private static void CheckIfDivingGearOutOfOxygen()
{
if (!CanDisplayHints()) { return; }
var divingGear = Character.Controlled.GetEquippedItem("diving");
var divingGear = Character.Controlled.GetEquippedItem("diving", InvSlotType.OuterClothes);
if (divingGear?.OwnInventory == null) { return; }
if (divingGear.GetContainedItemConditionPercentage() > 0.0f) { return; }
DisplayHint("ondivinggearoutofoxygen", onUpdate: () =>
@@ -218,12 +218,15 @@ namespace Barotrauma
};
List<Mission> missionsToDisplay = new List<Mission>(selectedMissions);
if (!selectedMissions.Any() && startLocation?.SelectedMission != null)
{
if (startLocation.SelectedMission.Locations[0] == startLocation.SelectedMission.Locations[1] ||
startLocation.SelectedMission.Locations.Contains(campaignMode?.Map.SelectedLocation))
if (!selectedMissions.Any() && startLocation != null)
{
foreach (Mission mission in startLocation.SelectedMissions)
{
missionsToDisplay.Add(startLocation.SelectedMission);
if (mission.Locations[0] == mission.Locations[1] ||
mission.Locations.Contains(campaignMode?.Map.SelectedLocation))
{
missionsToDisplay.Add(mission);
}
}
}
@@ -284,10 +287,11 @@ namespace Barotrauma
new GUIImage(new RectTransform(Vector2.One, missionIcon.RectTransform), displayedMission.Completed ? "MissionCompletedIcon" : "MissionFailedIcon", scaleToFit: true);
}
var missionTextContent = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 0.8f), missionContentHorizontal.RectTransform))
var missionTextContent = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 1.0f), missionContentHorizontal.RectTransform))
{
RelativeSpacing = 0.05f
AbsoluteSpacing = GUI.IntScale(5)
};
missionContentHorizontal.Recalculate();
var missionNameTextBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
displayedMission.Name, font: GUI.SubHeadingFont);
if (displayedMission.Difficulty.HasValue)
@@ -309,12 +313,13 @@ namespace Barotrauma
};
}
}
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
var missionDescription = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
missionMessage, wrap: true, parseRichText: true);
if (selectedMissions.Contains(displayedMission) && displayedMission.Completed && displayedMission.Reward > 0)
int reward = displayedMission.GetReward(Submarine.MainSub);
if (selectedMissions.Contains(displayedMission) && displayedMission.Completed && reward > 0)
{
string rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", displayedMission.Reward));
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), displayedMission.GetMissionRewardText(), parseRichText: true);
string rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", reward));
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), displayedMission.GetMissionRewardText(Submarine.MainSub), parseRichText: true);
}
if (displayedMission != missionsToDisplay.Last())
@@ -322,6 +327,13 @@ namespace Barotrauma
var spacing = new GUIFrame(new RectTransform(new Vector2(1.0f, 1.0f), missionList.Content.RectTransform) { MaxSize = new Point(int.MaxValue, GUI.IntScale(15)) }, style: null);
new GUIFrame(new RectTransform(new Vector2(0.8f, 1.0f), spacing.RectTransform, Anchor.Center) { RelativeOffset = new Vector2(0.1f, 0.0f) }, "HorizontalLine");
}
foreach (GUIComponent child in missionTextContent.Children)
{
child.RectTransform.IsFixedSize = true;
}
missionTextContent.RectTransform.MinSize = new Point(0, missionTextContent.Children.Sum(c => c.Rect.Height + missionTextContent.AbsoluteSpacing));
missionContentHorizontal.RectTransform.MinSize = new Point(0, (int)(missionTextContent.Rect.Height / missionTextContent.RectTransform.RelativeSize.Y));
}
if (!missionsToDisplay.Any())
@@ -39,43 +39,5 @@ namespace Barotrauma
}
}
}
/// <summary>
/// Server has notified us that upgrades were reset.
/// </summary>
/// <param name="inc"></param>
/// <see cref="UpgradeManager.SendUpgradeResetMessage"/>
public void ClientRead(IReadMessage inc)
{
bool shouldReset = inc.ReadBoolean();
int money = inc.ReadInt32();
// uint length = inc.ReadUInt32();
//
// for (int i = 0; i < length; i++)
// {
// string key = inc.ReadString();
// byte value = inc.ReadByte();
// Metadata.SetValue(key, value);
// }
Campaign.Money = money;
if (shouldReset)
{
ResetUpgrades();
}
// spentMoney is local, so this message box should only appear for those who have spent money on upgrades
if (spentMoney > 0)
{
GUIMessageBox msgBox = new GUIMessageBox(TextManager.Get("UpgradeRefundTitle"), TextManager.Get("UpgradeRefundBody"), new [] { TextManager.Get("Ok") });
msgBox.Buttons[0].OnClicked += msgBox.Close;
}
spentMoney = 0;
PendingUpgrades.Clear();
PurchasedUpgrades.Clear();
CanUpgrade = false;
}
}
}