Unstable 0.16.2.0

This commit is contained in:
Markus Isberg
2022-02-02 00:55:02 +09:00
parent b259af5911
commit 6814a11520
106 changed files with 1634 additions and 793 deletions
@@ -73,7 +73,6 @@ namespace Barotrauma
public void Draw(SpriteBatch spriteBatch)
{
// TODO: move this into the character editor
//var mouthPos = ragdoll.GetMouthPosition();
//if (mouthPos != null)
//{
@@ -173,6 +172,7 @@ namespace Barotrauma
public float DefaultSpriteDepth { get; private set; }
public WearableSprite HairWithHatSprite { get; set; }
public WearableSprite HuskSprite { get; private set; }
public WearableSprite HerpesSprite { get; private set; }
@@ -236,8 +236,8 @@ namespace Barotrauma
public string HitSoundTag => Params?.Sound?.Tag;
private List<WearableSprite> wearableTypeHidingSprites = new List<WearableSprite>();
private List<WearableType> wearableTypesToHide = new List<WearableType>();
private readonly List<WearableSprite> wearableTypeHidingSprites = new List<WearableSprite>();
private readonly HashSet<WearableType> wearableTypesToHide = new HashSet<WearableType>();
private bool enableHuskSprite;
public bool EnableHuskSprite
{
@@ -895,7 +895,15 @@ namespace Barotrauma
foreach (WearableSprite wearable in OtherWearables)
{
if (wearable.Type == WearableType.Husk) { continue; }
if (wearableTypesToHide.Contains(wearable.Type)) { continue; }
if (wearableTypesToHide.Contains(wearable.Type))
{
if (wearable.Type == WearableType.Hair && HairWithHatSprite != null)
{
DrawWearable(HairWithHatSprite, depthStep, spriteBatch, blankColor, alpha: color.A / 255f, spriteEffect);
depthStep += step;
}
continue;
}
DrawWearable(wearable, depthStep, spriteBatch, blankColor, alpha: color.A / 255f, spriteEffect);
//if there are multiple sprites on this limb, make the successive ones be drawn in front
depthStep += step;
@@ -1195,6 +1203,9 @@ namespace Barotrauma
HuskSprite?.Sprite.Remove();
HuskSprite = null;
HairWithHatSprite?.Sprite.Remove();
HairWithHatSprite = null;
HerpesSprite?.Sprite.Remove();
HerpesSprite = null;
@@ -32,7 +32,8 @@ namespace Barotrauma
public void ClientExecute(string[] args)
{
if (!CheatsEnabled && IsCheat)
bool allowCheats = GameMain.NetworkMember == null && (GameMain.GameSession?.GameMode is TestGameMode || Screen.Selected is EditorScreen);
if (!allowCheats && !CheatsEnabled && IsCheat)
{
NewMessage("You need to enable cheats using the command \"enablecheats\" before you can use the command \"" + names[0] + "\".", Color.Red);
#if USE_STEAM
@@ -743,7 +744,7 @@ namespace Barotrauma
AssignOnExecute("explosion", (string[] args) =>
{
Vector2 explosionPos = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition);
float range = 500, force = 10, damage = 50, structureDamage = 10, itemDamage = 100, empStrength = 0.0f, ballastFloraStrength = 50f;
float range = 500, force = 10, damage = 50, structureDamage = 20, itemDamage = 100, empStrength = 0.0f, ballastFloraStrength = 50f;
if (args.Length > 0) float.TryParse(args[0], out range);
if (args.Length > 1) float.TryParse(args[1], out force);
if (args.Length > 2) float.TryParse(args[2], out damage);
@@ -1894,7 +1895,12 @@ namespace Barotrauma
ThrowError($"\"{args[0]}\" is not a valid Level.PositionType. Available options are: {string.Join(", ", enums)}");
return;
}
debugLines = EventSet.GetDebugStatistics(filter: monsterEvent => monsterEvent.SpawnPosType.HasFlag(spawnType));
bool fullLog = false;
if (args.Length > 1)
{
bool.TryParse(args[1], out fullLog);
}
debugLines = EventSet.GetDebugStatistics(filter: monsterEvent => monsterEvent.SpawnPosType.HasFlag(spawnType), fullLog: fullLog);
}
else
{
@@ -2409,7 +2415,7 @@ namespace Barotrauma
TextManager.CheckForDuplicates(args[0]);
}));
commands.Add(new Command("writetocsv", "Writes the default language (English) to a .csv file.", (string[] args) =>
commands.Add(new Command("writetocsv|xmltocsv", "Writes the default language (English) to a .csv file.", (string[] args) =>
{
TextManager.WriteToCSV();
NPCConversation.WriteToCSV();
@@ -1091,6 +1091,29 @@ namespace Barotrauma
var maxVersion = new Version(attribute.Value);
if (GameMain.Version > maxVersion) { return false; }
break;
case "buildconfiguration":
switch (attribute.Value.ToString().ToLowerInvariant())
{
case "debug":
#if DEBUG
return true;
#else
break;
#endif
case "unstable":
#if UNSTABLE
return true;
#else
break;
#endif
case "release":
#if !DEBUG && !UNSTABLE
return true;
#else
break;
#endif
}
return false;
}
}
@@ -439,7 +439,7 @@ namespace Barotrauma
for (int i = 0; i < Content.CountChildren; i++)
{
GUIComponent child = Content.GetChild(i);
if (!child.Visible) { continue; }
if (child == null || !child.Visible) { continue; }
if (RectTransform != null)
{
callback(i, new Point(x, y));
@@ -43,36 +43,42 @@ namespace Barotrauma
private bool needsRefresh, needsBuyingRefresh, needsSellingRefresh, needsItemsToSellRefresh, needsSellingFromSubRefresh, needsItemsToSellFromSubRefresh;
private Point resolutionWhenCreated;
private bool hadPermissions;
private Dictionary<ItemPrefab, int> OwnedItems { get; } = new Dictionary<ItemPrefab, int>();
private CargoManager CargoManager => campaignUI.Campaign.CargoManager;
private Location CurrentLocation => campaignUI.Campaign.Map?.CurrentLocation;
private int PlayerMoney => campaignUI.Campaign.Money;
private bool HasPermissions => campaignUI.Campaign.AllowedToManageCampaign();
private bool IsBuying => activeTab switch
{
StoreTab.Buy => true,
StoreTab.Sell => false,
StoreTab.SellFromSub => false,
StoreTab.SellSub => false,
_ => throw new NotImplementedException()
};
private GUIListBox ActiveShoppingCrateList => activeTab switch
{
StoreTab.Buy => shoppingCrateBuyList,
StoreTab.Sell => shoppingCrateSellList,
StoreTab.SellFromSub => shoppingCrateSellFromSubList,
StoreTab.SellSub => shoppingCrateSellFromSubList,
_ => throw new NotImplementedException()
};
private bool IsTabUnavailable(StoreTab tab) => !tabLists.ContainsKey(tab);
public enum StoreTab
{
/// <summary>
/// Buy items from the store
/// </summary>
Buy,
/// <summary>
/// Sell items from the character inventory
/// </summary>
Sell,
SellFromSub
/// <summary>
/// Sell items from the sub
/// </summary>
SellSub
}
private enum SortingMethod
@@ -84,11 +90,117 @@ namespace Barotrauma
CategoryAsc
}
#region Permissions
private bool hadPermissions, hadBuyPermissions, hadSellInventoryPermissions, hadSellSubPermissions;
private bool HasPermissions
{
get => GetPermissions();
set => hadPermissions = value;
}
private bool HasBuyPermissions
{
get => HasPermissions || GetPermissions(StoreTab.Buy);
set => hadBuyPermissions = value;
}
private bool HasSellInventoryPermissions
{
get => HasPermissions || GetPermissions(StoreTab.Sell);
set => hadSellInventoryPermissions = value;
}
private bool HasSellSubPermissions
{
get => HasPermissions || GetPermissions(StoreTab.SellSub);
set => hadSellSubPermissions = value;
}
private bool GetPermissions(StoreTab? tab = null)
{
if (!tab.HasValue)
{
return campaignUI.Campaign.AllowedToManageCampaign() || campaignUI.Campaign.AllowedToManageCampaign(Networking.ClientPermissions.CampaignStore);
}
else
{
return tab.Value switch
{
StoreTab.Buy => campaignUI.Campaign.AllowedToManageCampaign(Networking.ClientPermissions.BuyItems),
StoreTab.Sell => campaignUI.Campaign.AllowedToManageCampaign(Networking.ClientPermissions.SellInventoryItems),
StoreTab.SellSub => campaignUI.Campaign.AllowedToManageCampaign(Networking.ClientPermissions.SellSubItems),
_ => false,
};
}
}
private void UpdatePermissions(StoreTab? tab = null)
{
HasPermissions = GetPermissions();
if (!tab.HasValue)
{
HasBuyPermissions = GetPermissions(StoreTab.Buy);
HasSellInventoryPermissions = GetPermissions(StoreTab.Sell);
HasSellSubPermissions = GetPermissions(StoreTab.SellSub);
}
else
{
switch (tab.Value)
{
case StoreTab.Buy:
HasBuyPermissions = GetPermissions(tab.Value);
break;
case StoreTab.Sell:
HasSellInventoryPermissions = GetPermissions(tab.Value);
break;
case StoreTab.SellSub:
HasSellSubPermissions = GetPermissions(tab.Value);
break;
}
}
}
private bool HasTabPermissions(StoreTab tab)
{
return tab switch
{
StoreTab.Buy => HasBuyPermissions,
StoreTab.Sell => HasSellInventoryPermissions,
StoreTab.SellSub => HasSellSubPermissions,
_ => false
};
}
private bool HasActiveTabPermissions()
{
return HasTabPermissions(activeTab);
}
private bool HavePermissionsChanged(StoreTab? tab = null)
{
if (!tab.HasValue)
{
return hadPermissions != HasPermissions;
}
else
{
bool hadTabPermissions = tab.Value switch
{
StoreTab.Buy => hadBuyPermissions,
StoreTab.Sell => hadSellInventoryPermissions,
StoreTab.SellSub => hadSellSubPermissions,
_ => false
};
return hadTabPermissions != HasTabPermissions(tab.Value);
}
}
#endregion
public Store(CampaignUI campaignUI, GUIComponent parentComponent)
{
this.campaignUI = campaignUI;
this.parentComponent = parentComponent;
hadPermissions = HasPermissions;
UpdatePermissions();
CreateUI();
campaignUI.Campaign.Map.OnLocationChanged += UpdateLocation;
if (CurrentLocation?.Reputation != null)
@@ -109,7 +221,7 @@ namespace Barotrauma
public void Refresh(bool updateOwned = true)
{
hadPermissions = HasPermissions;
UpdatePermissions();
if (updateOwned) { UpdateOwnedItems(); }
RefreshBuying(updateOwned: false);
RefreshSelling(updateOwned: false);
@@ -122,7 +234,7 @@ namespace Barotrauma
if (updateOwned) { UpdateOwnedItems(); }
RefreshShoppingCrateBuyList();
RefreshStoreBuyList();
var hasPermissions = HasPermissions;
bool hasPermissions = HasTabPermissions(StoreTab.Buy);
storeBuyList.Enabled = hasPermissions;
shoppingCrateBuyList.Enabled = hasPermissions;
needsBuyingRefresh = false;
@@ -133,7 +245,7 @@ namespace Barotrauma
if (updateOwned) { UpdateOwnedItems(); }
RefreshShoppingCrateSellList();
RefreshStoreSellList();
var hasPermissions = HasPermissions;
bool hasPermissions = HasTabPermissions(StoreTab.Sell);
storeSellList.Enabled = hasPermissions;
shoppingCrateSellList.Enabled = hasPermissions;
needsSellingRefresh = false;
@@ -141,13 +253,11 @@ namespace Barotrauma
private void RefreshSellingFromSub(bool updateOwned = true, bool updateItemsToSellFromSub = true)
{
if (IsTabUnavailable(StoreTab.SellFromSub)) { return; }
if (updateOwned) { UpdateOwnedItems(); }
if (updateItemsToSellFromSub) RefreshItemsToSellFromSub();
RefreshShoppingCrateSellFromSubList();
RefreshStoreSellFromSubList();
// TODO: Separate permissions from regular campaign permissions
var hasPermissions = HasPermissions;
bool hasPermissions = HasTabPermissions(StoreTab.SellSub);
storeSellFromSubList.Enabled = hasPermissions;
shoppingCrateSellFromSubList.Enabled = hasPermissions;
needsSellingFromSubRefresh = false;
@@ -261,7 +371,7 @@ namespace Barotrauma
{
StoreTab.Buy => CurrentLocation.StoreCurrentBalance + buyTotal,
StoreTab.Sell => CurrentLocation.StoreCurrentBalance - sellTotal,
StoreTab.SellFromSub => CurrentLocation.StoreCurrentBalance - sellFromSubTotal,
StoreTab.SellSub => CurrentLocation.StoreCurrentBalance - sellFromSubTotal,
_ => throw new NotImplementedException(),
};
if (balanceAfterTransaction != CurrentLocation.StoreCurrentBalance)
@@ -325,10 +435,9 @@ namespace Barotrauma
tabSortingMethods.Clear();
foreach (StoreTab tab in tabs)
{
if (tab == StoreTab.SellFromSub && GameMain.IsMultiplayer) { continue; }
string text = tab switch
{
StoreTab.SellFromSub => TextManager.Get("submarine"),
StoreTab.SellSub => TextManager.Get("submarine"),
_ => TextManager.Get("campaignstoretab." + tab)
};
var tabButton = new GUIButton(new RectTransform(new Vector2(1.0f / (tabs.Length + 1), 1.0f), modeButtonContainer.RectTransform),
@@ -456,16 +565,13 @@ namespace Barotrauma
storeRequestedGoodGroup = CreateDealsGroup(storeSellList);
tabLists.Add(StoreTab.Sell, storeSellList);
if (GameMain.IsSingleplayer)
storeSellFromSubList = new GUIListBox(new RectTransform(Vector2.One, storeItemListContainer.RectTransform))
{
storeSellFromSubList = new GUIListBox(new RectTransform(Vector2.One, storeItemListContainer.RectTransform))
{
AutoHideScrollBar = false,
Visible = false
};
storeRequestedSubGoodGroup = CreateDealsGroup(storeSellFromSubList);
tabLists.Add(StoreTab.SellFromSub, storeSellFromSubList);
}
AutoHideScrollBar = false,
Visible = false
};
storeRequestedSubGoodGroup = CreateDealsGroup(storeSellFromSubList);
tabLists.Add(StoreTab.SellSub, storeSellFromSubList);
// Shopping Crate ------------------------------------------------------------------------------------------------------------------------------------------
@@ -526,10 +632,7 @@ namespace Barotrauma
var shoppingCrateListContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.8f), shoppingCrateInventoryContainer.RectTransform), style: null);
shoppingCrateBuyList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false };
shoppingCrateSellList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false };
if (GameMain.IsSingleplayer)
{
shoppingCrateSellFromSubList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false };
}
shoppingCrateSellFromSubList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false };
var relevantBalanceContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), shoppingCrateInventoryContainer.RectTransform), isHorizontal: true)
{
@@ -569,16 +672,16 @@ namespace Barotrauma
clearAllButton = new GUIButton(new RectTransform(new Vector2(0.35f, 1.0f), buttonContainer.RectTransform), TextManager.Get("campaignstore.clearall"))
{
ClickSound = GUISoundType.DecreaseQuantity,
Enabled = HasPermissions,
Enabled = HasActiveTabPermissions(),
ForceUpperCase = true,
OnClicked = (button, userData) =>
{
if (!HasPermissions) { return false; }
if (!HasActiveTabPermissions()) { return false; }
var itemsToRemove = activeTab switch
{
StoreTab.Buy => new List<PurchasedItem>(CargoManager.ItemsInBuyCrate),
StoreTab.Sell => new List<PurchasedItem>(CargoManager.ItemsInSellCrate),
StoreTab.SellFromSub => new List<PurchasedItem>(CargoManager.ItemsInSellFromSubCrate),
StoreTab.SellSub => new List<PurchasedItem>(CargoManager.ItemsInSellFromSubCrate),
_ => throw new NotImplementedException(),
};
itemsToRemove.ForEach(i => ClearFromShoppingCrate(i));
@@ -637,7 +740,6 @@ namespace Barotrauma
private void ChangeStoreTab(StoreTab tab)
{
if (IsTabUnavailable(tab)) { return; }
activeTab = tab;
foreach (GUIButton tabButton in storeTabButtons)
{
@@ -680,7 +782,7 @@ namespace Barotrauma
}
shoppingCrateSellList.Visible = true;
break;
case StoreTab.SellFromSub:
case StoreTab.SellSub:
storeBuyList.Visible = false;
storeSellList.Visible = false;
if (storeSellFromSubList != null)
@@ -699,7 +801,6 @@ namespace Barotrauma
private void FilterStoreItems(MapEntityCategory? category, string filter)
{
if (IsTabUnavailable(activeTab)) { return; }
selectedItemCategory = category;
var list = tabLists[activeTab];
filter = filter?.ToLower();
@@ -733,7 +834,7 @@ namespace Barotrauma
float prevBuyListScroll = storeBuyList.BarScroll;
float prevShoppingCrateScroll = shoppingCrateBuyList.BarScroll;
bool hasPermissions = HasPermissions;
bool hasPermissions = HasBuyPermissions;
HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>();
int dailySpecialCount = CurrentLocation?.DailySpecials.Count() ?? 3;
@@ -816,7 +917,7 @@ namespace Barotrauma
{
float prevSellListScroll = storeSellList.BarScroll;
float prevShoppingCrateScroll = shoppingCrateSellList.BarScroll;
bool hasPermissions = HasPermissions;
bool hasPermissions = HasTabPermissions(StoreTab.Sell);
HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>();
if ((storeRequestedGoodGroup != null) != CurrentLocation.RequestedGoods.Any())
@@ -894,7 +995,7 @@ namespace Barotrauma
{
float prevSellListScroll = storeSellFromSubList.BarScroll;
float prevShoppingCrateScroll = shoppingCrateSellFromSubList.BarScroll;
bool hasPermissions = HasPermissions;
bool hasPermissions = HasSellSubPermissions;
HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>();
if ((storeRequestedSubGoodGroup != null) != CurrentLocation.RequestedGoods.Any())
@@ -938,12 +1039,12 @@ namespace Barotrauma
if (itemFrame == null)
{
var parentComponent = isRequestedGood ? storeRequestedSubGoodGroup : storeSellFromSubList as GUIComponent;
itemFrame = CreateItemFrame(new PurchasedItem(itemPrefab, itemQuantity), parentComponent, StoreTab.SellFromSub, forceDisable: !hasPermissions);
itemFrame = CreateItemFrame(new PurchasedItem(itemPrefab, itemQuantity), parentComponent, StoreTab.SellSub, forceDisable: !hasPermissions);
}
else
{
(itemFrame.UserData as PurchasedItem).Quantity = itemQuantity;
SetQuantityLabelText(StoreTab.SellFromSub, itemFrame);
SetQuantityLabelText(StoreTab.SellSub, itemFrame);
SetOwnedLabelText(itemFrame);
SetPriceGetters(itemFrame, false);
}
@@ -961,8 +1062,8 @@ namespace Barotrauma
removedItemFrames.AddRange(storeRequestedSubGoodGroup.Children.Where(c => c.UserData is PurchasedItem).Except(existingItemFrames).ToList());
}
removedItemFrames.ForEach(f => f.RectTransform.Parent = null);
if (activeTab == StoreTab.SellFromSub) { FilterStoreItems(); }
SortItems(StoreTab.SellFromSub);
if (activeTab == StoreTab.SellSub) { FilterStoreItems(); }
SortItems(StoreTab.SellSub);
storeSellFromSubList.BarScroll = prevSellListScroll;
shoppingCrateSellFromSubList.BarScroll = prevShoppingCrateScroll;
@@ -1062,17 +1163,14 @@ namespace Barotrauma
private void RefreshShoppingCrateList(List<PurchasedItem> items, GUIListBox listBox, StoreTab tab)
{
bool hasPermissions = HasPermissions;
bool hasPermissions = HasTabPermissions(tab);
HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>();
int totalPrice = 0;
foreach (PurchasedItem item in items)
{
PriceInfo priceInfo = item.ItemPrefab.GetPriceInfo(CurrentLocation);
if (priceInfo == null) { continue; }
var itemFrame = listBox.Content.FindChild(c => c.UserData is PurchasedItem pi && pi.ItemPrefab.Identifier == item.ItemPrefab.Identifier);
if (!(item.ItemPrefab.GetPriceInfo(CurrentLocation) is { } priceInfo)) { continue; }
GUINumberInput numInput = null;
if (itemFrame == null)
if (!(listBox.Content.FindChild(c => c.UserData is PurchasedItem pi && pi.ItemPrefab.Identifier == item.ItemPrefab.Identifier) is { } itemFrame))
{
itemFrame = CreateItemFrame(item, listBox, tab, forceDisable: !hasPermissions);
numInput = itemFrame.FindChild(c => c is GUINumberInput, recursive: true) as GUINumberInput;
@@ -1100,10 +1198,21 @@ namespace Barotrauma
}
suppressBuySell = false;
var price = tab == StoreTab.Buy ?
CurrentLocation.GetAdjustedItemBuyPrice(item.ItemPrefab, priceInfo: priceInfo) :
CurrentLocation.GetAdjustedItemSellPrice(item.ItemPrefab, priceInfo: priceInfo);
totalPrice += item.Quantity * price;
try
{
int price = tab switch
{
StoreTab.Buy => CurrentLocation.GetAdjustedItemBuyPrice(item.ItemPrefab, priceInfo: priceInfo),
StoreTab.Sell => CurrentLocation.GetAdjustedItemSellPrice(item.ItemPrefab, priceInfo: priceInfo),
StoreTab.SellSub => CurrentLocation.GetAdjustedItemSellPrice(item.ItemPrefab, priceInfo: priceInfo),
_ => throw new NotImplementedException()
};
totalPrice += item.Quantity * price;
}
catch (NotImplementedException e)
{
DebugConsole.ShowError($"Error getting item price: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
}
}
var removedItemFrames = listBox.Content.Children.Except(existingItemFrames).ToList();
@@ -1119,7 +1228,7 @@ namespace Barotrauma
case StoreTab.Sell:
sellTotal = totalPrice;
break;
case StoreTab.SellFromSub:
case StoreTab.SellSub:
sellFromSubTotal = totalPrice;
break;
}
@@ -1135,7 +1244,7 @@ namespace Barotrauma
private void RefreshShoppingCrateSellList() => RefreshShoppingCrateList(CargoManager.ItemsInSellCrate, shoppingCrateSellList, StoreTab.Sell);
private void RefreshShoppingCrateSellFromSubList() => RefreshShoppingCrateList(CargoManager.ItemsInSellFromSubCrate, shoppingCrateSellFromSubList, StoreTab.SellFromSub);
private void RefreshShoppingCrateSellFromSubList() => RefreshShoppingCrateList(CargoManager.ItemsInSellFromSubCrate, shoppingCrateSellFromSubList, StoreTab.SellSub);
private void SortItems(GUIListBox list, SortingMethod sortingMethod)
{
@@ -1286,14 +1395,12 @@ namespace Barotrauma
private void SortItems(StoreTab tab, SortingMethod sortingMethod)
{
if (IsTabUnavailable(tab)) { return; }
tabSortingMethods[tab] = sortingMethod;
SortItems(tabLists[tab], sortingMethod);
}
private void SortItems(StoreTab tab)
{
if (IsTabUnavailable(tab)) { return; }
SortItems(tab, tabSortingMethods[tab]);
}
@@ -1301,12 +1408,6 @@ namespace Barotrauma
private GUIComponent CreateItemFrame(PurchasedItem pi, GUIComponent parentComponent, StoreTab containingTab, bool forceDisable = false)
{
var tooltip = pi.ItemPrefab.Name;
if (!string.IsNullOrWhiteSpace(pi.ItemPrefab.Description))
{
tooltip += "\n" + pi.ItemPrefab.Description;
}
GUIListBox parentListBox = parentComponent as GUIListBox;
int width = 0;
RectTransform parent = null;
@@ -1320,7 +1421,11 @@ namespace Barotrauma
width = parentComponent.Rect.Width;
parent = parentComponent.RectTransform;
}
string tooltip = pi.ItemPrefab.Name;
if (!string.IsNullOrWhiteSpace(pi.ItemPrefab.Description))
{
tooltip += $"\n{pi.ItemPrefab.Description}";
}
GUIFrame frame = new GUIFrame(new RectTransform(new Point(width, (int)(GUI.yScale * 80)), parent: parent), style: "ListBoxElement")
{
ToolTip = tooltip,
@@ -1338,8 +1443,7 @@ namespace Barotrauma
var iconRelativeWidth = 0.0f;
var priceAndButtonRelativeWidth = 1.0f - nameAndIconRelativeWidth;
Sprite itemIcon = pi.ItemPrefab.InventoryIcon ?? pi.ItemPrefab.sprite;
if (itemIcon != null)
if ((pi.ItemPrefab.InventoryIcon ?? pi.ItemPrefab.sprite) is { } itemIcon)
{
iconRelativeWidth = (0.9f * mainGroup.Rect.Height) / mainGroup.Rect.Width;
GUIImage img = new GUIImage(new RectTransform(new Vector2(iconRelativeWidth, 0.9f), mainGroup.RectTransform), itemIcon, scaleToFit: true)
@@ -1425,7 +1529,7 @@ namespace Barotrauma
{
if (suppressBuySell) { return; }
PurchasedItem purchasedItem = numberInput.UserData as PurchasedItem;
if (!HasPermissions)
if (!HasActiveTabPermissions())
{
numberInput.IntValue = purchasedItem.Quantity;
return;
@@ -1528,11 +1632,16 @@ namespace Barotrauma
OwnedItems.Clear();
// Add items on the sub(s)
Submarine.MainSub?.GetItems(true)
.Where(i => i.Components.All(c => !(c is Holdable h) || !h.Attachable || !h.Attached) &&
i.Components.All(c => !(c is Wire w) || w.Connections.All(c => c == null)) &&
ItemAndAllContainersInteractable(i))
.ForEach(i => AddToOwnedItems(i.Prefab));
if (Submarine.MainSub?.GetItems(true) is List<Item> subItems)
{
foreach (var subItem in subItems)
{
if (!subItem.Components.All(c => !(c is Holdable h) || !h.Attachable || !h.Attached)) { continue; }
if (!subItem.Components.All(c => !(c is Wire w) || w.Connections.All(c => c == null))) { continue; }
if (!ItemAndAllContainersInteractable(subItem)) { continue; }
AddToOwnedItems(subItem.Prefab);
}
}
// Add items in character inventories
foreach (var item in Item.ItemList)
@@ -1664,14 +1773,22 @@ namespace Barotrauma
private int GetMaxAvailable(ItemPrefab itemPrefab, StoreTab mode)
{
var list = mode switch
List<PurchasedItem> list = null;
try
{
StoreTab.Buy => CurrentLocation.StoreStock,
StoreTab.Sell => itemsToSell,
StoreTab.SellFromSub => itemsToSellFromSub,
_ => throw new NotImplementedException()
};
if (list.Find(i => i.ItemPrefab == itemPrefab) is PurchasedItem item)
list = mode switch
{
StoreTab.Buy => CurrentLocation?.StoreStock,
StoreTab.Sell => itemsToSell,
StoreTab.SellSub => itemsToSellFromSub,
_ => throw new NotImplementedException()
};
}
catch (NotImplementedException e)
{
DebugConsole.ShowError($"Error getting item availability: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
}
if (list != null && list.Find(i => i.ItemPrefab == itemPrefab) is PurchasedItem item)
{
if (mode == StoreTab.Buy)
{
@@ -1691,8 +1808,8 @@ namespace Barotrauma
private bool ModifyBuyQuantity(PurchasedItem item, int quantity)
{
if (item == null || item.ItemPrefab == null) { return false; }
if (!HasPermissions) { return false; }
if (item?.ItemPrefab == null) { return false; }
if (!HasBuyPermissions) { return false; }
if (quantity > 0)
{
var itemInCrate = CargoManager.ItemsInBuyCrate.Find(i => i.ItemPrefab == item.ItemPrefab);
@@ -1703,13 +1820,13 @@ namespace Barotrauma
}
CargoManager.ModifyItemQuantityInBuyCrate(item.ItemPrefab, quantity);
GameMain.Client?.SendCampaignState();
return false;
return true;
}
private bool ModifySellQuantity(PurchasedItem item, int quantity)
{
if (item == null || item.ItemPrefab == null) { return false; }
if (!HasPermissions) { return false; }
if (item?.ItemPrefab == null) { return false; }
if (!HasSellInventoryPermissions) { return false; }
if (quantity > 0)
{
// Make sure there's enough available to sell
@@ -1718,45 +1835,68 @@ namespace Barotrauma
if (totalQuantityToSell > GetMaxAvailable(item.ItemPrefab, StoreTab.Sell)) { return false; }
}
CargoManager.ModifyItemQuantityInSellCrate(item.ItemPrefab, quantity);
//GameMain.Client?.SendCampaignState();
return false;
return true;
}
private bool ModifySellFromSubQuantity(PurchasedItem item, int quantity)
{
if (item == null || item.ItemPrefab == null) { return false; }
if (!HasPermissions) { return false; }
if (item?.ItemPrefab == null) { return false; }
if (!HasSellSubPermissions) { return false; }
if (quantity > 0)
{
// Make sure there's enough available to sell
var itemToSell = CargoManager.ItemsInSellFromSubCrate.Find(i => i.ItemPrefab == item.ItemPrefab);
var totalQuantityToSell = itemToSell != null ? itemToSell.Quantity + quantity : quantity;
if (totalQuantityToSell > GetMaxAvailable(item.ItemPrefab, StoreTab.SellFromSub)) { return false; }
if (totalQuantityToSell > GetMaxAvailable(item.ItemPrefab, StoreTab.SellSub)) { return false; }
}
CargoManager.ModifyItemQuantityInSellFromSubCrate(item.ItemPrefab, quantity);
// TODO: GameMain.Client?.SendCampaignState();
return false;
GameMain.Client?.SendCampaignState();
return true;
}
private bool AddToShoppingCrate(PurchasedItem item, int quantity = 1) => activeTab switch
private bool AddToShoppingCrate(PurchasedItem item, int quantity = 1)
{
StoreTab.Buy => ModifyBuyQuantity(item, quantity),
StoreTab.Sell => ModifySellQuantity(item, quantity),
StoreTab.SellFromSub => ModifySellFromSubQuantity(item, quantity),
_ => throw new NotImplementedException(),
};
if (item == null) { return false; }
try
{
return activeTab switch
{
StoreTab.Buy => ModifyBuyQuantity(item, quantity),
StoreTab.Sell => ModifySellQuantity(item, quantity),
StoreTab.SellSub => ModifySellFromSubQuantity(item, quantity),
_ => throw new NotImplementedException()
};
}
catch (NotImplementedException e)
{
DebugConsole.ShowError($"Error adding an item to the shopping crate: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
return false;
}
}
private bool ClearFromShoppingCrate(PurchasedItem item) => activeTab switch
private bool ClearFromShoppingCrate(PurchasedItem item)
{
StoreTab.Buy => ModifyBuyQuantity(item, -item.Quantity),
StoreTab.Sell => ModifySellQuantity(item, -item.Quantity),
StoreTab.SellFromSub => ModifySellFromSubQuantity(item, -item.Quantity),
_ => throw new NotImplementedException(),
};
if (item == null) { return false; }
try
{
return activeTab switch
{
StoreTab.Buy => ModifyBuyQuantity(item, -item.Quantity),
StoreTab.Sell => ModifySellQuantity(item, -item.Quantity),
StoreTab.SellSub => ModifySellFromSubQuantity(item, -item.Quantity),
_ => throw new NotImplementedException(),
};
}
catch (NotImplementedException e)
{
DebugConsole.ShowError($"Error clearing the shopping crate: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
return false;
}
}
private bool BuyItems()
{
if (!HasPermissions) { return false; }
if (!HasBuyPermissions) { return false; }
var itemsToPurchase = new List<PurchasedItem>(CargoManager.ItemsInBuyCrate);
var itemsToRemove = new List<PurchasedItem>();
@@ -1788,15 +1928,24 @@ namespace Barotrauma
private bool SellItems()
{
if (!HasPermissions) { return false; }
var itemsToSell = activeTab switch
if (!HasActiveTabPermissions()) { return false; }
List<PurchasedItem> itemsToSell;
try
{
StoreTab.Sell => new List<PurchasedItem>(CargoManager.ItemsInSellCrate),
StoreTab.SellFromSub => new List<PurchasedItem>(CargoManager.ItemsInSellFromSubCrate),
_ => throw new NotImplementedException()
};
itemsToSell = activeTab switch
{
StoreTab.Sell => new List<PurchasedItem>(CargoManager.ItemsInSellCrate),
StoreTab.SellSub => new List<PurchasedItem>(CargoManager.ItemsInSellFromSubCrate),
_ => throw new NotImplementedException()
};
}
catch (NotImplementedException e)
{
DebugConsole.ShowError($"Error confirming the store transaction: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
return false;
}
var itemsToRemove = new List<PurchasedItem>();
var totalValue = 0;
int totalValue = 0;
foreach (PurchasedItem item in itemsToSell)
{
if (item?.ItemPrefab?.GetPriceInfo(CurrentLocation) is PriceInfo priceInfo)
@@ -1811,11 +1960,7 @@ namespace Barotrauma
itemsToRemove.ForEach(i => itemsToSell.Remove(i));
if (itemsToSell.None() || totalValue > CurrentLocation.StoreCurrentBalance) { return false; }
CargoManager.SellItems(itemsToSell, activeTab);
if (activeTab == StoreTab.Sell)
{
// TODO: Implement selling sub items in multiplayer
GameMain.Client?.SendCampaignState();
}
GameMain.Client?.SendCampaignState();
return false;
}
@@ -1831,7 +1976,7 @@ namespace Barotrauma
int total = activeTab switch
{
StoreTab.Sell => sellTotal,
StoreTab.SellFromSub => sellFromSubTotal,
StoreTab.SellSub => sellFromSubTotal,
_ => throw new NotImplementedException(),
};
shoppingCrateTotal.Text = GetCurrencyFormatted(total);
@@ -1863,21 +2008,29 @@ namespace Barotrauma
}
}
private void SetConfirmButtonStatus() => confirmButton.Enabled =
HasPermissions && ActiveShoppingCrateList.Content.RectTransform.Children.Any() &&
activeTab switch
{
StoreTab.Buy => buyTotal <= PlayerMoney,
StoreTab.Sell => CurrentLocation != null && sellTotal <= CurrentLocation.StoreCurrentBalance,
StoreTab.SellFromSub => CurrentLocation != null && sellFromSubTotal <= CurrentLocation.StoreCurrentBalance,
_ => throw new NotImplementedException(),
};
private void SetConfirmButtonStatus()
{
confirmButton.Enabled =
HasActiveTabPermissions() &&
ActiveShoppingCrateList.Content.RectTransform.Children.Any() &&
activeTab switch
{
StoreTab.Buy => buyTotal <= PlayerMoney,
StoreTab.Sell => CurrentLocation != null && sellTotal <= CurrentLocation.StoreCurrentBalance,
StoreTab.SellSub => CurrentLocation != null && sellFromSubTotal <= CurrentLocation.StoreCurrentBalance,
_ => false
};
}
private void SetClearAllButtonStatus() => clearAllButton.Enabled =
HasPermissions && ActiveShoppingCrateList.Content.RectTransform.Children.Any();
private void SetClearAllButtonStatus()
{
clearAllButton.Enabled =
HasActiveTabPermissions() &&
ActiveShoppingCrateList.Content.RectTransform.Children.Any();
}
private float ownedItemsUpdateTimer = 0.0f, sellableItemsFromSubUpdateTimer = 0.0f;
private readonly float timerUpdateInterval = 1.5f;
private const float timerUpdateInterval = 1.5f;
public void Update(float deltaTime)
{
@@ -1914,10 +2067,10 @@ namespace Barotrauma
if (needsItemsToSellRefresh) { RefreshItemsToSell(); }
if (needsItemsToSellFromSubRefresh) { RefreshItemsToSellFromSub(); }
if (needsRefresh || hadPermissions != HasPermissions) { Refresh(updateOwned: ownedItemsUpdateTimer > 0.0f); }
if (needsBuyingRefresh) { RefreshBuying(); }
if (needsSellingRefresh) { RefreshSelling(); }
if (needsSellingFromSubRefresh) { RefreshSellingFromSub(updateItemsToSellFromSub: sellableItemsFromSubUpdateTimer > 0.0f); }
if (needsRefresh || HavePermissionsChanged()) { Refresh(updateOwned: ownedItemsUpdateTimer > 0.0f); }
if (needsBuyingRefresh || HavePermissionsChanged(StoreTab.Buy)) { RefreshBuying(updateOwned: ownedItemsUpdateTimer > 0.0f); }
if (needsSellingRefresh || HavePermissionsChanged(StoreTab.Sell)) { RefreshSelling(updateOwned: ownedItemsUpdateTimer > 0.0f); }
if (needsSellingFromSubRefresh || HavePermissionsChanged(StoreTab.SellSub)) { RefreshSellingFromSub(updateItemsToSellFromSub: sellableItemsFromSubUpdateTimer > 0.0f); }
}
}
}
@@ -167,6 +167,7 @@ namespace Barotrauma
//TODO: move this somewhere else
public static void UpdateCategoryList(GUIListBox categoryList, CampaignMode campaign, Submarine? drawnSubmarine, IEnumerable<UpgradeCategory> applicableCategories)
{
var subItems = GetSubItems();
foreach (GUIComponent component in categoryList.Content.Children)
{
if (!(component.UserData is CategoryData data)) { continue; }
@@ -178,7 +179,7 @@ namespace Barotrauma
var customizeButton = component.FindChild("customizebutton", true);
if (customizeButton != null)
{
customizeButton.Visible = HasSwappableItems(data.Category);
customizeButton.Visible = HasSwappableItems(data.Category, subItems);
}
}
@@ -719,16 +720,19 @@ namespace Barotrauma
private bool customizeTabOpen;
private static bool HasSwappableItems(UpgradeCategory category)
private static bool HasSwappableItems(UpgradeCategory category, List<Item>? subItems = null)
{
if (Submarine.MainSub == null) { return false; }
return Submarine.MainSub.GetItems(true).Any(i =>
subItems ??= GetSubItems();
return subItems.Any(i =>
i.Prefab.SwappableItem != null &&
!i.HiddenInGame && i.AllowSwapping &&
(i.Prefab.SwappableItem.CanBeBought || ItemPrefab.Prefabs.Any(ip => ip.SwappableItem?.ReplacementOnUninstall == i.Prefab.Identifier)) &&
Submarine.MainSub.IsEntityFoundOnThisSub(i, true) && category.ItemTags.Any(t => i.HasTag(t)));
}
private static List<Item> GetSubItems() => Submarine.MainSub?.GetItems(true) ?? new List<Item>();
private void SelectUpgradeCategory(List<UpgradePrefab> prefabs, UpgradeCategory category, Submarine submarine)
{
if (selectedUpgradeCategoryLayout == null) { return; }
@@ -1,5 +1,6 @@
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -7,37 +8,6 @@ namespace Barotrauma
{
partial class CargoManager
{
private class SoldEntity
{
public enum SellStatus
{
/// <summary>
/// Entity sold in SP. Or, entity sold by client and confirmed by server in MP.
/// </summary>
Confirmed,
/// <summary>
/// Entity sold by client in MP. Client has received at least one update from server after selling, but this entity wasn't yet confirmed.
/// </summary>
Unconfirmed,
/// <summary>
/// Entity sold by client in MP. Client hasn't yet received an update from server after selling.
/// </summary>
Local
}
public Item Item { get; }
public SellStatus Status { get; set; }
private SoldEntity(Item item, SellStatus status)
{
Item = item;
Status = status;
}
public static SoldEntity CreateInSinglePlayer(Item item) => new SoldEntity(item, SellStatus.Confirmed);
public static SoldEntity CreateInMultiPlayer(Item item) => new SoldEntity(item, SellStatus.Local);
}
private List<SoldEntity> SoldEntities { get; } = new List<SoldEntity>();
// The bag slot is intentionally left out since we want to be able to sell items from there
@@ -67,31 +37,6 @@ 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 (!IsItemSellable(item, confirmedSoldEntities)) { return false; }
if (item.GetRootInventoryOwner() is Character) { 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; }
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:
@@ -100,24 +45,6 @@ namespace Barotrauma
return SoldEntities.Where(se => se.Status != SoldEntity.SellStatus.Unconfirmed);
}
private bool IsItemSellable(Item item, IEnumerable<SoldEntity> confirmedSoldEntities)
{
if (!item.Prefab.CanBeSold) { return false; }
if (item.SpawnedInCurrentOutpost) { return false; }
if (!item.Prefab.AllowSellingWhenBroken && item.ConditionPercentage < 90.0f) { return false; }
if (confirmedSoldEntities.Any(it => it.Item == item)) { return false; }
if (item.OwnInventory?.Container is ItemContainer itemContainer)
{
var containedItems = item.ContainedItems;
if (containedItems.None()) { return true; }
// Allow selling the item if contained items are unsellable and set to be removed on deconstruct
if (itemContainer.RemoveContainedItemsOnDeconstruct && containedItems.All(it => !it.Prefab.CanBeSold)) { return true; }
// Otherwise there must be no contained items or the contained items must be confirmed as sold
if (!containedItems.All(it => confirmedSoldEntities.Any(se => se.Item == it))) { return false; }
}
return true;
}
public void SetItemsInBuyCrate(List<PurchasedItem> items)
{
ItemsInBuyCrate.Clear();
@@ -125,15 +52,21 @@ namespace Barotrauma
OnItemsInBuyCrateChanged?.Invoke();
}
public void SetItemsInSubSellCrate(List<PurchasedItem> items)
{
ItemsInSellFromSubCrate.Clear();
ItemsInSellFromSubCrate.AddRange(items);
OnItemsInSellFromSubCrateChanged?.Invoke();
}
public void SetSoldItems(List<SoldItem> items)
{
SoldItems.Clear();
SoldItems.AddRange(items);
foreach (SoldEntity se in SoldEntities)
foreach (var se in SoldEntities)
{
if (se.Status == SoldEntity.SellStatus.Confirmed) { continue; }
if (SoldItems.Any(si => si.ID == se.Item.ID && si.ItemPrefab == se.Item.Prefab && (GameMain.Client == null || GameMain.Client.ID == si.SellerID)))
if (SoldItems.Any(si => Match(si, se, true)))
{
se.Status = SoldEntity.SellStatus.Confirmed;
}
@@ -142,13 +75,28 @@ namespace Barotrauma
se.Status = SoldEntity.SellStatus.Unconfirmed;
}
}
foreach (var si in SoldItems)
{
if (si.Origin != SoldItem.SellOrigin.Submarine) { continue; }
if (!(SoldEntities.FirstOrDefault(se => se.Item == null && Match(si, se, false)) is SoldEntity soldEntityMatch)) { continue; }
if (!(Entity.FindEntityByID(si.ID) is Item item)) { continue; }
soldEntityMatch.SetItem(item);
soldEntityMatch.Status = SoldEntity.SellStatus.Confirmed;
}
OnSoldItemsChanged?.Invoke();
static bool Match(SoldItem soldItem, SoldEntity soldEntity, bool matchId)
{
if (soldItem.ItemPrefab != soldEntity.ItemPrefab) { return false; }
if (matchId && (soldEntity.Item == null || soldItem.ID != soldEntity.Item.ID)) { return false; }
if (soldItem.Origin == SoldItem.SellOrigin.Character && GameMain.Client != null && soldItem.SellerID != GameMain.Client.ID) { return false; }
return true;
}
}
public void ModifyItemQuantityInSellCrate(ItemPrefab itemPrefab, int changeInQuantity)
{
PurchasedItem itemToSell = ItemsInSellCrate.Find(i => i.ItemPrefab == itemPrefab);
var itemToSell = ItemsInSellCrate.Find(i => i.ItemPrefab == itemPrefab);
if (itemToSell != null)
{
itemToSell.Quantity += changeInQuantity;
@@ -186,74 +134,69 @@ namespace Barotrauma
public void SellItems(List<PurchasedItem> itemsToSell, Store.StoreTab sellingMode)
{
var sellableItems = sellingMode switch
IEnumerable<Item> sellableItems;
try
{
Store.StoreTab.Sell => GetSellableItems(Character.Controlled),
Store.StoreTab.SellFromSub => GetSellableItemsFromSub(),
_ => throw new System.NotImplementedException(),
};
sellableItems = sellingMode switch
{
Store.StoreTab.Sell => GetSellableItems(Character.Controlled),
Store.StoreTab.SellSub => GetSellableItemsFromSub(),
_ => throw new NotImplementedException()
};
}
catch (NotImplementedException e)
{
DebugConsole.ShowError($"Error selling items: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
return;
}
bool canAddToRemoveQueue = campaign.IsSinglePlayer && Entity.Spawner != null;
var sellerId = GameMain.Client?.ID ?? 0;
byte sellerId = GameMain.Client?.ID ?? 0;
// Check all the prices before starting the transaction
// to make sure the modifiers stay the same for the whole transaction
Dictionary<ItemPrefab, int> sellValues = GetSellValuesAtCurrentLocation(itemsToSell.Select(i => i.ItemPrefab));
foreach (PurchasedItem item in itemsToSell)
{
var itemValue = item.Quantity * sellValues[item.ItemPrefab];
int itemValue = item.Quantity * sellValues[item.ItemPrefab];
// check if the store can afford the item
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 = sellableItems.Where(i => i.Prefab == item.ItemPrefab);
if (matchingItems.Count() <= item.Quantity)
int count = Math.Min(item.Quantity, matchingItems.Count());
SoldItem.SellOrigin origin = sellingMode == Store.StoreTab.Sell ? SoldItem.SellOrigin.Character : SoldItem.SellOrigin.Submarine;
if (origin == SoldItem.SellOrigin.Character || GameMain.IsSingleplayer)
{
foreach (Item i in matchingItems)
for (int i = 0; i < count; i++)
{
SoldItems.Add(new SoldItem(i.Prefab, i.ID, canAddToRemoveQueue, sellerId));
SoldEntities.Add(campaign.IsSinglePlayer ? SoldEntity.CreateInSinglePlayer(i) : SoldEntity.CreateInMultiPlayer(i));
if (canAddToRemoveQueue) { Entity.Spawner.AddToRemoveQueue(i); }
var matchingItem = matchingItems.ElementAt(i);
SoldItems.Add(new SoldItem(matchingItem.Prefab, matchingItem.ID, canAddToRemoveQueue, sellerId, origin));
SoldEntities.Add(new SoldEntity(matchingItem, campaign.IsSinglePlayer ? SoldEntity.SellStatus.Confirmed : SoldEntity.SellStatus.Local));
if (canAddToRemoveQueue) { Entity.Spawner.AddToRemoveQueue(matchingItem); }
}
}
else
{
for (int i = 0; i < item.Quantity; i++)
// When selling from the sub in multiplayer, the server will determine the items that are sold
for (int i = 0; i < count; i++)
{
var matchingItem = matchingItems.ElementAt(i);
SoldItems.Add(new SoldItem(matchingItem.Prefab, matchingItem.ID, canAddToRemoveQueue, sellerId));
SoldEntities.Add(campaign.IsSinglePlayer ? SoldEntity.CreateInSinglePlayer(matchingItem) : SoldEntity.CreateInMultiPlayer(matchingItem));
if (canAddToRemoveQueue) { Entity.Spawner.AddToRemoveQueue(matchingItem); }
SoldItems.Add(new SoldItem(item.ItemPrefab, Entity.NullEntityID, canAddToRemoveQueue, sellerId, origin));
SoldEntities.Add(new SoldEntity(item.ItemPrefab, SoldEntity.SellStatus.Local));
}
}
// Exchange money
Location.StoreCurrentBalance -= itemValue;
campaign.Money += itemValue;
GameAnalyticsManager.AddMoneyGainedEvent(itemValue, GameAnalyticsManager.MoneySource.Store, item.ItemPrefab.Identifier);
// Remove from the sell crate
// TODO: Simplify duplicate logic?
if (sellingMode == Store.StoreTab.Sell && ItemsInSellCrate.Find(pi => pi.ItemPrefab == item.ItemPrefab) is { } inventoryItem)
if ((sellingMode == Store.StoreTab.Sell ? ItemsInSellCrate : ItemsInSellFromSubCrate)?.Find(pi => pi.ItemPrefab == item.ItemPrefab) is { } itemToSell)
{
inventoryItem.Quantity -= item.Quantity;
if (inventoryItem.Quantity < 1)
itemToSell.Quantity -= item.Quantity;
if (itemToSell.Quantity < 1)
{
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);
(sellingMode == Store.StoreTab.Sell ? ItemsInSellCrate : ItemsInSellFromSubCrate)?.Remove(itemToSell);
}
}
}
OnSoldItemsChanged?.Invoke();
}
@@ -145,12 +145,14 @@ namespace Barotrauma
string msgCommand = ChatMessage.GetChatMessageCommand(text, out string msg);
// add to local history
ChatBox.ChatManager.Store(text);
WifiComponent headset = null;
ChatMessageType messageType =
((msgCommand == "r" || msgCommand == "radio") && ChatMessage.CanUseRadio(Character.Controlled, out headset)) ? ChatMessageType.Radio : ChatMessageType.Default;
AddSinglePlayerChatMessage(
Character.Controlled.Info.Name,
msg,
((msgCommand == "r" || msgCommand == "radio") && ChatMessage.CanUseRadio(Character.Controlled)) ? ChatMessageType.Radio : ChatMessageType.Default,
msg, messageType,
Character.Controlled);
if (ChatMessage.CanUseRadio(Character.Controlled, out WifiComponent headset))
if (messageType == ChatMessageType.Radio && headset != null)
{
Signal s = new Signal(msg, sender: Character.Controlled, source: headset.Item);
headset.TransmitSignal(s, sentFromChat: true);
@@ -819,7 +821,7 @@ namespace Barotrauma
}
else
{
OrderChatMessage msg = new OrderChatMessage(order, "", priority, order.IsReport ? hull : order.TargetEntity, null, orderGiver);
OrderChatMessage msg = new OrderChatMessage(order, "", priority, order.IsReport ? hull : order.TargetEntity, null, orderGiver, isNewOrder: isNewOrder);
GameMain.Client?.SendChatMessage(msg);
}
}
@@ -836,7 +838,7 @@ namespace Barotrauma
}
else if (orderGiver != null)
{
OrderChatMessage msg = new OrderChatMessage(order, option, priority, order?.TargetSpatialEntity ?? order?.TargetItemComponent?.Item, character, orderGiver);
OrderChatMessage msg = new OrderChatMessage(order, option, priority, order?.TargetSpatialEntity ?? order?.TargetItemComponent?.Item, character, orderGiver, isNewOrder: isNewOrder);
GameMain.Client?.SendChatMessage(msg);
}
}
@@ -2533,6 +2535,7 @@ namespace Barotrauma
{
shortcutNodes.Add(CreateOrderNode(shortcutNodeSize, null, Point.Zero, dismissedOrderPrefab, -1));
}
shortcutNodes.RemoveAll(n => n.UserData is Order o && !IsOrderAvailable(o));
if (shortcutNodes.Count < 1) { return; }
shortcutCenterNode = new GUIFrame(new RectTransform(shortcutCenterNodeSize, parent: commandFrame.RectTransform, anchor: Anchor.Center), style: null)
{
@@ -2573,7 +2576,7 @@ namespace Barotrauma
private void CreateOrderNodes(OrderCategory orderCategory)
{
var orders = Order.PrefabList.FindAll(o => o.Category == orderCategory && !o.IsReport);
var orders = Order.PrefabList.FindAll(o => o.Category == orderCategory && !o.IsReport && IsOrderAvailable(o));
Order order;
bool disableNode;
var offsets = MathUtils.GetPointsOnCircumference(Vector2.Zero, nodeDistance,
@@ -2725,6 +2728,7 @@ namespace Barotrauma
contextualOrders.Add(new OrderInfo(Order.GetPrefab(orderIdentifier), null));
}
}
contextualOrders.RemoveAll(o => !IsOrderAvailable(o.Order));
var offsets = MathUtils.GetPointsOnCircumference(Vector2.Zero, nodeDistance, contextualOrders.Count, MathHelper.ToRadians(90f + 180f / contextualOrders.Count));
bool disableNode = !CanCharacterBeHeard();
for (int i = 0; i < contextualOrders.Count; i++)
@@ -3483,6 +3487,20 @@ namespace Barotrauma
return character?.Info?.GetManualOrderPriority(order) ?? CharacterInfo.HighestManualOrderPriority;
}
private bool IsOrderAvailable(Order order)
{
if (order == null) { return false; }
switch (order.Identifier.ToLowerInvariant())
{
case "assaultenemy":
Character character = characterContext ?? Character.Controlled;
if (character?.Submarine == null) { return false; }
return character.Submarine.GetConnectedSubs().Any(s => s.TeamID != character.TeamID);
default:
return true;
}
}
#region Crew Member Assignment Logic
private bool CanOpenManualAssignment(GUIComponent node)
{
@@ -3559,7 +3577,7 @@ namespace Barotrauma
bool hasLeaks = Character.Controlled.CurrentHull.ConnectedGaps.Any(g => !g.IsRoomToRoom && g.Open > 0.0f);
ToggleReportButton("reportbreach", hasLeaks);
bool hasIntruders = Character.CharacterList.Any(c => c.CurrentHull == Character.Controlled.CurrentHull && AIObjectiveFightIntruders.IsValidTarget(c, Character.Controlled));
bool hasIntruders = Character.CharacterList.Any(c => c.CurrentHull == Character.Controlled.CurrentHull && AIObjectiveFightIntruders.IsValidTarget(c, Character.Controlled, false));
ToggleReportButton("reportintruders", hasIntruders);
foreach (GUIComponent reportButton in ReportButtonFrame.Children)
@@ -97,17 +97,16 @@ namespace Barotrauma
/// <summary>
/// There is a server-side implementation of the method in <see cref="MultiPlayerCampaign"/>
/// </summary>
public bool AllowedToManageCampaign()
public bool AllowedToManageCampaign(ClientPermissions permissions = ClientPermissions.ManageCampaign)
{
//allow ending the round if the client has permissions, is the owner, the only client in the server,
//allow managing the round if the client has permissions, is the owner, the only client in the server,
//or if no-one has management permissions
if (GameMain.Client == null) { return true; }
return
GameMain.Client.HasPermission(ClientPermissions.ManageCampaign) ||
GameMain.Client.HasPermission(permissions) ||
GameMain.Client.ConnectedClients.Count == 1 ||
GameMain.Client.IsServerOwner ||
GameMain.Client.ConnectedClients.None(c =>
c.InGame && (c.IsOwner || c.HasPermission(ClientPermissions.ManageCampaign)));
GameMain.Client.ConnectedClients.None(c => c.InGame && (c.IsOwner || c.HasPermission(permissions)));
}
public override void Draw(SpriteBatch spriteBatch)
@@ -545,14 +545,21 @@ namespace Barotrauma
foreach (PurchasedItem pi in CargoManager.ItemsInBuyCrate)
{
msg.Write(pi.ItemPrefab.Identifier);
msg.WriteRangedInteger(pi.Quantity, 0, 100);
msg.WriteRangedInteger(pi.Quantity, 0, CargoManager.MaxQuantity);
}
msg.Write((UInt16)CargoManager.ItemsInSellFromSubCrate.Count);
foreach (PurchasedItem pi in CargoManager.ItemsInSellFromSubCrate)
{
msg.Write(pi.ItemPrefab.Identifier);
msg.WriteRangedInteger(pi.Quantity, 0, CargoManager.MaxQuantity);
}
msg.Write((UInt16)CargoManager.PurchasedItems.Count);
foreach (PurchasedItem pi in CargoManager.PurchasedItems)
{
msg.Write(pi.ItemPrefab.Identifier);
msg.WriteRangedInteger(pi.Quantity, 0, 100);
msg.WriteRangedInteger(pi.Quantity, 0, CargoManager.MaxQuantity);
}
msg.Write((UInt16)CargoManager.SoldItems.Count);
@@ -562,6 +569,7 @@ namespace Barotrauma
msg.Write((UInt16)si.ID);
msg.Write(si.Removed);
msg.Write(si.SellerID);
msg.Write((byte)si.Origin);
}
msg.Write((ushort)UpgradeManager.PurchasedUpgrades.Count);
@@ -640,6 +648,15 @@ namespace Barotrauma
buyCrateItems.Add(new PurchasedItem(ItemPrefab.Prefabs[itemPrefabIdentifier], itemQuantity));
}
UInt16 subSellCrateItemCount = msg.ReadUInt16();
List<PurchasedItem> subSellCrateItems = new List<PurchasedItem>();
for (int i = 0; i < subSellCrateItemCount; i++)
{
string itemPrefabIdentifier = msg.ReadString();
int itemQuantity = msg.ReadRangedInteger(0, CargoManager.MaxQuantity);
subSellCrateItems.Add(new PurchasedItem(ItemPrefab.Prefabs[itemPrefabIdentifier], itemQuantity));
}
UInt16 purchasedItemCount = msg.ReadUInt16();
List<PurchasedItem> purchasedItems = new List<PurchasedItem>();
for (int i = 0; i < purchasedItemCount; i++)
@@ -657,7 +674,8 @@ namespace Barotrauma
UInt16 id = msg.ReadUInt16();
bool removed = msg.ReadBoolean();
byte sellerId = msg.ReadByte();
soldItems.Add(new SoldItem(ItemPrefab.Prefabs[itemPrefabIdentifier], id, removed, sellerId));
byte origin = msg.ReadByte();
soldItems.Add(new SoldItem(ItemPrefab.Prefabs[itemPrefabIdentifier], id, removed, sellerId, (SoldItem.SellOrigin)origin));
}
ushort pendingUpgradeCount = msg.ReadUInt16();
@@ -678,13 +696,9 @@ namespace Barotrauma
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; }
if (!(Entity.FindEntityByID(itemToRemoveID) is Item itemToRemove)) { continue; }
purchasedItemSwaps.Add(new PurchasedItemSwap(itemToRemove, itemToInstall));
}
@@ -728,12 +742,11 @@ namespace Barotrauma
campaign.Map.SelectMission(selectedMissionIndices);
campaign.Map.AllowDebugTeleport = allowDebugTeleport;
campaign.CargoManager.SetItemsInBuyCrate(buyCrateItems);
campaign.CargoManager.SetItemsInSubSellCrate(subSellCrateItems);
campaign.CargoManager.SetPurchasedItems(purchasedItems);
campaign.CargoManager.SetSoldItems(soldItems);
if (storeBalance.HasValue) { campaign.Map.CurrentLocation.StoreCurrentBalance = storeBalance.Value; }
campaign.UpgradeManager.SetPendingUpgrades(pendingUpgrades);
campaign.UpgradeManager.PurchasedUpgrades.Clear();
campaign.UpgradeManager.PurchasedUpgrades.Clear();
foreach (var purchasedItemSwap in purchasedItemSwaps)
{
@@ -1116,6 +1116,7 @@ namespace Barotrauma.Items.Components
foreach (DockingPort dockingPort in DockingPort.List)
{
if (Level.Loaded != null && dockingPort.Item.Submarine.WorldPosition.Y > Level.Loaded.Size.Y) { continue; }
if (dockingPort.Item.HiddenInGame) { continue; }
if (dockingPort.Item.Submarine == null) { continue; }
if (dockingPort.Item.Submarine.Info.IsWreck) { continue; }
// docking ports should be shown even if defined as not, if the submarine is the same as the sonar's
@@ -682,7 +682,7 @@ namespace Barotrauma.Items.Components
enterOutpostPrompt?.Close();
}
}
else if (DockingSources.Any(d => d.Docked))
else if (connectedPorts.Any(d => d.Docked))
{
dockingButton.Text = undockText;
dockingContainer.Visible = true;
@@ -819,7 +819,7 @@ namespace Barotrauma.Items.Components
Connection dockingConnection = item.Connections?.FirstOrDefault(c => c.Name == "toggle_docking");
if (dockingConnection != null)
{
connectedPorts = item.GetConnectedComponentsRecursive<DockingPort>(dockingConnection);
connectedPorts = item.GetConnectedComponentsRecursive<DockingPort>(dockingConnection, ignoreInactiveRelays: true);
}
checkConnectedPortsTimer = CheckConnectedPortsInterval;
}
@@ -199,6 +199,26 @@ namespace Barotrauma.Items.Components
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
snapped = msg.ReadBoolean();
if (!snapped)
{
UInt16 targetId = msg.ReadUInt16();
UInt16 sourceId = msg.ReadUInt16();
byte limbIndex = msg.ReadByte();
Item target = Entity.FindEntityByID(targetId) as Item;
if (target == null) { return; }
var source = Entity.FindEntityByID(sourceId);
if (source is Character sourceCharacter && limbIndex >= 0 && limbIndex < sourceCharacter.AnimController.Limbs.Length)
{
Limb sourceLimb = sourceCharacter.AnimController.Limbs[limbIndex];
Attach(sourceLimb, target);
}
else if (source is ISpatialEntity spatialEntity)
{
Attach(spatialEntity, target);
}
}
}
protected override void RemoveComponentSpecific()
@@ -135,7 +135,13 @@ namespace Barotrauma.Items.Components
wireSprite = overrideSprite ?? defaultWireSprite;
}
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
{
Draw(spriteBatch, editing, Vector2.Zero, itemDepth);
}
public void Draw(SpriteBatch spriteBatch, bool editing, Vector2 offset, float itemDepth = -1)
{
if (sections.Count == 0 && !IsActive || Hidden)
{
@@ -156,6 +162,8 @@ namespace Barotrauma.Items.Components
drawOffset = sub.DrawPosition + sub.HiddenSubPosition;
}
drawOffset += offset;
float baseDepth = UseSpriteDepth ? item.SpriteDepth : wireSprite.Depth;
float depth = item.IsSelected ? 0.0f : SubEditorScreen.IsWiringMode() ? 0.02f : baseDepth + (item.ID % 100) * 0.000001f;// item.GetDrawDepth(wireSprite.Depth, wireSprite);
@@ -2,7 +2,6 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Linq;
namespace Barotrauma
{
@@ -208,9 +207,9 @@ namespace Barotrauma
"watersplash",
(Submarine == null ? pos : pos + Submarine.Position) - Vector2.UnitY * Rand.Range(0.0f, 10.0f),
velocity, 0, flowTargetHull);
if (particle != null)
{
if (particle.CurrentHull == null) { GameMain.ParticleManager.RemoveParticle(particle); }
particle.Size *= Math.Min(Math.Abs(flowForce.X / 500.0f), 5.0f);
}
}
@@ -238,9 +237,9 @@ namespace Barotrauma
}
else
{
if (Math.Sign(flowTargetHull.Rect.Y - rect.Y) != Math.Sign(lerpedFlowForce.Y)) return;
if (Math.Sign(flowTargetHull.Rect.Y - rect.Y) != Math.Sign(lerpedFlowForce.Y)) { return; }
float particlesPerSec = open * rect.Width * 0.3f * particleAmountMultiplier;
float particlesPerSec = Math.Max(open * rect.Width * 0.3f * particleAmountMultiplier, 20.0f);
float emitInterval = 1.0f / particlesPerSec;
while (particleTimer > emitInterval)
{
@@ -252,17 +251,21 @@ namespace Barotrauma
if (flowTargetHull.WaterVolume < flowTargetHull.Volume * 0.95f)
{
var splash = GameMain.ParticleManager.CreateParticle(
"watersplash",
Submarine == null ? pos : pos + Submarine.Position,
velocity, 0, FlowTargetHull);
if (splash != null) splash.Size = splash.Size * MathHelper.Clamp(rect.Width / 50.0f, 0.8f, 4.0f);
"watersplash",
Submarine == null ? pos : pos + Submarine.Position,
velocity, 0, FlowTargetHull);
if (splash != null)
{
if (splash.CurrentHull == null) { GameMain.ParticleManager.RemoveParticle(splash); }
splash.Size *= MathHelper.Clamp(rect.Width / 50.0f, 1.5f, 4.0f);
}
}
if (Math.Abs(flowForce.Y) > 190.0f && Rand.Range(0.0f, 1.0f) < 0.3f && flowTargetHull.WaterVolume > flowTargetHull.Volume * 0.1f)
{
GameMain.ParticleManager.CreateParticle(
"bubbles",
Submarine == null ? pos : pos + Submarine.Position,
flowForce / 2.0f, 0, FlowTargetHull);
"bubbles",
Submarine == null ? pos : pos + Submarine.Position,
flowForce / 2.0f, 0, FlowTargetHull);
}
particleTimer -= emitInterval;
}
@@ -37,6 +37,19 @@ namespace Barotrauma
private double lastAmbientLightEditTime;
private float drawSurface;
public float DrawSurface
{
get { return drawSurface; }
set
{
if (Math.Abs(drawSurface - value) < 0.00001f) { return; }
drawSurface = MathHelper.Clamp(value, rect.Y - rect.Height, rect.Y);
update = true;
}
}
public override bool SelectableInEditor
{
get
@@ -138,8 +151,15 @@ namespace Barotrauma
}
}
partial void UpdateProjSpecific(float deltaTime, Camera cam)
partial void UpdateProjSpecific(float deltaTime, Camera _)
{
float waterDepth = WaterVolume / rect.Width;
//interpolate the position of the rendered surface towards the "target surface"
drawSurface = Math.Max(MathHelper.Lerp(
drawSurface,
rect.Y - rect.Height + waterDepth,
deltaTime * 10.0f), rect.Y - rect.Height);
if (GameMain.Client != null)
{
serverUpdateDelay -= deltaTime;
@@ -171,55 +191,56 @@ namespace Barotrauma
}
}
if (!IdFreed)
{
if (EditWater)
{
Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
if (Submarine.RectContains(WorldRect, position))
{
if (PlayerInput.PrimaryMouseButtonHeld())
{
WaterVolume += 1500.0f;
networkUpdatePending = true;
serverUpdateDelay = 0.5f;
}
else if (PlayerInput.SecondaryMouseButtonHeld())
{
WaterVolume -= 1500.0f;
networkUpdatePending = true;
serverUpdateDelay = 0.5f;
}
}
}
else if (EditFire)
{
Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
if (Submarine.RectContains(WorldRect, position))
{
if (PlayerInput.PrimaryMouseButtonClicked())
{
new FireSource(position, this, isNetworkMessage: true);
networkUpdatePending = true;
serverUpdateDelay = 0.5f;
}
}
}
}
if (waterVolume < 1.0f) { return; }
/*if (waterVolume < 1.0f) { return; }
for (int i = 1; i < waveY.Length - 1; i++)
{
float maxDelta = Math.Max(Math.Abs(rightDelta[i]), Math.Abs(leftDelta[i]));
if (maxDelta > 1.0f && maxDelta > Rand.Range(1.0f, 10.0f))
if (maxDelta > 0.1f && maxDelta > Rand.Range(0.1f, 10.0f))
{
var particlePos = new Vector2(rect.X + WaveWidth * i, surface + waveY[i]);
if (Submarine != null) particlePos += Submarine.Position;
if (Submarine != null) { particlePos += Submarine.Position; }
GameMain.ParticleManager.CreateParticle("mist",
particlePos,
new Vector2(0.0f, -50.0f), 0.0f, this);
}
}*/
}
public static void UpdateCheats(float deltaTime, Camera cam)
{
bool primaryMouseButtonHeld = PlayerInput.PrimaryMouseButtonHeld();
bool secondaryMouseButtonHeld = PlayerInput.SecondaryMouseButtonHeld();
if (!primaryMouseButtonHeld && !secondaryMouseButtonHeld) { return; }
Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
Hull hull = FindHull(position);
if (hull == null || hull.IdFreed) { return; }
if (EditWater)
{
if (primaryMouseButtonHeld)
{
hull.WaterVolume += 100000.0f * deltaTime;
hull.networkUpdatePending = true;
hull.serverUpdateDelay = 0.5f;
}
else if (secondaryMouseButtonHeld)
{
hull.WaterVolume -= 100000.0f * deltaTime;
hull.networkUpdatePending = true;
hull.serverUpdateDelay = 0.5f;
}
}
else if (EditFire)
{
if (primaryMouseButtonHeld)
{
new FireSource(position, hull, isNetworkMessage: true);
hull.networkUpdatePending = true;
hull.serverUpdateDelay = 0.5f;
}
}
}
@@ -194,7 +194,11 @@ namespace Barotrauma
private void RemoveFogOfWar(Location location, bool removeFromAdjacentLocations = true)
{
if (location == null) { return; }
Vector2 mapTileSize = mapTiles[0, 0].size * generationParams.MapTileScale;
var mapTile = generationParams.MapTiles.Values.FirstOrDefault()?.FirstOrDefault();
if (mapTile == null) { return; }
Vector2 mapTileSize = mapTile.size * generationParams.MapTileScale;
int startX = (int)Math.Max(Math.Floor(location.MapPosition.X / mapTileSize.X - 0.25f), 0);
int startY = (int)Math.Max(Math.Floor(location.MapPosition.Y / mapTileSize.Y - 0.25f), 0);
int endX = (int)Math.Min(Math.Floor(location.MapPosition.X / mapTileSize.X + 0.25f), mapTiles.GetLength(0));
@@ -768,34 +768,40 @@ namespace Barotrauma
switch (e)
{
case Item item:
{
if (item.FlippedX && item.Prefab.CanSpriteFlipX) spriteEffects ^= SpriteEffects.FlipHorizontally;
if (item.flippedY && item.Prefab.CanSpriteFlipY) spriteEffects ^= SpriteEffects.FlipVertically;
break;
}
{
if (item.FlippedX && item.Prefab.CanSpriteFlipX) { spriteEffects ^= SpriteEffects.FlipHorizontally; }
if (item.flippedY && item.Prefab.CanSpriteFlipY) { spriteEffects ^= SpriteEffects.FlipVertically; }
var wire = item.GetComponent<Wire>();
if (wire != null && !wire.Item.body.Enabled)
{
wire.Draw(spriteBatch, editing: false, new Vector2(moveAmount.X, -moveAmount.Y));
continue;
}
break;
}
case Structure structure:
{
if (structure.FlippedX && structure.Prefab.CanSpriteFlipX) spriteEffects ^= SpriteEffects.FlipHorizontally;
if (structure.flippedY && structure.Prefab.CanSpriteFlipY) spriteEffects ^= SpriteEffects.FlipVertically;
break;
}
{
if (structure.FlippedX && structure.Prefab.CanSpriteFlipX) { spriteEffects ^= SpriteEffects.FlipHorizontally; }
if (structure.flippedY && structure.Prefab.CanSpriteFlipY) { spriteEffects ^= SpriteEffects.FlipVertically; }
break;
}
case WayPoint wayPoint:
{
Vector2 drawPos = e.WorldPosition;
drawPos.Y = -drawPos.Y;
drawPos += moveAmount;
wayPoint.Draw(spriteBatch, drawPos);
continue;
}
{
Vector2 drawPos = e.WorldPosition;
drawPos.Y = -drawPos.Y;
drawPos += moveAmount;
wayPoint.Draw(spriteBatch, drawPos);
continue;
}
case LinkedSubmarine linkedSub:
{
var ma = moveAmount;
ma.Y = -ma.Y;
Vector2 lPos = linkedSub.Position;
lPos += ma;
linkedSub.Draw(spriteBatch, lPos, alpha: 0.5f);
break;
}
{
var ma = moveAmount;
ma.Y = -ma.Y;
Vector2 lPos = linkedSub.Position;
lPos += ma;
linkedSub.Draw(spriteBatch, lPos, alpha: 0.5f);
break;
}
}
e.prefab?.DrawPlacing(spriteBatch,
new Rectangle(e.WorldRect.Location + new Point((int)moveAmount.X, (int)-moveAmount.Y), e.WorldRect.Size), e.Scale, spriteEffects);
@@ -1,6 +1,6 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace Barotrauma
@@ -91,7 +91,7 @@ namespace Barotrauma
public void CreateSpecsWindow(GUIListBox parent, ScalableFont font, bool includeTitle = true, bool includeClass = true, bool includeDescription = false)
{
float leftPanelWidth = 0.6f;
float rightPanelWidth = 0.4f;
float rightPanelWidth = 0.4f / leftPanelWidth;
string className = !HasTag(SubmarineTag.Shuttle) ? TextManager.Get($"submarineclass.{SubmarineClass}") : TextManager.Get("shuttle");
int classHeight = (int)GUI.SubHeadingFont.MeasureString(className).Y;
@@ -110,6 +110,17 @@ namespace Barotrauma
submarineClassText = new GUITextBlock(new RectTransform(new Point(leftPanelWidthInt, classHeight), parent.Content.RectTransform), className, textAlignment: Alignment.CenterLeft, font: GUI.SubHeadingFont) { CanBeFocused = false };
submarineClassText.RectTransform.MinSize = new Point(0, (int)submarineClassText.TextSize.Y);
}
if (Price > 0)
{
var priceText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), parent.Content.RectTransform),
TextManager.Get("subeditor.price"), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), priceText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", Price)), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
}
Vector2 realWorldDimensions = Dimensions * Physics.DisplayToRealWorldRatio;
if (realWorldDimensions != Vector2.Zero)
{
@@ -2870,19 +2870,16 @@ namespace Barotrauma.Networking
public void SendCampaignState()
{
MultiPlayerCampaign campaign = GameMain.GameSession.GameMode as MultiPlayerCampaign;
if (campaign == null)
if (!(GameMain.GameSession.GameMode is MultiPlayerCampaign campaign))
{
DebugConsole.ThrowError("Failed send campaign state to the server (no campaign active).\n" + Environment.StackTrace.CleanupStackTrace());
return;
}
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ClientPacketHeader.SERVER_COMMAND);
msg.Write((UInt16)ClientPermissions.ManageCampaign);
campaign.ClientWrite(msg);
msg.Write((byte)ServerNetObject.END_OF_MESSAGE);
clientPeer.Send(msg, DeliveryMethod.Reliable);
}
@@ -196,6 +196,19 @@ namespace Barotrauma.Particles
particles[particleCount] = swap;
}
public void RemoveParticle(Particle particle)
{
for (int i = 0; i < particleCount; i++)
{
if (particles[i] == particle)
{
RemoveParticle(i);
return;
}
}
}
public void Update(float deltaTime)
{
MaxParticles = GameMain.Config.ParticleLimit;
@@ -244,7 +244,11 @@ namespace Barotrauma
foreach (string saveFile in saveFiles)
{
if (string.IsNullOrEmpty(saveFile)) { continue; }
if (string.IsNullOrEmpty(saveFile))
{
DebugConsole.AddWarning("Error when updating campaign load menu: path to a save file was empty.\n" + Environment.StackTrace);
continue;
}
string fileName = saveFile;
string subName = "";
@@ -500,29 +500,34 @@ namespace Barotrauma.CharacterEditor
int index = 0;
bool isSwimming = character.AnimController.ForceSelectAnimationType == AnimationType.SwimFast || character.AnimController.ForceSelectAnimationType == AnimationType.SwimSlow;
bool isMovingFast = character.AnimController.ForceSelectAnimationType == AnimationType.Run || character.AnimController.ForceSelectAnimationType == AnimationType.SwimFast;
if (isMovingFast)
if (character.AnimController.CanWalk)
{
if (isSwimming || !character.AnimController.CanWalk)
if (isMovingFast)
{
index = !character.AnimController.CanWalk ? (int)AnimationType.SwimFast : (int)AnimationType.SwimSlow;
if (isSwimming)
{
index = 2;
}
else
{
index = 0;
}
}
else
{
index = (int)AnimationType.Walk;
if (isSwimming)
{
index = 3;
}
else
{
index = 1;
}
}
index -= 1;
}
else
{
if (isSwimming || !character.AnimController.CanWalk)
{
index = !character.AnimController.CanWalk ? (int)AnimationType.SwimSlow : (int)AnimationType.SwimFast;
}
else
{
index = (int)AnimationType.Run;
}
index -= 1;
index = isMovingFast ? 0 : 1;
}
if (animSelection.SelectedIndex != index)
{
@@ -536,17 +541,13 @@ namespace Barotrauma.CharacterEditor
bool isSwimming = character.AnimController.ForceSelectAnimationType == AnimationType.SwimFast || character.AnimController.ForceSelectAnimationType == AnimationType.SwimSlow;
if (isSwimming)
{
animSelection.Select((int)AnimationType.Walk - 1);
animSelection.Select(0);
}
else
{
animSelection.Select((int)AnimationType.SwimSlow - 1);
animSelection.Select(2);
}
}
if (PlayerInput.KeyHit(Keys.F))
{
SetToggle(freezeToggle, !freezeToggle.Selected);
}
if (PlayerInput.SecondaryMouseButtonClicked() || PlayerInput.KeyHit(Keys.Escape))
{
bool reset = false;
@@ -853,6 +854,16 @@ namespace Barotrauma.CharacterEditor
{
DrawRagdoll(spriteBatch, (float)deltaTime);
}
// Mouth
Limb head = character.AnimController.GetLimb(LimbType.Head);
if (head != null && character.CanEat && selectedLimbs.Contains(head))
{
var mouthPos = character.AnimController.GetMouthPosition();
if (mouthPos.HasValue)
{
ShapeExtensions.DrawPoint(spriteBatch, SimToScreen(mouthPos.Value), GUI.Style.Red, size: 8);
}
}
if (showSpritesheet)
{
DrawSpritesheetEditor(spriteBatch, (float)deltaTime);
@@ -2606,13 +2617,13 @@ namespace Barotrauma.CharacterEditor
{
animSelection.AddItem(AnimationType.Walk.ToString(), AnimationType.Walk);
animSelection.AddItem(AnimationType.Run.ToString(), AnimationType.Run);
if (character.IsHumanoid)
{
animSelection.AddItem(AnimationType.Crouch.ToString(), AnimationType.Crouch);
}
}
animSelection.AddItem(AnimationType.SwimSlow.ToString(), AnimationType.SwimSlow);
animSelection.AddItem(AnimationType.SwimFast.ToString(), AnimationType.SwimFast);
if (character.AnimController.CanWalk && character.IsHumanoid)
{
animSelection.AddItem(AnimationType.Crouch.ToString(), AnimationType.Crouch);
}
if (character.AnimController.ForceSelectAnimationType == AnimationType.NotDefined)
{
animSelection.SelectItem(character.AnimController.CanWalk ? AnimationType.Walk : AnimationType.SwimSlow);
@@ -717,7 +717,7 @@ namespace Barotrauma
}
#endregion
public void QuickStart(bool fixedSeed = false, string sub = null, float difficulty = 40, LevelGenerationParams levelGenerationParams = null)
public void QuickStart(bool fixedSeed = false, string sub = null, float difficulty = 50, LevelGenerationParams levelGenerationParams = null)
{
if (fixedSeed)
{
@@ -1396,7 +1396,7 @@ namespace Barotrauma
return false;
}
if (ForbiddenWordFilter.IsForbidden(name, out string forbiddenWord))
if (isPublicBox.Selected && ForbiddenWordFilter.IsForbidden(name, out string forbiddenWord))
{
var msgBox = new GUIMessageBox("",
TextManager.GetWithVariables("forbiddenservernameverification", new string[] { "[forbiddenword]", "[servername]" }, new string[] { forbiddenWord, name }),