Faction Test 100.13.0.0
This commit is contained in:
@@ -444,10 +444,20 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (Limb limb in Limbs)
|
||||
{
|
||||
if (limb == null || limb.IsSevered || limb.ActiveSprite == null || !limb.DoesFlip) { continue; }
|
||||
Vector2 spriteOrigin = limb.ActiveSprite.Origin;
|
||||
spriteOrigin.X = limb.ActiveSprite.SourceRect.Width - spriteOrigin.X;
|
||||
limb.ActiveSprite.Origin = spriteOrigin;
|
||||
if (limb == null || limb.IsSevered || !limb.DoesMirror) { continue; }
|
||||
|
||||
FlipSprite(limb.DeformSprite?.Sprite ?? limb.Sprite);
|
||||
foreach (var conditionalSprite in limb.ConditionalSprites)
|
||||
{
|
||||
FlipSprite(conditionalSprite.DeformableSprite?.Sprite ?? conditionalSprite.Sprite);
|
||||
}
|
||||
}
|
||||
static void FlipSprite(Sprite sprite)
|
||||
{
|
||||
if (sprite == null) { return; }
|
||||
Vector2 spriteOrigin = sprite.Origin;
|
||||
spriteOrigin.X = sprite.SourceRect.Width - spriteOrigin.X;
|
||||
sprite.Origin = spriteOrigin;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -946,7 +946,7 @@ namespace Barotrauma
|
||||
var headPreset = obj as HeadPreset;
|
||||
if (info.Head.Preset != headPreset)
|
||||
{
|
||||
info.Head = new HeadInfo(info, headPreset)
|
||||
info.Head = new HeadInfo(info, headPreset, info.Head.HairIndex, info.Head.BeardIndex, info.Head.MoustacheIndex, info.Head.FaceAttachmentIndex)
|
||||
{
|
||||
SkinColor = info.Head.SkinColor,
|
||||
HairColor = info.Head.HairColor,
|
||||
|
||||
@@ -640,8 +640,7 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
forceAfflictionContainerUpdate = true;
|
||||
currentDisplayedAfflictions = GetAllAfflictions(mergeSameAfflictions: true)
|
||||
.FindAll(a => a.ShouldShowIcon(Character) && a.Prefab.Icon != null);
|
||||
currentDisplayedAfflictions = GetAllAfflictions(mergeSameAfflictions: true, predicate: a => a.ShouldShowIcon(Character) && a.Prefab.Icon != null);
|
||||
currentDisplayedAfflictions.Sort((a1, a2) =>
|
||||
{
|
||||
int dmgPerSecond = Math.Sign(a1.DamagePerSecond - a2.DamagePerSecond);
|
||||
@@ -1275,7 +1274,7 @@ namespace Barotrauma
|
||||
//displaying an affliction we no longer have -> dirty
|
||||
foreach ((Affliction affliction, float strength) in displayedAfflictions)
|
||||
{
|
||||
if (!afflictions.Any(a => a.Key == affliction)) { return true; }
|
||||
if (afflictions.None(a => a.Key == affliction && a.Key.ShouldShowIcon(Character))) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -83,6 +83,7 @@ namespace Barotrauma
|
||||
GUIListBox conversationList = lastMessageBox.FindChild("conversationlist", true) as GUIListBox;
|
||||
Debug.Assert(conversationList != null);
|
||||
|
||||
DisableButtons(conversationList.Content.GetAllChildren<GUIButton>(), selectedButton: null);
|
||||
// gray out the last text block
|
||||
if (conversationList.Content.Children.LastOrDefault() is GUILayoutGroup lastElement)
|
||||
{
|
||||
@@ -269,14 +270,7 @@ namespace Barotrauma
|
||||
if (actionInstance != null)
|
||||
{
|
||||
actionInstance.selectedOption = selectedOption;
|
||||
foreach (GUIButton otherButton in optionButtons)
|
||||
{
|
||||
otherButton.CanBeFocused = false;
|
||||
if (otherButton != btn)
|
||||
{
|
||||
otherButton.TextBlock.OverrideTextColor(Color.DarkGray * 0.8f);
|
||||
}
|
||||
}
|
||||
DisableButtons(optionButtons, btn);
|
||||
btn.ExternalHighlight = true;
|
||||
return true;
|
||||
}
|
||||
@@ -286,14 +280,7 @@ namespace Barotrauma
|
||||
SendResponse(actionId.Value, selectedOption);
|
||||
btn.CanBeFocused = false;
|
||||
btn.ExternalHighlight = true;
|
||||
foreach (GUIButton otherButton in optionButtons)
|
||||
{
|
||||
otherButton.CanBeFocused = false;
|
||||
if (otherButton != btn)
|
||||
{
|
||||
otherButton.TextBlock.OverrideTextColor(Color.DarkGray * 0.8f);
|
||||
}
|
||||
}
|
||||
DisableButtons(optionButtons, btn);
|
||||
return true;
|
||||
}
|
||||
//should not happen
|
||||
@@ -305,6 +292,18 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public static void SelectOption(ushort actionId, int option)
|
||||
{
|
||||
if (lastMessageBox.UserData is Pair<string, ushort> userData)
|
||||
{
|
||||
if (userData.Second != actionId) { return; }
|
||||
|
||||
GUIListBox conversationList = lastMessageBox.FindChild("conversationlist", true) as GUIListBox;
|
||||
Debug.Assert(conversationList != null);
|
||||
DisableButtons(conversationList.Content.GetAllChildren<GUIButton>(), (btn) => btn.UserData is int i && i == option);
|
||||
}
|
||||
}
|
||||
|
||||
private static Tuple<Vector2, Point> GetSizes(DialogTypes dialogTypes)
|
||||
{
|
||||
return dialogTypes switch
|
||||
@@ -383,6 +382,30 @@ namespace Barotrauma
|
||||
return buttons;
|
||||
}
|
||||
|
||||
private static void DisableButtons(IEnumerable<GUIButton> buttons, GUIButton selectedButton)
|
||||
{
|
||||
DisableButtons(buttons, (btn) => btn == selectedButton);
|
||||
}
|
||||
|
||||
private static void DisableButtons(IEnumerable<GUIButton> buttons, Func<GUIButton, bool> isSelectedButton)
|
||||
{
|
||||
foreach (GUIButton btn in buttons)
|
||||
{
|
||||
if (btn.CanBeFocused)
|
||||
{
|
||||
btn.CanBeFocused = false;
|
||||
if (isSelectedButton(btn))
|
||||
{
|
||||
btn.Selected = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
btn.TextBlock.OverrideTextColor(Color.DarkGray * 0.8f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void SendResponse(UInt16 actionId, int selectedOption)
|
||||
{
|
||||
IWriteMessage outmsg = new WriteOnlyMessage();
|
||||
|
||||
@@ -608,47 +608,56 @@ namespace Barotrauma
|
||||
}
|
||||
break;
|
||||
case NetworkEventType.CONVERSATION:
|
||||
UInt16 identifier = msg.ReadUInt16();
|
||||
string eventSprite = msg.ReadString();
|
||||
byte dialogType = msg.ReadByte();
|
||||
bool continueConversation = msg.ReadBoolean();
|
||||
UInt16 speakerId = msg.ReadUInt16();
|
||||
string text = msg.ReadString();
|
||||
bool fadeToBlack = msg.ReadBoolean();
|
||||
byte optionCount = msg.ReadByte();
|
||||
List<string> options = new List<string>();
|
||||
for (int i = 0; i < optionCount; i++)
|
||||
{
|
||||
options.Add(msg.ReadString());
|
||||
}
|
||||
|
||||
byte endCount = msg.ReadByte();
|
||||
int[] endings = new int[endCount];
|
||||
for (int i = 0; i < endCount; i++)
|
||||
{
|
||||
endings[i] = msg.ReadByte();
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(text) && optionCount == 0)
|
||||
{
|
||||
GUIMessageBox.MessageBoxes.ForEachMod(mb =>
|
||||
UInt16 identifier = msg.ReadUInt16();
|
||||
string eventSprite = msg.ReadString();
|
||||
byte dialogType = msg.ReadByte();
|
||||
bool continueConversation = msg.ReadBoolean();
|
||||
UInt16 speakerId = msg.ReadUInt16();
|
||||
string text = msg.ReadString();
|
||||
bool fadeToBlack = msg.ReadBoolean();
|
||||
byte optionCount = msg.ReadByte();
|
||||
List<string> options = new List<string>();
|
||||
for (int i = 0; i < optionCount; i++)
|
||||
{
|
||||
if (mb.UserData is Pair<string, UInt16> pair && pair.First == "ConversationAction" && pair.Second == identifier)
|
||||
options.Add(msg.ReadString());
|
||||
}
|
||||
|
||||
byte endCount = msg.ReadByte();
|
||||
int[] endings = new int[endCount];
|
||||
for (int i = 0; i < endCount; i++)
|
||||
{
|
||||
endings[i] = msg.ReadByte();
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(text) && optionCount == 0)
|
||||
{
|
||||
GUIMessageBox.MessageBoxes.ForEachMod(mb =>
|
||||
{
|
||||
(mb as GUIMessageBox)?.Close();
|
||||
}
|
||||
});
|
||||
if (mb.UserData is Pair<string, UInt16> pair && pair.First == "ConversationAction" && pair.Second == identifier)
|
||||
{
|
||||
(mb as GUIMessageBox)?.Close();
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
ConversationAction.CreateDialog(text, Entity.FindEntityByID(speakerId) as Character, options, endings, eventSprite, identifier, fadeToBlack, (ConversationAction.DialogTypes)dialogType, continueConversation);
|
||||
}
|
||||
if (Entity.FindEntityByID(speakerId) is Character speaker)
|
||||
{
|
||||
speaker.CampaignInteractionType = CampaignMode.InteractionType.None;
|
||||
speaker.SetCustomInteract(null, null);
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
case NetworkEventType.CONVERSATION_SELECTED_OPTION:
|
||||
{
|
||||
ConversationAction.CreateDialog(text, Entity.FindEntityByID(speakerId) as Character, options, endings, eventSprite, identifier, fadeToBlack, (ConversationAction.DialogTypes)dialogType, continueConversation);
|
||||
UInt16 identifier = msg.ReadUInt16();
|
||||
int selectedOption = msg.ReadByte() - 1;
|
||||
ConversationAction.SelectOption(identifier, selectedOption);
|
||||
break;
|
||||
}
|
||||
if (Entity.FindEntityByID(speakerId) is Character speaker)
|
||||
{
|
||||
speaker.CampaignInteractionType = CampaignMode.InteractionType.None;
|
||||
speaker.SetCustomInteract(null, null);
|
||||
}
|
||||
break;
|
||||
case NetworkEventType.MISSION:
|
||||
Identifier missionIdentifier = msg.ReadIdentifier();
|
||||
int locationIndex = msg.ReadInt32();
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace Barotrauma
|
||||
return RichString.Rich(TextManager.GetWithVariable("missionreward", "[reward]", "‖color:gui.orange‖"+rewardText+"‖end‖"));
|
||||
}
|
||||
|
||||
public RichString GetReputationRewardText(Location currLocation)
|
||||
public RichString GetReputationRewardText()
|
||||
{
|
||||
List<LocalizedString> reputationRewardTexts = new List<LocalizedString>();
|
||||
foreach (var reputationReward in ReputationRewards)
|
||||
@@ -46,7 +46,7 @@ namespace Barotrauma
|
||||
FactionPrefab targetFaction;
|
||||
if (reputationReward.Key == "location" )
|
||||
{
|
||||
targetFaction = currLocation.Faction?.Prefab;
|
||||
targetFaction = OriginLocation.Faction?.Prefab;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1563,7 +1563,7 @@ namespace Barotrauma
|
||||
descriptionText += "\n\n" + missionMessage;
|
||||
}
|
||||
RichString rewardText = mission.GetMissionRewardText(Submarine.MainSub);
|
||||
RichString reputationText = mission.GetReputationRewardText(mission.Locations[0]);
|
||||
RichString reputationText = mission.GetReputationRewardText();
|
||||
|
||||
Func<string, string> wrapMissionText(GUIFont font)
|
||||
{
|
||||
@@ -1773,7 +1773,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (UpgradePrefab prefab in categoryData.Prefabs)
|
||||
{
|
||||
var frame = UpgradeStore.CreateUpgradeFrame(prefab, categoryData.Category, campaign, new RectTransform(new Vector2(1f, 0.3f), upgradePanel.Content.RectTransform), addBuyButton: false);
|
||||
var frame = UpgradeStore.CreateUpgradeFrame(prefab, categoryData.Category, campaign, new RectTransform(new Vector2(1f, 0.3f), upgradePanel.Content.RectTransform), addBuyButton: false).Frame;
|
||||
UpgradeStore.UpdateUpgradeEntry(frame, prefab, categoryData.Category, campaign);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -796,7 +796,7 @@ namespace Barotrauma
|
||||
CharacterInfo? ownCharacterInfo = Character.Controlled?.Info ?? GameMain.Client?.CharacterInfo;
|
||||
if (ownCharacterInfo is null) { return false; }
|
||||
|
||||
return info == ownCharacterInfo;
|
||||
return info.GetIdentifierUsingOriginalName() == ownCharacterInfo.GetIdentifierUsingOriginalName();
|
||||
}
|
||||
|
||||
public static bool CanManageTalents(CharacterInfo targetInfo)
|
||||
|
||||
@@ -4,7 +4,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
@@ -93,6 +92,16 @@ namespace Barotrauma
|
||||
Repairs
|
||||
}
|
||||
|
||||
private enum UpgradeStoreUserData
|
||||
{
|
||||
BuyButton,
|
||||
BuyButtonLayout,
|
||||
ProgressBarLayout,
|
||||
IncreaseLabel,
|
||||
PriceLabel,
|
||||
MaterialCostList
|
||||
}
|
||||
|
||||
public UpgradeStore(CampaignUI campaignUI, GUIComponent parent)
|
||||
{
|
||||
WaitForServerUpdate = false;
|
||||
@@ -605,7 +614,7 @@ namespace Barotrauma
|
||||
GUILayoutGroup textLayout = new GUILayoutGroup(rectT(0.8f - repairIcon.RectTransform.RelativeSize.X, 1, contentLayout)) { Stretch = true };
|
||||
new GUITextBlock(rectT(1, 0, textLayout), title, font: GUIStyle.SubHeadingFont) { CanBeFocused = false, AutoScaleHorizontal = true };
|
||||
new GUITextBlock(rectT(1, 0, textLayout), TextManager.FormatCurrency(price));
|
||||
GUILayoutGroup buyButtonLayout = new GUILayoutGroup(rectT(0.2f, 1, contentLayout), childAnchor: Anchor.Center) { UserData = "buybutton" };
|
||||
GUILayoutGroup buyButtonLayout = new GUILayoutGroup(rectT(0.2f, 1, contentLayout), childAnchor: Anchor.Center) { UserData = UpgradeStoreUserData.BuyButtonLayout };
|
||||
new GUIButton(rectT(0.7f, 0.5f, buyButtonLayout), string.Empty, style: "RepairBuyButton") { Enabled = PlayerBalance >= price && !isDisabled, OnClicked = onPressed };
|
||||
contentLayout.Recalculate();
|
||||
buyButtonLayout.Recalculate();
|
||||
@@ -955,7 +964,7 @@ namespace Barotrauma
|
||||
frames.Add(CreateUpgradeEntry(rectT(1f, 0.25f, parent.Content), currentOrPending.UpgradePreviewSprite,
|
||||
item.PendingItemSwap != null ? TextManager.GetWithVariable("upgrades.pendingitem", "[itemname]", name) : TextManager.GetWithVariable("upgrades.installeditem", "[itemname]", nameWithQuantity),
|
||||
currentOrPending.Description,
|
||||
0, null, addBuyButton: canUninstall, addProgressBar: false, buttonStyle: "WeaponUninstallButton"));
|
||||
0, null, addBuyButton: canUninstall, addProgressBar: false, buttonStyle: "WeaponUninstallButton").Frame);
|
||||
|
||||
if (canUninstall && frames.Last().FindChild(c => c is GUIButton, recursive: true) is GUIButton refundButton)
|
||||
{
|
||||
@@ -992,11 +1001,11 @@ namespace Barotrauma
|
||||
|
||||
int price = isPurchased || replacement == item.Prefab ? 0 : replacement.SwappableItem.GetPrice(Campaign.Map?.CurrentLocation) * linkedItems.Count();
|
||||
|
||||
frames.Add(CreateUpgradeEntry(rectT(1f, 0.25f, parent.Content), replacement.UpgradePreviewSprite, replacement.Name, replacement.Description,
|
||||
price, replacement,
|
||||
addBuyButton: true,
|
||||
frames.Add(CreateUpgradeEntry(rectT(1f, 0.25f, parent.Content), replacement.UpgradePreviewSprite, replacement.Name, replacement.Description,
|
||||
price, replacement,
|
||||
addBuyButton: true,
|
||||
addProgressBar: false,
|
||||
buttonStyle: isPurchased ? "WeaponInstallButton" : "StoreAddToCrateButton"));
|
||||
buttonStyle: isPurchased ? "WeaponInstallButton" : "StoreAddToCrateButton").Frame);
|
||||
|
||||
if (!(frames.Last().FindChild(c => c is GUIButton, recursive: true) is GUIButton buyButton)) { continue; }
|
||||
if (PlayerBalance >= price)
|
||||
@@ -1086,13 +1095,23 @@ namespace Barotrauma
|
||||
};
|
||||
}
|
||||
|
||||
public static GUIFrame CreateUpgradeFrame(UpgradePrefab prefab, UpgradeCategory category, CampaignMode campaign, RectTransform rectTransform, bool addBuyButton = true)
|
||||
public readonly record struct BuyButtonFrame(GUILayoutGroup Layout, GUIListBox MaterialCostList, GUIButton BuyButton, GUITextBlock PriceText);
|
||||
public readonly record struct ProgressBarFrame(GUITextBlock ProgressText, GUIProgressBar ProgressBar);
|
||||
|
||||
public readonly record struct UpgradeFrame(GUIFrame Frame,
|
||||
GUIImage Icon,
|
||||
GUITextBlock Name,
|
||||
GUITextBlock Description,
|
||||
Option<BuyButtonFrame> BuyButton,
|
||||
Option<ProgressBarFrame> ProgressBar);
|
||||
|
||||
public static UpgradeFrame CreateUpgradeFrame(UpgradePrefab prefab, UpgradeCategory category, CampaignMode campaign, RectTransform rectTransform, bool addBuyButton = true)
|
||||
{
|
||||
int price = prefab.Price.GetBuyPrice(campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation, characterList);
|
||||
return CreateUpgradeEntry(rectTransform, prefab.Sprite, prefab.Name, prefab.Description, price, new CategoryData(category, prefab), addBuyButton, upgradePrefab: prefab, currentLevel: campaign.UpgradeManager.GetUpgradeLevel(prefab, category));
|
||||
}
|
||||
|
||||
public static GUIFrame CreateUpgradeEntry(RectTransform parent, Sprite sprite, LocalizedString title, LocalizedString body, int price, object? userData, bool addBuyButton = true, bool addProgressBar = true, string buttonStyle = "UpgradeBuyButton", UpgradePrefab? upgradePrefab = null, int currentLevel = 0)
|
||||
public static UpgradeFrame CreateUpgradeEntry(RectTransform parent, Sprite sprite, LocalizedString title, LocalizedString body, int price, object? userData, bool addBuyButton = true, bool addProgressBar = true, string buttonStyle = "UpgradeBuyButton", UpgradePrefab? upgradePrefab = null, int currentLevel = 0)
|
||||
{
|
||||
float progressBarHeight = 0.25f;
|
||||
|
||||
@@ -1110,21 +1129,26 @@ namespace Barotrauma
|
||||
* |------------------------------------------------------------------|
|
||||
*/
|
||||
GUIFrame prefabFrame = new GUIFrame(parent, style: "ListBoxElement") { SelectedColor = Color.Transparent, UserData = userData };
|
||||
GUILayoutGroup prefabLayout = new GUILayoutGroup(rectT(0.98f, 0.95f, prefabFrame, Anchor.Center), isHorizontal: true) { Stretch = true };
|
||||
GUILayoutGroup imageLayout = new GUILayoutGroup(rectT(new Point(prefabLayout.Rect.Height, prefabLayout.Rect.Height), prefabLayout), childAnchor: Anchor.Center);
|
||||
var icon = new GUIImage(rectT(0.9f, 0.9f, imageLayout, scaleBasis: ScaleBasis.BothHeight), sprite, scaleToFit: true) { CanBeFocused = false };
|
||||
GUILayoutGroup textLayout = new GUILayoutGroup(rectT(0.8f - imageLayout.RectTransform.RelativeSize.X, 1, prefabLayout));
|
||||
var name = new GUITextBlock(rectT(1, 0.25f, textLayout), RichString.Rich(title), font: GUIStyle.SubHeadingFont) { AutoScaleHorizontal = true, AutoScaleVertical = true, Padding = Vector4.Zero };
|
||||
GUILayoutGroup descriptionLayout = new GUILayoutGroup(rectT(1, 0.75f - progressBarHeight, textLayout));
|
||||
var description = new GUITextBlock(rectT(1, 1, descriptionLayout), body, font: GUIStyle.SmallFont, wrap: true, textAlignment: Alignment.TopLeft) { Padding = Vector4.Zero };
|
||||
GUILayoutGroup? progressLayout = null;
|
||||
GUILayoutGroup mainLayout = new GUILayoutGroup(rectT(0.98f, 0.95f, prefabFrame, Anchor.Center), isHorizontal: false);
|
||||
GUILayoutGroup prefabLayout = new GUILayoutGroup(rectT(1f, addBuyButton ? 0.65f : 1f, mainLayout, Anchor.Center), isHorizontal: true) { Stretch = true };
|
||||
GUILayoutGroup imageLayout = new GUILayoutGroup(rectT(new Point(prefabLayout.Rect.Height, prefabLayout.Rect.Height), prefabLayout), childAnchor: Anchor.Center);
|
||||
var icon = new GUIImage(rectT(0.9f, 0.9f, imageLayout, scaleBasis: ScaleBasis.BothHeight), sprite, scaleToFit: true) { CanBeFocused = false };
|
||||
GUILayoutGroup textLayout = new GUILayoutGroup(rectT(1f - imageLayout.RectTransform.RelativeSize.X, 1, prefabLayout));
|
||||
var name = new GUITextBlock(rectT(1, 0.25f, textLayout), RichString.Rich(title), font: GUIStyle.SubHeadingFont) { AutoScaleHorizontal = true, AutoScaleVertical = true, Padding = Vector4.Zero };
|
||||
GUILayoutGroup descriptionLayout = new GUILayoutGroup(rectT(1, 0.75f - progressBarHeight, textLayout));
|
||||
var description = new GUITextBlock(rectT(1, 1, descriptionLayout), body, font: GUIStyle.SmallFont, wrap: true, textAlignment: Alignment.TopLeft) { Padding = Vector4.Zero };
|
||||
GUILayoutGroup? progressLayout = null;
|
||||
GUILayoutGroup? buyButtonLayout = null;
|
||||
|
||||
Option<BuyButtonFrame> buyButtonOption = Option<BuyButtonFrame>.None();
|
||||
Option<ProgressBarFrame> progressBarOption = Option<ProgressBarFrame>.None();
|
||||
|
||||
if (addProgressBar)
|
||||
{
|
||||
progressLayout = new GUILayoutGroup(rectT(1, 0.25f, textLayout), isHorizontal: true, childAnchor: Anchor.CenterLeft) { UserData = "progressbar" };
|
||||
new GUIProgressBar(rectT(0.8f, 0.75f, progressLayout), 0.0f, GUIStyle.Orange);
|
||||
new GUITextBlock(rectT(0.2f, 1, progressLayout), string.Empty, font: GUIStyle.SmallFont, textAlignment: Alignment.Center) { Padding = Vector4.Zero };
|
||||
progressLayout = new GUILayoutGroup(rectT(1, 0.25f, textLayout), isHorizontal: true, childAnchor: Anchor.CenterLeft) { UserData = UpgradeStoreUserData.ProgressBarLayout };
|
||||
GUITextBlock progressText = new GUITextBlock(rectT(0.15f, 1, progressLayout), string.Empty, font: GUIStyle.SmallFont, textAlignment: Alignment.Center) { Padding = Vector4.Zero };
|
||||
GUIProgressBar progressBar = new GUIProgressBar(rectT(0.85f, 0.75f, progressLayout), 0.0f, GUIStyle.Orange);
|
||||
progressBarOption = Option.Some(new ProgressBarFrame(progressText, progressBar));
|
||||
}
|
||||
|
||||
if (addBuyButton)
|
||||
@@ -1132,12 +1156,33 @@ namespace Barotrauma
|
||||
var formattedPrice = TextManager.FormatCurrency(Math.Abs(price));
|
||||
//negative price = refund
|
||||
if (price < 0) { formattedPrice = "+" + formattedPrice; }
|
||||
buyButtonLayout = new GUILayoutGroup(rectT(0.2f, 1, prefabLayout), childAnchor: Anchor.TopCenter) { UserData = "buybutton" };
|
||||
var priceText = new GUITextBlock(rectT(1, 0.2f, buyButtonLayout), formattedPrice, textAlignment: Alignment.Center)
|
||||
buyButtonLayout = new GUILayoutGroup(rectT(1f, 0.35f, mainLayout), isHorizontal: true) { UserData = UpgradeStoreUserData.BuyButtonLayout };;
|
||||
|
||||
GUIListBox materialCostList;
|
||||
if (upgradePrefab is not null)
|
||||
{
|
||||
var increaseText = new GUITextBlock(rectT(imageLayout.RectTransform.RelativeSize.X, 1f, buyButtonLayout), "", textAlignment: Alignment.Center, font: GUIStyle.SubHeadingFont)
|
||||
{
|
||||
UserData = UpgradeStoreUserData.IncreaseLabel
|
||||
};
|
||||
UpdateUpgradePercentageText(increaseText, upgradePrefab, currentLevel);
|
||||
materialCostList = new GUIListBox(rectT(0.65f - imageLayout.RectTransform.RelativeSize.X, 1f, buyButtonLayout), isHorizontal: true, style: null);
|
||||
}
|
||||
else
|
||||
{
|
||||
materialCostList = new GUIListBox(rectT(0.65f, 1f, buyButtonLayout), isHorizontal: true, style: null);
|
||||
}
|
||||
|
||||
materialCostList.Visible = false;
|
||||
materialCostList.UserData = UpgradeStoreUserData.MaterialCostList;
|
||||
|
||||
var priceText = new GUITextBlock(rectT(0.2f, 1f, buyButtonLayout), formattedPrice)
|
||||
{
|
||||
UserData = UpgradeStoreUserData.PriceLabel,
|
||||
//prices on swappable items are always visible, upgrade prices are enabled in UpdateUpgradeEntry for purchasable upgrades
|
||||
Visible = userData is ItemPrefab
|
||||
};
|
||||
|
||||
if (price < 0)
|
||||
{
|
||||
priceText.TextColor = GUIStyle.Green;
|
||||
@@ -1146,15 +1191,13 @@ namespace Barotrauma
|
||||
{
|
||||
priceText.Text = string.Empty;
|
||||
}
|
||||
new GUIButton(rectT(0.7f, 0.5f, buyButtonLayout), string.Empty, style: buttonStyle)
|
||||
GUIButton buyButton = new GUIButton(rectT(0.15f, 1f, buyButtonLayout), string.Empty, style: buttonStyle)
|
||||
{
|
||||
UserData = UpgradeStoreUserData.BuyButton,
|
||||
Enabled = false
|
||||
};
|
||||
if (upgradePrefab != null)
|
||||
{
|
||||
var increaseText = new GUITextBlock(rectT(1, 0.2f, buyButtonLayout), "", textAlignment: Alignment.Center);
|
||||
UpdateUpgradePercentageText(increaseText, upgradePrefab, currentLevel);
|
||||
}
|
||||
|
||||
buyButtonOption = Option.Some(new BuyButtonFrame(buyButtonLayout, materialCostList, buyButton, priceText));
|
||||
}
|
||||
|
||||
description.CalculateHeightFromText();
|
||||
@@ -1180,7 +1223,7 @@ namespace Barotrauma
|
||||
progressLayout?.Recalculate();
|
||||
buyButtonLayout?.Recalculate();
|
||||
|
||||
return prefabFrame;
|
||||
return new UpgradeFrame(prefabFrame, icon, name, description, buyButtonOption, progressBarOption);
|
||||
}
|
||||
|
||||
private static void UpdateUpgradePercentageText(GUITextBlock text, UpgradePrefab upgradePrefab, int currentLevel)
|
||||
@@ -1202,31 +1245,21 @@ namespace Barotrauma
|
||||
Submarine? sub = GameMain.GameSession?.Submarine ?? Submarine.MainSub;
|
||||
if (Campaign is null || sub is null) { return; }
|
||||
|
||||
GUIFrame prefabFrame = CreateUpgradeFrame(prefab, category, Campaign, rectT(1f, 0.25f, parent));
|
||||
var prefabLayout = prefabFrame.GetChild<GUILayoutGroup>();
|
||||
GUILayoutGroup[] childLayouts = prefabLayout.GetAllChildren<GUILayoutGroup>().ToArray();
|
||||
var imageLayout = childLayouts[0];
|
||||
var icon = imageLayout.GetChild<GUIImage>();
|
||||
var textLayout = childLayouts[1];
|
||||
var name = textLayout.GetChild<GUITextBlock>();
|
||||
GUILayoutGroup[] textChildLayouts = textLayout.GetAllChildren<GUILayoutGroup>().ToArray();
|
||||
var descriptionLayout = textChildLayouts[0];
|
||||
var description = descriptionLayout.GetChild<GUITextBlock>();
|
||||
var progressLayout = textChildLayouts[1];
|
||||
var buyButtonLayout = childLayouts[2];
|
||||
var buyButton = buyButtonLayout.GetChild<GUIButton>();
|
||||
UpgradeFrame prefabFrame = CreateUpgradeFrame(prefab, category, Campaign, rectT(1f, 0.4f, parent));
|
||||
|
||||
if (!prefabFrame.BuyButton.TryUnwrap(out BuyButtonFrame buyButtonFrame)) { return; }
|
||||
|
||||
if (!HasPermission || !prefab.IsApplicable(submarine.Info) || (itemsOnSubmarine != null && !itemsOnSubmarine.Any(it => category.CanBeApplied(it, prefab))))
|
||||
{
|
||||
prefabFrame.Enabled = false;
|
||||
description.Enabled = false;
|
||||
name.Enabled = false;
|
||||
icon.Color = Color.Gray;
|
||||
buyButton.Enabled = false;
|
||||
buyButtonLayout.UserData = null; // prevent UpdateUpgradeEntry() from enabling the button
|
||||
prefabFrame.Frame.Enabled = false;
|
||||
prefabFrame.Description.Enabled = false;
|
||||
prefabFrame.Name.Enabled = false;
|
||||
prefabFrame.Icon.Color = Color.Gray;
|
||||
buyButtonFrame.BuyButton.Enabled = false;
|
||||
buyButtonFrame.Layout.UserData = null; // prevent UpdateUpgradeEntry() from enabling the button
|
||||
}
|
||||
|
||||
buyButton.OnClicked += (button, o) =>
|
||||
buyButtonFrame.BuyButton.OnClicked += (button, o) =>
|
||||
{
|
||||
LocalizedString promptBody = TextManager.GetWithVariables("Upgrades.PurchasePromptBody",
|
||||
("[upgradename]", prefab.Name),
|
||||
@@ -1245,7 +1278,7 @@ namespace Barotrauma
|
||||
return true;
|
||||
};
|
||||
|
||||
UpdateUpgradeEntry(prefabFrame, prefab, category, Campaign);
|
||||
UpdateUpgradeEntry(prefabFrame.Frame, prefab, category, Campaign);
|
||||
}
|
||||
|
||||
private void CreateItemTooltip(MapEntity entity)
|
||||
@@ -1628,7 +1661,7 @@ namespace Barotrauma
|
||||
|
||||
int maxLevel = prefab.GetMaxLevelForCurrentSub();
|
||||
LocalizedString progressText = TextManager.GetWithVariables("upgrades.progressformat", ("[level]", currentLevel.ToString()), ("[maxlevel]", maxLevel.ToString()));
|
||||
if (prefabFrame.FindChild("progressbar", true) is { } progressParent)
|
||||
if (prefabFrame.FindChild(UpgradeStoreUserData.ProgressBarLayout, true) is { } progressParent)
|
||||
{
|
||||
GUIProgressBar bar = progressParent.GetChild<GUIProgressBar>();
|
||||
if (bar != null)
|
||||
@@ -1641,36 +1674,111 @@ namespace Barotrauma
|
||||
if (block != null) { block.Text = progressText; }
|
||||
}
|
||||
|
||||
if (prefabFrame.FindChild("buybutton", true) is { } buttonParent)
|
||||
if (prefabFrame.FindChild(UpgradeStoreUserData.BuyButtonLayout, true) is not { } buttonParent) { return; }
|
||||
|
||||
GUITextBlock priceLabel = (GUITextBlock)buttonParent.FindChild(UpgradeStoreUserData.PriceLabel, recursive: true);
|
||||
priceLabel.Visible = true;
|
||||
int price = prefab.Price.GetBuyPrice(campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation, characterList);
|
||||
|
||||
if (!WaitForServerUpdate)
|
||||
{
|
||||
List<GUITextBlock> textBlocks = buttonParent.GetAllChildren<GUITextBlock>().ToList();
|
||||
|
||||
GUITextBlock priceLabel = textBlocks[0];
|
||||
priceLabel.Visible = true;
|
||||
int price = prefab.Price.GetBuyPrice(campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation, characterList);
|
||||
|
||||
if (priceLabel != null && !WaitForServerUpdate)
|
||||
priceLabel.Text = TextManager.FormatCurrency(price);
|
||||
if (currentLevel >= maxLevel)
|
||||
{
|
||||
priceLabel.Text = TextManager.FormatCurrency(price);
|
||||
if (currentLevel >= maxLevel)
|
||||
{
|
||||
priceLabel.Text = TextManager.Get("Upgrade.MaxedUpgrade");
|
||||
}
|
||||
priceLabel.Text = TextManager.Get("Upgrade.MaxedUpgrade");
|
||||
}
|
||||
}
|
||||
|
||||
GUIButton button = buttonParent.GetChild<GUIButton>();
|
||||
if (button != null)
|
||||
if (buttonParent.FindChild(UpgradeStoreUserData.IncreaseLabel, recursive: true) is GUITextBlock increaseLabel && !WaitForServerUpdate)
|
||||
{
|
||||
UpdateUpgradePercentageText(increaseLabel, prefab, currentLevel);
|
||||
}
|
||||
|
||||
bool isMax = currentLevel >= maxLevel;
|
||||
|
||||
if (buttonParent.FindChild(UpgradeStoreUserData.BuyButton, recursive: true) is GUIButton button)
|
||||
{
|
||||
bool canBuy = !WaitForServerUpdate && !isMax && campaign.GetBalance() >= price && prefab.HasResourcesToUpgrade(Character.Controlled, currentLevel + 1);
|
||||
|
||||
button.Enabled = canBuy;
|
||||
}
|
||||
|
||||
if (prefabFrame.FindChild(UpgradeStoreUserData.MaterialCostList, true) is GUIListBox itemList)
|
||||
{
|
||||
if (isMax)
|
||||
{
|
||||
button.Enabled = currentLevel < maxLevel;
|
||||
if (WaitForServerUpdate || campaign.GetBalance() < price)
|
||||
{
|
||||
button.Enabled = false;
|
||||
}
|
||||
itemList.Visible = false;
|
||||
}
|
||||
GUITextBlock increaseLabel = textBlocks[1];
|
||||
if (increaseLabel != null && !WaitForServerUpdate)
|
||||
else
|
||||
{
|
||||
UpdateUpgradePercentageText(increaseLabel, prefab, currentLevel);
|
||||
CreateMaterialCosts(itemList, prefab, currentLevel + 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void CreateMaterialCosts(GUIListBox list, UpgradePrefab prefab, int targetLevel)
|
||||
{
|
||||
list.Content.ClearChildren();
|
||||
List<Item> allItems = Character.Controlled?.Inventory?.FindAllItems(recursive: true) ?? new List<Item>();
|
||||
|
||||
var resources = prefab.GetApplicableResources(targetLevel);
|
||||
|
||||
foreach (ApplicableResourceCollection collection in resources)
|
||||
{
|
||||
list.Visible = true;
|
||||
|
||||
int length = collection.MatchingItems.Length;
|
||||
|
||||
if (length is 0) { continue; }
|
||||
|
||||
ItemPrefab defaultItemPrefab = collection.MatchingItems.First();
|
||||
|
||||
GUILayoutGroup wrapperLayout = new GUILayoutGroup(rectT(0.25f, 1f, list.Content));
|
||||
|
||||
GUIFrame itemFrame = new GUIFrame(rectT(1f, 1f, wrapperLayout), style: null)
|
||||
{
|
||||
ToolTip = defaultItemPrefab.Name
|
||||
};
|
||||
|
||||
bool hasItems = collection.Cost.Amount <= allItems.Count(collection.Cost.MatchesItem);
|
||||
|
||||
Sprite icon = defaultItemPrefab.InventoryIcon ?? prefab.Sprite;
|
||||
Color iconColor = defaultItemPrefab.InventoryIcon is null ? defaultItemPrefab.SpriteColor : defaultItemPrefab.InventoryIconColor;
|
||||
|
||||
GUIImage itemIcon = new GUIImage(new RectTransform(Vector2.One, itemFrame.RectTransform, scaleBasis: ScaleBasis.Smallest, anchor: Anchor.Center), sprite: icon, scaleToFit: true)
|
||||
{
|
||||
Color = hasItems ? iconColor : iconColor * 0.9f,
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
// item count text
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.5f), itemIcon.RectTransform, anchor: Anchor.BottomRight), $"{collection.Count}", font: GUIStyle.Font, textAlignment: Alignment.BottomRight)
|
||||
{
|
||||
Shadow = true,
|
||||
CanBeFocused = false,
|
||||
Padding = Vector4.Zero,
|
||||
TextColor = hasItems ? Color.White : GUIStyle.Red,
|
||||
};
|
||||
|
||||
if (length is 1) { continue; }
|
||||
|
||||
// we have more than 1 item, show a "slideshow" of the items
|
||||
|
||||
float index = 0f;
|
||||
GUICustomComponent customComponent = new GUICustomComponent(rectT(1f, 1f, itemFrame), null, (deltaTime, component) =>
|
||||
{
|
||||
index += deltaTime / 3f;
|
||||
if (index > length) { index = 0; }
|
||||
|
||||
ItemPrefab currentPrefab = collection.MatchingItems[(int)MathF.Floor(index)];
|
||||
Sprite icon = currentPrefab.InventoryIcon ?? prefab.Sprite;
|
||||
Color iconColor = currentPrefab.InventoryIcon is null ? currentPrefab.SpriteColor : currentPrefab.InventoryIconColor;
|
||||
itemIcon.Sprite = icon;
|
||||
itemIcon.Color = hasItems ? iconColor : iconColor * 0.9f;
|
||||
itemFrame.ToolTip = currentPrefab.Name;
|
||||
})
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -735,8 +735,8 @@ namespace Barotrauma
|
||||
{
|
||||
Client.Quit();
|
||||
Client = null;
|
||||
MainMenuScreen.Select();
|
||||
}
|
||||
MainMenuScreen.Select();
|
||||
|
||||
if (connectCommand.EndpointOrLobby.TryGet(out ulong lobbyId))
|
||||
{
|
||||
@@ -1099,37 +1099,6 @@ namespace Barotrauma
|
||||
GameSession = null;
|
||||
}
|
||||
|
||||
public void ShowEditorDisclaimer()
|
||||
{
|
||||
var msgBox = new GUIMessageBox(TextManager.Get("EditorDisclaimerTitle"), TextManager.Get("EditorDisclaimerText"));
|
||||
var linkHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), msgBox.Content.RectTransform)) { Stretch = true, RelativeSpacing = 0.025f };
|
||||
linkHolder.RectTransform.MaxSize = new Point(int.MaxValue, linkHolder.Rect.Height);
|
||||
List<(LocalizedString Caption, string Url)> links = new List<(LocalizedString, string)>()
|
||||
{
|
||||
(TextManager.Get("EditorDisclaimerWikiLink"), TextManager.Get("EditorDisclaimerWikiUrl").Fallback("https://barotraumagame.com/wiki").Value),
|
||||
(TextManager.Get("EditorDisclaimerDiscordLink"), TextManager.Get("EditorDisclaimerDiscordUrl").Fallback("https://discordapp.com/invite/undertow").Value),
|
||||
};
|
||||
foreach (var link in links)
|
||||
{
|
||||
new GUIButton(new RectTransform(new Vector2(1.0f, 0.2f), linkHolder.RectTransform), link.Caption, style: "MainMenuGUIButton", textAlignment: Alignment.Left)
|
||||
{
|
||||
UserData = link.Url,
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
ShowOpenUrlInWebBrowserPrompt(userdata as string);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
msgBox.InnerFrame.RectTransform.MinSize = new Point(0,
|
||||
msgBox.InnerFrame.Rect.Height + linkHolder.Rect.Height + msgBox.Content.AbsoluteSpacing * 2 + 10);
|
||||
var config = GameSettings.CurrentConfig;
|
||||
config.EditorDisclaimerShown = true;
|
||||
GameSettings.SetCurrentConfig(config);
|
||||
GameSettings.SaveCurrentConfig();
|
||||
}
|
||||
|
||||
public void ShowBugReporter()
|
||||
{
|
||||
if (GUIMessageBox.VisibleBox != null && GUIMessageBox.VisibleBox.UserData as string == "bugreporter")
|
||||
|
||||
@@ -85,7 +85,6 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
private SinglePlayerCampaign(string mapSeed, CampaignSettings settings) : base(GameModePreset.SinglePlayerCampaign, settings)
|
||||
{
|
||||
CampaignMetadata = new CampaignMetadata();
|
||||
UpgradeManager = new UpgradeManager(this);
|
||||
Settings = settings;
|
||||
InitFactions();
|
||||
@@ -107,18 +106,16 @@ namespace Barotrauma
|
||||
private SinglePlayerCampaign(XElement element) : base(GameModePreset.SinglePlayerCampaign, CampaignSettings.Empty)
|
||||
{
|
||||
IsFirstRound = false;
|
||||
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "metadata":
|
||||
CampaignMetadata = new CampaignMetadata(subElement);
|
||||
CampaignMetadata.Load(subElement);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CampaignMetadata ??= new CampaignMetadata();
|
||||
InitFactions();
|
||||
|
||||
foreach (var subElement in element.Elements())
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Barotrauma
|
||||
|
||||
private readonly GameMode gameMode;
|
||||
|
||||
private readonly Dictionary<Faction, float> initialFactionReputations = new Dictionary<Faction, float>();
|
||||
private readonly Dictionary<Identifier, float> initialFactionReputations = new Dictionary<Identifier, float>();
|
||||
|
||||
public GUILayoutGroup ButtonArea { get; private set; }
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (Faction faction in campaignMode.Factions)
|
||||
{
|
||||
initialFactionReputations.Add(faction, faction.Reputation.Value);
|
||||
initialFactionReputations.Add(faction.Prefab.Identifier, faction.Reputation.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -312,17 +312,26 @@ namespace Barotrauma
|
||||
var missionDescription = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
|
||||
RichString.Rich(missionMessage), wrap: true);
|
||||
int reward = displayedMission.GetReward(Submarine.MainSub);
|
||||
if (selectedMissions.Contains(displayedMission) && displayedMission.Completed && reward > 0)
|
||||
if (selectedMissions.Contains(displayedMission) && displayedMission.Completed)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), RichString.Rich(displayedMission.GetMissionRewardText(Submarine.MainSub)));
|
||||
if (GameMain.IsMultiplayer && Character.Controlled is { } controlled)
|
||||
RichString reputationText = displayedMission.GetReputationRewardText();
|
||||
if (!reputationText.IsNullOrEmpty())
|
||||
{
|
||||
var (share, percentage, _) = Mission.GetRewardShare(controlled.Wallet.RewardDistribution, GameSession.GetSessionCrewCharacters(CharacterType.Player).Where(c => c != controlled), Option<int>.Some(reward));
|
||||
if (share > 0)
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), reputationText);
|
||||
}
|
||||
|
||||
if (reward > 0)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), RichString.Rich(displayedMission.GetMissionRewardText(Submarine.MainSub)));
|
||||
if (GameMain.IsMultiplayer && Character.Controlled is { } controlled)
|
||||
{
|
||||
string shareFormatted = string.Format(CultureInfo.InvariantCulture, "{0:N0}", share);
|
||||
RichString yourShareString = RichString.Rich(TextManager.GetWithVariables("crewwallet.missionreward.get", ("[money]", $"{shareFormatted}"), ("[share]", $"{percentage}")));
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), yourShareString);
|
||||
var (share, percentage, _) = Mission.GetRewardShare(controlled.Wallet.RewardDistribution, GameSession.GetSessionCrewCharacters(CharacterType.Player).Where(c => c != controlled), Option<int>.Some(reward));
|
||||
if (share > 0)
|
||||
{
|
||||
string shareFormatted = string.Format(CultureInfo.InvariantCulture, "{0:N0}", share);
|
||||
RichString yourShareString = RichString.Rich(TextManager.GetWithVariables("crewwallet.missionreward.get", ("[money]", $"{shareFormatted}"), ("[share]", $"{percentage}")));
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), yourShareString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -400,26 +409,10 @@ namespace Barotrauma
|
||||
};
|
||||
reputationList.ContentBackground.Color = Color.Transparent;
|
||||
|
||||
/*if (startLocation.Type.HasOutpost && startLocation.Reputation != null)
|
||||
{
|
||||
var iconStyle = GUIStyle.GetComponentStyle("LocationReputationIcon");
|
||||
var locationFrame = CreateReputationElement(
|
||||
reputationList.Content,
|
||||
startLocation.Name,
|
||||
startLocation.Reputation.Value, startLocation.Reputation.NormalizedValue, initialLocationReputation,
|
||||
startLocation.Type.Name, "",
|
||||
iconStyle?.GetDefaultSprite(), startLocation.Type.GetPortrait(0), iconStyle?.Color ?? Color.White);
|
||||
CreatePathUnlockElement(locationFrame, null, startLocation);
|
||||
}*/
|
||||
|
||||
foreach (Faction faction in campaignMode.Factions.OrderBy(f => f.Prefab.MenuOrder).ThenBy(f => f.Prefab.Name))
|
||||
{
|
||||
float initialReputation = faction.Reputation.Value;
|
||||
if (initialFactionReputations.ContainsKey(faction))
|
||||
{
|
||||
initialReputation = initialFactionReputations[faction];
|
||||
}
|
||||
else
|
||||
if (!initialFactionReputations.TryGetValue(faction.Prefab.Identifier, out initialReputation))
|
||||
{
|
||||
DebugConsole.AddWarning($"Could not determine reputation change for faction \"{faction.Prefab.Name}\" (faction was not present at the start of the round).");
|
||||
}
|
||||
@@ -454,50 +447,60 @@ namespace Barotrauma
|
||||
|
||||
void CreatePathUnlockElement(GUIComponent reputationFrame, Faction faction, Location location)
|
||||
{
|
||||
if (GameMain.GameSession?.Campaign?.Map != null)
|
||||
if (GameMain.GameSession?.Campaign?.Map == null) { return; }
|
||||
|
||||
IEnumerable<LocationConnection> connectionsBetweenBiomes =
|
||||
GameMain.GameSession.Campaign.Map.Connections.Where(c => c.Locations[0].Biome != c.Locations[1].Biome);
|
||||
|
||||
foreach (LocationConnection connection in connectionsBetweenBiomes)
|
||||
{
|
||||
foreach (LocationConnection connection in GameMain.GameSession.Campaign.Map.Connections)
|
||||
if (!connection.Locked || (!connection.Locations[0].Discovered && !connection.Locations[1].Discovered)) { continue; }
|
||||
|
||||
//don't show the "reputation required to unlock" text if another connection between the biomes has already been unlocked
|
||||
if (connectionsBetweenBiomes.Where(c => !c.Locked).Any(c =>
|
||||
(c.Locations[0].Biome == connection.Locations[0].Biome && c.Locations[1].Biome == connection.Locations[1].Biome) ||
|
||||
(c.Locations[1].Biome == connection.Locations[0].Biome && c.Locations[0].Biome == connection.Locations[1].Biome)))
|
||||
{
|
||||
if (!connection.Locked || (!connection.Locations[0].Discovered && !connection.Locations[1].Discovered)) { continue; }
|
||||
continue;
|
||||
}
|
||||
|
||||
var gateLocation = connection.Locations[0].IsGateBetweenBiomes ? connection.Locations[0] : connection.Locations[1];
|
||||
var unlockEvent = EventPrefab.GetUnlockPathEvent(gateLocation.LevelData.Biome.Identifier, gateLocation.Faction);
|
||||
var gateLocation = connection.Locations[0].IsGateBetweenBiomes ? connection.Locations[0] : connection.Locations[1];
|
||||
var unlockEvent = EventPrefab.GetUnlockPathEvent(gateLocation.LevelData.Biome.Identifier, gateLocation.Faction);
|
||||
|
||||
if (unlockEvent == null) { continue; }
|
||||
if (unlockEvent.Faction.IsEmpty)
|
||||
if (unlockEvent == null) { continue; }
|
||||
if (unlockEvent.Faction.IsEmpty)
|
||||
{
|
||||
if (location == null || gateLocation != location) { continue; }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (faction == null || faction.Prefab.Identifier != unlockEvent.Faction) { continue; }
|
||||
}
|
||||
|
||||
if (unlockEvent != null)
|
||||
{
|
||||
Reputation unlockReputation = gateLocation.Reputation;
|
||||
Faction unlockFaction = null;
|
||||
if (!unlockEvent.Faction.IsEmpty)
|
||||
{
|
||||
if (location == null || gateLocation != location) { continue; }
|
||||
unlockFaction = GameMain.GameSession.Campaign.Factions.Find(f => f.Prefab.Identifier == unlockEvent.Faction);
|
||||
unlockReputation = unlockFaction?.Reputation;
|
||||
}
|
||||
else
|
||||
float normalizedUnlockReputation = MathUtils.InverseLerp(unlockReputation.MinReputation, unlockReputation.MaxReputation, unlockEvent.UnlockPathReputation);
|
||||
RichString unlockText = RichString.Rich(TextManager.GetWithVariables(
|
||||
"lockedpathreputationrequirement",
|
||||
("[reputation]", Reputation.GetFormattedReputationText(normalizedUnlockReputation, unlockEvent.UnlockPathReputation, addColorTags: true)),
|
||||
("[biomename]", $"‖color:gui.orange‖{connection.LevelData.Biome.DisplayName}‖end‖")));
|
||||
var unlockInfoPanel = new GUITextBlock(new RectTransform(new Vector2(0.8f, 0.0f), reputationFrame.RectTransform, Anchor.BottomCenter) { MinSize = new Point(0, GUI.IntScale(30)), AbsoluteOffset = new Point(0, GUI.IntScale(3)) },
|
||||
unlockText, style: "GUIButtonRound", textAlignment: Alignment.Center, textColor: GUIStyle.TextColorNormal);
|
||||
unlockInfoPanel.Color = Color.Lerp(unlockInfoPanel.Color, Color.Black, 0.8f);
|
||||
unlockInfoPanel.UserData = "unlockinfo";
|
||||
if (unlockInfoPanel.TextSize.X > unlockInfoPanel.Rect.Width * 0.7f)
|
||||
{
|
||||
if (faction == null || faction.Prefab.Identifier != unlockEvent.Faction) { continue; }
|
||||
}
|
||||
|
||||
if (unlockEvent != null)
|
||||
{
|
||||
Reputation unlockReputation = gateLocation.Reputation;
|
||||
Faction unlockFaction = null;
|
||||
if (!unlockEvent.Faction.IsEmpty)
|
||||
{
|
||||
unlockFaction = GameMain.GameSession.Campaign.Factions.Find(f => f.Prefab.Identifier == unlockEvent.Faction);
|
||||
unlockReputation = unlockFaction?.Reputation;
|
||||
}
|
||||
float normalizedUnlockReputation = MathUtils.InverseLerp(unlockReputation.MinReputation, unlockReputation.MaxReputation, unlockEvent.UnlockPathReputation);
|
||||
RichString unlockText = RichString.Rich(TextManager.GetWithVariables(
|
||||
"lockedpathreputationrequirement",
|
||||
("[reputation]", Reputation.GetFormattedReputationText(normalizedUnlockReputation, unlockEvent.UnlockPathReputation, addColorTags: true)),
|
||||
("[biomename]", $"‖color:gui.orange‖{connection.LevelData.Biome.DisplayName}‖end‖")));
|
||||
var unlockInfoPanel = new GUITextBlock(new RectTransform(new Vector2(0.8f, 0.0f), reputationFrame.RectTransform, Anchor.BottomCenter) { MinSize = new Point(0, GUI.IntScale(30)), AbsoluteOffset = new Point(0, GUI.IntScale(3)) },
|
||||
unlockText, style: "GUIButtonRound", textAlignment: Alignment.Center, textColor: GUIStyle.TextColorNormal);
|
||||
unlockInfoPanel.Color = Color.Lerp(unlockInfoPanel.Color, Color.Black, 0.8f);
|
||||
unlockInfoPanel.UserData = "unlockinfo";
|
||||
if (unlockInfoPanel.TextSize.X > unlockInfoPanel.Rect.Width * 0.7f)
|
||||
{
|
||||
unlockInfoPanel.Font = GUIStyle.SmallFont;
|
||||
}
|
||||
unlockInfoPanel.Font = GUIStyle.SmallFont;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -965,7 +965,7 @@ namespace Barotrauma
|
||||
break;
|
||||
case QuickUseAction.PutToEquippedItem:
|
||||
//order by the condition of the contained item to prefer putting into the item with the emptiest ammo/battery/tank
|
||||
foreach (Item heldItem in character.HeldItems.OrderBy(it => it.ContainedItems.FirstOrDefault()?.Condition ?? 0.0f))
|
||||
foreach (Item heldItem in character.HeldItems.OrderBy(it => it.GetComponent<ItemContainer>()?.GetContainedIndicatorState() ?? 0.0f))
|
||||
{
|
||||
if (heldItem.OwnInventory == null) { continue; }
|
||||
//don't allow swapping if we're moving items into an item with 1 slot holding a stack of items
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void DrawElectricity(SpriteBatch spriteBatch)
|
||||
{
|
||||
if (timer <= 0.0f) { return; }
|
||||
if (timer <= 0.0f && Screen.Selected is { IsEditor: false }) { return; }
|
||||
for (int i = 0; i < nodes.Count; i++)
|
||||
{
|
||||
if (nodes[i].Length <= 1.0f) { continue; }
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using static Barotrauma.Inventory;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -250,6 +252,83 @@ namespace Barotrauma.Items.Components
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public float GetContainedIndicatorState()
|
||||
{
|
||||
if (ShowConditionInContainedStateIndicator)
|
||||
{
|
||||
return item.Condition / item.MaxCondition;
|
||||
}
|
||||
|
||||
int targetSlot = Math.Max(ContainedStateIndicatorSlot, 0);
|
||||
if (targetSlot >= Inventory.Capacity) { return 0.0f; }
|
||||
|
||||
var containedItems = Inventory.GetItemsAt(targetSlot);
|
||||
if (containedItems == null) { return 0.0f; }
|
||||
|
||||
Item containedItem = containedItems.FirstOrDefault();
|
||||
if (ShowTotalStackCapacityInContainedStateIndicator)
|
||||
{
|
||||
// No item on the defined slot, check if the items on other slots can be used.
|
||||
containedItem ??=
|
||||
containedItems.FirstOrDefault() ??
|
||||
Inventory.AllItems.FirstOrDefault(it => CanBeContained(it, targetSlot));
|
||||
if (containedItem == null) { return 0.0f; }
|
||||
|
||||
int ignoredItemCount = 0;
|
||||
var subContainableItems = AllSubContainableItems;
|
||||
float capacity = GetMaxStackSize(targetSlot);
|
||||
if (subContainableItems != null)
|
||||
{
|
||||
bool useMainContainerCapacity = true;
|
||||
foreach (Item it in Inventory.AllItems)
|
||||
{
|
||||
// Ignore all items in the sub containers.
|
||||
foreach (RelatedItem ri in subContainableItems)
|
||||
{
|
||||
if (ri.MatchesItem(containedItem))
|
||||
{
|
||||
// The target item is in a subcontainer -> inverse the logic.
|
||||
useMainContainerCapacity = false;
|
||||
break;
|
||||
}
|
||||
if (ri.MatchesItem(it))
|
||||
{
|
||||
ignoredItemCount++;
|
||||
}
|
||||
}
|
||||
if (!useMainContainerCapacity) { break; }
|
||||
}
|
||||
if (useMainContainerCapacity)
|
||||
{
|
||||
capacity *= MainContainerCapacity;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ignore all items in the main container.
|
||||
ignoredItemCount = Inventory.AllItems.Count(it => subContainableItems.Any(ri => !ri.MatchesItem(it)));
|
||||
capacity *= Capacity - MainContainerCapacity;
|
||||
}
|
||||
}
|
||||
int itemCount = Inventory.AllItems.Count() - ignoredItemCount;
|
||||
return Math.Min(itemCount / Math.Max(capacity, 1), 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (containedItem != null && (Inventory.Capacity == 1 || HasSubContainers))
|
||||
{
|
||||
int maxStackSize = Math.Min(containedItem.Prefab.MaxStackSize, GetMaxStackSize(targetSlot));
|
||||
if (maxStackSize > 1 || containedItem.Prefab.HideConditionBar)
|
||||
{
|
||||
return containedItems.Count() / (float)maxStackSize;
|
||||
}
|
||||
}
|
||||
return Inventory.Capacity == 1 || ContainedStateIndicatorSlot > -1 ?
|
||||
(containedItem == null ? 0.0f : containedItem.Condition / containedItem.MaxCondition) :
|
||||
Inventory.EmptySlotCount / (float)Inventory.Capacity;
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
|
||||
{
|
||||
if (hideItems || (item.body != null && !item.body.Enabled)) { return; }
|
||||
|
||||
@@ -14,8 +14,6 @@ namespace Barotrauma.Items.Components
|
||||
private CoroutineHandle resetPredictionCoroutine;
|
||||
private float resetPredictionTimer;
|
||||
|
||||
private float currentBrightness;
|
||||
|
||||
public Vector2 DrawSize
|
||||
{
|
||||
get { return new Vector2(Light.Range * 2, Light.Range * 2); }
|
||||
@@ -29,14 +27,21 @@ namespace Barotrauma.Items.Components
|
||||
Light.Position = ParentBody != null ? ParentBody.Position : item.Position;
|
||||
}
|
||||
|
||||
partial void SetLightSourceState(bool enabled, float brightness)
|
||||
partial void SetLightSourceState(bool enabled, float? brightness)
|
||||
{
|
||||
if (Light == null) { return; }
|
||||
Light.Enabled = enabled;
|
||||
currentBrightness = brightness;
|
||||
if (brightness.HasValue)
|
||||
{
|
||||
lightBrightness = brightness.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
lightBrightness = enabled ? 1.0f : 0.0f;
|
||||
}
|
||||
if (enabled)
|
||||
{
|
||||
Light.Color = LightColor.Multiply(brightness);
|
||||
Light.Color = LightColor.Multiply(lightBrightness);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -519,7 +519,10 @@ namespace Barotrauma.Items.Components
|
||||
Color color = !hasPower ? NoPowerColor : turret.ActiveUser is null ? Color.DimGray : GUIStyle.Green;
|
||||
weaponSprite.Draw(batch, center, color, origin, rotation, scale, SpriteEffects.None);
|
||||
}
|
||||
});
|
||||
})
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
weaponChilds.Add(component, frame);
|
||||
}
|
||||
|
||||
@@ -1181,13 +1181,18 @@ namespace Barotrauma.Items.Components
|
||||
if (dockingPort.Item.Submarine == null) { continue; }
|
||||
if (dockingPort.Item.Submarine.Info.IsWreck) { continue; }
|
||||
// docking ports should be shown even if defined as not, if the submarine is the same as the sonar's
|
||||
if (!dockingPort.Item.Submarine.ShowSonarMarker && dockingPort.Item.Submarine != item.Submarine && !dockingPort.Item.Submarine.Info.IsOutpost) { continue; }
|
||||
if (!dockingPort.Item.Submarine.ShowSonarMarker && dockingPort.Item.Submarine != item.Submarine &&
|
||||
!dockingPort.Item.Submarine.Info.IsOutpost && !dockingPort.Item.Submarine.Info.IsBeacon)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//don't show the docking ports of the opposing team on the sonar
|
||||
if (item.Submarine != null &&
|
||||
item.Submarine != GameMain.NetworkMember?.RespawnManager?.RespawnShuttle &&
|
||||
dockingPort.Item.Submarine != GameMain.NetworkMember?.RespawnManager?.RespawnShuttle &&
|
||||
dockingPort.Item.Submarine.Info.Type != SubmarineType.Outpost)
|
||||
!dockingPort.Item.Submarine.Info.IsOutpost &&
|
||||
!dockingPort.Item.Submarine.Info.IsBeacon)
|
||||
{
|
||||
// specifically checking for friendlyNPC seems more logical here
|
||||
if (dockingPort.Item.Submarine.TeamID != item.Submarine.TeamID && dockingPort.Item.Submarine.TeamID != CharacterTeamType.FriendlyNPC) { continue; }
|
||||
|
||||
@@ -1603,88 +1603,7 @@ namespace Barotrauma
|
||||
|
||||
if (itemContainer != null && itemContainer.ShowContainedStateIndicator && itemContainer.Capacity > 0)
|
||||
{
|
||||
float containedState = 0.0f;
|
||||
if (itemContainer.ShowConditionInContainedStateIndicator)
|
||||
{
|
||||
containedState = item.Condition / item.MaxCondition;
|
||||
}
|
||||
else
|
||||
{
|
||||
int targetSlot = Math.Max(itemContainer.ContainedStateIndicatorSlot, 0);
|
||||
ItemSlot containedItemSlot = null;
|
||||
if (targetSlot < itemContainer.Inventory.slots.Length)
|
||||
{
|
||||
containedItemSlot = itemContainer.Inventory.slots[targetSlot];
|
||||
}
|
||||
if (containedItemSlot != null)
|
||||
{
|
||||
Item containedItem = containedItemSlot.FirstOrDefault();
|
||||
if (itemContainer.ShowTotalStackCapacityInContainedStateIndicator)
|
||||
{
|
||||
if (containedItem == null)
|
||||
{
|
||||
// No item on the defined slot, check if the items on other slots can be used.
|
||||
containedItem = containedItemSlot.FirstOrDefault() ?? itemContainer.Inventory.AllItems.FirstOrDefault(it => itemContainer.CanBeContained(it, targetSlot));
|
||||
}
|
||||
if (containedItem != null)
|
||||
{
|
||||
int ignoredItemCount = 0;
|
||||
var subContainableItems = itemContainer.AllSubContainableItems;
|
||||
float capacity = itemContainer.GetMaxStackSize(targetSlot);
|
||||
if (subContainableItems != null)
|
||||
{
|
||||
bool useMainContainerCapacity = true;
|
||||
foreach (Item it in itemContainer.Inventory.AllItems)
|
||||
{
|
||||
// Ignore all items in the sub containers.
|
||||
foreach (RelatedItem ri in subContainableItems)
|
||||
{
|
||||
if (ri.MatchesItem(containedItem))
|
||||
{
|
||||
// The target item is in a subcontainer -> inverse the logic.
|
||||
useMainContainerCapacity = false;
|
||||
break;
|
||||
}
|
||||
if (ri.MatchesItem(it))
|
||||
{
|
||||
ignoredItemCount++;
|
||||
}
|
||||
}
|
||||
if (!useMainContainerCapacity) { break; }
|
||||
}
|
||||
if (useMainContainerCapacity)
|
||||
{
|
||||
capacity *= itemContainer.MainContainerCapacity;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ignore all items in the main container.
|
||||
ignoredItemCount = itemContainer.Inventory.AllItems.Count(it => subContainableItems.Any(ri => !ri.MatchesItem(it)));
|
||||
capacity *= itemContainer.Capacity - itemContainer.MainContainerCapacity;
|
||||
}
|
||||
}
|
||||
int itemCount = itemContainer.Inventory.AllItems.Count() - ignoredItemCount;
|
||||
containedState = Math.Min(itemCount / Math.Max(capacity, 1), 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
containedState = itemContainer.Inventory.Capacity == 1 || itemContainer.ContainedStateIndicatorSlot > -1 ?
|
||||
(containedItem == null ? 0.0f : containedItem.Condition / containedItem.MaxCondition) :
|
||||
itemContainer.Inventory.slots.Count(i => !i.Empty()) / (float)itemContainer.Inventory.capacity;
|
||||
|
||||
if (containedItem != null && (itemContainer.Inventory.Capacity == 1 || itemContainer.HasSubContainers))
|
||||
{
|
||||
int maxStackSize = Math.Min(containedItem.Prefab.MaxStackSize, itemContainer.GetMaxStackSize(targetSlot));
|
||||
if (maxStackSize > 1 || containedItem.Prefab.HideConditionBar)
|
||||
{
|
||||
containedState = containedItemSlot.Items.Count / (float)maxStackSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float containedState = itemContainer.GetContainedIndicatorState();
|
||||
int dir = slot.SubInventoryDir;
|
||||
Rectangle containedIndicatorArea = new Rectangle(rect.X,
|
||||
dir < 0 ? rect.Bottom + HUDLayoutSettings.Padding / 2 : rect.Y - HUDLayoutSettings.Padding / 2 - ContainedIndicatorHeight, rect.Width, ContainedIndicatorHeight);
|
||||
@@ -1807,6 +1726,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void DrawItemStateIndicator(
|
||||
SpriteBatch spriteBatch, Inventory inventory,
|
||||
Sprite indicatorSprite, Sprite emptyIndicatorSprite, Rectangle containedIndicatorArea, float containedState,
|
||||
|
||||
@@ -260,16 +260,16 @@ namespace Barotrauma
|
||||
|
||||
public override void UpdatePlacing(Camera cam)
|
||||
{
|
||||
Vector2 position = Submarine.MouseToWorldGrid(cam, Submarine.MainSub);
|
||||
|
||||
if (PlayerInput.SecondaryMouseButtonClicked())
|
||||
{
|
||||
Selected = null;
|
||||
return;
|
||||
}
|
||||
|
||||
var potentialContainer = MapEntity.GetPotentialContainer(cam.ScreenToWorld(PlayerInput.MousePosition));
|
||||
|
||||
var potentialContainer = MapEntity.GetPotentialContainer(position);
|
||||
|
||||
Vector2 position = Submarine.MouseToWorldGrid(cam, Submarine.MainSub);
|
||||
if (!ResizeHorizontal && !ResizeVertical)
|
||||
{
|
||||
if (PlayerInput.PrimaryMouseButtonClicked() && GUI.MouseOn == null)
|
||||
|
||||
@@ -293,11 +293,11 @@ namespace Barotrauma
|
||||
GUI.DrawRectangle(spriteBatch,
|
||||
new Vector2(drawRect.X, -drawRect.Y),
|
||||
new Vector2(rect.Width, rect.Height),
|
||||
Color.Blue * alpha, false, (ID % 255) * 0.000001f, (int)Math.Max(1.5f / Screen.Selected.Cam.Zoom, 1.0f));
|
||||
Color.Blue * alpha, false, (ID % 255) * 0.000001f, (int)Math.Max(MathF.Ceiling(1.5f / Screen.Selected.Cam.Zoom), 1.0f));
|
||||
|
||||
GUI.DrawRectangle(spriteBatch,
|
||||
new Rectangle(drawRect.X, -drawRect.Y, rect.Width, rect.Height),
|
||||
GUIStyle.Red * ((100.0f - OxygenPercentage) / 400.0f) * alpha, true, 0, (int)Math.Max(1.5f / Screen.Selected.Cam.Zoom, 1.0f));
|
||||
GUIStyle.Red * ((100.0f - OxygenPercentage) / 400.0f) * alpha, true, 0, (int)Math.Max(MathF.Ceiling(1.5f / Screen.Selected.Cam.Zoom), 1.0f));
|
||||
|
||||
if (GameMain.DebugDraw)
|
||||
{
|
||||
|
||||
@@ -544,9 +544,6 @@ namespace Barotrauma
|
||||
|
||||
Vector2 rectCenter = new Vector2(rect.Center.X, rect.Center.Y);
|
||||
Vector2 viewOffset = DrawOffset + drawOffsetNoise;
|
||||
|
||||
|
||||
bool cursorOnOverlay = false;
|
||||
if (HighlightedLocation != null)
|
||||
{
|
||||
Vector2 highlightedLocationDrawPos = rectCenter + (HighlightedLocation.MapPosition + viewOffset) * zoom;
|
||||
@@ -574,52 +571,41 @@ namespace Barotrauma
|
||||
{
|
||||
locationInfoRt.Pivot = locationInfoRt.Pivot == Pivot.TopLeft ? Pivot.TopRight : Pivot.BottomRight;
|
||||
}
|
||||
|
||||
Rectangle highlightedLocationRect = new Rectangle(highlightedLocationDrawPos.ToPoint(), new Point(GUI.IntScale(25)));
|
||||
Rectangle overlayRect = Rectangle.Union(highlightedLocationRect, locationInfoRt.Rect);
|
||||
if (overlayRect.Contains(PlayerInput.MousePosition))
|
||||
{
|
||||
cursorOnOverlay = true;
|
||||
}
|
||||
locationInfoOverlay?.AddToGUIUpdateList(order: 1);
|
||||
}
|
||||
|
||||
if (!cursorOnOverlay)
|
||||
float closestDist = 0.0f;
|
||||
HighlightedLocation = null;
|
||||
if ((GUI.MouseOn == null || GUI.MouseOn == mapContainer))
|
||||
{
|
||||
float closestDist = 0.0f;
|
||||
HighlightedLocation = null;
|
||||
if ((GUI.MouseOn == null || GUI.MouseOn == mapContainer))
|
||||
for (int i = 0; i < Locations.Count; i++)
|
||||
{
|
||||
for (int i = 0; i < Locations.Count; i++)
|
||||
Location location = Locations[i];
|
||||
if (IsInFogOfWar(location) && !(currentDisplayLocation?.Connections.Any(c => c.Locations.Contains(location)) ?? false) && !GameMain.DebugDraw) { continue; }
|
||||
|
||||
Vector2 pos = rectCenter + (location.MapPosition + viewOffset) * zoom;
|
||||
if (!rect.Contains(pos)) { continue; }
|
||||
|
||||
Sprite locationSprite = location.IsCriticallyRadiated() ? location.Type.RadiationSprite ?? location.Type.Sprite : location.Type.Sprite;
|
||||
float iconScale = generationParams.LocationIconSize / locationSprite.size.X;
|
||||
if (location == currentDisplayLocation) { iconScale *= 1.2f; }
|
||||
|
||||
Rectangle drawRect = locationSprite.SourceRect;
|
||||
drawRect.Width = (int)(drawRect.Width * iconScale * zoom * 1.4f);
|
||||
drawRect.Height = (int)(drawRect.Height * iconScale * zoom * 1.4f);
|
||||
drawRect.X = (int)pos.X - drawRect.Width / 2;
|
||||
drawRect.Y = (int)pos.Y - drawRect.Width / 2;
|
||||
|
||||
if (!drawRect.Contains(PlayerInput.MousePosition)) { continue; }
|
||||
|
||||
float dist = Vector2.Distance(PlayerInput.MousePosition, pos);
|
||||
if (HighlightedLocation == null || dist < closestDist)
|
||||
{
|
||||
Location location = Locations[i];
|
||||
if (IsInFogOfWar(location) && !(currentDisplayLocation?.Connections.Any(c => c.Locations.Contains(location)) ?? false) && !GameMain.DebugDraw) { continue; }
|
||||
|
||||
Vector2 pos = rectCenter + (location.MapPosition + viewOffset) * zoom;
|
||||
if (!rect.Contains(pos)) { continue; }
|
||||
|
||||
Sprite locationSprite = location.IsCriticallyRadiated() ? location.Type.RadiationSprite ?? location.Type.Sprite : location.Type.Sprite;
|
||||
float iconScale = generationParams.LocationIconSize / locationSprite.size.X;
|
||||
if (location == currentDisplayLocation) { iconScale *= 1.2f; }
|
||||
|
||||
Rectangle drawRect = locationSprite.SourceRect;
|
||||
drawRect.Width = (int)(drawRect.Width * iconScale * zoom * 1.4f);
|
||||
drawRect.Height = (int)(drawRect.Height * iconScale * zoom * 1.4f);
|
||||
drawRect.X = (int)pos.X - drawRect.Width / 2;
|
||||
drawRect.Y = (int)pos.Y - drawRect.Width / 2;
|
||||
|
||||
if (!drawRect.Contains(PlayerInput.MousePosition)) { continue; }
|
||||
|
||||
float dist = Vector2.Distance(PlayerInput.MousePosition, pos);
|
||||
if (HighlightedLocation == null || dist < closestDist)
|
||||
{
|
||||
closestDist = dist;
|
||||
HighlightedLocation = location;
|
||||
}
|
||||
closestDist = dist;
|
||||
HighlightedLocation = location;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (SelectedConnection != null)
|
||||
{
|
||||
|
||||
@@ -528,11 +528,11 @@ namespace Barotrauma
|
||||
Item targetContainer = null;
|
||||
bool isShiftDown = PlayerInput.IsShiftDown();
|
||||
|
||||
if (!isShiftDown) return null;
|
||||
if (!isShiftDown) { return null; }
|
||||
|
||||
foreach (MapEntity e in mapEntityList)
|
||||
{
|
||||
if (!e.SelectableInEditor ||!(e is Item potentialContainer)) { continue; }
|
||||
if (!e.SelectableInEditor || e is not Item potentialContainer) { continue; }
|
||||
|
||||
if (e.IsMouseOn(position))
|
||||
{
|
||||
|
||||
@@ -83,7 +83,10 @@ namespace Barotrauma
|
||||
{
|
||||
string errorMsg = "Failed to load sound file \"" + filename + "\" (file not found).";
|
||||
DebugConsole.ThrowError(errorMsg, e);
|
||||
GameAnalyticsManager.AddErrorEventOnce("RoundSound.LoadRoundSound:FileNotFound" + filename, GameAnalyticsManager.ErrorSeverity.Error, errorMsg + "\n" + Environment.StackTrace.CleanupStackTrace());
|
||||
if (!ContentPackageManager.ModsEnabled)
|
||||
{
|
||||
GameAnalyticsManager.AddErrorEventOnce("RoundSound.LoadRoundSound:FileNotFound" + filename, GameAnalyticsManager.ErrorSeverity.Error, errorMsg + "\n" + Environment.StackTrace.CleanupStackTrace());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
catch (System.IO.InvalidDataException e)
|
||||
|
||||
@@ -66,9 +66,20 @@ namespace Barotrauma.Networking
|
||||
};
|
||||
|
||||
var addressOrAccountId = bannedPlayer.AddressOrAccountId;
|
||||
GUITextBlock textBlock = new GUITextBlock(
|
||||
new RectTransform(new Vector2(0.5f, 1.0f), topArea.RectTransform),
|
||||
bannedPlayer.Name + " (" + addressOrAccountId + ")") { CanBeFocused = true };
|
||||
|
||||
string nameText = bannedPlayer.Name;
|
||||
if (addressOrAccountId.TryCast(out Address address))
|
||||
{
|
||||
nameText += $" ({address.StringRepresentation})";
|
||||
}
|
||||
else if (addressOrAccountId.TryCast(out AccountId accountId))
|
||||
{
|
||||
nameText += $" ({accountId.StringRepresentation})";
|
||||
}
|
||||
GUITextBlock textBlock = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), topArea.RectTransform), nameText)
|
||||
{
|
||||
CanBeFocused = true
|
||||
};
|
||||
textBlock.RectTransform.MinSize = new Point(
|
||||
(int)textBlock.Font.MeasureString(textBlock.Text.SanitizedValue).X, 0);
|
||||
|
||||
@@ -106,7 +117,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
private bool RemoveBan(GUIButton button, object obj)
|
||||
{
|
||||
if (!(obj is BannedPlayer banned)) { return false; }
|
||||
if (obj is not BannedPlayer banned) { return false; }
|
||||
|
||||
localRemovedBans.Add(banned.UniqueIdentifier);
|
||||
RecreateBanFrame();
|
||||
|
||||
@@ -34,6 +34,7 @@ namespace Barotrauma.Networking
|
||||
catch
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to start ChildServerRelay Process. File: {processInfo.FileName}, arguments: {processInfo.Arguments}");
|
||||
ForceShutDown();
|
||||
throw;
|
||||
}
|
||||
|
||||
|
||||
@@ -1102,11 +1102,7 @@ namespace Barotrauma.Networking
|
||||
VoipClient = new VoipClient(this, ClientPeer);
|
||||
|
||||
//if we're still in the game, roundsummary or lobby screen, we don't need to redownload the mods
|
||||
if (!(Screen.Selected is GameScreen) && !(Screen.Selected is RoundSummaryScreen) && !(Screen.Selected is NetLobbyScreen))
|
||||
{
|
||||
GameMain.ModDownloadScreen.Select();
|
||||
}
|
||||
else
|
||||
if (Screen.Selected is GameScreen or RoundSummaryScreen or NetLobbyScreen)
|
||||
{
|
||||
EntityEventManager.ClearSelf();
|
||||
foreach (Character c in Character.CharacterList)
|
||||
@@ -1114,6 +1110,10 @@ namespace Barotrauma.Networking
|
||||
c.ResetNetState();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GameMain.ModDownloadScreen.Select();
|
||||
}
|
||||
|
||||
chatBox.InputBox.Enabled = true;
|
||||
if (GameMain.NetLobbyScreen?.ChatInput != null)
|
||||
@@ -1535,8 +1535,9 @@ namespace Barotrauma.Networking
|
||||
|
||||
roundInitStatus = RoundInitStatus.WaitingForStartGameFinalize;
|
||||
|
||||
DateTime? timeOut = null;
|
||||
//wait for up to 30 seconds for the server to send the STARTGAMEFINALIZE message
|
||||
TimeSpan timeOutDuration = new TimeSpan(0, 0, seconds: 30);
|
||||
DateTime timeOut = DateTime.Now + timeOutDuration;
|
||||
DateTime requestFinalizeTime = DateTime.Now;
|
||||
TimeSpan requestFinalizeInterval = new TimeSpan(0, 0, 2);
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
@@ -1545,11 +1546,15 @@ namespace Barotrauma.Networking
|
||||
|
||||
GUIMessageBox interruptPrompt = null;
|
||||
|
||||
while (true)
|
||||
if (includesFinalize)
|
||||
{
|
||||
try
|
||||
ReadStartGameFinalize(inc);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (timeOut.HasValue)
|
||||
try
|
||||
{
|
||||
if (DateTime.Now > requestFinalizeTime)
|
||||
{
|
||||
@@ -1583,41 +1588,30 @@ namespace Barotrauma.Networking
|
||||
return true;
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (includesFinalize)
|
||||
|
||||
if (!connected)
|
||||
{
|
||||
ReadStartGameFinalize(inc);
|
||||
roundInitStatus = RoundInitStatus.Interrupted;
|
||||
break;
|
||||
}
|
||||
|
||||
//wait for up to 30 seconds for the server to send the STARTGAMEFINALIZE message
|
||||
timeOut = DateTime.Now + timeOutDuration;
|
||||
if (roundInitStatus != RoundInitStatus.WaitingForStartGameFinalize) { break; }
|
||||
}
|
||||
|
||||
if (!connected)
|
||||
catch (Exception e)
|
||||
{
|
||||
roundInitStatus = RoundInitStatus.Interrupted;
|
||||
DebugConsole.ThrowError("There was an error initializing the round.", e, true);
|
||||
roundInitStatus = RoundInitStatus.Error;
|
||||
break;
|
||||
}
|
||||
|
||||
if (roundInitStatus != RoundInitStatus.WaitingForStartGameFinalize) { break; }
|
||||
//waiting for a STARTGAMEFINALIZE message
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("There was an error initializing the round.", e, true);
|
||||
roundInitStatus = RoundInitStatus.Error;
|
||||
break;
|
||||
}
|
||||
|
||||
//waiting for a STARTGAMEFINALIZE message
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
|
||||
interruptPrompt?.Close();
|
||||
interruptPrompt = null;
|
||||
|
||||
|
||||
if (roundInitStatus != RoundInitStatus.Started)
|
||||
{
|
||||
if (roundInitStatus != RoundInitStatus.Interrupted)
|
||||
@@ -2101,13 +2095,12 @@ namespace Barotrauma.Networking
|
||||
case ServerNetSegment.EntityPosition:
|
||||
inc.ReadPadBits(); //padding is required here to make sure any padding bits within tempBuffer are read correctly
|
||||
|
||||
bool isItem = inc.ReadBoolean(); inc.ReadPadBits();
|
||||
UInt32 incomingUintIdentifier = inc.ReadUInt32();
|
||||
UInt16 id = inc.ReadUInt16();
|
||||
uint msgLength = inc.ReadVariableUInt32();
|
||||
int msgEndPos = (int)(inc.BitPosition + msgLength * 8);
|
||||
|
||||
var entity = Entity.FindEntityByID(id) as IServerPositionSync;
|
||||
|
||||
var header = INetSerializableStruct.Read<EntityPositionHeader>(inc);
|
||||
|
||||
var entity = Entity.FindEntityByID(header.EntityId) as IServerPositionSync;
|
||||
if (msgEndPos > inc.LengthBits)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error while reading a position update for the entity \"({entity?.ToString() ?? "null"})\". Message length exceeds the size of the buffer.");
|
||||
@@ -2117,15 +2110,15 @@ namespace Barotrauma.Networking
|
||||
debugEntityList.Add(entity);
|
||||
if (entity != null)
|
||||
{
|
||||
if (entity is Item != isItem)
|
||||
if (entity is Item != header.IsItem)
|
||||
{
|
||||
DebugConsole.AddWarning($"Received a potentially invalid ENTITY_POSITION message. Entity type does not match (server entity is {(isItem ? "an item" : "not an item")}, client entity is {(entity?.GetType().ToString() ?? "null")}). Ignoring the message...");
|
||||
DebugConsole.AddWarning($"Received a potentially invalid ENTITY_POSITION message. Entity type does not match (server entity is {(header.IsItem ? "an item" : "not an item")}, client entity is {(entity?.GetType().ToString() ?? "null")}). Ignoring the message...");
|
||||
}
|
||||
else if (entity is MapEntity { Prefab: { UintIdentifier: { } uintIdentifier } } me &&
|
||||
uintIdentifier != incomingUintIdentifier)
|
||||
else if (entity is MapEntity { Prefab.UintIdentifier: var uintIdentifier } me &&
|
||||
uintIdentifier != header.PrefabUintIdentifier)
|
||||
{
|
||||
DebugConsole.AddWarning($"Received a potentially invalid ENTITY_POSITION message."
|
||||
+$"Entity identifier does not match (server entity is {MapEntityPrefab.List.FirstOrDefault(p => p.UintIdentifier == incomingUintIdentifier)?.Identifier.Value ?? "[not found]"}, "
|
||||
+$"Entity identifier does not match (server entity is {MapEntityPrefab.List.FirstOrDefault(p => p.UintIdentifier == header.PrefabUintIdentifier)?.Identifier.Value ?? "[not found]"}, "
|
||||
+$"client entity is {me.Prefab.Identifier}). Ignoring the message...");
|
||||
}
|
||||
else
|
||||
@@ -2133,7 +2126,6 @@ namespace Barotrauma.Networking
|
||||
entity.ClientReadPosition(inc, sendingTime);
|
||||
}
|
||||
}
|
||||
|
||||
//force to the correct position in case the entity doesn't exist
|
||||
//or the message wasn't read correctly for whatever reason
|
||||
inc.BitPosition = msgEndPos;
|
||||
@@ -2144,7 +2136,7 @@ namespace Barotrauma.Networking
|
||||
break;
|
||||
case ServerNetSegment.EntityEvent:
|
||||
case ServerNetSegment.EntityEventInitial:
|
||||
if (!EntityEventManager.Read(segment, inc, sendingTime, debugEntityList))
|
||||
if (!EntityEventManager.Read(segment, inc, sendingTime))
|
||||
{
|
||||
return SegmentTableReader<ServerNetSegment>.BreakSegmentReading.Yes;
|
||||
}
|
||||
|
||||
@@ -109,16 +109,15 @@ namespace Barotrauma.Networking
|
||||
|
||||
private UInt16? firstNewID;
|
||||
|
||||
private readonly List<IServerSerializable> tempEntityList = new List<IServerSerializable>();
|
||||
/// <summary>
|
||||
/// Read the events from the message, ignoring ones we've already received. Returns false if reading the events fails.
|
||||
/// </summary>
|
||||
public bool Read(ServerNetSegment type, IReadMessage msg, float sendingTime, List<IServerSerializable> entities)
|
||||
public bool Read(ServerNetSegment type, IReadMessage msg, float sendingTime)
|
||||
{
|
||||
UInt16 unreceivedEntityEventCount = 0;
|
||||
|
||||
if (type == ServerNetSegment.EntityEventInitial)
|
||||
{
|
||||
unreceivedEntityEventCount = msg.ReadUInt16();
|
||||
UInt16 unreceivedEntityEventCount = msg.ReadUInt16();
|
||||
firstNewID = msg.ReadUInt16();
|
||||
|
||||
if (GameSettings.CurrentConfig.VerboseLogging)
|
||||
@@ -143,7 +142,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
entities.Clear();
|
||||
tempEntityList.Clear();
|
||||
|
||||
msg.ReadPadBits();
|
||||
UInt16 firstEventID = msg.ReadUInt16();
|
||||
@@ -156,9 +155,9 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
string errorMsg = $"Error while reading a message from the server. Entity event data exceeds the size of the buffer (current position: {msg.BitPosition}, length: {msg.LengthBits}).";
|
||||
errorMsg += "\nPrevious entities:";
|
||||
for (int j = entities.Count - 1; j >= 0; j--)
|
||||
for (int j = tempEntityList.Count - 1; j >= 0; j--)
|
||||
{
|
||||
errorMsg += "\n" + (entities[j] == null ? "NULL" : entities[j].ToString());
|
||||
errorMsg += "\n" + (tempEntityList[j] == null ? "NULL" : tempEntityList[j].ToString());
|
||||
}
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
return false;
|
||||
@@ -174,7 +173,7 @@ namespace Barotrauma.Networking
|
||||
DebugConsole.NewMessage("received msg " + thisEventID + " (null entity)",
|
||||
Microsoft.Xna.Framework.Color.Orange);
|
||||
}
|
||||
entities.Add(null);
|
||||
tempEntityList.Add(null);
|
||||
if (thisEventID == (UInt16)(lastReceivedID + 1)) { lastReceivedID++; }
|
||||
continue;
|
||||
}
|
||||
@@ -182,7 +181,7 @@ namespace Barotrauma.Networking
|
||||
int msgLength = (int)msg.ReadVariableUInt32();
|
||||
|
||||
IServerSerializable entity = Entity.FindEntityByID(entityID) as IServerSerializable;
|
||||
entities.Add(entity);
|
||||
tempEntityList.Add(entity);
|
||||
|
||||
//skip the event if we've already received it or if the entity isn't found
|
||||
if (thisEventID != (UInt16)(lastReceivedID + 1) || entity == null)
|
||||
@@ -223,7 +222,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (msg.BitPosition != msgPosition + msgLength * 8)
|
||||
{
|
||||
var prevEntity = entities.Count >= 2 ? entities[entities.Count - 2] : null;
|
||||
var prevEntity = tempEntityList.Count >= 2 ? tempEntityList[tempEntityList.Count - 2] : null;
|
||||
ushort prevId = prevEntity is Entity p ? p.ID : (ushort)0;
|
||||
string errorMsg = $"Message byte position incorrect after reading an event for the entity \"{entity}\" (ID {(entity is Entity e ? e.ID : 0)}). "
|
||||
+$"The previous entity was \"{prevEntity}\" (ID {prevId}) "
|
||||
|
||||
@@ -30,6 +30,8 @@ namespace Barotrauma.Networking
|
||||
protected readonly bool isOwner;
|
||||
protected readonly Option<int> ownerKey;
|
||||
|
||||
public bool IsActive => isActive;
|
||||
|
||||
protected bool isActive;
|
||||
|
||||
public ClientPeer(Endpoint serverEndpoint, Callbacks callbacks, Option<int> ownerKey)
|
||||
|
||||
@@ -303,7 +303,7 @@ namespace Barotrauma
|
||||
|
||||
bool ChangeValue(GUIButton btn, object userData)
|
||||
{
|
||||
if (!(userData is int change)) { return false; }
|
||||
if (userData is not int change) { return false; }
|
||||
|
||||
int hiddenOptions = 0;
|
||||
|
||||
|
||||
@@ -365,7 +365,7 @@ namespace Barotrauma
|
||||
|
||||
private void CreateCustomizeWindow(CampaignSettings prevSettings, Action<CampaignSettings> onClosed = null)
|
||||
{
|
||||
CampaignCustomizeSettings = new GUIMessageBox("", "", new[] { TextManager.Get("OK") }, new Vector2(0.25f, 0.3f), minSize: new Point(450, 350));
|
||||
CampaignCustomizeSettings = new GUIMessageBox("", "", new[] { TextManager.Get("OK") }, new Vector2(0.25f, 0.5f), minSize: new Point(450, 350));
|
||||
|
||||
GUILayoutGroup campaignSettingContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.8f), CampaignCustomizeSettings.Content.RectTransform, Anchor.TopCenter));
|
||||
|
||||
|
||||
@@ -474,7 +474,7 @@ namespace Barotrauma
|
||||
};
|
||||
missionRewardTexts.Add(rewardText);
|
||||
|
||||
LocalizedString reputationText = mission.GetReputationRewardText(mission.Locations[0]);
|
||||
LocalizedString reputationText = mission.GetReputationRewardText();
|
||||
if (!reputationText.IsNullOrEmpty())
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), RichString.Rich(reputationText), wrap: true);
|
||||
|
||||
@@ -125,7 +125,7 @@ namespace Barotrauma.CharacterEditor
|
||||
{
|
||||
ResetVariables();
|
||||
var subInfo = new SubmarineInfo("Content/AnimEditor.sub");
|
||||
Submarine.MainSub = new Submarine(subInfo);
|
||||
Submarine.MainSub = new Submarine(subInfo, showErrorMessages: false);
|
||||
if (Submarine.MainSub.PhysicsBody != null)
|
||||
{
|
||||
Submarine.MainSub.PhysicsBody.Enabled = false;
|
||||
@@ -162,11 +162,6 @@ namespace Barotrauma.CharacterEditor
|
||||
OpenDoors();
|
||||
GameMain.Instance.ResolutionChanged += OnResolutionChanged;
|
||||
Instance = this;
|
||||
|
||||
if (!GameSettings.CurrentConfig.EditorDisclaimerShown)
|
||||
{
|
||||
GameMain.Instance.ShowEditorDisclaimer();
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetVariables()
|
||||
@@ -2688,10 +2683,6 @@ namespace Barotrauma.CharacterEditor
|
||||
|
||||
// Character selection
|
||||
var characterLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), GetCharacterEditorTranslation("CharacterPanel"), font: GUIStyle.LargeFont);
|
||||
var disclaimerBtn = new GUIButton(new RectTransform(new Vector2(0.2f, 0.7f), characterLabel.RectTransform, Anchor.CenterRight), style: "GUINotificationButton")
|
||||
{
|
||||
OnClicked = (btn, userdata) => { GameMain.Instance.ShowEditorDisclaimer(); return true; }
|
||||
};
|
||||
|
||||
var characterDropDown = new GUIDropDown(new RectTransform(new Vector2(1, 0.2f), content.RectTransform)
|
||||
{
|
||||
|
||||
@@ -820,6 +820,15 @@ namespace Barotrauma
|
||||
};
|
||||
valueInput.Text = newValue?.ToString() ?? "<type here>";
|
||||
}
|
||||
else if (type == typeof(Identifier))
|
||||
{
|
||||
GUITextBox valueInput = new GUITextBox(new RectTransform(Vector2.One, layout.RectTransform), newValue?.ToString() ?? string.Empty);
|
||||
valueInput.OnTextChanged += (component, o) =>
|
||||
{
|
||||
newValue = new Identifier(o);
|
||||
return true;
|
||||
};
|
||||
}
|
||||
else if (type == typeof(float) || type == typeof(int))
|
||||
{
|
||||
GUINumberInput valueInput = new GUINumberInput(new RectTransform(Vector2.One, layout.RectTransform), NumberType.Float) { FloatValue = (float) (newValue ?? 0.0f) };
|
||||
|
||||
@@ -51,9 +51,8 @@ namespace Barotrauma
|
||||
|
||||
private readonly GUIFrame modsButtonContainer;
|
||||
private readonly GUIButton modsButton, modUpdatesButton;
|
||||
private Task<IReadOnlyList<Steamworks.Ugc.Item>> modUpdateTask;
|
||||
private float modUpdateTimer = 0.0f;
|
||||
private const float ModUpdateInterval = 60.0f;
|
||||
private (DateTime WhenToRefresh, int Count) modUpdateStatus = (DateTime.Now, 0);
|
||||
private static readonly TimeSpan ModUpdateInterval = TimeSpan.FromSeconds(60.0f);
|
||||
|
||||
private readonly GameMain game;
|
||||
|
||||
@@ -736,8 +735,7 @@ namespace Barotrauma
|
||||
|
||||
public void ResetModUpdateButton()
|
||||
{
|
||||
modUpdateTask = null;
|
||||
modUpdateTimer = 0;
|
||||
modUpdateStatus = (DateTime.Now, 0);
|
||||
modUpdatesButton.Visible = false;
|
||||
}
|
||||
|
||||
@@ -875,7 +873,25 @@ namespace Barotrauma
|
||||
GameMain.ResetNetLobbyScreen();
|
||||
try
|
||||
{
|
||||
string exeName = serverExecutableDropdown.SelectedComponent?.UserData is ServerExecutableFile f ? f.Path.Value : "DedicatedServer";
|
||||
string fileName;
|
||||
if (serverExecutableDropdown.SelectedComponent?.UserData is ServerExecutableFile f &&
|
||||
f.ContentPackage != GameMain.VanillaContent)
|
||||
{
|
||||
fileName = Path.Combine(
|
||||
Path.GetDirectoryName(f.Path.Value),
|
||||
Path.GetFileNameWithoutExtension(f.Path.Value));
|
||||
#if WINDOWS
|
||||
fileName += ".exe";
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#if WINDOWS
|
||||
fileName = "DedicatedServer.exe";
|
||||
#else
|
||||
fileName = "./DedicatedServer";
|
||||
#endif
|
||||
}
|
||||
|
||||
string arguments = "-name \"" + ToolBox.EscapeCharacters(name) + "\"" +
|
||||
" -public " + isPublicBox.Selected.ToString() +
|
||||
@@ -899,19 +915,10 @@ namespace Barotrauma
|
||||
}
|
||||
int ownerKey = Math.Max(CryptoRandom.Instance.Next(), 1);
|
||||
arguments += " -ownerkey " + ownerKey;
|
||||
|
||||
string filename = Path.Combine(
|
||||
Path.GetDirectoryName(exeName),
|
||||
Path.GetFileNameWithoutExtension(exeName));
|
||||
#if WINDOWS
|
||||
filename += ".exe";
|
||||
#else
|
||||
filename = "./" + exeName;
|
||||
#endif
|
||||
|
||||
|
||||
var processInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = filename,
|
||||
FileName = fileName,
|
||||
Arguments = arguments,
|
||||
WorkingDirectory = Directory.GetCurrentDirectory(),
|
||||
#if !DEBUG
|
||||
@@ -958,15 +965,42 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateOutOfDateWorkshopItemCount()
|
||||
{
|
||||
if (DateTime.Now < modUpdateStatus.WhenToRefresh) { return; }
|
||||
if (!SteamManager.IsInitialized) { return; }
|
||||
|
||||
var installedPackages = ContentPackageManager.WorkshopPackages;
|
||||
|
||||
var ids = SteamManager.Workshop.GetSubscribedItemIds()
|
||||
.Select(id => id.Value)
|
||||
.Union(installedPackages
|
||||
.Select(pkg => pkg.UgcId)
|
||||
.NotNone()
|
||||
.OfType<SteamWorkshopId>()
|
||||
.Select(id => id.Value));
|
||||
var count = ids
|
||||
// Deliberately construct Steamworks.Ugc.Item directly
|
||||
// to not immediately generate a Workshop data request
|
||||
.Select(id => new Steamworks.Ugc.Item(id))
|
||||
.Count(item =>
|
||||
installedPackages.FirstOrDefault(p
|
||||
=> p.UgcId.TryUnwrap(out SteamWorkshopId id) && id.Value == item.Id)
|
||||
is { } pkg
|
||||
// Checking that this item is downloading, waiting to be downloaded
|
||||
// or is newer than the currently installed copy should be good enough,
|
||||
// and should still not make a Workshop data request
|
||||
&& (item.IsDownloading
|
||||
|| item.IsDownloadPending
|
||||
|| (item.InstallTime.TryGetValue(out var workshopInstallTime)
|
||||
&& pkg.InstallTime.TryUnwrap(out var localInstallTime)
|
||||
&& localInstallTime < workshopInstallTime)));
|
||||
|
||||
modUpdateStatus = (DateTime.Now + ModUpdateInterval, count);
|
||||
}
|
||||
|
||||
public override void Update(double deltaTime)
|
||||
{
|
||||
modUpdateTimer -= (float)deltaTime;
|
||||
if (modUpdateTimer <= 0.0f && modUpdateTask is not { IsCompleted: false })
|
||||
{
|
||||
modUpdateTask = BulkDownloader.GetItemsThatNeedUpdating();
|
||||
modUpdateTimer = ModUpdateInterval;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
hostServerButton.Enabled = true;
|
||||
#else
|
||||
@@ -976,10 +1010,8 @@ namespace Barotrauma
|
||||
}
|
||||
#endif
|
||||
|
||||
if (modUpdateTask is { IsCompletedSuccessfully: true })
|
||||
{
|
||||
modUpdatesButton.Visible = modUpdateTask.Result.Count > 0;
|
||||
}
|
||||
UpdateOutOfDateWorkshopItemCount();
|
||||
modUpdatesButton.Visible = modUpdateStatus.Count > 0;
|
||||
|
||||
if (modUpdatesButton.Visible)
|
||||
{
|
||||
|
||||
@@ -106,7 +106,7 @@ namespace Barotrauma
|
||||
{
|
||||
var slide = slideshowPrefab.Slides[Math.Min(state, slideshowPrefab.Slides.Length - 1)];
|
||||
currentText = slide.Text
|
||||
.Replace("[submarine]", Submarine.MainSub?.Info.Name ?? "Unknown")
|
||||
.Replace("[submarine]", Submarine.MainSub?.Info.Name ?? GameMain.GameSession?.SubmarineInfo?.Name ?? "Unknown")
|
||||
.Replace("[location]", Level.Loaded?.StartOutpost?.Info.Name ?? "Unknown");
|
||||
}
|
||||
|
||||
|
||||
@@ -543,13 +543,6 @@ namespace Barotrauma
|
||||
}
|
||||
};
|
||||
|
||||
var disclaimerBtn = new GUIButton(new RectTransform(new Vector2(0.1f, 1.0f), paddedTopPanel.RectTransform, Anchor.CenterRight), style: "GUINotificationButton")
|
||||
{
|
||||
IgnoreLayoutGroups = true,
|
||||
OnClicked = (btn, userdata) => { GameMain.Instance.ShowEditorDisclaimer(); return true; }
|
||||
};
|
||||
disclaimerBtn.RectTransform.MaxSize = new Point(disclaimerBtn.Rect.Height);
|
||||
|
||||
TopPanel.RectTransform.MinSize = new Point(0, (int)(paddedTopPanel.RectTransform.Children.Max(c => c.MinSize.Y) / paddedTopPanel.RectTransform.RelativeSize.Y));
|
||||
paddedTopPanel.Recalculate();
|
||||
|
||||
@@ -1425,7 +1418,7 @@ namespace Barotrauma
|
||||
else if (MainSub == null)
|
||||
{
|
||||
var subInfo = new SubmarineInfo();
|
||||
MainSub = new Submarine(subInfo);
|
||||
MainSub = new Submarine(subInfo, showErrorMessages: false);
|
||||
}
|
||||
|
||||
MainSub.UpdateTransform(interpolate: false);
|
||||
@@ -1462,11 +1455,6 @@ namespace Barotrauma
|
||||
|
||||
ImageManager.OnEditorSelected();
|
||||
ReconstructLayers();
|
||||
|
||||
if (!GameSettings.CurrentConfig.EditorDisclaimerShown)
|
||||
{
|
||||
GameMain.Instance.ShowEditorDisclaimer();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnFileDropped(string filePath, string extension)
|
||||
@@ -5646,8 +5634,7 @@ namespace Barotrauma
|
||||
MouseDragStart = Vector2.Zero;
|
||||
}
|
||||
|
||||
if (!saveAssemblyFrame.Rect.Contains(PlayerInput.MousePosition)
|
||||
&& !snapToGridFrame.Rect.Contains(PlayerInput.MousePosition)
|
||||
if ((GUI.MouseOn == null || !GUI.MouseOn.IsChildOf(TopPanel))
|
||||
&& dummyCharacter?.SelectedItem == null && !WiringMode
|
||||
&& (GUI.MouseOn == null || MapEntity.SelectedAny || MapEntity.SelectionPos != Vector2.Zero))
|
||||
{
|
||||
|
||||
@@ -779,6 +779,8 @@ namespace Barotrauma
|
||||
workshopMenu = Screen.Selected is MainMenuScreen
|
||||
? (WorkshopMenu)new MutableWorkshopMenu(content)
|
||||
: (WorkshopMenu)new ImmutableWorkshopMenu(content);
|
||||
|
||||
GameMain.MainMenuScreen.ResetModUpdateButton();
|
||||
}
|
||||
|
||||
private void CreateBottomButtons()
|
||||
|
||||
@@ -904,17 +904,18 @@ namespace Barotrauma
|
||||
|
||||
public static void PlayDamageSound(string damageType, float damage, Vector2 position, float range = 2000.0f, IEnumerable<Identifier> tags = null)
|
||||
{
|
||||
var suitableSounds = damageSounds.Where(s =>
|
||||
s.DamageType == damageType &&
|
||||
(s.RequiredTag.IsEmpty || (tags == null ? s.RequiredTag.IsEmpty : tags.Contains(s.RequiredTag))));
|
||||
|
||||
//if the damage is too low for any sound, don't play anything
|
||||
if (damageSounds.All(d => damage < d.DamageRange.X)) { return; }
|
||||
if (suitableSounds.All(d => damage < d.DamageRange.X)) { return; }
|
||||
|
||||
//allow the damage to differ by 10 from the configured damage range,
|
||||
//so the same amount of damage doesn't always play the same sound
|
||||
float randomizedDamage = MathHelper.Clamp(damage + Rand.Range(-10.0f, 10.0f), 0.0f, 100.0f);
|
||||
|
||||
var suitableSounds = damageSounds.Where(s =>
|
||||
s.DamageType == damageType &&
|
||||
(s.DamageRange == Vector2.Zero || (randomizedDamage >= s.DamageRange.X && randomizedDamage <= s.DamageRange.Y)) &&
|
||||
(s.RequiredTag.IsEmpty || (tags == null ? s.RequiredTag.IsEmpty : tags.Contains(s.RequiredTag))));
|
||||
suitableSounds = suitableSounds.Where(s =>
|
||||
s.DamageRange == Vector2.Zero || (randomizedDamage >= s.DamageRange.X && randomizedDamage <= s.DamageRange.Y));
|
||||
|
||||
var damageSound = suitableSounds.GetRandomUnsynced();
|
||||
damageSound?.Sound?.Play(1.0f, range, position, muffle: !damageSound.IgnoreMuffling && ShouldMuffleSound(Character.Controlled, position, range, null));
|
||||
|
||||
@@ -45,18 +45,26 @@ namespace Barotrauma.Steam
|
||||
protected static readonly Regex bbTagRegex = new Regex(@"\[(.+?)\]",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
|
||||
|
||||
protected static GUICustomComponent CreateBBCodeElement(string bbCode, GUIListBox container)
|
||||
protected static void CreateBBCodeElement(Steamworks.Ugc.Item workshopItem, GUIListBox container)
|
||||
{
|
||||
Point cachedContainerSize = Point.Zero;
|
||||
List<BBWord> bbWords = new List<BBWord>();
|
||||
Stack<BBWord.TagType> tagStack = new Stack<BBWord.TagType>();
|
||||
|
||||
void recalculate()
|
||||
string bbCode = "";
|
||||
|
||||
void forceReset()
|
||||
{
|
||||
if (cachedContainerSize == container.Content.RectTransform.NonScaledSize) { return; }
|
||||
bbWords.Clear();
|
||||
cachedContainerSize = Point.Zero;
|
||||
}
|
||||
|
||||
void recalculate(GUICustomComponent component)
|
||||
{
|
||||
if (cachedContainerSize == component.RectTransform.NonScaledSize) { return; }
|
||||
|
||||
bbWords.Clear();
|
||||
cachedContainerSize = container.Content.RectTransform.NonScaledSize;
|
||||
cachedContainerSize = component.RectTransform.NonScaledSize;
|
||||
|
||||
var matches = new Stack<Match>(bbTagRegex.Matches(bbCode).Reverse());
|
||||
Match? nextTag = null;
|
||||
@@ -133,11 +141,14 @@ namespace Barotrauma.Steam
|
||||
{
|
||||
bbWords.Add(new BBWord(finalWord, currTagType));
|
||||
}
|
||||
|
||||
container.RecalculateChildren();
|
||||
container.UpdateScrollBarSize();
|
||||
}
|
||||
|
||||
void draw(SpriteBatch spriteBatch, GUICustomComponent component)
|
||||
{
|
||||
recalculate();
|
||||
recalculate(component);
|
||||
Vector2 currPos = Vector2.Zero;
|
||||
Vector2 rectPos = component.Rect.Location.ToVector2();
|
||||
for (int i = 0; i < bbWords.Count; i++)
|
||||
@@ -180,7 +191,19 @@ namespace Barotrauma.Steam
|
||||
= component.RectTransform.NonScaledSize.ToVector2() / component.Parent.Rect.Size.ToVector2();
|
||||
}
|
||||
|
||||
return new GUICustomComponent(new RectTransform(Vector2.One, container.Content.RectTransform),
|
||||
TaskPool.Add(
|
||||
$"GetWorkshopItemLongDescriptionFor{workshopItem.Id.Value}",
|
||||
SteamManager.Workshop.GetItemAsap(workshopItem.Id.Value, withLongDescription: true),
|
||||
t =>
|
||||
{
|
||||
if (!t.TryGetResult(out Steamworks.Ugc.Item? workshopItemWithDescription)) { return; }
|
||||
|
||||
bbCode = workshopItemWithDescription?.Description ?? "";
|
||||
forceReset();
|
||||
});
|
||||
|
||||
new GUICustomComponent(
|
||||
new RectTransform(Vector2.One, container.Content.RectTransform),
|
||||
onDraw: draw);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Barotrauma.Steam
|
||||
if (numSubscribedMods == memSubscribedModCount) { return; }
|
||||
memSubscribedModCount = numSubscribedMods;
|
||||
|
||||
var subscribedIds = SteamManager.GetSubscribedItems().ToHashSet();
|
||||
var subscribedIds = SteamManager.Workshop.GetSubscribedItemIds();
|
||||
var installedIds = ContentPackageManager.WorkshopPackages
|
||||
.Select(p => p.UgcId)
|
||||
.NotNone()
|
||||
|
||||
@@ -773,7 +773,7 @@ namespace Barotrauma.Steam
|
||||
#endregion
|
||||
|
||||
var descriptionListBox = new GUIListBox(new RectTransform((1.0f, 0.38f), verticalLayout.RectTransform));
|
||||
CreateBBCodeElement(workshopItem.Description, descriptionListBox);
|
||||
CreateBBCodeElement(workshopItem, descriptionListBox);
|
||||
|
||||
var showInSteamContainer
|
||||
= new GUIFrame(new RectTransform((1.0f, 0.05f), verticalLayout.RectTransform), style: null);
|
||||
|
||||
@@ -218,6 +218,20 @@ namespace Barotrauma.Steam
|
||||
var descriptionTextBox
|
||||
= ScrollableTextBox(rightTop, 6.0f, workshopItem.Description ?? string.Empty);
|
||||
|
||||
if (workshopItem.Id != 0)
|
||||
{
|
||||
TaskPool.Add(
|
||||
$"GetFullDescription{workshopItem.Id}",
|
||||
SteamManager.Workshop.GetItemAsap(workshopItem.Id.Value, withLongDescription: true),
|
||||
t =>
|
||||
{
|
||||
if (!t.TryGetResult(out Steamworks.Ugc.Item? itemWithDescription)) { return; }
|
||||
|
||||
descriptionTextBox.Text = itemWithDescription?.Description ?? descriptionTextBox.Text;
|
||||
descriptionTextBox.Deselect();
|
||||
});
|
||||
}
|
||||
|
||||
var (leftBottom, _, rightBottom)
|
||||
= CreateSidebars(mainLayout, leftWidth: 0.49f, centerWidth: 0.01f, rightWidth: 0.5f, height: 0.5f);
|
||||
leftBottom.Stretch = true;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class UpgradePrefab
|
||||
sealed partial class UpgradePrefab
|
||||
{
|
||||
public readonly ImmutableArray<DecorativeSprite> DecorativeSprites = new ImmutableArray<DecorativeSprite>();
|
||||
public Sprite Sprite { get; private set; }
|
||||
|
||||
@@ -23,36 +23,55 @@ namespace Barotrauma
|
||||
|
||||
public static void ConvertMasterLocalizationKit(string outputTextsDirectory, string outputConversationsDirectory, bool convertConversations)
|
||||
{
|
||||
string textFilePath = Path.Combine(infoTextPath, "Texts.csv");
|
||||
string conversationFilePath = Path.Combine(infoTextPath, "NPCConversations.csv");
|
||||
List<string> languages = new List<string>();
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
string textFilePath;
|
||||
string outputFileName;
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
textFilePath = Path.Combine(infoTextPath, "Texts.csv");
|
||||
outputFileName = "Vanilla.xml";
|
||||
break;
|
||||
case 1:
|
||||
textFilePath = Path.Combine(infoTextPath, "EditorTexts.csv");
|
||||
outputFileName = "VanillaEditorTexts.xml";
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Dictionary<string, List<string>> xmlContent;
|
||||
try
|
||||
{
|
||||
xmlContent = ConvertInfoTextToXML(File.ReadAllLines(textFilePath, Encoding.UTF8));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("InfoText Localization .csv to .xml conversion failed for: " + textFilePath, e);
|
||||
return;
|
||||
}
|
||||
if (xmlContent == null)
|
||||
{
|
||||
DebugConsole.ThrowError("InfoText Localization .csv to .xml conversion failed for: " + textFilePath);
|
||||
return;
|
||||
}
|
||||
foreach (string language in xmlContent.Keys)
|
||||
{
|
||||
string languageNoWhitespace = language.Replace(" ", "");
|
||||
string xmlFileFullPath = Path.Combine(outputTextsDirectory, $"{languageNoWhitespace}/{languageNoWhitespace}Vanilla.xml");
|
||||
File.WriteAllLines(xmlFileFullPath, xmlContent[language], Encoding.UTF8);
|
||||
DebugConsole.NewMessage("InfoText localization .xml file successfully created at: " + xmlFileFullPath);
|
||||
Dictionary<string, List<string>> xmlContent;
|
||||
try
|
||||
{
|
||||
xmlContent = ConvertInfoTextToXML(File.ReadAllLines(textFilePath, Encoding.UTF8));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("InfoText Localization .csv to .xml conversion failed for: " + textFilePath, e);
|
||||
return;
|
||||
}
|
||||
if (xmlContent == null)
|
||||
{
|
||||
DebugConsole.ThrowError("InfoText Localization .csv to .xml conversion failed for: " + textFilePath);
|
||||
return;
|
||||
}
|
||||
foreach (string language in xmlContent.Keys)
|
||||
{
|
||||
languages.Add(language);
|
||||
string languageNoWhitespace = language.Replace(" ", "");
|
||||
string xmlFileFullPath = Path.Combine(outputTextsDirectory, $"{languageNoWhitespace}/{languageNoWhitespace}{outputFileName}");
|
||||
File.WriteAllLines(xmlFileFullPath, xmlContent[language], Encoding.UTF8);
|
||||
DebugConsole.NewMessage("InfoText localization .xml file successfully created at: " + xmlFileFullPath);
|
||||
}
|
||||
}
|
||||
|
||||
if (convertConversations)
|
||||
{
|
||||
string conversationFilePath = Path.Combine(infoTextPath, "NPCConversations.csv");
|
||||
var conversationLinesAll = File.ReadAllLines(conversationFilePath, Encoding.UTF8);
|
||||
foreach (string language in xmlContent.Keys)
|
||||
foreach (string language in languages)
|
||||
{
|
||||
List<string> convXmlContent = ConvertConversationsToXML(conversationLinesAll, language);
|
||||
if (convXmlContent == null)
|
||||
@@ -61,7 +80,7 @@ namespace Barotrauma
|
||||
continue;
|
||||
}
|
||||
string languageNoWhitespace = language.Replace(" ", "");
|
||||
string xmlFileFullPath = Path.Combine(outputTextsDirectory, $"NpcConversations_{languageNoWhitespace}.xml");
|
||||
string xmlFileFullPath = Path.Combine(outputConversationsDirectory, languageNoWhitespace, $"NpcConversations_{languageNoWhitespace}.xml");
|
||||
File.WriteAllLines(xmlFileFullPath, convXmlContent, Encoding.UTF8);
|
||||
DebugConsole.NewMessage("Conversation localization .xml file successfully created at: " + xmlFileFullPath);
|
||||
}
|
||||
@@ -339,7 +358,8 @@ namespace Barotrauma
|
||||
string[] headerSplit = csvContent[0].Split(separator);
|
||||
for (int i = 0; i < headerSplit.Length; i++)
|
||||
{
|
||||
if (headerSplit[i] == language)
|
||||
if (headerSplit[i] == language ||
|
||||
(language == "English" && headerSplit[i]== "Line (Original)"))
|
||||
{
|
||||
languageColumn = i;
|
||||
break;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>100.11.0.0</Version>
|
||||
<Version>100.13.0.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>100.11.0.0</Version>
|
||||
<Version>100.13.0.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>100.11.0.0</Version>
|
||||
<Version>100.13.0.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>100.11.0.0</Version>
|
||||
<Version>100.13.0.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>100.11.0.0</Version>
|
||||
<Version>100.13.0.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
|
||||
@@ -302,12 +302,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void ServerWritePosition(IWriteMessage msg, Client c)
|
||||
public void ServerWritePosition(ReadWriteMessage tempBuffer, Client c)
|
||||
{
|
||||
msg.WriteUInt16(ID);
|
||||
|
||||
IWriteMessage tempBuffer = new WriteOnlyMessage();
|
||||
|
||||
if (this == c.Character)
|
||||
{
|
||||
tempBuffer.WriteBoolean(true);
|
||||
@@ -405,11 +401,6 @@ namespace Barotrauma
|
||||
AIController?.ServerWrite(tempBuffer);
|
||||
HealthUpdatePending = false;
|
||||
}
|
||||
|
||||
tempBuffer.WritePadBits();
|
||||
|
||||
msg.WriteVariableUInt32((uint)tempBuffer.LengthBytes);
|
||||
msg.WriteBytes(tempBuffer.Buffer, 0, tempBuffer.LengthBytes);
|
||||
}
|
||||
|
||||
public virtual void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Barotrauma
|
||||
clientsToRemove.Add(k);
|
||||
}
|
||||
}
|
||||
if (!(clientsToRemove is null))
|
||||
if (clientsToRemove is not null)
|
||||
{
|
||||
foreach (var k in clientsToRemove)
|
||||
{
|
||||
@@ -62,7 +62,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (Entity e in targets)
|
||||
{
|
||||
if (!(e is Character character) || !character.IsRemotePlayer) { continue; }
|
||||
if (e is not Character character || !character.IsRemotePlayer) { continue; }
|
||||
Client targetClient = GameMain.Server.ConnectedClients.Find(c => c.Character == character);
|
||||
if (targetClient != null)
|
||||
{
|
||||
@@ -85,7 +85,7 @@ namespace Barotrauma
|
||||
IEnumerable<Entity> entities = ParentEvent.GetTargets(TargetTag);
|
||||
foreach (Entity e in entities)
|
||||
{
|
||||
if (!(e is Character character) || !character.IsRemotePlayer) { continue; }
|
||||
if (e is not Character character || !character.IsRemotePlayer) { continue; }
|
||||
Client targetClient = GameMain.Server.ConnectedClients.Find(c => c.Character == character);
|
||||
if (targetClient != null)
|
||||
{
|
||||
@@ -149,5 +149,15 @@ namespace Barotrauma
|
||||
}
|
||||
GameMain.Server?.ServerPeer?.Send(outmsg, client.Connection, DeliveryMethod.Reliable);
|
||||
}
|
||||
|
||||
public void ServerWriteSelectedOption(Client client)
|
||||
{
|
||||
IWriteMessage outmsg = new WriteOnlyMessage();
|
||||
outmsg.WriteByte((byte)ServerPacketHeader.EVENTACTION);
|
||||
outmsg.WriteByte((byte)EventManager.NetworkEventType.CONVERSATION_SELECTED_OPTION);
|
||||
outmsg.WriteUInt16(Identifier);
|
||||
outmsg.WriteByte((byte)(selectedOption + 1));
|
||||
GameMain.Server?.ServerPeer?.Send(outmsg, client.Connection, DeliveryMethod.Reliable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,12 +14,12 @@ namespace Barotrauma
|
||||
|
||||
foreach (Event ev in activeEvents)
|
||||
{
|
||||
if (!(ev is ScriptedEvent scriptedEvent)) { continue; }
|
||||
if (ev is not ScriptedEvent scriptedEvent) { continue; }
|
||||
|
||||
var actions = FindActions(scriptedEvent);
|
||||
foreach (EventAction action in actions.Select(a => a.Item2))
|
||||
{
|
||||
if (!(action is ConversationAction convAction) || convAction.Identifier != actionId) { continue; }
|
||||
if (action is not ConversationAction convAction || convAction.Identifier != actionId) { continue; }
|
||||
if (!convAction.TargetClients.Contains(sender))
|
||||
{
|
||||
#if DEBUG || UNSTABLE
|
||||
@@ -42,6 +42,14 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
convAction.SelectedOption = selectedOption;
|
||||
if (convAction.Options.Any() && !convAction.GetEndingOptions().Contains(selectedOption))
|
||||
{
|
||||
foreach (Client c in convAction.TargetClients)
|
||||
{
|
||||
if (c == sender) { continue; }
|
||||
convAction.ServerWriteSelectedOption(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -66,6 +66,7 @@ namespace Barotrauma
|
||||
|
||||
public static ContentPackage VanillaContent => ContentPackageManager.VanillaCorePackage;
|
||||
|
||||
|
||||
public readonly string[] CommandLineArgs;
|
||||
|
||||
public GameMain(string[] args)
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Barotrauma.Items.Components
|
||||
msg.WriteBoolean(launch);
|
||||
if (launch)
|
||||
{
|
||||
msg.WriteUInt16(User?.ID ?? 0);
|
||||
msg.WriteUInt16(User?.ID ?? Entity.NullEntityID);
|
||||
msg.WriteSingle(launchPos.X);
|
||||
msg.WriteSingle(launchPos.Y);
|
||||
msg.WriteSingle(launchRot);
|
||||
|
||||
@@ -349,15 +349,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void ServerWritePosition(IWriteMessage msg, Client c)
|
||||
public void ServerWritePosition(ReadWriteMessage tempBuffer, Client c)
|
||||
{
|
||||
msg.WriteUInt16(ID);
|
||||
|
||||
IWriteMessage tempBuffer = new WriteOnlyMessage();
|
||||
body.ServerWrite(tempBuffer);
|
||||
msg.WriteVariableUInt32((uint)tempBuffer.LengthBytes);
|
||||
msg.WriteBytes(tempBuffer.Buffer, 0, tempBuffer.LengthBytes);
|
||||
msg.WritePadBits();
|
||||
}
|
||||
|
||||
public void CreateServerEvent<T>(T ic) where T : ItemComponent, IServerSerializable
|
||||
@@ -379,7 +373,7 @@ namespace Barotrauma
|
||||
if (!components.Contains(ic)) { return; }
|
||||
|
||||
var eventData = new ComponentStateEventData(ic, extraData);
|
||||
if (!ic.ValidateEventData(eventData)) { throw new Exception($"Component event creation failed: {typeof(T).Name}.{nameof(ItemComponent.ValidateEventData)} returned false"); }
|
||||
if (!ic.ValidateEventData(eventData)) { throw new Exception($"Component event creation for the item \"{Prefab.Identifier}\" failed: {typeof(T).Name}.{nameof(ItemComponent.ValidateEventData)} returned false."); }
|
||||
GameMain.Server.CreateEntityEvent(this, eventData);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,14 +5,9 @@ namespace Barotrauma
|
||||
{
|
||||
partial class Submarine
|
||||
{
|
||||
public void ServerWritePosition(IWriteMessage msg, Client c)
|
||||
public void ServerWritePosition(ReadWriteMessage tempBuffer, Client c)
|
||||
{
|
||||
msg.WriteUInt16(ID);
|
||||
IWriteMessage tempBuffer = new WriteOnlyMessage();
|
||||
subBody.Body.ServerWrite(tempBuffer);
|
||||
msg.WriteByte((byte)tempBuffer.LengthBytes);
|
||||
msg.WriteBytes(tempBuffer.Buffer, 0, tempBuffer.LengthBytes);
|
||||
msg.WritePadBits();
|
||||
}
|
||||
|
||||
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
|
||||
|
||||
@@ -1745,9 +1745,9 @@ namespace Barotrauma.Networking
|
||||
continue;
|
||||
}
|
||||
|
||||
IWriteMessage tempBuffer = new ReadWriteMessage();
|
||||
tempBuffer.WriteBoolean(entity is Item); tempBuffer.WritePadBits();
|
||||
tempBuffer.WriteUInt32(entity is MapEntity me ? me.Prefab.UintIdentifier : (UInt32)0);
|
||||
var tempBuffer = new ReadWriteMessage();
|
||||
var entityPositionHeader = EntityPositionHeader.FromEntity(entity);
|
||||
tempBuffer.WriteNetSerializableStruct(entityPositionHeader);
|
||||
entityPositionSync.ServerWritePosition(tempBuffer, c);
|
||||
|
||||
//no more room in this packet
|
||||
@@ -1758,6 +1758,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
segmentTable.StartNewSegment(ServerNetSegment.EntityPosition);
|
||||
outmsg.WritePadBits(); //padding is required here to make sure any padding bits within tempBuffer are read correctly
|
||||
outmsg.WriteVariableUInt32((uint)tempBuffer.LengthBytes);
|
||||
outmsg.WriteBytes(tempBuffer.Buffer, 0, tempBuffer.LengthBytes);
|
||||
outmsg.WritePadBits();
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>100.11.0.0</Version>
|
||||
<Version>100.13.0.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
@@ -29,6 +29,7 @@
|
||||
<DebugType>full</DebugType>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DefineConstants>TRACE;SERVER;WINDOWS;USE_STEAM</DefineConstants>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
RadiationEnabled="false"
|
||||
StartingBalanceAmount="High"
|
||||
StartItemSet="easy"
|
||||
MaxMissionCount="3"
|
||||
Difficulty="Easy"/>
|
||||
<CampaignSettings
|
||||
presetname="Normal"
|
||||
@@ -18,12 +19,14 @@
|
||||
RadiationEnabled="false"
|
||||
StartingBalanceAmount="Medium"
|
||||
StartItemSet="normal"
|
||||
MaxMissionCount="2"
|
||||
Difficulty="Medium"/>
|
||||
<CampaignSettings
|
||||
presetname="Hard"
|
||||
TutorialEnabled="false"
|
||||
RadiationEnabled="true"
|
||||
RadiationEnabled="false"
|
||||
StartingBalanceAmount="Low"
|
||||
StartItemSet="hard"
|
||||
MaxMissionCount="1"
|
||||
Difficulty="Hard"/>
|
||||
</CampaignSettingPresets>
|
||||
@@ -352,6 +352,21 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsOnFriendlyTeam(CharacterTeamType myTeam, CharacterTeamType otherTeam)
|
||||
{
|
||||
if (myTeam == otherTeam) { return true; }
|
||||
return myTeam switch
|
||||
{
|
||||
// NPCs are friendly to the same team and the friendly NPCs
|
||||
CharacterTeamType.None or CharacterTeamType.Team1 or CharacterTeamType.Team2 => otherTeam == CharacterTeamType.FriendlyNPC,
|
||||
// Friendly NPCs are friendly to both player teams
|
||||
CharacterTeamType.FriendlyNPC => otherTeam == CharacterTeamType.Team1 || otherTeam == CharacterTeamType.Team2,
|
||||
_ => true
|
||||
};
|
||||
}
|
||||
|
||||
public static bool IsOnFriendlyTeam(Character me, Character other) => IsOnFriendlyTeam(me.TeamID, other.TeamID);
|
||||
|
||||
public void ReequipUnequipped()
|
||||
{
|
||||
foreach (var item in unequippedItems)
|
||||
|
||||
@@ -354,6 +354,10 @@ namespace Barotrauma
|
||||
{
|
||||
targetingTag = "owner";
|
||||
}
|
||||
else if (targetCharacter.AIController is HumanAIController && !IsOnFriendlyTeam(Character, targetCharacter))
|
||||
{
|
||||
targetingTag = "hostile";
|
||||
}
|
||||
else if (AIParams.TryGetTarget(targetCharacter, out CharacterParams.TargetParams tP))
|
||||
{
|
||||
targetingTag = tP.Tag;
|
||||
@@ -364,7 +368,7 @@ namespace Barotrauma
|
||||
{
|
||||
targetingTag = "husk";
|
||||
}
|
||||
else if (!Character.IsFriendly(targetCharacter))
|
||||
else if (!Character.IsSameSpeciesOrGroup(targetCharacter))
|
||||
{
|
||||
if (enemy.CombatStrength > CombatStrength)
|
||||
{
|
||||
@@ -689,12 +693,9 @@ namespace Barotrauma
|
||||
return a.Damage >= selectedTargetingParams.Threshold;
|
||||
}
|
||||
Character attacker = targetCharacter.LastAttackers.LastOrDefault(IsValid)?.Character;
|
||||
//if the attacker has the same targeting tag as the character we're protecting, we can't change the TargetState
|
||||
//otherwise e.g. a pet that's set to follow humans would start attacking all humans (and other pets, since they're considered part of the same group) when a hostile human attacks it
|
||||
//TODO: a way for pets to differentiate hostile and friendly humans?
|
||||
if (attacker?.AiTarget != null && targetCharacter.SpeciesName != GetTargetingTag(attacker.AiTarget) && !attacker.IsFriendly(targetCharacter))
|
||||
if (attacker?.AiTarget != null && !Character.IsSameSpeciesOrGroup(attacker) && !targetCharacter.IsSameSpeciesOrGroup(attacker))
|
||||
{
|
||||
// Attack the character that attacked the target we are protecting
|
||||
// Can't retaliate on characters of same species or group because that would make us hostile to all friendly characters in the same group.
|
||||
ChangeTargetState(attacker, AIState.Attack, selectedTargetingParams.Priority * 2);
|
||||
SelectTarget(attacker.AiTarget);
|
||||
State = AIState.Attack;
|
||||
|
||||
@@ -1514,9 +1514,18 @@ namespace Barotrauma
|
||||
startPos.X += MathHelper.Clamp(Character.AnimController.TargetMovement.X, -1.0f, 1.0f);
|
||||
|
||||
//do a raycast upwards to find any walls
|
||||
float minCeilingDist = Character.AnimController.Collider.Height / 2 + Character.AnimController.Collider.Radius + 0.1f;
|
||||
if (!Character.AnimController.TryGetCollider(0, out PhysicsBody mainCollider))
|
||||
{
|
||||
mainCollider = Character.AnimController.Collider;
|
||||
}
|
||||
float margin = 0.1f;
|
||||
if (shouldCrouch)
|
||||
{
|
||||
margin *= 2;
|
||||
}
|
||||
float minCeilingDist = mainCollider.Height / 2 + mainCollider.Radius + margin;
|
||||
|
||||
shouldCrouch = Submarine.PickBody(startPos, startPos + Vector2.UnitY * minCeilingDist, null, Physics.CollisionWall, customPredicate: (fixture) => { return !(fixture.Body.UserData is Submarine); }) != null;
|
||||
shouldCrouch = Submarine.PickBody(startPos, startPos + Vector2.UnitY * minCeilingDist, null, Physics.CollisionWall, customPredicate: (fixture) => { return fixture.Body.UserData is not Submarine; }) != null;
|
||||
}
|
||||
|
||||
public bool AllowCampaignInteraction()
|
||||
@@ -1589,7 +1598,27 @@ namespace Barotrauma
|
||||
(!requireEquipped || character.HasEquippedItem(i)) &&
|
||||
(predicate == null || predicate(i)), recursive, matchingItems);
|
||||
items = matchingItems;
|
||||
return matchingItems.Any(i => i != null && (containedTag.IsEmpty || i.OwnInventory == null || i.ContainedItems.Any(it => it.HasTag(containedTag) && it.ConditionPercentage > conditionPercentage)));
|
||||
foreach (var item in matchingItems)
|
||||
{
|
||||
if (item == null) { continue; }
|
||||
|
||||
if (containedTag.IsEmpty || item.OwnInventory == null)
|
||||
{
|
||||
//no contained items required, this item's ok
|
||||
return true;
|
||||
}
|
||||
var suitableSlot = item.GetComponent<ItemContainer>().FindSuitableSubContainerIndex(containedTag);
|
||||
if (suitableSlot == null)
|
||||
{
|
||||
//no restrictions on the suitable slot
|
||||
return item.ContainedItems.Any(it => it.HasTag(containedTag) && it.ConditionPercentage > conditionPercentage);
|
||||
}
|
||||
else
|
||||
{
|
||||
return item.ContainedItems.Any(it => it.HasTag(containedTag) && it.ConditionPercentage > conditionPercentage && it.ParentInventory.IsInSlot(it, suitableSlot.Value));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void StructureDamaged(Structure structure, float damageAmount, Character character)
|
||||
@@ -2016,11 +2045,9 @@ namespace Barotrauma
|
||||
public static bool IsFriendly(Character me, Character other, bool onlySameTeam = false)
|
||||
{
|
||||
bool sameTeam = me.TeamID == other.TeamID;
|
||||
bool friendlyTeam = IsOnFriendlyTeam(me, other);
|
||||
bool teamGood = sameTeam || friendlyTeam && !onlySameTeam;
|
||||
bool teamGood = sameTeam || !onlySameTeam && IsOnFriendlyTeam(me, other);
|
||||
if (!teamGood) { return false; }
|
||||
bool speciesGood = other.IsPet || other.SpeciesName == me.SpeciesName || CharacterParams.CompareGroup(me.Group, other.Group);
|
||||
if (!speciesGood) { return false; }
|
||||
if (!me.IsSameSpeciesOrGroup(other)) { return false; }
|
||||
if (me.TeamID == CharacterTeamType.FriendlyNPC && other.TeamID == CharacterTeamType.Team1 && GameMain.GameSession?.GameMode is CampaignMode campaign)
|
||||
{
|
||||
var reputation = campaign.Map?.CurrentLocation?.Reputation;
|
||||
@@ -2029,30 +2056,14 @@ namespace Barotrauma
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!sameTeam && me.TeamID == CharacterTeamType.None && other.IsPet)
|
||||
{
|
||||
// Hostile NPCs are hostile to all pets, unless they are in the same team.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool IsOnFriendlyTeam(CharacterTeamType myTeam, CharacterTeamType otherTeam)
|
||||
{
|
||||
if (myTeam == otherTeam) { return true; }
|
||||
|
||||
switch (myTeam)
|
||||
{
|
||||
case CharacterTeamType.None:
|
||||
case CharacterTeamType.Team1:
|
||||
case CharacterTeamType.Team2:
|
||||
// Only friendly to the same team and friendly NPCs
|
||||
return otherTeam == CharacterTeamType.FriendlyNPC;
|
||||
case CharacterTeamType.FriendlyNPC:
|
||||
// Friendly NPCs are friendly to both teams
|
||||
return otherTeam == CharacterTeamType.Team1 || otherTeam == CharacterTeamType.Team2;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsOnFriendlyTeam(Character me, Character other) => IsOnFriendlyTeam(me.TeamID, other.TeamID);
|
||||
|
||||
public static bool IsActive(Character other) => other != null && !other.Removed && !other.IsDead && !other.IsUnconscious;
|
||||
|
||||
public static bool IsTrueForAllCrewMembers(Character character, Func<HumanAIController, bool> predicate)
|
||||
|
||||
@@ -83,6 +83,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (GameMain.GameSession.RoundDuration < 120.0f &&
|
||||
speaker?.CurrentHull != null &&
|
||||
GameMain.GameSession.Map?.CurrentLocation?.Reputation?.Value >= 0.0f &&
|
||||
(speaker.TeamID == CharacterTeamType.FriendlyNPC || speaker.TeamID == CharacterTeamType.None) &&
|
||||
Character.CharacterList.Any(c => c.TeamID != speaker.TeamID && c.CurrentHull == speaker.CurrentHull))
|
||||
{
|
||||
|
||||
@@ -98,7 +98,7 @@ namespace Barotrauma
|
||||
int containedItemCount = 0;
|
||||
foreach (Item it in container.Inventory.AllItems)
|
||||
{
|
||||
if (CheckItem(it))
|
||||
if (CheckItem(it) && IsInTargetSlot(it))
|
||||
{
|
||||
containedItemCount++;
|
||||
}
|
||||
@@ -118,7 +118,11 @@ namespace Barotrauma
|
||||
Abandon = true;
|
||||
return;
|
||||
}
|
||||
ItemToContain = item ?? character.Inventory.FindItem(i => CheckItem(i) && i.Container != container.Item, recursive: true);
|
||||
ItemToContain = item ?? character.Inventory.FindItem(it =>
|
||||
CheckItem(it) &&
|
||||
//ignore items already in the container, unless we're trying to place to a specific slot, and the item's not in it
|
||||
(it.Container != container.Item || (TargetSlot.HasValue && it.Container.OwnInventory.FindIndex(it) != TargetSlot)),
|
||||
recursive: true);
|
||||
if (ItemToContain != null)
|
||||
{
|
||||
if (!character.CanInteractWith(ItemToContain, checkLinked: false))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace Barotrauma
|
||||
private AIObjectiveGetItem getDivingGear;
|
||||
private AIObjectiveContainItem getOxygen;
|
||||
private Item targetItem;
|
||||
private int? oxygenSourceSlotIndex;
|
||||
|
||||
public const float MIN_OXYGEN = 10;
|
||||
|
||||
@@ -43,12 +44,15 @@ namespace Barotrauma
|
||||
Abandon = true;
|
||||
return;
|
||||
}
|
||||
targetItem = character.Inventory.FindItemByTag(gearTag, true);
|
||||
|
||||
TrySetTargetItem(character.Inventory.FindItemByTag(gearTag, true));
|
||||
if (targetItem == null && gearTag == LIGHT_DIVING_GEAR)
|
||||
{
|
||||
targetItem = character.Inventory.FindItemByTag(HEAVY_DIVING_GEAR, true);
|
||||
TrySetTargetItem(character.Inventory.FindItemByTag(HEAVY_DIVING_GEAR, true));
|
||||
}
|
||||
if (targetItem == null || !character.HasEquippedItem(targetItem, slotType: InvSlotType.OuterClothes | InvSlotType.Head | InvSlotType.InnerClothes) && targetItem.ContainedItems.Any(i => i.HasTag(OXYGEN_SOURCE) && i.Condition > 0))
|
||||
if (targetItem == null ||
|
||||
!character.HasEquippedItem(targetItem, slotType: InvSlotType.OuterClothes | InvSlotType.Head | InvSlotType.InnerClothes) &&
|
||||
targetItem.ContainedItems.Any(it => IsSuitableContainedOxygenSource(it)))
|
||||
{
|
||||
TryAddSubObjective(ref getDivingGear, () =>
|
||||
{
|
||||
@@ -84,7 +88,7 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
float min = GetMinOxygen(character);
|
||||
if (targetItem.OwnInventory != null && targetItem.OwnInventory.AllItems.None(it => it != null && it.HasTag(OXYGEN_SOURCE) && it.Condition > min))
|
||||
if (targetItem.OwnInventory != null && targetItem.OwnInventory.AllItems.None(it => IsSuitableContainedOxygenSource(it)))
|
||||
{
|
||||
TryAddSubObjective(ref getOxygen, () =>
|
||||
{
|
||||
@@ -93,7 +97,7 @@ namespace Barotrauma
|
||||
if (HumanAIController.HasItem(character, OXYGEN_SOURCE, out _, conditionPercentage: min))
|
||||
{
|
||||
character.Speak(TextManager.Get("dialogswappingoxygentank").Value, null, 0, "swappingoxygentank".ToIdentifier(), 30.0f);
|
||||
if (character.Inventory.FindAllItems(i => i.HasTag(OXYGEN_SOURCE) && i.Condition > min).Count == 1)
|
||||
if (character.Inventory.FindAllItems(i => i.HasTag(OXYGEN_SOURCE) && i.Condition > min, recursive: true).Count == 1)
|
||||
{
|
||||
character.Speak(TextManager.Get("dialoglastoxygentank").Value, null, 0.0f, "dialoglastoxygentank".ToIdentifier(), 30.0f);
|
||||
}
|
||||
@@ -109,7 +113,8 @@ namespace Barotrauma
|
||||
AllowToFindDivingGear = false,
|
||||
AllowDangerousPressure = true,
|
||||
ConditionLevel = MIN_OXYGEN,
|
||||
RemoveExistingWhenNecessary = true
|
||||
RemoveExistingWhenNecessary = true,
|
||||
TargetSlot = oxygenSourceSlotIndex
|
||||
};
|
||||
if (container.HasSubContainers)
|
||||
{
|
||||
@@ -167,12 +172,36 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsSuitableContainedOxygenSource(Item item)
|
||||
{
|
||||
return
|
||||
item != null &&
|
||||
item.HasTag(OXYGEN_SOURCE) &&
|
||||
item.Condition > 0 &&
|
||||
(oxygenSourceSlotIndex == null || item.ParentInventory.IsInSlot(item, oxygenSourceSlotIndex.Value));
|
||||
}
|
||||
|
||||
private void TrySetTargetItem(Item item)
|
||||
{
|
||||
if (targetItem == item) { return; }
|
||||
targetItem = item;
|
||||
if (targetItem != null)
|
||||
{
|
||||
oxygenSourceSlotIndex = targetItem.GetComponent<ItemContainer>()?.FindSuitableSubContainerIndex(OXYGEN_SOURCE);
|
||||
}
|
||||
else
|
||||
{
|
||||
oxygenSourceSlotIndex = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
getDivingGear = null;
|
||||
getOxygen = null;
|
||||
targetItem = null;
|
||||
oxygenSourceSlotIndex = null;
|
||||
}
|
||||
|
||||
public static float GetMinOxygen(Character character)
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace Barotrauma
|
||||
Priority = 100;
|
||||
}
|
||||
else if ((objectiveManager.IsCurrentOrder<AIObjectiveGoTo>() || objectiveManager.IsCurrentOrder<AIObjectiveReturn>()) &&
|
||||
character.Submarine != null && !HumanAIController.IsOnFriendlyTeam(character.TeamID, character.Submarine.TeamID))
|
||||
character.Submarine != null && !AIController.IsOnFriendlyTeam(character.TeamID, character.Submarine.TeamID))
|
||||
{
|
||||
// Ordered to follow, hold position, or return back to main sub inside a hostile sub
|
||||
// -> ignore find safety unless we need to find a diving gear
|
||||
|
||||
@@ -413,7 +413,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void ApplyTreatment(Affliction affliction, Item item)
|
||||
{
|
||||
item.ApplyTreatment(character, targetCharacter, targetCharacter.CharacterHealth.GetAfflictionLimb(affliction));
|
||||
|
||||
@@ -412,7 +412,8 @@ namespace Barotrauma
|
||||
private int CalculateCellCount(int minValue, int maxValue)
|
||||
{
|
||||
if (maxValue == 0) { return 0; }
|
||||
float t = MathUtils.InverseLerp(0, 100, Level.Loaded.Difficulty * Config.AgentSpawnCountDifficultyMultiplier);
|
||||
float difficulty = Level.Loaded?.Difficulty ?? 0.0f;
|
||||
float t = MathUtils.InverseLerp(0, 100, difficulty * Config.AgentSpawnCountDifficultyMultiplier);
|
||||
return (int)Math.Round(MathHelper.Lerp(minValue, maxValue, t));
|
||||
}
|
||||
|
||||
@@ -422,7 +423,8 @@ namespace Barotrauma
|
||||
float delay = Config.AgentSpawnDelay;
|
||||
float min = delay;
|
||||
float max = delay * 6;
|
||||
float t = Level.Loaded.Difficulty * Config.AgentSpawnDelayDifficultyMultiplier * Rand.Range(1 - randomFactor, 1 + randomFactor);
|
||||
float difficulty = Level.Loaded?.Difficulty ?? 0.0f;
|
||||
float t = difficulty * Config.AgentSpawnDelayDifficultyMultiplier * Rand.Range(1 - randomFactor, 1 + randomFactor);
|
||||
return MathHelper.Lerp(max, min, MathUtils.InverseLerp(0, 100, t));
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Barotrauma
|
||||
public bool IsAiming => wasAiming;
|
||||
public bool IsAimingMelee => wasAimingMelee;
|
||||
|
||||
protected bool Aiming => aiming || aimingMelee || LockFlippingUntil > Timing.TotalTime && character.IsKeyDown(InputType.Aim);
|
||||
protected bool Aiming => aiming || aimingMelee || FlipLockTime > Timing.TotalTime && character.IsKeyDown(InputType.Aim);
|
||||
|
||||
public float ArmLength => upperArmLength + forearmLength;
|
||||
|
||||
@@ -278,7 +278,11 @@ namespace Barotrauma
|
||||
// We need some margin, because if a hatch has closed, it's possible that the height from floor is slightly negative.
|
||||
public bool IsAboveFloor => GetHeightFromFloor() > -0.1f;
|
||||
|
||||
public float LockFlippingUntil;
|
||||
public float FlipLockTime { get; private set; }
|
||||
public void LockFlipping(float time = 0.2f)
|
||||
{
|
||||
FlipLockTime = (float)Timing.TotalTime + time;
|
||||
}
|
||||
|
||||
public void UpdateUseItem(bool allowMovement, Vector2 handWorldPos)
|
||||
{
|
||||
|
||||
@@ -1023,7 +1023,7 @@ namespace Barotrauma
|
||||
foreach (Limb l in Limbs)
|
||||
{
|
||||
if (l.IsSevered) { continue; }
|
||||
if (!l.DoesFlip) { continue; }
|
||||
if (!l.DoesFlip) { continue; }
|
||||
if (RagdollParams.IsSpritesheetOrientationHorizontal)
|
||||
{
|
||||
//horizontally aligned limbs need to be flipped 180 degrees
|
||||
@@ -1043,7 +1043,7 @@ namespace Barotrauma
|
||||
if (l.IsSevered) { continue; }
|
||||
|
||||
float rotation = l.body.Rotation;
|
||||
if (l.DoesFlip)
|
||||
if (l.DoesMirror)
|
||||
{
|
||||
if (RagdollParams.IsSpritesheetOrientationHorizontal)
|
||||
{
|
||||
|
||||
@@ -431,7 +431,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (Timing.TotalTime > LockFlippingUntil && TargetDir != dir && !IsStuck)
|
||||
if (Timing.TotalTime > FlipLockTime && TargetDir != dir && !IsStuck)
|
||||
{
|
||||
Flip();
|
||||
}
|
||||
@@ -1723,7 +1723,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (target.AnimController.Dir > 0 == WorldPosition.X > target.WorldPosition.X)
|
||||
{
|
||||
target.AnimController.LockFlippingUntil = (float)Timing.TotalTime + 0.5f;
|
||||
target.AnimController.LockFlipping(0.5f);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1822,16 +1822,22 @@ namespace Barotrauma
|
||||
|
||||
public override void Flip()
|
||||
{
|
||||
if (Character == null || Character.Removed)
|
||||
{
|
||||
LogAccessedRemovedCharacterError();
|
||||
return;
|
||||
}
|
||||
|
||||
base.Flip();
|
||||
|
||||
WalkPos = -WalkPos;
|
||||
|
||||
Limb torso = GetLimb(LimbType.Torso);
|
||||
|
||||
Vector2 difference;
|
||||
if (torso == null) { return; }
|
||||
|
||||
Matrix torsoTransform = Matrix.CreateRotationZ(torso.Rotation);
|
||||
|
||||
Vector2 difference;
|
||||
foreach (Item heldItem in character.HeldItems)
|
||||
{
|
||||
if (heldItem?.body != null && !heldItem.Removed && heldItem.GetComponent<Holdable>() != null)
|
||||
|
||||
@@ -57,17 +57,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (limbs == null)
|
||||
{
|
||||
if (!accessRemovedCharacterErrorShown)
|
||||
{
|
||||
string errorMsg = "Attempted to access a potentially removed ragdoll. Character: " + character.Name + ", id: " + character.ID + ", removed: " + character.Removed + ", ragdoll removed: " + !list.Contains(this);
|
||||
errorMsg += '\n' + Environment.StackTrace.CleanupStackTrace();
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce(
|
||||
"Ragdoll.Limbs:AccessRemoved",
|
||||
GameAnalyticsManager.ErrorSeverity.Error,
|
||||
"Attempted to access a potentially removed ragdoll. Character: " + character.SpeciesName + ", id: " + character.ID + ", removed: " + character.Removed + ", ragdoll removed: " + !list.Contains(this) + "\n" + Environment.StackTrace.CleanupStackTrace());
|
||||
accessRemovedCharacterErrorShown = true;
|
||||
}
|
||||
LogAccessedRemovedCharacterError();
|
||||
return Array.Empty<Limb>();
|
||||
}
|
||||
return limbs;
|
||||
@@ -158,6 +148,20 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetCollider(int index, out PhysicsBody collider)
|
||||
{
|
||||
collider = null;
|
||||
try
|
||||
{
|
||||
collider = this.collider?[index];
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int ColliderIndex
|
||||
{
|
||||
get
|
||||
@@ -881,7 +885,7 @@ namespace Barotrauma
|
||||
|
||||
foreach (Limb limb in Limbs)
|
||||
{
|
||||
if (limb == null || limb.IsSevered || !limb.DoesFlip) { continue; }
|
||||
if (limb == null || limb.IsSevered || !limb.DoesMirror) { continue; }
|
||||
limb.Dir = Dir;
|
||||
limb.MouthPos = new Vector2(-limb.MouthPos.X, limb.MouthPos.Y);
|
||||
limb.MirrorPullJoint();
|
||||
@@ -1436,6 +1440,21 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void LogAccessedRemovedCharacterError()
|
||||
{
|
||||
if (!accessRemovedCharacterErrorShown)
|
||||
{
|
||||
string errorMsg = "Attempted to access a potentially removed ragdoll. Character: " + character.Name + ", id: " + character.ID + ", removed: " + character.Removed + ", ragdoll removed: " + !list.Contains(this);
|
||||
errorMsg += '\n' + Environment.StackTrace.CleanupStackTrace();
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce(
|
||||
"Ragdoll:AccessRemoved",
|
||||
GameAnalyticsManager.ErrorSeverity.Error,
|
||||
"Attempted to access a potentially removed ragdoll. Character: " + character.SpeciesName + ", id: " + character.ID + ", removed: " + character.Removed + ", ragdoll removed: " + !list.Contains(this) + "\n" + Environment.StackTrace.CleanupStackTrace());
|
||||
accessRemovedCharacterErrorShown = true;
|
||||
}
|
||||
}
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime, Camera cam);
|
||||
|
||||
partial void Splash(Limb limb, Hull limbHull);
|
||||
|
||||
@@ -498,37 +498,52 @@ namespace Barotrauma
|
||||
DamageParticles(deltaTime, worldPosition);
|
||||
|
||||
var attackResult = target?.AddDamage(attacker, worldPosition, this, deltaTime, playSound) ?? new AttackResult();
|
||||
var effectType = attackResult.Damage > 0.0f ? ActionType.OnUse : ActionType.OnFailure;
|
||||
var conditionalEffectType = attackResult.Damage > 0.0f ? ActionType.OnSuccess : ActionType.OnFailure;
|
||||
var additionalEffectType = ActionType.OnUse;
|
||||
if (targetCharacter != null && targetCharacter.IsDead)
|
||||
{
|
||||
effectType = ActionType.OnEating;
|
||||
additionalEffectType = ActionType.OnEating;
|
||||
}
|
||||
|
||||
foreach (StatusEffect effect in statusEffects)
|
||||
{
|
||||
effect.sourceBody = sourceBody;
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.This))
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.This) || effect.HasTargetType(StatusEffect.TargetType.Character))
|
||||
{
|
||||
// TODO: do we want to apply the effect at the world position or the entity positions in each cases? -> go through also other cases where status effects are applied
|
||||
effect.Apply(effectType, deltaTime, attacker, sourceLimb ?? attacker as ISerializableEntity, worldPosition);
|
||||
var t = sourceLimb ?? attacker as ISerializableEntity;
|
||||
if (additionalEffectType != ActionType.OnEating)
|
||||
{
|
||||
effect.Apply(conditionalEffectType, deltaTime, attacker, t, worldPosition);
|
||||
}
|
||||
effect.Apply(additionalEffectType, deltaTime, attacker, t, worldPosition);
|
||||
}
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.Parent))
|
||||
{
|
||||
effect.Apply(effectType, deltaTime, attacker, attacker);
|
||||
if (additionalEffectType != ActionType.OnEating)
|
||||
{
|
||||
effect.Apply(conditionalEffectType, deltaTime, attacker, attacker);
|
||||
}
|
||||
effect.Apply(additionalEffectType, deltaTime, attacker, attacker);
|
||||
}
|
||||
if (targetCharacter != null)
|
||||
{
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.Character))
|
||||
{
|
||||
effect.Apply(effectType, deltaTime, targetCharacter, targetCharacter);
|
||||
}
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.Limb))
|
||||
{
|
||||
effect.Apply(effectType, deltaTime, targetCharacter, attackResult.HitLimb);
|
||||
if (additionalEffectType != ActionType.OnEating)
|
||||
{
|
||||
effect.Apply(conditionalEffectType, deltaTime, targetCharacter, attackResult.HitLimb);
|
||||
}
|
||||
effect.Apply(additionalEffectType, deltaTime, targetCharacter, attackResult.HitLimb);
|
||||
}
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.AllLimbs))
|
||||
{
|
||||
effect.Apply(effectType, deltaTime, targetCharacter, targetCharacter.AnimController.Limbs.Cast<ISerializableEntity>().ToList());
|
||||
// TODO: do we need the conversion to list here? It generates garbage.
|
||||
var targets = targetCharacter.AnimController.Limbs.Cast<ISerializableEntity>().ToList();
|
||||
if (additionalEffectType != ActionType.OnEating)
|
||||
{
|
||||
effect.Apply(conditionalEffectType, deltaTime, targetCharacter, targets);
|
||||
}
|
||||
effect.Apply(additionalEffectType, deltaTime, targetCharacter, targets);
|
||||
}
|
||||
}
|
||||
if (target is Entity targetEntity)
|
||||
@@ -538,18 +553,30 @@ namespace Barotrauma
|
||||
{
|
||||
targets.Clear();
|
||||
effect.AddNearbyTargets(worldPosition, targets);
|
||||
effect.Apply(effectType, deltaTime, targetEntity, targets);
|
||||
if (additionalEffectType != ActionType.OnEating)
|
||||
{
|
||||
effect.Apply(conditionalEffectType, deltaTime, targetEntity, targets);
|
||||
}
|
||||
effect.Apply(additionalEffectType, deltaTime, targetEntity, targets);
|
||||
}
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.UseTarget))
|
||||
{
|
||||
effect.Apply(effectType, deltaTime, targetEntity, attacker, worldPosition);
|
||||
if (additionalEffectType != ActionType.OnEating)
|
||||
{
|
||||
effect.Apply(conditionalEffectType, deltaTime, targetEntity, targetEntity as ISerializableEntity, worldPosition);
|
||||
}
|
||||
effect.Apply(additionalEffectType, deltaTime, targetEntity, targetEntity as ISerializableEntity, worldPosition);
|
||||
}
|
||||
}
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.Contained))
|
||||
{
|
||||
targets.Clear();
|
||||
targets.AddRange(attacker.Inventory.AllItems);
|
||||
effect.Apply(effectType, deltaTime, attacker, targets);
|
||||
if (additionalEffectType != ActionType.OnEating)
|
||||
{
|
||||
effect.Apply(conditionalEffectType, deltaTime, attacker, targets);
|
||||
}
|
||||
effect.Apply(additionalEffectType, deltaTime, attacker, targets);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -585,47 +612,52 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
var attackResult = targetLimb.character.ApplyAttack(attacker, worldPosition, this, deltaTime, playSound, targetLimb, penetration);
|
||||
var effectType = attackResult.Damage > 0.0f ? ActionType.OnUse : ActionType.OnFailure;
|
||||
var conditionalEffectType = attackResult.Damage > 0.0f ? ActionType.OnSuccess : ActionType.OnFailure;
|
||||
|
||||
foreach (StatusEffect effect in statusEffects)
|
||||
{
|
||||
effect.sourceBody = sourceBody;
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.This))
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.This) || effect.HasTargetType(StatusEffect.TargetType.Character))
|
||||
{
|
||||
effect.Apply(effectType, deltaTime, attacker, sourceLimb ?? attacker as ISerializableEntity);
|
||||
effect.Apply(conditionalEffectType, deltaTime, attacker, sourceLimb ?? attacker as ISerializableEntity);
|
||||
effect.Apply(ActionType.OnUse, deltaTime, attacker, sourceLimb ?? attacker as ISerializableEntity);
|
||||
}
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.Parent))
|
||||
{
|
||||
effect.Apply(effectType, deltaTime, attacker, attacker);
|
||||
effect.Apply(conditionalEffectType, deltaTime, attacker, attacker);
|
||||
effect.Apply(ActionType.OnUse, deltaTime, attacker, attacker);
|
||||
}
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.Character))
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.UseTarget))
|
||||
{
|
||||
effect.Apply(effectType, deltaTime, targetLimb.character, targetLimb.character);
|
||||
effect.Apply(conditionalEffectType, deltaTime, targetLimb.character, targetLimb.character);
|
||||
effect.Apply(ActionType.OnUse, deltaTime, targetLimb.character, targetLimb.character);
|
||||
}
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.Limb))
|
||||
{
|
||||
effect.Apply(effectType, deltaTime, targetLimb.character, targetLimb);
|
||||
effect.Apply(conditionalEffectType, deltaTime, targetLimb.character, targetLimb);
|
||||
effect.Apply(ActionType.OnUse, deltaTime, targetLimb.character, targetLimb);
|
||||
}
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.AllLimbs))
|
||||
{
|
||||
effect.Apply(effectType, deltaTime, targetLimb.character, targetLimb.character.AnimController.Limbs.Cast<ISerializableEntity>().ToList());
|
||||
// TODO: do we need the conversion to list here? It generates garbage.
|
||||
var targets = targetLimb.character.AnimController.Limbs.Cast<ISerializableEntity>().ToList();
|
||||
effect.Apply(conditionalEffectType, deltaTime, targetLimb.character, targets);
|
||||
effect.Apply(ActionType.OnUse, deltaTime, targetLimb.character, targets);
|
||||
}
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.NearbyItems) ||
|
||||
effect.HasTargetType(StatusEffect.TargetType.NearbyCharacters))
|
||||
{
|
||||
targets.Clear();
|
||||
effect.AddNearbyTargets(worldPosition, targets);
|
||||
effect.Apply(effectType, deltaTime, targetLimb.character, targets);
|
||||
}
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.UseTarget))
|
||||
{
|
||||
effect.Apply(effectType, deltaTime, targetLimb.character, attacker, worldPosition);
|
||||
effect.Apply(conditionalEffectType, deltaTime, targetLimb.character, targets);
|
||||
effect.Apply(ActionType.OnUse, deltaTime, targetLimb.character, targets);
|
||||
}
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.Contained))
|
||||
{
|
||||
targets.Clear();
|
||||
targets.AddRange(attacker.Inventory.AllItems);
|
||||
effect.Apply(effectType, deltaTime, attacker, targets);
|
||||
effect.Apply(conditionalEffectType, deltaTime, attacker, targets);
|
||||
effect.Apply(ActionType.OnUse, deltaTime, attacker, targets);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -365,9 +365,15 @@ namespace Barotrauma
|
||||
public readonly CharacterPrefab Prefab;
|
||||
|
||||
public readonly CharacterParams Params;
|
||||
|
||||
public Identifier SpeciesName => Params?.SpeciesName ?? "null".ToIdentifier();
|
||||
|
||||
public Identifier Group => HumanPrefab is HumanPrefab humanPrefab && !humanPrefab.Group.IsEmpty ? humanPrefab.Group : Params.Group;
|
||||
|
||||
public bool IsHumanoid => Params.Humanoid;
|
||||
|
||||
public bool IsMachine => Params.IsMachine;
|
||||
|
||||
public bool IsHusk => Params.Husk;
|
||||
|
||||
public bool IsMale => info?.IsMale ?? false;
|
||||
@@ -1613,7 +1619,7 @@ namespace Barotrauma
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to give job items for the character \"{Name}\" - could not find human prefab with the id \"{info.HumanPrefabIds.NpcIdentifier}\" from \"{info.HumanPrefabIds.NpcSetIdentifier}\".");
|
||||
}
|
||||
else if (humanPrefab.GiveItems(this, Submarine, spawnPoint))
|
||||
else if (humanPrefab.GiveItems(this, spawnPoint?.Submarine ?? Submarine, spawnPoint))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -1752,7 +1758,7 @@ namespace Barotrauma
|
||||
float maxSpeed = ApplyTemporarySpeedLimits(currentSpeed);
|
||||
targetMovement.X = MathHelper.Clamp(targetMovement.X, -maxSpeed, maxSpeed);
|
||||
targetMovement.Y = MathHelper.Clamp(targetMovement.Y, -maxSpeed, maxSpeed);
|
||||
SpeedMultiplier = greatestPositiveSpeedMultiplier - (1f - greatestNegativeSpeedMultiplier);
|
||||
SpeedMultiplier = Math.Max(0.0f, greatestPositiveSpeedMultiplier - (1f - greatestNegativeSpeedMultiplier));
|
||||
targetMovement *= SpeedMultiplier;
|
||||
// Reset, status effects will set the value before the next update
|
||||
ResetSpeedMultiplier();
|
||||
@@ -3881,8 +3887,8 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (Affliction affliction in attackResult.Afflictions)
|
||||
{
|
||||
if (affliction.Strength == 0.0f) continue;
|
||||
sb.Append($" {affliction.Prefab.Name}: {affliction.Strength}");
|
||||
if (Math.Abs(affliction.Strength) <= 0.1f) { continue;}
|
||||
sb.Append($" {affliction.Prefab.Name}: {affliction.Strength.ToString("0.0")}");
|
||||
}
|
||||
}
|
||||
GameServer.Log(sb.ToString(), ServerLog.MessageType.Attack);
|
||||
@@ -4481,7 +4487,10 @@ namespace Barotrauma
|
||||
|
||||
#if CLIENT
|
||||
//ensure we apply any pending inventory updates to drop any items that need to be dropped when the character despawns
|
||||
Inventory?.ApplyReceivedState();
|
||||
if (GameMain.Client?.ClientPeer is { IsActive: true })
|
||||
{
|
||||
Inventory?.ApplyReceivedState();
|
||||
}
|
||||
#endif
|
||||
|
||||
base.Remove();
|
||||
@@ -5197,15 +5206,13 @@ namespace Barotrauma
|
||||
|
||||
public void RemoveAbilityResistance(TalentResistanceIdentifier identifier) => abilityResistances.Remove(identifier);
|
||||
|
||||
/// <summary>
|
||||
/// Compares just the species name and the group, ignores teams. There's a more complex version found in HumanAIController.cs
|
||||
/// </summary>
|
||||
public bool IsFriendly(Character other) => IsFriendly(this, other);
|
||||
|
||||
/// <summary>
|
||||
/// Compares just the species name and the group, ignores teams. There's a more complex version found in HumanAIController.cs
|
||||
/// </summary>
|
||||
public static bool IsFriendly(Character me, Character other) => other.SpeciesName == me.SpeciesName || CharacterParams.CompareGroup(me.Group, other.Group);
|
||||
public static bool IsFriendly(Character me, Character other) => AIController.IsOnFriendlyTeam(me, other) && IsSameSpeciesOrGroup(me, other);
|
||||
|
||||
public bool IsSameSpeciesOrGroup(Character other) => IsSameSpeciesOrGroup(this, other);
|
||||
|
||||
public static bool IsSameSpeciesOrGroup(Character me, Character other) => other.SpeciesName == me.SpeciesName || CharacterParams.CompareGroup(me.Group, other.Group);
|
||||
|
||||
public void StopClimbing()
|
||||
{
|
||||
|
||||
@@ -779,9 +779,10 @@ namespace Barotrauma
|
||||
FacialHairColors = CharacterConfigElement.GetAttributeTupleArray("facialhaircolors", new (Color, float)[] { (Color.WhiteSmoke, 100f) }).ToImmutableArray();
|
||||
SkinColors = CharacterConfigElement.GetAttributeTupleArray("skincolors", new (Color, float)[] { (new Color(255, 215, 200, 255), 100f) }).ToImmutableArray();
|
||||
|
||||
Head.SkinColor = infoElement.GetAttributeColor("skincolor", Color.White);
|
||||
Head.HairColor = infoElement.GetAttributeColor("haircolor", Color.White);
|
||||
Head.FacialHairColor = infoElement.GetAttributeColor("facialhaircolor", Color.White);
|
||||
//default to transparent color, it's invalid and will be replaced with a random one in CheckColors
|
||||
Head.SkinColor = infoElement.GetAttributeColor("skincolor", Color.Transparent);
|
||||
Head.HairColor = infoElement.GetAttributeColor("haircolor", Color.Transparent);
|
||||
Head.FacialHairColor = infoElement.GetAttributeColor("facialhaircolor", Color.Transparent);
|
||||
CheckColors();
|
||||
|
||||
TryLoadNameAndTitle(npcIdentifier);
|
||||
|
||||
@@ -146,12 +146,6 @@ namespace Barotrauma
|
||||
{
|
||||
return minVitality;
|
||||
}
|
||||
|
||||
if (Character.HasAbilityFlag(AbilityFlags.CanNotDieToAfflictions))
|
||||
{
|
||||
return Math.Max(vitality, MinVitality + 1);
|
||||
}
|
||||
|
||||
return vitality;
|
||||
|
||||
}
|
||||
@@ -587,6 +581,15 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void KillIfOutOfVitality()
|
||||
{
|
||||
if (Vitality <= MinVitality &&
|
||||
!Character.HasAbilityFlag(AbilityFlags.CanNotDieToAfflictions))
|
||||
{
|
||||
Kill();
|
||||
}
|
||||
}
|
||||
|
||||
private readonly static List<Affliction> afflictionsToRemove = new List<Affliction>();
|
||||
private readonly static List<KeyValuePair<Affliction, LimbHealth>> afflictionsToUpdate = new List<KeyValuePair<Affliction, LimbHealth>>();
|
||||
public void SetAllDamage(float damageAmount, float bleedingDamageAmount, float burnDamageAmount)
|
||||
@@ -611,7 +614,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
CalculateVitality();
|
||||
if (Vitality <= MinVitality) { Kill(); }
|
||||
KillIfOutOfVitality();
|
||||
}
|
||||
|
||||
public float GetLimbDamage(Limb limb, string afflictionType = null)
|
||||
@@ -729,10 +732,7 @@ namespace Barotrauma
|
||||
existingAffliction.Duration = existingAffliction.Prefab.Duration;
|
||||
if (newAffliction.Source != null) { existingAffliction.Source = newAffliction.Source; }
|
||||
CalculateVitality();
|
||||
if (Vitality <= MinVitality)
|
||||
{
|
||||
Kill();
|
||||
}
|
||||
KillIfOutOfVitality();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -746,10 +746,7 @@ namespace Barotrauma
|
||||
Character.HealthUpdateInterval = 0.0f;
|
||||
|
||||
CalculateVitality();
|
||||
if (Vitality <= MinVitality)
|
||||
{
|
||||
Kill();
|
||||
}
|
||||
KillIfOutOfVitality();
|
||||
#if CLIENT
|
||||
if (OpenHealthWindow != this && limbHealth != null)
|
||||
{
|
||||
@@ -844,11 +841,7 @@ namespace Barotrauma
|
||||
}
|
||||
#endif
|
||||
CalculateVitality();
|
||||
|
||||
if (Vitality <= MinVitality)
|
||||
{
|
||||
Kill();
|
||||
}
|
||||
KillIfOutOfVitality();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -879,7 +872,11 @@ namespace Barotrauma
|
||||
|
||||
private void UpdateOxygen(float deltaTime)
|
||||
{
|
||||
if (!Character.NeedsOxygen) { return; }
|
||||
if (!Character.NeedsOxygen)
|
||||
{
|
||||
oxygenLowAffliction.Strength = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
float oxygenlowResistance = GetResistance(oxygenLowAffliction.Prefab);
|
||||
float prevOxygen = OxygenAmount;
|
||||
@@ -1025,17 +1022,18 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
private readonly List<Affliction> allAfflictions = new List<Affliction>();
|
||||
private List<Affliction> GetAllAfflictions(bool mergeSameAfflictions)
|
||||
private List<Affliction> GetAllAfflictions(bool mergeSameAfflictions, Func<Affliction, bool> predicate = null)
|
||||
{
|
||||
allAfflictions.Clear();
|
||||
if (!mergeSameAfflictions)
|
||||
{
|
||||
allAfflictions.AddRange(afflictions.Keys);
|
||||
allAfflictions.AddRange(predicate == null ? afflictions.Keys : afflictions.Keys.Where(predicate));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (Affliction affliction in afflictions.Keys)
|
||||
{
|
||||
if (predicate != null && !predicate(affliction)) { continue; }
|
||||
var existingAffliction = allAfflictions.Find(a => a.Prefab == affliction.Prefab);
|
||||
if (existingAffliction == null)
|
||||
{
|
||||
|
||||
@@ -200,7 +200,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
partial class Limb : ISerializableEntity, ISpatialEntity
|
||||
{
|
||||
//how long it takes for severed limbs to fade out
|
||||
@@ -215,7 +215,7 @@ namespace Barotrauma
|
||||
|
||||
//the physics body of the limb
|
||||
public PhysicsBody body;
|
||||
|
||||
|
||||
public Vector2 StepOffset => ConvertUnits.ToSimUnits(Params.StepOffset) * ragdoll.RagdollParams.JointScale;
|
||||
|
||||
public Hull Hull;
|
||||
@@ -249,7 +249,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private bool isSevered;
|
||||
private float severedFadeOutTimer;
|
||||
|
||||
@@ -269,7 +269,7 @@ namespace Barotrauma
|
||||
mouthPos = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public readonly Attack attack;
|
||||
public List<DamageModifier> DamageModifiers { get; private set; } = new List<DamageModifier>();
|
||||
|
||||
@@ -282,39 +282,73 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
if (character.AnimController.CurrentAnimationParams is GroundedMovementParams)
|
||||
if (character?.AnimController.CurrentAnimationParams is GroundedMovementParams && IsLeg)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case LimbType.LeftFoot:
|
||||
case LimbType.LeftLeg:
|
||||
case LimbType.LeftThigh:
|
||||
case LimbType.RightFoot:
|
||||
case LimbType.RightLeg:
|
||||
case LimbType.RightThigh:
|
||||
// Legs always has to flip
|
||||
return true;
|
||||
}
|
||||
// Legs always has to flip when not swimming
|
||||
return true;
|
||||
}
|
||||
return Params.Flip;
|
||||
}
|
||||
}
|
||||
|
||||
public bool DoesMirror
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsLeg)
|
||||
{
|
||||
// Legs always has to mirror
|
||||
return true;
|
||||
}
|
||||
return DoesFlip;
|
||||
}
|
||||
}
|
||||
|
||||
public float SteerForce => Params.SteerForce;
|
||||
|
||||
public Vector2 DebugTargetPos;
|
||||
public Vector2 DebugRefPos;
|
||||
|
||||
public bool IsLowerBody =>
|
||||
type == LimbType.LeftLeg ||
|
||||
type == LimbType.RightLeg ||
|
||||
type == LimbType.LeftFoot ||
|
||||
type == LimbType.RightFoot ||
|
||||
type == LimbType.Tail ||
|
||||
type == LimbType.Legs ||
|
||||
type == LimbType.RightThigh ||
|
||||
type == LimbType.LeftThigh ||
|
||||
type == LimbType.Waist;
|
||||
public bool IsLowerBody
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case LimbType.LeftLeg:
|
||||
case LimbType.RightLeg:
|
||||
case LimbType.LeftFoot:
|
||||
case LimbType.RightFoot:
|
||||
case LimbType.Tail:
|
||||
case LimbType.Legs:
|
||||
case LimbType.LeftThigh:
|
||||
case LimbType.RightThigh:
|
||||
case LimbType.Waist:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsLeg
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case LimbType.LeftFoot:
|
||||
case LimbType.LeftLeg:
|
||||
case LimbType.LeftThigh:
|
||||
case LimbType.RightFoot:
|
||||
case LimbType.RightLeg:
|
||||
case LimbType.RightThigh:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSevered
|
||||
{
|
||||
|
||||
@@ -75,13 +75,14 @@ namespace Barotrauma.Abilities
|
||||
if (wt == WeaponType.Any || !weapontype.HasFlag(wt)) { continue; }
|
||||
switch (wt)
|
||||
{
|
||||
// it is possible that an item that has both a melee and a projectile component will return true
|
||||
// even when not used as a melee/ranged weapon respectively
|
||||
// attackdata should contain data regarding whether the attack is melee or not
|
||||
case WeaponType.Melee:
|
||||
//if the item has an active projectile component (has been fired), don't consider it a melee weapon
|
||||
if (item?.GetComponent<Projectile>() is { IsActive: true }) { continue; }
|
||||
if (item?.GetComponent<MeleeWeapon>() != null) { return true; }
|
||||
break;
|
||||
case WeaponType.Ranged:
|
||||
//if the item has a melee weapon component that's being used now, don't consider it a projectile
|
||||
if (item?.GetComponent<MeleeWeapon>() is { Hitting: true }) { continue; }
|
||||
if (item?.GetComponent<Projectile>() != null) { return true; }
|
||||
break;
|
||||
case WeaponType.HandheldRanged:
|
||||
|
||||
@@ -17,6 +17,14 @@ namespace Barotrauma.Abilities
|
||||
tags = abilityElement.GetAttributeIdentifierImmutableHashSet("tags", ImmutableHashSet<Identifier>.Empty);
|
||||
}
|
||||
|
||||
public override void InitializeAbility(bool addingFirstTime)
|
||||
{
|
||||
if (addingFirstTime)
|
||||
{
|
||||
VerifyState(conditionsMatched: true, timeSinceLastUpdate: 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void VerifyState(bool conditionsMatched, float timeSinceLastUpdate)
|
||||
{
|
||||
if (conditionsMatched)
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
value = abilityElement.GetAttributeFloat("value", 0f);
|
||||
}
|
||||
|
||||
public override void InitializeAbility(bool addingFirstTime)
|
||||
{
|
||||
VerifyState(conditionsMatched: true, timeSinceLastUpdate: 0.0f);
|
||||
}
|
||||
|
||||
protected override void VerifyState(bool conditionsMatched, float timeSinceLastUpdate)
|
||||
{
|
||||
if (conditionsMatched != lastState)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using Barotrauma.Extensions;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
@@ -17,6 +18,9 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
if (!addingFirstTime) { return; }
|
||||
|
||||
// do not run client-side in multiplayer
|
||||
if (GameMain.NetworkMember is { IsClient: true }) { return; }
|
||||
|
||||
JobPrefab? apprentice = CharacterAbilityApplyStatusEffectsToApprenticeship.GetApprenticeJob(Character, JobPrefab.Prefabs.ToImmutableHashSet());
|
||||
if (apprentice is null)
|
||||
{
|
||||
|
||||
@@ -49,6 +49,16 @@ namespace Barotrauma.Abilities
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (abilityEffectType)
|
||||
{
|
||||
case AbilityEffectType.OnDieToCharacter:
|
||||
if (characterAbilities.Any(a => a.RequiresAlive))
|
||||
{
|
||||
DebugConsole.AddWarning($"Potential error in talent {characterTalent}: an ability group has the type {AbilityEffectType.OnDieToCharacter}, but includes abilities that require the character to be alive, meaning they will never execute.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void ActivateAbilityGroup(bool addingFirstTime)
|
||||
|
||||
@@ -23,6 +23,8 @@ namespace Barotrauma
|
||||
public const string RegularPackagesElementName = "regularpackages";
|
||||
public const string RegularPackagesSubElementName = "package";
|
||||
|
||||
public static bool ModsEnabled => GameMain.VanillaContent == null || EnabledPackages.All.Any(p => p.HasMultiplayerSyncedContent && p != GameMain.VanillaContent);
|
||||
|
||||
public static class EnabledPackages
|
||||
{
|
||||
public static CorePackage? Core { get; private set; } = null;
|
||||
|
||||
@@ -88,6 +88,7 @@ namespace Barotrauma
|
||||
public T GetAttributeEnum<T>(string key, in T def) where T : struct, Enum => Element.GetAttributeEnum(key, def);
|
||||
public (T1, T2) GetAttributeTuple<T1, T2>(string key, in (T1, T2) def) => Element.GetAttributeTuple(key, def);
|
||||
public (T1, T2)[] GetAttributeTupleArray<T1, T2>(string key, in (T1, T2)[] def) => Element.GetAttributeTupleArray(key, def);
|
||||
public Range<int> GetAttributeRange(string key, in Range<int> def) => Element.GetAttributeRange(key, def);
|
||||
|
||||
public Identifier VariantOf() => Element.VariantOf();
|
||||
|
||||
|
||||
@@ -1263,6 +1263,22 @@ namespace Barotrauma
|
||||
}
|
||||
#endif
|
||||
|
||||
commands.Add(new Command("showreputation", "showreputation: List the current reputation values.", (string[] args) =>
|
||||
{
|
||||
if (GameMain.GameSession?.GameMode is CampaignMode campaign)
|
||||
{
|
||||
NewMessage("Reputation:");
|
||||
foreach (var faction in campaign.Factions)
|
||||
{
|
||||
NewMessage($" - {faction.Prefab.Name}: {faction.Reputation.Value}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowError("Could not show reputation (no active campaign).");
|
||||
}
|
||||
}, null));
|
||||
|
||||
commands.Add(new Command("setlocationreputation", "setlocationreputation [value]: Set the reputation in the current location to the specified value.", (string[] args) =>
|
||||
{
|
||||
if (GameMain.GameSession?.GameMode is CampaignMode campaign)
|
||||
@@ -1315,10 +1331,10 @@ namespace Barotrauma
|
||||
}
|
||||
}, () =>
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
FactionPrefab.Prefabs.Select(f => f.Identifier.Value).ToArray(),
|
||||
GameMain.GameSession?.Campaign.Factions.Select(f => f.Prefab.Identifier.ToString()).ToArray() ?? Array.Empty<string>()
|
||||
return new[]
|
||||
{
|
||||
FactionPrefab.Prefabs.Select(static f => f.Identifier.Value).ToArray(),
|
||||
GameMain.GameSession?.Campaign?.Factions.Select(static f => f.Prefab.Identifier.ToString()).ToArray() ?? Array.Empty<string>()
|
||||
};
|
||||
}, true));
|
||||
|
||||
|
||||
@@ -171,7 +171,7 @@ namespace Barotrauma
|
||||
PumpSpeed,
|
||||
PumpMaxFlow,
|
||||
ReactorMaxOutput,
|
||||
ReactorFuelEfficiency,
|
||||
ReactorFuelConsumption,
|
||||
DeconstructorSpeed,
|
||||
FabricationSpeed
|
||||
}
|
||||
|
||||
@@ -200,7 +200,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private int[] GetEndingOptions()
|
||||
public int[] GetEndingOptions()
|
||||
{
|
||||
List<int> endings = Options.Where(group => !group.Actions.Any() || group.EndConversation).Select(group => Options.IndexOf(group)).ToList();
|
||||
if (!ContinueConversation) { endings.Add(-1); }
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace Barotrauma
|
||||
var emptyLocation = FindUnlockLocation(Math.Max(MinLocationDistance, 3), unlockFurtherOnMap: true, "none".ToIdentifier().ToEnumerable());
|
||||
if (emptyLocation != null)
|
||||
{
|
||||
emptyLocation.ChangeType(campaign, Barotrauma.LocationType.Prefabs[LocationTypes[0]]);
|
||||
emptyLocation.ChangeType(campaign, LocationType.Prefabs[LocationTypes[0]]);
|
||||
unlockLocation = emptyLocation;
|
||||
}
|
||||
}
|
||||
@@ -77,7 +77,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (!MissionIdentifier.IsEmpty)
|
||||
{
|
||||
unlockedMission = unlockLocation.UnlockMissionByIdentifier(MissionIdentifier);
|
||||
unlockedMission = unlockLocation.UnlockMissionByIdentifier(MissionIdentifier);
|
||||
}
|
||||
else if (!MissionTag.IsEmpty)
|
||||
{
|
||||
@@ -89,8 +89,9 @@ namespace Barotrauma
|
||||
}
|
||||
if (unlockedMission != null)
|
||||
{
|
||||
unlockedMission.OriginLocation = campaign.Map.CurrentLocation;
|
||||
campaign.Map.Discover(unlockLocation, checkTalents: false);
|
||||
if (unlockedMission.Locations[0] == unlockedMission.Locations[1] || unlockedMission.Locations[1] ==null)
|
||||
if (unlockedMission.Locations[0] == unlockedMission.Locations[1] || unlockedMission.Locations[1] == null)
|
||||
{
|
||||
DebugConsole.NewMessage($"Unlocked mission \"{unlockedMission.Name}\" in the location \"{unlockLocation.Name}\".");
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace Barotrauma
|
||||
public enum NetworkEventType
|
||||
{
|
||||
CONVERSATION,
|
||||
CONVERSATION_SELECTED_OPTION,
|
||||
STATUSEFFECT,
|
||||
MISSION,
|
||||
UNLOCKPATH
|
||||
|
||||
@@ -229,9 +229,8 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
bool requiresRescue = element.GetAttributeBool("requirerescue", false);
|
||||
|
||||
var teamId = element.GetAttributeEnum("teamid", requiresRescue ? CharacterTeamType.FriendlyNPC : CharacterTeamType.None);
|
||||
Character spawnedCharacter = CreateHuman(humanPrefab, characters, characterItems, submarine, teamId, spawnPos, giveTags: true);
|
||||
Character spawnedCharacter = CreateHuman(humanPrefab, characters, characterItems, submarine, teamId, spawnPos);
|
||||
if (Level.Loaded?.StartOutpost?.Info is { } outPostInfo)
|
||||
{
|
||||
outPostInfo.AddOutpostNPCIdentifierOrTag(spawnedCharacter, humanPrefab.Identifier);
|
||||
@@ -240,6 +239,7 @@ namespace Barotrauma
|
||||
outPostInfo.AddOutpostNPCIdentifierOrTag(spawnedCharacter, tag);
|
||||
}
|
||||
}
|
||||
|
||||
if (spawnPos is WayPoint wp)
|
||||
{
|
||||
spawnedCharacter.GiveIdCardTags(wp);
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace Barotrauma
|
||||
List<Submarine> connectedSubs = level.BeaconStation.GetConnectedSubs();
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
if (!connectedSubs.Contains(item.Submarine)) { continue; }
|
||||
if (!connectedSubs.Contains(item.Submarine) || item.Submarine?.Info is { IsPlayer: true }) { continue; }
|
||||
if (item.GetComponent<PowerTransfer>() != null ||
|
||||
item.GetComponent<PowerContainer>() != null ||
|
||||
item.GetComponent<Reactor>() != null ||
|
||||
|
||||
@@ -125,6 +125,12 @@ namespace Barotrauma
|
||||
|
||||
public Identifier SonarIconIdentifier => Prefab.SonarIconIdentifier;
|
||||
|
||||
/// <summary>
|
||||
/// Where was this mission received from? Affects which faction we give reputation for if the mission is configured to give reputation for the faction that gave the mission.
|
||||
/// Defaults to Locations[0]
|
||||
/// </summary>
|
||||
public Location OriginLocation;
|
||||
|
||||
public readonly Location[] Locations;
|
||||
|
||||
public int? Difficulty
|
||||
@@ -144,7 +150,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private List<DelayedTriggerEvent> delayedTriggerEvents = new List<DelayedTriggerEvent>();
|
||||
private readonly List<DelayedTriggerEvent> delayedTriggerEvents = new List<DelayedTriggerEvent>();
|
||||
|
||||
public Action<Mission> OnMissionStateChanged;
|
||||
|
||||
@@ -160,12 +166,13 @@ namespace Barotrauma
|
||||
Headers = prefab.Headers;
|
||||
var messages = prefab.Messages.ToArray();
|
||||
|
||||
OriginLocation = locations[0];
|
||||
Locations = locations;
|
||||
|
||||
var endConditionElement = prefab.ConfigElement.GetChildElement(nameof(completeCheckDataAction));
|
||||
if (endConditionElement != null)
|
||||
{
|
||||
completeCheckDataAction = new CheckDataAction(endConditionElement, $"Mission ({prefab.Identifier.ToString()})");
|
||||
completeCheckDataAction = new CheckDataAction(endConditionElement, $"Mission ({prefab.Identifier})");
|
||||
}
|
||||
|
||||
for (int n = 0; n < 2; n++)
|
||||
@@ -407,7 +414,7 @@ namespace Barotrauma
|
||||
{
|
||||
var experienceGainMultiplierIndividual = new AbilityMissionExperienceGainMultiplier(this, 1f);
|
||||
info?.Character?.CheckTalents(AbilityEffectType.OnGainMissionExperience, experienceGainMultiplierIndividual);
|
||||
info?.GiveExperience((int)(experienceGain * experienceGainMultiplier.Value));
|
||||
info?.GiveExperience((int)((experienceGain * experienceGainMultiplier.Value) * experienceGainMultiplierIndividual.Value));
|
||||
}
|
||||
|
||||
// apply money gains afterwards to prevent them from affecting XP gains
|
||||
@@ -436,7 +443,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (reputationReward.Key == "location")
|
||||
{
|
||||
Locations[0].Reputation?.AddReputation(reputationReward.Value);
|
||||
OriginLocation.Reputation?.AddReputation(reputationReward.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -546,17 +553,14 @@ namespace Barotrauma
|
||||
return humanPrefab;
|
||||
}
|
||||
|
||||
protected Character CreateHuman(HumanPrefab humanPrefab, List<Character> characters, Dictionary<Character, List<Item>> characterItems, Submarine submarine, CharacterTeamType teamType, ISpatialEntity positionToStayIn = null, Rand.RandSync humanPrefabRandSync = Rand.RandSync.ServerAndClient, bool giveTags = true)
|
||||
protected static Character CreateHuman(HumanPrefab humanPrefab, List<Character> characters, Dictionary<Character, List<Item>> characterItems, Submarine submarine, CharacterTeamType teamType, ISpatialEntity positionToStayIn = null, Rand.RandSync humanPrefabRandSync = Rand.RandSync.ServerAndClient)
|
||||
{
|
||||
var characterInfo = humanPrefab.CreateCharacterInfo(Rand.RandSync.ServerAndClient);
|
||||
characterInfo.TeamID = teamType;
|
||||
|
||||
if (positionToStayIn == null)
|
||||
{
|
||||
positionToStayIn =
|
||||
positionToStayIn ??=
|
||||
WayPoint.GetRandom(SpawnType.Human, characterInfo.Job?.Prefab, submarine) ??
|
||||
WayPoint.GetRandom(SpawnType.Human, null, submarine);
|
||||
}
|
||||
|
||||
Character spawnedCharacter = Character.Create(characterInfo.SpeciesName, positionToStayIn.WorldPosition, ToolBox.RandomSeed(8), characterInfo, createNetworkEvent: false);
|
||||
spawnedCharacter.HumanPrefab = humanPrefab;
|
||||
|
||||
@@ -201,7 +201,7 @@ namespace Barotrauma
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (Level.Loaded.ExtraWalls.Any(w => w.IsPointInside(position.Position.ToVector2())))
|
||||
if (Level.Loaded.IsPositionInsideWall(position.Position.ToVector2()))
|
||||
{
|
||||
removals.Add(position);
|
||||
}
|
||||
|
||||
@@ -371,7 +371,7 @@ namespace Barotrauma
|
||||
if (!SendUserStatistics) { return; }
|
||||
if (sentEventIdentifiers.Contains(identifier)) { return; }
|
||||
|
||||
if (GameMain.VanillaContent == null || ContentPackageManager.EnabledPackages.All.Any(p => p.HasMultiplayerSyncedContent && p != GameMain.VanillaContent))
|
||||
if (ContentPackageManager.ModsEnabled)
|
||||
{
|
||||
message = "[MODDED] " + message;
|
||||
}
|
||||
|
||||
@@ -290,6 +290,16 @@ namespace Barotrauma
|
||||
else if (!character.Info.StartItemsGiven)
|
||||
{
|
||||
character.GiveJobItems(mainSubWaypoints[i]);
|
||||
foreach (Item item in character.Inventory.AllItems)
|
||||
{
|
||||
//if the character is loaded from a human prefab with preconfigured items, its ID card gets assigned to the sub it spawns in
|
||||
//we don't want that in this case, the crew's cards shouldn't be submarine-specific
|
||||
var idCard = item.GetComponent<Items.Components.IdCard>();
|
||||
if (idCard != null)
|
||||
{
|
||||
idCard.SubmarineSpecificID = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (character.Info.HealthData != null)
|
||||
{
|
||||
@@ -298,6 +308,7 @@ namespace Barotrauma
|
||||
|
||||
character.LoadTalents();
|
||||
|
||||
character.GiveIdCardTags(mainSubWaypoints[i]);
|
||||
character.GiveIdCardTags(spawnWaypoints[i]);
|
||||
character.Info.StartItemsGiven = true;
|
||||
if (character.Info.OrderData != null)
|
||||
|
||||
@@ -14,8 +14,9 @@ namespace Barotrauma
|
||||
{
|
||||
}
|
||||
|
||||
public CampaignMetadata(XElement element)
|
||||
public void Load(XElement element)
|
||||
{
|
||||
data.Clear();
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
if (string.Equals(subElement.Name.ToString(), "data", StringComparison.InvariantCultureIgnoreCase))
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Barotrauma
|
||||
/// Get what kind of affiliation this faction has towards the player depending on who they chose to side with via talents
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static FactionAffiliation GetPlayerAffiliationStatus(Identifier identifier, ImmutableHashSet<Character>? characterList = null)
|
||||
public static FactionAffiliation GetPlayerAffiliationStatus(Faction faction, ImmutableHashSet<Character>? characterList = null)
|
||||
{
|
||||
if (GameMain.GameSession?.Campaign?.Factions is not { } factions) { return FactionAffiliation.Neutral; }
|
||||
|
||||
@@ -37,23 +37,20 @@ namespace Barotrauma
|
||||
{
|
||||
if (character.Info is not { } info) { continue; }
|
||||
|
||||
foreach (Faction faction in factions)
|
||||
foreach (Faction otherFaction in factions)
|
||||
{
|
||||
Identifier factionIdentifier = faction.Prefab.Identifier;
|
||||
Identifier factionIdentifier = otherFaction.Prefab.Identifier;
|
||||
if (info.GetSavedStatValue(StatTypes.Affiliation, factionIdentifier) > 0f)
|
||||
{
|
||||
return factionIdentifier == identifier
|
||||
return factionIdentifier == faction.Prefab.Identifier
|
||||
? FactionAffiliation.Positive
|
||||
: FactionAffiliation.Negative;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FactionAffiliation.Neutral;
|
||||
}
|
||||
|
||||
public static FactionAffiliation GetPlayerAffiliationStatus(Faction faction, ImmutableHashSet<Character>? characterList = null) => GetPlayerAffiliationStatus(faction.Prefab.Identifier, characterList);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{base.ToString()} ({Prefab?.Identifier.ToString() ?? "null"})";
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace Barotrauma
|
||||
private List<Faction> factions;
|
||||
public IReadOnlyList<Faction> Factions => factions;
|
||||
|
||||
public CampaignMetadata CampaignMetadata;
|
||||
public readonly CampaignMetadata CampaignMetadata;
|
||||
|
||||
protected XElement petsElement;
|
||||
|
||||
@@ -157,6 +157,7 @@ namespace Barotrauma
|
||||
|
||||
CargoManager = new CargoManager(this);
|
||||
MedicalClinic = new MedicalClinic(this);
|
||||
CampaignMetadata = new CampaignMetadata();
|
||||
Identifier messageIdentifier = new Identifier("money");
|
||||
|
||||
#if CLIENT
|
||||
@@ -675,9 +676,11 @@ namespace Barotrauma
|
||||
//TODO: ignore players who don't have the permission to trigger a transition between levels?
|
||||
var leavingPlayers = Character.CharacterList.Where(c => !c.IsDead && (c == Character.Controlled || c.IsRemotePlayer));
|
||||
|
||||
CharacterTeamType submarineTeam = leavingPlayers.FirstOrDefault()?.TeamID ?? CharacterTeamType.Team1;
|
||||
|
||||
//allow leaving if inside an outpost, and the submarine is either docked to it or close enough
|
||||
Submarine leavingSubAtStart = GetLeavingSubAtStart(leavingPlayers);
|
||||
Submarine leavingSubAtEnd = GetLeavingSubAtEnd(leavingPlayers);
|
||||
Submarine leavingSubAtStart = GetLeavingSubAtStart(leavingPlayers, submarineTeam);
|
||||
Submarine leavingSubAtEnd = GetLeavingSubAtEnd(leavingPlayers, submarineTeam);
|
||||
|
||||
int playersInSubAtStart = leavingSubAtStart == null || !leavingSubAtStart.AtStartExit ? 0 :
|
||||
leavingPlayers.Count(c => c.Submarine == leavingSubAtStart || leavingSubAtStart.DockedTo.Contains(c.Submarine) || (Level.Loaded.StartOutpost != null && c.Submarine == Level.Loaded.StartOutpost));
|
||||
@@ -691,11 +694,11 @@ namespace Barotrauma
|
||||
|
||||
return playersInSubAtStart > playersInSubAtEnd ? leavingSubAtStart : leavingSubAtEnd;
|
||||
|
||||
static Submarine GetLeavingSubAtStart(IEnumerable<Character> leavingPlayers)
|
||||
static Submarine GetLeavingSubAtStart(IEnumerable<Character> leavingPlayers, CharacterTeamType submarineTeam)
|
||||
{
|
||||
if (Level.Loaded.StartOutpost == null)
|
||||
{
|
||||
Submarine closestSub = Submarine.FindClosest(Level.Loaded.StartExitPosition, ignoreOutposts: true, ignoreRespawnShuttle: true, teamType: leavingPlayers.FirstOrDefault()?.TeamID);
|
||||
Submarine closestSub = Submarine.FindClosest(Level.Loaded.StartExitPosition, ignoreOutposts: true, ignoreRespawnShuttle: true, teamType: submarineTeam);
|
||||
if (closestSub == null) { return null; }
|
||||
return closestSub.DockedTo.Contains(Submarine.MainSub) ? Submarine.MainSub : closestSub;
|
||||
}
|
||||
@@ -705,23 +708,23 @@ namespace Barotrauma
|
||||
if (Level.Loaded.StartOutpost.DockedTo.Any())
|
||||
{
|
||||
var dockedSub = Level.Loaded.StartOutpost.DockedTo.FirstOrDefault();
|
||||
if (dockedSub == GameMain.NetworkMember?.RespawnManager?.RespawnShuttle || dockedSub.TeamID != leavingPlayers.FirstOrDefault()?.TeamID) { return null; }
|
||||
if (dockedSub == GameMain.NetworkMember?.RespawnManager?.RespawnShuttle || dockedSub.TeamID != submarineTeam) { return null; }
|
||||
return dockedSub.DockedTo.Contains(Submarine.MainSub) ? Submarine.MainSub : dockedSub;
|
||||
}
|
||||
|
||||
//nothing docked, check if there's a sub close enough to the outpost and someone inside the outpost
|
||||
if (Level.Loaded.Type == LevelData.LevelType.LocationConnection && !leavingPlayers.Any(s => s.Submarine == Level.Loaded.StartOutpost)) { return null; }
|
||||
Submarine closestSub = Submarine.FindClosest(Level.Loaded.StartOutpost.WorldPosition, ignoreOutposts: true, ignoreRespawnShuttle: true, teamType: leavingPlayers.FirstOrDefault()?.TeamID);
|
||||
Submarine closestSub = Submarine.FindClosest(Level.Loaded.StartOutpost.WorldPosition, ignoreOutposts: true, ignoreRespawnShuttle: true, teamType: submarineTeam);
|
||||
if (closestSub == null || !closestSub.AtStartExit) { return null; }
|
||||
return closestSub.DockedTo.Contains(Submarine.MainSub) ? Submarine.MainSub : closestSub;
|
||||
}
|
||||
}
|
||||
|
||||
static Submarine GetLeavingSubAtEnd(IEnumerable<Character> leavingPlayers)
|
||||
static Submarine GetLeavingSubAtEnd(IEnumerable<Character> leavingPlayers, CharacterTeamType submarineTeam)
|
||||
{
|
||||
if (Level.Loaded.EndOutpost != null && Level.Loaded.EndOutpost.ExitPoints.Any())
|
||||
{
|
||||
Submarine closestSub = Submarine.FindClosest(Level.Loaded.EndOutpost.WorldPosition, ignoreOutposts: true, ignoreRespawnShuttle: true, teamType: leavingPlayers.FirstOrDefault()?.TeamID);
|
||||
Submarine closestSub = Submarine.FindClosest(Level.Loaded.EndOutpost.WorldPosition, ignoreOutposts: true, ignoreRespawnShuttle: true, teamType: submarineTeam);
|
||||
if (closestSub == null || !closestSub.AtEndExit) { return null; }
|
||||
return closestSub.DockedTo.Contains(Submarine.MainSub) ? Submarine.MainSub : closestSub;
|
||||
}
|
||||
@@ -733,7 +736,7 @@ namespace Barotrauma
|
||||
|
||||
if (Level.Loaded.EndOutpost == null)
|
||||
{
|
||||
Submarine closestSub = Submarine.FindClosest(Level.Loaded.EndExitPosition, ignoreOutposts: true, ignoreRespawnShuttle: true, teamType: leavingPlayers.FirstOrDefault()?.TeamID);
|
||||
Submarine closestSub = Submarine.FindClosest(Level.Loaded.EndExitPosition, ignoreOutposts: true, ignoreRespawnShuttle: true, teamType: submarineTeam);
|
||||
if (closestSub == null) { return null; }
|
||||
return closestSub.DockedTo.Contains(Submarine.MainSub) ? Submarine.MainSub : closestSub;
|
||||
}
|
||||
@@ -743,13 +746,13 @@ namespace Barotrauma
|
||||
if (Level.Loaded.EndOutpost.DockedTo.Any())
|
||||
{
|
||||
var dockedSub = Level.Loaded.EndOutpost.DockedTo.FirstOrDefault();
|
||||
if (dockedSub == GameMain.NetworkMember?.RespawnManager?.RespawnShuttle || dockedSub.TeamID != leavingPlayers.FirstOrDefault()?.TeamID) { return null; }
|
||||
if (dockedSub == GameMain.NetworkMember?.RespawnManager?.RespawnShuttle || dockedSub.TeamID != submarineTeam) { return null; }
|
||||
return dockedSub.DockedTo.Contains(Submarine.MainSub) ? Submarine.MainSub : dockedSub;
|
||||
}
|
||||
|
||||
//nothing docked, check if there's a sub close enough to the outpost and someone inside the outpost
|
||||
if (Level.Loaded.Type == LevelData.LevelType.LocationConnection && !leavingPlayers.Any(s => s.Submarine == Level.Loaded.EndOutpost)) { return null; }
|
||||
Submarine closestSub = Submarine.FindClosest(Level.Loaded.EndOutpost.WorldPosition, ignoreOutposts: true, ignoreRespawnShuttle: true, teamType: leavingPlayers.FirstOrDefault()?.TeamID);
|
||||
Submarine closestSub = Submarine.FindClosest(Level.Loaded.EndOutpost.WorldPosition, ignoreOutposts: true, ignoreRespawnShuttle: true, teamType: submarineTeam);
|
||||
if (closestSub == null || !closestSub.AtEndExit) { return null; }
|
||||
return closestSub.DockedTo.Contains(Submarine.MainSub) ? Submarine.MainSub : closestSub;
|
||||
}
|
||||
@@ -870,7 +873,7 @@ namespace Barotrauma
|
||||
}
|
||||
foreach (Location location in Map.Locations)
|
||||
{
|
||||
location.LevelData = new LevelData(location, location.Biome.AdjustedMaxDifficulty);
|
||||
location.LevelData = new LevelData(location, Map, location.Biome.AdjustedMaxDifficulty);
|
||||
location.Reset(this);
|
||||
}
|
||||
Map.ClearLocationHistory();
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
public const int DefaultMaxMissionCount = 2;
|
||||
public const int MaxMissionCountLimit = 10;
|
||||
public const int MaxMissionCountLimit = 3;
|
||||
public const int MinMissionCountLimit = 1;
|
||||
|
||||
public Dictionary<Identifier, SerializableProperty> SerializableProperties { get; private set; }
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user