diff --git a/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterNetworking.cs b/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterNetworking.cs index c78789db0..49a1eb6d8 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterNetworking.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterNetworking.cs @@ -685,6 +685,7 @@ namespace Barotrauma causeOfDeathAffliction = AfflictionPrefab.Prefabs[afflictionName]; } } + bool containsAfflictionData = msg.ReadBoolean(); if (!IsDead) { if (causeOfDeathType == CauseOfDeathType.Pressure || causeOfDeathAffliction == AfflictionPrefab.Pressure) @@ -695,6 +696,11 @@ namespace Barotrauma { Kill(causeOfDeathType, causeOfDeathAffliction?.Instantiate(1.0f), true); } + } + if (containsAfflictionData) + { + CharacterHealth.ClientRead(msg); + CharacterHealth.ForceUpdateVisuals(); } } else diff --git a/Barotrauma/BarotraumaClient/ClientSource/Characters/Health/CharacterHealth.cs b/Barotrauma/BarotraumaClient/ClientSource/Characters/Health/CharacterHealth.cs index 5c563220d..48e87f489 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Characters/Health/CharacterHealth.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Characters/Health/CharacterHealth.cs @@ -2011,7 +2011,6 @@ namespace Barotrauma partial void UpdateSkinTint() { - if (!Character.IsVisible) { return; } FaceTint = DefaultFaceTint; BodyTint = Color.TransparentBlack; @@ -2029,7 +2028,6 @@ namespace Barotrauma partial void UpdateLimbAfflictionOverlays() { - if (!Character.IsVisible) { return; } foreach (Limb limb in Character.AnimController.Limbs) { if (limb.HealthIndex < 0 || limb.HealthIndex >= limbHealths.Count) { continue; } diff --git a/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs b/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs index b8cff8d92..1bee53b41 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs @@ -1750,9 +1750,9 @@ namespace Barotrauma addIfMissing($"missionfailure.{missionId}".ToIdentifier(), language); } } - for (int i = 0; i UpdateLocationView(newLocation, true, prevLocation); + campaignUI.Campaign.Map.OnLocationChanged.RegisterOverwriteExisting( + "CrewManagement.UpdateLocationView".ToIdentifier(), + (locationChangeInfo) => UpdateLocationView(locationChangeInfo.NewLocation, true, locationChangeInfo.PrevLocation)); } public void RefreshPermissions() diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIDropDown.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIDropDown.cs index f8e908d8d..59510e874 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIDropDown.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIDropDown.cs @@ -20,7 +20,7 @@ namespace Barotrauma private RectTransform currentHighestParent; private List parentHierarchy = new List(); - private bool selectMultiple; + private readonly bool selectMultiple; public bool Dropped { get; set; } @@ -129,18 +129,20 @@ namespace Barotrauma } } - private List selectedDataMultiple = new List(); + private readonly List selectedDataMultiple = new List(); public IEnumerable SelectedDataMultiple { get { return selectedDataMultiple; } } - private List selectedIndexMultiple = new List(); + private readonly List selectedIndexMultiple = new List(); public IEnumerable SelectedIndexMultiple { get { return selectedIndexMultiple; } } + public bool MustSelectAtLeastOne; + public LocalizedString Text { get { return button.Text; } @@ -269,6 +271,12 @@ namespace Barotrauma ToolTip = toolTip, OnSelected = (GUITickBox tb) => { + if (MustSelectAtLeastOne && selectedIndexMultiple.Count <= 1 && !tb.Selected) + { + tb.Selected = true; + return false; + } + List texts = new List(); selectedDataMultiple.Clear(); selectedIndexMultiple.Clear(); diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/Store.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/Store.cs index f27eed481..f32e5de6e 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/Store.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/Store.cs @@ -188,21 +188,23 @@ namespace Barotrauma this.parentComponent = parentComponent; UpdatePermissions(); CreateUI(); - campaignUI.Campaign.Map.OnLocationChanged += UpdateLocation; - if (CurrentLocation?.Reputation != null) - { - CurrentLocation.Reputation.OnReputationValueChanged += () => { needsRefresh = true; }; - } - campaignUI.Campaign.CargoManager.OnItemsInBuyCrateChanged += () => { needsBuyingRefresh = true; }; - campaignUI.Campaign.CargoManager.OnPurchasedItemsChanged += () => { needsRefresh = true; }; - campaignUI.Campaign.CargoManager.OnItemsInSellCrateChanged += () => { needsSellingRefresh = true; }; - campaignUI.Campaign.CargoManager.OnSoldItemsChanged += () => + Identifier refreshStoreId = new Identifier("RefreshStore"); + campaignUI.Campaign.Map.OnLocationChanged.RegisterOverwriteExisting( + refreshStoreId, + (locationChangeInfo) => UpdateLocation(locationChangeInfo.PrevLocation, locationChangeInfo.NewLocation)); + + CurrentLocation?.Reputation?.OnReputationValueChanged.RegisterOverwriteExisting(refreshStoreId, _ => needsRefresh = true); + CargoManager cargoManager = campaignUI.Campaign.CargoManager; + cargoManager.OnItemsInBuyCrateChanged.RegisterOverwriteExisting(refreshStoreId, _ => needsBuyingRefresh = true); + cargoManager.OnPurchasedItemsChanged.RegisterOverwriteExisting(refreshStoreId, _ => needsRefresh = true); + cargoManager.OnItemsInSellCrateChanged.RegisterOverwriteExisting(refreshStoreId, _ => needsSellingRefresh = true); + cargoManager.OnSoldItemsChanged.RegisterOverwriteExisting(refreshStoreId, _ => { needsItemsToSellRefresh = true; needsItemsToSellFromSubRefresh = true; needsRefresh = true; - }; - campaignUI.Campaign.CargoManager.OnItemsInSellFromSubCrateChanged += () => { needsSellingFromSubRefresh = true; }; + }); + cargoManager.OnItemsInSellFromSubCrateChanged.RegisterOverwriteExisting(refreshStoreId, _ => needsSellingFromSubRefresh = true); } public void SelectStore(Identifier identifier) @@ -713,7 +715,7 @@ namespace Barotrauma if (prevLocation == newLocation) { return; } if (prevLocation?.Reputation != null) { - prevLocation.Reputation.OnReputationValueChanged -= SetNeedsRefresh; + prevLocation.Reputation.OnReputationValueChanged.Dispose(); } if (ItemPrefab.Prefabs.Any(p => p.CanBeBoughtFrom(newLocation))) { @@ -722,7 +724,7 @@ namespace Barotrauma ChangeStoreTab(StoreTab.Buy); if (newLocation?.Reputation != null) { - newLocation.Reputation.OnReputationValueChanged += SetNeedsRefresh; + CurrentLocation.Reputation.OnReputationValueChanged.RegisterOverwriteExisting("RefreshStore".ToIdentifier(), _ => { SetNeedsRefresh(); }); } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/SubmarineSelection.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/SubmarineSelection.cs index 28de3502a..47d184dd0 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/SubmarineSelection.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/SubmarineSelection.cs @@ -4,7 +4,6 @@ using Microsoft.Xna.Framework; using System.Linq; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; -using System.Globalization; using PlayerBalanceElement = Barotrauma.CampaignUI.PlayerBalanceElement; namespace Barotrauma @@ -30,12 +29,12 @@ namespace Barotrauma private int selectionIndicatorThickness; private GUIImage listBackground; private GUITickBox transferItemsTickBox; - private GUITextBlock itemTransferReminderBlock; + private GUITextBlock itemTransferInfoBlock; private readonly List subsToShow; private readonly SubmarineDisplayContent[] submarineDisplays = new SubmarineDisplayContent[submarinesPerPage]; private SubmarineInfo selectedSubmarine = null; - private LocalizedString purchaseAndSwitchText, purchaseOnlyText, deliveryText, currentSubText, switchText, missingPreviewText, currencyName; + private LocalizedString purchaseAndSwitchText, purchaseOnlyText, deliveryText, selectedSubText, switchText, missingPreviewText, currencyName; private readonly RectTransform parent; private readonly Action closeAction; private Sprite pageIndicator; @@ -107,7 +106,7 @@ namespace Barotrauma private void Initialize() { initialized = true; - currentSubText = TextManager.Get("currentsub"); + selectedSubText = TextManager.Get("selectedsub"); deliveryText = TextManager.Get("requestdeliverybutton"); switchText = TextManager.Get("switchtosubmarinebutton"); purchaseAndSwitchText = TextManager.Get("purchaseandswitch"); @@ -203,7 +202,7 @@ namespace Barotrauma OnSelected = (tb) => transferItemsOnSwitch = tb.Selected }; transferItemsTickBox.RectTransform.Resize(new Point(Math.Min((int)transferItemsTickBox.ContentWidth, transferInfoFrame.Rect.Width), transferItemsTickBox.Rect.Height)); - itemTransferReminderBlock = new GUITextBlock(new RectTransform(Vector2.One, transferInfoFrame.RectTransform, Anchor.CenterRight), null) + itemTransferInfoBlock = new GUITextBlock(new RectTransform(Vector2.One, transferInfoFrame.RectTransform, Anchor.CenterRight), null, wrap: true) { TextAlignment = Alignment.CenterRight, Visible = false @@ -412,7 +411,7 @@ namespace Barotrauma } else { - submarineDisplays[i].submarineFee.Text = currentSubText; + submarineDisplays[i].submarineFee.Text = selectedSubText; } } @@ -474,7 +473,10 @@ namespace Barotrauma subsToShow.Sort((x, y) => x.SubmarineClass.CompareTo(y.SubmarineClass)); } - if (transferService) SetConfirmButtonState(selectedSubmarine != null && selectedSubmarine.Name != CurrentOrPendingSubmarine().Name); + if (transferService) + { + SetConfirmButtonState(selectedSubmarine != null && selectedSubmarine.Name != CurrentOrPendingSubmarine().Name); + } subsToShow.Sort((x, y) => x.SubmarineClass.CompareTo(y.SubmarineClass)); pageCount = Math.Max(1, (int)Math.Ceiling(subsToShow.Count / (float)submarinesPerPage)); @@ -607,35 +609,36 @@ namespace Barotrauma UpdateItemTransferInfoFrame(); } + private bool IsSelectedSubCurrentSub => Submarine.MainSub?.Info?.Name == selectedSubmarine?.Name; + private void UpdateItemTransferInfoFrame() { if (selectedSubmarine != null) { - var pendingSub = GameMain.GameSession?.Campaign?.PendingSubmarineSwitch; - if (Submarine.MainSub?.Info?.Name == selectedSubmarine.Name && pendingSub == null) + if (IsSelectedSubCurrentSub) { + TransferItemsOnSwitch = false; transferItemsTickBox.Visible = false; - itemTransferReminderBlock.Visible = false; + itemTransferInfoBlock.Visible = confirmButton.Enabled; + itemTransferInfoBlock.Text = TextManager.Get("switchingbacktocurrentsub"); } - else if (pendingSub?.Name == selectedSubmarine.Name) + else if (GameMain.GameSession?.Campaign?.PendingSubmarineSwitch?.Name == selectedSubmarine.Name) { transferItemsTickBox.Visible = false; - itemTransferReminderBlock.Text = GameMain.GameSession.Campaign.TransferItemsOnSubSwitch ? - TextManager.Get("itemtransferenabledreminder") : - TextManager.Get("itemtransferdisabledreminder"); - itemTransferReminderBlock.Visible = true; + itemTransferInfoBlock.Visible = true; + itemTransferInfoBlock.Text = GameMain.GameSession.Campaign.TransferItemsOnSubSwitch ? TextManager.Get("itemtransferenabledreminder") : TextManager.Get("itemtransferdisabledreminder"); } else { transferItemsTickBox.Selected = TransferItemsOnSwitch; transferItemsTickBox.Visible = true; - itemTransferReminderBlock.Visible = false; + itemTransferInfoBlock.Visible = false; } } else { transferItemsTickBox.Visible = false; - itemTransferReminderBlock.Visible = false; + itemTransferInfoBlock.Visible = false; } } @@ -710,6 +713,45 @@ namespace Barotrauma } msgBox.Buttons[0].OnClicked = (applyButton, obj) => + { + if (!TransferItemsOnSwitch && !IsSelectedSubCurrentSub) + { + if (selectedSubmarine.NoItems) + { + return ShowConfirmationPopup(TextManager.Get("noitemsheader"), TextManager.Get("noitemswarning")); + } + if (!GameMain.GameSession.IsSubmarineOwned(selectedSubmarine) && !selectedSubmarine.IsManuallyOutfitted) + { + var (header, body) = GetItemTransferWarningText(); + return ShowConfirmationPopup(header, body); + } + if (selectedSubmarine.LowFuel) + { + return ShowConfirmationPopup(TextManager.Get("lowfuelheader"), TextManager.Get("lowfuelwarning")); + } + } + return Confirm(); + }; + msgBox.Buttons[0].OnClicked += msgBox.Close; + msgBox.Buttons[1].OnClicked = msgBox.Close; + + bool ShowConfirmationPopup(LocalizedString header, LocalizedString textBody) + { + msgBox.Close(); + var extraConfirmationBox = new GUIMessageBox(header, textBody, new LocalizedString[2] { TextManager.Get("ok"), TextManager.Get("cancel") }); + extraConfirmationBox.Buttons[0].OnClicked = (b, o) => Confirm(); + extraConfirmationBox.Buttons[1].OnClicked = (b, o) => + { + selectedSubmarine = CurrentOrPendingSubmarine(); + RefreshSubmarineDisplay(true); + return true; + }; + extraConfirmationBox.Buttons[0].OnClicked += extraConfirmationBox.Close; + extraConfirmationBox.Buttons[1].OnClicked += extraConfirmationBox.Close; + return true; + } + + bool Confirm() { if (GameMain.Client == null) { @@ -721,9 +763,14 @@ namespace Barotrauma GameMain.Client.InitiateSubmarineChange(selectedSubmarine, TransferItemsOnSwitch, Networking.VoteType.SwitchSub); } return true; - }; - msgBox.Buttons[0].OnClicked += msgBox.Close; - msgBox.Buttons[1].OnClicked = msgBox.Close; + } + } + + private (LocalizedString header, LocalizedString body) GetItemTransferWarningText() + { + var header = TextManager.Get("itemtransferheader").Fallback("lowfuelheader"); + var body = TextManager.Get("itemtransferwarning").Fallback("lowfuelwarning"); + return (header, body); } private void ShowBuyPrompt(bool purchaseOnly) @@ -737,7 +784,6 @@ namespace Barotrauma } GUIMessageBox msgBox; - if (!purchaseOnly) { var text = TextManager.GetWithVariables("purchaseandswitchsubmarinetext", @@ -749,6 +795,39 @@ namespace Barotrauma msgBox = new GUIMessageBox(TextManager.Get("purchaseandswitchsubmarineheader"), text, messageBoxOptions); msgBox.Buttons[0].OnClicked = (applyButton, obj) => + { + if (!TransferItemsOnSwitch && !IsSelectedSubCurrentSub) + { + if (selectedSubmarine.NoItems) + { + ShowConfirmationPopup(TextManager.Get("noitemsheader"), TextManager.Get("noitemswarning")); + return false; + } + if (!GameMain.GameSession.IsSubmarineOwned(selectedSubmarine) && !selectedSubmarine.IsManuallyOutfitted) + { + var (header, body) = GetItemTransferWarningText(); + ShowConfirmationPopup(header, body); + return false; + } + if (selectedSubmarine.LowFuel) + { + ShowConfirmationPopup(TextManager.Get("lowfuelheader"), TextManager.Get("lowfuelwarning")); + return false; + } + } + return Confirm(); + }; + + void ShowConfirmationPopup(LocalizedString header, LocalizedString textBody) + { + msgBox.Close(); + var extraConfirmationBox = new GUIMessageBox(header, textBody, new LocalizedString[2] { TextManager.Get("ok"), TextManager.Get("cancel") }); + extraConfirmationBox.Buttons[0].OnClicked = (b, o) => Confirm(); + extraConfirmationBox.Buttons[0].OnClicked += extraConfirmationBox.Close; + extraConfirmationBox.Buttons[1].OnClicked += extraConfirmationBox.Close; + } + + bool Confirm() { if (GameMain.Client == null) { @@ -761,7 +840,7 @@ namespace Barotrauma GameMain.Client.InitiateSubmarineChange(selectedSubmarine, TransferItemsOnSwitch, Networking.VoteType.PurchaseAndSwitchSub); } return true; - }; + } } else { @@ -792,7 +871,13 @@ namespace Barotrauma private LocalizedString GetItemTransferText() { - return "\n\n" + TextManager.Get(TransferItemsOnSwitch ? "itemswillbetransferred" : "itemswontbetransferred"); + if (Submarine.MainSub?.Info?.Name == selectedSubmarine.Name) + { + return $"\n\n{TextManager.Get("switchingbacktocurrentsub")}"; + } + var s = "\n\n" + TextManager.Get(TransferItemsOnSwitch ? "itemswillbetransferred" : "itemswontbetransferred"); + s += $" {TextManager.Get("toggleitemtransferprompt")}"; + return s; } } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/TabMenu.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/TabMenu.cs index ecdb03592..6bd80ccc1 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/TabMenu.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/TabMenu.cs @@ -1885,6 +1885,7 @@ namespace Barotrauma { IgnoreLayoutGroups = true }; + newCharacterBox.TextBlock.AutoScaleHorizontal = true; newCharacterBox.OnClicked = (button, o) => { diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/UpgradeStore.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/UpgradeStore.cs index 6833b74d3..4d5f3f806 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/UpgradeStore.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/UpgradeStore.cs @@ -107,10 +107,11 @@ namespace Barotrauma CreateUI(upgradeFrame); if (Campaign == null) { return; } - Campaign.UpgradeManager.OnUpgradesChanged += RequestRefresh; - Campaign.CargoManager.OnPurchasedItemsChanged += RequestRefresh; - Campaign.CargoManager.OnSoldItemsChanged += RequestRefresh; - Campaign.OnMoneyChanged.RegisterOverwriteExisting(nameof(UpgradeStore).ToIdentifier(), e => { RequestRefresh(); } ); + Identifier eventId = new Identifier(nameof(UpgradeStore)); + Campaign.UpgradeManager.OnUpgradesChanged?.RegisterOverwriteExisting(eventId, _ => RequestRefresh()); + Campaign.CargoManager.OnPurchasedItemsChanged.RegisterOverwriteExisting(eventId, _ => RequestRefresh()); + Campaign.CargoManager.OnSoldItemsChanged.RegisterOverwriteExisting(eventId, _ => RequestRefresh()); + Campaign.OnMoneyChanged.RegisterOverwriteExisting(eventId, _ => RequestRefresh()); } public void RequestRefresh() diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs b/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs index 6a926c4da..f3908eb62 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs @@ -1109,27 +1109,6 @@ namespace Barotrauma GUIMessageBox.CloseAll(); MainMenuScreen.Select(); GameSession = null; - - } - - public void ShowCampaignDisclaimer(Action onContinue = null) - { - var msgBox = new GUIMessageBox(TextManager.Get("CampaignDisclaimerTitle"), TextManager.Get("CampaignDisclaimerText"), - new LocalizedString[] { TextManager.Get("CampaignRoadMapTitle"), TextManager.Get("OK") }); - - msgBox.Buttons[0].OnClicked = (btn, userdata) => - { - ShowOpenUrlInWebBrowserPrompt("https://trello.com/b/hBXI8ltN/barotrauma-roadmap-known-issues"); - return true; - }; - msgBox.Buttons[0].OnClicked += msgBox.Close; - msgBox.Buttons[1].OnClicked += msgBox.Close; - msgBox.Buttons[1].OnClicked += (_, __) => { onContinue?.Invoke(); return true; }; - - var config = GameSettings.CurrentConfig; - config.CampaignDisclaimerShown = true; - GameSettings.SetCurrentConfig(config); - GameSettings.SaveCurrentConfig(); } public void ShowEditorDisclaimer() diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSession/CargoManager.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSession/CargoManager.cs index bbbd12105..07ed155ff 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameSession/CargoManager.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameSession/CargoManager.cs @@ -52,7 +52,7 @@ namespace Barotrauma { ItemsInBuyCrate.Add(entry.Key, entry.Value); } - OnItemsInBuyCrateChanged?.Invoke(); + OnItemsInBuyCrateChanged?.Invoke(this); } public void SetItemsInSubSellCrate(Dictionary> items) @@ -62,7 +62,7 @@ namespace Barotrauma { ItemsInSellFromSubCrate.Add(entry.Key, entry.Value); } - OnItemsInSellFromSubCrateChanged?.Invoke(); + OnItemsInSellFromSubCrateChanged?.Invoke(this); } public void SetSoldItems(Dictionary> items) @@ -97,7 +97,7 @@ namespace Barotrauma soldEntityMatch.Status = SoldEntity.SellStatus.Confirmed; } } - OnSoldItemsChanged?.Invoke(); + OnSoldItemsChanged?.Invoke(this); static bool Match(SoldItem soldItem, SoldEntity soldEntity, bool matchId) { @@ -122,7 +122,7 @@ namespace Barotrauma { GetSellCrateItems(storeIdentifier, create: true).Add(new PurchasedItem(itemPrefab, changeInQuantity)); } - OnItemsInSellCrateChanged?.Invoke(); + OnItemsInSellCrateChanged?.Invoke(this); } public void SellItems(Identifier storeIdentifier, List itemsToSell, Store.StoreTab sellingMode) @@ -199,7 +199,7 @@ namespace Barotrauma } } } - OnSoldItemsChanged?.Invoke(); + OnSoldItemsChanged?.Invoke(this); } public void ClearSoldItemsProjSpecific() diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/CampaignMode.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/CampaignMode.cs index 4f9411739..54d942d3b 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/CampaignMode.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/CampaignMode.cs @@ -326,6 +326,48 @@ namespace Barotrauma ReadyCheckButton?.AddToGUIUpdateList(); } + protected void TryEndRoundWithFuelCheck(Action onConfirm, Action onReturnToMapScreen) + { + Submarine.MainSub.CheckFuel(); + SubmarineInfo nextSub = PendingSubmarineSwitch ?? Submarine.MainSub.Info; + bool lowFuel = nextSub.Name == Submarine.MainSub.Info.Name ? Submarine.MainSub.Info.LowFuel : nextSub.LowFuel; + if (Level.IsLoadedFriendlyOutpost && lowFuel && CargoManager.PurchasedItems.None(i => i.Value.Any(pi => pi.ItemPrefab.Tags.Contains("reactorfuel")))) + { + var extraConfirmationBox = + new GUIMessageBox(TextManager.Get("lowfuelheader"), + TextManager.Get("lowfuelwarning"), + new LocalizedString[2] { TextManager.Get("ok"), TextManager.Get("cancel") }); + extraConfirmationBox.Buttons[0].OnClicked = (b, o) => { Confirm(); return true; }; + extraConfirmationBox.Buttons[0].OnClicked += extraConfirmationBox.Close; + extraConfirmationBox.Buttons[1].OnClicked = extraConfirmationBox.Close; + } + else + { + Confirm(); + } + + void Confirm() + { + var availableTransition = GetAvailableTransition(out _, out _); + if (Character.Controlled != null && + availableTransition == TransitionType.ReturnToPreviousLocation && + Character.Controlled?.Submarine == Level.Loaded?.StartOutpost) + { + onConfirm(); + } + else if (Character.Controlled != null && + availableTransition == TransitionType.ProgressToNextLocation && + Character.Controlled?.Submarine == Level.Loaded?.EndOutpost) + { + onConfirm(); + } + else + { + onReturnToMapScreen(); + } + } + } + public override void Update(float deltaTime) { base.Update(deltaTime); diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/MultiPlayerCampaign.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/MultiPlayerCampaign.cs index 8fc431ad8..c7d279a2d 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/MultiPlayerCampaign.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/MultiPlayerCampaign.cs @@ -137,25 +137,14 @@ namespace Barotrauma }, OnClicked = (btn, userdata) => { - var availableTransition = GetAvailableTransition(out _, out _); - if (Character.Controlled != null && - availableTransition == TransitionType.ReturnToPreviousLocation && - Character.Controlled?.Submarine == Level.Loaded?.StartOutpost) - { - GameMain.Client.RequestStartRound(); - } - else if (Character.Controlled != null && - availableTransition == TransitionType.ProgressToNextLocation && - Character.Controlled?.Submarine == Level.Loaded?.EndOutpost) - { - GameMain.Client.RequestStartRound(); - } - else - { - ShowCampaignUI = true; - if (CampaignUI == null) { InitCampaignUI(); } - CampaignUI.SelectTab(InteractionType.Map); - } + TryEndRoundWithFuelCheck( + onConfirm: () => GameMain.Client.RequestStartRound(), + onReturnToMapScreen: () => + { + ShowCampaignUI = true; + if (CampaignUI == null) { InitCampaignUI(); } + CampaignUI.SelectTab(InteractionType.Map); + }); return true; } }; @@ -704,13 +693,15 @@ namespace Barotrauma if (ShouldApply(NetFlags.SubList, id, requireUpToDateSave: false)) { - GameMain.GameSession.OwnedSubmarines.Clear(); foreach (int ownedSubIndex in ownedSubIndices) { SubmarineInfo sub = GameMain.Client.ServerSubmarines[ownedSubIndex]; if (GameMain.NetLobbyScreen.CheckIfCampaignSubMatches(sub, NetLobbyScreen.SubmarineDeliveryData.Owned)) { - GameMain.GameSession.OwnedSubmarines.Add(sub); + if (GameMain.GameSession.OwnedSubmarines.None(s => s.Name == sub.Name)) + { + GameMain.GameSession.OwnedSubmarines.Add(sub); + } } } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/SinglePlayerCampaign.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/SinglePlayerCampaign.cs index 065bfeb8a..fc3f8f71c 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/SinglePlayerCampaign.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/SinglePlayerCampaign.cs @@ -184,24 +184,9 @@ namespace Barotrauma }, OnClicked = (btn, userdata) => { - var availableTransition = GetAvailableTransition(out _, out _); - if (Character.Controlled != null && - availableTransition == TransitionType.ReturnToPreviousLocation && - Character.Controlled?.Submarine == Level.Loaded?.StartOutpost) - { - TryEndRound(); - } - else if (Character.Controlled != null && - availableTransition == TransitionType.ProgressToNextLocation && - Character.Controlled?.Submarine == Level.Loaded?.EndOutpost) - { - TryEndRound(); - } - else - { - ShowCampaignUI = true; - CampaignUI.SelectTab(InteractionType.Map); - } + TryEndRoundWithFuelCheck( + onConfirm: () => TryEndRound(), + onReturnToMapScreen: () => { ShowCampaignUI = true; CampaignUI.SelectTab(InteractionType.Map); }); return true; } }; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/LightComponent.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/LightComponent.cs index 337a21274..5bfc4e192 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/LightComponent.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/LightComponent.cs @@ -89,7 +89,8 @@ namespace Barotrauma.Items.Components if (Light?.LightSprite != null && item.Prefab.CanSpriteFlipX && item.body == null) { Light.LightSpriteEffect = Light.LightSpriteEffect == SpriteEffects.None ? - SpriteEffects.FlipHorizontally : SpriteEffects.None; + SpriteEffects.FlipHorizontally : SpriteEffects.None; + SetLightSourceTransformProjSpecific(); } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Sonar.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Sonar.cs index 0b3583f3b..ca210f1fb 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Sonar.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Sonar.cs @@ -938,7 +938,7 @@ namespace Barotrauma.Items.Components DrawMarker(spriteBatch, Level.Loaded.StartLocation.Name, (Level.Loaded.StartOutpost != null ? "outpost" : "location").ToIdentifier(), - Level.Loaded.StartLocation.Name, + "startlocation", Level.Loaded.StartExitPosition, transducerCenter, displayScale, center, DisplayRadius); } @@ -948,7 +948,7 @@ namespace Barotrauma.Items.Components DrawMarker(spriteBatch, Level.Loaded.EndLocation.Name, (Level.Loaded.EndOutpost != null ? "outpost" : "location").ToIdentifier(), - Level.Loaded.EndLocation.Name, + "endlocation", Level.Loaded.EndExitPosition, transducerCenter, displayScale, center, DisplayRadius); } @@ -985,7 +985,8 @@ namespace Barotrauma.Items.Components missionIndex++; } - if (AllowUsingMineralScanner && useMineralScanner && CurrentMode == Mode.Active && MineralClusters != null) + if (AllowUsingMineralScanner && useMineralScanner && CurrentMode == Mode.Active && MineralClusters != null && + (item.CurrentHull == null || !DetectSubmarineWalls)) { foreach (var c in MineralClusters) { diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Projectile.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Projectile.cs index 8523f56f1..28a25c88e 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Projectile.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Projectile.cs @@ -1,11 +1,9 @@ using Barotrauma.Networking; using Barotrauma.Particles; -using FarseerPhysics; using Microsoft.Xna.Framework; using System; using System.Collections.Generic; using System.Linq; -using System.Xml.Linq; namespace Barotrauma.Items.Components { diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Wearable.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Wearable.cs index 4c5848a45..8918490f2 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Wearable.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Wearable.cs @@ -10,22 +10,18 @@ namespace Barotrauma.Items.Components { int roundedValue = (int)Math.Round((1 - damageModifier.DamageMultiplier * damageModifier.ProbabilityMultiplier) * 100); if (roundedValue == 0) { return; } - string colorStr = XMLExtensions.ColorToString(GUIStyle.Green); + string colorStr = XMLExtensions.ToStringHex(GUIStyle.Green); LocalizedString afflictionName = AfflictionPrefab.List.FirstOrDefault(ap => ap.Identifier == afflictionIdentifier)?.Name ?? TextManager.Get($"afflictiontype.{afflictionIdentifier}").Fallback(afflictionIdentifier.Value); - description += $"\n ‖color:{colorStr}‖{roundedValue.ToString("-0;+#")}%‖color:end‖ {afflictionName}"; + if (!description.IsNullOrWhiteSpace()) { description += '\n'; } + description += $" ‖color:{colorStr}‖{roundedValue.ToString("-0;+#")}%‖color:end‖ {afflictionName}"; } public override void AddTooltipInfo(ref LocalizedString name, ref LocalizedString description) { - if (damageModifiers.Any(d => !MathUtils.NearlyEqual(d.DamageMultiplier, 1f) || !MathUtils.NearlyEqual(d.ProbabilityMultiplier, 1f)) || SkillModifiers.Any()) - { - description += "\n"; - } - if (damageModifiers.Any()) { foreach (DamageModifier damageModifier in damageModifiers) @@ -49,10 +45,11 @@ namespace Barotrauma.Items.Components { foreach (var skillModifier in SkillModifiers) { - string colorStr = XMLExtensions.ColorToString(GUIStyle.Green); + string colorStr = XMLExtensions.ToStringHex(GUIStyle.Green); int roundedValue = (int)Math.Round(skillModifier.Value); if (roundedValue == 0) { continue; } - description += $"\n ‖color:{colorStr}‖{roundedValue.ToString("+0;-#")}‖color:end‖ {TextManager.Get($"SkillName.{skillModifier.Key}").Fallback(skillModifier.Key.Value)}"; + if (!description.IsNullOrWhiteSpace()) { description += '\n'; } + description += $" ‖color:{colorStr}‖{roundedValue.ToString("+0;-#")}‖color:end‖ {TextManager.Get($"SkillName.{skillModifier.Key}").Fallback(skillModifier.Key.Value)}"; } } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Inventory.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Inventory.cs index b23835fec..d4ad96681 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Inventory.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Inventory.cs @@ -292,7 +292,9 @@ namespace Barotrauma } else { - description = TextManager.GetWithVariables("IDCardNameJob", ("[name]", idName, FormatCapitals.No), ("[job]", idJob, FormatCapitals.Yes)); + description = TextManager.GetWithVariables("IDCardNameJob", + ("[name]", idName, FormatCapitals.No), + ("[job]", TextManager.Get("jobname." + idJob).Fallback(idJob), FormatCapitals.Yes)); } if (!string.IsNullOrEmpty(item.Description)) { @@ -1582,7 +1584,7 @@ namespace Barotrauma DrawItemStateIndicator(spriteBatch, inventory, indicatorSprite, emptyIndicatorSprite, conditionIndicatorArea, item.Condition / item.MaxCondition); } - if (itemContainer != null && itemContainer.ShowContainedStateIndicator) + if (itemContainer != null && itemContainer.ShowContainedStateIndicator && itemContainer.Capacity > 0) { float containedState = 0.0f; if (itemContainer.ShowConditionInContainedStateIndicator) diff --git a/Barotrauma/BarotraumaClient/ClientSource/Map/Creatures/BallastFloraBehavior.cs b/Barotrauma/BarotraumaClient/ClientSource/Map/Creatures/BallastFloraBehavior.cs index 0fc5d2c4f..6aee6dd4f 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Map/Creatures/BallastFloraBehavior.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Map/Creatures/BallastFloraBehavior.cs @@ -91,6 +91,8 @@ namespace Barotrauma.MapCreatures.Behavior { foreach (BallastFloraBranch branch in Branches) { + if (branch.IsRoot && isDead) { continue; } + if (branch.AccumulatedDamage > 0) { CreateDamageParticle(branch, branch.AccumulatedDamage); @@ -101,8 +103,7 @@ namespace Barotrauma.MapCreatures.Behavior GUI.AddMessage($"{(int)branch.AccumulatedDamage}", GUIStyle.Red, pos, Vector2.UnitY * 10.0f, 3f, playSound: false, subId: Parent?.Submarine?.ID ?? -1); } } - if (Character.Controlled != null && Character.Controlled.CurrentHull == branch.CurrentHull && - branch.IsRoot && + if (Character.Controlled != null && Character.Controlled.CurrentHull == branch.CurrentHull && branch.IsRoot && (branch.AccumulatedDamage > 0.0f || branch.AccumulatedDamage < -0.1f)) { Character.Controlled.UpdateHUDProgressBar(this, diff --git a/Barotrauma/BarotraumaClient/ClientSource/Map/Gap.cs b/Barotrauma/BarotraumaClient/ClientSource/Map/Gap.cs index 088ffdf3d..8da238592 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Map/Gap.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Map/Gap.cs @@ -149,51 +149,18 @@ namespace Barotrauma pos.X += Math.Sign(flowForce.X); pos.Y = MathHelper.Clamp(Rand.Range(higherSurface, lowerSurface), rect.Y - rect.Height, rect.Y); } - else + if (flowTargetHull != null) { - pos.Y += Math.Sign(flowForce.Y) * rect.Height / 2.0f; + pos.X = MathHelper.Clamp(pos.X, flowTargetHull.Rect.X + 1, flowTargetHull.Rect.Right - 1); + pos.Y = MathHelper.Clamp(pos.Y, flowTargetHull.Rect.Y - flowTargetHull.Rect.Height + 1, flowTargetHull.Rect.Y - 1); } //spawn less particles when there's already a large number of them float particleAmountMultiplier = 1.0f - GameMain.ParticleManager.ParticleCount / (float)GameMain.ParticleManager.MaxParticles; particleAmountMultiplier *= particleAmountMultiplier; - //light dripping - if (open < 0.2f && LerpedFlowForce.LengthSquared() > 100.0f) - { - particleTimer += deltaTime; - float particlesPerSec = open * 100.0f * particleAmountMultiplier; - float emitInterval = 1.0f / particlesPerSec; - while (particleTimer > emitInterval) - { - Vector2 velocity = flowForce; - if (!IsHorizontal) - { - velocity.X = Rand.Range(-500.0f, 500.0f) * open; - } - else - { - velocity.X *= Rand.Range(1.0f, 3.0f); - } - - if (flowTargetHull.WaterVolume < flowTargetHull.Volume) - { - GameMain.ParticleManager.CreateParticle( - Rand.Range(0.0f, open) < 0.05f ? "waterdrop" : "watersplash", - (Submarine == null ? pos : pos + Submarine.Position), - velocity, 0, flowTargetHull); - } - - GameMain.ParticleManager.CreateParticle( - "bubbles", - (Submarine == null ? pos : pos + Submarine.Position), - velocity, 0, flowTargetHull); - - particleTimer -= emitInterval; - } - } //heavy flow -> strong waterfall type of particles - else if (LerpedFlowForce.LengthSquared() > 20000.0f) + if (LerpedFlowForce.LengthSquared() > 20000.0f) { particleTimer += deltaTime; if (IsHorizontal) @@ -218,6 +185,10 @@ namespace Barotrauma if (particle.CurrentHull == null) { GameMain.ParticleManager.RemoveParticle(particle); } particle.Size *= Math.Min(Math.Abs(flowForce.X / 500.0f), 5.0f); } + if (GapSize() <= Structure.WallSectionSize || !IsRoomToRoom) + { + CreateWaterSpatter(); + } } if (Math.Abs(flowForce.X) > 300.0f && flowTargetHull.WaterVolume > flowTargetHull.Volume * 0.1f) @@ -245,7 +216,7 @@ namespace Barotrauma { if (Math.Sign(flowTargetHull.Rect.Y - rect.Y) != Math.Sign(lerpedFlowForce.Y)) { return; } - float particlesPerSec = Math.Max(open * rect.Width * 0.3f * particleAmountMultiplier, 20.0f); + float particlesPerSec = Math.Max(open * rect.Width * particleAmountMultiplier, 10.0f); float emitInterval = 1.0f / particlesPerSec; while (particleTimer > emitInterval) { @@ -263,7 +234,11 @@ namespace Barotrauma if (splash != null) { if (splash.CurrentHull == null) { GameMain.ParticleManager.RemoveParticle(splash); } - splash.Size *= MathHelper.Clamp(rect.Width / 50.0f, 1.5f, 4.0f); + splash.Size *= MathHelper.Clamp(rect.Width / 50.0f, 1.5f, 4.0f); + } + if (GapSize() <= Structure.WallSectionSize || !IsRoomToRoom) + { + CreateWaterSpatter(); } } if (Math.Abs(flowForce.Y) > 190.0f && Rand.Range(0.0f, 1.0f) < 0.3f && flowTargetHull.WaterVolume > flowTargetHull.Volume * 0.1f) @@ -277,10 +252,77 @@ namespace Barotrauma } } } + //light dripping + else if (LerpedFlowForce.LengthSquared() > 100.0f && + /*no dripping from large gaps between rooms (looks bad)*/ + ((GapSize() <= Structure.WallSectionSize) || !IsRoomToRoom)) + { + particleTimer += deltaTime; + float particlesPerSec = open * 10.0f * particleAmountMultiplier; + float emitInterval = 1.0f / particlesPerSec; + while (particleTimer > emitInterval) + { + Vector2 velocity = flowForce; + if (!IsHorizontal) + { + velocity.X = Rand.Range(-100.0f, 100.0f) * open; + } + else + { + velocity.X *= Rand.Range(1.0f, 3.0f); + } + + if (flowTargetHull.WaterVolume < flowTargetHull.Volume) + { + GameMain.ParticleManager.CreateParticle( + Rand.Range(0.0f, open) < 0.05f ? "waterdrop" : "watersplash", + Submarine == null ? pos : pos + Submarine.Position, + velocity, 0, flowTargetHull); + CreateWaterSpatter(); + } + + GameMain.ParticleManager.CreateParticle( + "bubbles", + (Submarine == null ? pos : pos + Submarine.Position), + velocity, 0, flowTargetHull); + + particleTimer -= emitInterval; + } + } else { particleTimer = 0.0f; } + + void CreateWaterSpatter() + { + Vector2 spatterPos = pos; + float rotation; + if (IsHorizontal) + { + rotation = LerpedFlowForce.X > 0 ? 0 : MathHelper.Pi; + spatterPos.Y = rect.Y - rect.Height / 2; + } + else + { + rotation = LerpedFlowForce.Y > 0 ? -MathHelper.PiOver2 : MathHelper.PiOver2; + spatterPos.X = rect.Center.X; + } + var spatter = GameMain.ParticleManager.CreateParticle( + "waterspatter", + Submarine == null ? spatterPos : spatterPos + Submarine.Position, + Vector2.Zero, rotation, flowTargetHull); + if (spatter != null) + { + if (spatter.CurrentHull == null) { GameMain.ParticleManager.RemoveParticle(spatter); } + spatter.Size *= MathHelper.Clamp(LerpedFlowForce.Length() / 200.0f, 0.5f, 1.0f); + } + } + + float GapSize() + { + return IsHorizontal ? rect.Height : rect.Width; + } } } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Map/Lights/LightManager.cs b/Barotrauma/BarotraumaClient/ClientSource/Map/Lights/LightManager.cs index e49b2cf55..ca5456156 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Map/Lights/LightManager.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Map/Lights/LightManager.cs @@ -254,7 +254,7 @@ namespace Barotrauma.Lights } //remove some lights with a light volume if there's too many of them - if (activeLightsWithLightVolume.Count > GameSettings.CurrentConfig.Graphics.VisibleLightLimit) + if (activeLightsWithLightVolume.Count > GameSettings.CurrentConfig.Graphics.VisibleLightLimit && Screen.Selected is { IsEditor: false }) { for (int i = GameSettings.CurrentConfig.Graphics.VisibleLightLimit; i < activeLightsWithLightVolume.Count; i++) { diff --git a/Barotrauma/BarotraumaClient/ClientSource/Map/Lights/LightSource.cs b/Barotrauma/BarotraumaClient/ClientSource/Map/Lights/LightSource.cs index 5a4454ccf..6fe45ed52 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Map/Lights/LightSource.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Map/Lights/LightSource.cs @@ -52,7 +52,7 @@ namespace Barotrauma.Lights [Serialize(0f, IsPropertySaveable.Yes), Editable(MinValueFloat = -360, MaxValueFloat = 360, ValueStep = 1, DecimalCount = 0)] public float Rotation { get; set; } - public Vector2 GetOffset() => Vector2.Transform(Offset, Matrix.CreateRotationZ(Rotation)); + public Vector2 GetOffset() => Vector2.Transform(Offset, Matrix.CreateRotationZ(MathHelper.ToRadians(Rotation))); private float flicker; [Editable, Serialize(0.0f, IsPropertySaveable.No, description: "How heavily the light flickers. 0 = no flickering, 1 = the light will alternate between completely dark and full brightness.")] diff --git a/Barotrauma/BarotraumaClient/ClientSource/Map/Map/Map.cs b/Barotrauma/BarotraumaClient/ClientSource/Map/Map/Map.cs index ecdbeeacc..25e6141c5 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Map/Map/Map.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Map/Map/Map.cs @@ -110,7 +110,9 @@ namespace Barotrauma { noiseOverlay ??= new Sprite("Content/UI/noise.png", Vector2.Zero); - OnLocationChanged = LocationChanged; + OnLocationChanged.RegisterOverwriteExisting( + "Map.InitProjSpecific".ToIdentifier(), + (locationChangeInfo) => LocationChanged(locationChangeInfo.PrevLocation, locationChangeInfo.NewLocation)); borders = new Rectangle( (int)Locations.Min(l => l.MapPosition.X), @@ -447,7 +449,7 @@ namespace Barotrauma Level.Loaded.DebugSetEndLocation(null); CurrentLocation.Discover(); - OnLocationChanged?.Invoke(prevLocation, CurrentLocation); + OnLocationChanged?.Invoke(new LocationChangeInfo(prevLocation, CurrentLocation)); SelectLocation(-1); if (GameMain.Client == null) { diff --git a/Barotrauma/BarotraumaClient/ClientSource/Map/Submarine.cs b/Barotrauma/BarotraumaClient/ClientSource/Map/Submarine.cs index 44c3c5e92..06cfbc539 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Map/Submarine.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Map/Submarine.cs @@ -88,6 +88,11 @@ namespace Barotrauma prevCullTime = 0; } + public static void ForceRemoveFromVisibleEntities(MapEntity entity) + { + visibleEntities?.Remove(entity); + } + public static void Draw(SpriteBatch spriteBatch, bool editing = false) { var entitiesToRender = !editing && visibleEntities != null ? visibleEntities : MapEntity.mapEntityList; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs b/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs index fdb7451f0..e5a4c4b0d 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs @@ -1845,8 +1845,11 @@ namespace Barotrauma.Networking GameMain.GameScreen.Select(); - // TODO: Re-enable the server message once it's been edited and translated - //AddChatMessage($"ServerMessage.HowToCommunicate~[chatbutton]={GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Chat)}~[radiobutton]={GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.RadioChat)}", ChatMessageType.Server); + string message = "ServerMessage.HowToCommunicate" + + $"~[chatbutton]={GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.ActiveChat)}" + + $"~[pttbutton]={GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Voice)}" + + $"~[switchbutton]={GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.ToggleChatMode)}"; + AddChatMessage(message, ChatMessageType.Server); yield return CoroutineStatus.Success; } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Networking/Voting.cs b/Barotrauma/BarotraumaClient/ClientSource/Networking/Voting.cs index a889a9b4d..90382e7c9 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Networking/Voting.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Networking/Voting.cs @@ -271,7 +271,7 @@ namespace Barotrauma case VoteType.SwitchSub: string subName1 = inc.ReadString(); bool transferItems = inc.ReadBoolean(); - SubmarineInfo info = GameMain.Client.ServerSubmarines.FirstOrDefault(s => s.Name == subName1); + SubmarineInfo info = GameMain.GameSession.OwnedSubmarines.FirstOrDefault(s => s.Name == subName1) ?? GameMain.Client.ServerSubmarines.FirstOrDefault(s => s.Name == subName1); if (info == null) { DebugConsole.ThrowError("Failed to find a matching submarine, vote aborted"); @@ -303,7 +303,7 @@ namespace Barotrauma case VoteType.PurchaseAndSwitchSub: case VoteType.SwitchSub: string subName2 = inc.ReadString(); - var submarineInfo = GameMain.Client.ServerSubmarines.FirstOrDefault(s => s.Name == subName2); + var submarineInfo = GameMain.GameSession.OwnedSubmarines.FirstOrDefault(s => s.Name == subName2) ?? GameMain.Client.ServerSubmarines.FirstOrDefault(s => s.Name == subName2); bool transferItems = inc.ReadBoolean(); int deliveryFee = inc.ReadInt16(); if (submarineInfo == null) diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/CampaignSetupUI.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/CampaignSetupUI.cs index 5e679768b..0b33e0a5a 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/CampaignSetupUI.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/CampaignSetupUI.cs @@ -275,7 +275,7 @@ namespace Barotrauma { GUILayoutGroup inputContainer = CreateSettingBase(parent, description, tooltip, horizontalSize: 0.55f, verticalSize: verticalSize); - GUIButton minusButton = new GUIButton(new RectTransform(Vector2.One, inputContainer.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "GUIMinusButton", textAlignment: Alignment.Center) { UserData = -1 }; + GUIButton minusButton = new GUIButton(new RectTransform(Vector2.One, inputContainer.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "GUIButtonToggleLeft", textAlignment: Alignment.Center) { UserData = -1 }; GUIFrame inputFrame = new GUIFrame(new RectTransform(Vector2.One, inputContainer.RectTransform), style: null); GUINumberInput numberInput = new GUINumberInput(new RectTransform(Vector2.One, inputFrame.RectTransform, Anchor.Center), NumberType.Int, textAlignment: Alignment.Center, style: "GUITextBox", hidePlusMinusButtons: true) { @@ -290,7 +290,7 @@ namespace Barotrauma CanBeFocused = false }; - GUIButton plusButton = new GUIButton(new RectTransform(Vector2.One, inputContainer.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "GUIPlusButton", textAlignment: Alignment.Center) { UserData = 1 }; + GUIButton plusButton = new GUIButton(new RectTransform(Vector2.One, inputContainer.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "GUIButtonToggleRight", textAlignment: Alignment.Center) { UserData = 1 }; minusButton.OnClicked = plusButton.OnClicked = ChangeValue; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/SinglePlayerCampaignSetupUI.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/SinglePlayerCampaignSetupUI.cs index b093b494d..7f98cdc43 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/SinglePlayerCampaignSetupUI.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/SinglePlayerCampaignSetupUI.cs @@ -235,13 +235,6 @@ namespace Barotrauma } }; - var disclaimerBtn = new GUIButton(new RectTransform(new Vector2(1.0f, 0.8f), rightColumn.RectTransform, Anchor.TopRight) { AbsoluteOffset = new Point(5) }, style: "GUINotificationButton") - { - IgnoreLayoutGroups = true, - OnClicked = (btn, userdata) => { GameMain.Instance.ShowCampaignDisclaimer(); return true; } - }; - disclaimerBtn.RectTransform.MaxSize = new Point((int)(30 * GUI.Scale)); - columnContainer.Recalculate(); leftColumn.Recalculate(); rightColumn.Recalculate(); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignUI.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignUI.cs index 3d0a30141..ba4d9eaa3 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignUI.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignUI.cs @@ -57,8 +57,8 @@ namespace Barotrauma CreateUI(container); - campaign.Map.OnLocationSelected += SelectLocation; - campaign.Map.OnMissionsSelected += (connection, missions) => + campaign.Map.OnLocationSelected = SelectLocation; + campaign.Map.OnMissionsSelected = (connection, missions) => { if (missionList?.Content != null) { diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/MainMenuScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/MainMenuScreen.cs index 88691410e..b61b535f0 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/MainMenuScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/MainMenuScreen.cs @@ -534,12 +534,6 @@ namespace Barotrauma ShowTutorialSkipWarning(Tab.NewGame); return true; } - if (!GameSettings.CurrentConfig.CampaignDisclaimerShown) - { - selectedTab = Tab.Empty; - GameMain.Instance.ShowCampaignDisclaimer(() => { SelectTab(null, Tab.NewGame); }); - return true; - } campaignSetupUI.RandomizeCrew(); campaignSetupUI.SetPage(0); campaignSetupUI.CreateDefaultSaveName(); @@ -559,12 +553,6 @@ namespace Barotrauma ShowTutorialSkipWarning(Tab.JoinServer); return true; } - if (!GameSettings.CurrentConfig.CampaignDisclaimerShown) - { - selectedTab = Tab.Empty; - GameMain.Instance.ShowCampaignDisclaimer(() => { SelectTab(null, Tab.JoinServer); }); - return true; - } GameMain.ServerListScreen.Select(); break; case Tab.HostServer: @@ -574,13 +562,6 @@ namespace Barotrauma ShowTutorialSkipWarning(tab); return true; } - if (!GameSettings.CurrentConfig.CampaignDisclaimerShown) - { - selectedTab = Tab.Empty; - GameMain.Instance.ShowCampaignDisclaimer(() => { SelectTab(null, Tab.HostServer); }); - return true; - } - serverExecutableDropdown.ListBox.Content.Children.ToArray() .Where(c => c.UserData is ServerExecutableFile f && !ContentPackageManager.EnabledPackages.All.Contains(f.ContentPackage)) .ForEach(serverExecutableDropdown.ListBox.RemoveChild); @@ -611,12 +592,6 @@ namespace Barotrauma } break; case Tab.Tutorials: - if (!GameSettings.CurrentConfig.CampaignDisclaimerShown) - { - selectedTab = Tab.Empty; - GameMain.Instance.ShowCampaignDisclaimer(() => { SelectTab(null, Tab.Tutorials); }); - return true; - } UpdateTutorialList(); break; case Tab.CharacterEditor: diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs index 61a57a7ef..66c404987 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs @@ -921,7 +921,7 @@ namespace Barotrauma toggleEntityMenuButton = new GUIButton(new RectTransform(new Vector2(0.15f, 0.08f), EntityMenu.RectTransform, Anchor.TopCenter, Pivot.BottomCenter) { MinSize = new Point(0, 15) }, style: "UIToggleButtonVertical") { - ToolTip = RichString.Rich(TextManager.Get("EntityMenuToggleTooltip") + "‖color:125,125,125‖\nQ‖color:end‖"), + ToolTip = RichString.Rich($"{TextManager.Get("EntityMenuToggleTooltip")}\n‖color:125,125,125‖{GameSettings.CurrentConfig.KeyMap.Bindings[InputType.ToggleInventory].Name}‖color:end‖"), OnClicked = (btn, userdata) => { entityMenuOpen = !entityMenuOpen; @@ -1814,7 +1814,7 @@ namespace Barotrauma { if (Submarine.GetLightCount() > MaxLights) { - new GUIMessageBox(TextManager.Get("error"), TextManager.GetWithVariable("subeditor.lightcounterror", "[max]", MaxShadowCastingLights.ToString())); + new GUIMessageBox(TextManager.Get("error"), TextManager.GetWithVariable("subeditor.lightcounterror", "[max]", MaxLights.ToString())); return false; } @@ -2522,6 +2522,27 @@ namespace Barotrauma } }; + var outFittingArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), subSettingsContainer.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) + { + Stretch = true, + AbsoluteSpacing = 5 + }; + new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), outFittingArea.RectTransform), + TextManager.Get("ManuallyOutfitted"), textAlignment: Alignment.CenterLeft, wrap: true, font: GUIStyle.SmallFont) + { + ToolTip = TextManager.Get("manuallyoutfittedtooltip") + }; + new GUITickBox(new RectTransform((0.4f, 1.0f), outFittingArea.RectTransform), "") + { + ToolTip = TextManager.Get("manuallyoutfittedtooltip"), + Selected = MainSub.Info.IsManuallyOutfitted, + OnSelected = box => + { + MainSub.Info.IsManuallyOutfitted = box.Selected; + return true; + } + }; + if (MainSub != null) { int min = MainSub.Info.RecommendedCrewSizeMin; @@ -2824,7 +2845,7 @@ namespace Barotrauma }; var saveButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonArea.RectTransform, Anchor.BottomRight), - TextManager.Get("SaveSubButton")) + TextManager.Get("SaveSubButton").Fallback(TextManager.Get("save"))) { OnClicked = (button, o) => SaveSub(packageToSaveInList.SelectedData as ContentPackage) }; @@ -3502,8 +3523,11 @@ namespace Barotrauma modProject.RemoveFile(modProject.Files.First(f => ContentPath.FromRaw(subPackage, f.Path) == sub.FilePath)); modProject.Save(subPackage.Path); ReloadModifiedPackage(subPackage); - } - + if (MainSub?.Info != null && MainSub.Info.FilePath == sub.FilePath) + { + MainSub.Info.FilePath = null; + } + } sub.Dispose(); CreateLoadScreen(); } @@ -5036,8 +5060,8 @@ namespace Barotrauma hullVolumeFrame.Visible = MapEntity.SelectedList.Any(s => s is Hull); hullVolumeFrame.RectTransform.AbsoluteOffset = new Point(Math.Max(showEntitiesPanel.Rect.Right, previouslyUsedPanel.Rect.Right), 0); - saveAssemblyFrame.Visible = MapEntity.SelectedList.Count > 0; - snapToGridFrame.Visible = MapEntity.SelectedList.Count > 0; + saveAssemblyFrame.Visible = MapEntity.SelectedList.Count > 0 && !WiringMode; + snapToGridFrame.Visible = MapEntity.SelectedList.Count > 0 && !WiringMode; var offset = cam.WorldView.Top - cam.ScreenToWorld(new Vector2(0, GameMain.GraphicsHeight - EntityMenu.Rect.Top)).Y; @@ -5163,7 +5187,7 @@ namespace Barotrauma } } - if (PlayerInput.KeyHit(Keys.Q) && mode == Mode.Default) + if (GameSettings.CurrentConfig.KeyMap.Bindings[InputType.ToggleInventory].IsHit() && mode == Mode.Default) { toggleEntityMenuButton.OnClicked?.Invoke(toggleEntityMenuButton, toggleEntityMenuButton.UserData); } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Serialization/SerializableEntityEditor.cs b/Barotrauma/BarotraumaClient/ClientSource/Serialization/SerializableEntityEditor.cs index afd88b6da..636331079 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Serialization/SerializableEntityEditor.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Serialization/SerializableEntityEditor.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using Barotrauma.Items.Components; using Barotrauma.Extensions; +using System.Diagnostics; namespace Barotrauma { @@ -629,16 +630,18 @@ namespace Barotrauma bool isFlagsAttribute = value.GetType().IsDefined(typeof(FlagsAttribute), false); + bool hasNoneOption = false; foreach (object enumValue in Enum.GetValues(value.GetType())) { if (isFlagsAttribute && !MathHelper.IsPowerOfTwo((int)enumValue)) { continue; } - + hasNoneOption |= (int)enumValue == 0; enumDropDown.AddItem(enumValue.ToString(), enumValue); if (((int)enumValue != 0 || (int)value == 0) && ((Enum)value).HasFlag((Enum)enumValue)) { enumDropDown.SelectItem(enumValue); } } + enumDropDown.MustSelectAtLeastOne = !hasNoneOption; enumDropDown.OnSelected += (selected, val) => { if (SetPropertyValue(property, entity, string.Join(", ", enumDropDown.SelectedDataMultiple.Select(d => d.ToString())))) @@ -1435,14 +1438,31 @@ namespace Barotrauma } } break; - case ItemComponent _ when entity is Item item: - foreach (var component in item.Components) + case ItemComponent parentComponent when entity is Item otherItem: + if (otherItem == parentComponent.Item) { continue; } + int componentIndex = parentComponent.Item.Components.FindAll(c => c.GetType() == parentComponent.GetType()).IndexOf(parentComponent); + //find the component of the same type and same index from the other item + var otherComponents = otherItem.Components.FindAll(c => c.GetType() == parentComponent.GetType()); + if (componentIndex >= 0 && componentIndex < otherComponents.Count) { - if (component.GetType() == parentObject.GetType() && component != parentObject) + var component = otherComponents[componentIndex]; + Debug.Assert(component.GetType() == parentObject.GetType()); + SafeAdd(component, property); + if (value is string stringValue && Enum.TryParse(property.PropertyType, stringValue, out var enumValue)) { - SafeAdd(component, property); - property.PropertyInfo.SetValue(component, value); + property.PropertyInfo.SetValue(component, enumValue); } + else + { + try + { + property.PropertyInfo.SetValue(component, value); + } + catch (ArgumentException e) + { + DebugConsole.ThrowError($"Failed to set the value of the property \"{property.Name}\" to {value?.ToString() ?? "null"}", e); + } + } } break; } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Settings/SettingsMenu.cs b/Barotrauma/BarotraumaClient/ClientSource/Settings/SettingsMenu.cs index 4e9d8b6a6..9cf545dc8 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Settings/SettingsMenu.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Settings/SettingsMenu.cs @@ -45,8 +45,8 @@ namespace Barotrauma Instance = new SettingsMenu(mainParent); return Instance; } - - private SettingsMenu(RectTransform mainParent) + + private SettingsMenu(RectTransform mainParent, GameSettings.Config setConfig = default) { unsavedConfig = GameSettings.CurrentConfig; @@ -462,8 +462,9 @@ namespace Barotrauma Slider(voiceChat, (0, 500), 26, (v) => $"{Round(v)} ms", unsavedConfig.Audio.VoiceChatCutoffPrevention, (v) => unsavedConfig.Audio.VoiceChatCutoffPrevention = Round(v), TextManager.Get("CutoffPreventionTooltip")); } - + private readonly Dictionary> inputButtonValueNameGetters = new Dictionary>(); private bool inputBoxSelectedThisFrame = false; + private void CreateControlsTab() { GUIFrame content = CreateNewContentFrame(Tab.Controls); @@ -487,7 +488,7 @@ namespace Barotrauma GUILayoutGroup createInputRowLayout() => new GUILayoutGroup(new RectTransform((1.0f, 0.1f), keyMapList.Content.RectTransform), isHorizontal: true); - HashSet inputButtons = new HashSet(); + inputButtonValueNameGetters.Clear(); Action? currentSetter = null; void addInputToRow(GUILayoutGroup currRow, LocalizedString labelText, Func valueNameGetter, Action valueSetter, bool isLegacyBind = false) { @@ -505,7 +506,7 @@ namespace Barotrauma { OnClicked = (btn, obj) => { - inputButtons.ForEach(b => + inputButtonValueNameGetters.Keys.ForEach(b => { if (b != btn) { b.Selected = false; } }); @@ -530,7 +531,7 @@ namespace Barotrauma inputBox.Color = Color.Lerp(inputBox.Color, inputBox.DisabledColor, 0.5f); inputBox.TextColor = Color.Lerp(inputBox.TextColor, label.DisabledTextColor, 0.5f); } - inputButtons.Add(inputBox); + inputButtonValueNameGetters.Add(inputBox, valueNameGetter); } var inputListener = new GUICustomComponent(new RectTransform(Vector2.Zero, layout.RectTransform), onUpdate: (deltaTime, component) => @@ -546,7 +547,7 @@ namespace Barotrauma void clearSetter() { currentSetter = null; - inputButtons.ForEach(b => b.Selected = false); + inputButtonValueNameGetters.Keys.ForEach(b => b.Selected = false); } void callSetter(KeyOrMouse v) @@ -649,11 +650,14 @@ namespace Barotrauma TextManager.Get("Reset"), style: "GUIButtonSmall") { ToolTip = TextManager.Get("SetDefaultBindingsTooltip"), - OnClicked = (btn, userdata) => + OnClicked = (_, userdata) => { unsavedConfig.InventoryKeyMap = GameSettings.Config.InventoryKeyMapping.GetDefault(); unsavedConfig.KeyMap = GameSettings.Config.KeyMapping.GetDefault(); - Create(mainFrame.Parent.RectTransform); + foreach (var btn in inputButtonValueNameGetters.Keys) + { + btn.Text = inputButtonValueNameGetters[btn](); + } Instance?.SelectTab(Tab.Controls); return true; } @@ -761,7 +765,7 @@ namespace Barotrauma private void CreateBottomButtons() { GUIButton cancelButton = - new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), bottom.RectTransform), text: "Cancel") + new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), bottom.RectTransform), text: TextManager.Get("Cancel")) { OnClicked = (btn, obj) => { @@ -770,7 +774,7 @@ namespace Barotrauma } }; GUIButton applyButton = - new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), bottom.RectTransform), text: "Apply") + new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), bottom.RectTransform), text: TextManager.Get("applysettingsbutton")) { OnClicked = (btn, obj) => { diff --git a/Barotrauma/BarotraumaClient/ClientSource/Sprite/Sprite.cs b/Barotrauma/BarotraumaClient/ClientSource/Sprite/Sprite.cs index 2a71f2bd7..d4e1648a3 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Sprite/Sprite.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Sprite/Sprite.cs @@ -253,12 +253,12 @@ namespace Barotrauma if (flipHorizontal) { float diff = targetSize.X % (sourceRect.Width * scale.X); - flippedDrawOffset.X = (int)MathF.Round((sourceRect.Width * scale.X - diff) / scale.X); + flippedDrawOffset.X = (int)((sourceRect.Width * scale.X - diff) / scale.X); } if (flipVertical) { float diff = targetSize.Y % (sourceRect.Height * scale.Y); - flippedDrawOffset.Y = (int)MathF.Round((sourceRect.Height * scale.Y - diff) / scale.Y); + flippedDrawOffset.Y = (int)((sourceRect.Height * scale.Y - diff) / scale.Y); } drawOffset += flippedDrawOffset; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Steam/Lobby.cs b/Barotrauma/BarotraumaClient/ClientSource/Steam/Lobby.cs index d7a3f7b70..03760d937 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Steam/Lobby.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Steam/Lobby.cs @@ -360,8 +360,8 @@ namespace Barotrauma.Steam if (rules == null) { return; } - if (rules.ContainsKey("message")) serverInfo.ServerMessage = rules["message"]; - if (rules.ContainsKey("version")) serverInfo.GameVersion = rules["version"]; + if (rules.ContainsKey("message")) { serverInfo.ServerMessage = rules["message"]; } + if (rules.ContainsKey("version")) { serverInfo.GameVersion = rules["version"]; } if (rules.ContainsKey("playercount")) { @@ -371,8 +371,8 @@ namespace Barotrauma.Steam serverInfo.ContentPackageNames.Clear(); serverInfo.ContentPackageHashes.Clear(); serverInfo.ContentPackageWorkshopIds.Clear(); - if (rules.ContainsKey("contentpackage")) serverInfo.ContentPackageNames.AddRange(rules["contentpackage"].Split(',')); - if (rules.ContainsKey("contentpackagehash")) serverInfo.ContentPackageHashes.AddRange(rules["contentpackagehash"].Split(',')); + if (rules.ContainsKey("contentpackage")) { serverInfo.ContentPackageNames.AddRange(rules["contentpackage"].Split(',')); } + if (rules.ContainsKey("contentpackagehash")) { serverInfo.ContentPackageHashes.AddRange(rules["contentpackagehash"].Split(',')); } if (rules.ContainsKey("contentpackageid")) { serverInfo.ContentPackageWorkshopIds.AddRange(ParseWorkshopIds(rules["contentpackageid"])); @@ -383,24 +383,26 @@ namespace Barotrauma.Steam serverInfo.ContentPackageWorkshopIds.AddRange(WorkshopUrlsToIds(workshopUrls)); } - if (rules.ContainsKey("usingwhitelist")) serverInfo.UsingWhiteList = rules["usingwhitelist"] == "True"; + if (rules.ContainsKey("usingwhitelist")) { serverInfo.UsingWhiteList = rules["usingwhitelist"] == "True"; } if (rules.ContainsKey("modeselectionmode")) { - if (Enum.TryParse(rules["modeselectionmode"], out SelectionMode selectionMode)) serverInfo.ModeSelectionMode = selectionMode; + if (Enum.TryParse(rules["modeselectionmode"], out SelectionMode selectionMode)) { serverInfo.ModeSelectionMode = selectionMode; } } if (rules.ContainsKey("subselectionmode")) { - if (Enum.TryParse(rules["subselectionmode"], out SelectionMode selectionMode)) serverInfo.SubSelectionMode = selectionMode; + if (Enum.TryParse(rules["subselectionmode"], out SelectionMode selectionMode)) { serverInfo.SubSelectionMode = selectionMode; } } - if (rules.ContainsKey("allowspectating")) serverInfo.AllowSpectating = rules["allowspectating"] == "True"; - if (rules.ContainsKey("allowrespawn")) serverInfo.AllowRespawn = rules["allowrespawn"] == "True"; - if (rules.ContainsKey("voicechatenabled")) serverInfo.VoipEnabled = rules["voicechatenabled"] == "True"; + if (rules.ContainsKey("allowspectating")) { serverInfo.AllowSpectating = rules["allowspectating"] == "True"; } + if (rules.ContainsKey("allowrespawn")) { serverInfo.AllowRespawn = rules["allowrespawn"] == "True"; } + if (rules.ContainsKey("voicechatenabled")) { serverInfo.VoipEnabled = rules["voicechatenabled"] == "True"; } + if (rules.ContainsKey("friendlyfireenabled")) { serverInfo.AllowRespawn = rules["friendlyfireenabled"] == "True"; } + if (rules.ContainsKey("karmaenabled")) { serverInfo.VoipEnabled = rules["karmaenabled"] == "True"; } if (rules.ContainsKey("traitors")) { - if (Enum.TryParse(rules["traitors"], out YesNoMaybe traitorsEnabled)) serverInfo.TraitorsEnabled = traitorsEnabled; + if (Enum.TryParse(rules["traitors"], out YesNoMaybe traitorsEnabled)) { serverInfo.TraitorsEnabled = traitorsEnabled; } } - if (rules.ContainsKey("gamestarted")) serverInfo.GameStarted = rules["gamestarted"] == "True"; + if (rules.ContainsKey("gamestarted")) { serverInfo.GameStarted = rules["gamestarted"] == "True"; } if (rules.ContainsKey("gamemode")) { serverInfo.GameMode = rules["gamemode"].ToIdentifier(); diff --git a/Barotrauma/BarotraumaClient/LinuxClient.csproj b/Barotrauma/BarotraumaClient/LinuxClient.csproj index 9f63ed147..251b78f36 100644 --- a/Barotrauma/BarotraumaClient/LinuxClient.csproj +++ b/Barotrauma/BarotraumaClient/LinuxClient.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma - 0.18.12.0 + 0.18.15.0 Copyright © FakeFish 2018-2022 AnyCPU;x64 Barotrauma diff --git a/Barotrauma/BarotraumaClient/MacClient.csproj b/Barotrauma/BarotraumaClient/MacClient.csproj index b7b86ed76..065e6b693 100644 --- a/Barotrauma/BarotraumaClient/MacClient.csproj +++ b/Barotrauma/BarotraumaClient/MacClient.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma - 0.18.12.0 + 0.18.15.0 Copyright © FakeFish 2018-2022 AnyCPU;x64 Barotrauma diff --git a/Barotrauma/BarotraumaClient/WindowsClient.csproj b/Barotrauma/BarotraumaClient/WindowsClient.csproj index 646efcdd1..f684f83ae 100644 --- a/Barotrauma/BarotraumaClient/WindowsClient.csproj +++ b/Barotrauma/BarotraumaClient/WindowsClient.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma - 0.18.12.0 + 0.18.15.0 Copyright © FakeFish 2018-2022 AnyCPU;x64 Barotrauma diff --git a/Barotrauma/BarotraumaServer/LinuxServer.csproj b/Barotrauma/BarotraumaServer/LinuxServer.csproj index 4e9201acb..868528655 100644 --- a/Barotrauma/BarotraumaServer/LinuxServer.csproj +++ b/Barotrauma/BarotraumaServer/LinuxServer.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma Dedicated Server - 0.18.12.0 + 0.18.15.0 Copyright © FakeFish 2018-2022 AnyCPU;x64 DedicatedServer diff --git a/Barotrauma/BarotraumaServer/MacServer.csproj b/Barotrauma/BarotraumaServer/MacServer.csproj index ddb07ed6a..523a650c6 100644 --- a/Barotrauma/BarotraumaServer/MacServer.csproj +++ b/Barotrauma/BarotraumaServer/MacServer.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma Dedicated Server - 0.18.12.0 + 0.18.15.0 Copyright © FakeFish 2018-2022 AnyCPU;x64 DedicatedServer diff --git a/Barotrauma/BarotraumaServer/ServerSource/Characters/CharacterNetworking.cs b/Barotrauma/BarotraumaServer/ServerSource/Characters/CharacterNetworking.cs index 9ad728290..f896b27c5 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Characters/CharacterNetworking.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Characters/CharacterNetworking.cs @@ -562,8 +562,10 @@ namespace Barotrauma } } - private List severedJointIndices = new List(); - private void WriteStatus(IWriteMessage msg) + private readonly List severedJointIndices = new List(); + + /// Normally full affliction data is not written for dead characters, this can be used to force them to be written + private void WriteStatus(IWriteMessage msg, bool forceAfflictionData = false) { msg.Write(IsDead); if (IsDead) @@ -573,6 +575,11 @@ namespace Barotrauma { msg.Write(CauseOfDeath.Affliction.Identifier); } + msg.Write(forceAfflictionData); + if (forceAfflictionData) + { + CharacterHealth.ServerWrite(msg); + } } else { @@ -689,7 +696,7 @@ namespace Barotrauma if (msg.LengthBytes - initialMsgLength >= 255 && restrictMessageSize) { - string errorMsg = $"Error when writing character spawn data: data exceeded 255 bytes (info: {infoLength}, orders: {ordersLength}, total: {msg.LengthBytes - initialMsgLength})"; + string errorMsg = $"Error when writing character spawn data for \"{Name}\": data exceeded 255 bytes (info: {infoLength}, orders: {ordersLength}, total: {msg.LengthBytes - initialMsgLength})"; DebugConsole.ThrowError(errorMsg); GameAnalyticsManager.AddErrorEventOnce("Character.WriteSpawnData:TooMuchData", GameAnalyticsManager.ErrorSeverity.Error, errorMsg); } @@ -701,13 +708,13 @@ namespace Barotrauma int msgLengthBeforeStatus = msg.LengthBytes - initialMsgLength; var tempBuffer = new ReadWriteMessage(); - WriteStatus(tempBuffer); + WriteStatus(tempBuffer, forceAfflictionData: true); if (msgLengthBeforeStatus + tempBuffer.LengthBytes >= 255 && restrictMessageSize) { msg.Write(false); if (msgLengthBeforeStatus < 255) { - string errorMsg = $"Error when writing character spawn data: status data caused the length of the message to exceed 255 bytes ({msgLengthBeforeStatus} + {tempBuffer.LengthBytes})"; + string errorMsg = $"Error when writing character spawn data for \"{Name}\": status data caused the length of the message to exceed 255 bytes ({msgLengthBeforeStatus} + {tempBuffer.LengthBytes})"; DebugConsole.ThrowError(errorMsg); GameAnalyticsManager.AddErrorEventOnce("Character.WriteSpawnData:TooMuchDataForStatus", GameAnalyticsManager.ErrorSeverity.Error, errorMsg); } @@ -715,7 +722,7 @@ namespace Barotrauma else { msg.Write(true); - WriteStatus(msg); + WriteStatus(msg, forceAfflictionData: true); } } diff --git a/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs b/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs index a9a05313a..58cb3e0ee 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs @@ -1367,6 +1367,11 @@ namespace Barotrauma } else { + if (GameMain.NetLobbyScreen.SelectedMode == GameModePreset.MultiPlayerCampaign) + { + MultiPlayerCampaign.StartCampaignSetup(); + return; + } if (!GameMain.Server.StartGame()) { NewMessage("Failed to start a new round", Color.Yellow); } } })); diff --git a/Barotrauma/BarotraumaServer/ServerSource/GameSession/CargoManager.cs b/Barotrauma/BarotraumaServer/ServerSource/GameSession/CargoManager.cs index 454f1627d..79597042e 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/GameSession/CargoManager.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/GameSession/CargoManager.cs @@ -77,7 +77,7 @@ namespace Barotrauma campaign.GetWallet(client).Give(itemValue); GameAnalyticsManager.AddMoneyGainedEvent(itemValue, GameAnalyticsManager.MoneySource.Store, item.ItemPrefab.Identifier.Value); } - OnSoldItemsChanged?.Invoke(); + OnSoldItemsChanged?.Invoke(this); } public void ClearSoldItemsProjSpecific() diff --git a/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/MultiPlayerCampaign.cs b/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/MultiPlayerCampaign.cs index ae5c4e510..6dbe8ff73 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/MultiPlayerCampaign.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/MultiPlayerCampaign.cs @@ -393,14 +393,17 @@ namespace Barotrauma } partial void InitProjSpecific() - { - CargoManager.OnItemsInBuyCrateChanged += () => { IncrementLastUpdateIdForFlag(NetFlags.ItemsInBuyCrate); }; - CargoManager.OnPurchasedItemsChanged += () => { IncrementLastUpdateIdForFlag(NetFlags.PurchasedItems); }; - CargoManager.OnSoldItemsChanged += () => { IncrementLastUpdateIdForFlag(NetFlags.SoldItems); }; - UpgradeManager.OnUpgradesChanged += () => { IncrementLastUpdateIdForFlag(NetFlags.UpgradeManager); }; - Map.OnLocationSelected += (loc, connection) => { IncrementLastUpdateIdForFlag(NetFlags.MapAndMissions); }; - Map.OnMissionsSelected += (loc, mission) => { IncrementLastUpdateIdForFlag(NetFlags.MapAndMissions); }; - Reputation.OnAnyReputationValueChanged += () => { IncrementLastUpdateIdForFlag(NetFlags.Reputation); }; + { + Identifier eventId = nameof(MultiPlayerCampaign).ToIdentifier(); + CargoManager.OnItemsInBuyCrateChanged.RegisterOverwriteExisting(eventId, _ => IncrementLastUpdateIdForFlag(NetFlags.ItemsInBuyCrate)); + CargoManager.OnPurchasedItemsChanged.RegisterOverwriteExisting(eventId, _ => IncrementLastUpdateIdForFlag(NetFlags.PurchasedItems)); + CargoManager.OnSoldItemsChanged.RegisterOverwriteExisting(eventId, _ => IncrementLastUpdateIdForFlag(NetFlags.SoldItems)); + UpgradeManager.OnUpgradesChanged.RegisterOverwriteExisting(eventId, _ => IncrementLastUpdateIdForFlag(NetFlags.UpgradeManager)); + + Reputation.OnAnyReputationValueChanged.RegisterOverwriteExisting(eventId, _ => IncrementLastUpdateIdForFlag(NetFlags.Reputation)); + + Map.OnLocationSelected = (loc, connection) => IncrementLastUpdateIdForFlag(NetFlags.MapAndMissions); + Map.OnMissionsSelected = (loc, mission) => IncrementLastUpdateIdForFlag(NetFlags.MapAndMissions); //increment save ID so clients know they're lacking the most up-to-date save file LastSaveID++; diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs index f9085de45..e246d4549 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs @@ -1598,10 +1598,11 @@ namespace Barotrauma.Networking outmsg.Write((UInt16)subList.Count); for (int i = 0; i < subList.Count; i++) { - outmsg.Write(subList[i].Name); - outmsg.Write(subList[i].MD5Hash.ToString()); - outmsg.Write((byte)subList[i].SubmarineClass); - outmsg.Write(subList[i].RequiredContentPackagesInstalled); + var sub = subList[i]; + outmsg.Write(sub.Name); + outmsg.Write(sub.MD5Hash.ToString()); + outmsg.Write((byte)sub.SubmarineClass); + outmsg.Write(sub.RequiredContentPackagesInstalled); } outmsg.Write(GameStarted); @@ -2030,12 +2031,15 @@ namespace Barotrauma.Networking GameModePreset selectedMode = Voting.HighestVoted(VoteType.Mode, connectedClients); if (selectedMode == null) { selectedMode = GameMain.NetLobbyScreen.SelectedMode; } - if (selectedMode == null) { return false; } - + if (selectedMode == GameModePreset.MultiPlayerCampaign && !(GameMain.GameSession?.GameMode is CampaignMode)) + { + DebugConsole.ThrowError("StartGame failed. Cannot start a multiplayer campaign via StartGame - use MultiPlayerCampaign.StartNewCampaign or MultiPlayerCampaign.LoadCampaign instead."); + return false; + } initiatedStartGame = true; startGameCoroutine = CoroutineManager.StartCoroutine(InitiateStartGame(selectedSub, selectedShuttle, selectedMode), "InitiateStartGame"); @@ -2122,11 +2126,17 @@ namespace Barotrauma.Networking yield return CoroutineStatus.Failure; } + bool initialSuppliesSpawned = false; //don't instantiate a new gamesession if we're playing a campaign if (campaign == null || GameMain.GameSession == null) { GameMain.GameSession = new GameSession(selectedSub, "", selectedMode, settings, GameMain.NetLobbyScreen.LevelSeed, missionType: GameMain.NetLobbyScreen.MissionType); } + else + { + initialSuppliesSpawned = GameMain.GameSession.SubmarineInfo is { InitialSuppliesSpawned: true }; + } + List playingClients = new List(connectedClients); if (serverSettings.AllowSpectating) @@ -2409,17 +2419,18 @@ namespace Barotrauma.Networking campaign?.CargoManager.InitPurchasedIDCards(); - foreach (Submarine sub in Submarine.MainSubs) + if (campaign == null || !initialSuppliesSpawned) { - if (sub == null) { continue; } - - List spawnList = new List(); - foreach (KeyValuePair kvp in serverSettings.ExtraCargo) + foreach (Submarine sub in Submarine.MainSubs) { - spawnList.Add(new PurchasedItem(kvp.Key, kvp.Value, buyer: null)); + if (sub == null) { continue; } + List spawnList = new List(); + foreach (KeyValuePair kvp in serverSettings.ExtraCargo) + { + spawnList.Add(new PurchasedItem(kvp.Key, kvp.Value, buyer: null)); + } + CargoManager.CreateItems(spawnList, sub, cargoManager: null); } - - CargoManager.CreateItems(spawnList, sub, cargoManager: null); } TraitorManager = null; diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/Voting.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/Voting.cs index 4a3577fe5..4724420b4 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Networking/Voting.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/Voting.cs @@ -295,7 +295,7 @@ namespace Barotrauma else { string subName = inc.ReadString(); - SubmarineInfo subInfo = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == subName); + SubmarineInfo subInfo = GameMain.GameSession.OwnedSubmarines.FirstOrDefault(s => s.Name == subName) ?? SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == subName); bool transferItems = inc.ReadBoolean(); if (!ShouldRejectVote(sender, voteType)) { @@ -389,8 +389,9 @@ namespace Barotrauma case VoteType.PurchaseSub: case VoteType.PurchaseAndSwitchSub: case VoteType.SwitchSub: - msg.Write((ActiveVote as SubmarineVote).Sub.Name); - msg.Write((ActiveVote as SubmarineVote).TransferItems); + SubmarineVote vote = ActiveVote as SubmarineVote; + msg.Write(vote.Sub.Name); + msg.Write(vote.TransferItems); break; case VoteType.TransferMoney: var transferVote = (ActiveVote as TransferVote); diff --git a/Barotrauma/BarotraumaServer/ServerSource/Steam/SteamManager.cs b/Barotrauma/BarotraumaServer/ServerSource/Steam/SteamManager.cs index bd622322a..25d6cc5e7 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Steam/SteamManager.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Steam/SteamManager.cs @@ -65,6 +65,8 @@ namespace Barotrauma.Steam Steamworks.SteamServer.SetKey("allowspectating", server.ServerSettings.AllowSpectating.ToString()); Steamworks.SteamServer.SetKey("allowrespawn", server.ServerSettings.AllowRespawn.ToString()); Steamworks.SteamServer.SetKey("traitors", server.ServerSettings.TraitorsEnabled.ToString()); + Steamworks.SteamServer.SetKey("friendlyfireenabled", server.ServerSettings.AllowFriendlyFire.ToString()); + Steamworks.SteamServer.SetKey("karmaenabled", server.ServerSettings.KarmaEnabled.ToString()); Steamworks.SteamServer.SetKey("gamestarted", server.GameStarted.ToString()); Steamworks.SteamServer.SetKey("gamemode", server.ServerSettings.GameModeIdentifier.Value); Steamworks.SteamServer.SetKey("playstyle", server.ServerSettings.PlayStyle.ToString()); diff --git a/Barotrauma/BarotraumaServer/WindowsServer.csproj b/Barotrauma/BarotraumaServer/WindowsServer.csproj index b1431c932..43732773c 100644 --- a/Barotrauma/BarotraumaServer/WindowsServer.csproj +++ b/Barotrauma/BarotraumaServer/WindowsServer.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma Dedicated Server - 0.18.12.0 + 0.18.15.0 Copyright © FakeFish 2018-2022 AnyCPU;x64 DedicatedServer diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/AIController.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/AIController.cs index cc9a9100e..f0eae6be8 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/AIController.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/AIController.cs @@ -308,6 +308,7 @@ namespace Barotrauma } } } + if (targetSlot < 0) { return false; } return targetInventory.TryPutItem(item, targetSlot, allowSwapping, allowCombine: false, Character); } else diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs index ff7c37218..6727b9637 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs @@ -1297,6 +1297,7 @@ namespace Barotrauma AIObjectiveCombat.CombatMode DetermineCombatMode(Character c, float cumulativeDamage = 0, bool isWitnessing = false) { + if (!(c.AIController is HumanAIController humanAI)) { return AIObjectiveCombat.CombatMode.None; } if (!IsFriendly(attacker)) { if (c.Submarine == null) @@ -1306,12 +1307,15 @@ namespace Barotrauma } if (!c.Submarine.GetConnectedSubs().Contains(attacker.Submarine)) { - // Attacked from an unconnected submarine. - return c.SelectedConstruction?.GetComponent() != null ? AIObjectiveCombat.CombatMode.None : AIObjectiveCombat.CombatMode.Retreat; + // Attacked from an unconnected submarine (pirate/pvp) + return + humanAI.ObjectiveManager.CurrentOrder is AIObjectiveOperateItem operateOrder && operateOrder.GetTarget() is Controller ? + AIObjectiveCombat.CombatMode.None : AIObjectiveCombat.CombatMode.Retreat; } - return c.AIController is HumanAIController humanAI && - (humanAI.ObjectiveManager.IsCurrentOrder() || humanAI.ObjectiveManager.Objectives.Any(o => o is AIObjectiveFightIntruders)) - ? AIObjectiveCombat.CombatMode.Offensive : AIObjectiveCombat.CombatMode.Defensive; + return + humanAI.ObjectiveManager.IsCurrentOrder() || + humanAI.ObjectiveManager.Objectives.Any(o => o is AIObjectiveFightIntruders) ? + AIObjectiveCombat.CombatMode.Offensive : AIObjectiveCombat.CombatMode.Defensive; } else { @@ -1362,7 +1366,7 @@ namespace Barotrauma } else { - if (c.AIController is HumanAIController humanAI && humanAI.ObjectiveManager.GetActiveObjective()?.Enemy == attacker) + if (humanAI.ObjectiveManager.GetActiveObjective()?.Enemy == attacker) { // Already targeting the attacker -> treat as a more serious threat. cumulativeDamage *= 2; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs index f0225823d..9a57f98a9 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs @@ -1519,9 +1519,10 @@ namespace Barotrauma if (skillIdentifier != null) { - for (int i = 0; i < Inventory.Capacity; i++) + foreach (Item item in Inventory.AllItems) { - if (Inventory.SlotTypes[i] != InvSlotType.Any && Inventory.GetItemAt(i)?.GetComponent() is Wearable wearable) + if (item?.GetComponent() is Wearable wearable && + !Inventory.IsInLimbSlot(item, InvSlotType.Any)) { if (wearable.SkillModifiers.TryGetValue(skillIdentifier, out float skillValue)) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/CharacterInfo.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/CharacterInfo.cs index 49d36bdfa..1a9df1f90 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/CharacterInfo.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/CharacterInfo.cs @@ -1639,8 +1639,13 @@ namespace Barotrauma private void RefreshHeadSprites() { - HeadSprite = null; - AttachmentSprites = null; + _headSprite = null; + LoadHeadSprite(); +#if CLIENT + CalculateHeadPosition(_headSprite); +#endif + attachmentSprites?.Clear(); + LoadAttachmentSprites(); } // This could maybe be a LookUp instead? diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/CharacterHealth.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/CharacterHealth.cs index bdd89998c..2381b31cc 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/CharacterHealth.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/CharacterHealth.cs @@ -809,8 +809,13 @@ namespace Barotrauma if (!Character.GodMode) { - UpdateLimbAfflictionOverlays(); - UpdateSkinTint(); +#if CLIENT + if (Character.IsVisible) + { + UpdateLimbAfflictionOverlays(); + UpdateSkinTint(); + } +#endif CalculateVitality(); if (Vitality <= MinVitality) @@ -820,6 +825,12 @@ namespace Barotrauma } } + public void ForceUpdateVisuals() + { + UpdateLimbAfflictionOverlays(); + UpdateSkinTint(); + } + private void UpdateDamageReductions(float deltaTime) { float healthRegen = Character.Params.Health.ConstantHealthRegeneration; diff --git a/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentPackage/ContentPackage.cs b/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentPackage/ContentPackage.cs index bcb068006..1cfef53ce 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentPackage/ContentPackage.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentPackage/ContentPackage.cs @@ -14,7 +14,7 @@ namespace Barotrauma { public abstract class ContentPackage { - public static readonly Version MinimumHashCompatibleVersion = new Version(0, 18, 3, 0); + public static readonly Version MinimumHashCompatibleVersion = new Version(0, 18, 13, 0); public const string LocalModsDir = "LocalMods"; public static readonly string WorkshopModsDir = Barotrauma.IO.Path.Combine( @@ -180,7 +180,7 @@ namespace Barotrauma { if (!condition) { - throw new InvalidOperationException($"Failed to load \"{Name ?? Path}\": {errorMsg}"); + throw new InvalidOperationException($"Failed to load \"{Name}\" at {Path}: {errorMsg}"); } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/GameSession/AutoItemPlacer.cs b/Barotrauma/BarotraumaShared/SharedSource/GameSession/AutoItemPlacer.cs index 7764c444e..4d06bc9a9 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/GameSession/AutoItemPlacer.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/GameSession/AutoItemPlacer.cs @@ -28,6 +28,7 @@ namespace Barotrauma var subs = sub.GetConnectedSubs().Where(s => s.TeamID == sub.TeamID); CreateAndPlace(subs); subs.ForEach(s => s.Info.InitialSuppliesSpawned = true); + sub.CheckFuel(); } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/GameSession/CargoManager.cs b/Barotrauma/BarotraumaShared/SharedSource/GameSession/CargoManager.cs index b14a19dde..c4d5be01a 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/GameSession/CargoManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/GameSession/CargoManager.cs @@ -147,12 +147,12 @@ namespace Barotrauma private Location Location => campaign?.Map?.CurrentLocation; - public Action OnItemsInBuyCrateChanged; - public Action OnItemsInSellCrateChanged; - public Action OnItemsInSellFromSubCrateChanged; - public Action OnPurchasedItemsChanged; - public Action OnSoldItemsChanged; - + public readonly NamedEvent OnItemsInBuyCrateChanged = new NamedEvent(); + public readonly NamedEvent OnItemsInSellCrateChanged = new NamedEvent(); + public readonly NamedEvent OnItemsInSellFromSubCrateChanged = new NamedEvent(); + public readonly NamedEvent OnPurchasedItemsChanged = new NamedEvent(); + public readonly NamedEvent OnSoldItemsChanged = new NamedEvent(); + public CargoManager(CampaignMode campaign) { this.campaign = campaign; @@ -215,19 +215,19 @@ namespace Barotrauma public void ClearItemsInBuyCrate() { ItemsInBuyCrate.Clear(); - OnItemsInBuyCrateChanged?.Invoke(); + OnItemsInBuyCrateChanged?.Invoke(this); } public void ClearItemsInSellCrate() { ItemsInSellCrate.Clear(); - OnItemsInSellCrateChanged?.Invoke(); + OnItemsInSellCrateChanged?.Invoke(this); } public void ClearItemsInSellFromSubCrate() { ItemsInSellFromSubCrate.Clear(); - OnItemsInSellFromSubCrateChanged?.Invoke(); + OnItemsInSellFromSubCrateChanged?.Invoke(this); } public void SetPurchasedItems(Dictionary> purchasedItems) @@ -238,7 +238,7 @@ namespace Barotrauma { PurchasedItems.Add(entry.Key, entry.Value); } - OnPurchasedItemsChanged?.Invoke(); + OnPurchasedItemsChanged?.Invoke(this); } public void ModifyItemQuantityInBuyCrate(Identifier storeIdentifier, ItemPrefab itemPrefab, int changeInQuantity, Client client = null) @@ -255,7 +255,7 @@ namespace Barotrauma { GetBuyCrateItems(storeIdentifier, create: true).Add(new PurchasedItem(itemPrefab, changeInQuantity, client)); } - OnItemsInBuyCrateChanged?.Invoke(); + OnItemsInBuyCrateChanged?.Invoke(this); } public void ModifyItemQuantityInSubSellCrate(Identifier storeIdentifier, ItemPrefab itemPrefab, int changeInQuantity, Client client = null) @@ -272,7 +272,7 @@ namespace Barotrauma { GetSubCrateItems(storeIdentifier, create: true).Add(new PurchasedItem(itemPrefab, changeInQuantity, client)); } - OnItemsInSellFromSubCrateChanged?.Invoke(); + OnItemsInSellFromSubCrateChanged?.Invoke(this); } #if SERVER @@ -331,7 +331,7 @@ namespace Barotrauma } } } - OnPurchasedItemsChanged?.Invoke(); + OnPurchasedItemsChanged?.Invoke(this); } public Dictionary GetBuyValuesAtCurrentLocation(Identifier storeIdentifier, IEnumerable items) @@ -378,7 +378,7 @@ namespace Barotrauma } CreateItems(items, Submarine.MainSub, this); PurchasedItems.Clear(); - OnPurchasedItemsChanged?.Invoke(); + OnPurchasedItemsChanged?.Invoke(this); } private Dictionary UndeterminedSoldEntities { get; } = new Dictionary(); diff --git a/Barotrauma/BarotraumaShared/SharedSource/GameSession/Data/Reputation.cs b/Barotrauma/BarotraumaShared/SharedSource/GameSession/Data/Reputation.cs index 8a70b39e3..fe45446d3 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/GameSession/Data/Reputation.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/GameSession/Data/Reputation.cs @@ -39,8 +39,8 @@ namespace Barotrauma float prevValue = Value; Metadata.SetValue(metaDataIdentifier, Math.Clamp(value, MinReputation, MaxReputation)); - OnReputationValueChanged?.Invoke(); - OnAnyReputationValueChanged?.Invoke(); + OnReputationValueChanged?.Invoke(this); + OnAnyReputationValueChanged?.Invoke(this); #if CLIENT int increase = (int)Value - (int)prevValue; if (increase != 0 && Character.Controlled != null) @@ -73,8 +73,8 @@ namespace Barotrauma Value += reputationChange; } - public Action OnReputationValueChanged; - public static Action OnAnyReputationValueChanged; + public readonly NamedEvent OnReputationValueChanged = new NamedEvent(); + public static readonly NamedEvent OnAnyReputationValueChanged = new NamedEvent(); public readonly Faction Faction; public readonly Location Location; diff --git a/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/CampaignMode.cs b/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/CampaignMode.cs index 9edca9159..7bf83c372 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/CampaignMode.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/CampaignMode.cs @@ -1005,7 +1005,7 @@ namespace Barotrauma } } - public SubmarineInfo SwitchSubs() + public void SwitchSubs() { if (TransferItemsOnSubSwitch) { @@ -1013,7 +1013,6 @@ namespace Barotrauma } RefreshOwnedSubmarines(); PendingSubmarineSwitch = null; - return GameMain.GameSession.SubmarineInfo; } /// @@ -1035,13 +1034,16 @@ namespace Barotrauma foreach (Item item in Item.ItemList) { if (item.Removed) { continue; } - if (item.NonInteractable) { continue; } + if (item.NonInteractable || item.NonPlayerTeamInteractable) { continue; } if (item.HiddenInGame) { continue; } if (!connectedSubs.Contains(item.Submarine)) { continue; } if (item.Prefab.DontTransferBetweenSubs) { continue; } - if (item.GetRootInventoryOwner() is Character) { continue; } - if (item.GetComponent() == null && item.GetComponent() == null && item.GetComponent() == null) { continue; } - if (item.Components.Any(c => c is Holdable h && h.Attached)) { continue; } + var rootOwner = item.GetRootInventoryOwner(); + if (rootOwner is Character) { continue; } + if (rootOwner is Item ownerItem && (ownerItem.NonInteractable || item.NonPlayerTeamInteractable || ownerItem.HiddenInGame)) { continue; } + if (item.GetComponent() != null) { continue; } + if (item.Components.None(c => c is Pickable)) { continue; } + if (item.Components.Any(c => c is Pickable p && p.IsAttached)) { continue; } if (item.Components.Any(c => c is Wire w && w.Connections.Any(c => c != null))) { continue; } itemsToTransfer.Add((item, item.Container)); item.Submarine = null; @@ -1054,6 +1056,7 @@ namespace Barotrauma item.Drop(null, createNetworkEvent: false, setTransform: false); } } + currentSub.Info.NoItems = true; } // Serialize the current sub GameMain.GameSession.SubmarineInfo = new SubmarineInfo(currentSub); @@ -1064,7 +1067,7 @@ namespace Barotrauma var connectedSubs = newSub.GetConnectedSubs().Where(s => s.Info.Type == SubmarineType.Player).ToHashSet(); // Move the transferred items List availableContainers = Item.ItemList - .Where(it => connectedSubs.Contains(it.Submarine) && it.HasTag("crate") && !it.NonInteractable && !it.HiddenInGame && !it.Removed) + .Where(it => connectedSubs.Contains(it.Submarine) && it.HasTag("crate") && !it.NonInteractable && !it.NonPlayerTeamInteractable && !it.HiddenInGame && !it.Removed) .Select(it => it.GetComponent()) .Where(c => c != null) .ToList(); @@ -1122,6 +1125,7 @@ namespace Barotrauma DebugConsole.Log(msg); #endif } + newSub.Info.NoItems = false; // Serialize the new sub PendingSubmarineSwitch = new SubmarineInfo(newSub); } diff --git a/Barotrauma/BarotraumaShared/SharedSource/GameSession/UpgradeManager.cs b/Barotrauma/BarotraumaShared/SharedSource/GameSession/UpgradeManager.cs index ac3b984b5..b133e0228 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/GameSession/UpgradeManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/GameSession/UpgradeManager.cs @@ -94,7 +94,7 @@ namespace Barotrauma private CampaignMetadata Metadata => Campaign.CampaignMetadata; private readonly CampaignMode Campaign; - public event Action? OnUpgradesChanged; + public readonly NamedEvent OnUpgradesChanged = new NamedEvent(); public UpgradeManager(CampaignMode campaign) { @@ -248,7 +248,7 @@ namespace Barotrauma // tell the server that this item is yet to be paid for server side PurchasedUpgrades.Add(new PurchasedUpgrade(prefab, category)); #endif - OnUpgradesChanged?.Invoke(); + OnUpgradesChanged?.Invoke(this); } else { @@ -349,7 +349,7 @@ namespace Barotrauma } } - OnUpgradesChanged?.Invoke(); + OnUpgradesChanged?.Invoke(this); } else { @@ -418,7 +418,7 @@ namespace Barotrauma } #if CLIENT - OnUpgradesChanged?.Invoke(); + OnUpgradesChanged?.Invoke(this); #endif } @@ -802,7 +802,7 @@ namespace Barotrauma { PendingUpgrades.Clear(); PendingUpgrades.AddRange(upgrades); - OnUpgradesChanged?.Invoke(); + OnUpgradesChanged?.Invoke(this); } public static void DebugLog(string msg, Color? color = null) diff --git a/Barotrauma/BarotraumaShared/SharedSource/InputType.cs b/Barotrauma/BarotraumaShared/SharedSource/InputType.cs index 3e792f98d..2b496a231 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/InputType.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/InputType.cs @@ -16,6 +16,7 @@ namespace Barotrauma Deselect, Shoot, Command, + ToggleInventory, TakeOneFromInventorySlot, TakeHalfFromInventorySlot, NextFireMode, diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/MeleeWeapon.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/MeleeWeapon.cs index 53a55e033..a321e27a2 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/MeleeWeapon.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/MeleeWeapon.cs @@ -191,7 +191,7 @@ namespace Barotrauma.Items.Components while (impactQueue.Count > 0) { var impact = impactQueue.Dequeue(); - HandleImpact(impact.Body); + HandleImpact(impact); } //in case handling the impact does something to the picker if (picker == null) { return; } @@ -342,7 +342,7 @@ namespace Barotrauma.Items.Components } hitTargets.Add(targetCharacter); } - else if (f2.Body.UserData is Structure targetStructure) + else if ((f2.Body.UserData as Structure ?? f2.UserData as Structure) is Structure targetStructure) { if (AllowHitMultiple) { @@ -380,8 +380,9 @@ namespace Barotrauma.Items.Components return true; } - private void HandleImpact(Body target) + private void HandleImpact(Fixture targetFixture) { + var target = targetFixture.Body; if (User == null || User.Removed || target == null) { RestoreCollision(); @@ -410,7 +411,7 @@ namespace Barotrauma.Items.Components targetCharacter.LastDamageSource = item; Attack.DoDamage(User, targetCharacter, item.WorldPosition, 1.0f); } - else if (target.UserData is Structure targetStructure) + else if ((target.UserData as Structure ?? targetFixture.UserData as Structure) is Structure targetStructure) { if (targetStructure.Removed) { return; } Attack.DoDamage(User, targetStructure, item.WorldPosition, 1.0f); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/ItemContainer.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/ItemContainer.cs index 2b0e7db1d..0ea9d4cad 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/ItemContainer.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/ItemContainer.cs @@ -193,6 +193,9 @@ namespace Barotrauma.Items.Components private Vector2 prevContainedItemPositions; + private float autoInjectCooldown = 1.0f; + const float AutoInjectInterval = 1.0f; + public bool ShouldBeContained(string[] identifiersOrTags, out bool isRestrictionsDefined) { @@ -412,7 +415,15 @@ namespace Barotrauma.Items.Components if (AutoInject) { - if (ownerInventory?.Owner is Character ownerCharacter && + //normally autoinjection should delete the (medical) item, so it only gets applied once + //but in multiplayer clients aren't allowed to remove items themselves, so they may be able to trigger this dozens of times + //before the server notifies them of the item being removed, leading to a sharp lag spike. + //this can also happen with mods, if there's a way to autoinject something that doesn't get removed On Use. + //so let's ensure the item is only applied once per second at most. + + autoInjectCooldown -= deltaTime; + if (autoInjectCooldown <= 0.0f && + ownerInventory?.Owner is Character ownerCharacter && ownerCharacter.HealthPercentage / 100f <= AutoInjectThreshold && ownerCharacter.HasEquippedItem(item)) { @@ -420,6 +431,7 @@ namespace Barotrauma.Items.Components { item.ApplyStatusEffects(ActionType.OnUse, 1.0f, ownerCharacter); item.GetComponent()?.Equip(ownerCharacter); + autoInjectCooldown = AutoInjectInterval; } } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Fabricator.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Fabricator.cs index a35141def..1d48a1378 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Fabricator.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Fabricator.cs @@ -338,9 +338,7 @@ namespace Barotrauma.Items.Components if (user == null) { return; } if (GameMain.GameSession?.GameMode is MultiPlayerCampaign mpCampaign) { -#if CLIENT - mpCampaign.TryPurchase(null, fabricatedItem.RequiredMoney); -#elif SERVER +#if SERVER if (GetUsingClient() is { } client) { mpCampaign.TryPurchase(client, fabricatedItem.RequiredMoney); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Pump.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Pump.cs index fc0d44ebb..bfd0b23fb 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Pump.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Pump.cs @@ -142,9 +142,8 @@ namespace Barotrauma.Items.Components //less effective when in a bad condition currFlow *= MathHelper.Lerp(0.5f, 1.0f, item.Condition / item.MaxCondition); - item.CurrentHull.WaterVolume += currFlow * deltaTime * Timing.FixedUpdateRate; + item.CurrentHull.WaterVolume += currFlow * deltaTime * Timing.FixedUpdateRate; if (item.CurrentHull.WaterVolume > item.CurrentHull.Volume) { item.CurrentHull.Pressure += 30.0f * deltaTime; } - } public void InfectBallast(Identifier identifier, bool allowMultiplePerShip = false) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Projectile.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Projectile.cs index 7ca7eff7d..9c281f00d 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Projectile.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Projectile.cs @@ -742,7 +742,7 @@ namespace Barotrauma.Items.Components limb.body?.ApplyLinearImpulse(item.body.LinearVelocity * item.body.Mass * 0.1f, item.SimPosition); return false; } - if (!FriendlyFire && User != null && limb.character.IsFriendly(User)) + if (!FriendlyFire && User != null && limb.character.IsFriendly(User) && HumanAIController.IsOnFriendlyTeam(limb.character, User)) { return false; } @@ -789,6 +789,11 @@ namespace Barotrauma.Items.Components private bool ShouldIgnoreSubmarineCollision(ref Fixture target, Contact contact) { + //not in the projectile category: the projectile has not been launched (e.g. just dropped from an inventory) + if (item.body.CollisionCategories != Physics.CollisionProjectile) + { + return false; + } if (target.Body.UserData is Submarine sub) { Vector2 dir = item.body.LinearVelocity.LengthSquared() < 0.001f ? diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/LightComponent.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/LightComponent.cs index fd5ee7f13..b44d136c2 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/LightComponent.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/LightComponent.cs @@ -238,6 +238,12 @@ namespace Barotrauma.Items.Components base.OnItemLoaded(); SetLightSourceState(IsActive, lightBrightness); turret = item.GetComponent(); +#if CLIENT + if (Screen.Selected.IsEditor) + { + OnMapLoaded(); + } +#endif } public override void OnMapLoaded() diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/RegExFindComponent.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/RegExFindComponent.cs index f23292212..33e8f4538 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/RegExFindComponent.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/RegExFindComponent.cs @@ -1,12 +1,11 @@ using System; using System.Text.RegularExpressions; -using System.Xml.Linq; namespace Barotrauma.Items.Components { class RegExFindComponent : ItemComponent { - private static readonly TimeSpan timeout = TimeSpan.FromSeconds(Timing.Step); + private static readonly TimeSpan timeout = TimeSpan.FromMilliseconds(1); private string expression; @@ -63,10 +62,9 @@ namespace Barotrauma.Items.Components get { return expression; } set { - if (expression == value) return; + if (expression == value) { return; } expression = value; previousReceivedSignal = ""; - try { regex = new Regex( @@ -74,11 +72,12 @@ namespace Barotrauma.Items.Components options: RegexOptions.None, matchTimeout: timeout); } - catch { return; } + //reactivate the component, in case some faulty/malicious expression caused it to time out and deactivate itself + IsActive = true; } } @@ -105,11 +104,16 @@ namespace Barotrauma.Items.Components } catch (Exception e) { - item.SendSignal( - e is RegexMatchTimeoutException - ? "TIMEOUT" - : "ERROR", - "signal_out"); + if (e is RegexMatchTimeoutException) + { + item.SendSignal("TIMEOUT", "signal_out"); + //deactivate the component if the expression caused it to time out + IsActive = false; + } + else + { + item.SendSignal("ERROR", "signal_out"); + } previousResult = false; return; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs index 66bc3c1ce..02095120c 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs @@ -877,6 +877,7 @@ namespace Barotrauma //and check if the collision should be ignored in the OnCollision callback, but //that'd make the hit detection more expensive because every item would be included) collisionCategory = Physics.CollisionCharacter; + collidesWith |= Physics.CollisionProjectile; } if (collisionCategoryStr != null) { @@ -1265,8 +1266,8 @@ namespace Barotrauma Vector2 displayPos = ConvertUnits.ToDisplayUnits(simPosition); - rect.X = (int)(displayPos.X - rect.Width / 2.0f); - rect.Y = (int)(displayPos.Y + rect.Height / 2.0f); + rect.X = (int)MathF.Round(displayPos.X - rect.Width / 2.0f); + rect.Y = (int)MathF.Round(displayPos.Y + rect.Height / 2.0f); if (findNewHull) { FindHull(); } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Creatures/BallastFloraBehavior.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Creatures/BallastFloraBehavior.cs index 2a961dcd8..8eabe4b03 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Creatures/BallastFloraBehavior.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Creatures/BallastFloraBehavior.cs @@ -308,6 +308,8 @@ namespace Barotrauma.MapCreatures.Behavior private BallastFloraBranch? root; private readonly List bodies = new List(); + private bool isDead; + public readonly BallastFloraStateMachine StateMachine; public int GrowthWarps; @@ -403,13 +405,14 @@ namespace Barotrauma.MapCreatures.Behavior new XAttribute("health", branch.Health.ToString("G", CultureInfo.InvariantCulture)), new XAttribute("maxhealth", branch.MaxHealth.ToString("G", CultureInfo.InvariantCulture)), new XAttribute("sides", (int)branch.Sides), - new XAttribute("blockedsides", (int)branch.BlockedSides)); + new XAttribute("blockedsides", (int)branch.BlockedSides), + new XAttribute("tile", (int)branch.Type)); if (branch.ClaimedItem != null) { be.Add(new XAttribute("claimed", (int)(branch.ClaimedItem?.ID ?? -1))); } - if (branch.ParentBranch != null) + if (branch.ParentBranch != null && !branch.ParentBranch.Removed) { be.Add(new XAttribute("parentbranch", (int)(branch.ParentBranch?.ID ?? -1))); } @@ -495,14 +498,15 @@ namespace Barotrauma.MapCreatures.Behavior int blockedSides = getInt("blockedsides"); int claimedId = branchElement.GetAttributeInt("claimed", -1); int parentBranchId = branchElement.GetAttributeInt("parentbranch", -1); + VineTileType type = (VineTileType)branchElement.GetAttributeInt("tile", 0); - BallastFloraBranch newBranch = new BallastFloraBranch(this, null, pos, VineTileType.CrossJunction, FoliageConfig.Deserialize(flowerConfig), FoliageConfig.Deserialize(leafconfig)) + BallastFloraBranch newBranch = new BallastFloraBranch(this, null, pos, type, FoliageConfig.Deserialize(flowerConfig), FoliageConfig.Deserialize(leafconfig)) { ID = id, Health = health, MaxHealth = maxhealth, - Sides = (TileSide) sides, - BlockedSides = (TileSide) blockedSides, + Sides = (TileSide)sides, + BlockedSides = (TileSide)blockedSides, IsRoot = isRoot, IsRootGrowth = isRootGrowth }; @@ -683,7 +687,6 @@ namespace Barotrauma.MapCreatures.Behavior if (branch.ClaimedItem != null) { RemoveClaim(branch.ClaimedItem); - branch.ClaimedItem = null; } branch.RemoveTimer -= deltaTime; @@ -1196,6 +1199,14 @@ namespace Barotrauma.MapCreatures.Behavior ClaimedTargets.Remove(item); item.Infector = null; + foreach (var branch in Branches) + { + if (branch.ClaimedItem == item) + { + branch.ClaimedItem = null; + } + } + ClaimedJunctionBoxes.ForEachMod(jb => { if (jb.Item == item) @@ -1221,15 +1232,21 @@ namespace Barotrauma.MapCreatures.Behavior public void Kill() { + isDead = true; + foreach (var branch in Branches) { branch.DisconnectedFromRoot = true; } - foreach (Item target in ClaimedTargets) + foreach (Item target in ClaimedTargets.ToList()) { + RemoveClaim(target); target.Infector = null; } + Debug.Assert(ClaimedTargets.Count == 0); + Debug.Assert(ClaimedJunctionBoxes.Count == 0); + Debug.Assert(ClaimedBatteries.Count == 0); StateMachine?.State?.Exit(); #if SERVER diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Level.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Level.cs index 4dd4ad9f2..981ea9027 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Level.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Level.cs @@ -549,7 +549,7 @@ namespace Barotrauma startPath = new Tunnel( TunnelType.SidePath, new List() { startExitPosition, startPosition }, - minWidth / 2, parentTunnel: mainPath); + minWidth, parentTunnel: mainPath); Tunnels.Add(startPath); } else @@ -561,7 +561,7 @@ namespace Barotrauma endPath = new Tunnel( TunnelType.SidePath, new List() { endPosition, endExitPosition }, - minWidth / 2, parentTunnel: mainPath); + minWidth, parentTunnel: mainPath); Tunnels.Add(endPath); } else @@ -576,14 +576,14 @@ namespace Barotrauma endHole = new Tunnel( TunnelType.SidePath, new List() { startPosition, startExitPosition, new Point(0, Size.Y) }, - minWidth / 2, parentTunnel: mainPath); + minWidth, parentTunnel: mainPath); } else { endHole = new Tunnel( TunnelType.SidePath, new List() { endPosition, endExitPosition, Size }, - minWidth / 2, parentTunnel: mainPath); + minWidth, parentTunnel: mainPath); } Tunnels.Add(endHole); } @@ -601,7 +601,7 @@ namespace Barotrauma abyssTunnel = new Tunnel( TunnelType.SidePath, new List() { lowestPoint, new Point(lowestPoint.X, 0) }, - minWidth / 2, parentTunnel: mainPath); + minWidth, parentTunnel: mainPath); Tunnels.Add(abyssTunnel); } @@ -4266,6 +4266,7 @@ namespace Barotrauma corpse.TeamID = CharacterTeamType.None; corpse.EnableDespawn = false; selectedPrefab.GiveItems(corpse, wreck); + corpse.Kill(CauseOfDeathType.Unknown, causeOfDeathAffliction: null, log: false); corpse.CharacterHealth.ApplyAffliction(corpse.AnimController.MainLimb, AfflictionPrefab.OxygenLow.Instantiate(200)); bool applyBurns = Rand.Value() < 0.1f; bool applyDamage = Rand.Value() < 0.3f; @@ -4294,7 +4295,7 @@ namespace Barotrauma return strength; } } - corpse.Kill(CauseOfDeathType.Unknown, causeOfDeathAffliction: null, log: false); + corpse.CharacterHealth.ForceUpdateVisuals(); corpse.GiveIdCardTags(sp); bool isServerOrSingleplayer = GameMain.IsSingleplayer || GameMain.NetworkMember is { IsServer: true }; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/LinkedSubmarine.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/LinkedSubmarine.cs index da8d88b14..3b6aad96f 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/LinkedSubmarine.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/LinkedSubmarine.cs @@ -182,8 +182,8 @@ namespace Barotrauma float scale = element.GetAttributeFloat("scale", prefab.Scale); var rect = element.GetAttributeVector4("rect", Vector4.Zero); - rect.Z *= scale / prefab.Scale; - rect.W *= scale / prefab.Scale; + if (!prefab.ResizeHorizontal) { rect.Z *= scale / prefab.Scale; } + if (!prefab.ResizeVertical) { rect.W *= scale / prefab.Scale; } points.Add(new Vector2(rect.X, rect.Y)); points.Add(new Vector2(rect.X + rect.Z, rect.Y)); @@ -203,7 +203,7 @@ namespace Barotrauma if (Screen.Selected == GameMain.SubEditorScreen) { linkedSub = CreateDummy(submarine, element, pos, id); - linkedSub.saveElement = element; + linkedSub.saveElement = new XElement(element); linkedSub.purchasedLostShuttles = false; } else @@ -212,8 +212,10 @@ namespace Barotrauma LevelData levelData = GameMain.GameSession?.Campaign?.NextLevel ?? GameMain.GameSession?.LevelData; linkedSub = new LinkedSubmarine(submarine, id) { - purchasedLostShuttles = GameMain.GameSession?.GameMode is CampaignMode campaign && campaign.PurchasedLostShuttles, - saveElement = element + purchasedLostShuttles = + (GameMain.GameSession?.GameMode is CampaignMode campaign && campaign.PurchasedLostShuttles) || + element.GetAttributeBool("purchasedlostshuttle", false), + saveElement = new XElement(element) }; bool levelMatches = string.IsNullOrWhiteSpace(levelSeed) || levelData == null || levelData.Seed == levelSeed; @@ -282,6 +284,8 @@ namespace Barotrauma return; } + saveElement.Attribute("purchasedlostshuttle")?.Remove(); + IdRemap parentRemap = new IdRemap(Submarine.Info.SubmarineElement, Submarine.IdOffset); sub = Submarine.Load(info, false, parentRemap); sub.Info.SubmarineClass = Submarine.Info.SubmarineClass; @@ -442,14 +446,22 @@ namespace Barotrauma saveElement.Attribute("previewimage").Remove(); } - if (saveElement.Attribute("pos") != null) { saveElement.Attribute("pos").Remove(); } - saveElement.Add(new XAttribute("pos", XMLExtensions.Vector2ToString(Position - Submarine.HiddenSubPosition))); - - var linkedPort = linkedTo.FirstOrDefault(lt => (lt is Item) && ((Item)lt).GetComponent() != null); - if (linkedPort != null) + if (GameMain.GameSession?.GameMode is CampaignMode campaign && campaign.PurchasedLostShuttles) { - saveElement.Attribute("linkedto")?.Remove(); - saveElement.Add(new XAttribute("linkedto", linkedPort.ID)); + saveElement.SetAttributeValue("purchasedlostshuttle", true); + } + + saveElement.SetAttributeValue("pos", XMLExtensions.Vector2ToString(Position - Submarine.HiddenSubPosition)); + + if (linkedTo.Any() || linkedToID.Any()) + { + var linkedPort = + linkedTo.FirstOrDefault(lt => (lt is Item item) && item.GetComponent() != null) ?? + FindEntityByID(linkedToID.First()) as MapEntity; + if (linkedPort != null) + { + saveElement.SetAttributeValue("linkedto", linkedPort.ID); + } } } else @@ -458,10 +470,8 @@ namespace Barotrauma sub.SaveToXElement(saveElement); } - saveElement.Attribute("originallinkedto")?.Remove(); - saveElement.Add(new XAttribute("originallinkedto", originalLinkedPort != null ? originalLinkedPort.Item.ID : originalLinkedToID)); - saveElement.Attribute("originalmyport")?.Remove(); - saveElement.Add(new XAttribute("originalmyport", originalMyPortID)); + saveElement.SetAttributeValue("originallinkedto", originalLinkedPort != null ? originalLinkedPort.Item.ID : originalLinkedToID); + saveElement.SetAttributeValue("originalmyport", originalMyPortID); if (sub != null) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/LocationType.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/LocationType.cs index efa64c5d4..e402b8073 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/LocationType.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/LocationType.cs @@ -113,15 +113,23 @@ namespace Barotrauma string teamStr = element.GetAttributeString("outpostteam", "FriendlyNPC"); Enum.TryParse(teamStr, out OutpostTeam); - ContentPath nameFile = element.GetAttributeContentPath("namefile") ?? ContentPath.FromRaw(null, "Content/Map/locationNames.txt"); - try + string[] rawNamePaths = element.GetAttributeStringArray("namefile", new string[] { "Content/Map/locationNames.txt" }); + names = new List(); + foreach (string rawPath in rawNamePaths) { - names = File.ReadAllLines(nameFile.Value).ToList(); + try + { + var path = ContentPath.FromRaw(element.ContentPackage, rawPath.Trim()); + names.AddRange(File.ReadAllLines(path.Value).ToList()); + } + catch (Exception e) + { + DebugConsole.ThrowError($"Failed to read name file \"rawPath\" for location type \"{Identifier}\"!", e); + } } - catch (Exception e) + if (!names.Any()) { - DebugConsole.ThrowError("Failed to read name file for location type \"" + Identifier + "\"!", e); - names = new List() { "Name file not found" }; + names.Add("ERROR: No names found"); } string[] commonnessPerZoneStrs = element.GetAttributeStringArray("commonnessperzone", Array.Empty()); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Map.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Map.cs index 274935593..b09b2400a 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Map.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Map.cs @@ -20,11 +20,24 @@ namespace Barotrauma public int Height { get; private set; } public Action OnLocationSelected; + public Action> OnMissionsSelected; + + public readonly struct LocationChangeInfo + { + public readonly Location PrevLocation; + public readonly Location NewLocation; + + public LocationChangeInfo(Location prevLocation, Location newLocation) + { + PrevLocation = prevLocation; + NewLocation = newLocation; + } + } + /// /// From -> To /// - public Action OnLocationChanged; - public Action> OnMissionsSelected; + public readonly NamedEvent OnLocationChanged = new NamedEvent(); public Location EndLocation { get; private set; } @@ -766,7 +779,7 @@ namespace Barotrauma SelectedLocation = null; CurrentLocation.CreateStores(); - OnLocationChanged?.Invoke(prevLocation, CurrentLocation); + OnLocationChanged?.Invoke(new LocationChangeInfo(prevLocation, CurrentLocation)); if (GameMain.GameSession is { Campaign: { CampaignMetadata: { } metadata } }) { @@ -803,7 +816,7 @@ namespace Barotrauma { connection.Passed = true; } - OnLocationChanged?.Invoke(prevLocation, CurrentLocation); + OnLocationChanged?.Invoke(new LocationChangeInfo(prevLocation, CurrentLocation)); } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/MapEntity.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/MapEntity.cs index 42ca533d6..7ab8451c7 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/MapEntity.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/MapEntity.cs @@ -540,6 +540,7 @@ namespace Barotrauma mapEntityList.Remove(this); #if CLIENT + Submarine.ForceRemoveFromVisibleEntities(this); if (SelectedList.Contains(this)) { SelectedList = SelectedList.Where(e => e != this).ToHashSet(); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Submarine.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Submarine.cs index dc47f42f9..4e6e22f7f 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Submarine.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Submarine.cs @@ -52,7 +52,9 @@ namespace Barotrauma get { return MainSubs[0]; } set { MainSubs[0] = value; } } - private static List loaded = new List(); + private static readonly List loaded = new List(); + + private readonly Identifier upgradeEventIdentifier; private static List visibleEntities; public static IEnumerable VisibleEntities @@ -1301,6 +1303,7 @@ namespace Barotrauma public Submarine(SubmarineInfo info, bool showWarningMessages = true, Func> loadEntities = null, IdRemap linkedRemap = null) : base(null, Entity.NullEntityID) { + upgradeEventIdentifier = new Identifier($"Submarine{ID}"); Loading = true; GameMain.World.Enabled = false; try @@ -1462,10 +1465,7 @@ namespace Barotrauma } } - if (GameMain.GameSession?.Campaign?.UpgradeManager != null) - { - GameMain.GameSession.Campaign.UpgradeManager.OnUpgradesChanged += ResetCrushDepth; - } + GameMain.GameSession?.Campaign?.UpgradeManager?.OnUpgradesChanged.Register(upgradeEventIdentifier, _ => ResetCrushDepth()); #if CLIENT GameMain.LightManager.OnMapLoaded(); @@ -1527,6 +1527,13 @@ namespace Barotrauma } } + public bool CheckFuel() + { + float fuel = GetItems(true).Where(i => i.HasTag("reactorfuel")).Sum(i => i.Condition); + Info.LowFuel = fuel < 200; + return !Info.LowFuel; + } + public void SaveToXElement(XElement element) { element.Add(new XAttribute("name", Info.Name)); @@ -1534,7 +1541,10 @@ namespace Barotrauma element.Add(new XAttribute("checkval", Rand.Int(int.MaxValue))); element.Add(new XAttribute("price", Info.Price)); element.Add(new XAttribute("initialsuppliesspawned", Info.InitialSuppliesSpawned)); + element.Add(new XAttribute("noitems", Info.NoItems)); + element.Add(new XAttribute("lowfuel", !CheckFuel())); element.Add(new XAttribute("type", Info.Type.ToString())); + element.Add(new XAttribute("ismanuallyoutfitted", Info.IsManuallyOutfitted)); if (Info.IsPlayer && !Info.HasTag(SubmarineTag.Shuttle)) { element.Add(new XAttribute("class", Info.SubmarineClass.ToString())); @@ -1623,7 +1633,6 @@ namespace Barotrauma e.Save(element); } - Info.CheckSubsLeftBehind(element); } @@ -1727,10 +1736,7 @@ namespace Barotrauma outdoorNodes?.Clear(); outdoorNodes = null; - if (GameMain.GameSession?.Campaign?.UpgradeManager != null) - { - GameMain.GameSession.Campaign.UpgradeManager.OnUpgradesChanged -= ResetCrushDepth; - } + GameMain.GameSession?.Campaign?.UpgradeManager?.OnUpgradesChanged?.TryDeregister(upgradeEventIdentifier); if (entityGrid != null) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/SubmarineBody.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/SubmarineBody.cs index c2340805b..673a6218b 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/SubmarineBody.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/SubmarineBody.cs @@ -598,7 +598,12 @@ namespace Barotrauma if (newHull != null) { CoroutineManager.Invoke(() => - character.AnimController.FindHull(newHull.WorldPosition, setSubmarine: true)); + { + if (character != null && !character.Removed) + { + character.AnimController.FindHull(newHull.WorldPosition, setSubmarine: true); + } + }); } return false; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/SubmarineInfo.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/SubmarineInfo.cs index 1e37ab37e..898f27b77 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/SubmarineInfo.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/SubmarineInfo.cs @@ -86,6 +86,21 @@ namespace Barotrauma set; } + public bool NoItems + { + get; + set; + } + + /// + /// Note: Refreshed for loaded submarines when they are saved, when they are loaded, and on round end. If you need to refresh it, please use Submarine.CheckFuel() method! + /// + public bool LowFuel + { + get; + set; + } + public Version GameVersion { get; @@ -94,6 +109,8 @@ namespace Barotrauma public SubmarineType Type { get; set; } + public bool IsManuallyOutfitted { get; set; } + public SubmarineClass SubmarineClass; public OutpostModuleInfo OutpostModuleInfo { get; set; } @@ -272,6 +289,8 @@ namespace Barotrauma Description = original.Description; Price = original.Price; InitialSuppliesSpawned = original.InitialSuppliesSpawned; + NoItems = original.NoItems; + LowFuel = original.LowFuel; GameVersion = original.GameVersion; Type = original.Type; SubmarineClass = original.SubmarineClass; @@ -286,6 +305,7 @@ namespace Barotrauma RecommendedCrewExperience = original.RecommendedCrewExperience; RecommendedCrewSizeMin = original.RecommendedCrewSizeMin; RecommendedCrewSizeMax = original.RecommendedCrewSizeMax; + IsManuallyOutfitted = original.IsManuallyOutfitted; Tags = original.Tags; if (original.OutpostModuleInfo != null) { @@ -335,6 +355,9 @@ namespace Barotrauma Price = SubmarineElement.GetAttributeInt("price", 1000); InitialSuppliesSpawned = SubmarineElement.GetAttributeBool("initialsuppliesspawned", false); + NoItems = SubmarineElement.GetAttributeBool("noitems", false); + LowFuel = SubmarineElement.GetAttributeBool("lowfuel", false); + IsManuallyOutfitted = SubmarineElement.GetAttributeBool("ismanuallyoutfitted", false); GameVersion = new Version(SubmarineElement.GetAttributeString("gameversion", "0.0.0.0")); if (Enum.TryParse(SubmarineElement.GetAttributeString("tags", ""), out SubmarineTag tags)) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Networking/RespawnManager.cs b/Barotrauma/BarotraumaShared/SharedSource/Networking/RespawnManager.cs index b80fd2455..c282b31f2 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Networking/RespawnManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Networking/RespawnManager.cs @@ -284,7 +284,7 @@ namespace Barotrauma.Networking if (hull.Submarine != RespawnShuttle) { continue; } hull.OxygenPercentage = 100.0f; hull.WaterVolume = 0.0f; - hull.BallastFlora?.Kill(); + hull.BallastFlora?.Remove(); } Dictionary characterPositions = new Dictionary(); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Networking/ServerSettings.cs b/Barotrauma/BarotraumaShared/SharedSource/Networking/ServerSettings.cs index 7c612b9ef..bdafe7ea8 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Networking/ServerSettings.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Networking/ServerSettings.cs @@ -1,17 +1,12 @@ -using Microsoft.Xna.Framework; +using Barotrauma.Extensions; +using Barotrauma.IO; +using Microsoft.Xna.Framework; using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.ComponentModel; -using System.Globalization; -using Barotrauma.IO; using System.Linq; -using System.Net; using System.Security.Cryptography; using System.Text; -using System.Xml; -using System.Xml.Linq; -using Barotrauma.Extensions; namespace Barotrauma.Networking { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Physics/PhysicsBody.cs b/Barotrauma/BarotraumaShared/SharedSource/Physics/PhysicsBody.cs index 74da0ce7d..95ce1d5c7 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Physics/PhysicsBody.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Physics/PhysicsBody.cs @@ -405,6 +405,8 @@ namespace Barotrauma FarseerBody.Restitution = limbParams.Restitution; FarseerBody.AngularDamping = limbParams.AngularDamping; FarseerBody.UserData = this; + _collisionCategories = collisionCategory; + _collidesWith = collidesWith; SetTransformIgnoreContacts(position, 0.0f); LastSentPosition = position; list.Add(this); @@ -418,6 +420,8 @@ namespace Barotrauma density = Math.Max(forceDensity ?? element.GetAttributeFloat("density", 10.0f), MinDensity); Enum.TryParse(element.GetAttributeString("bodytype", "Dynamic"), out BodyType bodyType); CreateBody(width, height, radius, density, bodyType, collisionCategory, collidesWith, findNewContacts); + _collisionCategories = collisionCategory; + _collidesWith = collidesWith; FarseerBody.Friction = element.GetAttributeFloat("friction", 0.5f); FarseerBody.Restitution = element.GetAttributeFloat("restitution", 0.05f); FarseerBody.UserData = this; @@ -456,6 +460,8 @@ namespace Barotrauma this.width = width; this.height = height; this.radius = radius; + _collisionCategories = collisionCategory; + _collidesWith = collidesWith; } /// diff --git a/Barotrauma/BarotraumaShared/SharedSource/Settings/GameSettings.cs b/Barotrauma/BarotraumaShared/SharedSource/Settings/GameSettings.cs index c21422874..d13c4975e 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Settings/GameSettings.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Settings/GameSettings.cs @@ -54,7 +54,6 @@ namespace Barotrauma EnableMouseLook = true, ChatOpen = true, CrewMenuOpen = true, - CampaignDisclaimerShown = false, EditorDisclaimerShown = false, ShowOffensiveServerPrompt = true, TutorialSkipWarning = true, @@ -127,7 +126,6 @@ namespace Barotrauma public bool EnableMouseLook; public bool ChatOpen; public bool CrewMenuOpen; - public bool CampaignDisclaimerShown; public bool EditorDisclaimerShown; public bool ShowOffensiveServerPrompt; public bool TutorialSkipWarning; @@ -304,6 +302,7 @@ namespace Barotrauma { InputType.Down, Keys.S }, { InputType.Left, Keys.A }, { InputType.Right, Keys.D }, + { InputType.ToggleInventory, Keys.Q }, { InputType.SelectNextCharacter, Keys.Z }, { InputType.SelectPreviousCharacter, Keys.X }, diff --git a/Barotrauma/BarotraumaShared/SharedSource/Steam/Workshop.cs b/Barotrauma/BarotraumaShared/SharedSource/Steam/Workshop.cs index 7db7254a0..3c274b2c3 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Steam/Workshop.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Steam/Workshop.cs @@ -324,7 +324,10 @@ namespace Barotrauma.Steam using (var copyIndicator = new CopyIndicator(copyIndicatorPath)) { - await CopyDirectory(itemDirectory, modPathDirName ?? modName, itemDirectory, installDir, ShouldCorrectPaths.Yes); + await CopyDirectory(itemDirectory, modPathDirName ?? modName, itemDirectory, installDir, + gameVersion < new Version(0, 18, 3, 0) + ? ShouldCorrectPaths.Yes + : ShouldCorrectPaths.No); string fileListDestPath = Path.Combine(installDir, ContentPackage.FileListFileName); XDocument fileListDest = XMLExtensions.TryLoadXml(fileListDestPath); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Text/LocalizedString/FallbackLString.cs b/Barotrauma/BarotraumaShared/SharedSource/Text/LocalizedString/FallbackLString.cs index d4ca38f08..d16faf612 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Text/LocalizedString/FallbackLString.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Text/LocalizedString/FallbackLString.cs @@ -6,11 +6,11 @@ namespace Barotrauma private readonly LocalizedString primary; private readonly LocalizedString fallback; - private bool primaryIsLoaded = false; - + public bool PrimaryIsLoaded { get; private set; } + public FallbackLString(LocalizedString primary, LocalizedString fallback) { - if (primary is FallbackLString {primary: { } innerPrimary, fallback: { } innerFallback}) + if (primary is FallbackLString { primary: { } innerPrimary, fallback: { } innerFallback }) { this.primary = innerPrimary; this.fallback = innerFallback.Fallback(fallback); @@ -27,18 +27,27 @@ namespace Barotrauma return base.MustRetrieveValue() || MustRetrieveValue(primary) || MustRetrieveValue(fallback) - || primaryIsLoaded != primary.Loaded; + || PrimaryIsLoaded != primary.Loaded; } public override bool Loaded => primary.Loaded || fallback.Loaded; public override void RetrieveValue() { cachedValue = primary.Value; - primaryIsLoaded = primary.Loaded; + PrimaryIsLoaded = primary.Loaded; if (!primary.Loaded) { cachedValue = fallback.Value; } } + + public LocalizedString GetLastFallback() + { + if (fallback is FallbackLString innerFallback) + { + return innerFallback.GetLastFallback(); + } + return fallback; + } } } \ No newline at end of file diff --git a/Barotrauma/BarotraumaShared/changelog.txt b/Barotrauma/BarotraumaShared/changelog.txt index 5758bd7e9..635a98b76 100644 --- a/Barotrauma/BarotraumaShared/changelog.txt +++ b/Barotrauma/BarotraumaShared/changelog.txt @@ -1,3 +1,58 @@ +--------------------------------------------------------------------------------------------------------- +v0.18.15.0 +--------------------------------------------------------------------------------------------------------- + +Changes: +- Show a warning when trying to switch to a submarine that's low on fuel or to a submarine that has no manually placed items to prevent softlocking the campaign if you switch to a sub that has no fuel. Whether a submarine is considered to have manually placed items can be set when saving it in the submarine editor (the checkbox "manually outfitted" in the saving dialog). +- Also show the low fuel warning when leaving an outpost without enough fuel. +- Handheld sonars can't detect minerals from inside the sub. +- Changed the plus and minus button in the campaign settings into arrows. The button on the right increases difficulty, which in the case of the starting balance and supplies means reducing them, making the plus and minus buttons misleading. +- Reduced costs of handheld weapon ammunition significantly. +- Slightly reduced effectiveness of harpoons and revolver round to compensate for the cheaper ammo. +- Changed recipes for Handcannon, Assault Rifle and Auto-Shotgun. Weapon crafting is more expensive, to compensate for cheaper ammo. +- Adjusted numerous other recipes and price costs of materials. Previously little used materials (like tin) are now used more. +- Partially reintroduced the "toggle inventory" keybind, now called "toggle entity list". Even though toggling the in-game inventory is no longer possible, the keybind can be used to change the hotkey for toggling the sub editor's entity list. + +Fixes: +- Fixed shuttles getting misplaced when switching and transferring items to a new sub with shuttles. +- Fixed inability to damage items (such as monster eggs) with melee weapons or handheld weapons. +- Fixed "failed to parse the string 'COLOR.GUI.GREEN' to Color" errors when using the submarine upgrade interface in Spanish. +- Fixed items with a projectile component (e.g. bullets, harpoons, syringes) going through external walls when dropped. +- Fixed incorrect value in the "too many lights" error message in the sub editor. +- Fixed lightcomponent not getting refreshed when flipping a lightcomponent horizontally. +- Fixed extra cargo defined in the server settings spawning every round in multiplayer campaign. +- Fixes wreck corpses' damage visuals (bloodstains, burns) not showing client-side. +- Fixed wearables that are worn (or held) in multiple limbs applying skill bonuses multiple times (e.g. when holding a bandolier with both hands). +- Fixed console error when deselecting all target types from a motion sensor in the sub editor. +- Fixed crashing when multi-editing motion sensor targets in the sub editor. +- Fixed one of the path unlock event texts still using the old price (talking about 12,000 mk even though the price is 4,000 mk). +- Fixed money getting incorrectly deducted on the client side when buying items from the vending machine. +- Fixed item transfer taking items from non-interactable containers. +- Made depleted fuel sprite darker to match the rest of the "depleted" items. +- Fixed PUCS's autoinjection feature sometimes causing performance drops in multiplayer. +- Fixed "tried to overwrite a submarine that's not in a local package" error when you delete a sub that's in a local package and try to resave it. +- Fixed misaligned broken duffelbag sprite. +- Fixed full-white x-ray monitors. +- Fixed FPS drop when adjusting character appearance in the campaign setup menu or server lobby. +- Fixed tunnels at the beginning or end of the level sometimes being too narrow to pass through (especially with a small sub) when there's no outpost at that side of the level. +- Fixed missing background sprite in duct block. +- Fixed water particles not showing up when water is flowing down a duct block. +- Fixed changing the scale of resizeable structures (such as background doors) messing up the outline of linked subs in the sub editor. +- Fixed non-player-team interactable items getting transferred on sub switch. +- Fixed ballast flora root emitting particles when damaged client-side, even if it's already been destroyed. +- Fixed recycle recipes for Piercing Ammunition Box and Pulse Tri-Laser Fuel Box. +- Fixed friendly fire and karma always showing up as disabled on dedicated servers in the server list. +- Fixed some lights (e.g. vending machines, neon lights, holographics displays) looking different in the sub editor than they do in-game. +- Fixed undocked shuttles remaining undocked if you save and start a new game with the same submarine during the same session. Restarting the game fixed the issue though. +- Fixed sonar markers going crazy if the start and end locations have the same name + added some more variety to location names to prevent duplicate location names. +- Fixed multiediting an ItemComponent modifying all the components of that type in all the selected items (e.g. when editing the 1st light component of a switch, all lights in all switches would be edited). +- Fixed spineling spikes fired by a human with spineling genes not damaging any human characters (enemies in PvP, pirates in pirate missions) when friendly fire is disabled. +- Fixed melee weapons not damaging structures from outside. + +Modding: +- Fixed removing a door mid-round crashing the game. Does not affect the vanilla game, because doors are never removed mid-round. +- Fixed having a vending machine in your inventory crashing the game due to the 0-capacity input inventory of the machine. + --------------------------------------------------------------------------------------------------------- v0.18.12.0 --------------------------------------------------------------------------------------------------------- diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a40f994b6..770fb43d8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,7 +3,10 @@ Welcome to Barotrauma's GitHub repository! If you're here to report an issue or to contribute to the development, please read the instructions below before you do. ## I have a question! -Please check [our FAQ](https://barotraumagame.com/faq/) in case the question has already been answered. If not, you can post the question on the [Barotrauma discussion forum](https://undertowgames.com/forum/viewforum.php?f=17) or stop by at our [Discord Server](https://discord.gg/undertow). +Please check [our FAQ](https://barotraumagame.com/faq/) in case the question has already been answered. If not, you can post the question on the [GitHub discussions](https://github.com/Regalis11/Barotrauma/discussions) or stop by at our [Discord Server](https://discord.gg/undertow). + +## I have an idea or feature request! +This GitHub issue tracker is only intended for bug reports. Ideas, development suggestions and feature requests should be posted in the [GitHub discussions](https://github.com/Regalis11/Barotrauma/discussions). ## Reporting a bug If you've encountered a bug, you can report it in the [issue tracker](https://github.com/Regalis11/Barotrauma/issues). Please follow the instructions in the issue template to make it easier for us to diagnose and fix the issue. diff --git a/Libraries/MonoGame.Framework/Src/MonoGame.Framework/Input/Mouse.SDL.cs b/Libraries/MonoGame.Framework/Src/MonoGame.Framework/Input/Mouse.SDL.cs index f558a7120..ad79e4b62 100644 --- a/Libraries/MonoGame.Framework/Src/MonoGame.Framework/Input/Mouse.SDL.cs +++ b/Libraries/MonoGame.Framework/Src/MonoGame.Framework/Input/Mouse.SDL.cs @@ -15,16 +15,16 @@ namespace Microsoft.Xna.Framework.Input { return PrimaryWindow.Handle; } - + private static void PlatformSetWindowHandle(IntPtr windowHandle) { } private static MouseState PlatformGetState(GameWindow window) { - int x, y; var winFlags = Sdl.Window.GetWindowFlags(window.Handle); - var state = Sdl.Mouse.GetGlobalState(out x, out y); + var state = Sdl.Mouse.GetState(out int x, out int y); + var globalState = Sdl.Mouse.GetGlobalState(out int globalX, out int globalY); if ((winFlags & Sdl.Window.State.MouseFocus) != 0) { @@ -42,8 +42,8 @@ namespace Microsoft.Xna.Framework.Input { // Window does not have mouse focus, we need to manually get the position var clientBounds = window.ClientBounds; - window.MouseState.X = x - clientBounds.X; - window.MouseState.Y = y - clientBounds.Y; + window.MouseState.X = globalX - clientBounds.X; + window.MouseState.Y = globalY - clientBounds.Y; } return window.MouseState; @@ -53,7 +53,6 @@ namespace Microsoft.Xna.Framework.Input { PrimaryWindow.MouseState.X = x; PrimaryWindow.MouseState.Y = y; - Sdl.Mouse.WarpInWindow(PrimaryWindow.Handle, x, y); }