Build 0.21.1.0
This commit is contained in:
@@ -107,7 +107,7 @@ namespace Barotrauma
|
|||||||
Collider.AngularVelocity = newAngularVelocity;
|
Collider.AngularVelocity = newAngularVelocity;
|
||||||
|
|
||||||
float distSqrd = Vector2.DistanceSquared(newPosition, Collider.SimPosition);
|
float distSqrd = Vector2.DistanceSquared(newPosition, Collider.SimPosition);
|
||||||
float errorTolerance = character.CanMove ? 0.01f : 0.2f;
|
float errorTolerance = character.CanMove && !character.IsRagdolled ? 0.01f : 0.2f;
|
||||||
if (distSqrd > errorTolerance)
|
if (distSqrd > errorTolerance)
|
||||||
{
|
{
|
||||||
if (distSqrd > 10.0f || !character.CanMove)
|
if (distSqrd > 10.0f || !character.CanMove)
|
||||||
@@ -145,6 +145,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
MainLimb.PullJointWorldAnchorB = Collider.SimPosition;
|
MainLimb.PullJointWorldAnchorB = Collider.SimPosition;
|
||||||
MainLimb.PullJointEnabled = true;
|
MainLimb.PullJointEnabled = true;
|
||||||
|
MainLimb.body.LinearVelocity = newVelocity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -442,10 +443,20 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
foreach (Limb limb in Limbs)
|
foreach (Limb limb in Limbs)
|
||||||
{
|
{
|
||||||
if (limb == null || limb.IsSevered || limb.ActiveSprite == null || !limb.DoesFlip) { continue; }
|
if (limb == null || limb.IsSevered || !limb.DoesMirror) { continue; }
|
||||||
Vector2 spriteOrigin = limb.ActiveSprite.Origin;
|
|
||||||
spriteOrigin.X = limb.ActiveSprite.SourceRect.Width - spriteOrigin.X;
|
FlipSprite(limb.DeformSprite?.Sprite ?? limb.Sprite);
|
||||||
limb.ActiveSprite.Origin = spriteOrigin;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -616,7 +616,7 @@ namespace Barotrauma
|
|||||||
return closestItem;
|
return closestItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Character FindCharacterAtPosition(Vector2 mouseSimPos, float maxDist = 150.0f)
|
private Character FindCharacterAtPosition(Vector2 mouseSimPos, float maxDist = MaxHighlightDistance)
|
||||||
{
|
{
|
||||||
Character closestCharacter = null;
|
Character closestCharacter = null;
|
||||||
|
|
||||||
@@ -626,7 +626,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
if (!CanInteractWith(c, checkVisibility: false) || (c.AnimController?.SimplePhysicsEnabled ?? true)) { continue; }
|
if (!CanInteractWith(c, checkVisibility: false) || (c.AnimController?.SimplePhysicsEnabled ?? true)) { continue; }
|
||||||
|
|
||||||
float dist = Vector2.DistanceSquared(mouseSimPos, c.SimPosition);
|
float dist = c.GetDistanceToClosestLimb(mouseSimPos);
|
||||||
if (dist < closestDist ||
|
if (dist < closestDist ||
|
||||||
(c.CampaignInteractionType != CampaignMode.InteractionType.None && closestCharacter?.CampaignInteractionType == CampaignMode.InteractionType.None && dist * 0.9f < closestDist))
|
(c.CampaignInteractionType != CampaignMode.InteractionType.None && closestCharacter?.CampaignInteractionType == CampaignMode.InteractionType.None && dist * 0.9f < closestDist))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -608,7 +608,8 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2 startPos = character.DrawPosition + (character.FocusedCharacter.DrawPosition - character.DrawPosition) * 0.7f;
|
float dist = Vector2.Distance(character.FocusedCharacter.DrawPosition, character.DrawPosition);
|
||||||
|
Vector2 startPos = character.DrawPosition + (character.FocusedCharacter.DrawPosition - character.DrawPosition) / dist * Math.Min(dist, Character.MaxDragDistance);
|
||||||
startPos = cam.WorldToScreen(startPos);
|
startPos = cam.WorldToScreen(startPos);
|
||||||
|
|
||||||
string focusName = character.FocusedCharacter.Info == null ? character.FocusedCharacter.DisplayName : character.FocusedCharacter.Info.DisplayName;
|
string focusName = character.FocusedCharacter.Info == null ? character.FocusedCharacter.DisplayName : character.FocusedCharacter.Info.DisplayName;
|
||||||
|
|||||||
@@ -938,7 +938,7 @@ namespace Barotrauma
|
|||||||
var headPreset = obj as HeadPreset;
|
var headPreset = obj as HeadPreset;
|
||||||
if (info.Head.Preset != 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,
|
SkinColor = info.Head.SkinColor,
|
||||||
HairColor = info.Head.HairColor,
|
HairColor = info.Head.HairColor,
|
||||||
|
|||||||
@@ -640,8 +640,7 @@ namespace Barotrauma
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
forceAfflictionContainerUpdate = true;
|
forceAfflictionContainerUpdate = true;
|
||||||
currentDisplayedAfflictions = GetAllAfflictions(mergeSameAfflictions: true)
|
currentDisplayedAfflictions = GetAllAfflictions(mergeSameAfflictions: true, predicate: a => a.ShouldShowIcon(Character) && a.Prefab.Icon != null);
|
||||||
.FindAll(a => a.ShouldShowIcon(Character) && a.Prefab.Icon != null);
|
|
||||||
currentDisplayedAfflictions.Sort((a1, a2) =>
|
currentDisplayedAfflictions.Sort((a1, a2) =>
|
||||||
{
|
{
|
||||||
int dmgPerSecond = Math.Sign(a1.DamagePerSecond - a2.DamagePerSecond);
|
int dmgPerSecond = Math.Sign(a1.DamagePerSecond - a2.DamagePerSecond);
|
||||||
@@ -1275,7 +1274,7 @@ namespace Barotrauma
|
|||||||
//displaying an affliction we no longer have -> dirty
|
//displaying an affliction we no longer have -> dirty
|
||||||
foreach ((Affliction affliction, float strength) in displayedAfflictions)
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1766,7 +1766,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
foreach (UpgradePrefab prefab in categoryData.Prefabs)
|
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);
|
UpgradeStore.UpdateUpgradeEntry(frame, prefab, categoryData.Category, campaign);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -796,7 +796,7 @@ namespace Barotrauma
|
|||||||
CharacterInfo? ownCharacterInfo = Character.Controlled?.Info ?? GameMain.Client?.CharacterInfo;
|
CharacterInfo? ownCharacterInfo = Character.Controlled?.Info ?? GameMain.Client?.CharacterInfo;
|
||||||
if (ownCharacterInfo is null) { return false; }
|
if (ownCharacterInfo is null) { return false; }
|
||||||
|
|
||||||
return info == ownCharacterInfo;
|
return info.GetIdentifierUsingOriginalName() == ownCharacterInfo.GetIdentifierUsingOriginalName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool CanManageTalents(CharacterInfo targetInfo)
|
public static bool CanManageTalents(CharacterInfo targetInfo)
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Barotrauma.Extensions;
|
using Barotrauma.Extensions;
|
||||||
using Barotrauma.Items.Components;
|
using Barotrauma.Items.Components;
|
||||||
@@ -90,6 +89,16 @@ namespace Barotrauma
|
|||||||
Repairs
|
Repairs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enum UpgradeStoreUserData
|
||||||
|
{
|
||||||
|
BuyButton,
|
||||||
|
BuyButtonLayout,
|
||||||
|
ProgressBarLayout,
|
||||||
|
IncreaseLabel,
|
||||||
|
PriceLabel,
|
||||||
|
MaterialCostList
|
||||||
|
}
|
||||||
|
|
||||||
public UpgradeStore(CampaignUI campaignUI, GUIComponent parent)
|
public UpgradeStore(CampaignUI campaignUI, GUIComponent parent)
|
||||||
{
|
{
|
||||||
WaitForServerUpdate = false;
|
WaitForServerUpdate = false;
|
||||||
@@ -600,7 +609,7 @@ namespace Barotrauma
|
|||||||
GUILayoutGroup textLayout = new GUILayoutGroup(rectT(0.8f - repairIcon.RectTransform.RelativeSize.X, 1, contentLayout)) { Stretch = true };
|
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), title, font: GUIStyle.SubHeadingFont) { CanBeFocused = false, AutoScaleHorizontal = true };
|
||||||
new GUITextBlock(rectT(1, 0, textLayout), TextManager.FormatCurrency(price));
|
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 };
|
new GUIButton(rectT(0.7f, 0.5f, buyButtonLayout), string.Empty, style: "RepairBuyButton") { Enabled = PlayerBalance >= price && !isDisabled, OnClicked = onPressed };
|
||||||
contentLayout.Recalculate();
|
contentLayout.Recalculate();
|
||||||
buyButtonLayout.Recalculate();
|
buyButtonLayout.Recalculate();
|
||||||
@@ -950,7 +959,7 @@ namespace Barotrauma
|
|||||||
frames.Add(CreateUpgradeEntry(rectT(1f, 0.25f, parent.Content), currentOrPending.UpgradePreviewSprite,
|
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),
|
item.PendingItemSwap != null ? TextManager.GetWithVariable("upgrades.pendingitem", "[itemname]", name) : TextManager.GetWithVariable("upgrades.installeditem", "[itemname]", nameWithQuantity),
|
||||||
currentOrPending.Description,
|
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)
|
if (canUninstall && frames.Last().FindChild(c => c is GUIButton, recursive: true) is GUIButton refundButton)
|
||||||
{
|
{
|
||||||
@@ -987,11 +996,11 @@ namespace Barotrauma
|
|||||||
|
|
||||||
int price = isPurchased || replacement == item.Prefab ? 0 : replacement.SwappableItem.GetPrice(Campaign.Map?.CurrentLocation) * linkedItems.Count();
|
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,
|
frames.Add(CreateUpgradeEntry(rectT(1f, 0.25f, parent.Content), replacement.UpgradePreviewSprite, replacement.Name, replacement.Description,
|
||||||
price, replacement,
|
price, replacement,
|
||||||
addBuyButton: true,
|
addBuyButton: true,
|
||||||
addProgressBar: false,
|
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 (!(frames.Last().FindChild(c => c is GUIButton, recursive: true) is GUIButton buyButton)) { continue; }
|
||||||
if (PlayerBalance >= price)
|
if (PlayerBalance >= price)
|
||||||
@@ -1081,13 +1090,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);
|
int price = prefab.Price.GetBuyPrice(campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation);
|
||||||
return CreateUpgradeEntry(rectTransform, prefab.Sprite, prefab.Name, prefab.Description, price, new CategoryData(category, prefab), addBuyButton, upgradePrefab: prefab, currentLevel: campaign.UpgradeManager.GetUpgradeLevel(prefab, category));
|
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;
|
float progressBarHeight = 0.25f;
|
||||||
|
|
||||||
@@ -1105,21 +1124,26 @@ namespace Barotrauma
|
|||||||
* |------------------------------------------------------------------|
|
* |------------------------------------------------------------------|
|
||||||
*/
|
*/
|
||||||
GUIFrame prefabFrame = new GUIFrame(parent, style: "ListBoxElement") { SelectedColor = Color.Transparent, UserData = userData };
|
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 mainLayout = new GUILayoutGroup(rectT(0.98f, 0.95f, prefabFrame, Anchor.Center), isHorizontal: false);
|
||||||
GUILayoutGroup imageLayout = new GUILayoutGroup(rectT(new Point(prefabLayout.Rect.Height, prefabLayout.Rect.Height), prefabLayout), childAnchor: Anchor.Center);
|
GUILayoutGroup prefabLayout = new GUILayoutGroup(rectT(1f, addBuyButton ? 0.65f : 1f, mainLayout, Anchor.Center), isHorizontal: true) { Stretch = true };
|
||||||
var icon = new GUIImage(rectT(0.9f, 0.9f, imageLayout, scaleBasis: ScaleBasis.BothHeight), sprite, scaleToFit: true) { CanBeFocused = false };
|
GUILayoutGroup imageLayout = new GUILayoutGroup(rectT(new Point(prefabLayout.Rect.Height, prefabLayout.Rect.Height), prefabLayout), childAnchor: Anchor.Center);
|
||||||
GUILayoutGroup textLayout = new GUILayoutGroup(rectT(0.8f - imageLayout.RectTransform.RelativeSize.X, 1, prefabLayout));
|
var icon = new GUIImage(rectT(0.9f, 0.9f, imageLayout, scaleBasis: ScaleBasis.BothHeight), sprite, scaleToFit: true) { CanBeFocused = false };
|
||||||
var name = new GUITextBlock(rectT(1, 0.25f, textLayout), RichString.Rich(title), font: GUIStyle.SubHeadingFont) { AutoScaleHorizontal = true, AutoScaleVertical = true, Padding = Vector4.Zero };
|
GUILayoutGroup textLayout = new GUILayoutGroup(rectT(1f - imageLayout.RectTransform.RelativeSize.X, 1, prefabLayout));
|
||||||
GUILayoutGroup descriptionLayout = new GUILayoutGroup(rectT(1, 0.75f - progressBarHeight, textLayout));
|
var name = new GUITextBlock(rectT(1, 0.25f, textLayout), RichString.Rich(title), font: GUIStyle.SubHeadingFont) { AutoScaleHorizontal = true, AutoScaleVertical = true, Padding = Vector4.Zero };
|
||||||
var description = new GUITextBlock(rectT(1, 1, descriptionLayout), body, font: GUIStyle.SmallFont, wrap: true, textAlignment: Alignment.TopLeft) { Padding = Vector4.Zero };
|
GUILayoutGroup descriptionLayout = new GUILayoutGroup(rectT(1, 0.75f - progressBarHeight, textLayout));
|
||||||
GUILayoutGroup? progressLayout = null;
|
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;
|
GUILayoutGroup? buyButtonLayout = null;
|
||||||
|
|
||||||
|
Option<BuyButtonFrame> buyButtonOption = Option<BuyButtonFrame>.None();
|
||||||
|
Option<ProgressBarFrame> progressBarOption = Option<ProgressBarFrame>.None();
|
||||||
|
|
||||||
if (addProgressBar)
|
if (addProgressBar)
|
||||||
{
|
{
|
||||||
progressLayout = new GUILayoutGroup(rectT(1, 0.25f, textLayout), isHorizontal: true, childAnchor: Anchor.CenterLeft) { UserData = "progressbar" };
|
progressLayout = new GUILayoutGroup(rectT(1, 0.25f, textLayout), isHorizontal: true, childAnchor: Anchor.CenterLeft) { UserData = UpgradeStoreUserData.ProgressBarLayout };
|
||||||
new GUIProgressBar(rectT(0.8f, 0.75f, progressLayout), 0.0f, GUIStyle.Orange);
|
GUITextBlock progressText = new GUITextBlock(rectT(0.15f, 1, progressLayout), string.Empty, font: GUIStyle.SmallFont, textAlignment: Alignment.Center) { Padding = Vector4.Zero };
|
||||||
new GUITextBlock(rectT(0.2f, 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)
|
if (addBuyButton)
|
||||||
@@ -1127,12 +1151,33 @@ namespace Barotrauma
|
|||||||
var formattedPrice = TextManager.FormatCurrency(Math.Abs(price));
|
var formattedPrice = TextManager.FormatCurrency(Math.Abs(price));
|
||||||
//negative price = refund
|
//negative price = refund
|
||||||
if (price < 0) { formattedPrice = "+" + formattedPrice; }
|
if (price < 0) { formattedPrice = "+" + formattedPrice; }
|
||||||
buyButtonLayout = new GUILayoutGroup(rectT(0.2f, 1, prefabLayout), childAnchor: Anchor.TopCenter) { UserData = "buybutton" };
|
buyButtonLayout = new GUILayoutGroup(rectT(1f, 0.35f, mainLayout), isHorizontal: true) { UserData = UpgradeStoreUserData.BuyButtonLayout };;
|
||||||
var priceText = new GUITextBlock(rectT(1, 0.2f, buyButtonLayout), formattedPrice, textAlignment: Alignment.Center)
|
|
||||||
|
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
|
//prices on swappable items are always visible, upgrade prices are enabled in UpdateUpgradeEntry for purchasable upgrades
|
||||||
Visible = userData is ItemPrefab
|
Visible = userData is ItemPrefab
|
||||||
};
|
};
|
||||||
|
|
||||||
if (price < 0)
|
if (price < 0)
|
||||||
{
|
{
|
||||||
priceText.TextColor = GUIStyle.Green;
|
priceText.TextColor = GUIStyle.Green;
|
||||||
@@ -1141,15 +1186,13 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
priceText.Text = string.Empty;
|
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
|
Enabled = false
|
||||||
};
|
};
|
||||||
if (upgradePrefab != null)
|
|
||||||
{
|
buyButtonOption = Option.Some(new BuyButtonFrame(buyButtonLayout, materialCostList, buyButton, priceText));
|
||||||
var increaseText = new GUITextBlock(rectT(1, 0.2f, buyButtonLayout), "", textAlignment: Alignment.Center);
|
|
||||||
UpdateUpgradePercentageText(increaseText, upgradePrefab, currentLevel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
description.CalculateHeightFromText();
|
description.CalculateHeightFromText();
|
||||||
@@ -1175,7 +1218,7 @@ namespace Barotrauma
|
|||||||
progressLayout?.Recalculate();
|
progressLayout?.Recalculate();
|
||||||
buyButtonLayout?.Recalculate();
|
buyButtonLayout?.Recalculate();
|
||||||
|
|
||||||
return prefabFrame;
|
return new UpgradeFrame(prefabFrame, icon, name, description, buyButtonOption, progressBarOption);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void UpdateUpgradePercentageText(GUITextBlock text, UpgradePrefab upgradePrefab, int currentLevel)
|
private static void UpdateUpgradePercentageText(GUITextBlock text, UpgradePrefab upgradePrefab, int currentLevel)
|
||||||
@@ -1197,31 +1240,21 @@ namespace Barotrauma
|
|||||||
Submarine? sub = GameMain.GameSession?.Submarine ?? Submarine.MainSub;
|
Submarine? sub = GameMain.GameSession?.Submarine ?? Submarine.MainSub;
|
||||||
if (Campaign is null || sub is null) { return; }
|
if (Campaign is null || sub is null) { return; }
|
||||||
|
|
||||||
GUIFrame prefabFrame = CreateUpgradeFrame(prefab, category, Campaign, rectT(1f, 0.25f, parent));
|
UpgradeFrame prefabFrame = CreateUpgradeFrame(prefab, category, Campaign, rectT(1f, 0.4f, parent));
|
||||||
var prefabLayout = prefabFrame.GetChild<GUILayoutGroup>();
|
|
||||||
GUILayoutGroup[] childLayouts = prefabLayout.GetAllChildren<GUILayoutGroup>().ToArray();
|
if (!prefabFrame.BuyButton.TryUnwrap(out BuyButtonFrame buyButtonFrame)) { return; }
|
||||||
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>();
|
|
||||||
|
|
||||||
if (!HasPermission || !prefab.IsApplicable(submarine.Info) || (itemsOnSubmarine != null && !itemsOnSubmarine.Any(it => category.CanBeApplied(it, prefab))))
|
if (!HasPermission || !prefab.IsApplicable(submarine.Info) || (itemsOnSubmarine != null && !itemsOnSubmarine.Any(it => category.CanBeApplied(it, prefab))))
|
||||||
{
|
{
|
||||||
prefabFrame.Enabled = false;
|
prefabFrame.Frame.Enabled = false;
|
||||||
description.Enabled = false;
|
prefabFrame.Description.Enabled = false;
|
||||||
name.Enabled = false;
|
prefabFrame.Name.Enabled = false;
|
||||||
icon.Color = Color.Gray;
|
prefabFrame.Icon.Color = Color.Gray;
|
||||||
buyButton.Enabled = false;
|
buyButtonFrame.BuyButton.Enabled = false;
|
||||||
buyButtonLayout.UserData = null; // prevent UpdateUpgradeEntry() from enabling the button
|
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",
|
LocalizedString promptBody = TextManager.GetWithVariables("Upgrades.PurchasePromptBody",
|
||||||
("[upgradename]", prefab.Name),
|
("[upgradename]", prefab.Name),
|
||||||
@@ -1240,7 +1273,7 @@ namespace Barotrauma
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
UpdateUpgradeEntry(prefabFrame, prefab, category, Campaign);
|
UpdateUpgradeEntry(prefabFrame.Frame, prefab, category, Campaign);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateItemTooltip(MapEntity entity)
|
private void CreateItemTooltip(MapEntity entity)
|
||||||
@@ -1623,7 +1656,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
int maxLevel = prefab.GetMaxLevelForCurrentSub();
|
int maxLevel = prefab.GetMaxLevelForCurrentSub();
|
||||||
LocalizedString progressText = TextManager.GetWithVariables("upgrades.progressformat", ("[level]", currentLevel.ToString()), ("[maxlevel]", maxLevel.ToString()));
|
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>();
|
GUIProgressBar bar = progressParent.GetChild<GUIProgressBar>();
|
||||||
if (bar != null)
|
if (bar != null)
|
||||||
@@ -1636,36 +1669,111 @@ namespace Barotrauma
|
|||||||
if (block != null) { block.Text = progressText; }
|
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);
|
||||||
|
|
||||||
|
if (!WaitForServerUpdate)
|
||||||
{
|
{
|
||||||
List<GUITextBlock> textBlocks = buttonParent.GetAllChildren<GUITextBlock>().ToList();
|
priceLabel.Text = TextManager.FormatCurrency(price);
|
||||||
|
if (currentLevel >= maxLevel)
|
||||||
GUITextBlock priceLabel = textBlocks[0];
|
|
||||||
priceLabel.Visible = true;
|
|
||||||
int price = prefab.Price.GetBuyPrice(campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation);
|
|
||||||
|
|
||||||
if (priceLabel != null && !WaitForServerUpdate)
|
|
||||||
{
|
{
|
||||||
priceLabel.Text = TextManager.FormatCurrency(price);
|
priceLabel.Text = TextManager.Get("Upgrade.MaxedUpgrade");
|
||||||
if (currentLevel >= maxLevel)
|
|
||||||
{
|
|
||||||
priceLabel.Text = TextManager.Get("Upgrade.MaxedUpgrade");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GUIButton button = buttonParent.GetChild<GUIButton>();
|
if (buttonParent.FindChild(UpgradeStoreUserData.IncreaseLabel, recursive: true) is GUITextBlock increaseLabel && !WaitForServerUpdate)
|
||||||
if (button != null)
|
{
|
||||||
|
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;
|
itemList.Visible = false;
|
||||||
if (WaitForServerUpdate || campaign.GetBalance() < price)
|
|
||||||
{
|
|
||||||
button.Enabled = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
GUITextBlock increaseLabel = textBlocks[1];
|
else
|
||||||
if (increaseLabel != null && !WaitForServerUpdate)
|
|
||||||
{
|
{
|
||||||
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
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ using System.Linq;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Barotrauma.Extensions;
|
using Barotrauma.Extensions;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
|
||||||
namespace Barotrauma
|
namespace Barotrauma
|
||||||
{
|
{
|
||||||
@@ -475,6 +476,19 @@ namespace Barotrauma
|
|||||||
yield return CoroutineStatus.Running;
|
yield return CoroutineStatus.Running;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var corePackage = ContentPackageManager.EnabledPackages.Core;
|
||||||
|
if (corePackage.EnableError.TryUnwrap(out var error))
|
||||||
|
{
|
||||||
|
if (error.ErrorsOrException.TryGet(out ImmutableArray<string> errorMessages))
|
||||||
|
{
|
||||||
|
throw new Exception($"Error while loading the core content package \"{corePackage.Name}\": {errorMessages.First()}");
|
||||||
|
}
|
||||||
|
else if (error.ErrorsOrException.TryGet(out Exception exception))
|
||||||
|
{
|
||||||
|
throw new Exception($"Error while loading the core content package \"{corePackage.Name}\": {exception.Message}", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TextManager.VerifyLanguageAvailable();
|
TextManager.VerifyLanguageAvailable();
|
||||||
|
|
||||||
DebugConsole.Init();
|
DebugConsole.Init();
|
||||||
@@ -735,8 +749,8 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
Client.Quit();
|
Client.Quit();
|
||||||
Client = null;
|
Client = null;
|
||||||
MainMenuScreen.Select();
|
|
||||||
}
|
}
|
||||||
|
MainMenuScreen.Select();
|
||||||
|
|
||||||
if (connectCommand.EndpointOrLobby.TryGet(out ulong lobbyId))
|
if (connectCommand.EndpointOrLobby.TryGet(out ulong lobbyId))
|
||||||
{
|
{
|
||||||
@@ -1099,37 +1113,6 @@ namespace Barotrauma
|
|||||||
GameSession = null;
|
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()
|
public void ShowBugReporter()
|
||||||
{
|
{
|
||||||
if (GUIMessageBox.VisibleBox != null && GUIMessageBox.VisibleBox.UserData as string == "bugreporter")
|
if (GUIMessageBox.VisibleBox != null && GUIMessageBox.VisibleBox.UserData as string == "bugreporter")
|
||||||
|
|||||||
@@ -106,12 +106,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public static bool AllowedToManageWallets()
|
public static bool AllowedToManageWallets()
|
||||||
{
|
{
|
||||||
if (GameMain.Client == null) { return true; }
|
return AllowedToManageCampaign(ClientPermissions.ManageMoney);
|
||||||
|
|
||||||
return
|
|
||||||
GameMain.Client.HasPermission(ClientPermissions.ManageMoney) ||
|
|
||||||
GameMain.Client.HasPermission(ClientPermissions.ManageCampaign) ||
|
|
||||||
GameMain.Client.IsServerOwner;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Draw(SpriteBatch spriteBatch)
|
public override void Draw(SpriteBatch spriteBatch)
|
||||||
|
|||||||
@@ -965,7 +965,7 @@ namespace Barotrauma
|
|||||||
break;
|
break;
|
||||||
case QuickUseAction.PutToEquippedItem:
|
case QuickUseAction.PutToEquippedItem:
|
||||||
//order by the condition of the contained item to prefer putting into the item with the emptiest ammo/battery/tank
|
//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; }
|
if (heldItem.OwnInventory == null) { continue; }
|
||||||
//don't allow swapping if we're moving items into an item with 1 slot holding a stack of items
|
//don't allow swapping if we're moving items into an item with 1 slot holding a stack of items
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
public void DrawElectricity(SpriteBatch spriteBatch)
|
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++)
|
for (int i = 0; i < nodes.Count; i++)
|
||||||
{
|
{
|
||||||
if (nodes[i].Length <= 1.0f) { continue; }
|
if (nodes[i].Length <= 1.0f) { continue; }
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using Microsoft.Xna.Framework.Graphics;
|
using Microsoft.Xna.Framework.Graphics;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using static Barotrauma.Inventory;
|
||||||
|
|
||||||
namespace Barotrauma.Items.Components
|
namespace Barotrauma.Items.Components
|
||||||
{
|
{
|
||||||
@@ -250,6 +252,83 @@ namespace Barotrauma.Items.Components
|
|||||||
return true;
|
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)
|
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
|
||||||
{
|
{
|
||||||
if (hideItems || (item.body != null && !item.body.Enabled)) { return; }
|
if (hideItems || (item.body != null && !item.body.Enabled)) { return; }
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ namespace Barotrauma.Items.Components
|
|||||||
private CoroutineHandle resetPredictionCoroutine;
|
private CoroutineHandle resetPredictionCoroutine;
|
||||||
private float resetPredictionTimer;
|
private float resetPredictionTimer;
|
||||||
|
|
||||||
private float currentBrightness;
|
|
||||||
|
|
||||||
public Vector2 DrawSize
|
public Vector2 DrawSize
|
||||||
{
|
{
|
||||||
get { return new Vector2(Light.Range * 2, Light.Range * 2); }
|
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;
|
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; }
|
if (Light == null) { return; }
|
||||||
Light.Enabled = enabled;
|
Light.Enabled = enabled;
|
||||||
currentBrightness = brightness;
|
if (brightness.HasValue)
|
||||||
|
{
|
||||||
|
lightBrightness = brightness.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lightBrightness = enabled ? 1.0f : 0.0f;
|
||||||
|
}
|
||||||
if (enabled)
|
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;
|
Color color = !hasPower ? NoPowerColor : turret.ActiveUser is null ? Color.DimGray : GUIStyle.Green;
|
||||||
weaponSprite.Draw(batch, center, color, origin, rotation, scale, SpriteEffects.None);
|
weaponSprite.Draw(batch, center, color, origin, rotation, scale, SpriteEffects.None);
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
{
|
||||||
|
CanBeFocused = false
|
||||||
|
};
|
||||||
|
|
||||||
weaponChilds.Add(component, frame);
|
weaponChilds.Add(component, frame);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,14 +25,14 @@ namespace Barotrauma.Items.Components
|
|||||||
public static Color editorHighlightColor = Color.Yellow;
|
public static Color editorHighlightColor = Color.Yellow;
|
||||||
public static Color editorSelectedColor = Color.Red;
|
public static Color editorSelectedColor = Color.Red;
|
||||||
|
|
||||||
partial class WireSection
|
public partial class WireSection
|
||||||
{
|
{
|
||||||
public VertexPositionColorTexture[] vertices;
|
public VertexPositionColorTexture[] vertices;
|
||||||
public VertexPositionColorTexture[] shiftedVertices;
|
public VertexPositionColorTexture[] shiftedVertices;
|
||||||
|
|
||||||
private float cachedWidth = 0f;
|
private float cachedWidth = 0f;
|
||||||
|
|
||||||
private void RecalculateVertices(Wire wire, float width)
|
private void RecalculateVertices(Sprite wireSprite, float width)
|
||||||
{
|
{
|
||||||
if (MathUtils.NearlyEqual(cachedWidth, width)) { return; }
|
if (MathUtils.NearlyEqual(cachedWidth, width)) { return; }
|
||||||
cachedWidth = width;
|
cachedWidth = width;
|
||||||
@@ -45,13 +45,13 @@ namespace Barotrauma.Items.Components
|
|||||||
expandDir.X = -expandDir.Y;
|
expandDir.X = -expandDir.Y;
|
||||||
expandDir.Y = -temp;
|
expandDir.Y = -temp;
|
||||||
|
|
||||||
Rectangle srcRect = wire.wireSprite.SourceRect;
|
Rectangle srcRect = wireSprite.SourceRect;
|
||||||
|
|
||||||
expandDir *= width * srcRect.Height * 0.5f;
|
expandDir *= width * srcRect.Height * 0.5f;
|
||||||
|
|
||||||
Vector2 rectLocation = srcRect.Location.ToVector2();
|
Vector2 rectLocation = srcRect.Location.ToVector2();
|
||||||
Vector2 rectSize = srcRect.Size.ToVector2();
|
Vector2 rectSize = srcRect.Size.ToVector2();
|
||||||
Vector2 textureSize = new Vector2(wire.wireSprite.Texture.Width, wire.wireSprite.Texture.Height);
|
Vector2 textureSize = new Vector2(wireSprite.Texture.Width, wireSprite.Texture.Height);
|
||||||
|
|
||||||
Vector2 topLeftUv = rectLocation / textureSize;
|
Vector2 topLeftUv = rectLocation / textureSize;
|
||||||
Vector2 bottomRightUv = (rectLocation + rectSize) / textureSize;
|
Vector2 bottomRightUv = (rectLocation + rectSize) / textureSize;
|
||||||
@@ -67,10 +67,10 @@ namespace Barotrauma.Items.Components
|
|||||||
shiftedVertices = (VertexPositionColorTexture[])vertices.Clone();
|
shiftedVertices = (VertexPositionColorTexture[])vertices.Clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Draw(SpriteBatch spriteBatch, Wire wire, Color color, Vector2 offset, float depth, float width = 0.3f)
|
public void Draw(ISpriteBatch spriteBatch, Sprite wireSprite, Color color, Vector2 offset, float depth, float width = 0.3f)
|
||||||
{
|
{
|
||||||
if (width <= 0f) { return; }
|
if (width <= 0f) { return; }
|
||||||
RecalculateVertices(wire, width);
|
RecalculateVertices(wireSprite, width);
|
||||||
|
|
||||||
for (int i = 0; i < vertices.Length; i++)
|
for (int i = 0; i < vertices.Length; i++)
|
||||||
{
|
{
|
||||||
@@ -79,21 +79,22 @@ namespace Barotrauma.Items.Components
|
|||||||
shiftedVertices[i].Position.X += offset.X;
|
shiftedVertices[i].Position.X += offset.X;
|
||||||
shiftedVertices[i].Position.Y -= offset.Y;
|
shiftedVertices[i].Position.Y -= offset.Y;
|
||||||
}
|
}
|
||||||
spriteBatch.Draw(wire.wireSprite.Texture,
|
spriteBatch.Draw(
|
||||||
|
wireSprite.Texture,
|
||||||
shiftedVertices,
|
shiftedVertices,
|
||||||
depth);
|
depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Draw(SpriteBatch spriteBatch, Wire wire, Vector2 start, Vector2 end, Color color, float depth, float width = 0.3f)
|
public static void Draw(ISpriteBatch spriteBatch, Sprite wireSprite, Vector2 start, Vector2 end, Color color, float depth, float width = 0.3f)
|
||||||
{
|
{
|
||||||
start.Y = -start.Y;
|
start.Y = -start.Y;
|
||||||
end.Y = -end.Y;
|
end.Y = -end.Y;
|
||||||
|
|
||||||
spriteBatch.Draw(wire.wireSprite.Texture,
|
spriteBatch.Draw(wireSprite.Texture,
|
||||||
start, wire.wireSprite.SourceRect, color,
|
start, wireSprite.SourceRect, color,
|
||||||
MathUtils.VectorToAngle(end - start),
|
MathUtils.VectorToAngle(end - start),
|
||||||
new Vector2(0.0f, wire.wireSprite.size.Y / 2.0f),
|
new Vector2(0.0f, wireSprite.size.Y / 2.0f),
|
||||||
new Vector2((Vector2.Distance(start, end)) / wire.wireSprite.size.X, width),
|
new Vector2((Vector2.Distance(start, end)) / wireSprite.size.X, width),
|
||||||
SpriteEffects.None,
|
SpriteEffects.None,
|
||||||
depth);
|
depth);
|
||||||
}
|
}
|
||||||
@@ -123,7 +124,7 @@ namespace Barotrauma.Items.Components
|
|||||||
get => draggingWire;
|
get => draggingWire;
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void InitProjSpecific(ContentXElement element)
|
public static Sprite ExtractWireSprite(ContentXElement element)
|
||||||
{
|
{
|
||||||
if (defaultWireSprite == null)
|
if (defaultWireSprite == null)
|
||||||
{
|
{
|
||||||
@@ -133,6 +134,7 @@ namespace Barotrauma.Items.Components
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Sprite overrideSprite = null;
|
||||||
foreach (var subElement in element.Elements())
|
foreach (var subElement in element.Elements())
|
||||||
{
|
{
|
||||||
if (subElement.Name.ToString().Equals("wiresprite", StringComparison.OrdinalIgnoreCase))
|
if (subElement.Name.ToString().Equals("wiresprite", StringComparison.OrdinalIgnoreCase))
|
||||||
@@ -142,9 +144,14 @@ namespace Barotrauma.Items.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wireSprite = overrideSprite ?? defaultWireSprite;
|
return overrideSprite ?? defaultWireSprite;
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void InitProjSpecific(ContentXElement element)
|
||||||
|
{
|
||||||
|
wireSprite = ExtractWireSprite(element);
|
||||||
|
if (wireSprite != defaultWireSprite) { overrideSprite = wireSprite; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
|
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
|
||||||
{
|
{
|
||||||
@@ -181,20 +188,20 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
foreach (WireSection section in sections)
|
foreach (WireSection section in sections)
|
||||||
{
|
{
|
||||||
section.Draw(spriteBatch, this, Screen.Selected == GameMain.GameScreen ? higlightColor : editorHighlightColor, drawOffset, depth + 0.00001f, Width * 2.0f);
|
section.Draw(spriteBatch, wireSprite, Screen.Selected == GameMain.GameScreen ? higlightColor : editorHighlightColor, drawOffset, depth + 0.00001f, Width * 2.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (item.IsSelected)
|
else if (item.IsSelected)
|
||||||
{
|
{
|
||||||
foreach (WireSection section in sections)
|
foreach (WireSection section in sections)
|
||||||
{
|
{
|
||||||
section.Draw(spriteBatch, this, editorSelectedColor, drawOffset, depth + 0.00001f, Width * 2.0f);
|
section.Draw(spriteBatch, wireSprite, editorSelectedColor, drawOffset, depth + 0.00001f, Width * 2.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (WireSection section in sections)
|
foreach (WireSection section in sections)
|
||||||
{
|
{
|
||||||
section.Draw(spriteBatch, this, item.Color, drawOffset, depth, Width);
|
section.Draw(spriteBatch, wireSprite, item.Color, drawOffset, depth, Width);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nodes.Count > 0)
|
if (nodes.Count > 0)
|
||||||
@@ -239,13 +246,13 @@ namespace Barotrauma.Items.Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
WireSection.Draw(
|
WireSection.Draw(
|
||||||
spriteBatch, this,
|
spriteBatch, wireSprite,
|
||||||
new Vector2(nodes[nodes.Count - 1].X, nodes[nodes.Count - 1].Y) + drawOffset,
|
nodes[^1] + drawOffset,
|
||||||
new Vector2(newNodePos.X, newNodePos.Y) + drawOffset,
|
new Vector2(newNodePos.X, newNodePos.Y) + drawOffset,
|
||||||
item.Color, 0.0f, Width);
|
item.Color, 0.0f, Width);
|
||||||
|
|
||||||
WireSection.Draw(
|
WireSection.Draw(
|
||||||
spriteBatch, this,
|
spriteBatch, wireSprite,
|
||||||
new Vector2(newNodePos.X, newNodePos.Y) + drawOffset,
|
new Vector2(newNodePos.X, newNodePos.Y) + drawOffset,
|
||||||
item.DrawPosition,
|
item.DrawPosition,
|
||||||
item.Color, itemDepth, Width);
|
item.Color, itemDepth, Width);
|
||||||
@@ -255,8 +262,8 @@ namespace Barotrauma.Items.Components
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
WireSection.Draw(
|
WireSection.Draw(
|
||||||
spriteBatch, this,
|
spriteBatch, wireSprite,
|
||||||
new Vector2(nodes[nodes.Count - 1].X, nodes[nodes.Count - 1].Y) + drawOffset,
|
nodes[^1] + drawOffset,
|
||||||
item.DrawPosition,
|
item.DrawPosition,
|
||||||
item.Color, 0.0f, Width);
|
item.Color, 0.0f, Width);
|
||||||
}
|
}
|
||||||
@@ -294,12 +301,12 @@ namespace Barotrauma.Items.Components
|
|||||||
Vector2 endPos = start + new Vector2((float)Math.Sin(angle), -(float)Math.Cos(angle)) * 50.0f;
|
Vector2 endPos = start + new Vector2((float)Math.Sin(angle), -(float)Math.Cos(angle)) * 50.0f;
|
||||||
|
|
||||||
WireSection.Draw(
|
WireSection.Draw(
|
||||||
spriteBatch, this,
|
spriteBatch, wireSprite,
|
||||||
start, endPos,
|
start, endPos,
|
||||||
GUIStyle.Orange, depth + 0.00001f, 0.2f);
|
GUIStyle.Orange, depth + 0.00001f, 0.2f);
|
||||||
|
|
||||||
WireSection.Draw(
|
WireSection.Draw(
|
||||||
spriteBatch, this,
|
spriteBatch, wireSprite,
|
||||||
start, start + (endPos - start) * 0.7f,
|
start, start + (endPos - start) * 0.7f,
|
||||||
item.Color, depth, 0.3f);
|
item.Color, depth, 0.3f);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1603,88 +1603,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
if (itemContainer != null && itemContainer.ShowContainedStateIndicator && itemContainer.Capacity > 0)
|
if (itemContainer != null && itemContainer.ShowContainedStateIndicator && itemContainer.Capacity > 0)
|
||||||
{
|
{
|
||||||
float containedState = 0.0f;
|
float containedState = itemContainer.GetContainedIndicatorState();
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int dir = slot.SubInventoryDir;
|
int dir = slot.SubInventoryDir;
|
||||||
Rectangle containedIndicatorArea = new Rectangle(rect.X,
|
Rectangle containedIndicatorArea = new Rectangle(rect.X,
|
||||||
dir < 0 ? rect.Bottom + HUDLayoutSettings.Padding / 2 : rect.Y - HUDLayoutSettings.Padding / 2 - ContainedIndicatorHeight, rect.Width, ContainedIndicatorHeight);
|
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(
|
private static void DrawItemStateIndicator(
|
||||||
SpriteBatch spriteBatch, Inventory inventory,
|
SpriteBatch spriteBatch, Inventory inventory,
|
||||||
Sprite indicatorSprite, Sprite emptyIndicatorSprite, Rectangle containedIndicatorArea, float containedState,
|
Sprite indicatorSprite, Sprite emptyIndicatorSprite, Rectangle containedIndicatorArea, float containedState,
|
||||||
|
|||||||
@@ -260,16 +260,16 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public override void UpdatePlacing(Camera cam)
|
public override void UpdatePlacing(Camera cam)
|
||||||
{
|
{
|
||||||
Vector2 position = Submarine.MouseToWorldGrid(cam, Submarine.MainSub);
|
|
||||||
|
|
||||||
if (PlayerInput.SecondaryMouseButtonClicked())
|
if (PlayerInput.SecondaryMouseButtonClicked())
|
||||||
{
|
{
|
||||||
Selected = null;
|
Selected = null;
|
||||||
return;
|
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 (!ResizeHorizontal && !ResizeVertical)
|
||||||
{
|
{
|
||||||
if (PlayerInput.PrimaryMouseButtonClicked() && GUI.MouseOn == null)
|
if (PlayerInput.PrimaryMouseButtonClicked() && GUI.MouseOn == null)
|
||||||
|
|||||||
@@ -293,11 +293,11 @@ namespace Barotrauma
|
|||||||
GUI.DrawRectangle(spriteBatch,
|
GUI.DrawRectangle(spriteBatch,
|
||||||
new Vector2(drawRect.X, -drawRect.Y),
|
new Vector2(drawRect.X, -drawRect.Y),
|
||||||
new Vector2(rect.Width, rect.Height),
|
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,
|
GUI.DrawRectangle(spriteBatch,
|
||||||
new Rectangle(drawRect.X, -drawRect.Y, rect.Width, rect.Height),
|
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)
|
if (GameMain.DebugDraw)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -515,11 +515,11 @@ namespace Barotrauma
|
|||||||
Item targetContainer = null;
|
Item targetContainer = null;
|
||||||
bool isShiftDown = PlayerInput.IsShiftDown();
|
bool isShiftDown = PlayerInput.IsShiftDown();
|
||||||
|
|
||||||
if (!isShiftDown) return null;
|
if (!isShiftDown) { return null; }
|
||||||
|
|
||||||
foreach (MapEntity e in mapEntityList)
|
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))
|
if (e.IsMouseOn(position))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -83,7 +83,10 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
string errorMsg = "Failed to load sound file \"" + filename + "\" (file not found).";
|
string errorMsg = "Failed to load sound file \"" + filename + "\" (file not found).";
|
||||||
DebugConsole.ThrowError(errorMsg, e);
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
catch (System.IO.InvalidDataException e)
|
catch (System.IO.InvalidDataException e)
|
||||||
|
|||||||
@@ -4,26 +4,29 @@ using Microsoft.Xna.Framework;
|
|||||||
using Microsoft.Xna.Framework.Graphics;
|
using Microsoft.Xna.Framework.Graphics;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
|
using Barotrauma.Items.Components;
|
||||||
|
|
||||||
namespace Barotrauma
|
namespace Barotrauma
|
||||||
{
|
{
|
||||||
class SubmarinePreview : IDisposable
|
sealed class SubmarinePreview : IDisposable
|
||||||
{
|
{
|
||||||
private SpriteRecorder spriteRecorder;
|
|
||||||
private readonly SubmarineInfo submarineInfo;
|
private readonly SubmarineInfo submarineInfo;
|
||||||
|
|
||||||
|
private SpriteRecorder spriteRecorder;
|
||||||
private Camera camera;
|
private Camera camera;
|
||||||
private Task loadTask;
|
private Task loadTask;
|
||||||
|
private (Vector2 Min, Vector2 Max) bounds;
|
||||||
|
|
||||||
private volatile bool isDisposed;
|
private volatile bool isDisposed;
|
||||||
|
|
||||||
private GUIFrame previewFrame;
|
private GUIFrame previewFrame;
|
||||||
|
|
||||||
private class HullCollection
|
private sealed class HullCollection
|
||||||
{
|
{
|
||||||
public readonly List<Rectangle> Rects;
|
public readonly List<Rectangle> Rects;
|
||||||
public readonly LocalizedString Name;
|
public readonly LocalizedString Name;
|
||||||
@@ -186,7 +189,21 @@ namespace Barotrauma
|
|||||||
});
|
});
|
||||||
recalculateSpecsContainerHeight();
|
recalculateSpecsContainerHeight();
|
||||||
|
|
||||||
GeneratePreviewMeshes();
|
TaskPool.Add(nameof(GeneratePreviewMeshes), GeneratePreviewMeshes(), _ =>
|
||||||
|
{
|
||||||
|
// Reset the camera's position on the main thread,
|
||||||
|
// because the Camera class is not thread-safe and
|
||||||
|
// it's possible for its state to not get updated
|
||||||
|
// properly if done within a task
|
||||||
|
camera.Position = (bounds.Min + bounds.Max) * (0.5f, -0.5f);
|
||||||
|
Vector2 span2d = bounds.Max - bounds.Min;
|
||||||
|
Vector2 scaledSpan2d = span2d / camera.Resolution.ToVector2();
|
||||||
|
float scaledSpan = Math.Max(scaledSpan2d.X, scaledSpan2d.Y);
|
||||||
|
camera.MinZoom = Math.Min(0.1f, 0.4f / scaledSpan);
|
||||||
|
camera.Zoom = 0.7f / scaledSpan;
|
||||||
|
camera.StopMovement();
|
||||||
|
camera.UpdateTransform(interpolate: false, updateListener: false);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddToGUIUpdateList()
|
public static void AddToGUIUpdateList()
|
||||||
@@ -207,6 +224,7 @@ namespace Barotrauma
|
|||||||
spriteRecorder.Begin(SpriteSortMode.BackToFront);
|
spriteRecorder.Begin(SpriteSortMode.BackToFront);
|
||||||
|
|
||||||
HashSet<int> toIgnore = new HashSet<int>();
|
HashSet<int> toIgnore = new HashSet<int>();
|
||||||
|
HashSet<int> wires = new HashSet<int>();
|
||||||
|
|
||||||
foreach (var subElement in submarineInfo.SubmarineElement.Elements())
|
foreach (var subElement in submarineInfo.SubmarineElement.Elements())
|
||||||
{
|
{
|
||||||
@@ -221,7 +239,7 @@ namespace Barotrauma
|
|||||||
ExtractItemContainerIds(component, toIgnore);
|
ExtractItemContainerIds(component, toIgnore);
|
||||||
break;
|
break;
|
||||||
case "connectionpanel":
|
case "connectionpanel":
|
||||||
ExtractConnectionPanelLinks(component, toIgnore);
|
ExtractConnectionPanelLinks(component, wires);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -231,20 +249,25 @@ namespace Barotrauma
|
|||||||
await Task.Yield();
|
await Task.Yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var wireNodes = new List<XElement>();
|
||||||
|
|
||||||
foreach (var subElement in submarineInfo.SubmarineElement.Elements())
|
foreach (var subElement in submarineInfo.SubmarineElement.Elements())
|
||||||
{
|
{
|
||||||
if (subElement.GetAttributeBool("hiddeningame", false)) { continue; }
|
if (subElement.GetAttributeBool("hiddeningame", false)) { continue; }
|
||||||
switch (subElement.Name.LocalName.ToLowerInvariant())
|
switch (subElement.Name.LocalName.ToLowerInvariant())
|
||||||
{
|
{
|
||||||
|
case "structure":
|
||||||
case "item":
|
case "item":
|
||||||
if (!toIgnore.Contains(subElement.GetAttributeInt("ID", 0)))
|
var id = subElement.GetAttributeInt("ID", 0);
|
||||||
|
if (wires.Contains(id))
|
||||||
|
{
|
||||||
|
wireNodes.Add(subElement);
|
||||||
|
}
|
||||||
|
else if (!toIgnore.Contains(id))
|
||||||
{
|
{
|
||||||
BakeMapEntity(subElement);
|
BakeMapEntity(subElement);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "structure":
|
|
||||||
BakeMapEntity(subElement);
|
|
||||||
break;
|
|
||||||
case "hull":
|
case "hull":
|
||||||
Identifier identifier = subElement.GetAttributeIdentifier("roomname", "");
|
Identifier identifier = subElement.GetAttributeIdentifier("roomname", "");
|
||||||
if (!identifier.IsEmpty)
|
if (!identifier.IsEmpty)
|
||||||
@@ -261,15 +284,14 @@ namespace Barotrauma
|
|||||||
if (isDisposed) { return; }
|
if (isDisposed) { return; }
|
||||||
await Task.Yield();
|
await Task.Yield();
|
||||||
}
|
}
|
||||||
spriteRecorder.End();
|
|
||||||
|
|
||||||
camera.Position = (spriteRecorder.Min + spriteRecorder.Max) * 0.5f;
|
bounds = (spriteRecorder.Min, spriteRecorder.Max);
|
||||||
float scaledSpan = (spriteRecorder.Max - spriteRecorder.Min).X / camera.Resolution.X;
|
wireNodes.ForEach(BakeWireNodes);
|
||||||
camera.Zoom = 0.8f / scaledSpan;
|
|
||||||
camera.StopMovement();
|
spriteRecorder.End();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExtractItemContainerIds(XElement component, HashSet<int> ids)
|
private static void ExtractItemContainerIds(XElement component, HashSet<int> ids)
|
||||||
{
|
{
|
||||||
string containedString = component.GetAttributeString("contained", "");
|
string containedString = component.GetAttributeString("contained", "");
|
||||||
string[] itemIdStrings = containedString.Split(',');
|
string[] itemIdStrings = containedString.Split(',');
|
||||||
@@ -283,7 +305,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExtractConnectionPanelLinks(XElement component, HashSet<int> ids)
|
private static void ExtractConnectionPanelLinks(XElement component, HashSet<int> ids)
|
||||||
{
|
{
|
||||||
var pins = component.Elements("input").Concat(component.Elements("output"));
|
var pins = component.Elements("input").Concat(component.Elements("output"));
|
||||||
foreach (var pin in pins)
|
foreach (var pin in pins)
|
||||||
@@ -297,6 +319,39 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void BakeWireNodes(XElement element)
|
||||||
|
{
|
||||||
|
var prefabIdentifier = element.GetAttributeIdentifier("identifier", "");
|
||||||
|
if (prefabIdentifier.IsEmpty) { return; }
|
||||||
|
if (!ItemPrefab.Prefabs.TryGet(prefabIdentifier, out var prefab)) { return; }
|
||||||
|
|
||||||
|
var prefabWireComponentElement = prefab.ConfigElement.GetChildElement("wire");
|
||||||
|
if (prefabWireComponentElement is null) { return; }
|
||||||
|
|
||||||
|
var wireComponent = element.GetChildElement("wire");
|
||||||
|
if (wireComponent is null) { return; }
|
||||||
|
|
||||||
|
var color = element.GetAttributeColor("spritecolor") ?? Color.White;
|
||||||
|
|
||||||
|
var nodes = Wire.ExtractNodes(wireComponent).ToImmutableArray();
|
||||||
|
var wireSprite = Wire.ExtractWireSprite(prefab.ConfigElement);
|
||||||
|
|
||||||
|
var useSpriteDepth = element.GetAttributeBool("usespritedepth", false);
|
||||||
|
var depth =
|
||||||
|
useSpriteDepth
|
||||||
|
? element.GetAttributeFloat("spritedepth", 1.0f)
|
||||||
|
: wireSprite.Depth;
|
||||||
|
|
||||||
|
var width = prefabWireComponentElement.GetAttributeFloat("width", 0.3f);
|
||||||
|
|
||||||
|
for (int i = 0; i < nodes.Length - 1; i++)
|
||||||
|
{
|
||||||
|
var line = (Start: nodes[i], End: nodes[i + 1]);
|
||||||
|
var wireSegment = new Wire.WireSection(line.Start, line.End);
|
||||||
|
wireSegment.Draw(spriteRecorder, wireSprite, color, Vector2.Zero, depth, width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void BakeMapEntity(XElement element)
|
private void BakeMapEntity(XElement element)
|
||||||
{
|
{
|
||||||
Identifier identifier = element.GetAttributeIdentifier("identifier", Identifier.Empty);
|
Identifier identifier = element.GetAttributeIdentifier("identifier", Identifier.Empty);
|
||||||
@@ -313,27 +368,27 @@ namespace Barotrauma
|
|||||||
|
|
||||||
float rotation = element.GetAttributeFloat("rotation", 0f);
|
float rotation = element.GetAttributeFloat("rotation", 0f);
|
||||||
|
|
||||||
MapEntityPrefab prefab = null;
|
MapEntityPrefab prefab;
|
||||||
if (element.Name.ToString().Equals("item", StringComparison.OrdinalIgnoreCase) &&
|
if (element.NameAsIdentifier() == "item"
|
||||||
ItemPrefab.Prefabs.TryGet(identifier, out ItemPrefab ip))
|
&& ItemPrefab.Prefabs.TryGet(identifier, out ItemPrefab ip))
|
||||||
{
|
{
|
||||||
prefab = ip;
|
prefab = ip;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
prefab = MapEntityPrefab.List.FirstOrDefault(p => p.Identifier == identifier);
|
prefab = MapEntityPrefab.FindByIdentifier(identifier);
|
||||||
}
|
}
|
||||||
if (prefab == null) { return; }
|
if (prefab == null) { return; }
|
||||||
|
|
||||||
var texture = prefab.Sprite.Texture;
|
flippedX &= prefab.CanSpriteFlipX;
|
||||||
var srcRect = prefab.Sprite.SourceRect;
|
flippedY &= prefab.CanSpriteFlipY;
|
||||||
|
|
||||||
SpriteEffects spriteEffects = SpriteEffects.None;
|
SpriteEffects spriteEffects = SpriteEffects.None;
|
||||||
if (flippedX && ((prefab as ItemPrefab)?.CanSpriteFlipX ?? true))
|
if (flippedX)
|
||||||
{
|
{
|
||||||
spriteEffects |= SpriteEffects.FlipHorizontally;
|
spriteEffects |= SpriteEffects.FlipHorizontally;
|
||||||
}
|
}
|
||||||
if (flippedY && ((prefab as ItemPrefab)?.CanSpriteFlipY ?? true))
|
if (flippedY)
|
||||||
{
|
{
|
||||||
spriteEffects |= SpriteEffects.FlipVertically;
|
spriteEffects |= SpriteEffects.FlipVertically;
|
||||||
}
|
}
|
||||||
@@ -419,8 +474,8 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
float offsetState = 0f;
|
float offsetState = 0f;
|
||||||
Vector2 offset = decorativeSprite.GetOffset(ref offsetState, Vector2.Zero) * scale;
|
Vector2 offset = decorativeSprite.GetOffset(ref offsetState, Vector2.Zero) * scale;
|
||||||
if (flippedX && itemPrefab.CanSpriteFlipX) { offset.X = -offset.X; }
|
if (flippedX) { offset.X = -offset.X; }
|
||||||
if (flippedY && itemPrefab.CanSpriteFlipY) { offset.Y = -offset.Y; }
|
if (flippedY) { offset.Y = -offset.Y; }
|
||||||
decorativeSprite.Sprite.DrawTiled(spriteRecorder,
|
decorativeSprite.Sprite.DrawTiled(spriteRecorder,
|
||||||
new Vector2(spritePos.X + offset.X - rect.Width / 2, -(spritePos.Y + offset.Y + rect.Height / 2)),
|
new Vector2(spritePos.X + offset.X - rect.Width / 2, -(spritePos.Y + offset.Y + rect.Height / 2)),
|
||||||
rect.Size.ToVector2(), color: color,
|
rect.Size.ToVector2(), color: color,
|
||||||
@@ -451,8 +506,8 @@ namespace Barotrauma
|
|||||||
float rotationState = 0f; float offsetState = 0f;
|
float rotationState = 0f; float offsetState = 0f;
|
||||||
float rot = decorativeSprite.GetRotation(ref rotationState, 0f);
|
float rot = decorativeSprite.GetRotation(ref rotationState, 0f);
|
||||||
Vector2 offset = decorativeSprite.GetOffset(ref offsetState, Vector2.Zero) * scale;
|
Vector2 offset = decorativeSprite.GetOffset(ref offsetState, Vector2.Zero) * scale;
|
||||||
if (flippedX && itemPrefab.CanSpriteFlipX) { offset.X = -offset.X; }
|
if (flippedX) { offset.X = -offset.X; }
|
||||||
if (flippedY && itemPrefab.CanSpriteFlipY) { offset.Y = -offset.Y; }
|
if (flippedY) { offset.Y = -offset.Y; }
|
||||||
decorativeSprite.Sprite.Draw(spriteRecorder, new Vector2(spritePos.X + offset.X, -(spritePos.Y + offset.Y)), color,
|
decorativeSprite.Sprite.Draw(spriteRecorder, new Vector2(spritePos.X + offset.X, -(spritePos.Y + offset.Y)), color,
|
||||||
MathHelper.ToRadians(rotation) + rot, decorativeSprite.GetScale(0f) * scale, prefab.Sprite.effects,
|
MathHelper.ToRadians(rotation) + rot, decorativeSprite.GetScale(0f) * scale, prefab.Sprite.effects,
|
||||||
depth: Math.Min(depth + (decorativeSprite.Sprite.Depth - prefab.Sprite.Depth), 0.999f));
|
depth: Math.Min(depth + (decorativeSprite.Sprite.Depth - prefab.Sprite.Depth), 0.999f));
|
||||||
@@ -472,6 +527,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
overrideSprite = false;
|
overrideSprite = false;
|
||||||
|
|
||||||
|
float relativeScale = scale / prefab.Scale;
|
||||||
foreach (var subElement in prefab.ConfigElement.Elements())
|
foreach (var subElement in prefab.ConfigElement.Elements())
|
||||||
{
|
{
|
||||||
switch (subElement.Name.LocalName.ToLowerInvariant())
|
switch (subElement.Name.LocalName.ToLowerInvariant())
|
||||||
@@ -498,7 +554,6 @@ namespace Barotrauma
|
|||||||
relativeBarrelPos,
|
relativeBarrelPos,
|
||||||
MathHelper.ToRadians(rotation));
|
MathHelper.ToRadians(rotation));
|
||||||
|
|
||||||
float relativeScale = scale / prefab.Scale;
|
|
||||||
Vector2 drawPos = new Vector2(rect.X + rect.Width * relativeScale / 2 + transformedBarrelPos.X * relativeScale, rect.Y - rect.Height * relativeScale / 2 - transformedBarrelPos.Y * relativeScale);
|
Vector2 drawPos = new Vector2(rect.X + rect.Width * relativeScale / 2 + transformedBarrelPos.X * relativeScale, rect.Y - rect.Height * relativeScale / 2 - transformedBarrelPos.Y * relativeScale);
|
||||||
drawPos.Y = -drawPos.Y;
|
drawPos.Y = -drawPos.Y;
|
||||||
|
|
||||||
@@ -516,20 +571,22 @@ namespace Barotrauma
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
case "door":
|
case "door":
|
||||||
doors.Add(new Door(rect));
|
var scaledRect = rect with { Size = (rect.Size.ToVector2() * relativeScale).ToPoint() };
|
||||||
|
|
||||||
|
doors.Add(new Door(scaledRect));
|
||||||
|
|
||||||
var doorSpriteElem = subElement.Elements().FirstOrDefault(e => e.Name.LocalName.Equals("sprite", StringComparison.OrdinalIgnoreCase));
|
var doorSpriteElem = subElement.Elements().FirstOrDefault(e => e.Name.LocalName.Equals("sprite", StringComparison.OrdinalIgnoreCase));
|
||||||
if (doorSpriteElem != null)
|
if (doorSpriteElem != null)
|
||||||
{
|
{
|
||||||
string texturePath = doorSpriteElem.GetAttributeString("texture", "");
|
string texturePath = doorSpriteElem.GetAttributeStringUnrestricted("texture", "");
|
||||||
Vector2 pos = rect.Location.ToVector2() * new Vector2(1f, -1f);
|
Vector2 pos = scaledRect.Location.ToVector2() * new Vector2(1f, -1f);
|
||||||
if (subElement.GetAttributeBool("horizontal", false))
|
if (subElement.GetAttributeBool("horizontal", false))
|
||||||
{
|
{
|
||||||
pos.Y += (float)rect.Height * 0.5f;
|
pos.Y += (float)scaledRect.Height * 0.5f;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
pos.X += (float)rect.Width * 0.5f;
|
pos.X += (float)scaledRect.Width * 0.5f;
|
||||||
}
|
}
|
||||||
Sprite doorSprite = new Sprite(doorSpriteElem, texturePath.Contains("/") ? "" : Path.GetDirectoryName(prefab.FilePath));
|
Sprite doorSprite = new Sprite(doorSpriteElem, texturePath.Contains("/") ? "" : Path.GetDirectoryName(prefab.FilePath));
|
||||||
spriteRecorder.Draw(doorSprite.Texture, pos,
|
spriteRecorder.Draw(doorSprite.Texture, pos,
|
||||||
@@ -555,7 +612,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ParseUpgrades(XElement prefabConfigElement, ref float scale)
|
private void ParseUpgrades(XElement prefabConfigElement, ref float scale)
|
||||||
{
|
{
|
||||||
foreach (var upgrade in prefabConfigElement.Elements("Upgrade"))
|
foreach (var upgrade in prefabConfigElement.Elements("Upgrade"))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -66,9 +66,20 @@ namespace Barotrauma.Networking
|
|||||||
};
|
};
|
||||||
|
|
||||||
var addressOrAccountId = bannedPlayer.AddressOrAccountId;
|
var addressOrAccountId = bannedPlayer.AddressOrAccountId;
|
||||||
GUITextBlock textBlock = new GUITextBlock(
|
|
||||||
new RectTransform(new Vector2(0.5f, 1.0f), topArea.RectTransform),
|
string nameText = bannedPlayer.Name;
|
||||||
bannedPlayer.Name + " (" + addressOrAccountId + ")") { CanBeFocused = true };
|
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(
|
textBlock.RectTransform.MinSize = new Point(
|
||||||
(int)textBlock.Font.MeasureString(textBlock.Text.SanitizedValue).X, 0);
|
(int)textBlock.Font.MeasureString(textBlock.Text.SanitizedValue).X, 0);
|
||||||
|
|
||||||
@@ -106,7 +117,7 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
private bool RemoveBan(GUIButton button, object obj)
|
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);
|
localRemovedBans.Add(banned.UniqueIdentifier);
|
||||||
RecreateBanFrame();
|
RecreateBanFrame();
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ namespace Barotrauma.Networking
|
|||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
DebugConsole.ThrowError($"Failed to start ChildServerRelay Process. File: {processInfo.FileName}, arguments: {processInfo.Arguments}");
|
DebugConsole.ThrowError($"Failed to start ChildServerRelay Process. File: {processInfo.FileName}, arguments: {processInfo.Arguments}");
|
||||||
|
ForceShutDown();
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -522,7 +522,7 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
if (GameStarted && Screen.Selected == GameMain.GameScreen)
|
if (GameStarted && Screen.Selected == GameMain.GameScreen)
|
||||||
{
|
{
|
||||||
EndVoteTickBox.Visible = ServerSettings.AllowEndVoting && HasSpawned && !(GameMain.GameSession?.GameMode is CampaignMode);
|
EndVoteTickBox.Visible = ServerSettings.AllowEndVoting && HasSpawned;
|
||||||
|
|
||||||
RespawnManager?.Update(deltaTime);
|
RespawnManager?.Update(deltaTime);
|
||||||
|
|
||||||
@@ -1102,11 +1102,7 @@ namespace Barotrauma.Networking
|
|||||||
VoipClient = new VoipClient(this, ClientPeer);
|
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 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))
|
if (Screen.Selected is GameScreen or RoundSummaryScreen or NetLobbyScreen)
|
||||||
{
|
|
||||||
GameMain.ModDownloadScreen.Select();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
EntityEventManager.ClearSelf();
|
EntityEventManager.ClearSelf();
|
||||||
foreach (Character c in Character.CharacterList)
|
foreach (Character c in Character.CharacterList)
|
||||||
@@ -1114,6 +1110,10 @@ namespace Barotrauma.Networking
|
|||||||
c.ResetNetState();
|
c.ResetNetState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GameMain.ModDownloadScreen.Select();
|
||||||
|
}
|
||||||
|
|
||||||
chatBox.InputBox.Enabled = true;
|
chatBox.InputBox.Enabled = true;
|
||||||
if (GameMain.NetLobbyScreen?.ChatInput != null)
|
if (GameMain.NetLobbyScreen?.ChatInput != null)
|
||||||
@@ -1535,8 +1535,9 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
roundInitStatus = RoundInitStatus.WaitingForStartGameFinalize;
|
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);
|
TimeSpan timeOutDuration = new TimeSpan(0, 0, seconds: 30);
|
||||||
|
DateTime timeOut = DateTime.Now + timeOutDuration;
|
||||||
DateTime requestFinalizeTime = DateTime.Now;
|
DateTime requestFinalizeTime = DateTime.Now;
|
||||||
TimeSpan requestFinalizeInterval = new TimeSpan(0, 0, 2);
|
TimeSpan requestFinalizeInterval = new TimeSpan(0, 0, 2);
|
||||||
IWriteMessage msg = new WriteOnlyMessage();
|
IWriteMessage msg = new WriteOnlyMessage();
|
||||||
@@ -1545,11 +1546,15 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
GUIMessageBox interruptPrompt = null;
|
GUIMessageBox interruptPrompt = null;
|
||||||
|
|
||||||
while (true)
|
if (includesFinalize)
|
||||||
{
|
{
|
||||||
try
|
ReadStartGameFinalize(inc);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
{
|
{
|
||||||
if (timeOut.HasValue)
|
try
|
||||||
{
|
{
|
||||||
if (DateTime.Now > requestFinalizeTime)
|
if (DateTime.Now > requestFinalizeTime)
|
||||||
{
|
{
|
||||||
@@ -1583,41 +1588,30 @@ namespace Barotrauma.Networking
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
if (!connected)
|
||||||
{
|
|
||||||
if (includesFinalize)
|
|
||||||
{
|
{
|
||||||
ReadStartGameFinalize(inc);
|
roundInitStatus = RoundInitStatus.Interrupted;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//wait for up to 30 seconds for the server to send the STARTGAMEFINALIZE message
|
if (roundInitStatus != RoundInitStatus.WaitingForStartGameFinalize) { break; }
|
||||||
timeOut = DateTime.Now + timeOutDuration;
|
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
if (!connected)
|
|
||||||
{
|
{
|
||||||
roundInitStatus = RoundInitStatus.Interrupted;
|
DebugConsole.ThrowError("There was an error initializing the round.", e, true);
|
||||||
|
roundInitStatus = RoundInitStatus.Error;
|
||||||
break;
|
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?.Close();
|
||||||
interruptPrompt = null;
|
interruptPrompt = null;
|
||||||
|
|
||||||
if (roundInitStatus != RoundInitStatus.Started)
|
if (roundInitStatus != RoundInitStatus.Started)
|
||||||
{
|
{
|
||||||
if (roundInitStatus != RoundInitStatus.Interrupted)
|
if (roundInitStatus != RoundInitStatus.Interrupted)
|
||||||
@@ -1767,7 +1761,7 @@ namespace Barotrauma.Networking
|
|||||||
{
|
{
|
||||||
string subName = inc.ReadString();
|
string subName = inc.ReadString();
|
||||||
string subHash = inc.ReadString();
|
string subHash = inc.ReadString();
|
||||||
byte subClass = inc.ReadByte();
|
SubmarineClass subClass = (SubmarineClass)inc.ReadByte();
|
||||||
bool isShuttle = inc.ReadBoolean();
|
bool isShuttle = inc.ReadBoolean();
|
||||||
bool requiredContentPackagesInstalled = inc.ReadBoolean();
|
bool requiredContentPackagesInstalled = inc.ReadBoolean();
|
||||||
|
|
||||||
@@ -1776,7 +1770,7 @@ namespace Barotrauma.Networking
|
|||||||
{
|
{
|
||||||
matchingSub = new SubmarineInfo(Path.Combine(SaveUtil.SubmarineDownloadFolder, subName) + ".sub", subHash, tryLoad: false)
|
matchingSub = new SubmarineInfo(Path.Combine(SaveUtil.SubmarineDownloadFolder, subName) + ".sub", subHash, tryLoad: false)
|
||||||
{
|
{
|
||||||
SubmarineClass = (SubmarineClass)subClass
|
SubmarineClass = subClass
|
||||||
};
|
};
|
||||||
if (isShuttle) { matchingSub.AddTag(SubmarineTag.Shuttle); }
|
if (isShuttle) { matchingSub.AddTag(SubmarineTag.Shuttle); }
|
||||||
}
|
}
|
||||||
@@ -2009,10 +2003,10 @@ namespace Barotrauma.Networking
|
|||||||
GameMain.NetLobbyScreen.SetTraitorsEnabled(traitorsEnabled);
|
GameMain.NetLobbyScreen.SetTraitorsEnabled(traitorsEnabled);
|
||||||
GameMain.NetLobbyScreen.SetMissionType(missionType);
|
GameMain.NetLobbyScreen.SetMissionType(missionType);
|
||||||
|
|
||||||
if (!allowModeVoting) GameMain.NetLobbyScreen.SelectMode(modeIndex);
|
GameMain.NetLobbyScreen.SelectMode(modeIndex);
|
||||||
if (isInitialUpdate && GameMain.NetLobbyScreen.SelectedMode == GameModePreset.MultiPlayerCampaign)
|
if (isInitialUpdate && GameMain.NetLobbyScreen.SelectedMode == GameModePreset.MultiPlayerCampaign)
|
||||||
{
|
{
|
||||||
if (GameMain.Client.IsServerOwner) RequestSelectMode(modeIndex);
|
if (GameMain.Client.IsServerOwner) { RequestSelectMode(modeIndex); }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GameMain.NetLobbyScreen.SelectedMode == GameModePreset.MultiPlayerCampaign)
|
if (GameMain.NetLobbyScreen.SelectedMode == GameModePreset.MultiPlayerCampaign)
|
||||||
@@ -2101,13 +2095,12 @@ namespace Barotrauma.Networking
|
|||||||
case ServerNetSegment.EntityPosition:
|
case ServerNetSegment.EntityPosition:
|
||||||
inc.ReadPadBits(); //padding is required here to make sure any padding bits within tempBuffer are read correctly
|
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();
|
uint msgLength = inc.ReadVariableUInt32();
|
||||||
int msgEndPos = (int)(inc.BitPosition + msgLength * 8);
|
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)
|
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.");
|
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);
|
debugEntityList.Add(entity);
|
||||||
if (entity != null)
|
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 &&
|
else if (entity is MapEntity { Prefab.UintIdentifier: var uintIdentifier } me &&
|
||||||
uintIdentifier != incomingUintIdentifier)
|
uintIdentifier != header.PrefabUintIdentifier)
|
||||||
{
|
{
|
||||||
DebugConsole.AddWarning($"Received a potentially invalid ENTITY_POSITION message."
|
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...");
|
+$"client entity is {me.Prefab.Identifier}). Ignoring the message...");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -2133,7 +2126,6 @@ namespace Barotrauma.Networking
|
|||||||
entity.ClientReadPosition(inc, sendingTime);
|
entity.ClientReadPosition(inc, sendingTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//force to the correct position in case the entity doesn't exist
|
//force to the correct position in case the entity doesn't exist
|
||||||
//or the message wasn't read correctly for whatever reason
|
//or the message wasn't read correctly for whatever reason
|
||||||
inc.BitPosition = msgEndPos;
|
inc.BitPosition = msgEndPos;
|
||||||
@@ -2144,7 +2136,7 @@ namespace Barotrauma.Networking
|
|||||||
break;
|
break;
|
||||||
case ServerNetSegment.EntityEvent:
|
case ServerNetSegment.EntityEvent:
|
||||||
case ServerNetSegment.EntityEventInitial:
|
case ServerNetSegment.EntityEventInitial:
|
||||||
if (!EntityEventManager.Read(segment, inc, sendingTime, debugEntityList))
|
if (!EntityEventManager.Read(segment, inc, sendingTime))
|
||||||
{
|
{
|
||||||
return SegmentTableReader<ServerNetSegment>.BreakSegmentReading.Yes;
|
return SegmentTableReader<ServerNetSegment>.BreakSegmentReading.Yes;
|
||||||
}
|
}
|
||||||
@@ -2413,7 +2405,9 @@ namespace Barotrauma.Networking
|
|||||||
var newSub = new SubmarineInfo(transfer.FilePath);
|
var newSub = new SubmarineInfo(transfer.FilePath);
|
||||||
if (newSub.IsFileCorrupted) { return; }
|
if (newSub.IsFileCorrupted) { return; }
|
||||||
|
|
||||||
var existingSubs = SubmarineInfo.SavedSubmarines.Where(s => s.Name == newSub.Name && s.MD5Hash.StringRepresentation == newSub.MD5Hash.StringRepresentation).ToList();
|
var existingSubs = SubmarineInfo.SavedSubmarines
|
||||||
|
.Where(s => s.Name == newSub.Name && s.MD5Hash == newSub.MD5Hash)
|
||||||
|
.ToList();
|
||||||
foreach (SubmarineInfo existingSub in existingSubs)
|
foreach (SubmarineInfo existingSub in existingSubs)
|
||||||
{
|
{
|
||||||
existingSub.Dispose();
|
existingSub.Dispose();
|
||||||
@@ -2472,12 +2466,13 @@ namespace Barotrauma.Networking
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Replace a submarine dud with the downloaded version
|
// Replace a submarine dud with the downloaded version
|
||||||
SubmarineInfo existingServerSub = ServerSubmarines.Find(s => s.Name == newSub.Name && s.MD5Hash?.StringRepresentation == newSub.MD5Hash?.StringRepresentation);
|
SubmarineInfo existingServerSub = ServerSubmarines.Find(s =>
|
||||||
|
s.Name == newSub.Name
|
||||||
|
&& s.MD5Hash == newSub.MD5Hash);
|
||||||
if (existingServerSub != null)
|
if (existingServerSub != null)
|
||||||
{
|
{
|
||||||
int existingIndex = ServerSubmarines.IndexOf(existingServerSub);
|
int existingIndex = ServerSubmarines.IndexOf(existingServerSub);
|
||||||
ServerSubmarines.RemoveAt(existingIndex);
|
ServerSubmarines[existingIndex] = newSub;
|
||||||
ServerSubmarines.Insert(existingIndex, newSub);
|
|
||||||
existingServerSub.Dispose();
|
existingServerSub.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2798,7 +2793,6 @@ namespace Barotrauma.Networking
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void RequestSelectMode(int modeIndex)
|
public void RequestSelectMode(int modeIndex)
|
||||||
{
|
{
|
||||||
if (!HasPermission(ClientPermissions.SelectMode)) return;
|
|
||||||
if (modeIndex < 0 || modeIndex >= GameMain.NetLobbyScreen.ModeList.Content.CountChildren)
|
if (modeIndex < 0 || modeIndex >= GameMain.NetLobbyScreen.ModeList.Content.CountChildren)
|
||||||
{
|
{
|
||||||
DebugConsole.ThrowError("Gamemode index out of bounds (" + modeIndex + ")\n" + Environment.StackTrace.CleanupStackTrace());
|
DebugConsole.ThrowError("Gamemode index out of bounds (" + modeIndex + ")\n" + Environment.StackTrace.CleanupStackTrace());
|
||||||
@@ -2852,13 +2846,14 @@ namespace Barotrauma.Networking
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tell the server to end the round (permission required)
|
/// Tell the server to end the round (permission required)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void RequestRoundEnd(bool save)
|
public void RequestRoundEnd(bool save, bool quitCampaign = false)
|
||||||
{
|
{
|
||||||
IWriteMessage msg = new WriteOnlyMessage();
|
IWriteMessage msg = new WriteOnlyMessage();
|
||||||
msg.WriteByte((byte)ClientPacketHeader.SERVER_COMMAND);
|
msg.WriteByte((byte)ClientPacketHeader.SERVER_COMMAND);
|
||||||
msg.WriteUInt16((UInt16)ClientPermissions.ManageRound);
|
msg.WriteUInt16((UInt16)ClientPermissions.ManageRound);
|
||||||
msg.WriteBoolean(true); //indicates round end
|
msg.WriteBoolean(true); //indicates round end
|
||||||
msg.WriteBoolean(save);
|
msg.WriteBoolean(save);
|
||||||
|
msg.WriteBoolean(quitCampaign);
|
||||||
|
|
||||||
ClientPeer.Send(msg, DeliveryMethod.Reliable);
|
ClientPeer.Send(msg, DeliveryMethod.Reliable);
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-10
@@ -109,16 +109,15 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
private UInt16? firstNewID;
|
private UInt16? firstNewID;
|
||||||
|
|
||||||
|
private readonly List<IServerSerializable> tempEntityList = new List<IServerSerializable>();
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read the events from the message, ignoring ones we've already received. Returns false if reading the events fails.
|
/// Read the events from the message, ignoring ones we've already received. Returns false if reading the events fails.
|
||||||
/// </summary>
|
/// </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)
|
if (type == ServerNetSegment.EntityEventInitial)
|
||||||
{
|
{
|
||||||
unreceivedEntityEventCount = msg.ReadUInt16();
|
UInt16 unreceivedEntityEventCount = msg.ReadUInt16();
|
||||||
firstNewID = msg.ReadUInt16();
|
firstNewID = msg.ReadUInt16();
|
||||||
|
|
||||||
if (GameSettings.CurrentConfig.VerboseLogging)
|
if (GameSettings.CurrentConfig.VerboseLogging)
|
||||||
@@ -143,7 +142,7 @@ namespace Barotrauma.Networking
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
entities.Clear();
|
tempEntityList.Clear();
|
||||||
|
|
||||||
msg.ReadPadBits();
|
msg.ReadPadBits();
|
||||||
UInt16 firstEventID = msg.ReadUInt16();
|
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}).";
|
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:";
|
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);
|
DebugConsole.ThrowError(errorMsg);
|
||||||
return false;
|
return false;
|
||||||
@@ -174,7 +173,7 @@ namespace Barotrauma.Networking
|
|||||||
DebugConsole.NewMessage("received msg " + thisEventID + " (null entity)",
|
DebugConsole.NewMessage("received msg " + thisEventID + " (null entity)",
|
||||||
Microsoft.Xna.Framework.Color.Orange);
|
Microsoft.Xna.Framework.Color.Orange);
|
||||||
}
|
}
|
||||||
entities.Add(null);
|
tempEntityList.Add(null);
|
||||||
if (thisEventID == (UInt16)(lastReceivedID + 1)) { lastReceivedID++; }
|
if (thisEventID == (UInt16)(lastReceivedID + 1)) { lastReceivedID++; }
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -182,7 +181,7 @@ namespace Barotrauma.Networking
|
|||||||
int msgLength = (int)msg.ReadVariableUInt32();
|
int msgLength = (int)msg.ReadVariableUInt32();
|
||||||
|
|
||||||
IServerSerializable entity = Entity.FindEntityByID(entityID) as IServerSerializable;
|
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
|
//skip the event if we've already received it or if the entity isn't found
|
||||||
if (thisEventID != (UInt16)(lastReceivedID + 1) || entity == null)
|
if (thisEventID != (UInt16)(lastReceivedID + 1) || entity == null)
|
||||||
@@ -223,7 +222,7 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
if (msg.BitPosition != msgPosition + msgLength * 8)
|
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;
|
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)}). "
|
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}) "
|
+$"The previous entity was \"{prevEntity}\" (ID {prevId}) "
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ namespace Barotrauma.Networking
|
|||||||
protected readonly bool isOwner;
|
protected readonly bool isOwner;
|
||||||
protected readonly Option<int> ownerKey;
|
protected readonly Option<int> ownerKey;
|
||||||
|
|
||||||
|
public bool IsActive => isActive;
|
||||||
|
|
||||||
protected bool isActive;
|
protected bool isActive;
|
||||||
|
|
||||||
public ClientPeer(Endpoint serverEndpoint, Callbacks callbacks, Option<int> ownerKey)
|
public ClientPeer(Endpoint serverEndpoint, Callbacks callbacks, Option<int> ownerKey)
|
||||||
|
|||||||
@@ -98,16 +98,15 @@ namespace Barotrauma
|
|||||||
foreach (GUIComponent comp in listBox.Content.Children)
|
foreach (GUIComponent comp in listBox.Content.Children)
|
||||||
{
|
{
|
||||||
if (comp.UserData != userData) { continue; }
|
if (comp.UserData != userData) { continue; }
|
||||||
if (!(comp.FindChild("votes") is GUITextBlock voteText))
|
if (comp.FindChild("votes") is not GUITextBlock voteText)
|
||||||
{
|
{
|
||||||
voteText = new GUITextBlock(new RectTransform(new Point(30, comp.Rect.Height), comp.RectTransform, Anchor.CenterRight),
|
voteText = new GUITextBlock(new RectTransform(new Point(GUI.IntScale(30), comp.Rect.Height), comp.RectTransform, Anchor.CenterRight),
|
||||||
"", textAlignment: Alignment.CenterRight)
|
"", textAlignment: Alignment.Center)
|
||||||
{
|
{
|
||||||
Padding = Vector4.Zero,
|
Padding = Vector4.Zero,
|
||||||
UserData = "votes"
|
UserData = "votes"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
voteText.Text = votes == 0 ? "" : votes.ToString();
|
voteText.Text = votes == 0 ? "" : votes.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -303,7 +303,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
bool ChangeValue(GUIButton btn, object userData)
|
bool ChangeValue(GUIButton btn, object userData)
|
||||||
{
|
{
|
||||||
if (!(userData is int change)) { return false; }
|
if (userData is not int change) { return false; }
|
||||||
|
|
||||||
int hiddenOptions = 0;
|
int hiddenOptions = 0;
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -365,7 +365,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private void CreateCustomizeWindow(CampaignSettings prevSettings, Action<CampaignSettings> onClosed = null)
|
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));
|
GUILayoutGroup campaignSettingContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.8f), CampaignCustomizeSettings.Content.RectTransform, Anchor.TopCenter));
|
||||||
|
|
||||||
|
|||||||
+1
-10
@@ -125,7 +125,7 @@ namespace Barotrauma.CharacterEditor
|
|||||||
{
|
{
|
||||||
ResetVariables();
|
ResetVariables();
|
||||||
var subInfo = new SubmarineInfo("Content/AnimEditor.sub");
|
var subInfo = new SubmarineInfo("Content/AnimEditor.sub");
|
||||||
Submarine.MainSub = new Submarine(subInfo);
|
Submarine.MainSub = new Submarine(subInfo, showErrorMessages: false);
|
||||||
if (Submarine.MainSub.PhysicsBody != null)
|
if (Submarine.MainSub.PhysicsBody != null)
|
||||||
{
|
{
|
||||||
Submarine.MainSub.PhysicsBody.Enabled = false;
|
Submarine.MainSub.PhysicsBody.Enabled = false;
|
||||||
@@ -162,11 +162,6 @@ namespace Barotrauma.CharacterEditor
|
|||||||
OpenDoors();
|
OpenDoors();
|
||||||
GameMain.Instance.ResolutionChanged += OnResolutionChanged;
|
GameMain.Instance.ResolutionChanged += OnResolutionChanged;
|
||||||
Instance = this;
|
Instance = this;
|
||||||
|
|
||||||
if (!GameSettings.CurrentConfig.EditorDisclaimerShown)
|
|
||||||
{
|
|
||||||
GameMain.Instance.ShowEditorDisclaimer();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ResetVariables()
|
private void ResetVariables()
|
||||||
@@ -2688,10 +2683,6 @@ namespace Barotrauma.CharacterEditor
|
|||||||
|
|
||||||
// Character selection
|
// Character selection
|
||||||
var characterLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), GetCharacterEditorTranslation("CharacterPanel"), font: GUIStyle.LargeFont);
|
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)
|
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>";
|
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))
|
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) };
|
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 GUIFrame modsButtonContainer;
|
||||||
private readonly GUIButton modsButton, modUpdatesButton;
|
private readonly GUIButton modsButton, modUpdatesButton;
|
||||||
private Task<IReadOnlyList<Steamworks.Ugc.Item>> modUpdateTask;
|
private (DateTime WhenToRefresh, int Count) modUpdateStatus = (DateTime.Now, 0);
|
||||||
private float modUpdateTimer = 0.0f;
|
private static readonly TimeSpan ModUpdateInterval = TimeSpan.FromSeconds(60.0f);
|
||||||
private const float ModUpdateInterval = 60.0f;
|
|
||||||
|
|
||||||
private readonly GameMain game;
|
private readonly GameMain game;
|
||||||
|
|
||||||
@@ -736,8 +735,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public void ResetModUpdateButton()
|
public void ResetModUpdateButton()
|
||||||
{
|
{
|
||||||
modUpdateTask = null;
|
modUpdateStatus = (DateTime.Now, 0);
|
||||||
modUpdateTimer = 0;
|
|
||||||
modUpdatesButton.Visible = false;
|
modUpdatesButton.Visible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -875,7 +873,25 @@ namespace Barotrauma
|
|||||||
GameMain.ResetNetLobbyScreen();
|
GameMain.ResetNetLobbyScreen();
|
||||||
try
|
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) + "\"" +
|
string arguments = "-name \"" + ToolBox.EscapeCharacters(name) + "\"" +
|
||||||
" -public " + isPublicBox.Selected.ToString() +
|
" -public " + isPublicBox.Selected.ToString() +
|
||||||
@@ -899,19 +915,10 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
int ownerKey = Math.Max(CryptoRandom.Instance.Next(), 1);
|
int ownerKey = Math.Max(CryptoRandom.Instance.Next(), 1);
|
||||||
arguments += " -ownerkey " + ownerKey;
|
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
|
var processInfo = new ProcessStartInfo
|
||||||
{
|
{
|
||||||
FileName = filename,
|
FileName = fileName,
|
||||||
Arguments = arguments,
|
Arguments = arguments,
|
||||||
WorkingDirectory = Directory.GetCurrentDirectory(),
|
WorkingDirectory = Directory.GetCurrentDirectory(),
|
||||||
#if !DEBUG
|
#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)
|
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
|
#if DEBUG
|
||||||
hostServerButton.Enabled = true;
|
hostServerButton.Enabled = true;
|
||||||
#else
|
#else
|
||||||
@@ -976,10 +1010,8 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (modUpdateTask is { IsCompletedSuccessfully: true })
|
UpdateOutOfDateWorkshopItemCount();
|
||||||
{
|
modUpdatesButton.Visible = modUpdateStatus.Count > 0;
|
||||||
modUpdatesButton.Visible = modUpdateTask.Result.Count > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (modUpdatesButton.Visible)
|
if (modUpdatesButton.Visible)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -189,7 +189,8 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
|
|
||||||
public IReadOnlyList<SubmarineInfo> GetSubList()
|
public IReadOnlyList<SubmarineInfo> GetSubList()
|
||||||
=> SubList.Content.Children.Select(c => c.UserData as SubmarineInfo).ToArray();
|
=> (IReadOnlyList<SubmarineInfo>)GameMain.Client?.ServerSubmarines
|
||||||
|
?? Array.Empty<SubmarineInfo>();
|
||||||
|
|
||||||
public readonly GUIListBox PlayerList;
|
public readonly GUIListBox PlayerList;
|
||||||
|
|
||||||
@@ -929,6 +930,8 @@ namespace Barotrauma
|
|||||||
|
|
||||||
var modeTitle = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), modeContent.RectTransform), mode.Name, font: GUIStyle.SubHeadingFont);
|
var modeTitle = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), modeContent.RectTransform), mode.Name, font: GUIStyle.SubHeadingFont);
|
||||||
var modeDescription = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), modeContent.RectTransform), mode.Description, font: GUIStyle.SmallFont, wrap: true);
|
var modeDescription = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), modeContent.RectTransform), mode.Description, font: GUIStyle.SmallFont, wrap: true);
|
||||||
|
//leave some padding for the vote count text
|
||||||
|
modeDescription.Padding = new Vector4(modeDescription.Padding.X, modeDescription.Padding.Y, GUI.IntScale(30), modeDescription.Padding.W);
|
||||||
modeTitle.HoverColor = modeDescription.HoverColor = modeTitle.SelectedColor = modeDescription.SelectedColor = Color.Transparent;
|
modeTitle.HoverColor = modeDescription.HoverColor = modeTitle.SelectedColor = modeDescription.SelectedColor = Color.Transparent;
|
||||||
modeTitle.HoverTextColor = modeDescription.HoverTextColor = modeTitle.TextColor;
|
modeTitle.HoverTextColor = modeDescription.HoverTextColor = modeTitle.TextColor;
|
||||||
modeTitle.TextColor = modeDescription.TextColor = modeTitle.TextColor * 0.5f;
|
modeTitle.TextColor = modeDescription.TextColor = modeTitle.TextColor * 0.5f;
|
||||||
@@ -981,7 +984,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
GameMain.Client.RequestSelectMode(ModeList.Content.GetChildIndex(ModeList.Content.GetChildByUserData(GameModePreset.Sandbox)));
|
GameMain.Client.RequestRoundEnd(save: false, quitCampaign: true);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -3291,16 +3294,15 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
//campaign running
|
//campaign running
|
||||||
settingsBlocker.Visible = true;
|
settingsBlocker.Visible = true;
|
||||||
CampaignFrame.Visible = GameMain.Client.HasPermission(ClientPermissions.ManageCampaign);
|
CampaignFrame.Visible = QuitCampaignButton.Enabled = CampaignMode.AllowedToManageCampaign(ClientPermissions.ManageRound);
|
||||||
ContinueCampaignButton.Enabled = !GameMain.Client.GameStarted && (GameMain.Client.HasPermission(ClientPermissions.ManageCampaign) || GameMain.Client.HasPermission(ClientPermissions.ManageRound));
|
ContinueCampaignButton.Enabled = !GameMain.Client.GameStarted && CampaignFrame.Visible;
|
||||||
QuitCampaignButton.Enabled = GameMain.Client.HasPermission(ClientPermissions.ManageCampaign);
|
|
||||||
CampaignSetupFrame.Visible = false;
|
CampaignSetupFrame.Visible = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
CampaignFrame.Visible = false;
|
CampaignFrame.Visible = false;
|
||||||
CampaignSetupFrame.Visible = true;
|
CampaignSetupFrame.Visible = true;
|
||||||
if (!GameMain.Client.HasPermission(ClientPermissions.ManageCampaign))
|
if (!CampaignMode.AllowedToManageCampaign(ClientPermissions.ManageRound))
|
||||||
{
|
{
|
||||||
CampaignSetupFrame.ClearChildren();
|
CampaignSetupFrame.ClearChildren();
|
||||||
new GUITextBlock(new RectTransform(new Vector2(0.8f, 0.5f), CampaignSetupFrame.RectTransform, Anchor.Center),
|
new GUITextBlock(new RectTransform(new Vector2(0.8f, 0.5f), CampaignSetupFrame.RectTransform, Anchor.Center),
|
||||||
@@ -3364,7 +3366,7 @@ namespace Barotrauma
|
|||||||
CampaignFrame.Visible = CampaignSetupFrame.Visible = false;
|
CampaignFrame.Visible = CampaignSetupFrame.Visible = false;
|
||||||
}
|
}
|
||||||
RefreshEnabledElements();
|
RefreshEnabledElements();
|
||||||
if (enabled)
|
if (enabled && SelectedMode != GameModePreset.MultiPlayerCampaign)
|
||||||
{
|
{
|
||||||
ModeList.Select(GameModePreset.MultiPlayerCampaign, GUIListBox.Force.Yes);
|
ModeList.Select(GameModePreset.MultiPlayerCampaign, GUIListBox.Force.Yes);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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));
|
TopPanel.RectTransform.MinSize = new Point(0, (int)(paddedTopPanel.RectTransform.Children.Max(c => c.MinSize.Y) / paddedTopPanel.RectTransform.RelativeSize.Y));
|
||||||
paddedTopPanel.Recalculate();
|
paddedTopPanel.Recalculate();
|
||||||
|
|
||||||
@@ -1425,7 +1418,7 @@ namespace Barotrauma
|
|||||||
else if (MainSub == null)
|
else if (MainSub == null)
|
||||||
{
|
{
|
||||||
var subInfo = new SubmarineInfo();
|
var subInfo = new SubmarineInfo();
|
||||||
MainSub = new Submarine(subInfo);
|
MainSub = new Submarine(subInfo, showErrorMessages: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
MainSub.UpdateTransform(interpolate: false);
|
MainSub.UpdateTransform(interpolate: false);
|
||||||
@@ -1462,11 +1455,6 @@ namespace Barotrauma
|
|||||||
|
|
||||||
ImageManager.OnEditorSelected();
|
ImageManager.OnEditorSelected();
|
||||||
ReconstructLayers();
|
ReconstructLayers();
|
||||||
|
|
||||||
if (!GameSettings.CurrentConfig.EditorDisclaimerShown)
|
|
||||||
{
|
|
||||||
GameMain.Instance.ShowEditorDisclaimer();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnFileDropped(string filePath, string extension)
|
public override void OnFileDropped(string filePath, string extension)
|
||||||
@@ -2726,11 +2714,13 @@ namespace Barotrauma
|
|||||||
|
|
||||||
previewImageButtonHolder.RectTransform.MinSize = new Point(0, previewImageButtonHolder.RectTransform.Children.Max(c => c.MinSize.Y));
|
previewImageButtonHolder.RectTransform.MinSize = new Point(0, previewImageButtonHolder.RectTransform.Children.Max(c => c.MinSize.Y));
|
||||||
|
|
||||||
var contentPackageTabber = new GUILayoutGroup(new RectTransform((1.0f, 0.06f), rightColumn.RectTransform), isHorizontal: true);
|
var contentPackageTabber = new GUILayoutGroup(new RectTransform((1.0f, 0.075f), rightColumn.RectTransform), isHorizontal: true);
|
||||||
|
|
||||||
GUIButton createTabberBtn(string labelTag)
|
GUIButton createTabberBtn(string labelTag)
|
||||||
{
|
{
|
||||||
var btn = new GUIButton(new RectTransform((0.5f, 1.0f), contentPackageTabber.RectTransform, Anchor.BottomCenter, Pivot.BottomCenter), TextManager.Get(labelTag), style: "GUITabButton");
|
var btn = new GUIButton(new RectTransform((0.5f, 1.0f), contentPackageTabber.RectTransform, Anchor.BottomCenter, Pivot.BottomCenter), TextManager.Get(labelTag), style: "GUITabButton");
|
||||||
|
btn.TextBlock.Wrap = true;
|
||||||
|
btn.TextBlock.SetTextPos();
|
||||||
btn.RectTransform.MaxSize = RectTransform.MaxPoint;
|
btn.RectTransform.MaxSize = RectTransform.MaxPoint;
|
||||||
btn.Children.ForEach(c => c.RectTransform.MaxSize = RectTransform.MaxPoint);
|
btn.Children.ForEach(c => c.RectTransform.MaxSize = RectTransform.MaxPoint);
|
||||||
btn.Font = GUIStyle.SmallFont;
|
btn.Font = GUIStyle.SmallFont;
|
||||||
@@ -5635,8 +5625,7 @@ namespace Barotrauma
|
|||||||
MouseDragStart = Vector2.Zero;
|
MouseDragStart = Vector2.Zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!saveAssemblyFrame.Rect.Contains(PlayerInput.MousePosition)
|
if ((GUI.MouseOn == null || !GUI.MouseOn.IsChildOf(TopPanel))
|
||||||
&& !snapToGridFrame.Rect.Contains(PlayerInput.MousePosition)
|
|
||||||
&& dummyCharacter?.SelectedItem == null && !WiringMode
|
&& dummyCharacter?.SelectedItem == null && !WiringMode
|
||||||
&& (GUI.MouseOn == null || MapEntity.SelectedAny || MapEntity.SelectionPos != Vector2.Zero))
|
&& (GUI.MouseOn == null || MapEntity.SelectedAny || MapEntity.SelectionPos != Vector2.Zero))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -779,6 +779,8 @@ namespace Barotrauma
|
|||||||
workshopMenu = Screen.Selected is MainMenuScreen
|
workshopMenu = Screen.Selected is MainMenuScreen
|
||||||
? (WorkshopMenu)new MutableWorkshopMenu(content)
|
? (WorkshopMenu)new MutableWorkshopMenu(content)
|
||||||
: (WorkshopMenu)new ImmutableWorkshopMenu(content);
|
: (WorkshopMenu)new ImmutableWorkshopMenu(content);
|
||||||
|
|
||||||
|
GameMain.MainMenuScreen.ResetModUpdateButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateBottomButtons()
|
private void CreateBottomButtons()
|
||||||
|
|||||||
@@ -648,6 +648,12 @@ namespace Barotrauma.Sounds
|
|||||||
|
|
||||||
if (isConnected == 0)
|
if (isConnected == 0)
|
||||||
{
|
{
|
||||||
|
if (!GameMain.Instance.HasLoaded)
|
||||||
|
{
|
||||||
|
//wait for loading to finish so we don't start releasing and reloading sounds when they're being loaded,
|
||||||
|
//or throw an error mid-loading that'd prevent the content package from being enabled
|
||||||
|
return;
|
||||||
|
}
|
||||||
DebugConsole.ThrowError("Playback device has been disconnected. You can select another available device in the settings.");
|
DebugConsole.ThrowError("Playback device has been disconnected. You can select another available device in the settings.");
|
||||||
SetAudioOutputDevice("<disconnected>");
|
SetAudioOutputDevice("<disconnected>");
|
||||||
Disconnected = true;
|
Disconnected = true;
|
||||||
|
|||||||
@@ -865,17 +865,18 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public static void PlayDamageSound(string damageType, float damage, Vector2 position, float range = 2000.0f, IEnumerable<Identifier> tags = null)
|
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 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,
|
//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
|
//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);
|
float randomizedDamage = MathHelper.Clamp(damage + Rand.Range(-10.0f, 10.0f), 0.0f, 100.0f);
|
||||||
|
suitableSounds = suitableSounds.Where(s =>
|
||||||
var suitableSounds = damageSounds.Where(s =>
|
s.DamageRange == Vector2.Zero || (randomizedDamage >= s.DamageRange.X && randomizedDamage <= s.DamageRange.Y));
|
||||||
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))));
|
|
||||||
|
|
||||||
var damageSound = suitableSounds.GetRandomUnsynced();
|
var damageSound = suitableSounds.GetRandomUnsynced();
|
||||||
damageSound?.Sound?.Play(1.0f, range, position, muffle: !damageSound.IgnoreMuffling && ShouldMuffleSound(Character.Controlled, position, range, null));
|
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(@"\[(.+?)\]",
|
protected static readonly Regex bbTagRegex = new Regex(@"\[(.+?)\]",
|
||||||
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
|
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;
|
Point cachedContainerSize = Point.Zero;
|
||||||
List<BBWord> bbWords = new List<BBWord>();
|
List<BBWord> bbWords = new List<BBWord>();
|
||||||
Stack<BBWord.TagType> tagStack = new Stack<BBWord.TagType>();
|
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();
|
bbWords.Clear();
|
||||||
cachedContainerSize = container.Content.RectTransform.NonScaledSize;
|
cachedContainerSize = component.RectTransform.NonScaledSize;
|
||||||
|
|
||||||
var matches = new Stack<Match>(bbTagRegex.Matches(bbCode).Reverse());
|
var matches = new Stack<Match>(bbTagRegex.Matches(bbCode).Reverse());
|
||||||
Match? nextTag = null;
|
Match? nextTag = null;
|
||||||
@@ -133,11 +141,14 @@ namespace Barotrauma.Steam
|
|||||||
{
|
{
|
||||||
bbWords.Add(new BBWord(finalWord, currTagType));
|
bbWords.Add(new BBWord(finalWord, currTagType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
container.RecalculateChildren();
|
||||||
|
container.UpdateScrollBarSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw(SpriteBatch spriteBatch, GUICustomComponent component)
|
void draw(SpriteBatch spriteBatch, GUICustomComponent component)
|
||||||
{
|
{
|
||||||
recalculate();
|
recalculate(component);
|
||||||
Vector2 currPos = Vector2.Zero;
|
Vector2 currPos = Vector2.Zero;
|
||||||
Vector2 rectPos = component.Rect.Location.ToVector2();
|
Vector2 rectPos = component.Rect.Location.ToVector2();
|
||||||
for (int i = 0; i < bbWords.Count; i++)
|
for (int i = 0; i < bbWords.Count; i++)
|
||||||
@@ -180,7 +191,19 @@ namespace Barotrauma.Steam
|
|||||||
= component.RectTransform.NonScaledSize.ToVector2() / component.Parent.Rect.Size.ToVector2();
|
= 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);
|
onDraw: draw);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace Barotrauma.Steam
|
|||||||
if (numSubscribedMods == memSubscribedModCount) { return; }
|
if (numSubscribedMods == memSubscribedModCount) { return; }
|
||||||
memSubscribedModCount = numSubscribedMods;
|
memSubscribedModCount = numSubscribedMods;
|
||||||
|
|
||||||
var subscribedIds = SteamManager.GetSubscribedItems().ToHashSet();
|
var subscribedIds = SteamManager.Workshop.GetSubscribedItemIds();
|
||||||
var installedIds = ContentPackageManager.WorkshopPackages
|
var installedIds = ContentPackageManager.WorkshopPackages
|
||||||
.Select(p => p.UgcId)
|
.Select(p => p.UgcId)
|
||||||
.NotNone()
|
.NotNone()
|
||||||
|
|||||||
@@ -773,7 +773,7 @@ namespace Barotrauma.Steam
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
var descriptionListBox = new GUIListBox(new RectTransform((1.0f, 0.38f), verticalLayout.RectTransform));
|
var descriptionListBox = new GUIListBox(new RectTransform((1.0f, 0.38f), verticalLayout.RectTransform));
|
||||||
CreateBBCodeElement(workshopItem.Description, descriptionListBox);
|
CreateBBCodeElement(workshopItem, descriptionListBox);
|
||||||
|
|
||||||
var showInSteamContainer
|
var showInSteamContainer
|
||||||
= new GUIFrame(new RectTransform((1.0f, 0.05f), verticalLayout.RectTransform), style: null);
|
= new GUIFrame(new RectTransform((1.0f, 0.05f), verticalLayout.RectTransform), style: null);
|
||||||
|
|||||||
@@ -218,6 +218,20 @@ namespace Barotrauma.Steam
|
|||||||
var descriptionTextBox
|
var descriptionTextBox
|
||||||
= ScrollableTextBox(rightTop, 6.0f, workshopItem.Description ?? string.Empty);
|
= 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)
|
var (leftBottom, _, rightBottom)
|
||||||
= CreateSidebars(mainLayout, leftWidth: 0.49f, centerWidth: 0.01f, rightWidth: 0.5f, height: 0.5f);
|
= CreateSidebars(mainLayout, leftWidth: 0.49f, centerWidth: 0.01f, rightWidth: 0.5f, height: 0.5f);
|
||||||
leftBottom.Stretch = true;
|
leftBottom.Stretch = true;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace Barotrauma
|
namespace Barotrauma
|
||||||
{
|
{
|
||||||
partial class UpgradePrefab
|
sealed partial class UpgradePrefab
|
||||||
{
|
{
|
||||||
public readonly ImmutableArray<DecorativeSprite> DecorativeSprites = new ImmutableArray<DecorativeSprite>();
|
public readonly ImmutableArray<DecorativeSprite> DecorativeSprites = new ImmutableArray<DecorativeSprite>();
|
||||||
public Sprite Sprite { get; private set; }
|
public Sprite Sprite { get; private set; }
|
||||||
|
|||||||
@@ -23,36 +23,55 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public static void ConvertMasterLocalizationKit(string outputTextsDirectory, string outputConversationsDirectory, bool convertConversations)
|
public static void ConvertMasterLocalizationKit(string outputTextsDirectory, string outputConversationsDirectory, bool convertConversations)
|
||||||
{
|
{
|
||||||
string textFilePath = Path.Combine(infoTextPath, "Texts.csv");
|
List<string> languages = new List<string>();
|
||||||
string conversationFilePath = Path.Combine(infoTextPath, "NPCConversations.csv");
|
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;
|
Dictionary<string, List<string>> xmlContent;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
xmlContent = ConvertInfoTextToXML(File.ReadAllLines(textFilePath, Encoding.UTF8));
|
xmlContent = ConvertInfoTextToXML(File.ReadAllLines(textFilePath, Encoding.UTF8));
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
DebugConsole.ThrowError("InfoText Localization .csv to .xml conversion failed for: " + textFilePath, e);
|
DebugConsole.ThrowError("InfoText Localization .csv to .xml conversion failed for: " + textFilePath, e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (xmlContent == null)
|
if (xmlContent == null)
|
||||||
{
|
{
|
||||||
DebugConsole.ThrowError("InfoText Localization .csv to .xml conversion failed for: " + textFilePath);
|
DebugConsole.ThrowError("InfoText Localization .csv to .xml conversion failed for: " + textFilePath);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
foreach (string language in xmlContent.Keys)
|
foreach (string language in xmlContent.Keys)
|
||||||
{
|
{
|
||||||
string languageNoWhitespace = language.Replace(" ", "");
|
languages.Add(language);
|
||||||
string xmlFileFullPath = Path.Combine(outputTextsDirectory, $"{languageNoWhitespace}/{languageNoWhitespace}Vanilla.xml");
|
string languageNoWhitespace = language.Replace(" ", "");
|
||||||
File.WriteAllLines(xmlFileFullPath, xmlContent[language], Encoding.UTF8);
|
string xmlFileFullPath = Path.Combine(outputTextsDirectory, $"{languageNoWhitespace}/{languageNoWhitespace}{outputFileName}");
|
||||||
DebugConsole.NewMessage("InfoText localization .xml file successfully created at: " + xmlFileFullPath);
|
File.WriteAllLines(xmlFileFullPath, xmlContent[language], Encoding.UTF8);
|
||||||
|
DebugConsole.NewMessage("InfoText localization .xml file successfully created at: " + xmlFileFullPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (convertConversations)
|
if (convertConversations)
|
||||||
{
|
{
|
||||||
|
string conversationFilePath = Path.Combine(infoTextPath, "NPCConversations.csv");
|
||||||
var conversationLinesAll = File.ReadAllLines(conversationFilePath, Encoding.UTF8);
|
var conversationLinesAll = File.ReadAllLines(conversationFilePath, Encoding.UTF8);
|
||||||
foreach (string language in xmlContent.Keys)
|
foreach (string language in languages)
|
||||||
{
|
{
|
||||||
List<string> convXmlContent = ConvertConversationsToXML(conversationLinesAll, language);
|
List<string> convXmlContent = ConvertConversationsToXML(conversationLinesAll, language);
|
||||||
if (convXmlContent == null)
|
if (convXmlContent == null)
|
||||||
@@ -61,7 +80,7 @@ namespace Barotrauma
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
string languageNoWhitespace = language.Replace(" ", "");
|
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);
|
File.WriteAllLines(xmlFileFullPath, convXmlContent, Encoding.UTF8);
|
||||||
DebugConsole.NewMessage("Conversation localization .xml file successfully created at: " + xmlFileFullPath);
|
DebugConsole.NewMessage("Conversation localization .xml file successfully created at: " + xmlFileFullPath);
|
||||||
}
|
}
|
||||||
@@ -339,7 +358,8 @@ namespace Barotrauma
|
|||||||
string[] headerSplit = csvContent[0].Split(separator);
|
string[] headerSplit = csvContent[0].Split(separator);
|
||||||
for (int i = 0; i < headerSplit.Length; i++)
|
for (int i = 0; i < headerSplit.Length; i++)
|
||||||
{
|
{
|
||||||
if (headerSplit[i] == language)
|
if (headerSplit[i] == language ||
|
||||||
|
(language == "English" && headerSplit[i]== "Line (Original)"))
|
||||||
{
|
{
|
||||||
languageColumn = i;
|
languageColumn = i;
|
||||||
break;
|
break;
|
||||||
@@ -348,6 +368,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
xmlContent.Add($"<Conversations identifier=\"vanillaconversations\" Language=\"{language}\" nowhitespace=\"{nowhitespace}\">");
|
xmlContent.Add($"<Conversations identifier=\"vanillaconversations\" Language=\"{language}\" nowhitespace=\"{nowhitespace}\">");
|
||||||
|
|
||||||
|
conversationClosingIndent.Clear();
|
||||||
int conversationStart = 1;
|
int conversationStart = 1;
|
||||||
|
|
||||||
xmlContent.Add(string.Empty);
|
xmlContent.Add(string.Empty);
|
||||||
@@ -399,9 +420,9 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
string[] nextConversationElement = csvContent[i + 1].Split(separator);
|
string[] nextConversationElement = csvContent[i + 1].Split(separator);
|
||||||
|
|
||||||
if (nextConversationElement[1] != string.Empty)
|
if (nextConversationElement[3] != string.Empty)
|
||||||
{
|
{
|
||||||
nextDepth = int.Parse(nextConversationElement[2]);
|
nextDepth = int.Parse(nextConversationElement[3]);
|
||||||
nextIsSubConvo = nextDepth > depthIndex;
|
nextIsSubConvo = nextDepth > depthIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -421,7 +442,12 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
//end of file, close remaining xml tags
|
||||||
xmlContent.Add(element.TrimEnd() + "/>");
|
xmlContent.Add(element.TrimEnd() + "/>");
|
||||||
|
for (int j = depthIndex - 1; j >= 0; j--)
|
||||||
|
{
|
||||||
|
HandleClosingElements(xmlContent, j);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,12 +459,12 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private static void HandleClosingElements(List<string> xmlContent, int targetDepth)
|
private static void HandleClosingElements(List<string> xmlContent, int targetDepth)
|
||||||
{
|
{
|
||||||
if (conversationClosingIndent.Count == 0) return;
|
if (conversationClosingIndent.Count == 0) { return; }
|
||||||
|
|
||||||
for (int k = conversationClosingIndent.Count - 1; k >= 0; k--)
|
for (int k = conversationClosingIndent.Count - 1; k >= 0; k--)
|
||||||
{
|
{
|
||||||
int currentIndent = conversationClosingIndent[k];
|
int currentIndent = conversationClosingIndent[k];
|
||||||
if (currentIndent < targetDepth) break;
|
if (currentIndent < targetDepth) { break; }
|
||||||
xmlContent.Add($"{GetIndenting(currentIndent)}</Conversation>");
|
xmlContent.Add($"{GetIndenting(currentIndent)}</Conversation>");
|
||||||
conversationClosingIndent.RemoveAt(k);
|
conversationClosingIndent.RemoveAt(k);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,28 +7,30 @@ using System.Text;
|
|||||||
|
|
||||||
namespace Barotrauma
|
namespace Barotrauma
|
||||||
{
|
{
|
||||||
class SpriteRecorder : ISpriteBatch, IDisposable
|
sealed class SpriteRecorder : ISpriteBatch, IDisposable
|
||||||
{
|
{
|
||||||
private struct Command
|
private readonly record struct Command(
|
||||||
|
Texture2D Texture,
|
||||||
|
VertexPositionColorTexture VertexBL,
|
||||||
|
VertexPositionColorTexture VertexBR,
|
||||||
|
VertexPositionColorTexture VertexTL,
|
||||||
|
VertexPositionColorTexture VertexTR,
|
||||||
|
float Depth,
|
||||||
|
Vector2 Min,
|
||||||
|
Vector2 Max,
|
||||||
|
int Index)
|
||||||
{
|
{
|
||||||
public readonly Texture2D Texture;
|
public static Vector2 GetMinPosition(params VertexPositionColorTexture[] vertices)
|
||||||
public readonly VertexPositionColorTexture VertexBL;
|
=> new Vector2(
|
||||||
public readonly VertexPositionColorTexture VertexBR;
|
MathUtils.Min(vertices.Select(v => v.Position.X).ToArray()),
|
||||||
public readonly VertexPositionColorTexture VertexTL;
|
MathUtils.Min(vertices.Select(v => v.Position.Y).ToArray()));
|
||||||
public readonly VertexPositionColorTexture VertexTR;
|
|
||||||
public readonly float Depth;
|
public static Vector2 GetMaxPosition(params VertexPositionColorTexture[] vertices)
|
||||||
public readonly Vector2 Min;
|
=> new Vector2(
|
||||||
public readonly Vector2 Max;
|
MathUtils.Max(vertices.Select(v => v.Position.X).ToArray()),
|
||||||
public readonly int Index;
|
MathUtils.Max(vertices.Select(v => v.Position.Y).ToArray()));
|
||||||
|
|
||||||
public bool Overlaps(Command other)
|
public static Command FromTransform(
|
||||||
{
|
|
||||||
return
|
|
||||||
Min.X <= other.Max.X && Max.X >= other.Min.X &&
|
|
||||||
Min.Y <= other.Max.Y && Max.Y >= other.Min.Y;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Command(
|
|
||||||
Texture2D texture,
|
Texture2D texture,
|
||||||
Vector2 pos,
|
Vector2 pos,
|
||||||
Rectangle srcRect,
|
Rectangle srcRect,
|
||||||
@@ -46,15 +48,11 @@ namespace Barotrauma
|
|||||||
int srcRectBottom = srcRect.Bottom;
|
int srcRectBottom = srcRect.Bottom;
|
||||||
if (effects.HasFlag(SpriteEffects.FlipHorizontally))
|
if (effects.HasFlag(SpriteEffects.FlipHorizontally))
|
||||||
{
|
{
|
||||||
var temp = srcRectRight;
|
(srcRectRight, srcRectLeft) = (srcRectLeft, srcRectRight);
|
||||||
srcRectRight = srcRectLeft;
|
|
||||||
srcRectLeft = temp;
|
|
||||||
}
|
}
|
||||||
if (effects.HasFlag(SpriteEffects.FlipVertically))
|
if (effects.HasFlag(SpriteEffects.FlipVertically))
|
||||||
{
|
{
|
||||||
var temp = srcRectBottom;
|
(srcRectBottom, srcRectTop) = (srcRectTop, srcRectBottom);
|
||||||
srcRectBottom = srcRectTop;
|
|
||||||
srcRectTop = temp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rotation = MathHelper.ToRadians(rotation);
|
rotation = MathHelper.ToRadians(rotation);
|
||||||
@@ -68,59 +66,63 @@ namespace Barotrauma
|
|||||||
pos.X -= origin.X * scale.X * cos - origin.Y * scale.Y * sin;
|
pos.X -= origin.X * scale.X * cos - origin.Y * scale.Y * sin;
|
||||||
pos.Y -= origin.Y * scale.Y * cos + origin.X * scale.X * sin;
|
pos.Y -= origin.Y * scale.Y * cos + origin.X * scale.X * sin;
|
||||||
|
|
||||||
Texture = texture;
|
var vertexTl = new VertexPositionColorTexture
|
||||||
|
{
|
||||||
|
Color = color,
|
||||||
|
Position = new Vector3(pos.X, pos.Y, 0f),
|
||||||
|
TextureCoordinate = new Vector2((float)srcRectLeft / (float)texture.Width, (float)srcRectTop / (float)texture.Height)
|
||||||
|
};
|
||||||
|
|
||||||
Depth = depth;
|
var vertexTr = new VertexPositionColorTexture
|
||||||
|
{
|
||||||
|
Color = color,
|
||||||
|
Position = new Vector3(pos.X + wAdd.X, pos.Y + wAdd.Y, 0f),
|
||||||
|
TextureCoordinate = new Vector2((float)srcRectRight / (float)texture.Width, (float)srcRectTop / (float)texture.Height)
|
||||||
|
};
|
||||||
|
|
||||||
VertexTL.Color = color;
|
var vertexBl = new VertexPositionColorTexture
|
||||||
VertexTR.Color = color;
|
{
|
||||||
VertexBL.Color = color;
|
Color = color,
|
||||||
VertexBR.Color = color;
|
Position = new Vector3(pos.X + hAdd.X, pos.Y + hAdd.Y, 0f),
|
||||||
|
TextureCoordinate = new Vector2((float)srcRectLeft / (float)texture.Width, (float)srcRectBottom / (float)texture.Height)
|
||||||
|
};
|
||||||
|
|
||||||
VertexTL.Position = new Vector3(pos.X, pos.Y, 0f);
|
var vertexBr = new VertexPositionColorTexture
|
||||||
VertexTR.Position = new Vector3(pos.X + wAdd.X, pos.Y + wAdd.Y, 0f);
|
{
|
||||||
VertexBL.Position = new Vector3(pos.X + hAdd.X, pos.Y + hAdd.Y, 0f);
|
Color = color,
|
||||||
VertexBR.Position = new Vector3(pos.X + wAdd.X + hAdd.X, pos.Y + wAdd.Y + hAdd.Y, 0f);
|
Position = new Vector3(pos.X + wAdd.X + hAdd.X, pos.Y + wAdd.Y + hAdd.Y, 0f),
|
||||||
|
TextureCoordinate = new Vector2((float)srcRectRight / (float)texture.Width, (float)srcRectBottom / (float)texture.Height)
|
||||||
|
};
|
||||||
|
|
||||||
Min = new Vector2(
|
var min = GetMinPosition(
|
||||||
MathUtils.Min
|
vertexTl,
|
||||||
(
|
vertexTr,
|
||||||
VertexTL.Position.X,
|
vertexBl,
|
||||||
VertexTR.Position.X,
|
vertexBr);
|
||||||
VertexBL.Position.X,
|
|
||||||
VertexBR.Position.X
|
|
||||||
),
|
|
||||||
MathUtils.Min
|
|
||||||
(
|
|
||||||
VertexTL.Position.Y,
|
|
||||||
VertexTR.Position.Y,
|
|
||||||
VertexBL.Position.Y,
|
|
||||||
VertexBR.Position.Y
|
|
||||||
));
|
|
||||||
|
|
||||||
Max = new Vector2(
|
var max = GetMaxPosition(
|
||||||
MathUtils.Max
|
vertexTl,
|
||||||
(
|
vertexTr,
|
||||||
VertexTL.Position.X,
|
vertexBl,
|
||||||
VertexTR.Position.X,
|
vertexBr);
|
||||||
VertexBL.Position.X,
|
|
||||||
VertexBR.Position.X
|
|
||||||
),
|
|
||||||
MathUtils.Max
|
|
||||||
(
|
|
||||||
VertexTL.Position.Y,
|
|
||||||
VertexTR.Position.Y,
|
|
||||||
VertexBL.Position.Y,
|
|
||||||
VertexBR.Position.Y
|
|
||||||
));
|
|
||||||
|
|
||||||
VertexTL.TextureCoordinate = new Vector2((float)srcRectLeft / (float)texture.Width, (float)srcRectTop / (float)texture.Height);
|
return new Command(
|
||||||
VertexTR.TextureCoordinate = new Vector2((float)srcRectRight / (float)texture.Width, (float)srcRectTop / (float)texture.Height);
|
texture,
|
||||||
VertexBL.TextureCoordinate = new Vector2((float)srcRectLeft / (float)texture.Width, (float)srcRectBottom / (float)texture.Height);
|
vertexBl,
|
||||||
VertexBR.TextureCoordinate = new Vector2((float)srcRectRight / (float)texture.Width, (float)srcRectBottom / (float)texture.Height);
|
vertexBr,
|
||||||
|
vertexTl,
|
||||||
Index = index;
|
vertexTr,
|
||||||
|
depth,
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Overlaps(Command other)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
Min.X <= other.Max.X && Max.X >= other.Min.X &&
|
||||||
|
Min.Y <= other.Max.Y && Max.Y >= other.Min.Y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,8 +153,8 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public static BasicEffect BasicEffect = null;
|
public static BasicEffect BasicEffect = null;
|
||||||
|
|
||||||
private List<RecordedBuffer> recordedBuffers = new List<RecordedBuffer>();
|
private readonly List<RecordedBuffer> recordedBuffers = new List<RecordedBuffer>();
|
||||||
private List<Command> commandList = new List<Command>();
|
private readonly List<Command> commandList = new List<Command>();
|
||||||
private SpriteSortMode currentSortMode;
|
private SpriteSortMode currentSortMode;
|
||||||
|
|
||||||
private IndexBuffer indexBuffer = null;
|
private IndexBuffer indexBuffer = null;
|
||||||
@@ -170,16 +172,45 @@ namespace Barotrauma
|
|||||||
currentSortMode = sortMode;
|
currentSortMode = sortMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Draw(Texture2D texture, Vector2 pos, Rectangle? srcRect, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float depth)
|
private void AppendCommand(Command command)
|
||||||
{
|
{
|
||||||
if (isDisposed) { return; }
|
if (isDisposed) { return; }
|
||||||
|
|
||||||
Command command = new Command(texture, pos, srcRect ?? texture.Bounds, color, rotation, origin, scale, effects, depth, commandList?.Count ?? 0);
|
|
||||||
if (commandList.Count == 0) { Min = command.Min; Max = command.Max; }
|
if (commandList.Count == 0) { Min = command.Min; Max = command.Max; }
|
||||||
Min = new Vector2(Math.Min(command.Min.X, Min.X), Math.Min(command.Min.Y, Min.Y));
|
Min = new Vector2(Math.Min(command.Min.X, Min.X), Math.Min(command.Min.Y, Min.Y));
|
||||||
Max = new Vector2(Math.Max(command.Max.X, Max.X), Math.Max(command.Max.Y, Max.Y));
|
Max = new Vector2(Math.Max(command.Max.X, Max.X), Math.Max(command.Max.Y, Max.Y));
|
||||||
|
|
||||||
commandList?.Add(command);
|
commandList.Add(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Draw(Texture2D texture, Vector2 pos, Rectangle? srcRect, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float depth)
|
||||||
|
{
|
||||||
|
if (isDisposed) { return; }
|
||||||
|
|
||||||
|
var command = Command.FromTransform(texture, pos, srcRect ?? texture.Bounds, color, rotation, origin, scale, effects, depth, commandList.Count);
|
||||||
|
AppendCommand(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Draw(Texture2D texture, VertexPositionColorTexture[] vertices, float layerDepth, int? count = null)
|
||||||
|
{
|
||||||
|
if (isDisposed) { return; }
|
||||||
|
|
||||||
|
int iters = count ?? (vertices.Length / 4);
|
||||||
|
for (int i=0;i<iters;i++)
|
||||||
|
{
|
||||||
|
var subset = vertices[((i * 4) + 0)..((i * 4) + 4)];
|
||||||
|
var command = new Command(
|
||||||
|
texture,
|
||||||
|
subset[2],
|
||||||
|
subset[3],
|
||||||
|
subset[0],
|
||||||
|
subset[1],
|
||||||
|
layerDepth,
|
||||||
|
Command.GetMinPosition(subset),
|
||||||
|
Command.GetMaxPosition(subset),
|
||||||
|
commandList.Count);
|
||||||
|
AppendCommand(command);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void End()
|
public void End()
|
||||||
@@ -309,16 +340,13 @@ namespace Barotrauma
|
|||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
isDisposed = true;
|
isDisposed = true;
|
||||||
if (recordedBuffers != null)
|
foreach (var buffer in recordedBuffers)
|
||||||
{
|
{
|
||||||
foreach (var buffer in recordedBuffers)
|
buffer.VertexBuffer.Dispose();
|
||||||
{
|
|
||||||
buffer.VertexBuffer.Dispose();
|
|
||||||
}
|
|
||||||
recordedBuffers.Clear(); recordedBuffers = null;
|
|
||||||
}
|
}
|
||||||
commandList?.Clear(); commandList = null;
|
recordedBuffers.Clear();
|
||||||
indexBuffer?.Dispose(); indexBuffer = null;
|
commandList.Clear();
|
||||||
|
indexBuffer.Dispose(); indexBuffer = null;
|
||||||
ReadyToRender = false;
|
ReadyToRender = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<RootNamespace>Barotrauma</RootNamespace>
|
<RootNamespace>Barotrauma</RootNamespace>
|
||||||
<Authors>FakeFish, Undertow Games</Authors>
|
<Authors>FakeFish, Undertow Games</Authors>
|
||||||
<Product>Barotrauma</Product>
|
<Product>Barotrauma</Product>
|
||||||
<Version>0.20.15.0</Version>
|
<Version>0.21.1.0</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
<AssemblyName>Barotrauma</AssemblyName>
|
<AssemblyName>Barotrauma</AssemblyName>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<RootNamespace>Barotrauma</RootNamespace>
|
<RootNamespace>Barotrauma</RootNamespace>
|
||||||
<Authors>FakeFish, Undertow Games</Authors>
|
<Authors>FakeFish, Undertow Games</Authors>
|
||||||
<Product>Barotrauma</Product>
|
<Product>Barotrauma</Product>
|
||||||
<Version>0.20.15.0</Version>
|
<Version>0.21.1.0</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
<AssemblyName>Barotrauma</AssemblyName>
|
<AssemblyName>Barotrauma</AssemblyName>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<RootNamespace>Barotrauma</RootNamespace>
|
<RootNamespace>Barotrauma</RootNamespace>
|
||||||
<Authors>FakeFish, Undertow Games</Authors>
|
<Authors>FakeFish, Undertow Games</Authors>
|
||||||
<Product>Barotrauma</Product>
|
<Product>Barotrauma</Product>
|
||||||
<Version>0.20.15.0</Version>
|
<Version>0.21.1.0</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
<AssemblyName>Barotrauma</AssemblyName>
|
<AssemblyName>Barotrauma</AssemblyName>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<RootNamespace>Barotrauma</RootNamespace>
|
<RootNamespace>Barotrauma</RootNamespace>
|
||||||
<Authors>FakeFish, Undertow Games</Authors>
|
<Authors>FakeFish, Undertow Games</Authors>
|
||||||
<Product>Barotrauma Dedicated Server</Product>
|
<Product>Barotrauma Dedicated Server</Product>
|
||||||
<Version>0.20.15.0</Version>
|
<Version>0.21.1.0</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
<AssemblyName>DedicatedServer</AssemblyName>
|
<AssemblyName>DedicatedServer</AssemblyName>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<RootNamespace>Barotrauma</RootNamespace>
|
<RootNamespace>Barotrauma</RootNamespace>
|
||||||
<Authors>FakeFish, Undertow Games</Authors>
|
<Authors>FakeFish, Undertow Games</Authors>
|
||||||
<Product>Barotrauma Dedicated Server</Product>
|
<Product>Barotrauma Dedicated Server</Product>
|
||||||
<Version>0.20.15.0</Version>
|
<Version>0.21.1.0</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
<AssemblyName>DedicatedServer</AssemblyName>
|
<AssemblyName>DedicatedServer</AssemblyName>
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
partial class Character
|
partial class Character
|
||||||
{
|
{
|
||||||
public Address OwnerClientAddress;
|
private Address ownerClientAddress;
|
||||||
public string OwnerClientName;
|
private Option<AccountId> ownerClientAccountId;
|
||||||
|
|
||||||
public bool ClientDisconnected;
|
public bool ClientDisconnected;
|
||||||
public float KillDisconnectedTimer;
|
public float KillDisconnectedTimer;
|
||||||
|
|
||||||
@@ -19,6 +20,35 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public bool HealthUpdatePending;
|
public bool HealthUpdatePending;
|
||||||
|
|
||||||
|
public void SetOwnerClient(Client client)
|
||||||
|
{
|
||||||
|
if (client == null)
|
||||||
|
{
|
||||||
|
ownerClientAddress = null;
|
||||||
|
ownerClientAccountId = Option<AccountId>.None();
|
||||||
|
IsRemotePlayer = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ownerClientAddress = client.Connection.Endpoint.Address;
|
||||||
|
ownerClientAccountId = client.AccountId;
|
||||||
|
IsRemotePlayer = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsClientOwner(Client client)
|
||||||
|
{
|
||||||
|
if (ownerClientAccountId.TryUnwrap(out var accountId)
|
||||||
|
&& client.AccountId.TryUnwrap(out var clientId))
|
||||||
|
{
|
||||||
|
return accountId == clientId;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return ownerClientAddress == client.Connection.Endpoint.Address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public float GetPositionUpdateInterval(Client recipient)
|
public float GetPositionUpdateInterval(Client recipient)
|
||||||
{
|
{
|
||||||
if (!Enabled) { return 1000.0f; }
|
if (!Enabled) { return 1000.0f; }
|
||||||
@@ -302,12 +332,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)
|
if (this == c.Character)
|
||||||
{
|
{
|
||||||
tempBuffer.WriteBoolean(true);
|
tempBuffer.WriteBoolean(true);
|
||||||
@@ -405,11 +431,6 @@ namespace Barotrauma
|
|||||||
AIController?.ServerWrite(tempBuffer);
|
AIController?.ServerWrite(tempBuffer);
|
||||||
HealthUpdatePending = false;
|
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)
|
public virtual void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
|
||||||
|
|||||||
@@ -1374,7 +1374,7 @@ namespace Barotrauma
|
|||||||
MultiPlayerCampaign.StartCampaignSetup();
|
MultiPlayerCampaign.StartCampaignSetup();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!GameMain.Server.StartGame()) { NewMessage("Failed to start a new round", Color.Yellow); }
|
if (!GameMain.Server.TryStartGame()) { NewMessage("Failed to start a new round", Color.Yellow); }
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public static ContentPackage VanillaContent => ContentPackageManager.VanillaCorePackage;
|
public static ContentPackage VanillaContent => ContentPackageManager.VanillaCorePackage;
|
||||||
|
|
||||||
|
|
||||||
public readonly string[] CommandLineArgs;
|
public readonly string[] CommandLineArgs;
|
||||||
|
|
||||||
public GameMain(string[] args)
|
public GameMain(string[] args)
|
||||||
|
|||||||
@@ -27,12 +27,9 @@ namespace Barotrauma
|
|||||||
AnyOneAllowedToManageCampaign(permissions);
|
AnyOneAllowedToManageCampaign(permissions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AllowedToManageWallets(Client client)
|
public static bool AllowedToManageWallets(Client client)
|
||||||
{
|
{
|
||||||
return
|
return AllowedToManageCampaign(client, ClientPermissions.ManageMoney);
|
||||||
client.HasPermission(ClientPermissions.ManageCampaign) ||
|
|
||||||
client.HasPermission(ClientPermissions.ManageMoney) ||
|
|
||||||
IsOwner(client);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void ShowStartMessage()
|
public override void ShowStartMessage()
|
||||||
|
|||||||
+2
-2
@@ -146,7 +146,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
NextLevel = map.SelectedConnection?.LevelData ?? map.CurrentLocation.LevelData;
|
NextLevel = map.SelectedConnection?.LevelData ?? map.CurrentLocation.LevelData;
|
||||||
MirrorLevel = false;
|
MirrorLevel = false;
|
||||||
GameMain.Server.StartGame();
|
GameMain.Server.TryStartGame();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void StartCampaignSetup()
|
public static void StartCampaignSetup()
|
||||||
@@ -395,7 +395,7 @@ namespace Barotrauma
|
|||||||
yield return new WaitForSeconds(EndTransitionDuration * 0.5f);
|
yield return new WaitForSeconds(EndTransitionDuration * 0.5f);
|
||||||
}
|
}
|
||||||
|
|
||||||
GameMain.Server.StartGame();
|
GameMain.Server.TryStartGame();
|
||||||
|
|
||||||
yield return CoroutineStatus.Success;
|
yield return CoroutineStatus.Success;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ namespace Barotrauma.Items.Components
|
|||||||
msg.WriteBoolean(launch);
|
msg.WriteBoolean(launch);
|
||||||
if (launch)
|
if (launch)
|
||||||
{
|
{
|
||||||
msg.WriteUInt16(User.ID);
|
msg.WriteUInt16(User?.ID ?? Entity.NullEntityID);
|
||||||
msg.WriteSingle(launchPos.X);
|
msg.WriteSingle(launchPos.X);
|
||||||
msg.WriteSingle(launchPos.Y);
|
msg.WriteSingle(launchPos.Y);
|
||||||
msg.WriteSingle(launchRot);
|
msg.WriteSingle(launchRot);
|
||||||
|
|||||||
@@ -348,15 +348,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);
|
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
|
public void CreateServerEvent<T>(T ic) where T : ItemComponent, IServerSerializable
|
||||||
@@ -378,7 +372,7 @@ namespace Barotrauma
|
|||||||
if (!components.Contains(ic)) { return; }
|
if (!components.Contains(ic)) { return; }
|
||||||
|
|
||||||
var eventData = new ComponentStateEventData(ic, extraData);
|
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);
|
GameMain.Server.CreateEntityEvent(this, eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,14 +5,9 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
partial class Submarine
|
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);
|
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)
|
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
private DateTime roundStartTime;
|
private DateTime roundStartTime;
|
||||||
|
|
||||||
|
private bool wasReadyToStartAutomatically;
|
||||||
private bool autoRestartTimerRunning;
|
private bool autoRestartTimerRunning;
|
||||||
private float endRoundTimer;
|
private float endRoundTimer;
|
||||||
|
|
||||||
@@ -366,8 +367,7 @@ namespace Barotrauma.Networking
|
|||||||
character.KillDisconnectedTimer += deltaTime;
|
character.KillDisconnectedTimer += deltaTime;
|
||||||
character.SetStun(1.0f);
|
character.SetStun(1.0f);
|
||||||
|
|
||||||
Client owner = connectedClients.Find(c => (c.Character == null || c.Character == character) && c.AddressMatches(character.OwnerClientAddress));
|
Client owner = connectedClients.Find(c => (c.Character == null || c.Character == character) && character.IsClientOwner(c));
|
||||||
|
|
||||||
if ((OwnerConnection == null || owner?.Connection != OwnerConnection) && character.KillDisconnectedTimer > ServerSettings.KillDisconnectedTime)
|
if ((OwnerConnection == null || owner?.Connection != OwnerConnection) && character.KillDisconnectedTimer > ServerSettings.KillDisconnectedTime)
|
||||||
{
|
{
|
||||||
character.Kill(CauseOfDeathType.Disconnected, null);
|
character.Kill(CauseOfDeathType.Disconnected, null);
|
||||||
@@ -504,8 +504,7 @@ namespace Barotrauma.Networking
|
|||||||
initiatedStartGame = false;
|
initiatedStartGame = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (Screen.Selected == GameMain.NetLobbyScreen && !GameStarted && !initiatedStartGame &&
|
else if (Screen.Selected == GameMain.NetLobbyScreen && !GameStarted && !initiatedStartGame)
|
||||||
(GameMain.NetLobbyScreen.SelectedMode != GameModePreset.MultiPlayerCampaign || GameMain.GameSession?.GameMode is MultiPlayerCampaign))
|
|
||||||
{
|
{
|
||||||
if (ServerSettings.AutoRestart)
|
if (ServerSettings.AutoRestart)
|
||||||
{
|
{
|
||||||
@@ -526,18 +525,25 @@ namespace Barotrauma.Networking
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool readyToStartAutomatically = false;
|
||||||
if (ServerSettings.AutoRestart && autoRestartTimerRunning && ServerSettings.AutoRestartTimer < 0.0f)
|
if (ServerSettings.AutoRestart && autoRestartTimerRunning && ServerSettings.AutoRestartTimer < 0.0f)
|
||||||
{
|
{
|
||||||
StartGame();
|
readyToStartAutomatically = true;
|
||||||
}
|
}
|
||||||
else if (ServerSettings.StartWhenClientsReady)
|
else if (ServerSettings.StartWhenClientsReady)
|
||||||
{
|
{
|
||||||
int clientsReady = connectedClients.Count(c => c.GetVote<bool>(VoteType.StartRound));
|
int clientsReady = connectedClients.Count(c => c.GetVote<bool>(VoteType.StartRound));
|
||||||
if (clientsReady / (float)connectedClients.Count >= ServerSettings.StartWhenClientsReadyRatio)
|
if (clientsReady / (float)connectedClients.Count >= ServerSettings.StartWhenClientsReadyRatio)
|
||||||
{
|
{
|
||||||
StartGame();
|
readyToStartAutomatically = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (readyToStartAutomatically)
|
||||||
|
{
|
||||||
|
if (!wasReadyToStartAutomatically) { GameMain.NetLobbyScreen.LastUpdateID++; }
|
||||||
|
TryStartGame();
|
||||||
|
}
|
||||||
|
wasReadyToStartAutomatically = readyToStartAutomatically;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = disconnectedClients.Count - 1; i >= 0; i--)
|
for (int i = disconnectedClients.Count - 1; i >= 0; i--)
|
||||||
@@ -763,7 +769,7 @@ namespace Barotrauma.Networking
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
string localSavePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer, saveName);
|
string localSavePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer, saveName);
|
||||||
if (connectedClient.HasPermission(ClientPermissions.SelectMode) || connectedClient.HasPermission(ClientPermissions.ManageCampaign))
|
if (CampaignMode.AllowedToManageCampaign(connectedClient, ClientPermissions.ManageRound))
|
||||||
{
|
{
|
||||||
ServerSettings.CampaignSettings = settings;
|
ServerSettings.CampaignSettings = settings;
|
||||||
ServerSettings.SaveSettings();
|
ServerSettings.SaveSettings();
|
||||||
@@ -779,7 +785,10 @@ namespace Barotrauma.Networking
|
|||||||
SendDirectChatMessage(TextManager.Get("CampaignStartFailedRoundRunning").Value, connectedClient, ChatMessageType.MessageBox);
|
SendDirectChatMessage(TextManager.Get("CampaignStartFailedRoundRunning").Value, connectedClient, ChatMessageType.MessageBox);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (connectedClient.HasPermission(ClientPermissions.SelectMode) || connectedClient.HasPermission(ClientPermissions.ManageCampaign)) { MultiPlayerCampaign.LoadCampaign(saveName); }
|
if (CampaignMode.AllowedToManageCampaign(connectedClient, ClientPermissions.ManageRound))
|
||||||
|
{
|
||||||
|
MultiPlayerCampaign.LoadCampaign(saveName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ClientPacketHeader.VOICE:
|
case ClientPacketHeader.VOICE:
|
||||||
@@ -1389,13 +1398,13 @@ namespace Barotrauma.Networking
|
|||||||
if (end)
|
if (end)
|
||||||
{
|
{
|
||||||
if (mpCampaign == null ||
|
if (mpCampaign == null ||
|
||||||
CampaignMode.AllowedToManageCampaign(sender, ClientPermissions.ManageRound) ||
|
CampaignMode.AllowedToManageCampaign(sender, ClientPermissions.ManageRound))
|
||||||
CampaignMode.AllowedToManageCampaign(sender, ClientPermissions.ManageCampaign))
|
|
||||||
{
|
{
|
||||||
bool save = inc.ReadBoolean();
|
bool save = inc.ReadBoolean();
|
||||||
|
bool quitCampaign = inc.ReadBoolean();
|
||||||
if (GameStarted)
|
if (GameStarted)
|
||||||
{
|
{
|
||||||
Log("Client \"" + GameServer.ClientLogName(sender) + "\" ended the round.", ServerLog.MessageType.ServerMessage);
|
Log($"Client \"{ClientLogName(sender)}\" ended the round.", ServerLog.MessageType.ServerMessage);
|
||||||
if (mpCampaign != null && Level.IsLoadedFriendlyOutpost && save)
|
if (mpCampaign != null && Level.IsLoadedFriendlyOutpost && save)
|
||||||
{
|
{
|
||||||
mpCampaign.SavePlayers();
|
mpCampaign.SavePlayers();
|
||||||
@@ -1409,6 +1418,14 @@ namespace Barotrauma.Networking
|
|||||||
}
|
}
|
||||||
EndGame(wasSaved: save);
|
EndGame(wasSaved: save);
|
||||||
}
|
}
|
||||||
|
else if (mpCampaign != null)
|
||||||
|
{
|
||||||
|
Log($"Client \"{ClientLogName(sender)}\" quit the currently active campaign.", ServerLog.MessageType.ServerMessage);
|
||||||
|
GameMain.GameSession = null;
|
||||||
|
GameMain.NetLobbyScreen.SelectedModeIdentifier = GameModePreset.Sandbox.Identifier;
|
||||||
|
GameMain.NetLobbyScreen.LastUpdateID++;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1425,12 +1442,11 @@ namespace Barotrauma.Networking
|
|||||||
{
|
{
|
||||||
MultiPlayerCampaign.LoadCampaign(GameMain.GameSession.SavePath);
|
MultiPlayerCampaign.LoadCampaign(GameMain.GameSession.SavePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (!GameStarted && !initiatedStartGame)
|
else if (!GameStarted && !initiatedStartGame)
|
||||||
{
|
{
|
||||||
Log("Client \"" + ClientLogName(sender) + "\" started the round.", ServerLog.MessageType.ServerMessage);
|
Log("Client \"" + ClientLogName(sender) + "\" started the round.", ServerLog.MessageType.ServerMessage);
|
||||||
StartGame();
|
TryStartGame();
|
||||||
}
|
}
|
||||||
else if (mpCampaign != null && (CampaignMode.AllowedToManageCampaign(sender, ClientPermissions.ManageCampaign) || CampaignMode.AllowedToManageCampaign(sender, ClientPermissions.ManageMap)))
|
else if (mpCampaign != null && (CampaignMode.AllowedToManageCampaign(sender, ClientPermissions.ManageCampaign) || CampaignMode.AllowedToManageCampaign(sender, ClientPermissions.ManageMap)))
|
||||||
{
|
{
|
||||||
@@ -1492,20 +1508,10 @@ namespace Barotrauma.Networking
|
|||||||
case ClientPermissions.SelectMode:
|
case ClientPermissions.SelectMode:
|
||||||
UInt16 modeIndex = inc.ReadUInt16();
|
UInt16 modeIndex = inc.ReadUInt16();
|
||||||
GameMain.NetLobbyScreen.SelectedModeIndex = modeIndex;
|
GameMain.NetLobbyScreen.SelectedModeIndex = modeIndex;
|
||||||
Log("Gamemode changed to " + GameMain.NetLobbyScreen.GameModes[GameMain.NetLobbyScreen.SelectedModeIndex].Name.Value, ServerLog.MessageType.ServerMessage);
|
Log("Gamemode changed to " + (GameMain.NetLobbyScreen.SelectedMode?.Name.Value ?? "none"), ServerLog.MessageType.ServerMessage);
|
||||||
|
if (GameMain.NetLobbyScreen.GameModes[modeIndex] == GameModePreset.MultiPlayerCampaign)
|
||||||
if (GameMain.NetLobbyScreen.GameModes[modeIndex].Identifier == "multiplayercampaign")
|
|
||||||
{
|
{
|
||||||
const int MaxSaves = 255;
|
TrySendCampaignSetupInfo(sender);
|
||||||
var saveInfos = SaveUtil.GetSaveFiles(SaveUtil.SaveType.Multiplayer, includeInCompatible: false);
|
|
||||||
IWriteMessage msg = new WriteOnlyMessage();
|
|
||||||
msg.WriteByte((byte)ServerPacketHeader.CAMPAIGN_SETUP_INFO);
|
|
||||||
msg.WriteByte((byte)Math.Min(saveInfos.Count, MaxSaves));
|
|
||||||
for (int i = 0; i < saveInfos.Count && i < MaxSaves; i++)
|
|
||||||
{
|
|
||||||
msg.WriteNetSerializableStruct(saveInfos[i]);
|
|
||||||
}
|
|
||||||
serverPeer.Send(msg, sender.Connection, DeliveryMethod.Reliable);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ClientPermissions.ManageCampaign:
|
case ClientPermissions.ManageCampaign:
|
||||||
@@ -1612,7 +1618,7 @@ namespace Barotrauma.Networking
|
|||||||
{
|
{
|
||||||
if (GameSettings.CurrentConfig.VerboseLogging)
|
if (GameSettings.CurrentConfig.VerboseLogging)
|
||||||
{
|
{
|
||||||
DebugConsole.NewMessage("Sending initial lobby update", Color.Gray);
|
DebugConsole.NewMessage($"Sending initial lobby update to {c.Name}", Color.Gray);
|
||||||
}
|
}
|
||||||
|
|
||||||
outmsg.WriteByte(c.SessionId);
|
outmsg.WriteByte(c.SessionId);
|
||||||
@@ -1745,9 +1751,9 @@ namespace Barotrauma.Networking
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
IWriteMessage tempBuffer = new ReadWriteMessage();
|
var tempBuffer = new ReadWriteMessage();
|
||||||
tempBuffer.WriteBoolean(entity is Item); tempBuffer.WritePadBits();
|
var entityPositionHeader = EntityPositionHeader.FromEntity(entity);
|
||||||
tempBuffer.WriteUInt32(entity is MapEntity me ? me.Prefab.UintIdentifier : (UInt32)0);
|
tempBuffer.WriteNetSerializableStruct(entityPositionHeader);
|
||||||
entityPositionSync.ServerWritePosition(tempBuffer, c);
|
entityPositionSync.ServerWritePosition(tempBuffer, c);
|
||||||
|
|
||||||
//no more room in this packet
|
//no more room in this packet
|
||||||
@@ -1758,6 +1764,7 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
segmentTable.StartNewSegment(ServerNetSegment.EntityPosition);
|
segmentTable.StartNewSegment(ServerNetSegment.EntityPosition);
|
||||||
outmsg.WritePadBits(); //padding is required here to make sure any padding bits within tempBuffer are read correctly
|
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.WriteBytes(tempBuffer.Buffer, 0, tempBuffer.LengthBytes);
|
||||||
outmsg.WritePadBits();
|
outmsg.WritePadBits();
|
||||||
|
|
||||||
@@ -1934,6 +1941,13 @@ namespace Barotrauma.Networking
|
|||||||
{
|
{
|
||||||
outmsg.WriteSingle(autoRestartTimerRunning ? ServerSettings.AutoRestartTimer : 0.0f);
|
outmsg.WriteSingle(autoRestartTimerRunning ? ServerSettings.AutoRestartTimer : 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (GameMain.NetLobbyScreen.SelectedMode == GameModePreset.MultiPlayerCampaign &&
|
||||||
|
connectedClients.None(c => c.Connection == OwnerConnection || c.HasPermission(ClientPermissions.ManageRound) || c.HasPermission(ClientPermissions.ManageCampaign)))
|
||||||
|
{
|
||||||
|
//if no-one has permissions to manage the campaign, show the setup UI to everyone
|
||||||
|
TrySendCampaignSetupInfo(c);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1943,9 +1957,8 @@ namespace Barotrauma.Networking
|
|||||||
settingsBytes = outmsg.LengthBytes - settingsBytes;
|
settingsBytes = outmsg.LengthBytes - settingsBytes;
|
||||||
|
|
||||||
int campaignBytes = outmsg.LengthBytes;
|
int campaignBytes = outmsg.LengthBytes;
|
||||||
var campaign = GameMain.GameSession?.GameMode as MultiPlayerCampaign;
|
|
||||||
if (outmsg.LengthBytes < MsgConstants.MTU - 500 &&
|
if (outmsg.LengthBytes < MsgConstants.MTU - 500 &&
|
||||||
campaign != null && campaign.Preset == GameMain.NetLobbyScreen.SelectedMode)
|
GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign && campaign.Preset == GameMain.NetLobbyScreen.SelectedMode)
|
||||||
{
|
{
|
||||||
outmsg.WriteBoolean(true);
|
outmsg.WriteBoolean(true);
|
||||||
outmsg.WritePadBits();
|
outmsg.WritePadBits();
|
||||||
@@ -2023,7 +2036,7 @@ namespace Barotrauma.Networking
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WriteChatMessages(in SegmentTableWriter<ServerNetSegment> segmentTable, IWriteMessage outmsg, Client c)
|
private static void WriteChatMessages(in SegmentTableWriter<ServerNetSegment> segmentTable, IWriteMessage outmsg, Client c)
|
||||||
{
|
{
|
||||||
c.ChatMsgQueue.RemoveAll(cMsg => !NetIdUtils.IdMoreRecent(cMsg.NetStateID, c.LastRecvChatMsgID));
|
c.ChatMsgQueue.RemoveAll(cMsg => !NetIdUtils.IdMoreRecent(cMsg.NetStateID, c.LastRecvChatMsgID));
|
||||||
for (int i = 0; i < c.ChatMsgQueue.Count && i < ChatMessage.MaxMessagesPerPacket; i++)
|
for (int i = 0; i < c.ChatMsgQueue.Count && i < ChatMessage.MaxMessagesPerPacket; i++)
|
||||||
@@ -2037,10 +2050,26 @@ namespace Barotrauma.Networking
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool StartGame()
|
public bool TryStartGame()
|
||||||
{
|
{
|
||||||
if (initiatedStartGame || GameStarted) { return false; }
|
if (initiatedStartGame || GameStarted) { return false; }
|
||||||
|
|
||||||
|
GameModePreset selectedMode =
|
||||||
|
Voting.HighestVoted<GameModePreset>(VoteType.Mode, connectedClients) ?? GameMain.NetLobbyScreen.SelectedMode;
|
||||||
|
if (selectedMode == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (selectedMode == GameModePreset.MultiPlayerCampaign && GameMain.GameSession?.GameMode is not MultiPlayerCampaign)
|
||||||
|
{
|
||||||
|
//DebugConsole.ThrowError($"{nameof(TryStartGame)} failed. Cannot start a multiplayer campaign via {nameof(TryStartGame)} - use {nameof(MultiPlayerCampaign.StartNewCampaign)} or {nameof(MultiPlayerCampaign.LoadCampaign)} instead.");
|
||||||
|
if (GameMain.NetLobbyScreen.SelectedMode != GameModePreset.MultiPlayerCampaign)
|
||||||
|
{
|
||||||
|
GameMain.NetLobbyScreen.SelectedModeIdentifier = GameModePreset.MultiPlayerCampaign.Identifier;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Log("Starting a new round...", ServerLog.MessageType.ServerMessage);
|
Log("Starting a new round...", ServerLog.MessageType.ServerMessage);
|
||||||
SubmarineInfo selectedShuttle = GameMain.NetLobbyScreen.SelectedShuttle;
|
SubmarineInfo selectedShuttle = GameMain.NetLobbyScreen.SelectedShuttle;
|
||||||
|
|
||||||
@@ -2060,23 +2089,13 @@ namespace Barotrauma.Networking
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
GameModePreset selectedMode = Voting.HighestVoted<GameModePreset>(VoteType.Mode, connectedClients);
|
|
||||||
if (selectedMode == null) { selectedMode = GameMain.NetLobbyScreen.SelectedMode; }
|
|
||||||
if (selectedMode == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (selectedMode == GameModePreset.MultiPlayerCampaign && !(GameMain.GameSession?.GameMode is CampaignMode))
|
|
||||||
{
|
|
||||||
DebugConsole.ThrowError("StartGame failed. Cannot start a multiplayer campaign via StartGame - use MultiPlayerCampaign.StartNewCampaign or MultiPlayerCampaign.LoadCampaign instead.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
initiatedStartGame = true;
|
initiatedStartGame = true;
|
||||||
startGameCoroutine = CoroutineManager.StartCoroutine(InitiateStartGame(selectedSub, selectedShuttle, selectedMode), "InitiateStartGame");
|
startGameCoroutine = CoroutineManager.StartCoroutine(InitiateStartGame(selectedSub, selectedShuttle, selectedMode), "InitiateStartGame");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private IEnumerable<CoroutineStatus> InitiateStartGame(SubmarineInfo selectedSub, SubmarineInfo selectedShuttle, GameModePreset selectedMode)
|
private IEnumerable<CoroutineStatus> InitiateStartGame(SubmarineInfo selectedSub, SubmarineInfo selectedShuttle, GameModePreset selectedMode)
|
||||||
{
|
{
|
||||||
initiatedStartGame = true;
|
initiatedStartGame = true;
|
||||||
@@ -2168,7 +2187,6 @@ namespace Barotrauma.Networking
|
|||||||
initialSuppliesSpawned = GameMain.GameSession.SubmarineInfo is { InitialSuppliesSpawned: true };
|
initialSuppliesSpawned = GameMain.GameSession.SubmarineInfo is { InitialSuppliesSpawned: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
List<Client> playingClients = new List<Client>(connectedClients);
|
List<Client> playingClients = new List<Client>(connectedClients);
|
||||||
if (ServerSettings.AllowSpectating)
|
if (ServerSettings.AllowSpectating)
|
||||||
{
|
{
|
||||||
@@ -2413,8 +2431,7 @@ namespace Barotrauma.Networking
|
|||||||
mpCampaign.ClearSavedExperiencePoints(teamClients[i]);
|
mpCampaign.ClearSavedExperiencePoints(teamClients[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
spawnedCharacter.OwnerClientAddress = teamClients[i].Connection.Endpoint.Address;
|
spawnedCharacter.SetOwnerClient(teamClients[i]);
|
||||||
spawnedCharacter.OwnerClientName = teamClients[i].Name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = teamClients.Count; i < teamClients.Count + bots.Count; i++)
|
for (int i = teamClients.Count; i < teamClients.Count + bots.Count; i++)
|
||||||
@@ -2479,7 +2496,7 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
yield return CoroutineStatus.Running;
|
yield return CoroutineStatus.Running;
|
||||||
|
|
||||||
Voting?.ResetVotes(GameMain.Server.ConnectedClients, resetKickVotes: false);
|
Voting.ResetVotes(GameMain.Server.ConnectedClients, resetKickVotes: false);
|
||||||
|
|
||||||
GameMain.GameScreen.Select();
|
GameMain.GameScreen.Select();
|
||||||
|
|
||||||
@@ -2507,14 +2524,11 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
private void SendStartMessage(int seed, string levelSeed, GameSession gameSession, Client client, bool includesFinalize)
|
private void SendStartMessage(int seed, string levelSeed, GameSession gameSession, Client client, bool includesFinalize)
|
||||||
{
|
{
|
||||||
MultiPlayerCampaign campaign = GameMain.GameSession?.GameMode as MultiPlayerCampaign;
|
|
||||||
MissionMode missionMode = GameMain.GameSession.GameMode as MissionMode;
|
|
||||||
|
|
||||||
IWriteMessage msg = new WriteOnlyMessage();
|
IWriteMessage msg = new WriteOnlyMessage();
|
||||||
msg.WriteByte((byte)ServerPacketHeader.STARTGAME);
|
msg.WriteByte((byte)ServerPacketHeader.STARTGAME);
|
||||||
msg.WriteInt32(seed);
|
msg.WriteInt32(seed);
|
||||||
msg.WriteIdentifier(gameSession.GameMode.Preset.Identifier);
|
msg.WriteIdentifier(gameSession.GameMode.Preset.Identifier);
|
||||||
bool missionAllowRespawn = missionMode == null || !missionMode.Missions.Any(m => !m.AllowRespawn);
|
bool missionAllowRespawn = GameMain.GameSession.GameMode is not MissionMode missionMode || !missionMode.Missions.Any(m => !m.AllowRespawn);
|
||||||
msg.WriteBoolean(ServerSettings.AllowRespawn && missionAllowRespawn);
|
msg.WriteBoolean(ServerSettings.AllowRespawn && missionAllowRespawn);
|
||||||
msg.WriteBoolean(ServerSettings.AllowDisguises);
|
msg.WriteBoolean(ServerSettings.AllowDisguises);
|
||||||
msg.WriteBoolean(ServerSettings.AllowRewiring);
|
msg.WriteBoolean(ServerSettings.AllowRewiring);
|
||||||
@@ -2530,7 +2544,7 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
ServerSettings.WriteMonsterEnabled(msg);
|
ServerSettings.WriteMonsterEnabled(msg);
|
||||||
|
|
||||||
if (campaign == null)
|
if (GameMain.GameSession?.GameMode is not MultiPlayerCampaign campaign)
|
||||||
{
|
{
|
||||||
msg.WriteString(levelSeed);
|
msg.WriteString(levelSeed);
|
||||||
msg.WriteSingle(ServerSettings.SelectedLevelDifficulty);
|
msg.WriteSingle(ServerSettings.SelectedLevelDifficulty);
|
||||||
@@ -2566,6 +2580,23 @@ namespace Barotrauma.Networking
|
|||||||
serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
|
serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool TrySendCampaignSetupInfo(Client client)
|
||||||
|
{
|
||||||
|
if (!CampaignMode.AllowedToManageCampaign(client, ClientPermissions.ManageRound)) { return false; }
|
||||||
|
|
||||||
|
const int MaxSaves = 255;
|
||||||
|
var saveInfos = SaveUtil.GetSaveFiles(SaveUtil.SaveType.Multiplayer, includeInCompatible: false);
|
||||||
|
IWriteMessage msg = new WriteOnlyMessage();
|
||||||
|
msg.WriteByte((byte)ServerPacketHeader.CAMPAIGN_SETUP_INFO);
|
||||||
|
msg.WriteByte((byte)Math.Min(saveInfos.Count, MaxSaves));
|
||||||
|
for (int i = 0; i < saveInfos.Count && i < MaxSaves; i++)
|
||||||
|
{
|
||||||
|
msg.WriteNetSerializableStruct(saveInfos[i]);
|
||||||
|
}
|
||||||
|
serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private bool IsUsingRespawnShuttle()
|
private bool IsUsingRespawnShuttle()
|
||||||
{
|
{
|
||||||
return ServerSettings.UseRespawnShuttle || (GameStarted && RespawnManager != null && RespawnManager.UsingShuttle);
|
return ServerSettings.UseRespawnShuttle || (GameStarted && RespawnManager != null && RespawnManager.UsingShuttle);
|
||||||
@@ -3521,9 +3552,7 @@ namespace Barotrauma.Networking
|
|||||||
//the client's previous character is no longer a remote player
|
//the client's previous character is no longer a remote player
|
||||||
if (client.Character != null)
|
if (client.Character != null)
|
||||||
{
|
{
|
||||||
client.Character.IsRemotePlayer = false;
|
client.Character.SetOwnerClient(null);
|
||||||
client.Character.OwnerClientAddress = null;
|
|
||||||
client.Character.OwnerClientName = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newCharacter == null)
|
if (newCharacter == null)
|
||||||
@@ -3549,9 +3578,7 @@ namespace Barotrauma.Networking
|
|||||||
newCharacter.Info.Character = newCharacter;
|
newCharacter.Info.Character = newCharacter;
|
||||||
}
|
}
|
||||||
|
|
||||||
newCharacter.OwnerClientAddress = client.Connection.Endpoint.Address;
|
newCharacter.SetOwnerClient(client);
|
||||||
newCharacter.OwnerClientName = client.Name;
|
|
||||||
newCharacter.IsRemotePlayer = true;
|
|
||||||
newCharacter.Enabled = true;
|
newCharacter.Enabled = true;
|
||||||
client.Character = newCharacter;
|
client.Character = newCharacter;
|
||||||
CreateEntityEvent(newCharacter, new Character.ControlEventData(client));
|
CreateEntityEvent(newCharacter, new Character.ControlEventData(client));
|
||||||
|
|||||||
@@ -439,8 +439,7 @@ namespace Barotrauma.Networking
|
|||||||
}
|
}
|
||||||
|
|
||||||
clients[i].Character = character;
|
clients[i].Character = character;
|
||||||
character.OwnerClientAddress = clients[i].Connection.Endpoint.Address;
|
character.SetOwnerClient(clients[i]);
|
||||||
character.OwnerClientName = clients[i].Name;
|
|
||||||
GameServer.Log(
|
GameServer.Log(
|
||||||
$"Respawning {GameServer.ClientLogName(clients[i])} ({clients[i].Connection.Endpoint}) as {characterInfos[i].Job.Name}", ServerLog.MessageType.Spawning);
|
$"Respawning {GameServer.ClientLogName(clients[i])} ({clients[i].Connection.Endpoint}) as {characterInfos[i].Job.Name}", ServerLog.MessageType.Spawning);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,13 +117,13 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public void StopSubmarineVote(bool passed)
|
public void StopSubmarineVote(bool passed)
|
||||||
{
|
{
|
||||||
if (!(ActiveVote is SubmarineVote)) { return; }
|
if (ActiveVote is not SubmarineVote) { return; }
|
||||||
StopActiveVote(passed);
|
StopActiveVote(passed);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StopMoneyTransferVote(bool passed)
|
public void StopMoneyTransferVote(bool passed)
|
||||||
{
|
{
|
||||||
if (!(ActiveVote is TransferVote)) { return; }
|
if (ActiveVote is not TransferVote) { return; }
|
||||||
StopActiveVote(passed);
|
StopActiveVote(passed);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,7 +155,7 @@ namespace Barotrauma
|
|||||||
GameMain.Server.UpdateVoteStatus(checkActiveVote: false);
|
GameMain.Server.UpdateVoteStatus(checkActiveVote: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StartOrEnqueueVote(IVote vote)
|
private static void StartOrEnqueueVote(IVote vote)
|
||||||
{
|
{
|
||||||
if (ActiveVote == null)
|
if (ActiveVote == null)
|
||||||
{
|
{
|
||||||
@@ -198,9 +198,9 @@ namespace Barotrauma
|
|||||||
|
|
||||||
ActiveVote.Timer += deltaTime;
|
ActiveVote.Timer += deltaTime;
|
||||||
|
|
||||||
if (ActiveVote.Timer >= GameMain.NetworkMember.ServerSettings.VoteTimeout)
|
var inGameClients = GameMain.Server.ConnectedClients.Where(c => c.InGame);
|
||||||
|
if (ActiveVote.Timer >= GameMain.NetworkMember.ServerSettings.VoteTimeout || inGameClients.Count() == 1)
|
||||||
{
|
{
|
||||||
var inGameClients = GameMain.Server.ConnectedClients.Where(c => c.InGame);
|
|
||||||
var eligibleClients = inGameClients.Where(c => c != ActiveVote.VoteStarter);
|
var eligibleClients = inGameClients.Where(c => c != ActiveVote.VoteStarter);
|
||||||
|
|
||||||
// Do not take unanswered into account for total
|
// Do not take unanswered into account for total
|
||||||
@@ -216,7 +216,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetVotes(IEnumerable<Client> connectedClients, bool resetKickVotes)
|
public static void ResetVotes(IEnumerable<Client> connectedClients, bool resetKickVotes)
|
||||||
{
|
{
|
||||||
foreach (Client client in connectedClients)
|
foreach (Client client in connectedClients)
|
||||||
{
|
{
|
||||||
@@ -254,7 +254,14 @@ namespace Barotrauma
|
|||||||
string modeIdentifier = inc.ReadString();
|
string modeIdentifier = inc.ReadString();
|
||||||
GameModePreset mode = GameModePreset.List.Find(gm => gm.Identifier == modeIdentifier);
|
GameModePreset mode = GameModePreset.List.Find(gm => gm.Identifier == modeIdentifier);
|
||||||
if (mode == null || !mode.Votable) { break; }
|
if (mode == null || !mode.Votable) { break; }
|
||||||
|
var prevHighestVoted = HighestVoted<GameModePreset>(VoteType.Mode, GameMain.Server.ConnectedClients);
|
||||||
sender.SetVote(voteType, mode);
|
sender.SetVote(voteType, mode);
|
||||||
|
var newHighestVoted = HighestVoted<GameModePreset>(VoteType.Mode, GameMain.Server.ConnectedClients);
|
||||||
|
if (prevHighestVoted != newHighestVoted)
|
||||||
|
{
|
||||||
|
GameMain.NetLobbyScreen.SelectedModeIdentifier = mode.Identifier;
|
||||||
|
GameMain.NetLobbyScreen.LastUpdateID++;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case VoteType.EndRound:
|
case VoteType.EndRound:
|
||||||
if (!sender.HasSpawned) { return; }
|
if (!sender.HasSpawned) { return; }
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ namespace Barotrauma
|
|||||||
get { return GameModes[SelectedModeIndex].Identifier; }
|
get { return GameModes[SelectedModeIndex].Identifier; }
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
if (SelectedModeIdentifier == value) { return; }
|
||||||
for (int i = 0; i < GameModes.Length; i++)
|
for (int i = 0; i < GameModes.Length; i++)
|
||||||
{
|
{
|
||||||
if (GameModes[i].Identifier == value)
|
if (GameModes[i].Identifier == value)
|
||||||
@@ -127,9 +128,11 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
LevelSeed = ToolBox.RandomSeed(8);
|
LevelSeed = ToolBox.RandomSeed(8);
|
||||||
|
|
||||||
subs = SubmarineInfo.SavedSubmarines.Where(s => s.Type == SubmarineType.Player && !s.HasTag(SubmarineTag.HideInMenus)).ToList();
|
subs = SubmarineInfo.SavedSubmarines
|
||||||
|
.Where(s => s.Type == SubmarineType.Player && !s.HasTag(SubmarineTag.HideInMenus))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
if (subs == null || subs.Count() == 0)
|
if (subs == null || subs.Count == 0)
|
||||||
{
|
{
|
||||||
throw new Exception("No submarines are available.");
|
throw new Exception("No submarines are available.");
|
||||||
}
|
}
|
||||||
@@ -156,7 +159,7 @@ namespace Barotrauma
|
|||||||
GameModes = GameModePreset.List.ToArray();
|
GameModes = GameModePreset.List.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<SubmarineInfo> subs;
|
private readonly List<SubmarineInfo> subs;
|
||||||
public IReadOnlyList<SubmarineInfo> GetSubList() => subs;
|
public IReadOnlyList<SubmarineInfo> GetSubList() => subs;
|
||||||
|
|
||||||
public string LevelSeed
|
public string LevelSeed
|
||||||
@@ -192,7 +195,7 @@ namespace Barotrauma
|
|||||||
public override void Select()
|
public override void Select()
|
||||||
{
|
{
|
||||||
base.Select();
|
base.Select();
|
||||||
GameMain.Server.Voting.ResetVotes(GameMain.Server.ConnectedClients, resetKickVotes: false);
|
Voting.ResetVotes(GameMain.Server.ConnectedClients, resetKickVotes: false);
|
||||||
if (SelectedMode != GameModePreset.MultiPlayerCampaign && GameMain.GameSession?.GameMode is CampaignMode && Selected == this)
|
if (SelectedMode != GameModePreset.MultiPlayerCampaign && GameMain.GameSession?.GameMode is CampaignMode && Selected == this)
|
||||||
{
|
{
|
||||||
GameMain.GameSession = null;
|
GameMain.GameSession = null;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<RootNamespace>Barotrauma</RootNamespace>
|
<RootNamespace>Barotrauma</RootNamespace>
|
||||||
<Authors>FakeFish, Undertow Games</Authors>
|
<Authors>FakeFish, Undertow Games</Authors>
|
||||||
<Product>Barotrauma Dedicated Server</Product>
|
<Product>Barotrauma Dedicated Server</Product>
|
||||||
<Version>0.20.15.0</Version>
|
<Version>0.21.1.0</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
<AssemblyName>DedicatedServer</AssemblyName>
|
<AssemblyName>DedicatedServer</AssemblyName>
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
RadiationEnabled="false"
|
RadiationEnabled="false"
|
||||||
StartingBalanceAmount="High"
|
StartingBalanceAmount="High"
|
||||||
StartItemSet="easy"
|
StartItemSet="easy"
|
||||||
|
MaxMissionCount="3"
|
||||||
Difficulty="Easy"/>
|
Difficulty="Easy"/>
|
||||||
<CampaignSettings
|
<CampaignSettings
|
||||||
presetname="Normal"
|
presetname="Normal"
|
||||||
@@ -18,12 +19,14 @@
|
|||||||
RadiationEnabled="false"
|
RadiationEnabled="false"
|
||||||
StartingBalanceAmount="Medium"
|
StartingBalanceAmount="Medium"
|
||||||
StartItemSet="normal"
|
StartItemSet="normal"
|
||||||
|
MaxMissionCount="2"
|
||||||
Difficulty="Medium"/>
|
Difficulty="Medium"/>
|
||||||
<CampaignSettings
|
<CampaignSettings
|
||||||
presetname="Hard"
|
presetname="Hard"
|
||||||
TutorialEnabled="false"
|
TutorialEnabled="false"
|
||||||
RadiationEnabled="true"
|
RadiationEnabled="false"
|
||||||
StartingBalanceAmount="Low"
|
StartingBalanceAmount="Low"
|
||||||
StartItemSet="hard"
|
StartItemSet="hard"
|
||||||
|
MaxMissionCount="1"
|
||||||
Difficulty="Hard"/>
|
Difficulty="Hard"/>
|
||||||
</CampaignSettingPresets>
|
</CampaignSettingPresets>
|
||||||
@@ -351,6 +351,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()
|
public void ReequipUnequipped()
|
||||||
{
|
{
|
||||||
foreach (var item in unequippedItems)
|
foreach (var item in unequippedItems)
|
||||||
|
|||||||
@@ -355,6 +355,10 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
targetingTag = "owner";
|
targetingTag = "owner";
|
||||||
}
|
}
|
||||||
|
else if (targetCharacter.AIController is HumanAIController && !IsOnFriendlyTeam(Character, targetCharacter))
|
||||||
|
{
|
||||||
|
targetingTag = "hostile";
|
||||||
|
}
|
||||||
else if (AIParams.TryGetTarget(targetCharacter, out CharacterParams.TargetParams tP))
|
else if (AIParams.TryGetTarget(targetCharacter, out CharacterParams.TargetParams tP))
|
||||||
{
|
{
|
||||||
targetingTag = tP.Tag;
|
targetingTag = tP.Tag;
|
||||||
@@ -365,7 +369,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
targetingTag = "husk";
|
targetingTag = "husk";
|
||||||
}
|
}
|
||||||
else if (!Character.IsFriendly(targetCharacter))
|
else if (!Character.IsSameSpeciesOrGroup(targetCharacter))
|
||||||
{
|
{
|
||||||
if (enemy.CombatStrength > CombatStrength)
|
if (enemy.CombatStrength > CombatStrength)
|
||||||
{
|
{
|
||||||
@@ -687,12 +691,9 @@ namespace Barotrauma
|
|||||||
return a.Damage >= selectedTargetingParams.Threshold;
|
return a.Damage >= selectedTargetingParams.Threshold;
|
||||||
}
|
}
|
||||||
Character attacker = targetCharacter.LastAttackers.LastOrDefault(IsValid)?.Character;
|
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
|
if (attacker?.AiTarget != null && !Character.IsSameSpeciesOrGroup(attacker) && !targetCharacter.IsSameSpeciesOrGroup(attacker))
|
||||||
//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))
|
|
||||||
{
|
{
|
||||||
// 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);
|
ChangeTargetState(attacker, AIState.Attack, selectedTargetingParams.Priority * 2);
|
||||||
SelectTarget(attacker.AiTarget);
|
SelectTarget(attacker.AiTarget);
|
||||||
State = AIState.Attack;
|
State = AIState.Attack;
|
||||||
|
|||||||
@@ -1514,9 +1514,18 @@ namespace Barotrauma
|
|||||||
startPos.X += MathHelper.Clamp(Character.AnimController.TargetMovement.X, -1.0f, 1.0f);
|
startPos.X += MathHelper.Clamp(Character.AnimController.TargetMovement.X, -1.0f, 1.0f);
|
||||||
|
|
||||||
//do a raycast upwards to find any walls
|
//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()
|
public bool AllowCampaignInteraction()
|
||||||
@@ -1589,7 +1598,27 @@ namespace Barotrauma
|
|||||||
(!requireEquipped || character.HasEquippedItem(i)) &&
|
(!requireEquipped || character.HasEquippedItem(i)) &&
|
||||||
(predicate == null || predicate(i)), recursive, matchingItems);
|
(predicate == null || predicate(i)), recursive, matchingItems);
|
||||||
items = 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)
|
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)
|
public static bool IsFriendly(Character me, Character other, bool onlySameTeam = false)
|
||||||
{
|
{
|
||||||
bool sameTeam = me.TeamID == other.TeamID;
|
bool sameTeam = me.TeamID == other.TeamID;
|
||||||
bool friendlyTeam = IsOnFriendlyTeam(me, other);
|
bool teamGood = sameTeam || !onlySameTeam && IsOnFriendlyTeam(me, other);
|
||||||
bool teamGood = sameTeam || friendlyTeam && !onlySameTeam;
|
|
||||||
if (!teamGood) { return false; }
|
if (!teamGood) { return false; }
|
||||||
bool speciesGood = other.SpeciesName == me.SpeciesName || other.Params.CompareGroup(me.Params.Group);
|
if (!me.IsSameSpeciesOrGroup(other)) { return false; }
|
||||||
if (!speciesGood) { return false; }
|
|
||||||
if (me.TeamID == CharacterTeamType.FriendlyNPC && other.TeamID == CharacterTeamType.Team1 && GameMain.GameSession?.GameMode is CampaignMode campaign)
|
if (me.TeamID == CharacterTeamType.FriendlyNPC && other.TeamID == CharacterTeamType.Team1 && GameMain.GameSession?.GameMode is CampaignMode campaign)
|
||||||
{
|
{
|
||||||
var reputation = campaign.Map?.CurrentLocation?.Reputation;
|
var reputation = campaign.Map?.CurrentLocation?.Reputation;
|
||||||
@@ -2029,30 +2056,14 @@ namespace Barotrauma
|
|||||||
return false;
|
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;
|
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 IsActive(Character other) => other != null && !other.Removed && !other.IsDead && !other.IsUnconscious;
|
||||||
|
|
||||||
public static bool IsTrueForAllCrewMembers(Character character, Func<HumanAIController, bool> predicate)
|
public static bool IsTrueForAllCrewMembers(Character character, Func<HumanAIController, bool> predicate)
|
||||||
|
|||||||
+1
-1
@@ -98,7 +98,7 @@ namespace Barotrauma
|
|||||||
int containedItemCount = 0;
|
int containedItemCount = 0;
|
||||||
foreach (Item it in container.Inventory.AllItems)
|
foreach (Item it in container.Inventory.AllItems)
|
||||||
{
|
{
|
||||||
if (CheckItem(it))
|
if (CheckItem(it) && IsInTargetSlot(it))
|
||||||
{
|
{
|
||||||
containedItemCount++;
|
containedItemCount++;
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-8
@@ -1,5 +1,5 @@
|
|||||||
using Barotrauma.Items.Components;
|
using Barotrauma.Extensions;
|
||||||
using Barotrauma.Extensions;
|
using Barotrauma.Items.Components;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
@@ -18,6 +18,7 @@ namespace Barotrauma
|
|||||||
private AIObjectiveGetItem getDivingGear;
|
private AIObjectiveGetItem getDivingGear;
|
||||||
private AIObjectiveContainItem getOxygen;
|
private AIObjectiveContainItem getOxygen;
|
||||||
private Item targetItem;
|
private Item targetItem;
|
||||||
|
private int? oxygenSourceSlotIndex;
|
||||||
|
|
||||||
public const float MIN_OXYGEN = 10;
|
public const float MIN_OXYGEN = 10;
|
||||||
|
|
||||||
@@ -43,12 +44,15 @@ namespace Barotrauma
|
|||||||
Abandon = true;
|
Abandon = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
targetItem = character.Inventory.FindItemByTag(gearTag, true);
|
|
||||||
|
TrySetTargetItem(character.Inventory.FindItemByTag(gearTag, true));
|
||||||
if (targetItem == null && gearTag == LIGHT_DIVING_GEAR)
|
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, () =>
|
TryAddSubObjective(ref getDivingGear, () =>
|
||||||
{
|
{
|
||||||
@@ -84,7 +88,7 @@ namespace Barotrauma
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
float min = GetMinOxygen(character);
|
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, () =>
|
TryAddSubObjective(ref getOxygen, () =>
|
||||||
{
|
{
|
||||||
@@ -93,7 +97,7 @@ namespace Barotrauma
|
|||||||
if (HumanAIController.HasItem(character, OXYGEN_SOURCE, out _, conditionPercentage: min))
|
if (HumanAIController.HasItem(character, OXYGEN_SOURCE, out _, conditionPercentage: min))
|
||||||
{
|
{
|
||||||
character.Speak(TextManager.Get("dialogswappingoxygentank").Value, null, 0, "swappingoxygentank".ToIdentifier(), 30.0f);
|
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);
|
character.Speak(TextManager.Get("dialoglastoxygentank").Value, null, 0.0f, "dialoglastoxygentank".ToIdentifier(), 30.0f);
|
||||||
}
|
}
|
||||||
@@ -109,7 +113,8 @@ namespace Barotrauma
|
|||||||
AllowToFindDivingGear = false,
|
AllowToFindDivingGear = false,
|
||||||
AllowDangerousPressure = true,
|
AllowDangerousPressure = true,
|
||||||
ConditionLevel = MIN_OXYGEN,
|
ConditionLevel = MIN_OXYGEN,
|
||||||
RemoveExistingWhenNecessary = true
|
RemoveExistingWhenNecessary = true,
|
||||||
|
TargetSlot = oxygenSourceSlotIndex
|
||||||
};
|
};
|
||||||
if (container.HasSubContainers)
|
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()
|
public override void Reset()
|
||||||
{
|
{
|
||||||
base.Reset();
|
base.Reset();
|
||||||
getDivingGear = null;
|
getDivingGear = null;
|
||||||
getOxygen = null;
|
getOxygen = null;
|
||||||
targetItem = null;
|
targetItem = null;
|
||||||
|
oxygenSourceSlotIndex = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float GetMinOxygen(Character character)
|
public static float GetMinOxygen(Character character)
|
||||||
|
|||||||
+1
-1
@@ -71,7 +71,7 @@ namespace Barotrauma
|
|||||||
Priority = 100;
|
Priority = 100;
|
||||||
}
|
}
|
||||||
else if ((objectiveManager.IsCurrentOrder<AIObjectiveGoTo>() || objectiveManager.IsCurrentOrder<AIObjectiveReturn>()) &&
|
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
|
// 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
|
// -> ignore find safety unless we need to find a diving gear
|
||||||
|
|||||||
+1
-1
@@ -413,7 +413,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplyTreatment(Affliction affliction, Item item)
|
private void ApplyTreatment(Affliction affliction, Item item)
|
||||||
{
|
{
|
||||||
item.ApplyTreatment(character, targetCharacter, targetCharacter.CharacterHealth.GetAfflictionLimb(affliction));
|
item.ApplyTreatment(character, targetCharacter, targetCharacter.CharacterHealth.GetAfflictionLimb(affliction));
|
||||||
|
|||||||
@@ -371,7 +371,8 @@ namespace Barotrauma
|
|||||||
private int CalculateCellCount(int minValue, int maxValue)
|
private int CalculateCellCount(int minValue, int maxValue)
|
||||||
{
|
{
|
||||||
if (maxValue == 0) { return 0; }
|
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));
|
return (int)Math.Round(MathHelper.Lerp(minValue, maxValue, t));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,7 +382,8 @@ namespace Barotrauma
|
|||||||
float delay = Config.AgentSpawnDelay;
|
float delay = Config.AgentSpawnDelay;
|
||||||
float min = delay;
|
float min = delay;
|
||||||
float max = delay * 6;
|
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));
|
return MathHelper.Lerp(max, min, MathUtils.InverseLerp(0, 100, t));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace Barotrauma
|
|||||||
public bool IsAiming => wasAiming;
|
public bool IsAiming => wasAiming;
|
||||||
public bool IsAimingMelee => wasAimingMelee;
|
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;
|
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.
|
// 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 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)
|
public void UpdateUseItem(bool allowMovement, Vector2 handWorldPos)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -994,7 +994,7 @@ namespace Barotrauma
|
|||||||
foreach (Limb l in Limbs)
|
foreach (Limb l in Limbs)
|
||||||
{
|
{
|
||||||
if (l.IsSevered) { continue; }
|
if (l.IsSevered) { continue; }
|
||||||
if (!l.DoesFlip) { continue; }
|
if (!l.DoesFlip) { continue; }
|
||||||
if (RagdollParams.IsSpritesheetOrientationHorizontal)
|
if (RagdollParams.IsSpritesheetOrientationHorizontal)
|
||||||
{
|
{
|
||||||
//horizontally aligned limbs need to be flipped 180 degrees
|
//horizontally aligned limbs need to be flipped 180 degrees
|
||||||
@@ -1014,7 +1014,7 @@ namespace Barotrauma
|
|||||||
if (l.IsSevered) { continue; }
|
if (l.IsSevered) { continue; }
|
||||||
|
|
||||||
float rotation = l.body.Rotation;
|
float rotation = l.body.Rotation;
|
||||||
if (l.DoesFlip)
|
if (l.DoesMirror)
|
||||||
{
|
{
|
||||||
if (RagdollParams.IsSpritesheetOrientationHorizontal)
|
if (RagdollParams.IsSpritesheetOrientationHorizontal)
|
||||||
{
|
{
|
||||||
|
|||||||
+55
-21
@@ -150,8 +150,10 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private readonly float movementLerp;
|
private readonly float movementLerp;
|
||||||
|
|
||||||
private float cprAnimTimer;
|
private float cprAnimTimer,cprPump;
|
||||||
private float cprPump;
|
|
||||||
|
private float fallingProneAnimTimer;
|
||||||
|
const float FallingProneAnimDuration = 1.0f;
|
||||||
|
|
||||||
private bool swimming;
|
private bool swimming;
|
||||||
//time until the character can switch from walking to swimming or vice versa
|
//time until the character can switch from walking to swimming or vice versa
|
||||||
@@ -268,7 +270,8 @@ namespace Barotrauma
|
|||||||
if (deathAnimTimer < deathAnimDuration)
|
if (deathAnimTimer < deathAnimDuration)
|
||||||
{
|
{
|
||||||
deathAnimTimer += deltaTime;
|
deathAnimTimer += deltaTime;
|
||||||
UpdateDying(deltaTime);
|
//the force/torque used to move the limbs goes from 1 to 0 during the death anim duration
|
||||||
|
UpdateFallingProne(1.0f - deathAnimTimer / deathAnimDuration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -278,6 +281,11 @@ namespace Barotrauma
|
|||||||
|
|
||||||
if (!character.CanMove)
|
if (!character.CanMove)
|
||||||
{
|
{
|
||||||
|
if (fallingProneAnimTimer < FallingProneAnimDuration)
|
||||||
|
{
|
||||||
|
fallingProneAnimTimer += deltaTime;
|
||||||
|
UpdateFallingProne(1.0f);
|
||||||
|
}
|
||||||
levitatingCollider = false;
|
levitatingCollider = false;
|
||||||
Collider.FarseerBody.FixedRotation = false;
|
Collider.FarseerBody.FixedRotation = false;
|
||||||
if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
|
if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
|
||||||
@@ -291,18 +299,20 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
fallingProneAnimTimer = 0.0f;
|
||||||
|
|
||||||
//re-enable collider
|
//re-enable collider
|
||||||
if (!Collider.Enabled)
|
if (!Collider.Enabled)
|
||||||
{
|
{
|
||||||
var lowestLimb = FindLowestLimb();
|
var lowestLimb = FindLowestLimb();
|
||||||
|
|
||||||
Collider.SetTransform(new Vector2(
|
Collider.SetTransform(new Vector2(
|
||||||
Collider.SimPosition.X,
|
Collider.SimPosition.X,
|
||||||
Math.Max(lowestLimb.SimPosition.Y + (Collider.radius + Collider.height / 2), Collider.SimPosition.Y)),
|
Math.Max(lowestLimb.SimPosition.Y + (Collider.radius + Collider.height / 2), Collider.SimPosition.Y)),
|
||||||
Collider.Rotation);
|
Collider.Rotation);
|
||||||
|
|
||||||
Collider.FarseerBody.ResetDynamics();
|
Collider.FarseerBody.ResetDynamics();
|
||||||
|
Collider.FarseerBody.LinearVelocity = MainLimb.LinearVelocity;
|
||||||
Collider.Enabled = true;
|
Collider.Enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -431,7 +441,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Timing.TotalTime > LockFlippingUntil && TargetDir != dir && !IsStuck)
|
if (Timing.TotalTime > FlipLockTime && TargetDir != dir && !IsStuck)
|
||||||
{
|
{
|
||||||
Flip();
|
Flip();
|
||||||
}
|
}
|
||||||
@@ -1292,10 +1302,9 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateDying(float deltaTime)
|
void UpdateFallingProne(float strength)
|
||||||
{
|
{
|
||||||
//the force/torque used to move the limbs goes from 1 to 0 during the death anim duration
|
if (strength <= 0.0f) { return; }
|
||||||
float strength = 1.0f - deathAnimTimer / deathAnimDuration;
|
|
||||||
|
|
||||||
Limb head = GetLimb(LimbType.Head);
|
Limb head = GetLimb(LimbType.Head);
|
||||||
Limb torso = GetLimb(LimbType.Torso);
|
Limb torso = GetLimb(LimbType.Torso);
|
||||||
@@ -1319,6 +1328,19 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (torso == null) { return; }
|
if (torso == null) { return; }
|
||||||
|
|
||||||
|
//make the torso tip over
|
||||||
|
//otherwise it tends to just drop straight down, pinning the characters legs in a weird pose
|
||||||
|
if (!InWater)
|
||||||
|
{
|
||||||
|
//prefer tipping over in the same direction the torso is rotating
|
||||||
|
//or moving
|
||||||
|
//or lastly, in the direction it's facing if it's not moving/rotating
|
||||||
|
float fallDirection = Math.Sign(torso.body.AngularVelocity - torso.body.LinearVelocity.X - Dir * 0.01f);
|
||||||
|
float torque = MathF.Cos(torso.Rotation) * fallDirection * 5.0f * strength;
|
||||||
|
torso.body.ApplyTorque(torque * torso.body.Mass);
|
||||||
|
}
|
||||||
|
|
||||||
//attempt to make legs stay in a straight line with the torso to prevent the character from doing a split
|
//attempt to make legs stay in a straight line with the torso to prevent the character from doing a split
|
||||||
for (int i = 0; i < 2; i++)
|
for (int i = 0; i < 2; i++)
|
||||||
{
|
{
|
||||||
@@ -1503,12 +1525,12 @@ namespace Barotrauma
|
|||||||
Limb rightHand = GetLimb(LimbType.RightHand);
|
Limb rightHand = GetLimb(LimbType.RightHand);
|
||||||
|
|
||||||
Limb targetLeftHand = target.AnimController.GetLimb(LimbType.LeftForearm);
|
Limb targetLeftHand = target.AnimController.GetLimb(LimbType.LeftForearm);
|
||||||
if (targetLeftHand == null) targetLeftHand = target.AnimController.GetLimb(LimbType.Torso);
|
if (targetLeftHand == null) { targetLeftHand = target.AnimController.GetLimb(LimbType.Torso); }
|
||||||
if (targetLeftHand == null) targetLeftHand = target.AnimController.MainLimb;
|
if (targetLeftHand == null) { targetLeftHand = target.AnimController.MainLimb; }
|
||||||
|
|
||||||
Limb targetRightHand = target.AnimController.GetLimb(LimbType.RightForearm);
|
Limb targetRightHand = target.AnimController.GetLimb(LimbType.RightForearm);
|
||||||
if (targetRightHand == null) targetRightHand = target.AnimController.GetLimb(LimbType.Torso);
|
if (targetRightHand == null) { targetRightHand = target.AnimController.GetLimb(LimbType.Torso); }
|
||||||
if (targetRightHand == null) targetRightHand = target.AnimController.MainLimb;
|
if (targetRightHand == null) { targetRightHand = target.AnimController.MainLimb; }
|
||||||
|
|
||||||
if (!target.AllowInput)
|
if (!target.AllowInput)
|
||||||
{
|
{
|
||||||
@@ -1644,18 +1666,24 @@ namespace Barotrauma
|
|||||||
pullLimb.PullJointEnabled = true;
|
pullLimb.PullJointEnabled = true;
|
||||||
if (targetLimb.type == LimbType.Torso || targetLimb == target.AnimController.MainLimb)
|
if (targetLimb.type == LimbType.Torso || targetLimb == target.AnimController.MainLimb)
|
||||||
{
|
{
|
||||||
Vector2 pullLimbAnchor = targetLimb.SimPosition;
|
|
||||||
pullLimb.PullJointMaxForce = 5000.0f;
|
pullLimb.PullJointMaxForce = 5000.0f;
|
||||||
if (!character.HasAbilityFlag(AbilityFlags.MoveNormallyWhileDragging))
|
if (!character.HasAbilityFlag(AbilityFlags.MoveNormallyWhileDragging))
|
||||||
{
|
{
|
||||||
targetMovement *= MathHelper.Clamp(Mass / target.Mass, 0.5f, 1.0f);
|
targetMovement *= MathHelper.Clamp(Mass / target.Mass, 0.5f, 1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2 shoulderPos = rightShoulder.WorldAnchorA;
|
|
||||||
Vector2 dragDir = inWater ? Vector2.Normalize(targetLimb.SimPosition - shoulderPos) : Vector2.UnitY;
|
|
||||||
if (!MathUtils.IsValid(dragDir)) { dragDir = Vector2.UnitY; }
|
|
||||||
|
|
||||||
targetAnchor = shoulderPos - dragDir * ConvertUnits.ToSimUnits(upperArmLength + forearmLength);
|
Vector2 shoulderPos = rightShoulder.WorldAnchorA;
|
||||||
|
float targetDist = Vector2.Distance(targetLimb.SimPosition, shoulderPos);
|
||||||
|
Vector2 dragDir = (targetLimb.SimPosition - shoulderPos) / targetDist;
|
||||||
|
if (!MathUtils.IsValid(dragDir)) { dragDir = -Vector2.UnitY; }
|
||||||
|
if (!InWater)
|
||||||
|
{
|
||||||
|
//lerp the arm downwards when not swimming
|
||||||
|
dragDir = Vector2.Lerp(dragDir, -Vector2.UnitY, 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector2 pullLimbAnchor = shoulderPos + dragDir * Math.Min(targetDist, (upperArmLength + forearmLength) * 2);
|
||||||
|
targetAnchor = shoulderPos + dragDir * (upperArmLength + forearmLength);
|
||||||
targetForce = 200.0f;
|
targetForce = 200.0f;
|
||||||
if (target.Submarine != character.Submarine)
|
if (target.Submarine != character.Submarine)
|
||||||
{
|
{
|
||||||
@@ -1723,7 +1751,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
if (target.AnimController.Dir > 0 == WorldPosition.X > target.WorldPosition.X)
|
if (target.AnimController.Dir > 0 == WorldPosition.X > target.WorldPosition.X)
|
||||||
{
|
{
|
||||||
target.AnimController.LockFlippingUntil = (float)Timing.TotalTime + 0.5f;
|
target.AnimController.LockFlipping(0.5f);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1822,16 +1850,22 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public override void Flip()
|
public override void Flip()
|
||||||
{
|
{
|
||||||
|
if (Character == null || Character.Removed)
|
||||||
|
{
|
||||||
|
LogAccessedRemovedCharacterError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
base.Flip();
|
base.Flip();
|
||||||
|
|
||||||
WalkPos = -WalkPos;
|
WalkPos = -WalkPos;
|
||||||
|
|
||||||
Limb torso = GetLimb(LimbType.Torso);
|
Limb torso = GetLimb(LimbType.Torso);
|
||||||
|
if (torso == null) { return; }
|
||||||
Vector2 difference;
|
|
||||||
|
|
||||||
Matrix torsoTransform = Matrix.CreateRotationZ(torso.Rotation);
|
Matrix torsoTransform = Matrix.CreateRotationZ(torso.Rotation);
|
||||||
|
|
||||||
|
Vector2 difference;
|
||||||
foreach (Item heldItem in character.HeldItems)
|
foreach (Item heldItem in character.HeldItems)
|
||||||
{
|
{
|
||||||
if (heldItem?.body != null && !heldItem.Removed && heldItem.GetComponent<Holdable>() != null)
|
if (heldItem?.body != null && !heldItem.Removed && heldItem.GetComponent<Holdable>() != null)
|
||||||
|
|||||||
@@ -57,17 +57,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
if (limbs == null)
|
if (limbs == null)
|
||||||
{
|
{
|
||||||
if (!accessRemovedCharacterErrorShown)
|
LogAccessedRemovedCharacterError();
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
return Array.Empty<Limb>();
|
return Array.Empty<Limb>();
|
||||||
}
|
}
|
||||||
return limbs;
|
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
|
public int ColliderIndex
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -873,7 +877,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
foreach (Limb limb in Limbs)
|
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.Dir = Dir;
|
||||||
limb.MouthPos = new Vector2(-limb.MouthPos.X, limb.MouthPos.Y);
|
limb.MouthPos = new Vector2(-limb.MouthPos.X, limb.MouthPos.Y);
|
||||||
limb.MirrorPullJoint();
|
limb.MirrorPullJoint();
|
||||||
@@ -1428,6 +1432,21 @@ namespace Barotrauma
|
|||||||
return true;
|
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 UpdateProjSpecific(float deltaTime, Camera cam);
|
||||||
|
|
||||||
partial void Splash(Limb limb, Hull limbHull);
|
partial void Splash(Limb limb, Hull limbHull);
|
||||||
|
|||||||
@@ -492,37 +492,52 @@ namespace Barotrauma
|
|||||||
DamageParticles(deltaTime, worldPosition);
|
DamageParticles(deltaTime, worldPosition);
|
||||||
|
|
||||||
var attackResult = target?.AddDamage(attacker, worldPosition, this, deltaTime, playSound) ?? new AttackResult();
|
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)
|
if (targetCharacter != null && targetCharacter.IsDead)
|
||||||
{
|
{
|
||||||
effectType = ActionType.OnEating;
|
additionalEffectType = ActionType.OnEating;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (StatusEffect effect in statusEffects)
|
foreach (StatusEffect effect in statusEffects)
|
||||||
{
|
{
|
||||||
effect.sourceBody = sourceBody;
|
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
|
var t = sourceLimb ?? attacker as ISerializableEntity;
|
||||||
effect.Apply(effectType, deltaTime, attacker, sourceLimb ?? attacker as ISerializableEntity, worldPosition);
|
if (additionalEffectType != ActionType.OnEating)
|
||||||
|
{
|
||||||
|
effect.Apply(conditionalEffectType, deltaTime, attacker, t, worldPosition);
|
||||||
|
}
|
||||||
|
effect.Apply(additionalEffectType, deltaTime, attacker, t, worldPosition);
|
||||||
}
|
}
|
||||||
if (effect.HasTargetType(StatusEffect.TargetType.Parent))
|
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 (targetCharacter != null)
|
||||||
{
|
{
|
||||||
if (effect.HasTargetType(StatusEffect.TargetType.Character))
|
|
||||||
{
|
|
||||||
effect.Apply(effectType, deltaTime, targetCharacter, targetCharacter);
|
|
||||||
}
|
|
||||||
if (effect.HasTargetType(StatusEffect.TargetType.Limb))
|
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))
|
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)
|
if (target is Entity targetEntity)
|
||||||
@@ -532,18 +547,30 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
targets.Clear();
|
targets.Clear();
|
||||||
effect.AddNearbyTargets(worldPosition, targets);
|
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))
|
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))
|
if (effect.HasTargetType(StatusEffect.TargetType.Contained))
|
||||||
{
|
{
|
||||||
targets.Clear();
|
targets.Clear();
|
||||||
targets.AddRange(attacker.Inventory.AllItems);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -579,47 +606,52 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
|
|
||||||
var attackResult = targetLimb.character.ApplyAttack(attacker, worldPosition, this, deltaTime, playSound, targetLimb, penetration);
|
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)
|
foreach (StatusEffect effect in statusEffects)
|
||||||
{
|
{
|
||||||
effect.sourceBody = sourceBody;
|
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))
|
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))
|
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))
|
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) ||
|
if (effect.HasTargetType(StatusEffect.TargetType.NearbyItems) ||
|
||||||
effect.HasTargetType(StatusEffect.TargetType.NearbyCharacters))
|
effect.HasTargetType(StatusEffect.TargetType.NearbyCharacters))
|
||||||
{
|
{
|
||||||
targets.Clear();
|
targets.Clear();
|
||||||
effect.AddNearbyTargets(worldPosition, targets);
|
effect.AddNearbyTargets(worldPosition, targets);
|
||||||
effect.Apply(effectType, deltaTime, targetLimb.character, targets);
|
effect.Apply(conditionalEffectType, deltaTime, targetLimb.character, targets);
|
||||||
}
|
effect.Apply(ActionType.OnUse, deltaTime, targetLimb.character, targets);
|
||||||
if (effect.HasTargetType(StatusEffect.TargetType.UseTarget))
|
|
||||||
{
|
|
||||||
effect.Apply(effectType, deltaTime, targetLimb.character, attacker, worldPosition);
|
|
||||||
}
|
}
|
||||||
if (effect.HasTargetType(StatusEffect.TargetType.Contained))
|
if (effect.HasTargetType(StatusEffect.TargetType.Contained))
|
||||||
{
|
{
|
||||||
targets.Clear();
|
targets.Clear();
|
||||||
targets.AddRange(attacker.Inventory.AllItems);
|
targets.AddRange(attacker.Inventory.AllItems);
|
||||||
effect.Apply(effectType, deltaTime, attacker, targets);
|
effect.Apply(conditionalEffectType, deltaTime, attacker, targets);
|
||||||
|
effect.Apply(ActionType.OnUse, deltaTime, attacker, targets);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
public readonly static List<Character> CharacterList = new List<Character>();
|
public readonly static List<Character> CharacterList = new List<Character>();
|
||||||
|
|
||||||
|
public const float MaxHighlightDistance = 150.0f;
|
||||||
|
public const float MaxDragDistance = 200.0f;
|
||||||
|
|
||||||
partial void UpdateLimbLightSource(Limb limb);
|
partial void UpdateLimbLightSource(Limb limb);
|
||||||
|
|
||||||
private bool enabled = true;
|
private bool enabled = true;
|
||||||
@@ -354,9 +357,15 @@ namespace Barotrauma
|
|||||||
public readonly CharacterPrefab Prefab;
|
public readonly CharacterPrefab Prefab;
|
||||||
|
|
||||||
public readonly CharacterParams Params;
|
public readonly CharacterParams Params;
|
||||||
|
|
||||||
public Identifier SpeciesName => Params?.SpeciesName ?? "null".ToIdentifier();
|
public Identifier SpeciesName => Params?.SpeciesName ?? "null".ToIdentifier();
|
||||||
|
|
||||||
public Identifier Group => Params.Group;
|
public Identifier Group => Params.Group;
|
||||||
|
|
||||||
public bool IsHumanoid => Params.Humanoid;
|
public bool IsHumanoid => Params.Humanoid;
|
||||||
|
|
||||||
|
public bool IsMachine => Params.IsMachine;
|
||||||
|
|
||||||
public bool IsHusk => Params.Husk;
|
public bool IsHusk => Params.Husk;
|
||||||
|
|
||||||
public bool IsMale => info?.IsMale ?? false;
|
public bool IsMale => info?.IsMale ?? false;
|
||||||
@@ -805,6 +814,11 @@ namespace Barotrauma
|
|||||||
public float HealthPercentage => CharacterHealth.HealthPercentage;
|
public float HealthPercentage => CharacterHealth.HealthPercentage;
|
||||||
public float MaxVitality => CharacterHealth.MaxVitality;
|
public float MaxVitality => CharacterHealth.MaxVitality;
|
||||||
public float MaxHealth => MaxVitality;
|
public float MaxHealth => MaxVitality;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Was the character in full health at the beginning of the frame?
|
||||||
|
/// </summary>
|
||||||
|
public bool WasFullHealth => CharacterHealth.WasInFullHealth;
|
||||||
public AIState AIState => AIController is EnemyAIController enemyAI ? enemyAI.State : AIState.Idle;
|
public AIState AIState => AIController is EnemyAIController enemyAI ? enemyAI.State : AIState.Idle;
|
||||||
public bool IsLatched => AIController is EnemyAIController enemyAI && enemyAI.LatchOntoAI != null && enemyAI.LatchOntoAI.IsAttached;
|
public bool IsLatched => AIController is EnemyAIController enemyAI && enemyAI.LatchOntoAI != null && enemyAI.LatchOntoAI.IsAttached;
|
||||||
public float EmpVulnerability => Params.Health.EmpVulnerability;
|
public float EmpVulnerability => Params.Health.EmpVulnerability;
|
||||||
@@ -1741,7 +1755,7 @@ namespace Barotrauma
|
|||||||
float maxSpeed = ApplyTemporarySpeedLimits(currentSpeed);
|
float maxSpeed = ApplyTemporarySpeedLimits(currentSpeed);
|
||||||
targetMovement.X = MathHelper.Clamp(targetMovement.X, -maxSpeed, maxSpeed);
|
targetMovement.X = MathHelper.Clamp(targetMovement.X, -maxSpeed, maxSpeed);
|
||||||
targetMovement.Y = MathHelper.Clamp(targetMovement.Y, -maxSpeed, maxSpeed);
|
targetMovement.Y = MathHelper.Clamp(targetMovement.Y, -maxSpeed, maxSpeed);
|
||||||
SpeedMultiplier = greatestPositiveSpeedMultiplier - (1f - greatestNegativeSpeedMultiplier);
|
SpeedMultiplier = Math.Max(0.0f, greatestPositiveSpeedMultiplier - (1f - greatestNegativeSpeedMultiplier));
|
||||||
targetMovement *= SpeedMultiplier;
|
targetMovement *= SpeedMultiplier;
|
||||||
// Reset, status effects will set the value before the next update
|
// Reset, status effects will set the value before the next update
|
||||||
ResetSpeedMultiplier();
|
ResetSpeedMultiplier();
|
||||||
@@ -2201,7 +2215,9 @@ namespace Barotrauma
|
|||||||
|
|
||||||
if (SelectedCharacter != null)
|
if (SelectedCharacter != null)
|
||||||
{
|
{
|
||||||
if (Vector2.DistanceSquared(SelectedCharacter.WorldPosition, WorldPosition) > 90000.0f || !SelectedCharacter.CanBeSelected)
|
if (!SelectedCharacter.CanBeSelected ||
|
||||||
|
(Vector2.DistanceSquared(SelectedCharacter.WorldPosition, WorldPosition) > MaxDragDistance * MaxDragDistance &&
|
||||||
|
SelectedCharacter.GetDistanceToClosestLimb(SimPosition) > ConvertUnits.ToSimUnits(MaxDragDistance)))
|
||||||
{
|
{
|
||||||
DeselectCharacter();
|
DeselectCharacter();
|
||||||
}
|
}
|
||||||
@@ -2494,8 +2510,12 @@ namespace Barotrauma
|
|||||||
|
|
||||||
if (!skipDistanceCheck)
|
if (!skipDistanceCheck)
|
||||||
{
|
{
|
||||||
maxDist = ConvertUnits.ToSimUnits(maxDist);
|
maxDist = Math.Max(ConvertUnits.ToSimUnits(maxDist), c.AnimController.Collider.GetMaxExtent());
|
||||||
if (Vector2.DistanceSquared(SimPosition, c.SimPosition) > maxDist * maxDist) { return false; }
|
if (Vector2.DistanceSquared(SimPosition, c.SimPosition) > maxDist * maxDist &&
|
||||||
|
Vector2.DistanceSquared(SimPosition, c.AnimController.MainLimb.SimPosition) > maxDist * maxDist)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return checkVisibility ? CanSeeCharacter(c) : true;
|
return checkVisibility ? CanSeeCharacter(c) : true;
|
||||||
@@ -3138,56 +3158,57 @@ namespace Barotrauma
|
|||||||
|
|
||||||
UpdateAIChatMessages(deltaTime);
|
UpdateAIChatMessages(deltaTime);
|
||||||
|
|
||||||
//Do ragdoll shenanigans before Stun because it's still technically a stun, innit? Less network updates for us!
|
if (GameMain.NetworkMember?.ServerSettings?.AllowRagdollButton ?? true)
|
||||||
bool allowRagdoll = GameMain.NetworkMember?.ServerSettings?.AllowRagdollButton ?? true;
|
|
||||||
bool tooFastToUnragdoll = AnimController.Collider.LinearVelocity.LengthSquared() > 8.0f * 8.0f;
|
|
||||||
bool wasRagdolled = false;
|
|
||||||
bool selfRagdolled = false;
|
|
||||||
|
|
||||||
if (IsForceRagdolled)
|
|
||||||
{
|
{
|
||||||
IsRagdolled = IsForceRagdolled;
|
bool wasRagdolled = IsRagdolled;
|
||||||
}
|
if (IsForceRagdolled)
|
||||||
else if (this != Controlled)
|
|
||||||
{
|
|
||||||
wasRagdolled = IsRagdolled;
|
|
||||||
IsRagdolled = selfRagdolled = IsKeyDown(InputType.Ragdoll);
|
|
||||||
}
|
|
||||||
//Keep us ragdolled if we were forced or we're too speedy to unragdoll
|
|
||||||
else if (allowRagdoll && (!IsRagdolled || !tooFastToUnragdoll))
|
|
||||||
{
|
|
||||||
if (ragdollingLockTimer > 0.0f)
|
|
||||||
{
|
{
|
||||||
SetInput(InputType.Ragdoll, false, true);
|
IsRagdolled = IsForceRagdolled;
|
||||||
ragdollingLockTimer -= deltaTime;
|
}
|
||||||
|
else if (this != Controlled)
|
||||||
|
{
|
||||||
|
wasRagdolled = IsRagdolled;
|
||||||
|
IsRagdolled = IsKeyDown(InputType.Ragdoll);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
wasRagdolled = IsRagdolled;
|
bool tooFastToUnragdoll = bodyMovingTooFast(AnimController.Collider) || bodyMovingTooFast(AnimController.MainLimb.body);
|
||||||
IsRagdolled = selfRagdolled = IsKeyDown(InputType.Ragdoll); //Handle this here instead of Control because we can stop being ragdolled ourselves
|
bool bodyMovingTooFast(PhysicsBody body)
|
||||||
if (wasRagdolled != IsRagdolled) { ragdollingLockTimer = 0.5f; }
|
{
|
||||||
|
return
|
||||||
|
body.LinearVelocity.LengthSquared() > 8.0f * 8.0f ||
|
||||||
|
//falling down counts as going too fast
|
||||||
|
(!InWater && body.LinearVelocity.Y < -5.0f);
|
||||||
|
}
|
||||||
|
if (ragdollingLockTimer > 0.0f)
|
||||||
|
{
|
||||||
|
ragdollingLockTimer -= deltaTime;
|
||||||
|
}
|
||||||
|
else if (!tooFastToUnragdoll)
|
||||||
|
{
|
||||||
|
IsRagdolled = IsKeyDown(InputType.Ragdoll); //Handle this here instead of Control because we can stop being ragdolled ourselves
|
||||||
|
if (wasRagdolled != IsRagdolled) { ragdollingLockTimer = 0.2f; }
|
||||||
|
}
|
||||||
|
if (IsRagdolled)
|
||||||
|
{
|
||||||
|
SetInput(InputType.Ragdoll, false, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if (!wasRagdolled && IsRagdolled)
|
||||||
|
|
||||||
if (!wasRagdolled && IsRagdolled)
|
|
||||||
{
|
|
||||||
if (selfRagdolled)
|
|
||||||
{
|
{
|
||||||
CheckTalents(AbilityEffectType.OnSelfRagdoll);
|
CheckTalents(AbilityEffectType.OnRagdoll);
|
||||||
}
|
}
|
||||||
// currently does not work when you are stunned, like it should
|
|
||||||
CheckTalents(AbilityEffectType.OnRagdoll);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lowPassMultiplier = MathHelper.Lerp(lowPassMultiplier, 1.0f, 0.1f);
|
lowPassMultiplier = MathHelper.Lerp(lowPassMultiplier, 1.0f, 0.1f);
|
||||||
|
|
||||||
//ragdoll button
|
|
||||||
if (IsRagdolled || !CanMove)
|
if (IsRagdolled || !CanMove)
|
||||||
{
|
{
|
||||||
if (AnimController is HumanoidAnimController humanAnimController)
|
if (AnimController is HumanoidAnimController humanAnimController)
|
||||||
{
|
{
|
||||||
humanAnimController.Crouching = false;
|
humanAnimController.Crouching = false;
|
||||||
}
|
}
|
||||||
|
if (IsRagdolled) { AnimController.IgnorePlatforms = true; }
|
||||||
AnimController.ResetPullJoints();
|
AnimController.ResetPullJoints();
|
||||||
SelectedItem = SelectedSecondaryItem = null;
|
SelectedItem = SelectedSecondaryItem = null;
|
||||||
return;
|
return;
|
||||||
@@ -3365,6 +3386,20 @@ namespace Barotrauma
|
|||||||
return distSqr;
|
return distSqr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public float GetDistanceToClosestLimb(Vector2 simPos)
|
||||||
|
{
|
||||||
|
float closestDist = float.MaxValue;
|
||||||
|
foreach (Limb limb in AnimController.Limbs)
|
||||||
|
{
|
||||||
|
if (limb.IsSevered) { continue; }
|
||||||
|
float dist = Vector2.Distance(simPos, limb.SimPosition);
|
||||||
|
dist -= limb.body.GetMaxExtent();
|
||||||
|
closestDist = Math.Min(closestDist, dist);
|
||||||
|
if (closestDist <= 0.0f) { return 0.0f; }
|
||||||
|
}
|
||||||
|
return closestDist;
|
||||||
|
}
|
||||||
|
|
||||||
private float despawnTimer;
|
private float despawnTimer;
|
||||||
private void UpdateDespawn(float deltaTime, bool ignoreThresholds = false, bool createNetworkEvents = true)
|
private void UpdateDespawn(float deltaTime, bool ignoreThresholds = false, bool createNetworkEvents = true)
|
||||||
{
|
{
|
||||||
@@ -3885,8 +3920,8 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
foreach (Affliction affliction in attackResult.Afflictions)
|
foreach (Affliction affliction in attackResult.Afflictions)
|
||||||
{
|
{
|
||||||
if (affliction.Strength == 0.0f) continue;
|
if (Math.Abs(affliction.Strength) <= 0.1f) { continue;}
|
||||||
sb.Append($" {affliction.Prefab.Name}: {affliction.Strength}");
|
sb.Append($" {affliction.Prefab.Name}: {affliction.Strength.ToString("0.0")}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GameServer.Log(sb.ToString(), ServerLog.MessageType.Attack);
|
GameServer.Log(sb.ToString(), ServerLog.MessageType.Attack);
|
||||||
@@ -4484,7 +4519,10 @@ namespace Barotrauma
|
|||||||
|
|
||||||
#if CLIENT
|
#if CLIENT
|
||||||
//ensure we apply any pending inventory updates to drop any items that need to be dropped when the character despawns
|
//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
|
#endif
|
||||||
|
|
||||||
base.Remove();
|
base.Remove();
|
||||||
@@ -5200,15 +5238,13 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public void RemoveAbilityResistance(TalentResistanceIdentifier identifier) => abilityResistances.Remove(identifier);
|
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);
|
public bool IsFriendly(Character other) => IsFriendly(this, other);
|
||||||
|
|
||||||
/// <summary>
|
public static bool IsFriendly(Character me, Character other) => AIController.IsOnFriendlyTeam(me, other) && IsSameSpeciesOrGroup(me, other);
|
||||||
/// Compares just the species name and the group, ignores teams. There's a more complex version found in HumanAIController.cs
|
|
||||||
/// </summary>
|
public bool IsSameSpeciesOrGroup(Character other) => IsSameSpeciesOrGroup(this, other);
|
||||||
public static bool IsFriendly(Character me, Character other) => other.SpeciesName == me.SpeciesName || other.Params.CompareGroup(me.Params.Group);
|
|
||||||
|
public static bool IsSameSpeciesOrGroup(Character me, Character other) => other.SpeciesName == me.SpeciesName || other.Params.CompareGroup(me.Params.Group);
|
||||||
|
|
||||||
public void StopClimbing()
|
public void StopClimbing()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -778,9 +778,10 @@ namespace Barotrauma
|
|||||||
FacialHairColors = CharacterConfigElement.GetAttributeTupleArray("facialhaircolors", new (Color, float)[] { (Color.WhiteSmoke, 100f) }).ToImmutableArray();
|
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();
|
SkinColors = CharacterConfigElement.GetAttributeTupleArray("skincolors", new (Color, float)[] { (new Color(255, 215, 200, 255), 100f) }).ToImmutableArray();
|
||||||
|
|
||||||
Head.SkinColor = infoElement.GetAttributeColor("skincolor", Color.White);
|
//default to transparent color, it's invalid and will be replaced with a random one in CheckColors
|
||||||
Head.HairColor = infoElement.GetAttributeColor("haircolor", Color.White);
|
Head.SkinColor = infoElement.GetAttributeColor("skincolor", Color.Transparent);
|
||||||
Head.FacialHairColor = infoElement.GetAttributeColor("facialhaircolor", Color.White);
|
Head.HairColor = infoElement.GetAttributeColor("haircolor", Color.Transparent);
|
||||||
|
Head.FacialHairColor = infoElement.GetAttributeColor("facialhaircolor", Color.Transparent);
|
||||||
CheckColors();
|
CheckColors();
|
||||||
|
|
||||||
TryLoadNameAndTitle(npcIdentifier);
|
TryLoadNameAndTitle(npcIdentifier);
|
||||||
|
|||||||
+12
@@ -602,6 +602,18 @@ namespace Barotrauma
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (int i = 0; i < effects.Count; i++)
|
||||||
|
{
|
||||||
|
for (int j = i + 1; j < effects.Count; j++)
|
||||||
|
{
|
||||||
|
var a = effects[i];
|
||||||
|
var b = effects[j];
|
||||||
|
if (a.MinStrength < b.MaxStrength && b.MinStrength < a.MaxStrength)
|
||||||
|
{
|
||||||
|
DebugConsole.AddWarning($"Affliction \"{Identifier}\" contains effects with overlapping strength ranges. Only one effect can be active at a time, meaning one of the effects won't work.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearEffects()
|
public void ClearEffects()
|
||||||
|
|||||||
@@ -230,6 +230,11 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public float StunTimer { get; private set; }
|
public float StunTimer { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Was the character in full health at the beginning of the frame?
|
||||||
|
/// </summary>
|
||||||
|
public bool WasInFullHealth { get; private set; }
|
||||||
|
|
||||||
public Affliction PressureAffliction
|
public Affliction PressureAffliction
|
||||||
{
|
{
|
||||||
get { return pressureAffliction; }
|
get { return pressureAffliction; }
|
||||||
@@ -772,6 +777,8 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public void Update(float deltaTime)
|
public void Update(float deltaTime)
|
||||||
{
|
{
|
||||||
|
WasInFullHealth = vitality >= MaxVitality;
|
||||||
|
|
||||||
UpdateOxygen(deltaTime);
|
UpdateOxygen(deltaTime);
|
||||||
|
|
||||||
StunTimer = Stun > 0 ? StunTimer + deltaTime : 0;
|
StunTimer = Stun > 0 ? StunTimer + deltaTime : 0;
|
||||||
@@ -878,7 +885,11 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private void UpdateOxygen(float deltaTime)
|
private void UpdateOxygen(float deltaTime)
|
||||||
{
|
{
|
||||||
if (!Character.NeedsOxygen) { return; }
|
if (!Character.NeedsOxygen)
|
||||||
|
{
|
||||||
|
oxygenLowAffliction.Strength = 0.0f;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
float oxygenlowResistance = GetResistance(oxygenLowAffliction.Prefab);
|
float oxygenlowResistance = GetResistance(oxygenLowAffliction.Prefab);
|
||||||
float prevOxygen = OxygenAmount;
|
float prevOxygen = OxygenAmount;
|
||||||
@@ -980,6 +991,8 @@ namespace Barotrauma
|
|||||||
UpdateLimbAfflictionOverlays();
|
UpdateLimbAfflictionOverlays();
|
||||||
UpdateSkinTint();
|
UpdateSkinTint();
|
||||||
Character.Kill(type, affliction);
|
Character.Kill(type, affliction);
|
||||||
|
|
||||||
|
WasInFullHealth = false;
|
||||||
#if CLIENT
|
#if CLIENT
|
||||||
DisplayVitalityDelay = 0.0f;
|
DisplayVitalityDelay = 0.0f;
|
||||||
DisplayedVitality = Vitality;
|
DisplayedVitality = Vitality;
|
||||||
@@ -1024,17 +1037,18 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
|
|
||||||
private readonly List<Affliction> allAfflictions = new List<Affliction>();
|
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();
|
allAfflictions.Clear();
|
||||||
if (!mergeSameAfflictions)
|
if (!mergeSameAfflictions)
|
||||||
{
|
{
|
||||||
allAfflictions.AddRange(afflictions.Keys);
|
allAfflictions.AddRange(predicate == null ? afflictions.Keys : afflictions.Keys.Where(predicate));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
foreach (Affliction affliction in afflictions.Keys)
|
foreach (Affliction affliction in afflictions.Keys)
|
||||||
{
|
{
|
||||||
|
if (predicate != null && !predicate(affliction)) { continue; }
|
||||||
var existingAffliction = allAfflictions.Find(a => a.Prefab == affliction.Prefab);
|
var existingAffliction = allAfflictions.Find(a => a.Prefab == affliction.Prefab);
|
||||||
if (existingAffliction == null)
|
if (existingAffliction == null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
partial class Limb : ISerializableEntity, ISpatialEntity
|
partial class Limb : ISerializableEntity, ISpatialEntity
|
||||||
{
|
{
|
||||||
//how long it takes for severed limbs to fade out
|
//how long it takes for severed limbs to fade out
|
||||||
@@ -215,7 +215,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
//the physics body of the limb
|
//the physics body of the limb
|
||||||
public PhysicsBody body;
|
public PhysicsBody body;
|
||||||
|
|
||||||
public Vector2 StepOffset => ConvertUnits.ToSimUnits(Params.StepOffset) * ragdoll.RagdollParams.JointScale;
|
public Vector2 StepOffset => ConvertUnits.ToSimUnits(Params.StepOffset) * ragdoll.RagdollParams.JointScale;
|
||||||
|
|
||||||
public Hull Hull;
|
public Hull Hull;
|
||||||
@@ -249,7 +249,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool isSevered;
|
private bool isSevered;
|
||||||
private float severedFadeOutTimer;
|
private float severedFadeOutTimer;
|
||||||
|
|
||||||
@@ -269,7 +269,7 @@ namespace Barotrauma
|
|||||||
mouthPos = value;
|
mouthPos = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly Attack attack;
|
public readonly Attack attack;
|
||||||
public List<DamageModifier> DamageModifiers { get; private set; } = new List<DamageModifier>();
|
public List<DamageModifier> DamageModifiers { get; private set; } = new List<DamageModifier>();
|
||||||
|
|
||||||
@@ -282,39 +282,73 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (character.AnimController.CurrentAnimationParams is GroundedMovementParams)
|
if (character?.AnimController.CurrentAnimationParams is GroundedMovementParams && IsLeg)
|
||||||
{
|
{
|
||||||
switch (type)
|
// Legs always has to flip when not swimming
|
||||||
{
|
return true;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return Params.Flip;
|
return Params.Flip;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool DoesMirror
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (IsLeg)
|
||||||
|
{
|
||||||
|
// Legs always has to mirror
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return DoesFlip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public float SteerForce => Params.SteerForce;
|
public float SteerForce => Params.SteerForce;
|
||||||
|
|
||||||
public Vector2 DebugTargetPos;
|
public Vector2 DebugTargetPos;
|
||||||
public Vector2 DebugRefPos;
|
public Vector2 DebugRefPos;
|
||||||
|
|
||||||
public bool IsLowerBody =>
|
public bool IsLowerBody
|
||||||
type == LimbType.LeftLeg ||
|
{
|
||||||
type == LimbType.RightLeg ||
|
get
|
||||||
type == LimbType.LeftFoot ||
|
{
|
||||||
type == LimbType.RightFoot ||
|
switch (type)
|
||||||
type == LimbType.Tail ||
|
{
|
||||||
type == LimbType.Legs ||
|
case LimbType.LeftLeg:
|
||||||
type == LimbType.RightThigh ||
|
case LimbType.RightLeg:
|
||||||
type == LimbType.LeftThigh ||
|
case LimbType.LeftFoot:
|
||||||
type == LimbType.Waist;
|
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
|
public bool IsSevered
|
||||||
{
|
{
|
||||||
|
|||||||
+4
-3
@@ -75,13 +75,14 @@ namespace Barotrauma.Abilities
|
|||||||
if (wt == WeaponType.Any || !weapontype.HasFlag(wt)) { continue; }
|
if (wt == WeaponType.Any || !weapontype.HasFlag(wt)) { continue; }
|
||||||
switch (wt)
|
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:
|
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; }
|
if (item?.GetComponent<MeleeWeapon>() != null) { return true; }
|
||||||
break;
|
break;
|
||||||
case WeaponType.Ranged:
|
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; }
|
if (item?.GetComponent<Projectile>() != null) { return true; }
|
||||||
break;
|
break;
|
||||||
case WeaponType.HandheldRanged:
|
case WeaponType.HandheldRanged:
|
||||||
|
|||||||
-1
@@ -28,7 +28,6 @@ namespace Barotrauma.Abilities
|
|||||||
conditionals.Add(new PropertyConditional(attribute));
|
conditionals.Add(new PropertyConditional(attribute));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+18
-13
@@ -37,19 +37,7 @@ namespace Barotrauma.Abilities
|
|||||||
|
|
||||||
if (itemPrefab != null)
|
if (itemPrefab != null)
|
||||||
{
|
{
|
||||||
if (category != MapEntityCategory.None)
|
return MatchesItem(itemPrefab);
|
||||||
{
|
|
||||||
if (!itemPrefab.Category.HasFlag(category)) { return false; }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (identifiers.Any())
|
|
||||||
{
|
|
||||||
if (!identifiers.Any(t => itemPrefab.Identifier == t))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return !tags.Any() || tags.Any(t => itemPrefab.Tags.Any(p => t == p));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -57,5 +45,22 @@ namespace Barotrauma.Abilities
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool MatchesItem(ItemPrefab itemPrefab)
|
||||||
|
{
|
||||||
|
if (category != MapEntityCategory.None)
|
||||||
|
{
|
||||||
|
if (!itemPrefab.Category.HasFlag(category)) { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (identifiers.Any())
|
||||||
|
{
|
||||||
|
if (!identifiers.Any(t => itemPrefab.Identifier == t))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !tags.Any() || tags.Any(t => itemPrefab.Tags.Any(p => t == p));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
@@ -17,6 +17,14 @@ namespace Barotrauma.Abilities
|
|||||||
tags = abilityElement.GetAttributeIdentifierImmutableHashSet("tags", ImmutableHashSet<Identifier>.Empty);
|
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)
|
protected override void VerifyState(bool conditionsMatched, float timeSinceLastUpdate)
|
||||||
{
|
{
|
||||||
if (conditionsMatched)
|
if (conditionsMatched)
|
||||||
|
|||||||
+5
@@ -13,6 +13,11 @@
|
|||||||
value = abilityElement.GetAttributeFloat("value", 0f);
|
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)
|
protected override void VerifyState(bool conditionsMatched, float timeSinceLastUpdate)
|
||||||
{
|
{
|
||||||
if (conditionsMatched != lastState)
|
if (conditionsMatched != lastState)
|
||||||
|
|||||||
+19
-3
@@ -1,19 +1,35 @@
|
|||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
|
using Barotrauma.Extensions;
|
||||||
using Barotrauma.Items.Components;
|
using Barotrauma.Items.Components;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Barotrauma.Abilities
|
namespace Barotrauma.Abilities
|
||||||
{
|
{
|
||||||
internal sealed class CharacterAbilityRemoveRandomIngredient : CharacterAbility
|
internal sealed class CharacterAbilityRemoveRandomIngredient : CharacterAbility
|
||||||
{
|
{
|
||||||
public CharacterAbilityRemoveRandomIngredient(CharacterAbilityGroup characterAbilityGroup, ContentXElement abilityElement) : base(characterAbilityGroup, abilityElement) { }
|
private readonly AbilityConditionItem? condition;
|
||||||
|
|
||||||
|
public CharacterAbilityRemoveRandomIngredient(CharacterAbilityGroup characterAbilityGroup, ContentXElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||||
|
{
|
||||||
|
var conditionElement = abilityElement.GetChildElement(nameof(AbilityConditionItem));
|
||||||
|
if (conditionElement != null)
|
||||||
|
{
|
||||||
|
condition = new AbilityConditionItem(CharacterTalent, conditionElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override void ApplyEffect(AbilityObject abilityObject)
|
protected override void ApplyEffect(AbilityObject abilityObject)
|
||||||
{
|
{
|
||||||
if (abilityObject is not Fabricator.AbilityFabricationItemIngredients { Items.Count: > 0 } ingredients) { return; }
|
if (abilityObject is not Fabricator.AbilityFabricationItemIngredients { Items.Count: > 0 } ingredients) { return; }
|
||||||
|
|
||||||
int randomIndex = Rand.Int(ingredients.Items.Count, Rand.RandSync.Unsynced);
|
List<Item> applicableIngredients = condition == null ?
|
||||||
ingredients.Items.RemoveAt(randomIndex);
|
ingredients.Items.ToList() :
|
||||||
|
ingredients.Items.Where(it => condition.MatchesItem(it.Prefab)).ToList();
|
||||||
|
if (applicableIngredients.None()) { return; }
|
||||||
|
|
||||||
|
ingredients.Items.Remove(applicableIngredients.GetRandom(Rand.RandSync.Unsynced));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
-3
@@ -288,9 +288,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
if (errorCatcher.Errors.Any())
|
if (errorCatcher.Errors.Any())
|
||||||
{
|
{
|
||||||
yield return ContentPackageManager.LoadProgress.Failure(
|
yield return ContentPackageManager.LoadProgress.Failure(errorCatcher.Errors.Select(e => e.Text));
|
||||||
ContentPackageManager.LoadProgress.Error
|
|
||||||
.Reason.ConsoleErrorsThrown);
|
|
||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
yield return ContentPackageManager.LoadProgress.Progress((i + indexOffset) / (float)Files.Length);
|
yield return ContentPackageManager.LoadProgress.Progress((i + indexOffset) / (float)Files.Length);
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ namespace Barotrauma
|
|||||||
public const string RegularPackagesElementName = "regularpackages";
|
public const string RegularPackagesElementName = "regularpackages";
|
||||||
public const string RegularPackagesSubElementName = "package";
|
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 class EnabledPackages
|
||||||
{
|
{
|
||||||
public static CorePackage? Core { get; private set; } = null;
|
public static CorePackage? Core { get; private set; } = null;
|
||||||
@@ -435,22 +437,19 @@ namespace Barotrauma
|
|||||||
public readonly record struct LoadProgress(Result<float, LoadProgress.Error> Result)
|
public readonly record struct LoadProgress(Result<float, LoadProgress.Error> Result)
|
||||||
{
|
{
|
||||||
public readonly record struct Error(
|
public readonly record struct Error(
|
||||||
Error.Reason ErrorReason,
|
Either<ImmutableArray<string>, Exception> ErrorsOrException)
|
||||||
Option<Exception> Exception)
|
|
||||||
{
|
{
|
||||||
public enum Reason { Exception, ConsoleErrorsThrown }
|
public Error(IEnumerable<string> errorMessages) : this(ErrorsOrException: errorMessages.ToImmutableArray()) { }
|
||||||
|
public Error(Exception exception) : this(ErrorsOrException: exception) { }
|
||||||
public Error(Reason reason) : this(reason, Option.None) { }
|
|
||||||
public Error(Exception exception) : this(Reason.Exception, Option.Some(exception)) { }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoadProgress Failure(Exception exception)
|
public static LoadProgress Failure(Exception exception)
|
||||||
=> new LoadProgress(
|
=> new LoadProgress(
|
||||||
Result<float, Error>.Failure(new Error(exception)));
|
Result<float, Error>.Failure(new Error(exception)));
|
||||||
|
|
||||||
public static LoadProgress Failure(Error.Reason reason)
|
public static LoadProgress Failure(IEnumerable<string> errorMessages)
|
||||||
=> new LoadProgress(
|
=> new LoadProgress(
|
||||||
Result<float, Error>.Failure(new Error(reason)));
|
Result<float, Error>.Failure(new Error(errorMessages)));
|
||||||
|
|
||||||
public static LoadProgress Progress(float value)
|
public static LoadProgress Progress(float value)
|
||||||
=> new LoadProgress(
|
=> new LoadProgress(
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ namespace Barotrauma
|
|||||||
public T GetAttributeEnum<T>(string key, in T def) where T : struct, Enum => Element.GetAttributeEnum(key, def);
|
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) 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 (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();
|
public Identifier VariantOf() => Element.VariantOf();
|
||||||
|
|
||||||
|
|||||||
@@ -1312,10 +1312,10 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}, () =>
|
}, () =>
|
||||||
{
|
{
|
||||||
return new[]
|
return new[]
|
||||||
{
|
{
|
||||||
FactionPrefab.Prefabs.Select(f => f.Identifier.Value).ToArray(),
|
FactionPrefab.Prefabs.Select(static f => f.Identifier.Value).ToArray(),
|
||||||
GameMain.GameSession?.Campaign.Factions.Select(f => f.Prefab.Identifier.ToString()).ToArray() ?? Array.Empty<string>()
|
GameMain.GameSession?.Campaign?.Factions.Select(static f => f.Prefab.Identifier.ToString()).ToArray() ?? Array.Empty<string>()
|
||||||
};
|
};
|
||||||
}, true));
|
}, true));
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ namespace Barotrauma
|
|||||||
OnUseRangedWeapon,
|
OnUseRangedWeapon,
|
||||||
OnReduceAffliction,
|
OnReduceAffliction,
|
||||||
OnAddDamageAffliction,
|
OnAddDamageAffliction,
|
||||||
OnSelfRagdoll,
|
|
||||||
OnRagdoll,
|
OnRagdoll,
|
||||||
OnRoundEnd,
|
OnRoundEnd,
|
||||||
OnLootCharacter,
|
OnLootCharacter,
|
||||||
@@ -168,7 +167,7 @@ namespace Barotrauma
|
|||||||
PumpSpeed,
|
PumpSpeed,
|
||||||
PumpMaxFlow,
|
PumpMaxFlow,
|
||||||
ReactorMaxOutput,
|
ReactorMaxOutput,
|
||||||
ReactorFuelEfficiency,
|
ReactorFuelConsumption,
|
||||||
DeconstructorSpeed,
|
DeconstructorSpeed,
|
||||||
FabricationSpeed
|
FabricationSpeed
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -226,7 +226,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
bool requiresRescue = element.GetAttributeBool("requirerescue", false);
|
bool requiresRescue = element.GetAttributeBool("requirerescue", false);
|
||||||
|
|
||||||
Character spawnedCharacter = CreateHuman(humanPrefab, characters, characterItems, submarine, requiresRescue ? CharacterTeamType.FriendlyNPC : CharacterTeamType.None, spawnPos, giveTags: true);
|
Character spawnedCharacter = CreateHuman(humanPrefab, characters, characterItems, submarine, requiresRescue ? CharacterTeamType.FriendlyNPC : CharacterTeamType.None, spawnPos);
|
||||||
|
|
||||||
if (spawnPos is WayPoint wp)
|
if (spawnPos is WayPoint wp)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ namespace Barotrauma
|
|||||||
List<Submarine> connectedSubs = level.BeaconStation.GetConnectedSubs();
|
List<Submarine> connectedSubs = level.BeaconStation.GetConnectedSubs();
|
||||||
foreach (Item item in Item.ItemList)
|
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 ||
|
if (item.GetComponent<PowerTransfer>() != null ||
|
||||||
item.GetComponent<PowerContainer>() != null ||
|
item.GetComponent<PowerContainer>() != null ||
|
||||||
item.GetComponent<Reactor>() != null ||
|
item.GetComponent<Reactor>() != null ||
|
||||||
|
|||||||
@@ -404,7 +404,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
var experienceGainMultiplierIndividual = new AbilityMissionExperienceGainMultiplier(this, 1f);
|
var experienceGainMultiplierIndividual = new AbilityMissionExperienceGainMultiplier(this, 1f);
|
||||||
info?.Character?.CheckTalents(AbilityEffectType.OnGainMissionExperience, experienceGainMultiplierIndividual);
|
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
|
// apply money gains afterwards to prevent them from affecting XP gains
|
||||||
@@ -545,17 +545,14 @@ namespace Barotrauma
|
|||||||
return humanPrefab;
|
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);
|
var characterInfo = humanPrefab.CreateCharacterInfo(Rand.RandSync.ServerAndClient);
|
||||||
characterInfo.TeamID = teamType;
|
characterInfo.TeamID = teamType;
|
||||||
|
|
||||||
if (positionToStayIn == null)
|
positionToStayIn ??=
|
||||||
{
|
|
||||||
positionToStayIn =
|
|
||||||
WayPoint.GetRandom(SpawnType.Human, characterInfo.Job?.Prefab, submarine) ??
|
WayPoint.GetRandom(SpawnType.Human, characterInfo.Job?.Prefab, submarine) ??
|
||||||
WayPoint.GetRandom(SpawnType.Human, null, submarine);
|
WayPoint.GetRandom(SpawnType.Human, null, submarine);
|
||||||
}
|
|
||||||
|
|
||||||
Character spawnedCharacter = Character.Create(characterInfo.SpeciesName, positionToStayIn.WorldPosition, ToolBox.RandomSeed(8), characterInfo, createNetworkEvent: false);
|
Character spawnedCharacter = Character.Create(characterInfo.SpeciesName, positionToStayIn.WorldPosition, ToolBox.RandomSeed(8), characterInfo, createNetworkEvent: false);
|
||||||
spawnedCharacter.HumanPrefab = humanPrefab;
|
spawnedCharacter.HumanPrefab = humanPrefab;
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (Level.Loaded.ExtraWalls.Any(w => w.IsPointInside(position.Position.ToVector2())))
|
if (Level.Loaded.IsPositionInsideWall(position.Position.ToVector2()))
|
||||||
{
|
{
|
||||||
removals.Add(position);
|
removals.Add(position);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -371,7 +371,7 @@ namespace Barotrauma
|
|||||||
if (!SendUserStatistics) { return; }
|
if (!SendUserStatistics) { return; }
|
||||||
if (sentEventIdentifiers.Contains(identifier)) { 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;
|
message = "[MODDED] " + message;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
|
|
||||||
public const int DefaultMaxMissionCount = 2;
|
public const int DefaultMaxMissionCount = 2;
|
||||||
public const int MaxMissionCountLimit = 10;
|
public const int MaxMissionCountLimit = 3;
|
||||||
public const int MinMissionCountLimit = 1;
|
public const int MinMissionCountLimit = 1;
|
||||||
|
|
||||||
public Dictionary<Identifier, SerializableProperty> SerializableProperties { get; private set; }
|
public Dictionary<Identifier, SerializableProperty> SerializableProperties { get; private set; }
|
||||||
|
|||||||
@@ -46,15 +46,15 @@ namespace Barotrauma
|
|||||||
public static void Init()
|
public static void Init()
|
||||||
{
|
{
|
||||||
#if CLIENT
|
#if CLIENT
|
||||||
Tutorial = new GameModePreset("tutorial".ToIdentifier(), typeof(TutorialMode), true);
|
Tutorial = new GameModePreset("tutorial".ToIdentifier(), typeof(TutorialMode), isSinglePlayer: true);
|
||||||
DevSandbox = new GameModePreset("devsandbox".ToIdentifier(), typeof(GameMode), true);
|
DevSandbox = new GameModePreset("devsandbox".ToIdentifier(), typeof(GameMode), isSinglePlayer: true);
|
||||||
SinglePlayerCampaign = new GameModePreset("singleplayercampaign".ToIdentifier(), typeof(SinglePlayerCampaign), true);
|
SinglePlayerCampaign = new GameModePreset("singleplayercampaign".ToIdentifier(), typeof(SinglePlayerCampaign), isSinglePlayer: true);
|
||||||
TestMode = new GameModePreset("testmode".ToIdentifier(), typeof(TestGameMode), true);
|
TestMode = new GameModePreset("testmode".ToIdentifier(), typeof(TestGameMode), isSinglePlayer: true);
|
||||||
#endif
|
#endif
|
||||||
Sandbox = new GameModePreset("sandbox".ToIdentifier(), typeof(GameMode), false);
|
Sandbox = new GameModePreset("sandbox".ToIdentifier(), typeof(GameMode), isSinglePlayer: false);
|
||||||
Mission = new GameModePreset("mission".ToIdentifier(), typeof(CoOpMode), false);
|
Mission = new GameModePreset("mission".ToIdentifier(), typeof(CoOpMode), isSinglePlayer: false);
|
||||||
PvP = new GameModePreset("pvp".ToIdentifier(), typeof(PvPMode), false);
|
PvP = new GameModePreset("pvp".ToIdentifier(), typeof(PvPMode), isSinglePlayer: false);
|
||||||
MultiPlayerCampaign = new GameModePreset("multiplayercampaign".ToIdentifier(), typeof(MultiPlayerCampaign), false, false);
|
MultiPlayerCampaign = new GameModePreset("multiplayercampaign".ToIdentifier(), typeof(MultiPlayerCampaign), isSinglePlayer: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user