Unstable 0.16.2.0
This commit is contained in:
@@ -73,7 +73,6 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public void Draw(SpriteBatch spriteBatch)
|
public void Draw(SpriteBatch spriteBatch)
|
||||||
{
|
{
|
||||||
// TODO: move this into the character editor
|
|
||||||
//var mouthPos = ragdoll.GetMouthPosition();
|
//var mouthPos = ragdoll.GetMouthPosition();
|
||||||
//if (mouthPos != null)
|
//if (mouthPos != null)
|
||||||
//{
|
//{
|
||||||
@@ -173,6 +172,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public float DefaultSpriteDepth { get; private set; }
|
public float DefaultSpriteDepth { get; private set; }
|
||||||
|
|
||||||
|
public WearableSprite HairWithHatSprite { get; set; }
|
||||||
public WearableSprite HuskSprite { get; private set; }
|
public WearableSprite HuskSprite { get; private set; }
|
||||||
public WearableSprite HerpesSprite { get; private set; }
|
public WearableSprite HerpesSprite { get; private set; }
|
||||||
|
|
||||||
@@ -236,8 +236,8 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public string HitSoundTag => Params?.Sound?.Tag;
|
public string HitSoundTag => Params?.Sound?.Tag;
|
||||||
|
|
||||||
private List<WearableSprite> wearableTypeHidingSprites = new List<WearableSprite>();
|
private readonly List<WearableSprite> wearableTypeHidingSprites = new List<WearableSprite>();
|
||||||
private List<WearableType> wearableTypesToHide = new List<WearableType>();
|
private readonly HashSet<WearableType> wearableTypesToHide = new HashSet<WearableType>();
|
||||||
private bool enableHuskSprite;
|
private bool enableHuskSprite;
|
||||||
public bool EnableHuskSprite
|
public bool EnableHuskSprite
|
||||||
{
|
{
|
||||||
@@ -895,7 +895,15 @@ namespace Barotrauma
|
|||||||
foreach (WearableSprite wearable in OtherWearables)
|
foreach (WearableSprite wearable in OtherWearables)
|
||||||
{
|
{
|
||||||
if (wearable.Type == WearableType.Husk) { continue; }
|
if (wearable.Type == WearableType.Husk) { continue; }
|
||||||
if (wearableTypesToHide.Contains(wearable.Type)) { continue; }
|
if (wearableTypesToHide.Contains(wearable.Type))
|
||||||
|
{
|
||||||
|
if (wearable.Type == WearableType.Hair && HairWithHatSprite != null)
|
||||||
|
{
|
||||||
|
DrawWearable(HairWithHatSprite, depthStep, spriteBatch, blankColor, alpha: color.A / 255f, spriteEffect);
|
||||||
|
depthStep += step;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
DrawWearable(wearable, depthStep, spriteBatch, blankColor, alpha: color.A / 255f, spriteEffect);
|
DrawWearable(wearable, depthStep, spriteBatch, blankColor, alpha: color.A / 255f, spriteEffect);
|
||||||
//if there are multiple sprites on this limb, make the successive ones be drawn in front
|
//if there are multiple sprites on this limb, make the successive ones be drawn in front
|
||||||
depthStep += step;
|
depthStep += step;
|
||||||
@@ -1195,6 +1203,9 @@ namespace Barotrauma
|
|||||||
HuskSprite?.Sprite.Remove();
|
HuskSprite?.Sprite.Remove();
|
||||||
HuskSprite = null;
|
HuskSprite = null;
|
||||||
|
|
||||||
|
HairWithHatSprite?.Sprite.Remove();
|
||||||
|
HairWithHatSprite = null;
|
||||||
|
|
||||||
HerpesSprite?.Sprite.Remove();
|
HerpesSprite?.Sprite.Remove();
|
||||||
HerpesSprite = null;
|
HerpesSprite = null;
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public void ClientExecute(string[] args)
|
public void ClientExecute(string[] args)
|
||||||
{
|
{
|
||||||
if (!CheatsEnabled && IsCheat)
|
bool allowCheats = GameMain.NetworkMember == null && (GameMain.GameSession?.GameMode is TestGameMode || Screen.Selected is EditorScreen);
|
||||||
|
if (!allowCheats && !CheatsEnabled && IsCheat)
|
||||||
{
|
{
|
||||||
NewMessage("You need to enable cheats using the command \"enablecheats\" before you can use the command \"" + names[0] + "\".", Color.Red);
|
NewMessage("You need to enable cheats using the command \"enablecheats\" before you can use the command \"" + names[0] + "\".", Color.Red);
|
||||||
#if USE_STEAM
|
#if USE_STEAM
|
||||||
@@ -743,7 +744,7 @@ namespace Barotrauma
|
|||||||
AssignOnExecute("explosion", (string[] args) =>
|
AssignOnExecute("explosion", (string[] args) =>
|
||||||
{
|
{
|
||||||
Vector2 explosionPos = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition);
|
Vector2 explosionPos = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition);
|
||||||
float range = 500, force = 10, damage = 50, structureDamage = 10, itemDamage = 100, empStrength = 0.0f, ballastFloraStrength = 50f;
|
float range = 500, force = 10, damage = 50, structureDamage = 20, itemDamage = 100, empStrength = 0.0f, ballastFloraStrength = 50f;
|
||||||
if (args.Length > 0) float.TryParse(args[0], out range);
|
if (args.Length > 0) float.TryParse(args[0], out range);
|
||||||
if (args.Length > 1) float.TryParse(args[1], out force);
|
if (args.Length > 1) float.TryParse(args[1], out force);
|
||||||
if (args.Length > 2) float.TryParse(args[2], out damage);
|
if (args.Length > 2) float.TryParse(args[2], out damage);
|
||||||
@@ -1894,7 +1895,12 @@ namespace Barotrauma
|
|||||||
ThrowError($"\"{args[0]}\" is not a valid Level.PositionType. Available options are: {string.Join(", ", enums)}");
|
ThrowError($"\"{args[0]}\" is not a valid Level.PositionType. Available options are: {string.Join(", ", enums)}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
debugLines = EventSet.GetDebugStatistics(filter: monsterEvent => monsterEvent.SpawnPosType.HasFlag(spawnType));
|
bool fullLog = false;
|
||||||
|
if (args.Length > 1)
|
||||||
|
{
|
||||||
|
bool.TryParse(args[1], out fullLog);
|
||||||
|
}
|
||||||
|
debugLines = EventSet.GetDebugStatistics(filter: monsterEvent => monsterEvent.SpawnPosType.HasFlag(spawnType), fullLog: fullLog);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -2409,7 +2415,7 @@ namespace Barotrauma
|
|||||||
TextManager.CheckForDuplicates(args[0]);
|
TextManager.CheckForDuplicates(args[0]);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
commands.Add(new Command("writetocsv", "Writes the default language (English) to a .csv file.", (string[] args) =>
|
commands.Add(new Command("writetocsv|xmltocsv", "Writes the default language (English) to a .csv file.", (string[] args) =>
|
||||||
{
|
{
|
||||||
TextManager.WriteToCSV();
|
TextManager.WriteToCSV();
|
||||||
NPCConversation.WriteToCSV();
|
NPCConversation.WriteToCSV();
|
||||||
|
|||||||
@@ -1091,6 +1091,29 @@ namespace Barotrauma
|
|||||||
var maxVersion = new Version(attribute.Value);
|
var maxVersion = new Version(attribute.Value);
|
||||||
if (GameMain.Version > maxVersion) { return false; }
|
if (GameMain.Version > maxVersion) { return false; }
|
||||||
break;
|
break;
|
||||||
|
case "buildconfiguration":
|
||||||
|
switch (attribute.Value.ToString().ToLowerInvariant())
|
||||||
|
{
|
||||||
|
case "debug":
|
||||||
|
#if DEBUG
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case "unstable":
|
||||||
|
#if UNSTABLE
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case "release":
|
||||||
|
#if !DEBUG && !UNSTABLE
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -439,7 +439,7 @@ namespace Barotrauma
|
|||||||
for (int i = 0; i < Content.CountChildren; i++)
|
for (int i = 0; i < Content.CountChildren; i++)
|
||||||
{
|
{
|
||||||
GUIComponent child = Content.GetChild(i);
|
GUIComponent child = Content.GetChild(i);
|
||||||
if (!child.Visible) { continue; }
|
if (child == null || !child.Visible) { continue; }
|
||||||
if (RectTransform != null)
|
if (RectTransform != null)
|
||||||
{
|
{
|
||||||
callback(i, new Point(x, y));
|
callback(i, new Point(x, y));
|
||||||
|
|||||||
@@ -43,36 +43,42 @@ namespace Barotrauma
|
|||||||
private bool needsRefresh, needsBuyingRefresh, needsSellingRefresh, needsItemsToSellRefresh, needsSellingFromSubRefresh, needsItemsToSellFromSubRefresh;
|
private bool needsRefresh, needsBuyingRefresh, needsSellingRefresh, needsItemsToSellRefresh, needsSellingFromSubRefresh, needsItemsToSellFromSubRefresh;
|
||||||
|
|
||||||
private Point resolutionWhenCreated;
|
private Point resolutionWhenCreated;
|
||||||
private bool hadPermissions;
|
|
||||||
|
|
||||||
private Dictionary<ItemPrefab, int> OwnedItems { get; } = new Dictionary<ItemPrefab, int>();
|
private Dictionary<ItemPrefab, int> OwnedItems { get; } = new Dictionary<ItemPrefab, int>();
|
||||||
|
|
||||||
private CargoManager CargoManager => campaignUI.Campaign.CargoManager;
|
private CargoManager CargoManager => campaignUI.Campaign.CargoManager;
|
||||||
private Location CurrentLocation => campaignUI.Campaign.Map?.CurrentLocation;
|
private Location CurrentLocation => campaignUI.Campaign.Map?.CurrentLocation;
|
||||||
private int PlayerMoney => campaignUI.Campaign.Money;
|
private int PlayerMoney => campaignUI.Campaign.Money;
|
||||||
private bool HasPermissions => campaignUI.Campaign.AllowedToManageCampaign();
|
|
||||||
private bool IsBuying => activeTab switch
|
private bool IsBuying => activeTab switch
|
||||||
{
|
{
|
||||||
StoreTab.Buy => true,
|
StoreTab.Buy => true,
|
||||||
StoreTab.Sell => false,
|
StoreTab.Sell => false,
|
||||||
StoreTab.SellFromSub => false,
|
StoreTab.SellSub => false,
|
||||||
_ => throw new NotImplementedException()
|
_ => throw new NotImplementedException()
|
||||||
};
|
};
|
||||||
private GUIListBox ActiveShoppingCrateList => activeTab switch
|
private GUIListBox ActiveShoppingCrateList => activeTab switch
|
||||||
{
|
{
|
||||||
StoreTab.Buy => shoppingCrateBuyList,
|
StoreTab.Buy => shoppingCrateBuyList,
|
||||||
StoreTab.Sell => shoppingCrateSellList,
|
StoreTab.Sell => shoppingCrateSellList,
|
||||||
StoreTab.SellFromSub => shoppingCrateSellFromSubList,
|
StoreTab.SellSub => shoppingCrateSellFromSubList,
|
||||||
_ => throw new NotImplementedException()
|
_ => throw new NotImplementedException()
|
||||||
};
|
};
|
||||||
|
|
||||||
private bool IsTabUnavailable(StoreTab tab) => !tabLists.ContainsKey(tab);
|
|
||||||
|
|
||||||
public enum StoreTab
|
public enum StoreTab
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Buy items from the store
|
||||||
|
/// </summary>
|
||||||
Buy,
|
Buy,
|
||||||
|
/// <summary>
|
||||||
|
/// Sell items from the character inventory
|
||||||
|
/// </summary>
|
||||||
Sell,
|
Sell,
|
||||||
SellFromSub
|
/// <summary>
|
||||||
|
/// Sell items from the sub
|
||||||
|
/// </summary>
|
||||||
|
SellSub
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum SortingMethod
|
private enum SortingMethod
|
||||||
@@ -84,11 +90,117 @@ namespace Barotrauma
|
|||||||
CategoryAsc
|
CategoryAsc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Permissions
|
||||||
|
|
||||||
|
private bool hadPermissions, hadBuyPermissions, hadSellInventoryPermissions, hadSellSubPermissions;
|
||||||
|
|
||||||
|
private bool HasPermissions
|
||||||
|
{
|
||||||
|
get => GetPermissions();
|
||||||
|
set => hadPermissions = value;
|
||||||
|
}
|
||||||
|
private bool HasBuyPermissions
|
||||||
|
{
|
||||||
|
get => HasPermissions || GetPermissions(StoreTab.Buy);
|
||||||
|
set => hadBuyPermissions = value;
|
||||||
|
}
|
||||||
|
private bool HasSellInventoryPermissions
|
||||||
|
{
|
||||||
|
get => HasPermissions || GetPermissions(StoreTab.Sell);
|
||||||
|
set => hadSellInventoryPermissions = value;
|
||||||
|
}
|
||||||
|
private bool HasSellSubPermissions
|
||||||
|
{
|
||||||
|
get => HasPermissions || GetPermissions(StoreTab.SellSub);
|
||||||
|
set => hadSellSubPermissions = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool GetPermissions(StoreTab? tab = null)
|
||||||
|
{
|
||||||
|
if (!tab.HasValue)
|
||||||
|
{
|
||||||
|
return campaignUI.Campaign.AllowedToManageCampaign() || campaignUI.Campaign.AllowedToManageCampaign(Networking.ClientPermissions.CampaignStore);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return tab.Value switch
|
||||||
|
{
|
||||||
|
StoreTab.Buy => campaignUI.Campaign.AllowedToManageCampaign(Networking.ClientPermissions.BuyItems),
|
||||||
|
StoreTab.Sell => campaignUI.Campaign.AllowedToManageCampaign(Networking.ClientPermissions.SellInventoryItems),
|
||||||
|
StoreTab.SellSub => campaignUI.Campaign.AllowedToManageCampaign(Networking.ClientPermissions.SellSubItems),
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdatePermissions(StoreTab? tab = null)
|
||||||
|
{
|
||||||
|
HasPermissions = GetPermissions();
|
||||||
|
if (!tab.HasValue)
|
||||||
|
{
|
||||||
|
HasBuyPermissions = GetPermissions(StoreTab.Buy);
|
||||||
|
HasSellInventoryPermissions = GetPermissions(StoreTab.Sell);
|
||||||
|
HasSellSubPermissions = GetPermissions(StoreTab.SellSub);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (tab.Value)
|
||||||
|
{
|
||||||
|
case StoreTab.Buy:
|
||||||
|
HasBuyPermissions = GetPermissions(tab.Value);
|
||||||
|
break;
|
||||||
|
case StoreTab.Sell:
|
||||||
|
HasSellInventoryPermissions = GetPermissions(tab.Value);
|
||||||
|
break;
|
||||||
|
case StoreTab.SellSub:
|
||||||
|
HasSellSubPermissions = GetPermissions(tab.Value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool HasTabPermissions(StoreTab tab)
|
||||||
|
{
|
||||||
|
return tab switch
|
||||||
|
{
|
||||||
|
StoreTab.Buy => HasBuyPermissions,
|
||||||
|
StoreTab.Sell => HasSellInventoryPermissions,
|
||||||
|
StoreTab.SellSub => HasSellSubPermissions,
|
||||||
|
_ => false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool HasActiveTabPermissions()
|
||||||
|
{
|
||||||
|
return HasTabPermissions(activeTab);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool HavePermissionsChanged(StoreTab? tab = null)
|
||||||
|
{
|
||||||
|
if (!tab.HasValue)
|
||||||
|
{
|
||||||
|
return hadPermissions != HasPermissions;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bool hadTabPermissions = tab.Value switch
|
||||||
|
{
|
||||||
|
StoreTab.Buy => hadBuyPermissions,
|
||||||
|
StoreTab.Sell => hadSellInventoryPermissions,
|
||||||
|
StoreTab.SellSub => hadSellSubPermissions,
|
||||||
|
_ => false
|
||||||
|
};
|
||||||
|
return hadTabPermissions != HasTabPermissions(tab.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
public Store(CampaignUI campaignUI, GUIComponent parentComponent)
|
public Store(CampaignUI campaignUI, GUIComponent parentComponent)
|
||||||
{
|
{
|
||||||
this.campaignUI = campaignUI;
|
this.campaignUI = campaignUI;
|
||||||
this.parentComponent = parentComponent;
|
this.parentComponent = parentComponent;
|
||||||
hadPermissions = HasPermissions;
|
UpdatePermissions();
|
||||||
CreateUI();
|
CreateUI();
|
||||||
campaignUI.Campaign.Map.OnLocationChanged += UpdateLocation;
|
campaignUI.Campaign.Map.OnLocationChanged += UpdateLocation;
|
||||||
if (CurrentLocation?.Reputation != null)
|
if (CurrentLocation?.Reputation != null)
|
||||||
@@ -109,7 +221,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public void Refresh(bool updateOwned = true)
|
public void Refresh(bool updateOwned = true)
|
||||||
{
|
{
|
||||||
hadPermissions = HasPermissions;
|
UpdatePermissions();
|
||||||
if (updateOwned) { UpdateOwnedItems(); }
|
if (updateOwned) { UpdateOwnedItems(); }
|
||||||
RefreshBuying(updateOwned: false);
|
RefreshBuying(updateOwned: false);
|
||||||
RefreshSelling(updateOwned: false);
|
RefreshSelling(updateOwned: false);
|
||||||
@@ -122,7 +234,7 @@ namespace Barotrauma
|
|||||||
if (updateOwned) { UpdateOwnedItems(); }
|
if (updateOwned) { UpdateOwnedItems(); }
|
||||||
RefreshShoppingCrateBuyList();
|
RefreshShoppingCrateBuyList();
|
||||||
RefreshStoreBuyList();
|
RefreshStoreBuyList();
|
||||||
var hasPermissions = HasPermissions;
|
bool hasPermissions = HasTabPermissions(StoreTab.Buy);
|
||||||
storeBuyList.Enabled = hasPermissions;
|
storeBuyList.Enabled = hasPermissions;
|
||||||
shoppingCrateBuyList.Enabled = hasPermissions;
|
shoppingCrateBuyList.Enabled = hasPermissions;
|
||||||
needsBuyingRefresh = false;
|
needsBuyingRefresh = false;
|
||||||
@@ -133,7 +245,7 @@ namespace Barotrauma
|
|||||||
if (updateOwned) { UpdateOwnedItems(); }
|
if (updateOwned) { UpdateOwnedItems(); }
|
||||||
RefreshShoppingCrateSellList();
|
RefreshShoppingCrateSellList();
|
||||||
RefreshStoreSellList();
|
RefreshStoreSellList();
|
||||||
var hasPermissions = HasPermissions;
|
bool hasPermissions = HasTabPermissions(StoreTab.Sell);
|
||||||
storeSellList.Enabled = hasPermissions;
|
storeSellList.Enabled = hasPermissions;
|
||||||
shoppingCrateSellList.Enabled = hasPermissions;
|
shoppingCrateSellList.Enabled = hasPermissions;
|
||||||
needsSellingRefresh = false;
|
needsSellingRefresh = false;
|
||||||
@@ -141,13 +253,11 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private void RefreshSellingFromSub(bool updateOwned = true, bool updateItemsToSellFromSub = true)
|
private void RefreshSellingFromSub(bool updateOwned = true, bool updateItemsToSellFromSub = true)
|
||||||
{
|
{
|
||||||
if (IsTabUnavailable(StoreTab.SellFromSub)) { return; }
|
|
||||||
if (updateOwned) { UpdateOwnedItems(); }
|
if (updateOwned) { UpdateOwnedItems(); }
|
||||||
if (updateItemsToSellFromSub) RefreshItemsToSellFromSub();
|
if (updateItemsToSellFromSub) RefreshItemsToSellFromSub();
|
||||||
RefreshShoppingCrateSellFromSubList();
|
RefreshShoppingCrateSellFromSubList();
|
||||||
RefreshStoreSellFromSubList();
|
RefreshStoreSellFromSubList();
|
||||||
// TODO: Separate permissions from regular campaign permissions
|
bool hasPermissions = HasTabPermissions(StoreTab.SellSub);
|
||||||
var hasPermissions = HasPermissions;
|
|
||||||
storeSellFromSubList.Enabled = hasPermissions;
|
storeSellFromSubList.Enabled = hasPermissions;
|
||||||
shoppingCrateSellFromSubList.Enabled = hasPermissions;
|
shoppingCrateSellFromSubList.Enabled = hasPermissions;
|
||||||
needsSellingFromSubRefresh = false;
|
needsSellingFromSubRefresh = false;
|
||||||
@@ -261,7 +371,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
StoreTab.Buy => CurrentLocation.StoreCurrentBalance + buyTotal,
|
StoreTab.Buy => CurrentLocation.StoreCurrentBalance + buyTotal,
|
||||||
StoreTab.Sell => CurrentLocation.StoreCurrentBalance - sellTotal,
|
StoreTab.Sell => CurrentLocation.StoreCurrentBalance - sellTotal,
|
||||||
StoreTab.SellFromSub => CurrentLocation.StoreCurrentBalance - sellFromSubTotal,
|
StoreTab.SellSub => CurrentLocation.StoreCurrentBalance - sellFromSubTotal,
|
||||||
_ => throw new NotImplementedException(),
|
_ => throw new NotImplementedException(),
|
||||||
};
|
};
|
||||||
if (balanceAfterTransaction != CurrentLocation.StoreCurrentBalance)
|
if (balanceAfterTransaction != CurrentLocation.StoreCurrentBalance)
|
||||||
@@ -325,10 +435,9 @@ namespace Barotrauma
|
|||||||
tabSortingMethods.Clear();
|
tabSortingMethods.Clear();
|
||||||
foreach (StoreTab tab in tabs)
|
foreach (StoreTab tab in tabs)
|
||||||
{
|
{
|
||||||
if (tab == StoreTab.SellFromSub && GameMain.IsMultiplayer) { continue; }
|
|
||||||
string text = tab switch
|
string text = tab switch
|
||||||
{
|
{
|
||||||
StoreTab.SellFromSub => TextManager.Get("submarine"),
|
StoreTab.SellSub => TextManager.Get("submarine"),
|
||||||
_ => TextManager.Get("campaignstoretab." + tab)
|
_ => TextManager.Get("campaignstoretab." + tab)
|
||||||
};
|
};
|
||||||
var tabButton = new GUIButton(new RectTransform(new Vector2(1.0f / (tabs.Length + 1), 1.0f), modeButtonContainer.RectTransform),
|
var tabButton = new GUIButton(new RectTransform(new Vector2(1.0f / (tabs.Length + 1), 1.0f), modeButtonContainer.RectTransform),
|
||||||
@@ -456,16 +565,13 @@ namespace Barotrauma
|
|||||||
storeRequestedGoodGroup = CreateDealsGroup(storeSellList);
|
storeRequestedGoodGroup = CreateDealsGroup(storeSellList);
|
||||||
tabLists.Add(StoreTab.Sell, storeSellList);
|
tabLists.Add(StoreTab.Sell, storeSellList);
|
||||||
|
|
||||||
if (GameMain.IsSingleplayer)
|
storeSellFromSubList = new GUIListBox(new RectTransform(Vector2.One, storeItemListContainer.RectTransform))
|
||||||
{
|
{
|
||||||
storeSellFromSubList = new GUIListBox(new RectTransform(Vector2.One, storeItemListContainer.RectTransform))
|
AutoHideScrollBar = false,
|
||||||
{
|
Visible = false
|
||||||
AutoHideScrollBar = false,
|
};
|
||||||
Visible = false
|
storeRequestedSubGoodGroup = CreateDealsGroup(storeSellFromSubList);
|
||||||
};
|
tabLists.Add(StoreTab.SellSub, storeSellFromSubList);
|
||||||
storeRequestedSubGoodGroup = CreateDealsGroup(storeSellFromSubList);
|
|
||||||
tabLists.Add(StoreTab.SellFromSub, storeSellFromSubList);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shopping Crate ------------------------------------------------------------------------------------------------------------------------------------------
|
// Shopping Crate ------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -526,10 +632,7 @@ namespace Barotrauma
|
|||||||
var shoppingCrateListContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.8f), shoppingCrateInventoryContainer.RectTransform), style: null);
|
var shoppingCrateListContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.8f), shoppingCrateInventoryContainer.RectTransform), style: null);
|
||||||
shoppingCrateBuyList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false };
|
shoppingCrateBuyList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false };
|
||||||
shoppingCrateSellList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false };
|
shoppingCrateSellList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false };
|
||||||
if (GameMain.IsSingleplayer)
|
shoppingCrateSellFromSubList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false };
|
||||||
{
|
|
||||||
shoppingCrateSellFromSubList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false };
|
|
||||||
}
|
|
||||||
|
|
||||||
var relevantBalanceContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), shoppingCrateInventoryContainer.RectTransform), isHorizontal: true)
|
var relevantBalanceContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), shoppingCrateInventoryContainer.RectTransform), isHorizontal: true)
|
||||||
{
|
{
|
||||||
@@ -569,16 +672,16 @@ namespace Barotrauma
|
|||||||
clearAllButton = new GUIButton(new RectTransform(new Vector2(0.35f, 1.0f), buttonContainer.RectTransform), TextManager.Get("campaignstore.clearall"))
|
clearAllButton = new GUIButton(new RectTransform(new Vector2(0.35f, 1.0f), buttonContainer.RectTransform), TextManager.Get("campaignstore.clearall"))
|
||||||
{
|
{
|
||||||
ClickSound = GUISoundType.DecreaseQuantity,
|
ClickSound = GUISoundType.DecreaseQuantity,
|
||||||
Enabled = HasPermissions,
|
Enabled = HasActiveTabPermissions(),
|
||||||
ForceUpperCase = true,
|
ForceUpperCase = true,
|
||||||
OnClicked = (button, userData) =>
|
OnClicked = (button, userData) =>
|
||||||
{
|
{
|
||||||
if (!HasPermissions) { return false; }
|
if (!HasActiveTabPermissions()) { return false; }
|
||||||
var itemsToRemove = activeTab switch
|
var itemsToRemove = activeTab switch
|
||||||
{
|
{
|
||||||
StoreTab.Buy => new List<PurchasedItem>(CargoManager.ItemsInBuyCrate),
|
StoreTab.Buy => new List<PurchasedItem>(CargoManager.ItemsInBuyCrate),
|
||||||
StoreTab.Sell => new List<PurchasedItem>(CargoManager.ItemsInSellCrate),
|
StoreTab.Sell => new List<PurchasedItem>(CargoManager.ItemsInSellCrate),
|
||||||
StoreTab.SellFromSub => new List<PurchasedItem>(CargoManager.ItemsInSellFromSubCrate),
|
StoreTab.SellSub => new List<PurchasedItem>(CargoManager.ItemsInSellFromSubCrate),
|
||||||
_ => throw new NotImplementedException(),
|
_ => throw new NotImplementedException(),
|
||||||
};
|
};
|
||||||
itemsToRemove.ForEach(i => ClearFromShoppingCrate(i));
|
itemsToRemove.ForEach(i => ClearFromShoppingCrate(i));
|
||||||
@@ -637,7 +740,6 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private void ChangeStoreTab(StoreTab tab)
|
private void ChangeStoreTab(StoreTab tab)
|
||||||
{
|
{
|
||||||
if (IsTabUnavailable(tab)) { return; }
|
|
||||||
activeTab = tab;
|
activeTab = tab;
|
||||||
foreach (GUIButton tabButton in storeTabButtons)
|
foreach (GUIButton tabButton in storeTabButtons)
|
||||||
{
|
{
|
||||||
@@ -680,7 +782,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
shoppingCrateSellList.Visible = true;
|
shoppingCrateSellList.Visible = true;
|
||||||
break;
|
break;
|
||||||
case StoreTab.SellFromSub:
|
case StoreTab.SellSub:
|
||||||
storeBuyList.Visible = false;
|
storeBuyList.Visible = false;
|
||||||
storeSellList.Visible = false;
|
storeSellList.Visible = false;
|
||||||
if (storeSellFromSubList != null)
|
if (storeSellFromSubList != null)
|
||||||
@@ -699,7 +801,6 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private void FilterStoreItems(MapEntityCategory? category, string filter)
|
private void FilterStoreItems(MapEntityCategory? category, string filter)
|
||||||
{
|
{
|
||||||
if (IsTabUnavailable(activeTab)) { return; }
|
|
||||||
selectedItemCategory = category;
|
selectedItemCategory = category;
|
||||||
var list = tabLists[activeTab];
|
var list = tabLists[activeTab];
|
||||||
filter = filter?.ToLower();
|
filter = filter?.ToLower();
|
||||||
@@ -733,7 +834,7 @@ namespace Barotrauma
|
|||||||
float prevBuyListScroll = storeBuyList.BarScroll;
|
float prevBuyListScroll = storeBuyList.BarScroll;
|
||||||
float prevShoppingCrateScroll = shoppingCrateBuyList.BarScroll;
|
float prevShoppingCrateScroll = shoppingCrateBuyList.BarScroll;
|
||||||
|
|
||||||
bool hasPermissions = HasPermissions;
|
bool hasPermissions = HasBuyPermissions;
|
||||||
HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>();
|
HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>();
|
||||||
|
|
||||||
int dailySpecialCount = CurrentLocation?.DailySpecials.Count() ?? 3;
|
int dailySpecialCount = CurrentLocation?.DailySpecials.Count() ?? 3;
|
||||||
@@ -816,7 +917,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
float prevSellListScroll = storeSellList.BarScroll;
|
float prevSellListScroll = storeSellList.BarScroll;
|
||||||
float prevShoppingCrateScroll = shoppingCrateSellList.BarScroll;
|
float prevShoppingCrateScroll = shoppingCrateSellList.BarScroll;
|
||||||
bool hasPermissions = HasPermissions;
|
bool hasPermissions = HasTabPermissions(StoreTab.Sell);
|
||||||
HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>();
|
HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>();
|
||||||
|
|
||||||
if ((storeRequestedGoodGroup != null) != CurrentLocation.RequestedGoods.Any())
|
if ((storeRequestedGoodGroup != null) != CurrentLocation.RequestedGoods.Any())
|
||||||
@@ -894,7 +995,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
float prevSellListScroll = storeSellFromSubList.BarScroll;
|
float prevSellListScroll = storeSellFromSubList.BarScroll;
|
||||||
float prevShoppingCrateScroll = shoppingCrateSellFromSubList.BarScroll;
|
float prevShoppingCrateScroll = shoppingCrateSellFromSubList.BarScroll;
|
||||||
bool hasPermissions = HasPermissions;
|
bool hasPermissions = HasSellSubPermissions;
|
||||||
HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>();
|
HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>();
|
||||||
|
|
||||||
if ((storeRequestedSubGoodGroup != null) != CurrentLocation.RequestedGoods.Any())
|
if ((storeRequestedSubGoodGroup != null) != CurrentLocation.RequestedGoods.Any())
|
||||||
@@ -938,12 +1039,12 @@ namespace Barotrauma
|
|||||||
if (itemFrame == null)
|
if (itemFrame == null)
|
||||||
{
|
{
|
||||||
var parentComponent = isRequestedGood ? storeRequestedSubGoodGroup : storeSellFromSubList as GUIComponent;
|
var parentComponent = isRequestedGood ? storeRequestedSubGoodGroup : storeSellFromSubList as GUIComponent;
|
||||||
itemFrame = CreateItemFrame(new PurchasedItem(itemPrefab, itemQuantity), parentComponent, StoreTab.SellFromSub, forceDisable: !hasPermissions);
|
itemFrame = CreateItemFrame(new PurchasedItem(itemPrefab, itemQuantity), parentComponent, StoreTab.SellSub, forceDisable: !hasPermissions);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
(itemFrame.UserData as PurchasedItem).Quantity = itemQuantity;
|
(itemFrame.UserData as PurchasedItem).Quantity = itemQuantity;
|
||||||
SetQuantityLabelText(StoreTab.SellFromSub, itemFrame);
|
SetQuantityLabelText(StoreTab.SellSub, itemFrame);
|
||||||
SetOwnedLabelText(itemFrame);
|
SetOwnedLabelText(itemFrame);
|
||||||
SetPriceGetters(itemFrame, false);
|
SetPriceGetters(itemFrame, false);
|
||||||
}
|
}
|
||||||
@@ -961,8 +1062,8 @@ namespace Barotrauma
|
|||||||
removedItemFrames.AddRange(storeRequestedSubGoodGroup.Children.Where(c => c.UserData is PurchasedItem).Except(existingItemFrames).ToList());
|
removedItemFrames.AddRange(storeRequestedSubGoodGroup.Children.Where(c => c.UserData is PurchasedItem).Except(existingItemFrames).ToList());
|
||||||
}
|
}
|
||||||
removedItemFrames.ForEach(f => f.RectTransform.Parent = null);
|
removedItemFrames.ForEach(f => f.RectTransform.Parent = null);
|
||||||
if (activeTab == StoreTab.SellFromSub) { FilterStoreItems(); }
|
if (activeTab == StoreTab.SellSub) { FilterStoreItems(); }
|
||||||
SortItems(StoreTab.SellFromSub);
|
SortItems(StoreTab.SellSub);
|
||||||
|
|
||||||
storeSellFromSubList.BarScroll = prevSellListScroll;
|
storeSellFromSubList.BarScroll = prevSellListScroll;
|
||||||
shoppingCrateSellFromSubList.BarScroll = prevShoppingCrateScroll;
|
shoppingCrateSellFromSubList.BarScroll = prevShoppingCrateScroll;
|
||||||
@@ -1062,17 +1163,14 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private void RefreshShoppingCrateList(List<PurchasedItem> items, GUIListBox listBox, StoreTab tab)
|
private void RefreshShoppingCrateList(List<PurchasedItem> items, GUIListBox listBox, StoreTab tab)
|
||||||
{
|
{
|
||||||
bool hasPermissions = HasPermissions;
|
bool hasPermissions = HasTabPermissions(tab);
|
||||||
HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>();
|
HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>();
|
||||||
int totalPrice = 0;
|
int totalPrice = 0;
|
||||||
foreach (PurchasedItem item in items)
|
foreach (PurchasedItem item in items)
|
||||||
{
|
{
|
||||||
PriceInfo priceInfo = item.ItemPrefab.GetPriceInfo(CurrentLocation);
|
if (!(item.ItemPrefab.GetPriceInfo(CurrentLocation) is { } priceInfo)) { continue; }
|
||||||
if (priceInfo == null) { continue; }
|
|
||||||
|
|
||||||
var itemFrame = listBox.Content.FindChild(c => c.UserData is PurchasedItem pi && pi.ItemPrefab.Identifier == item.ItemPrefab.Identifier);
|
|
||||||
GUINumberInput numInput = null;
|
GUINumberInput numInput = null;
|
||||||
if (itemFrame == null)
|
if (!(listBox.Content.FindChild(c => c.UserData is PurchasedItem pi && pi.ItemPrefab.Identifier == item.ItemPrefab.Identifier) is { } itemFrame))
|
||||||
{
|
{
|
||||||
itemFrame = CreateItemFrame(item, listBox, tab, forceDisable: !hasPermissions);
|
itemFrame = CreateItemFrame(item, listBox, tab, forceDisable: !hasPermissions);
|
||||||
numInput = itemFrame.FindChild(c => c is GUINumberInput, recursive: true) as GUINumberInput;
|
numInput = itemFrame.FindChild(c => c is GUINumberInput, recursive: true) as GUINumberInput;
|
||||||
@@ -1100,10 +1198,21 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
suppressBuySell = false;
|
suppressBuySell = false;
|
||||||
|
|
||||||
var price = tab == StoreTab.Buy ?
|
try
|
||||||
CurrentLocation.GetAdjustedItemBuyPrice(item.ItemPrefab, priceInfo: priceInfo) :
|
{
|
||||||
CurrentLocation.GetAdjustedItemSellPrice(item.ItemPrefab, priceInfo: priceInfo);
|
int price = tab switch
|
||||||
totalPrice += item.Quantity * price;
|
{
|
||||||
|
StoreTab.Buy => CurrentLocation.GetAdjustedItemBuyPrice(item.ItemPrefab, priceInfo: priceInfo),
|
||||||
|
StoreTab.Sell => CurrentLocation.GetAdjustedItemSellPrice(item.ItemPrefab, priceInfo: priceInfo),
|
||||||
|
StoreTab.SellSub => CurrentLocation.GetAdjustedItemSellPrice(item.ItemPrefab, priceInfo: priceInfo),
|
||||||
|
_ => throw new NotImplementedException()
|
||||||
|
};
|
||||||
|
totalPrice += item.Quantity * price;
|
||||||
|
}
|
||||||
|
catch (NotImplementedException e)
|
||||||
|
{
|
||||||
|
DebugConsole.ShowError($"Error getting item price: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var removedItemFrames = listBox.Content.Children.Except(existingItemFrames).ToList();
|
var removedItemFrames = listBox.Content.Children.Except(existingItemFrames).ToList();
|
||||||
@@ -1119,7 +1228,7 @@ namespace Barotrauma
|
|||||||
case StoreTab.Sell:
|
case StoreTab.Sell:
|
||||||
sellTotal = totalPrice;
|
sellTotal = totalPrice;
|
||||||
break;
|
break;
|
||||||
case StoreTab.SellFromSub:
|
case StoreTab.SellSub:
|
||||||
sellFromSubTotal = totalPrice;
|
sellFromSubTotal = totalPrice;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1135,7 +1244,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private void RefreshShoppingCrateSellList() => RefreshShoppingCrateList(CargoManager.ItemsInSellCrate, shoppingCrateSellList, StoreTab.Sell);
|
private void RefreshShoppingCrateSellList() => RefreshShoppingCrateList(CargoManager.ItemsInSellCrate, shoppingCrateSellList, StoreTab.Sell);
|
||||||
|
|
||||||
private void RefreshShoppingCrateSellFromSubList() => RefreshShoppingCrateList(CargoManager.ItemsInSellFromSubCrate, shoppingCrateSellFromSubList, StoreTab.SellFromSub);
|
private void RefreshShoppingCrateSellFromSubList() => RefreshShoppingCrateList(CargoManager.ItemsInSellFromSubCrate, shoppingCrateSellFromSubList, StoreTab.SellSub);
|
||||||
|
|
||||||
private void SortItems(GUIListBox list, SortingMethod sortingMethod)
|
private void SortItems(GUIListBox list, SortingMethod sortingMethod)
|
||||||
{
|
{
|
||||||
@@ -1286,14 +1395,12 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private void SortItems(StoreTab tab, SortingMethod sortingMethod)
|
private void SortItems(StoreTab tab, SortingMethod sortingMethod)
|
||||||
{
|
{
|
||||||
if (IsTabUnavailable(tab)) { return; }
|
|
||||||
tabSortingMethods[tab] = sortingMethod;
|
tabSortingMethods[tab] = sortingMethod;
|
||||||
SortItems(tabLists[tab], sortingMethod);
|
SortItems(tabLists[tab], sortingMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SortItems(StoreTab tab)
|
private void SortItems(StoreTab tab)
|
||||||
{
|
{
|
||||||
if (IsTabUnavailable(tab)) { return; }
|
|
||||||
SortItems(tab, tabSortingMethods[tab]);
|
SortItems(tab, tabSortingMethods[tab]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1301,12 +1408,6 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private GUIComponent CreateItemFrame(PurchasedItem pi, GUIComponent parentComponent, StoreTab containingTab, bool forceDisable = false)
|
private GUIComponent CreateItemFrame(PurchasedItem pi, GUIComponent parentComponent, StoreTab containingTab, bool forceDisable = false)
|
||||||
{
|
{
|
||||||
var tooltip = pi.ItemPrefab.Name;
|
|
||||||
if (!string.IsNullOrWhiteSpace(pi.ItemPrefab.Description))
|
|
||||||
{
|
|
||||||
tooltip += "\n" + pi.ItemPrefab.Description;
|
|
||||||
}
|
|
||||||
|
|
||||||
GUIListBox parentListBox = parentComponent as GUIListBox;
|
GUIListBox parentListBox = parentComponent as GUIListBox;
|
||||||
int width = 0;
|
int width = 0;
|
||||||
RectTransform parent = null;
|
RectTransform parent = null;
|
||||||
@@ -1320,7 +1421,11 @@ namespace Barotrauma
|
|||||||
width = parentComponent.Rect.Width;
|
width = parentComponent.Rect.Width;
|
||||||
parent = parentComponent.RectTransform;
|
parent = parentComponent.RectTransform;
|
||||||
}
|
}
|
||||||
|
string tooltip = pi.ItemPrefab.Name;
|
||||||
|
if (!string.IsNullOrWhiteSpace(pi.ItemPrefab.Description))
|
||||||
|
{
|
||||||
|
tooltip += $"\n{pi.ItemPrefab.Description}";
|
||||||
|
}
|
||||||
GUIFrame frame = new GUIFrame(new RectTransform(new Point(width, (int)(GUI.yScale * 80)), parent: parent), style: "ListBoxElement")
|
GUIFrame frame = new GUIFrame(new RectTransform(new Point(width, (int)(GUI.yScale * 80)), parent: parent), style: "ListBoxElement")
|
||||||
{
|
{
|
||||||
ToolTip = tooltip,
|
ToolTip = tooltip,
|
||||||
@@ -1338,8 +1443,7 @@ namespace Barotrauma
|
|||||||
var iconRelativeWidth = 0.0f;
|
var iconRelativeWidth = 0.0f;
|
||||||
var priceAndButtonRelativeWidth = 1.0f - nameAndIconRelativeWidth;
|
var priceAndButtonRelativeWidth = 1.0f - nameAndIconRelativeWidth;
|
||||||
|
|
||||||
Sprite itemIcon = pi.ItemPrefab.InventoryIcon ?? pi.ItemPrefab.sprite;
|
if ((pi.ItemPrefab.InventoryIcon ?? pi.ItemPrefab.sprite) is { } itemIcon)
|
||||||
if (itemIcon != null)
|
|
||||||
{
|
{
|
||||||
iconRelativeWidth = (0.9f * mainGroup.Rect.Height) / mainGroup.Rect.Width;
|
iconRelativeWidth = (0.9f * mainGroup.Rect.Height) / mainGroup.Rect.Width;
|
||||||
GUIImage img = new GUIImage(new RectTransform(new Vector2(iconRelativeWidth, 0.9f), mainGroup.RectTransform), itemIcon, scaleToFit: true)
|
GUIImage img = new GUIImage(new RectTransform(new Vector2(iconRelativeWidth, 0.9f), mainGroup.RectTransform), itemIcon, scaleToFit: true)
|
||||||
@@ -1425,7 +1529,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
if (suppressBuySell) { return; }
|
if (suppressBuySell) { return; }
|
||||||
PurchasedItem purchasedItem = numberInput.UserData as PurchasedItem;
|
PurchasedItem purchasedItem = numberInput.UserData as PurchasedItem;
|
||||||
if (!HasPermissions)
|
if (!HasActiveTabPermissions())
|
||||||
{
|
{
|
||||||
numberInput.IntValue = purchasedItem.Quantity;
|
numberInput.IntValue = purchasedItem.Quantity;
|
||||||
return;
|
return;
|
||||||
@@ -1528,11 +1632,16 @@ namespace Barotrauma
|
|||||||
OwnedItems.Clear();
|
OwnedItems.Clear();
|
||||||
|
|
||||||
// Add items on the sub(s)
|
// Add items on the sub(s)
|
||||||
Submarine.MainSub?.GetItems(true)
|
if (Submarine.MainSub?.GetItems(true) is List<Item> subItems)
|
||||||
.Where(i => i.Components.All(c => !(c is Holdable h) || !h.Attachable || !h.Attached) &&
|
{
|
||||||
i.Components.All(c => !(c is Wire w) || w.Connections.All(c => c == null)) &&
|
foreach (var subItem in subItems)
|
||||||
ItemAndAllContainersInteractable(i))
|
{
|
||||||
.ForEach(i => AddToOwnedItems(i.Prefab));
|
if (!subItem.Components.All(c => !(c is Holdable h) || !h.Attachable || !h.Attached)) { continue; }
|
||||||
|
if (!subItem.Components.All(c => !(c is Wire w) || w.Connections.All(c => c == null))) { continue; }
|
||||||
|
if (!ItemAndAllContainersInteractable(subItem)) { continue; }
|
||||||
|
AddToOwnedItems(subItem.Prefab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add items in character inventories
|
// Add items in character inventories
|
||||||
foreach (var item in Item.ItemList)
|
foreach (var item in Item.ItemList)
|
||||||
@@ -1664,14 +1773,22 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private int GetMaxAvailable(ItemPrefab itemPrefab, StoreTab mode)
|
private int GetMaxAvailable(ItemPrefab itemPrefab, StoreTab mode)
|
||||||
{
|
{
|
||||||
var list = mode switch
|
List<PurchasedItem> list = null;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
StoreTab.Buy => CurrentLocation.StoreStock,
|
list = mode switch
|
||||||
StoreTab.Sell => itemsToSell,
|
{
|
||||||
StoreTab.SellFromSub => itemsToSellFromSub,
|
StoreTab.Buy => CurrentLocation?.StoreStock,
|
||||||
_ => throw new NotImplementedException()
|
StoreTab.Sell => itemsToSell,
|
||||||
};
|
StoreTab.SellSub => itemsToSellFromSub,
|
||||||
if (list.Find(i => i.ItemPrefab == itemPrefab) is PurchasedItem item)
|
_ => throw new NotImplementedException()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (NotImplementedException e)
|
||||||
|
{
|
||||||
|
DebugConsole.ShowError($"Error getting item availability: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
|
||||||
|
}
|
||||||
|
if (list != null && list.Find(i => i.ItemPrefab == itemPrefab) is PurchasedItem item)
|
||||||
{
|
{
|
||||||
if (mode == StoreTab.Buy)
|
if (mode == StoreTab.Buy)
|
||||||
{
|
{
|
||||||
@@ -1691,8 +1808,8 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private bool ModifyBuyQuantity(PurchasedItem item, int quantity)
|
private bool ModifyBuyQuantity(PurchasedItem item, int quantity)
|
||||||
{
|
{
|
||||||
if (item == null || item.ItemPrefab == null) { return false; }
|
if (item?.ItemPrefab == null) { return false; }
|
||||||
if (!HasPermissions) { return false; }
|
if (!HasBuyPermissions) { return false; }
|
||||||
if (quantity > 0)
|
if (quantity > 0)
|
||||||
{
|
{
|
||||||
var itemInCrate = CargoManager.ItemsInBuyCrate.Find(i => i.ItemPrefab == item.ItemPrefab);
|
var itemInCrate = CargoManager.ItemsInBuyCrate.Find(i => i.ItemPrefab == item.ItemPrefab);
|
||||||
@@ -1703,13 +1820,13 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
CargoManager.ModifyItemQuantityInBuyCrate(item.ItemPrefab, quantity);
|
CargoManager.ModifyItemQuantityInBuyCrate(item.ItemPrefab, quantity);
|
||||||
GameMain.Client?.SendCampaignState();
|
GameMain.Client?.SendCampaignState();
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ModifySellQuantity(PurchasedItem item, int quantity)
|
private bool ModifySellQuantity(PurchasedItem item, int quantity)
|
||||||
{
|
{
|
||||||
if (item == null || item.ItemPrefab == null) { return false; }
|
if (item?.ItemPrefab == null) { return false; }
|
||||||
if (!HasPermissions) { return false; }
|
if (!HasSellInventoryPermissions) { return false; }
|
||||||
if (quantity > 0)
|
if (quantity > 0)
|
||||||
{
|
{
|
||||||
// Make sure there's enough available to sell
|
// Make sure there's enough available to sell
|
||||||
@@ -1718,45 +1835,68 @@ namespace Barotrauma
|
|||||||
if (totalQuantityToSell > GetMaxAvailable(item.ItemPrefab, StoreTab.Sell)) { return false; }
|
if (totalQuantityToSell > GetMaxAvailable(item.ItemPrefab, StoreTab.Sell)) { return false; }
|
||||||
}
|
}
|
||||||
CargoManager.ModifyItemQuantityInSellCrate(item.ItemPrefab, quantity);
|
CargoManager.ModifyItemQuantityInSellCrate(item.ItemPrefab, quantity);
|
||||||
//GameMain.Client?.SendCampaignState();
|
return true;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ModifySellFromSubQuantity(PurchasedItem item, int quantity)
|
private bool ModifySellFromSubQuantity(PurchasedItem item, int quantity)
|
||||||
{
|
{
|
||||||
if (item == null || item.ItemPrefab == null) { return false; }
|
if (item?.ItemPrefab == null) { return false; }
|
||||||
if (!HasPermissions) { return false; }
|
if (!HasSellSubPermissions) { return false; }
|
||||||
if (quantity > 0)
|
if (quantity > 0)
|
||||||
{
|
{
|
||||||
// Make sure there's enough available to sell
|
// Make sure there's enough available to sell
|
||||||
var itemToSell = CargoManager.ItemsInSellFromSubCrate.Find(i => i.ItemPrefab == item.ItemPrefab);
|
var itemToSell = CargoManager.ItemsInSellFromSubCrate.Find(i => i.ItemPrefab == item.ItemPrefab);
|
||||||
var totalQuantityToSell = itemToSell != null ? itemToSell.Quantity + quantity : quantity;
|
var totalQuantityToSell = itemToSell != null ? itemToSell.Quantity + quantity : quantity;
|
||||||
if (totalQuantityToSell > GetMaxAvailable(item.ItemPrefab, StoreTab.SellFromSub)) { return false; }
|
if (totalQuantityToSell > GetMaxAvailable(item.ItemPrefab, StoreTab.SellSub)) { return false; }
|
||||||
}
|
}
|
||||||
CargoManager.ModifyItemQuantityInSellFromSubCrate(item.ItemPrefab, quantity);
|
CargoManager.ModifyItemQuantityInSellFromSubCrate(item.ItemPrefab, quantity);
|
||||||
// TODO: GameMain.Client?.SendCampaignState();
|
GameMain.Client?.SendCampaignState();
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool AddToShoppingCrate(PurchasedItem item, int quantity = 1) => activeTab switch
|
private bool AddToShoppingCrate(PurchasedItem item, int quantity = 1)
|
||||||
{
|
{
|
||||||
StoreTab.Buy => ModifyBuyQuantity(item, quantity),
|
if (item == null) { return false; }
|
||||||
StoreTab.Sell => ModifySellQuantity(item, quantity),
|
try
|
||||||
StoreTab.SellFromSub => ModifySellFromSubQuantity(item, quantity),
|
{
|
||||||
_ => throw new NotImplementedException(),
|
return activeTab switch
|
||||||
};
|
{
|
||||||
|
StoreTab.Buy => ModifyBuyQuantity(item, quantity),
|
||||||
|
StoreTab.Sell => ModifySellQuantity(item, quantity),
|
||||||
|
StoreTab.SellSub => ModifySellFromSubQuantity(item, quantity),
|
||||||
|
_ => throw new NotImplementedException()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (NotImplementedException e)
|
||||||
|
{
|
||||||
|
DebugConsole.ShowError($"Error adding an item to the shopping crate: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private bool ClearFromShoppingCrate(PurchasedItem item) => activeTab switch
|
private bool ClearFromShoppingCrate(PurchasedItem item)
|
||||||
{
|
{
|
||||||
StoreTab.Buy => ModifyBuyQuantity(item, -item.Quantity),
|
if (item == null) { return false; }
|
||||||
StoreTab.Sell => ModifySellQuantity(item, -item.Quantity),
|
try
|
||||||
StoreTab.SellFromSub => ModifySellFromSubQuantity(item, -item.Quantity),
|
{
|
||||||
_ => throw new NotImplementedException(),
|
return activeTab switch
|
||||||
};
|
{
|
||||||
|
StoreTab.Buy => ModifyBuyQuantity(item, -item.Quantity),
|
||||||
|
StoreTab.Sell => ModifySellQuantity(item, -item.Quantity),
|
||||||
|
StoreTab.SellSub => ModifySellFromSubQuantity(item, -item.Quantity),
|
||||||
|
_ => throw new NotImplementedException(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (NotImplementedException e)
|
||||||
|
{
|
||||||
|
DebugConsole.ShowError($"Error clearing the shopping crate: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private bool BuyItems()
|
private bool BuyItems()
|
||||||
{
|
{
|
||||||
if (!HasPermissions) { return false; }
|
if (!HasBuyPermissions) { return false; }
|
||||||
|
|
||||||
var itemsToPurchase = new List<PurchasedItem>(CargoManager.ItemsInBuyCrate);
|
var itemsToPurchase = new List<PurchasedItem>(CargoManager.ItemsInBuyCrate);
|
||||||
var itemsToRemove = new List<PurchasedItem>();
|
var itemsToRemove = new List<PurchasedItem>();
|
||||||
@@ -1788,15 +1928,24 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private bool SellItems()
|
private bool SellItems()
|
||||||
{
|
{
|
||||||
if (!HasPermissions) { return false; }
|
if (!HasActiveTabPermissions()) { return false; }
|
||||||
var itemsToSell = activeTab switch
|
List<PurchasedItem> itemsToSell;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
StoreTab.Sell => new List<PurchasedItem>(CargoManager.ItemsInSellCrate),
|
itemsToSell = activeTab switch
|
||||||
StoreTab.SellFromSub => new List<PurchasedItem>(CargoManager.ItemsInSellFromSubCrate),
|
{
|
||||||
_ => throw new NotImplementedException()
|
StoreTab.Sell => new List<PurchasedItem>(CargoManager.ItemsInSellCrate),
|
||||||
};
|
StoreTab.SellSub => new List<PurchasedItem>(CargoManager.ItemsInSellFromSubCrate),
|
||||||
|
_ => throw new NotImplementedException()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (NotImplementedException e)
|
||||||
|
{
|
||||||
|
DebugConsole.ShowError($"Error confirming the store transaction: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
var itemsToRemove = new List<PurchasedItem>();
|
var itemsToRemove = new List<PurchasedItem>();
|
||||||
var totalValue = 0;
|
int totalValue = 0;
|
||||||
foreach (PurchasedItem item in itemsToSell)
|
foreach (PurchasedItem item in itemsToSell)
|
||||||
{
|
{
|
||||||
if (item?.ItemPrefab?.GetPriceInfo(CurrentLocation) is PriceInfo priceInfo)
|
if (item?.ItemPrefab?.GetPriceInfo(CurrentLocation) is PriceInfo priceInfo)
|
||||||
@@ -1811,11 +1960,7 @@ namespace Barotrauma
|
|||||||
itemsToRemove.ForEach(i => itemsToSell.Remove(i));
|
itemsToRemove.ForEach(i => itemsToSell.Remove(i));
|
||||||
if (itemsToSell.None() || totalValue > CurrentLocation.StoreCurrentBalance) { return false; }
|
if (itemsToSell.None() || totalValue > CurrentLocation.StoreCurrentBalance) { return false; }
|
||||||
CargoManager.SellItems(itemsToSell, activeTab);
|
CargoManager.SellItems(itemsToSell, activeTab);
|
||||||
if (activeTab == StoreTab.Sell)
|
GameMain.Client?.SendCampaignState();
|
||||||
{
|
|
||||||
// TODO: Implement selling sub items in multiplayer
|
|
||||||
GameMain.Client?.SendCampaignState();
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1831,7 +1976,7 @@ namespace Barotrauma
|
|||||||
int total = activeTab switch
|
int total = activeTab switch
|
||||||
{
|
{
|
||||||
StoreTab.Sell => sellTotal,
|
StoreTab.Sell => sellTotal,
|
||||||
StoreTab.SellFromSub => sellFromSubTotal,
|
StoreTab.SellSub => sellFromSubTotal,
|
||||||
_ => throw new NotImplementedException(),
|
_ => throw new NotImplementedException(),
|
||||||
};
|
};
|
||||||
shoppingCrateTotal.Text = GetCurrencyFormatted(total);
|
shoppingCrateTotal.Text = GetCurrencyFormatted(total);
|
||||||
@@ -1863,21 +2008,29 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetConfirmButtonStatus() => confirmButton.Enabled =
|
private void SetConfirmButtonStatus()
|
||||||
HasPermissions && ActiveShoppingCrateList.Content.RectTransform.Children.Any() &&
|
{
|
||||||
activeTab switch
|
confirmButton.Enabled =
|
||||||
{
|
HasActiveTabPermissions() &&
|
||||||
StoreTab.Buy => buyTotal <= PlayerMoney,
|
ActiveShoppingCrateList.Content.RectTransform.Children.Any() &&
|
||||||
StoreTab.Sell => CurrentLocation != null && sellTotal <= CurrentLocation.StoreCurrentBalance,
|
activeTab switch
|
||||||
StoreTab.SellFromSub => CurrentLocation != null && sellFromSubTotal <= CurrentLocation.StoreCurrentBalance,
|
{
|
||||||
_ => throw new NotImplementedException(),
|
StoreTab.Buy => buyTotal <= PlayerMoney,
|
||||||
};
|
StoreTab.Sell => CurrentLocation != null && sellTotal <= CurrentLocation.StoreCurrentBalance,
|
||||||
|
StoreTab.SellSub => CurrentLocation != null && sellFromSubTotal <= CurrentLocation.StoreCurrentBalance,
|
||||||
|
_ => false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private void SetClearAllButtonStatus() => clearAllButton.Enabled =
|
private void SetClearAllButtonStatus()
|
||||||
HasPermissions && ActiveShoppingCrateList.Content.RectTransform.Children.Any();
|
{
|
||||||
|
clearAllButton.Enabled =
|
||||||
|
HasActiveTabPermissions() &&
|
||||||
|
ActiveShoppingCrateList.Content.RectTransform.Children.Any();
|
||||||
|
}
|
||||||
|
|
||||||
private float ownedItemsUpdateTimer = 0.0f, sellableItemsFromSubUpdateTimer = 0.0f;
|
private float ownedItemsUpdateTimer = 0.0f, sellableItemsFromSubUpdateTimer = 0.0f;
|
||||||
private readonly float timerUpdateInterval = 1.5f;
|
private const float timerUpdateInterval = 1.5f;
|
||||||
|
|
||||||
public void Update(float deltaTime)
|
public void Update(float deltaTime)
|
||||||
{
|
{
|
||||||
@@ -1914,10 +2067,10 @@ namespace Barotrauma
|
|||||||
|
|
||||||
if (needsItemsToSellRefresh) { RefreshItemsToSell(); }
|
if (needsItemsToSellRefresh) { RefreshItemsToSell(); }
|
||||||
if (needsItemsToSellFromSubRefresh) { RefreshItemsToSellFromSub(); }
|
if (needsItemsToSellFromSubRefresh) { RefreshItemsToSellFromSub(); }
|
||||||
if (needsRefresh || hadPermissions != HasPermissions) { Refresh(updateOwned: ownedItemsUpdateTimer > 0.0f); }
|
if (needsRefresh || HavePermissionsChanged()) { Refresh(updateOwned: ownedItemsUpdateTimer > 0.0f); }
|
||||||
if (needsBuyingRefresh) { RefreshBuying(); }
|
if (needsBuyingRefresh || HavePermissionsChanged(StoreTab.Buy)) { RefreshBuying(updateOwned: ownedItemsUpdateTimer > 0.0f); }
|
||||||
if (needsSellingRefresh) { RefreshSelling(); }
|
if (needsSellingRefresh || HavePermissionsChanged(StoreTab.Sell)) { RefreshSelling(updateOwned: ownedItemsUpdateTimer > 0.0f); }
|
||||||
if (needsSellingFromSubRefresh) { RefreshSellingFromSub(updateItemsToSellFromSub: sellableItemsFromSubUpdateTimer > 0.0f); }
|
if (needsSellingFromSubRefresh || HavePermissionsChanged(StoreTab.SellSub)) { RefreshSellingFromSub(updateItemsToSellFromSub: sellableItemsFromSubUpdateTimer > 0.0f); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,6 +167,7 @@ namespace Barotrauma
|
|||||||
//TODO: move this somewhere else
|
//TODO: move this somewhere else
|
||||||
public static void UpdateCategoryList(GUIListBox categoryList, CampaignMode campaign, Submarine? drawnSubmarine, IEnumerable<UpgradeCategory> applicableCategories)
|
public static void UpdateCategoryList(GUIListBox categoryList, CampaignMode campaign, Submarine? drawnSubmarine, IEnumerable<UpgradeCategory> applicableCategories)
|
||||||
{
|
{
|
||||||
|
var subItems = GetSubItems();
|
||||||
foreach (GUIComponent component in categoryList.Content.Children)
|
foreach (GUIComponent component in categoryList.Content.Children)
|
||||||
{
|
{
|
||||||
if (!(component.UserData is CategoryData data)) { continue; }
|
if (!(component.UserData is CategoryData data)) { continue; }
|
||||||
@@ -178,7 +179,7 @@ namespace Barotrauma
|
|||||||
var customizeButton = component.FindChild("customizebutton", true);
|
var customizeButton = component.FindChild("customizebutton", true);
|
||||||
if (customizeButton != null)
|
if (customizeButton != null)
|
||||||
{
|
{
|
||||||
customizeButton.Visible = HasSwappableItems(data.Category);
|
customizeButton.Visible = HasSwappableItems(data.Category, subItems);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -719,16 +720,19 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private bool customizeTabOpen;
|
private bool customizeTabOpen;
|
||||||
|
|
||||||
private static bool HasSwappableItems(UpgradeCategory category)
|
private static bool HasSwappableItems(UpgradeCategory category, List<Item>? subItems = null)
|
||||||
{
|
{
|
||||||
if (Submarine.MainSub == null) { return false; }
|
if (Submarine.MainSub == null) { return false; }
|
||||||
return Submarine.MainSub.GetItems(true).Any(i =>
|
subItems ??= GetSubItems();
|
||||||
|
return subItems.Any(i =>
|
||||||
i.Prefab.SwappableItem != null &&
|
i.Prefab.SwappableItem != null &&
|
||||||
!i.HiddenInGame && i.AllowSwapping &&
|
!i.HiddenInGame && i.AllowSwapping &&
|
||||||
(i.Prefab.SwappableItem.CanBeBought || ItemPrefab.Prefabs.Any(ip => ip.SwappableItem?.ReplacementOnUninstall == i.Prefab.Identifier)) &&
|
(i.Prefab.SwappableItem.CanBeBought || ItemPrefab.Prefabs.Any(ip => ip.SwappableItem?.ReplacementOnUninstall == i.Prefab.Identifier)) &&
|
||||||
Submarine.MainSub.IsEntityFoundOnThisSub(i, true) && category.ItemTags.Any(t => i.HasTag(t)));
|
Submarine.MainSub.IsEntityFoundOnThisSub(i, true) && category.ItemTags.Any(t => i.HasTag(t)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static List<Item> GetSubItems() => Submarine.MainSub?.GetItems(true) ?? new List<Item>();
|
||||||
|
|
||||||
private void SelectUpgradeCategory(List<UpgradePrefab> prefabs, UpgradeCategory category, Submarine submarine)
|
private void SelectUpgradeCategory(List<UpgradePrefab> prefabs, UpgradeCategory category, Submarine submarine)
|
||||||
{
|
{
|
||||||
if (selectedUpgradeCategoryLayout == null) { return; }
|
if (selectedUpgradeCategoryLayout == null) { return; }
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Barotrauma.Extensions;
|
using Barotrauma.Extensions;
|
||||||
using Barotrauma.Items.Components;
|
using Barotrauma.Items.Components;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
@@ -7,37 +8,6 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
partial class CargoManager
|
partial class CargoManager
|
||||||
{
|
{
|
||||||
private class SoldEntity
|
|
||||||
{
|
|
||||||
public enum SellStatus
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Entity sold in SP. Or, entity sold by client and confirmed by server in MP.
|
|
||||||
/// </summary>
|
|
||||||
Confirmed,
|
|
||||||
/// <summary>
|
|
||||||
/// Entity sold by client in MP. Client has received at least one update from server after selling, but this entity wasn't yet confirmed.
|
|
||||||
/// </summary>
|
|
||||||
Unconfirmed,
|
|
||||||
/// <summary>
|
|
||||||
/// Entity sold by client in MP. Client hasn't yet received an update from server after selling.
|
|
||||||
/// </summary>
|
|
||||||
Local
|
|
||||||
}
|
|
||||||
|
|
||||||
public Item Item { get; }
|
|
||||||
public SellStatus Status { get; set; }
|
|
||||||
|
|
||||||
private SoldEntity(Item item, SellStatus status)
|
|
||||||
{
|
|
||||||
Item = item;
|
|
||||||
Status = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SoldEntity CreateInSinglePlayer(Item item) => new SoldEntity(item, SellStatus.Confirmed);
|
|
||||||
public static SoldEntity CreateInMultiPlayer(Item item) => new SoldEntity(item, SellStatus.Local);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<SoldEntity> SoldEntities { get; } = new List<SoldEntity>();
|
private List<SoldEntity> SoldEntities { get; } = new List<SoldEntity>();
|
||||||
|
|
||||||
// The bag slot is intentionally left out since we want to be able to sell items from there
|
// The bag slot is intentionally left out since we want to be able to sell items from there
|
||||||
@@ -67,31 +37,6 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<Item> GetSellableItemsFromSub()
|
|
||||||
{
|
|
||||||
if (Submarine.MainSub == null) { return new List<Item>(); }
|
|
||||||
var confirmedSoldEntities = GetConfirmedSoldEntities();
|
|
||||||
return Submarine.MainSub.GetItems(true).FindAll(item =>
|
|
||||||
{
|
|
||||||
if (!IsItemSellable(item, confirmedSoldEntities)) { return false; }
|
|
||||||
if (item.GetRootInventoryOwner() is Character) { return false; }
|
|
||||||
if (!item.Components.All(c => !(c is Holdable h) || !h.Attachable || !h.Attached)) { return false; }
|
|
||||||
if (!item.Components.All(c => !(c is Wire w) || w.Connections.All(c => c == null))) { return false; }
|
|
||||||
if (!ItemAndAllContainersInteractable(item)) { return false; }
|
|
||||||
return true;
|
|
||||||
}).Distinct();
|
|
||||||
|
|
||||||
static bool ItemAndAllContainersInteractable(Item item)
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (!item.IsPlayerTeamInteractable) { return false; }
|
|
||||||
item = item.Container;
|
|
||||||
} while (item != null);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerable<SoldEntity> GetConfirmedSoldEntities()
|
private IEnumerable<SoldEntity> GetConfirmedSoldEntities()
|
||||||
{
|
{
|
||||||
// Only consider items which have been:
|
// Only consider items which have been:
|
||||||
@@ -100,24 +45,6 @@ namespace Barotrauma
|
|||||||
return SoldEntities.Where(se => se.Status != SoldEntity.SellStatus.Unconfirmed);
|
return SoldEntities.Where(se => se.Status != SoldEntity.SellStatus.Unconfirmed);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsItemSellable(Item item, IEnumerable<SoldEntity> confirmedSoldEntities)
|
|
||||||
{
|
|
||||||
if (!item.Prefab.CanBeSold) { return false; }
|
|
||||||
if (item.SpawnedInCurrentOutpost) { return false; }
|
|
||||||
if (!item.Prefab.AllowSellingWhenBroken && item.ConditionPercentage < 90.0f) { return false; }
|
|
||||||
if (confirmedSoldEntities.Any(it => it.Item == item)) { return false; }
|
|
||||||
if (item.OwnInventory?.Container is ItemContainer itemContainer)
|
|
||||||
{
|
|
||||||
var containedItems = item.ContainedItems;
|
|
||||||
if (containedItems.None()) { return true; }
|
|
||||||
// Allow selling the item if contained items are unsellable and set to be removed on deconstruct
|
|
||||||
if (itemContainer.RemoveContainedItemsOnDeconstruct && containedItems.All(it => !it.Prefab.CanBeSold)) { return true; }
|
|
||||||
// Otherwise there must be no contained items or the contained items must be confirmed as sold
|
|
||||||
if (!containedItems.All(it => confirmedSoldEntities.Any(se => se.Item == it))) { return false; }
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetItemsInBuyCrate(List<PurchasedItem> items)
|
public void SetItemsInBuyCrate(List<PurchasedItem> items)
|
||||||
{
|
{
|
||||||
ItemsInBuyCrate.Clear();
|
ItemsInBuyCrate.Clear();
|
||||||
@@ -125,15 +52,21 @@ namespace Barotrauma
|
|||||||
OnItemsInBuyCrateChanged?.Invoke();
|
OnItemsInBuyCrateChanged?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetItemsInSubSellCrate(List<PurchasedItem> items)
|
||||||
|
{
|
||||||
|
ItemsInSellFromSubCrate.Clear();
|
||||||
|
ItemsInSellFromSubCrate.AddRange(items);
|
||||||
|
OnItemsInSellFromSubCrateChanged?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
public void SetSoldItems(List<SoldItem> items)
|
public void SetSoldItems(List<SoldItem> items)
|
||||||
{
|
{
|
||||||
SoldItems.Clear();
|
SoldItems.Clear();
|
||||||
SoldItems.AddRange(items);
|
SoldItems.AddRange(items);
|
||||||
|
foreach (var se in SoldEntities)
|
||||||
foreach (SoldEntity se in SoldEntities)
|
|
||||||
{
|
{
|
||||||
if (se.Status == SoldEntity.SellStatus.Confirmed) { continue; }
|
if (se.Status == SoldEntity.SellStatus.Confirmed) { continue; }
|
||||||
if (SoldItems.Any(si => si.ID == se.Item.ID && si.ItemPrefab == se.Item.Prefab && (GameMain.Client == null || GameMain.Client.ID == si.SellerID)))
|
if (SoldItems.Any(si => Match(si, se, true)))
|
||||||
{
|
{
|
||||||
se.Status = SoldEntity.SellStatus.Confirmed;
|
se.Status = SoldEntity.SellStatus.Confirmed;
|
||||||
}
|
}
|
||||||
@@ -142,13 +75,28 @@ namespace Barotrauma
|
|||||||
se.Status = SoldEntity.SellStatus.Unconfirmed;
|
se.Status = SoldEntity.SellStatus.Unconfirmed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
foreach (var si in SoldItems)
|
||||||
|
{
|
||||||
|
if (si.Origin != SoldItem.SellOrigin.Submarine) { continue; }
|
||||||
|
if (!(SoldEntities.FirstOrDefault(se => se.Item == null && Match(si, se, false)) is SoldEntity soldEntityMatch)) { continue; }
|
||||||
|
if (!(Entity.FindEntityByID(si.ID) is Item item)) { continue; }
|
||||||
|
soldEntityMatch.SetItem(item);
|
||||||
|
soldEntityMatch.Status = SoldEntity.SellStatus.Confirmed;
|
||||||
|
}
|
||||||
OnSoldItemsChanged?.Invoke();
|
OnSoldItemsChanged?.Invoke();
|
||||||
|
|
||||||
|
static bool Match(SoldItem soldItem, SoldEntity soldEntity, bool matchId)
|
||||||
|
{
|
||||||
|
if (soldItem.ItemPrefab != soldEntity.ItemPrefab) { return false; }
|
||||||
|
if (matchId && (soldEntity.Item == null || soldItem.ID != soldEntity.Item.ID)) { return false; }
|
||||||
|
if (soldItem.Origin == SoldItem.SellOrigin.Character && GameMain.Client != null && soldItem.SellerID != GameMain.Client.ID) { return false; }
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ModifyItemQuantityInSellCrate(ItemPrefab itemPrefab, int changeInQuantity)
|
public void ModifyItemQuantityInSellCrate(ItemPrefab itemPrefab, int changeInQuantity)
|
||||||
{
|
{
|
||||||
PurchasedItem itemToSell = ItemsInSellCrate.Find(i => i.ItemPrefab == itemPrefab);
|
var itemToSell = ItemsInSellCrate.Find(i => i.ItemPrefab == itemPrefab);
|
||||||
if (itemToSell != null)
|
if (itemToSell != null)
|
||||||
{
|
{
|
||||||
itemToSell.Quantity += changeInQuantity;
|
itemToSell.Quantity += changeInQuantity;
|
||||||
@@ -186,74 +134,69 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public void SellItems(List<PurchasedItem> itemsToSell, Store.StoreTab sellingMode)
|
public void SellItems(List<PurchasedItem> itemsToSell, Store.StoreTab sellingMode)
|
||||||
{
|
{
|
||||||
var sellableItems = sellingMode switch
|
IEnumerable<Item> sellableItems;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
Store.StoreTab.Sell => GetSellableItems(Character.Controlled),
|
sellableItems = sellingMode switch
|
||||||
Store.StoreTab.SellFromSub => GetSellableItemsFromSub(),
|
{
|
||||||
_ => throw new System.NotImplementedException(),
|
Store.StoreTab.Sell => GetSellableItems(Character.Controlled),
|
||||||
};
|
Store.StoreTab.SellSub => GetSellableItemsFromSub(),
|
||||||
|
_ => throw new NotImplementedException()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (NotImplementedException e)
|
||||||
|
{
|
||||||
|
DebugConsole.ShowError($"Error selling items: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
bool canAddToRemoveQueue = campaign.IsSinglePlayer && Entity.Spawner != null;
|
bool canAddToRemoveQueue = campaign.IsSinglePlayer && Entity.Spawner != null;
|
||||||
var sellerId = GameMain.Client?.ID ?? 0;
|
byte sellerId = GameMain.Client?.ID ?? 0;
|
||||||
|
|
||||||
// Check all the prices before starting the transaction
|
// Check all the prices before starting the transaction
|
||||||
// to make sure the modifiers stay the same for the whole transaction
|
// to make sure the modifiers stay the same for the whole transaction
|
||||||
Dictionary<ItemPrefab, int> sellValues = GetSellValuesAtCurrentLocation(itemsToSell.Select(i => i.ItemPrefab));
|
Dictionary<ItemPrefab, int> sellValues = GetSellValuesAtCurrentLocation(itemsToSell.Select(i => i.ItemPrefab));
|
||||||
|
|
||||||
foreach (PurchasedItem item in itemsToSell)
|
foreach (PurchasedItem item in itemsToSell)
|
||||||
{
|
{
|
||||||
var itemValue = item.Quantity * sellValues[item.ItemPrefab];
|
int itemValue = item.Quantity * sellValues[item.ItemPrefab];
|
||||||
|
|
||||||
// check if the store can afford the item
|
// check if the store can afford the item
|
||||||
if (Location.StoreCurrentBalance < itemValue) { continue; }
|
if (Location.StoreCurrentBalance < itemValue) { continue; }
|
||||||
|
|
||||||
// TODO: Write logic for prioritizing certain items over others (e.g. lone Battery Cell should be preferred over one inside a Stun Baton)
|
// TODO: Write logic for prioritizing certain items over others (e.g. lone Battery Cell should be preferred over one inside a Stun Baton)
|
||||||
var matchingItems = sellableItems.Where(i => i.Prefab == item.ItemPrefab);
|
var matchingItems = sellableItems.Where(i => i.Prefab == item.ItemPrefab);
|
||||||
if (matchingItems.Count() <= item.Quantity)
|
int count = Math.Min(item.Quantity, matchingItems.Count());
|
||||||
|
SoldItem.SellOrigin origin = sellingMode == Store.StoreTab.Sell ? SoldItem.SellOrigin.Character : SoldItem.SellOrigin.Submarine;
|
||||||
|
if (origin == SoldItem.SellOrigin.Character || GameMain.IsSingleplayer)
|
||||||
{
|
{
|
||||||
foreach (Item i in matchingItems)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
SoldItems.Add(new SoldItem(i.Prefab, i.ID, canAddToRemoveQueue, sellerId));
|
var matchingItem = matchingItems.ElementAt(i);
|
||||||
SoldEntities.Add(campaign.IsSinglePlayer ? SoldEntity.CreateInSinglePlayer(i) : SoldEntity.CreateInMultiPlayer(i));
|
SoldItems.Add(new SoldItem(matchingItem.Prefab, matchingItem.ID, canAddToRemoveQueue, sellerId, origin));
|
||||||
if (canAddToRemoveQueue) { Entity.Spawner.AddToRemoveQueue(i); }
|
SoldEntities.Add(new SoldEntity(matchingItem, campaign.IsSinglePlayer ? SoldEntity.SellStatus.Confirmed : SoldEntity.SellStatus.Local));
|
||||||
|
if (canAddToRemoveQueue) { Entity.Spawner.AddToRemoveQueue(matchingItem); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (int i = 0; i < item.Quantity; i++)
|
// When selling from the sub in multiplayer, the server will determine the items that are sold
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
var matchingItem = matchingItems.ElementAt(i);
|
SoldItems.Add(new SoldItem(item.ItemPrefab, Entity.NullEntityID, canAddToRemoveQueue, sellerId, origin));
|
||||||
SoldItems.Add(new SoldItem(matchingItem.Prefab, matchingItem.ID, canAddToRemoveQueue, sellerId));
|
SoldEntities.Add(new SoldEntity(item.ItemPrefab, SoldEntity.SellStatus.Local));
|
||||||
SoldEntities.Add(campaign.IsSinglePlayer ? SoldEntity.CreateInSinglePlayer(matchingItem) : SoldEntity.CreateInMultiPlayer(matchingItem));
|
|
||||||
if (canAddToRemoveQueue) { Entity.Spawner.AddToRemoveQueue(matchingItem); }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exchange money
|
// Exchange money
|
||||||
Location.StoreCurrentBalance -= itemValue;
|
Location.StoreCurrentBalance -= itemValue;
|
||||||
campaign.Money += itemValue;
|
campaign.Money += itemValue;
|
||||||
|
|
||||||
GameAnalyticsManager.AddMoneyGainedEvent(itemValue, GameAnalyticsManager.MoneySource.Store, item.ItemPrefab.Identifier);
|
GameAnalyticsManager.AddMoneyGainedEvent(itemValue, GameAnalyticsManager.MoneySource.Store, item.ItemPrefab.Identifier);
|
||||||
|
|
||||||
// Remove from the sell crate
|
// Remove from the sell crate
|
||||||
// TODO: Simplify duplicate logic?
|
if ((sellingMode == Store.StoreTab.Sell ? ItemsInSellCrate : ItemsInSellFromSubCrate)?.Find(pi => pi.ItemPrefab == item.ItemPrefab) is { } itemToSell)
|
||||||
if (sellingMode == Store.StoreTab.Sell && ItemsInSellCrate.Find(pi => pi.ItemPrefab == item.ItemPrefab) is { } inventoryItem)
|
|
||||||
{
|
{
|
||||||
inventoryItem.Quantity -= item.Quantity;
|
itemToSell.Quantity -= item.Quantity;
|
||||||
if (inventoryItem.Quantity < 1)
|
if (itemToSell.Quantity < 1)
|
||||||
{
|
{
|
||||||
ItemsInSellCrate.Remove(inventoryItem);
|
(sellingMode == Store.StoreTab.Sell ? ItemsInSellCrate : ItemsInSellFromSubCrate)?.Remove(itemToSell);
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(sellingMode == Store.StoreTab.SellFromSub && ItemsInSellFromSubCrate.Find(pi => pi.ItemPrefab == item.ItemPrefab) is { } subItem)
|
|
||||||
{
|
|
||||||
subItem.Quantity -= item.Quantity;
|
|
||||||
if (subItem.Quantity < 1)
|
|
||||||
{
|
|
||||||
ItemsInSellFromSubCrate.Remove(subItem);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
OnSoldItemsChanged?.Invoke();
|
OnSoldItemsChanged?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -145,12 +145,14 @@ namespace Barotrauma
|
|||||||
string msgCommand = ChatMessage.GetChatMessageCommand(text, out string msg);
|
string msgCommand = ChatMessage.GetChatMessageCommand(text, out string msg);
|
||||||
// add to local history
|
// add to local history
|
||||||
ChatBox.ChatManager.Store(text);
|
ChatBox.ChatManager.Store(text);
|
||||||
|
WifiComponent headset = null;
|
||||||
|
ChatMessageType messageType =
|
||||||
|
((msgCommand == "r" || msgCommand == "radio") && ChatMessage.CanUseRadio(Character.Controlled, out headset)) ? ChatMessageType.Radio : ChatMessageType.Default;
|
||||||
AddSinglePlayerChatMessage(
|
AddSinglePlayerChatMessage(
|
||||||
Character.Controlled.Info.Name,
|
Character.Controlled.Info.Name,
|
||||||
msg,
|
msg, messageType,
|
||||||
((msgCommand == "r" || msgCommand == "radio") && ChatMessage.CanUseRadio(Character.Controlled)) ? ChatMessageType.Radio : ChatMessageType.Default,
|
|
||||||
Character.Controlled);
|
Character.Controlled);
|
||||||
if (ChatMessage.CanUseRadio(Character.Controlled, out WifiComponent headset))
|
if (messageType == ChatMessageType.Radio && headset != null)
|
||||||
{
|
{
|
||||||
Signal s = new Signal(msg, sender: Character.Controlled, source: headset.Item);
|
Signal s = new Signal(msg, sender: Character.Controlled, source: headset.Item);
|
||||||
headset.TransmitSignal(s, sentFromChat: true);
|
headset.TransmitSignal(s, sentFromChat: true);
|
||||||
@@ -819,7 +821,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
OrderChatMessage msg = new OrderChatMessage(order, "", priority, order.IsReport ? hull : order.TargetEntity, null, orderGiver);
|
OrderChatMessage msg = new OrderChatMessage(order, "", priority, order.IsReport ? hull : order.TargetEntity, null, orderGiver, isNewOrder: isNewOrder);
|
||||||
GameMain.Client?.SendChatMessage(msg);
|
GameMain.Client?.SendChatMessage(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -836,7 +838,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
else if (orderGiver != null)
|
else if (orderGiver != null)
|
||||||
{
|
{
|
||||||
OrderChatMessage msg = new OrderChatMessage(order, option, priority, order?.TargetSpatialEntity ?? order?.TargetItemComponent?.Item, character, orderGiver);
|
OrderChatMessage msg = new OrderChatMessage(order, option, priority, order?.TargetSpatialEntity ?? order?.TargetItemComponent?.Item, character, orderGiver, isNewOrder: isNewOrder);
|
||||||
GameMain.Client?.SendChatMessage(msg);
|
GameMain.Client?.SendChatMessage(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2533,6 +2535,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
shortcutNodes.Add(CreateOrderNode(shortcutNodeSize, null, Point.Zero, dismissedOrderPrefab, -1));
|
shortcutNodes.Add(CreateOrderNode(shortcutNodeSize, null, Point.Zero, dismissedOrderPrefab, -1));
|
||||||
}
|
}
|
||||||
|
shortcutNodes.RemoveAll(n => n.UserData is Order o && !IsOrderAvailable(o));
|
||||||
if (shortcutNodes.Count < 1) { return; }
|
if (shortcutNodes.Count < 1) { return; }
|
||||||
shortcutCenterNode = new GUIFrame(new RectTransform(shortcutCenterNodeSize, parent: commandFrame.RectTransform, anchor: Anchor.Center), style: null)
|
shortcutCenterNode = new GUIFrame(new RectTransform(shortcutCenterNodeSize, parent: commandFrame.RectTransform, anchor: Anchor.Center), style: null)
|
||||||
{
|
{
|
||||||
@@ -2573,7 +2576,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private void CreateOrderNodes(OrderCategory orderCategory)
|
private void CreateOrderNodes(OrderCategory orderCategory)
|
||||||
{
|
{
|
||||||
var orders = Order.PrefabList.FindAll(o => o.Category == orderCategory && !o.IsReport);
|
var orders = Order.PrefabList.FindAll(o => o.Category == orderCategory && !o.IsReport && IsOrderAvailable(o));
|
||||||
Order order;
|
Order order;
|
||||||
bool disableNode;
|
bool disableNode;
|
||||||
var offsets = MathUtils.GetPointsOnCircumference(Vector2.Zero, nodeDistance,
|
var offsets = MathUtils.GetPointsOnCircumference(Vector2.Zero, nodeDistance,
|
||||||
@@ -2725,6 +2728,7 @@ namespace Barotrauma
|
|||||||
contextualOrders.Add(new OrderInfo(Order.GetPrefab(orderIdentifier), null));
|
contextualOrders.Add(new OrderInfo(Order.GetPrefab(orderIdentifier), null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
contextualOrders.RemoveAll(o => !IsOrderAvailable(o.Order));
|
||||||
var offsets = MathUtils.GetPointsOnCircumference(Vector2.Zero, nodeDistance, contextualOrders.Count, MathHelper.ToRadians(90f + 180f / contextualOrders.Count));
|
var offsets = MathUtils.GetPointsOnCircumference(Vector2.Zero, nodeDistance, contextualOrders.Count, MathHelper.ToRadians(90f + 180f / contextualOrders.Count));
|
||||||
bool disableNode = !CanCharacterBeHeard();
|
bool disableNode = !CanCharacterBeHeard();
|
||||||
for (int i = 0; i < contextualOrders.Count; i++)
|
for (int i = 0; i < contextualOrders.Count; i++)
|
||||||
@@ -3483,6 +3487,20 @@ namespace Barotrauma
|
|||||||
return character?.Info?.GetManualOrderPriority(order) ?? CharacterInfo.HighestManualOrderPriority;
|
return character?.Info?.GetManualOrderPriority(order) ?? CharacterInfo.HighestManualOrderPriority;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool IsOrderAvailable(Order order)
|
||||||
|
{
|
||||||
|
if (order == null) { return false; }
|
||||||
|
switch (order.Identifier.ToLowerInvariant())
|
||||||
|
{
|
||||||
|
case "assaultenemy":
|
||||||
|
Character character = characterContext ?? Character.Controlled;
|
||||||
|
if (character?.Submarine == null) { return false; }
|
||||||
|
return character.Submarine.GetConnectedSubs().Any(s => s.TeamID != character.TeamID);
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#region Crew Member Assignment Logic
|
#region Crew Member Assignment Logic
|
||||||
private bool CanOpenManualAssignment(GUIComponent node)
|
private bool CanOpenManualAssignment(GUIComponent node)
|
||||||
{
|
{
|
||||||
@@ -3559,7 +3577,7 @@ namespace Barotrauma
|
|||||||
bool hasLeaks = Character.Controlled.CurrentHull.ConnectedGaps.Any(g => !g.IsRoomToRoom && g.Open > 0.0f);
|
bool hasLeaks = Character.Controlled.CurrentHull.ConnectedGaps.Any(g => !g.IsRoomToRoom && g.Open > 0.0f);
|
||||||
ToggleReportButton("reportbreach", hasLeaks);
|
ToggleReportButton("reportbreach", hasLeaks);
|
||||||
|
|
||||||
bool hasIntruders = Character.CharacterList.Any(c => c.CurrentHull == Character.Controlled.CurrentHull && AIObjectiveFightIntruders.IsValidTarget(c, Character.Controlled));
|
bool hasIntruders = Character.CharacterList.Any(c => c.CurrentHull == Character.Controlled.CurrentHull && AIObjectiveFightIntruders.IsValidTarget(c, Character.Controlled, false));
|
||||||
ToggleReportButton("reportintruders", hasIntruders);
|
ToggleReportButton("reportintruders", hasIntruders);
|
||||||
|
|
||||||
foreach (GUIComponent reportButton in ReportButtonFrame.Children)
|
foreach (GUIComponent reportButton in ReportButtonFrame.Children)
|
||||||
|
|||||||
@@ -97,17 +97,16 @@ namespace Barotrauma
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// There is a server-side implementation of the method in <see cref="MultiPlayerCampaign"/>
|
/// There is a server-side implementation of the method in <see cref="MultiPlayerCampaign"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool AllowedToManageCampaign()
|
public bool AllowedToManageCampaign(ClientPermissions permissions = ClientPermissions.ManageCampaign)
|
||||||
{
|
{
|
||||||
//allow ending the round if the client has permissions, is the owner, the only client in the server,
|
//allow managing the round if the client has permissions, is the owner, the only client in the server,
|
||||||
//or if no-one has management permissions
|
//or if no-one has management permissions
|
||||||
if (GameMain.Client == null) { return true; }
|
if (GameMain.Client == null) { return true; }
|
||||||
return
|
return
|
||||||
GameMain.Client.HasPermission(ClientPermissions.ManageCampaign) ||
|
GameMain.Client.HasPermission(permissions) ||
|
||||||
GameMain.Client.ConnectedClients.Count == 1 ||
|
GameMain.Client.ConnectedClients.Count == 1 ||
|
||||||
GameMain.Client.IsServerOwner ||
|
GameMain.Client.IsServerOwner ||
|
||||||
GameMain.Client.ConnectedClients.None(c =>
|
GameMain.Client.ConnectedClients.None(c => c.InGame && (c.IsOwner || c.HasPermission(permissions)));
|
||||||
c.InGame && (c.IsOwner || c.HasPermission(ClientPermissions.ManageCampaign)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Draw(SpriteBatch spriteBatch)
|
public override void Draw(SpriteBatch spriteBatch)
|
||||||
|
|||||||
+23
-10
@@ -545,14 +545,21 @@ namespace Barotrauma
|
|||||||
foreach (PurchasedItem pi in CargoManager.ItemsInBuyCrate)
|
foreach (PurchasedItem pi in CargoManager.ItemsInBuyCrate)
|
||||||
{
|
{
|
||||||
msg.Write(pi.ItemPrefab.Identifier);
|
msg.Write(pi.ItemPrefab.Identifier);
|
||||||
msg.WriteRangedInteger(pi.Quantity, 0, 100);
|
msg.WriteRangedInteger(pi.Quantity, 0, CargoManager.MaxQuantity);
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.Write((UInt16)CargoManager.ItemsInSellFromSubCrate.Count);
|
||||||
|
foreach (PurchasedItem pi in CargoManager.ItemsInSellFromSubCrate)
|
||||||
|
{
|
||||||
|
msg.Write(pi.ItemPrefab.Identifier);
|
||||||
|
msg.WriteRangedInteger(pi.Quantity, 0, CargoManager.MaxQuantity);
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.Write((UInt16)CargoManager.PurchasedItems.Count);
|
msg.Write((UInt16)CargoManager.PurchasedItems.Count);
|
||||||
foreach (PurchasedItem pi in CargoManager.PurchasedItems)
|
foreach (PurchasedItem pi in CargoManager.PurchasedItems)
|
||||||
{
|
{
|
||||||
msg.Write(pi.ItemPrefab.Identifier);
|
msg.Write(pi.ItemPrefab.Identifier);
|
||||||
msg.WriteRangedInteger(pi.Quantity, 0, 100);
|
msg.WriteRangedInteger(pi.Quantity, 0, CargoManager.MaxQuantity);
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.Write((UInt16)CargoManager.SoldItems.Count);
|
msg.Write((UInt16)CargoManager.SoldItems.Count);
|
||||||
@@ -562,6 +569,7 @@ namespace Barotrauma
|
|||||||
msg.Write((UInt16)si.ID);
|
msg.Write((UInt16)si.ID);
|
||||||
msg.Write(si.Removed);
|
msg.Write(si.Removed);
|
||||||
msg.Write(si.SellerID);
|
msg.Write(si.SellerID);
|
||||||
|
msg.Write((byte)si.Origin);
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.Write((ushort)UpgradeManager.PurchasedUpgrades.Count);
|
msg.Write((ushort)UpgradeManager.PurchasedUpgrades.Count);
|
||||||
@@ -640,6 +648,15 @@ namespace Barotrauma
|
|||||||
buyCrateItems.Add(new PurchasedItem(ItemPrefab.Prefabs[itemPrefabIdentifier], itemQuantity));
|
buyCrateItems.Add(new PurchasedItem(ItemPrefab.Prefabs[itemPrefabIdentifier], itemQuantity));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UInt16 subSellCrateItemCount = msg.ReadUInt16();
|
||||||
|
List<PurchasedItem> subSellCrateItems = new List<PurchasedItem>();
|
||||||
|
for (int i = 0; i < subSellCrateItemCount; i++)
|
||||||
|
{
|
||||||
|
string itemPrefabIdentifier = msg.ReadString();
|
||||||
|
int itemQuantity = msg.ReadRangedInteger(0, CargoManager.MaxQuantity);
|
||||||
|
subSellCrateItems.Add(new PurchasedItem(ItemPrefab.Prefabs[itemPrefabIdentifier], itemQuantity));
|
||||||
|
}
|
||||||
|
|
||||||
UInt16 purchasedItemCount = msg.ReadUInt16();
|
UInt16 purchasedItemCount = msg.ReadUInt16();
|
||||||
List<PurchasedItem> purchasedItems = new List<PurchasedItem>();
|
List<PurchasedItem> purchasedItems = new List<PurchasedItem>();
|
||||||
for (int i = 0; i < purchasedItemCount; i++)
|
for (int i = 0; i < purchasedItemCount; i++)
|
||||||
@@ -657,7 +674,8 @@ namespace Barotrauma
|
|||||||
UInt16 id = msg.ReadUInt16();
|
UInt16 id = msg.ReadUInt16();
|
||||||
bool removed = msg.ReadBoolean();
|
bool removed = msg.ReadBoolean();
|
||||||
byte sellerId = msg.ReadByte();
|
byte sellerId = msg.ReadByte();
|
||||||
soldItems.Add(new SoldItem(ItemPrefab.Prefabs[itemPrefabIdentifier], id, removed, sellerId));
|
byte origin = msg.ReadByte();
|
||||||
|
soldItems.Add(new SoldItem(ItemPrefab.Prefabs[itemPrefabIdentifier], id, removed, sellerId, (SoldItem.SellOrigin)origin));
|
||||||
}
|
}
|
||||||
|
|
||||||
ushort pendingUpgradeCount = msg.ReadUInt16();
|
ushort pendingUpgradeCount = msg.ReadUInt16();
|
||||||
@@ -678,13 +696,9 @@ namespace Barotrauma
|
|||||||
for (int i = 0; i < purchasedItemSwapCount; i++)
|
for (int i = 0; i < purchasedItemSwapCount; i++)
|
||||||
{
|
{
|
||||||
UInt16 itemToRemoveID = msg.ReadUInt16();
|
UInt16 itemToRemoveID = msg.ReadUInt16();
|
||||||
Item itemToRemove = Entity.FindEntityByID(itemToRemoveID) as Item;
|
|
||||||
|
|
||||||
string itemToInstallIdentifier = msg.ReadString();
|
string itemToInstallIdentifier = msg.ReadString();
|
||||||
ItemPrefab itemToInstall = string.IsNullOrEmpty(itemToInstallIdentifier) ? null : ItemPrefab.Find(string.Empty, itemToInstallIdentifier);
|
ItemPrefab itemToInstall = string.IsNullOrEmpty(itemToInstallIdentifier) ? null : ItemPrefab.Find(string.Empty, itemToInstallIdentifier);
|
||||||
|
if (!(Entity.FindEntityByID(itemToRemoveID) is Item itemToRemove)) { continue; }
|
||||||
if (itemToRemove == null) { continue; }
|
|
||||||
|
|
||||||
purchasedItemSwaps.Add(new PurchasedItemSwap(itemToRemove, itemToInstall));
|
purchasedItemSwaps.Add(new PurchasedItemSwap(itemToRemove, itemToInstall));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -728,12 +742,11 @@ namespace Barotrauma
|
|||||||
campaign.Map.SelectMission(selectedMissionIndices);
|
campaign.Map.SelectMission(selectedMissionIndices);
|
||||||
campaign.Map.AllowDebugTeleport = allowDebugTeleport;
|
campaign.Map.AllowDebugTeleport = allowDebugTeleport;
|
||||||
campaign.CargoManager.SetItemsInBuyCrate(buyCrateItems);
|
campaign.CargoManager.SetItemsInBuyCrate(buyCrateItems);
|
||||||
|
campaign.CargoManager.SetItemsInSubSellCrate(subSellCrateItems);
|
||||||
campaign.CargoManager.SetPurchasedItems(purchasedItems);
|
campaign.CargoManager.SetPurchasedItems(purchasedItems);
|
||||||
campaign.CargoManager.SetSoldItems(soldItems);
|
campaign.CargoManager.SetSoldItems(soldItems);
|
||||||
if (storeBalance.HasValue) { campaign.Map.CurrentLocation.StoreCurrentBalance = storeBalance.Value; }
|
if (storeBalance.HasValue) { campaign.Map.CurrentLocation.StoreCurrentBalance = storeBalance.Value; }
|
||||||
campaign.UpgradeManager.SetPendingUpgrades(pendingUpgrades);
|
campaign.UpgradeManager.SetPendingUpgrades(pendingUpgrades);
|
||||||
campaign.UpgradeManager.PurchasedUpgrades.Clear();
|
|
||||||
|
|
||||||
campaign.UpgradeManager.PurchasedUpgrades.Clear();
|
campaign.UpgradeManager.PurchasedUpgrades.Clear();
|
||||||
foreach (var purchasedItemSwap in purchasedItemSwaps)
|
foreach (var purchasedItemSwap in purchasedItemSwaps)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1116,6 +1116,7 @@ namespace Barotrauma.Items.Components
|
|||||||
foreach (DockingPort dockingPort in DockingPort.List)
|
foreach (DockingPort dockingPort in DockingPort.List)
|
||||||
{
|
{
|
||||||
if (Level.Loaded != null && dockingPort.Item.Submarine.WorldPosition.Y > Level.Loaded.Size.Y) { continue; }
|
if (Level.Loaded != null && dockingPort.Item.Submarine.WorldPosition.Y > Level.Loaded.Size.Y) { continue; }
|
||||||
|
if (dockingPort.Item.HiddenInGame) { continue; }
|
||||||
if (dockingPort.Item.Submarine == null) { continue; }
|
if (dockingPort.Item.Submarine == null) { continue; }
|
||||||
if (dockingPort.Item.Submarine.Info.IsWreck) { continue; }
|
if (dockingPort.Item.Submarine.Info.IsWreck) { continue; }
|
||||||
// docking ports should be shown even if defined as not, if the submarine is the same as the sonar's
|
// docking ports should be shown even if defined as not, if the submarine is the same as the sonar's
|
||||||
|
|||||||
@@ -682,7 +682,7 @@ namespace Barotrauma.Items.Components
|
|||||||
enterOutpostPrompt?.Close();
|
enterOutpostPrompt?.Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (DockingSources.Any(d => d.Docked))
|
else if (connectedPorts.Any(d => d.Docked))
|
||||||
{
|
{
|
||||||
dockingButton.Text = undockText;
|
dockingButton.Text = undockText;
|
||||||
dockingContainer.Visible = true;
|
dockingContainer.Visible = true;
|
||||||
@@ -819,7 +819,7 @@ namespace Barotrauma.Items.Components
|
|||||||
Connection dockingConnection = item.Connections?.FirstOrDefault(c => c.Name == "toggle_docking");
|
Connection dockingConnection = item.Connections?.FirstOrDefault(c => c.Name == "toggle_docking");
|
||||||
if (dockingConnection != null)
|
if (dockingConnection != null)
|
||||||
{
|
{
|
||||||
connectedPorts = item.GetConnectedComponentsRecursive<DockingPort>(dockingConnection);
|
connectedPorts = item.GetConnectedComponentsRecursive<DockingPort>(dockingConnection, ignoreInactiveRelays: true);
|
||||||
}
|
}
|
||||||
checkConnectedPortsTimer = CheckConnectedPortsInterval;
|
checkConnectedPortsTimer = CheckConnectedPortsInterval;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -199,6 +199,26 @@ namespace Barotrauma.Items.Components
|
|||||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||||
{
|
{
|
||||||
snapped = msg.ReadBoolean();
|
snapped = msg.ReadBoolean();
|
||||||
|
|
||||||
|
if (!snapped)
|
||||||
|
{
|
||||||
|
UInt16 targetId = msg.ReadUInt16();
|
||||||
|
UInt16 sourceId = msg.ReadUInt16();
|
||||||
|
byte limbIndex = msg.ReadByte();
|
||||||
|
|
||||||
|
Item target = Entity.FindEntityByID(targetId) as Item;
|
||||||
|
if (target == null) { return; }
|
||||||
|
var source = Entity.FindEntityByID(sourceId);
|
||||||
|
if (source is Character sourceCharacter && limbIndex >= 0 && limbIndex < sourceCharacter.AnimController.Limbs.Length)
|
||||||
|
{
|
||||||
|
Limb sourceLimb = sourceCharacter.AnimController.Limbs[limbIndex];
|
||||||
|
Attach(sourceLimb, target);
|
||||||
|
}
|
||||||
|
else if (source is ISpatialEntity spatialEntity)
|
||||||
|
{
|
||||||
|
Attach(spatialEntity, target);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void RemoveComponentSpecific()
|
protected override void RemoveComponentSpecific()
|
||||||
|
|||||||
@@ -135,7 +135,13 @@ namespace Barotrauma.Items.Components
|
|||||||
wireSprite = overrideSprite ?? defaultWireSprite;
|
wireSprite = overrideSprite ?? defaultWireSprite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
|
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
|
||||||
|
{
|
||||||
|
Draw(spriteBatch, editing, Vector2.Zero, itemDepth);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Draw(SpriteBatch spriteBatch, bool editing, Vector2 offset, float itemDepth = -1)
|
||||||
{
|
{
|
||||||
if (sections.Count == 0 && !IsActive || Hidden)
|
if (sections.Count == 0 && !IsActive || Hidden)
|
||||||
{
|
{
|
||||||
@@ -156,6 +162,8 @@ namespace Barotrauma.Items.Components
|
|||||||
drawOffset = sub.DrawPosition + sub.HiddenSubPosition;
|
drawOffset = sub.DrawPosition + sub.HiddenSubPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drawOffset += offset;
|
||||||
|
|
||||||
float baseDepth = UseSpriteDepth ? item.SpriteDepth : wireSprite.Depth;
|
float baseDepth = UseSpriteDepth ? item.SpriteDepth : wireSprite.Depth;
|
||||||
float depth = item.IsSelected ? 0.0f : SubEditorScreen.IsWiringMode() ? 0.02f : baseDepth + (item.ID % 100) * 0.000001f;// item.GetDrawDepth(wireSprite.Depth, wireSprite);
|
float depth = item.IsSelected ? 0.0f : SubEditorScreen.IsWiringMode() ? 0.02f : baseDepth + (item.ID % 100) * 0.000001f;// item.GetDrawDepth(wireSprite.Depth, wireSprite);
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using Microsoft.Xna.Framework.Graphics;
|
using Microsoft.Xna.Framework.Graphics;
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Barotrauma
|
namespace Barotrauma
|
||||||
{
|
{
|
||||||
@@ -208,9 +207,9 @@ namespace Barotrauma
|
|||||||
"watersplash",
|
"watersplash",
|
||||||
(Submarine == null ? pos : pos + Submarine.Position) - Vector2.UnitY * Rand.Range(0.0f, 10.0f),
|
(Submarine == null ? pos : pos + Submarine.Position) - Vector2.UnitY * Rand.Range(0.0f, 10.0f),
|
||||||
velocity, 0, flowTargetHull);
|
velocity, 0, flowTargetHull);
|
||||||
|
|
||||||
if (particle != null)
|
if (particle != null)
|
||||||
{
|
{
|
||||||
|
if (particle.CurrentHull == null) { GameMain.ParticleManager.RemoveParticle(particle); }
|
||||||
particle.Size *= Math.Min(Math.Abs(flowForce.X / 500.0f), 5.0f);
|
particle.Size *= Math.Min(Math.Abs(flowForce.X / 500.0f), 5.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -238,9 +237,9 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (Math.Sign(flowTargetHull.Rect.Y - rect.Y) != Math.Sign(lerpedFlowForce.Y)) return;
|
if (Math.Sign(flowTargetHull.Rect.Y - rect.Y) != Math.Sign(lerpedFlowForce.Y)) { return; }
|
||||||
|
|
||||||
float particlesPerSec = open * rect.Width * 0.3f * particleAmountMultiplier;
|
float particlesPerSec = Math.Max(open * rect.Width * 0.3f * particleAmountMultiplier, 20.0f);
|
||||||
float emitInterval = 1.0f / particlesPerSec;
|
float emitInterval = 1.0f / particlesPerSec;
|
||||||
while (particleTimer > emitInterval)
|
while (particleTimer > emitInterval)
|
||||||
{
|
{
|
||||||
@@ -252,17 +251,21 @@ namespace Barotrauma
|
|||||||
if (flowTargetHull.WaterVolume < flowTargetHull.Volume * 0.95f)
|
if (flowTargetHull.WaterVolume < flowTargetHull.Volume * 0.95f)
|
||||||
{
|
{
|
||||||
var splash = GameMain.ParticleManager.CreateParticle(
|
var splash = GameMain.ParticleManager.CreateParticle(
|
||||||
"watersplash",
|
"watersplash",
|
||||||
Submarine == null ? pos : pos + Submarine.Position,
|
Submarine == null ? pos : pos + Submarine.Position,
|
||||||
velocity, 0, FlowTargetHull);
|
velocity, 0, FlowTargetHull);
|
||||||
if (splash != null) splash.Size = splash.Size * MathHelper.Clamp(rect.Width / 50.0f, 0.8f, 4.0f);
|
if (splash != null)
|
||||||
|
{
|
||||||
|
if (splash.CurrentHull == null) { GameMain.ParticleManager.RemoveParticle(splash); }
|
||||||
|
splash.Size *= MathHelper.Clamp(rect.Width / 50.0f, 1.5f, 4.0f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (Math.Abs(flowForce.Y) > 190.0f && Rand.Range(0.0f, 1.0f) < 0.3f && flowTargetHull.WaterVolume > flowTargetHull.Volume * 0.1f)
|
if (Math.Abs(flowForce.Y) > 190.0f && Rand.Range(0.0f, 1.0f) < 0.3f && flowTargetHull.WaterVolume > flowTargetHull.Volume * 0.1f)
|
||||||
{
|
{
|
||||||
GameMain.ParticleManager.CreateParticle(
|
GameMain.ParticleManager.CreateParticle(
|
||||||
"bubbles",
|
"bubbles",
|
||||||
Submarine == null ? pos : pos + Submarine.Position,
|
Submarine == null ? pos : pos + Submarine.Position,
|
||||||
flowForce / 2.0f, 0, FlowTargetHull);
|
flowForce / 2.0f, 0, FlowTargetHull);
|
||||||
}
|
}
|
||||||
particleTimer -= emitInterval;
|
particleTimer -= emitInterval;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,19 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private double lastAmbientLightEditTime;
|
private double lastAmbientLightEditTime;
|
||||||
|
|
||||||
|
private float drawSurface;
|
||||||
|
|
||||||
|
public float DrawSurface
|
||||||
|
{
|
||||||
|
get { return drawSurface; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (Math.Abs(drawSurface - value) < 0.00001f) { return; }
|
||||||
|
drawSurface = MathHelper.Clamp(value, rect.Y - rect.Height, rect.Y);
|
||||||
|
update = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override bool SelectableInEditor
|
public override bool SelectableInEditor
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -138,8 +151,15 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void UpdateProjSpecific(float deltaTime, Camera cam)
|
partial void UpdateProjSpecific(float deltaTime, Camera _)
|
||||||
{
|
{
|
||||||
|
float waterDepth = WaterVolume / rect.Width;
|
||||||
|
//interpolate the position of the rendered surface towards the "target surface"
|
||||||
|
drawSurface = Math.Max(MathHelper.Lerp(
|
||||||
|
drawSurface,
|
||||||
|
rect.Y - rect.Height + waterDepth,
|
||||||
|
deltaTime * 10.0f), rect.Y - rect.Height);
|
||||||
|
|
||||||
if (GameMain.Client != null)
|
if (GameMain.Client != null)
|
||||||
{
|
{
|
||||||
serverUpdateDelay -= deltaTime;
|
serverUpdateDelay -= deltaTime;
|
||||||
@@ -171,55 +191,56 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IdFreed)
|
/*if (waterVolume < 1.0f) { return; }
|
||||||
{
|
|
||||||
if (EditWater)
|
|
||||||
{
|
|
||||||
Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
|
|
||||||
if (Submarine.RectContains(WorldRect, position))
|
|
||||||
{
|
|
||||||
if (PlayerInput.PrimaryMouseButtonHeld())
|
|
||||||
{
|
|
||||||
WaterVolume += 1500.0f;
|
|
||||||
networkUpdatePending = true;
|
|
||||||
serverUpdateDelay = 0.5f;
|
|
||||||
}
|
|
||||||
else if (PlayerInput.SecondaryMouseButtonHeld())
|
|
||||||
{
|
|
||||||
WaterVolume -= 1500.0f;
|
|
||||||
networkUpdatePending = true;
|
|
||||||
serverUpdateDelay = 0.5f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (EditFire)
|
|
||||||
{
|
|
||||||
Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
|
|
||||||
if (Submarine.RectContains(WorldRect, position))
|
|
||||||
{
|
|
||||||
if (PlayerInput.PrimaryMouseButtonClicked())
|
|
||||||
{
|
|
||||||
new FireSource(position, this, isNetworkMessage: true);
|
|
||||||
networkUpdatePending = true;
|
|
||||||
serverUpdateDelay = 0.5f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (waterVolume < 1.0f) { return; }
|
|
||||||
for (int i = 1; i < waveY.Length - 1; i++)
|
for (int i = 1; i < waveY.Length - 1; i++)
|
||||||
{
|
{
|
||||||
float maxDelta = Math.Max(Math.Abs(rightDelta[i]), Math.Abs(leftDelta[i]));
|
float maxDelta = Math.Max(Math.Abs(rightDelta[i]), Math.Abs(leftDelta[i]));
|
||||||
if (maxDelta > 1.0f && maxDelta > Rand.Range(1.0f, 10.0f))
|
if (maxDelta > 0.1f && maxDelta > Rand.Range(0.1f, 10.0f))
|
||||||
{
|
{
|
||||||
var particlePos = new Vector2(rect.X + WaveWidth * i, surface + waveY[i]);
|
var particlePos = new Vector2(rect.X + WaveWidth * i, surface + waveY[i]);
|
||||||
if (Submarine != null) particlePos += Submarine.Position;
|
if (Submarine != null) { particlePos += Submarine.Position; }
|
||||||
|
|
||||||
GameMain.ParticleManager.CreateParticle("mist",
|
GameMain.ParticleManager.CreateParticle("mist",
|
||||||
particlePos,
|
particlePos,
|
||||||
new Vector2(0.0f, -50.0f), 0.0f, this);
|
new Vector2(0.0f, -50.0f), 0.0f, this);
|
||||||
}
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void UpdateCheats(float deltaTime, Camera cam)
|
||||||
|
{
|
||||||
|
bool primaryMouseButtonHeld = PlayerInput.PrimaryMouseButtonHeld();
|
||||||
|
bool secondaryMouseButtonHeld = PlayerInput.SecondaryMouseButtonHeld();
|
||||||
|
if (!primaryMouseButtonHeld && !secondaryMouseButtonHeld) { return; }
|
||||||
|
|
||||||
|
Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
|
||||||
|
Hull hull = FindHull(position);
|
||||||
|
|
||||||
|
if (hull == null || hull.IdFreed) { return; }
|
||||||
|
if (EditWater)
|
||||||
|
{
|
||||||
|
if (primaryMouseButtonHeld)
|
||||||
|
{
|
||||||
|
hull.WaterVolume += 100000.0f * deltaTime;
|
||||||
|
hull.networkUpdatePending = true;
|
||||||
|
hull.serverUpdateDelay = 0.5f;
|
||||||
|
}
|
||||||
|
else if (secondaryMouseButtonHeld)
|
||||||
|
{
|
||||||
|
hull.WaterVolume -= 100000.0f * deltaTime;
|
||||||
|
hull.networkUpdatePending = true;
|
||||||
|
hull.serverUpdateDelay = 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (EditFire)
|
||||||
|
{
|
||||||
|
if (primaryMouseButtonHeld)
|
||||||
|
{
|
||||||
|
new FireSource(position, hull, isNetworkMessage: true);
|
||||||
|
hull.networkUpdatePending = true;
|
||||||
|
hull.serverUpdateDelay = 0.5f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -194,7 +194,11 @@ namespace Barotrauma
|
|||||||
private void RemoveFogOfWar(Location location, bool removeFromAdjacentLocations = true)
|
private void RemoveFogOfWar(Location location, bool removeFromAdjacentLocations = true)
|
||||||
{
|
{
|
||||||
if (location == null) { return; }
|
if (location == null) { return; }
|
||||||
Vector2 mapTileSize = mapTiles[0, 0].size * generationParams.MapTileScale;
|
|
||||||
|
var mapTile = generationParams.MapTiles.Values.FirstOrDefault()?.FirstOrDefault();
|
||||||
|
if (mapTile == null) { return; }
|
||||||
|
|
||||||
|
Vector2 mapTileSize = mapTile.size * generationParams.MapTileScale;
|
||||||
int startX = (int)Math.Max(Math.Floor(location.MapPosition.X / mapTileSize.X - 0.25f), 0);
|
int startX = (int)Math.Max(Math.Floor(location.MapPosition.X / mapTileSize.X - 0.25f), 0);
|
||||||
int startY = (int)Math.Max(Math.Floor(location.MapPosition.Y / mapTileSize.Y - 0.25f), 0);
|
int startY = (int)Math.Max(Math.Floor(location.MapPosition.Y / mapTileSize.Y - 0.25f), 0);
|
||||||
int endX = (int)Math.Min(Math.Floor(location.MapPosition.X / mapTileSize.X + 0.25f), mapTiles.GetLength(0));
|
int endX = (int)Math.Min(Math.Floor(location.MapPosition.X / mapTileSize.X + 0.25f), mapTiles.GetLength(0));
|
||||||
|
|||||||
@@ -768,34 +768,40 @@ namespace Barotrauma
|
|||||||
switch (e)
|
switch (e)
|
||||||
{
|
{
|
||||||
case Item item:
|
case Item item:
|
||||||
{
|
{
|
||||||
if (item.FlippedX && item.Prefab.CanSpriteFlipX) spriteEffects ^= SpriteEffects.FlipHorizontally;
|
if (item.FlippedX && item.Prefab.CanSpriteFlipX) { spriteEffects ^= SpriteEffects.FlipHorizontally; }
|
||||||
if (item.flippedY && item.Prefab.CanSpriteFlipY) spriteEffects ^= SpriteEffects.FlipVertically;
|
if (item.flippedY && item.Prefab.CanSpriteFlipY) { spriteEffects ^= SpriteEffects.FlipVertically; }
|
||||||
break;
|
var wire = item.GetComponent<Wire>();
|
||||||
}
|
if (wire != null && !wire.Item.body.Enabled)
|
||||||
|
{
|
||||||
|
wire.Draw(spriteBatch, editing: false, new Vector2(moveAmount.X, -moveAmount.Y));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case Structure structure:
|
case Structure structure:
|
||||||
{
|
{
|
||||||
if (structure.FlippedX && structure.Prefab.CanSpriteFlipX) spriteEffects ^= SpriteEffects.FlipHorizontally;
|
if (structure.FlippedX && structure.Prefab.CanSpriteFlipX) { spriteEffects ^= SpriteEffects.FlipHorizontally; }
|
||||||
if (structure.flippedY && structure.Prefab.CanSpriteFlipY) spriteEffects ^= SpriteEffects.FlipVertically;
|
if (structure.flippedY && structure.Prefab.CanSpriteFlipY) { spriteEffects ^= SpriteEffects.FlipVertically; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case WayPoint wayPoint:
|
case WayPoint wayPoint:
|
||||||
{
|
{
|
||||||
Vector2 drawPos = e.WorldPosition;
|
Vector2 drawPos = e.WorldPosition;
|
||||||
drawPos.Y = -drawPos.Y;
|
drawPos.Y = -drawPos.Y;
|
||||||
drawPos += moveAmount;
|
drawPos += moveAmount;
|
||||||
wayPoint.Draw(spriteBatch, drawPos);
|
wayPoint.Draw(spriteBatch, drawPos);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
case LinkedSubmarine linkedSub:
|
case LinkedSubmarine linkedSub:
|
||||||
{
|
{
|
||||||
var ma = moveAmount;
|
var ma = moveAmount;
|
||||||
ma.Y = -ma.Y;
|
ma.Y = -ma.Y;
|
||||||
Vector2 lPos = linkedSub.Position;
|
Vector2 lPos = linkedSub.Position;
|
||||||
lPos += ma;
|
lPos += ma;
|
||||||
linkedSub.Draw(spriteBatch, lPos, alpha: 0.5f);
|
linkedSub.Draw(spriteBatch, lPos, alpha: 0.5f);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
e.prefab?.DrawPlacing(spriteBatch,
|
e.prefab?.DrawPlacing(spriteBatch,
|
||||||
new Rectangle(e.WorldRect.Location + new Point((int)moveAmount.X, (int)-moveAmount.Y), e.WorldRect.Size), e.Scale, spriteEffects);
|
new Rectangle(e.WorldRect.Location + new Point((int)moveAmount.X, (int)-moveAmount.Y), e.WorldRect.Size), e.Scale, spriteEffects);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Barotrauma
|
namespace Barotrauma
|
||||||
@@ -91,7 +91,7 @@ namespace Barotrauma
|
|||||||
public void CreateSpecsWindow(GUIListBox parent, ScalableFont font, bool includeTitle = true, bool includeClass = true, bool includeDescription = false)
|
public void CreateSpecsWindow(GUIListBox parent, ScalableFont font, bool includeTitle = true, bool includeClass = true, bool includeDescription = false)
|
||||||
{
|
{
|
||||||
float leftPanelWidth = 0.6f;
|
float leftPanelWidth = 0.6f;
|
||||||
float rightPanelWidth = 0.4f;
|
float rightPanelWidth = 0.4f / leftPanelWidth;
|
||||||
string className = !HasTag(SubmarineTag.Shuttle) ? TextManager.Get($"submarineclass.{SubmarineClass}") : TextManager.Get("shuttle");
|
string className = !HasTag(SubmarineTag.Shuttle) ? TextManager.Get($"submarineclass.{SubmarineClass}") : TextManager.Get("shuttle");
|
||||||
|
|
||||||
int classHeight = (int)GUI.SubHeadingFont.MeasureString(className).Y;
|
int classHeight = (int)GUI.SubHeadingFont.MeasureString(className).Y;
|
||||||
@@ -110,6 +110,17 @@ namespace Barotrauma
|
|||||||
submarineClassText = new GUITextBlock(new RectTransform(new Point(leftPanelWidthInt, classHeight), parent.Content.RectTransform), className, textAlignment: Alignment.CenterLeft, font: GUI.SubHeadingFont) { CanBeFocused = false };
|
submarineClassText = new GUITextBlock(new RectTransform(new Point(leftPanelWidthInt, classHeight), parent.Content.RectTransform), className, textAlignment: Alignment.CenterLeft, font: GUI.SubHeadingFont) { CanBeFocused = false };
|
||||||
submarineClassText.RectTransform.MinSize = new Point(0, (int)submarineClassText.TextSize.Y);
|
submarineClassText.RectTransform.MinSize = new Point(0, (int)submarineClassText.TextSize.Y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Price > 0)
|
||||||
|
{
|
||||||
|
var priceText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), parent.Content.RectTransform),
|
||||||
|
TextManager.Get("subeditor.price"), textAlignment: Alignment.TopLeft, font: font, wrap: true)
|
||||||
|
{ CanBeFocused = false };
|
||||||
|
new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), priceText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
|
||||||
|
TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", Price)), textAlignment: Alignment.TopLeft, font: font, wrap: true)
|
||||||
|
{ CanBeFocused = false };
|
||||||
|
}
|
||||||
|
|
||||||
Vector2 realWorldDimensions = Dimensions * Physics.DisplayToRealWorldRatio;
|
Vector2 realWorldDimensions = Dimensions * Physics.DisplayToRealWorldRatio;
|
||||||
if (realWorldDimensions != Vector2.Zero)
|
if (realWorldDimensions != Vector2.Zero)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2870,19 +2870,16 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
public void SendCampaignState()
|
public void SendCampaignState()
|
||||||
{
|
{
|
||||||
MultiPlayerCampaign campaign = GameMain.GameSession.GameMode as MultiPlayerCampaign;
|
if (!(GameMain.GameSession.GameMode is MultiPlayerCampaign campaign))
|
||||||
if (campaign == null)
|
|
||||||
{
|
{
|
||||||
DebugConsole.ThrowError("Failed send campaign state to the server (no campaign active).\n" + Environment.StackTrace.CleanupStackTrace());
|
DebugConsole.ThrowError("Failed send campaign state to the server (no campaign active).\n" + Environment.StackTrace.CleanupStackTrace());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
IWriteMessage msg = new WriteOnlyMessage();
|
IWriteMessage msg = new WriteOnlyMessage();
|
||||||
msg.Write((byte)ClientPacketHeader.SERVER_COMMAND);
|
msg.Write((byte)ClientPacketHeader.SERVER_COMMAND);
|
||||||
msg.Write((UInt16)ClientPermissions.ManageCampaign);
|
msg.Write((UInt16)ClientPermissions.ManageCampaign);
|
||||||
campaign.ClientWrite(msg);
|
campaign.ClientWrite(msg);
|
||||||
msg.Write((byte)ServerNetObject.END_OF_MESSAGE);
|
msg.Write((byte)ServerNetObject.END_OF_MESSAGE);
|
||||||
|
|
||||||
clientPeer.Send(msg, DeliveryMethod.Reliable);
|
clientPeer.Send(msg, DeliveryMethod.Reliable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -196,6 +196,19 @@ namespace Barotrauma.Particles
|
|||||||
particles[particleCount] = swap;
|
particles[particleCount] = swap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void RemoveParticle(Particle particle)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < particleCount; i++)
|
||||||
|
{
|
||||||
|
if (particles[i] == particle)
|
||||||
|
{
|
||||||
|
RemoveParticle(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Update(float deltaTime)
|
public void Update(float deltaTime)
|
||||||
{
|
{
|
||||||
MaxParticles = GameMain.Config.ParticleLimit;
|
MaxParticles = GameMain.Config.ParticleLimit;
|
||||||
|
|||||||
+5
-1
@@ -244,7 +244,11 @@ namespace Barotrauma
|
|||||||
|
|
||||||
foreach (string saveFile in saveFiles)
|
foreach (string saveFile in saveFiles)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(saveFile)) { continue; }
|
if (string.IsNullOrEmpty(saveFile))
|
||||||
|
{
|
||||||
|
DebugConsole.AddWarning("Error when updating campaign load menu: path to a save file was empty.\n" + Environment.StackTrace);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
string fileName = saveFile;
|
string fileName = saveFile;
|
||||||
string subName = "";
|
string subName = "";
|
||||||
|
|||||||
+35
-24
@@ -500,29 +500,34 @@ namespace Barotrauma.CharacterEditor
|
|||||||
int index = 0;
|
int index = 0;
|
||||||
bool isSwimming = character.AnimController.ForceSelectAnimationType == AnimationType.SwimFast || character.AnimController.ForceSelectAnimationType == AnimationType.SwimSlow;
|
bool isSwimming = character.AnimController.ForceSelectAnimationType == AnimationType.SwimFast || character.AnimController.ForceSelectAnimationType == AnimationType.SwimSlow;
|
||||||
bool isMovingFast = character.AnimController.ForceSelectAnimationType == AnimationType.Run || character.AnimController.ForceSelectAnimationType == AnimationType.SwimFast;
|
bool isMovingFast = character.AnimController.ForceSelectAnimationType == AnimationType.Run || character.AnimController.ForceSelectAnimationType == AnimationType.SwimFast;
|
||||||
if (isMovingFast)
|
if (character.AnimController.CanWalk)
|
||||||
{
|
{
|
||||||
if (isSwimming || !character.AnimController.CanWalk)
|
if (isMovingFast)
|
||||||
{
|
{
|
||||||
index = !character.AnimController.CanWalk ? (int)AnimationType.SwimFast : (int)AnimationType.SwimSlow;
|
if (isSwimming)
|
||||||
|
{
|
||||||
|
index = 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
index = (int)AnimationType.Walk;
|
if (isSwimming)
|
||||||
|
{
|
||||||
|
index = 3;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
index = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
index -= 1;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (isSwimming || !character.AnimController.CanWalk)
|
index = isMovingFast ? 0 : 1;
|
||||||
{
|
|
||||||
index = !character.AnimController.CanWalk ? (int)AnimationType.SwimSlow : (int)AnimationType.SwimFast;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
index = (int)AnimationType.Run;
|
|
||||||
}
|
|
||||||
index -= 1;
|
|
||||||
}
|
}
|
||||||
if (animSelection.SelectedIndex != index)
|
if (animSelection.SelectedIndex != index)
|
||||||
{
|
{
|
||||||
@@ -536,17 +541,13 @@ namespace Barotrauma.CharacterEditor
|
|||||||
bool isSwimming = character.AnimController.ForceSelectAnimationType == AnimationType.SwimFast || character.AnimController.ForceSelectAnimationType == AnimationType.SwimSlow;
|
bool isSwimming = character.AnimController.ForceSelectAnimationType == AnimationType.SwimFast || character.AnimController.ForceSelectAnimationType == AnimationType.SwimSlow;
|
||||||
if (isSwimming)
|
if (isSwimming)
|
||||||
{
|
{
|
||||||
animSelection.Select((int)AnimationType.Walk - 1);
|
animSelection.Select(0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
animSelection.Select((int)AnimationType.SwimSlow - 1);
|
animSelection.Select(2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (PlayerInput.KeyHit(Keys.F))
|
|
||||||
{
|
|
||||||
SetToggle(freezeToggle, !freezeToggle.Selected);
|
|
||||||
}
|
|
||||||
if (PlayerInput.SecondaryMouseButtonClicked() || PlayerInput.KeyHit(Keys.Escape))
|
if (PlayerInput.SecondaryMouseButtonClicked() || PlayerInput.KeyHit(Keys.Escape))
|
||||||
{
|
{
|
||||||
bool reset = false;
|
bool reset = false;
|
||||||
@@ -853,6 +854,16 @@ namespace Barotrauma.CharacterEditor
|
|||||||
{
|
{
|
||||||
DrawRagdoll(spriteBatch, (float)deltaTime);
|
DrawRagdoll(spriteBatch, (float)deltaTime);
|
||||||
}
|
}
|
||||||
|
// Mouth
|
||||||
|
Limb head = character.AnimController.GetLimb(LimbType.Head);
|
||||||
|
if (head != null && character.CanEat && selectedLimbs.Contains(head))
|
||||||
|
{
|
||||||
|
var mouthPos = character.AnimController.GetMouthPosition();
|
||||||
|
if (mouthPos.HasValue)
|
||||||
|
{
|
||||||
|
ShapeExtensions.DrawPoint(spriteBatch, SimToScreen(mouthPos.Value), GUI.Style.Red, size: 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (showSpritesheet)
|
if (showSpritesheet)
|
||||||
{
|
{
|
||||||
DrawSpritesheetEditor(spriteBatch, (float)deltaTime);
|
DrawSpritesheetEditor(spriteBatch, (float)deltaTime);
|
||||||
@@ -2606,13 +2617,13 @@ namespace Barotrauma.CharacterEditor
|
|||||||
{
|
{
|
||||||
animSelection.AddItem(AnimationType.Walk.ToString(), AnimationType.Walk);
|
animSelection.AddItem(AnimationType.Walk.ToString(), AnimationType.Walk);
|
||||||
animSelection.AddItem(AnimationType.Run.ToString(), AnimationType.Run);
|
animSelection.AddItem(AnimationType.Run.ToString(), AnimationType.Run);
|
||||||
if (character.IsHumanoid)
|
|
||||||
{
|
|
||||||
animSelection.AddItem(AnimationType.Crouch.ToString(), AnimationType.Crouch);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
animSelection.AddItem(AnimationType.SwimSlow.ToString(), AnimationType.SwimSlow);
|
animSelection.AddItem(AnimationType.SwimSlow.ToString(), AnimationType.SwimSlow);
|
||||||
animSelection.AddItem(AnimationType.SwimFast.ToString(), AnimationType.SwimFast);
|
animSelection.AddItem(AnimationType.SwimFast.ToString(), AnimationType.SwimFast);
|
||||||
|
if (character.AnimController.CanWalk && character.IsHumanoid)
|
||||||
|
{
|
||||||
|
animSelection.AddItem(AnimationType.Crouch.ToString(), AnimationType.Crouch);
|
||||||
|
}
|
||||||
if (character.AnimController.ForceSelectAnimationType == AnimationType.NotDefined)
|
if (character.AnimController.ForceSelectAnimationType == AnimationType.NotDefined)
|
||||||
{
|
{
|
||||||
animSelection.SelectItem(character.AnimController.CanWalk ? AnimationType.Walk : AnimationType.SwimSlow);
|
animSelection.SelectItem(character.AnimController.CanWalk ? AnimationType.Walk : AnimationType.SwimSlow);
|
||||||
|
|||||||
@@ -717,7 +717,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public void QuickStart(bool fixedSeed = false, string sub = null, float difficulty = 40, LevelGenerationParams levelGenerationParams = null)
|
public void QuickStart(bool fixedSeed = false, string sub = null, float difficulty = 50, LevelGenerationParams levelGenerationParams = null)
|
||||||
{
|
{
|
||||||
if (fixedSeed)
|
if (fixedSeed)
|
||||||
{
|
{
|
||||||
@@ -1396,7 +1396,7 @@ namespace Barotrauma
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ForbiddenWordFilter.IsForbidden(name, out string forbiddenWord))
|
if (isPublicBox.Selected && ForbiddenWordFilter.IsForbidden(name, out string forbiddenWord))
|
||||||
{
|
{
|
||||||
var msgBox = new GUIMessageBox("",
|
var msgBox = new GUIMessageBox("",
|
||||||
TextManager.GetWithVariables("forbiddenservernameverification", new string[] { "[forbiddenword]", "[servername]" }, new string[] { forbiddenWord, name }),
|
TextManager.GetWithVariables("forbiddenservernameverification", new string[] { "[forbiddenword]", "[servername]" }, new string[] { forbiddenWord, name }),
|
||||||
|
|||||||
@@ -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.16.1.0</Version>
|
<Version>0.16.2.0</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2020</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.16.1.0</Version>
|
<Version>0.16.2.0</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2020</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.16.1.0</Version>
|
<Version>0.16.2.0</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2020</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.16.1.0</Version>
|
<Version>0.16.2.0</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2020</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.16.1.0</Version>
|
<Version>0.16.2.0</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
<AssemblyName>DedicatedServer</AssemblyName>
|
<AssemblyName>DedicatedServer</AssemblyName>
|
||||||
|
|||||||
@@ -1841,7 +1841,7 @@ namespace Barotrauma
|
|||||||
(Client client, Vector2 cursorWorldPos, string[] args) =>
|
(Client client, Vector2 cursorWorldPos, string[] args) =>
|
||||||
{
|
{
|
||||||
Vector2 explosionPos = cursorWorldPos;
|
Vector2 explosionPos = cursorWorldPos;
|
||||||
float range = 500, force = 10, damage = 50, structureDamage = 10, itemDamage = 100, empStrength = 0.0f, ballastFloraStrength = 50f;
|
float range = 500, force = 10, damage = 50, structureDamage = 20, itemDamage = 100, empStrength = 0.0f, ballastFloraStrength = 50f;
|
||||||
if (args.Length > 0) float.TryParse(args[0], out range);
|
if (args.Length > 0) float.TryParse(args[0], out range);
|
||||||
if (args.Length > 1) float.TryParse(args[1], out force);
|
if (args.Length > 1) float.TryParse(args[1], out force);
|
||||||
if (args.Length > 2) float.TryParse(args[2], out damage);
|
if (args.Length > 2) float.TryParse(args[2], out damage);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using Barotrauma.Extensions;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Barotrauma
|
namespace Barotrauma
|
||||||
@@ -23,10 +24,10 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
// Check all the prices before starting the transaction
|
// Check all the prices before starting the transaction
|
||||||
// to make sure the modifiers stay the same for the whole transaction
|
// to make sure the modifiers stay the same for the whole transaction
|
||||||
Dictionary<ItemPrefab, int> sellValues = GetSellValuesAtCurrentLocation(itemsToBuy.Select(i => i.ItemPrefab));
|
var sellValues = GetSellValuesAtCurrentLocation(itemsToBuy.Select(i => i.ItemPrefab));
|
||||||
foreach (SoldItem item in itemsToBuy)
|
foreach (var item in itemsToBuy)
|
||||||
{
|
{
|
||||||
var itemValue = sellValues[item.ItemPrefab];
|
int itemValue = sellValues[item.ItemPrefab];
|
||||||
if (Location.StoreCurrentBalance < itemValue || item.Removed) { continue; }
|
if (Location.StoreCurrentBalance < itemValue || item.Removed) { continue; }
|
||||||
Location.StoreCurrentBalance += itemValue;
|
Location.StoreCurrentBalance += itemValue;
|
||||||
campaign.Money -= itemValue;
|
campaign.Money -= itemValue;
|
||||||
@@ -36,17 +37,29 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public void SellItems(List<SoldItem> itemsToSell)
|
public void SellItems(List<SoldItem> itemsToSell)
|
||||||
{
|
{
|
||||||
|
bool canAddToRemoveQueue = (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer) && Entity.Spawner != null;
|
||||||
|
IEnumerable<Item> sellableItemsInSub = Enumerable.Empty<Item>();
|
||||||
|
if (canAddToRemoveQueue && itemsToSell.Any(i => i.Origin == SoldItem.SellOrigin.Submarine && i.ID == Entity.NullEntityID && !i.Removed))
|
||||||
|
{
|
||||||
|
sellableItemsInSub = GetSellableItemsFromSub();
|
||||||
|
}
|
||||||
// Check all the prices before starting the transaction
|
// Check all the prices before starting the transaction
|
||||||
// to make sure the modifiers stay the same for the whole transaction
|
// to make sure the modifiers stay the same for the whole transaction
|
||||||
Dictionary<ItemPrefab, int> sellValues = GetSellValuesAtCurrentLocation(itemsToSell.Select(i => i.ItemPrefab));
|
var sellValues = GetSellValuesAtCurrentLocation(itemsToSell.Select(i => i.ItemPrefab));
|
||||||
var canAddToRemoveQueue = (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer) && Entity.Spawner != null;
|
foreach (var item in itemsToSell)
|
||||||
foreach (SoldItem item in itemsToSell)
|
|
||||||
{
|
{
|
||||||
var itemValue = sellValues[item.ItemPrefab];
|
int itemValue = sellValues[item.ItemPrefab];
|
||||||
|
|
||||||
// check if the store can afford the item and if the item hasn't been removed already
|
// check if the store can afford the item and if the item hasn't been removed already
|
||||||
if (Location.StoreCurrentBalance < itemValue || item.Removed) { continue; }
|
if (Location.StoreCurrentBalance < itemValue || item.Removed) { continue; }
|
||||||
|
// Server determines the items that are sold from the sub in multiplayer
|
||||||
|
if (item.Origin == SoldItem.SellOrigin.Submarine && item.ID == Entity.NullEntityID && !item.Removed)
|
||||||
|
{
|
||||||
|
var matchingItem = sellableItemsInSub.FirstOrDefault(i => !i.Removed && i.Prefab == item.ItemPrefab &&
|
||||||
|
itemsToSell.None(itemToSell => itemToSell.ItemPrefab == i.Prefab && itemToSell.ID == i.ID));
|
||||||
|
// This is a failsafe for scenarios where a client is trying to sell more items than there's available on the sub
|
||||||
|
if (matchingItem == null) { continue; }
|
||||||
|
item.SetItemId(matchingItem.ID);
|
||||||
|
}
|
||||||
if (!item.Removed && canAddToRemoveQueue && Entity.FindEntityByID(item.ID) is Item entity)
|
if (!item.Removed && canAddToRemoveQueue && Entity.FindEntityByID(item.ID) is Item entity)
|
||||||
{
|
{
|
||||||
item.Removed = true;
|
item.Removed = true;
|
||||||
|
|||||||
+140
-102
@@ -166,16 +166,15 @@ namespace Barotrauma
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// There is a client-side implementation of the method in <see cref="CampaignMode"/>
|
/// There is a client-side implementation of the method in <see cref="CampaignMode"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool AllowedToManageCampaign(Client client)
|
public bool AllowedToManageCampaign(Client client, ClientPermissions permissions = ClientPermissions.ManageCampaign)
|
||||||
{
|
{
|
||||||
//allow ending the round if the client has permissions, is the owner, or the only client in the server,
|
//allow managing the campaign if the client has permissions, is the owner, or the only client in the server,
|
||||||
//or if no-one has management permissions
|
//or if no-one has management permissions
|
||||||
return
|
return
|
||||||
client.HasPermission(ClientPermissions.ManageCampaign) ||
|
client.HasPermission(permissions) ||
|
||||||
GameMain.Server.ConnectedClients.Count == 1 ||
|
GameMain.Server.ConnectedClients.Count == 1 ||
|
||||||
IsOwner(client) ||
|
IsOwner(client) ||
|
||||||
GameMain.Server.ConnectedClients.None(c =>
|
GameMain.Server.ConnectedClients.None(c => c.InGame && (IsOwner(c) || c.HasPermission(permissions)));
|
||||||
c.InGame && (IsOwner(c) || c.HasPermission(ClientPermissions.ManageCampaign)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveExperiencePoints(Client client)
|
public void SaveExperiencePoints(Client client)
|
||||||
@@ -562,14 +561,21 @@ namespace Barotrauma
|
|||||||
foreach (PurchasedItem pi in CargoManager.ItemsInBuyCrate)
|
foreach (PurchasedItem pi in CargoManager.ItemsInBuyCrate)
|
||||||
{
|
{
|
||||||
msg.Write(pi.ItemPrefab.Identifier);
|
msg.Write(pi.ItemPrefab.Identifier);
|
||||||
msg.WriteRangedInteger(pi.Quantity, 0, 100);
|
msg.WriteRangedInteger(pi.Quantity, 0, CargoManager.MaxQuantity);
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.Write((UInt16)CargoManager.ItemsInSellFromSubCrate.Count);
|
||||||
|
foreach (PurchasedItem pi in CargoManager.ItemsInSellFromSubCrate)
|
||||||
|
{
|
||||||
|
msg.Write(pi.ItemPrefab.Identifier);
|
||||||
|
msg.WriteRangedInteger(pi.Quantity, 0, CargoManager.MaxQuantity);
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.Write((UInt16)CargoManager.PurchasedItems.Count);
|
msg.Write((UInt16)CargoManager.PurchasedItems.Count);
|
||||||
foreach (PurchasedItem pi in CargoManager.PurchasedItems)
|
foreach (PurchasedItem pi in CargoManager.PurchasedItems)
|
||||||
{
|
{
|
||||||
msg.Write(pi.ItemPrefab.Identifier);
|
msg.Write(pi.ItemPrefab.Identifier);
|
||||||
msg.WriteRangedInteger(pi.Quantity, 0, 100);
|
msg.WriteRangedInteger(pi.Quantity, 0, CargoManager.MaxQuantity);
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.Write((UInt16)CargoManager.SoldItems.Count);
|
msg.Write((UInt16)CargoManager.SoldItems.Count);
|
||||||
@@ -579,6 +585,7 @@ namespace Barotrauma
|
|||||||
msg.Write((UInt16)si.ID);
|
msg.Write((UInt16)si.ID);
|
||||||
msg.Write(si.Removed);
|
msg.Write(si.Removed);
|
||||||
msg.Write(si.SellerID);
|
msg.Write(si.SellerID);
|
||||||
|
msg.Write((byte)si.Origin);
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.Write((ushort)UpgradeManager.PendingUpgrades.Count);
|
msg.Write((ushort)UpgradeManager.PendingUpgrades.Count);
|
||||||
@@ -633,6 +640,15 @@ namespace Barotrauma
|
|||||||
buyCrateItems.Add(new PurchasedItem(ItemPrefab.Prefabs[itemPrefabIdentifier], itemQuantity));
|
buyCrateItems.Add(new PurchasedItem(ItemPrefab.Prefabs[itemPrefabIdentifier], itemQuantity));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UInt16 subSellCrateItemCount = msg.ReadUInt16();
|
||||||
|
List<PurchasedItem> subSellCrateItems = new List<PurchasedItem>();
|
||||||
|
for (int i = 0; i < subSellCrateItemCount; i++)
|
||||||
|
{
|
||||||
|
string itemPrefabIdentifier = msg.ReadString();
|
||||||
|
int itemQuantity = msg.ReadRangedInteger(0, CargoManager.MaxQuantity);
|
||||||
|
subSellCrateItems.Add(new PurchasedItem(ItemPrefab.Prefabs[itemPrefabIdentifier], itemQuantity));
|
||||||
|
}
|
||||||
|
|
||||||
UInt16 purchasedItemCount = msg.ReadUInt16();
|
UInt16 purchasedItemCount = msg.ReadUInt16();
|
||||||
List<PurchasedItem> purchasedItems = new List<PurchasedItem>();
|
List<PurchasedItem> purchasedItems = new List<PurchasedItem>();
|
||||||
for (int i = 0; i < purchasedItemCount; i++)
|
for (int i = 0; i < purchasedItemCount; i++)
|
||||||
@@ -650,7 +666,8 @@ namespace Barotrauma
|
|||||||
UInt16 id = msg.ReadUInt16();
|
UInt16 id = msg.ReadUInt16();
|
||||||
bool removed = msg.ReadBoolean();
|
bool removed = msg.ReadBoolean();
|
||||||
byte sellerId = msg.ReadByte();
|
byte sellerId = msg.ReadByte();
|
||||||
soldItems.Add(new SoldItem(ItemPrefab.Prefabs[itemPrefabIdentifier], id, removed, sellerId));
|
byte origin = msg.ReadByte();
|
||||||
|
soldItems.Add(new SoldItem(ItemPrefab.Prefabs[itemPrefabIdentifier], id, removed, sellerId, (SoldItem.SellOrigin)origin));
|
||||||
}
|
}
|
||||||
|
|
||||||
ushort purchasedUpgradeCount = msg.ReadUInt16();
|
ushort purchasedUpgradeCount = msg.ReadUInt16();
|
||||||
@@ -674,125 +691,146 @@ namespace Barotrauma
|
|||||||
for (int i = 0; i < purchasedItemSwapCount; i++)
|
for (int i = 0; i < purchasedItemSwapCount; i++)
|
||||||
{
|
{
|
||||||
UInt16 itemToRemoveID = msg.ReadUInt16();
|
UInt16 itemToRemoveID = msg.ReadUInt16();
|
||||||
Item itemToRemove = Entity.FindEntityByID(itemToRemoveID) as Item;
|
|
||||||
|
|
||||||
string itemToInstallIdentifier = msg.ReadString();
|
string itemToInstallIdentifier = msg.ReadString();
|
||||||
ItemPrefab itemToInstall = string.IsNullOrEmpty(itemToInstallIdentifier) ? null : ItemPrefab.Find(string.Empty, itemToInstallIdentifier);
|
ItemPrefab itemToInstall = string.IsNullOrEmpty(itemToInstallIdentifier) ? null : ItemPrefab.Find(string.Empty, itemToInstallIdentifier);
|
||||||
|
if (!(Entity.FindEntityByID(itemToRemoveID) is Item itemToRemove)) { continue; }
|
||||||
if (itemToRemove == null) { continue; }
|
|
||||||
|
|
||||||
purchasedItemSwaps.Add(new PurchasedItemSwap(itemToRemove, itemToInstall));
|
purchasedItemSwaps.Add(new PurchasedItemSwap(itemToRemove, itemToInstall));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!AllowedToManageCampaign(sender))
|
bool allowedToManageCampaign = AllowedToManageCampaign(sender);
|
||||||
|
if (AllowedToManageCampaign(sender))
|
||||||
{
|
{
|
||||||
DebugConsole.ThrowError("Client \"" + sender.Name + "\" does not have a permission to manage the campaign");
|
Location location = Map.CurrentLocation;
|
||||||
return;
|
int hullRepairCost = location?.GetAdjustedMechanicalCost(HullRepairCost) ?? HullRepairCost;
|
||||||
|
int itemRepairCost = location?.GetAdjustedMechanicalCost(ItemRepairCost) ?? ItemRepairCost;
|
||||||
|
int shuttleRetrieveCost = location?.GetAdjustedMechanicalCost(ShuttleReplaceCost) ?? ShuttleReplaceCost;
|
||||||
|
if (purchasedHullRepairs != this.PurchasedHullRepairs)
|
||||||
|
{
|
||||||
|
if (purchasedHullRepairs && Money >= hullRepairCost)
|
||||||
|
{
|
||||||
|
this.PurchasedHullRepairs = true;
|
||||||
|
Money -= hullRepairCost;
|
||||||
|
GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service, "hullrepairs");
|
||||||
|
}
|
||||||
|
else if (!purchasedHullRepairs)
|
||||||
|
{
|
||||||
|
this.PurchasedHullRepairs = false;
|
||||||
|
Money += hullRepairCost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (purchasedItemRepairs != this.PurchasedItemRepairs)
|
||||||
|
{
|
||||||
|
if (purchasedItemRepairs && Money >= itemRepairCost)
|
||||||
|
{
|
||||||
|
this.PurchasedItemRepairs = true;
|
||||||
|
Money -= itemRepairCost;
|
||||||
|
GameAnalyticsManager.AddMoneySpentEvent(itemRepairCost, GameAnalyticsManager.MoneySink.Service, "devicerepairs");
|
||||||
|
}
|
||||||
|
else if (!purchasedItemRepairs)
|
||||||
|
{
|
||||||
|
this.PurchasedItemRepairs = false;
|
||||||
|
Money += itemRepairCost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (purchasedLostShuttles != this.PurchasedLostShuttles)
|
||||||
|
{
|
||||||
|
if (GameMain.GameSession?.SubmarineInfo != null &&
|
||||||
|
GameMain.GameSession.SubmarineInfo.LeftBehindSubDockingPortOccupied)
|
||||||
|
{
|
||||||
|
GameMain.Server.SendDirectChatMessage(TextManager.FormatServerMessage("ReplaceShuttleDockingPortOccupied"), sender, ChatMessageType.MessageBox);
|
||||||
|
}
|
||||||
|
else if (purchasedLostShuttles && Money >= shuttleRetrieveCost)
|
||||||
|
{
|
||||||
|
this.PurchasedLostShuttles = true;
|
||||||
|
Money -= shuttleRetrieveCost;
|
||||||
|
GameAnalyticsManager.AddMoneySpentEvent(shuttleRetrieveCost, GameAnalyticsManager.MoneySink.Service, "retrieveshuttle");
|
||||||
|
}
|
||||||
|
else if (!purchasedItemRepairs)
|
||||||
|
{
|
||||||
|
this.PurchasedLostShuttles = false;
|
||||||
|
Money += shuttleRetrieveCost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (currentLocIndex < Map.Locations.Count && Map.AllowDebugTeleport)
|
||||||
|
{
|
||||||
|
Map.SetLocation(currentLocIndex);
|
||||||
|
}
|
||||||
|
Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex);
|
||||||
|
if (Map.SelectedLocation == null) { Map.SelectRandomLocation(preferUndiscovered: true); }
|
||||||
|
if (Map.SelectedConnection != null) { Map.SelectMission(selectedMissionIndices); }
|
||||||
|
CheckTooManyMissions(Map.CurrentLocation, sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
Location location = Map.CurrentLocation;
|
bool allowedToUseStore = AllowedToManageCampaign(sender, ClientPermissions.CampaignStore);
|
||||||
int hullRepairCost = location?.GetAdjustedMechanicalCost(HullRepairCost) ?? HullRepairCost;
|
if (allowedToManageCampaign || allowedToUseStore || AllowedToManageCampaign(sender, ClientPermissions.BuyItems))
|
||||||
int itemRepairCost = location?.GetAdjustedMechanicalCost(ItemRepairCost) ?? ItemRepairCost;
|
|
||||||
int shuttleRetrieveCost = location?.GetAdjustedMechanicalCost(ShuttleReplaceCost) ?? ShuttleReplaceCost;
|
|
||||||
|
|
||||||
if (purchasedHullRepairs != this.PurchasedHullRepairs)
|
|
||||||
{
|
{
|
||||||
if (purchasedHullRepairs && Money >= hullRepairCost)
|
var currentBuyCrateItems = new List<PurchasedItem>(CargoManager.ItemsInBuyCrate);
|
||||||
{
|
currentBuyCrateItems.ForEach(i => CargoManager.ModifyItemQuantityInBuyCrate(i.ItemPrefab, -i.Quantity));
|
||||||
this.PurchasedHullRepairs = true;
|
buyCrateItems.ForEach(i => CargoManager.ModifyItemQuantityInBuyCrate(i.ItemPrefab, i.Quantity));
|
||||||
Money -= hullRepairCost;
|
CargoManager.SellBackPurchasedItems(new List<PurchasedItem>(CargoManager.PurchasedItems));
|
||||||
GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service, "hullrepairs");
|
CargoManager.PurchaseItems(purchasedItems, false);
|
||||||
}
|
|
||||||
else if (!purchasedHullRepairs)
|
|
||||||
{
|
|
||||||
this.PurchasedHullRepairs = false;
|
|
||||||
Money += hullRepairCost;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (purchasedItemRepairs != this.PurchasedItemRepairs)
|
|
||||||
{
|
|
||||||
if (purchasedItemRepairs && Money >= itemRepairCost)
|
|
||||||
{
|
|
||||||
this.PurchasedItemRepairs = true;
|
|
||||||
Money -= itemRepairCost;
|
|
||||||
GameAnalyticsManager.AddMoneySpentEvent(itemRepairCost, GameAnalyticsManager.MoneySink.Service, "devicerepairs");
|
|
||||||
}
|
|
||||||
else if (!purchasedItemRepairs)
|
|
||||||
{
|
|
||||||
this.PurchasedItemRepairs = false;
|
|
||||||
Money += itemRepairCost;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (purchasedLostShuttles != this.PurchasedLostShuttles)
|
|
||||||
{
|
|
||||||
if (GameMain.GameSession?.SubmarineInfo != null &&
|
|
||||||
GameMain.GameSession.SubmarineInfo.LeftBehindSubDockingPortOccupied)
|
|
||||||
{
|
|
||||||
GameMain.Server.SendDirectChatMessage(TextManager.FormatServerMessage("ReplaceShuttleDockingPortOccupied"), sender, ChatMessageType.MessageBox);
|
|
||||||
}
|
|
||||||
else if (purchasedLostShuttles && Money >= shuttleRetrieveCost)
|
|
||||||
{
|
|
||||||
this.PurchasedLostShuttles = true;
|
|
||||||
Money -= shuttleRetrieveCost;
|
|
||||||
GameAnalyticsManager.AddMoneySpentEvent(shuttleRetrieveCost, GameAnalyticsManager.MoneySink.Service, "retrieveshuttle");
|
|
||||||
}
|
|
||||||
else if (!purchasedItemRepairs)
|
|
||||||
{
|
|
||||||
this.PurchasedLostShuttles = false;
|
|
||||||
Money += shuttleRetrieveCost;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentLocIndex < Map.Locations.Count && Map.AllowDebugTeleport)
|
bool allowedToSellSubItems = AllowedToManageCampaign(sender, ClientPermissions.SellSubItems);
|
||||||
|
if (allowedToManageCampaign || allowedToUseStore || allowedToSellSubItems)
|
||||||
{
|
{
|
||||||
Map.SetLocation(currentLocIndex);
|
var currentSubSellCrateItems = new List<PurchasedItem>(CargoManager.ItemsInSellFromSubCrate);
|
||||||
|
currentSubSellCrateItems.ForEach(i => CargoManager.ModifyItemQuantityInSubSellCrate(i.ItemPrefab, -i.Quantity));
|
||||||
|
subSellCrateItems.ForEach(i => CargoManager.ModifyItemQuantityInSubSellCrate(i.ItemPrefab, i.Quantity));
|
||||||
}
|
}
|
||||||
|
|
||||||
Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex);
|
bool allowedToSellInventoryItems = AllowedToManageCampaign(sender, ClientPermissions.SellInventoryItems);
|
||||||
if (Map.SelectedLocation == null) { Map.SelectRandomLocation(preferUndiscovered: true); }
|
if (allowedToManageCampaign || allowedToUseStore || (allowedToSellInventoryItems && allowedToSellSubItems))
|
||||||
if (Map.SelectedConnection != null) { Map.SelectMission(selectedMissionIndices); }
|
|
||||||
|
|
||||||
CheckTooManyMissions(Map.CurrentLocation, sender);
|
|
||||||
|
|
||||||
List<PurchasedItem> currentBuyCrateItems = new List<PurchasedItem>(CargoManager.ItemsInBuyCrate);
|
|
||||||
currentBuyCrateItems.ForEach(i => CargoManager.ModifyItemQuantityInBuyCrate(i.ItemPrefab, -i.Quantity));
|
|
||||||
buyCrateItems.ForEach(i => CargoManager.ModifyItemQuantityInBuyCrate(i.ItemPrefab, i.Quantity));
|
|
||||||
|
|
||||||
CargoManager.SellBackPurchasedItems(new List<PurchasedItem>(CargoManager.PurchasedItems));
|
|
||||||
CargoManager.PurchaseItems(purchasedItems, false);
|
|
||||||
|
|
||||||
// for some reason CargoManager.SoldItem is never cleared by the server, I've added a check to SellItems that ignores all
|
|
||||||
// sold items that are removed so they should be discarded on the next message
|
|
||||||
CargoManager.BuyBackSoldItems(new List<SoldItem>(CargoManager.SoldItems));
|
|
||||||
CargoManager.SellItems(soldItems);
|
|
||||||
|
|
||||||
foreach (var (prefab, category, _) in purchasedUpgrades)
|
|
||||||
{
|
{
|
||||||
UpgradeManager.PurchaseUpgrade(prefab, category);
|
// for some reason CargoManager.SoldItem is never cleared by the server, I've added a check to SellItems that ignores all
|
||||||
|
// sold items that are removed so they should be discarded on the next message
|
||||||
// unstable logging
|
CargoManager.BuyBackSoldItems(new List<SoldItem>(CargoManager.SoldItems));
|
||||||
int price = prefab.Price.GetBuyprice(UpgradeManager.GetUpgradeLevel(prefab, category), Map?.CurrentLocation);
|
CargoManager.SellItems(soldItems);
|
||||||
int level = UpgradeManager.GetUpgradeLevel(prefab, category);
|
|
||||||
GameServer.Log($"SERVER: Purchased level {level} {category.Identifier}.{prefab.Identifier} for {price}", ServerLog.MessageType.ServerMessage);
|
|
||||||
}
|
}
|
||||||
|
else if (allowedToSellInventoryItems || allowedToSellSubItems)
|
||||||
foreach (var purchasedItemSwap in purchasedItemSwaps)
|
|
||||||
{
|
{
|
||||||
if (purchasedItemSwap.ItemToInstall == null)
|
if (allowedToSellInventoryItems)
|
||||||
{
|
{
|
||||||
UpgradeManager.CancelItemSwap(purchasedItemSwap.ItemToRemove);
|
CargoManager.BuyBackSoldItems(new List<SoldItem>(CargoManager.SoldItems.Where(i => i.Origin == SoldItem.SellOrigin.Character)));
|
||||||
|
soldItems.RemoveAll(i => i.Origin != SoldItem.SellOrigin.Character);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
UpgradeManager.PurchaseItemSwap(purchasedItemSwap.ItemToRemove, purchasedItemSwap.ItemToInstall);
|
CargoManager.BuyBackSoldItems(new List<SoldItem>(CargoManager.SoldItems.Where(i => i.Origin == SoldItem.SellOrigin.Submarine)));
|
||||||
|
soldItems.RemoveAll(i => i.Origin != SoldItem.SellOrigin.Submarine);
|
||||||
}
|
}
|
||||||
|
CargoManager.SellItems(soldItems);
|
||||||
}
|
}
|
||||||
foreach (Item item in Item.ItemList)
|
|
||||||
|
if (allowedToManageCampaign)
|
||||||
{
|
{
|
||||||
if (item.PendingItemSwap != null && !purchasedItemSwaps.Any(it => it.ItemToRemove == item))
|
foreach (var (prefab, category, _) in purchasedUpgrades)
|
||||||
{
|
{
|
||||||
UpgradeManager.CancelItemSwap(item);
|
UpgradeManager.PurchaseUpgrade(prefab, category);
|
||||||
item.PendingItemSwap = null;
|
|
||||||
|
// unstable logging
|
||||||
|
int price = prefab.Price.GetBuyprice(UpgradeManager.GetUpgradeLevel(prefab, category), Map?.CurrentLocation);
|
||||||
|
int level = UpgradeManager.GetUpgradeLevel(prefab, category);
|
||||||
|
GameServer.Log($"SERVER: Purchased level {level} {category.Identifier}.{prefab.Identifier} for {price}", ServerLog.MessageType.ServerMessage);
|
||||||
|
}
|
||||||
|
foreach (var purchasedItemSwap in purchasedItemSwaps)
|
||||||
|
{
|
||||||
|
if (purchasedItemSwap.ItemToInstall == null)
|
||||||
|
{
|
||||||
|
UpgradeManager.CancelItemSwap(purchasedItemSwap.ItemToRemove);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UpgradeManager.PurchaseItemSwap(purchasedItemSwap.ItemToRemove, purchasedItemSwap.ItemToInstall);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (Item item in Item.ItemList)
|
||||||
|
{
|
||||||
|
if (item.PendingItemSwap != null && !purchasedItemSwaps.Any(it => it.ItemToRemove == item))
|
||||||
|
{
|
||||||
|
UpgradeManager.CancelItemSwap(item);
|
||||||
|
item.PendingItemSwap = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,26 @@ namespace Barotrauma.Items.Components
|
|||||||
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
|
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
|
||||||
{
|
{
|
||||||
msg.Write(Snapped);
|
msg.Write(Snapped);
|
||||||
|
|
||||||
|
if (!Snapped)
|
||||||
|
{
|
||||||
|
msg.Write(target?.ID ?? Entity.NullEntityID);
|
||||||
|
if (source is Entity entity && !entity.Removed)
|
||||||
|
{
|
||||||
|
msg.Write(entity?.ID ?? Entity.NullEntityID);
|
||||||
|
msg.Write((byte)0);
|
||||||
|
}
|
||||||
|
else if (source is Limb limb && limb.character != null && !limb.character.Removed)
|
||||||
|
{
|
||||||
|
msg.Write(limb.character?.ID ?? Entity.NullEntityID);
|
||||||
|
msg.Write((byte)limb.character.AnimController.Limbs.IndexOf(limb));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
msg.Write(Entity.NullEntityID);
|
||||||
|
msg.Write((byte)0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,22 +98,6 @@ namespace Barotrauma
|
|||||||
case NetEntityEvent.Type.AssignCampaignInteraction:
|
case NetEntityEvent.Type.AssignCampaignInteraction:
|
||||||
msg.Write((byte)CampaignInteractionType);
|
msg.Write((byte)CampaignInteractionType);
|
||||||
break;
|
break;
|
||||||
case NetEntityEvent.Type.Treatment:
|
|
||||||
{
|
|
||||||
ItemComponent targetComponent = (ItemComponent)extraData[1];
|
|
||||||
ActionType actionType = (ActionType)extraData[2];
|
|
||||||
ushort targetID = (ushort)extraData[3];
|
|
||||||
Limb targetLimb = (Limb)extraData[4];
|
|
||||||
|
|
||||||
Character targetCharacter = FindEntityByID(targetID) as Character;
|
|
||||||
byte targetLimbIndex = targetLimb != null && targetCharacter != null ? (byte)Array.IndexOf(targetCharacter.AnimController.Limbs, targetLimb) : (byte)255;
|
|
||||||
|
|
||||||
msg.Write((byte)components.IndexOf(targetComponent));
|
|
||||||
msg.WriteRangedInteger((int)actionType, 0, Enum.GetValues(typeof(ActionType)).Length - 1);
|
|
||||||
msg.Write(targetID);
|
|
||||||
msg.Write(targetLimbIndex);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case NetEntityEvent.Type.ApplyStatusEffect:
|
case NetEntityEvent.Type.ApplyStatusEffect:
|
||||||
{
|
{
|
||||||
ActionType actionType = (ActionType)extraData[1];
|
ActionType actionType = (ActionType)extraData[1];
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ namespace Barotrauma
|
|||||||
//don't create updates if all clients are very far from the hull
|
//don't create updates if all clients are very far from the hull
|
||||||
float hullUpdateDistanceSqr = NetConfig.HullUpdateDistance * NetConfig.HullUpdateDistance;
|
float hullUpdateDistanceSqr = NetConfig.HullUpdateDistance * NetConfig.HullUpdateDistance;
|
||||||
if (!GameMain.Server.ConnectedClients.Any(c =>
|
if (!GameMain.Server.ConnectedClients.Any(c =>
|
||||||
c.Character != null &&
|
c.Character != null &&
|
||||||
Vector2.DistanceSquared(c.Character.WorldPosition, WorldPosition) < hullUpdateDistanceSqr))
|
Vector2.DistanceSquared(c.Character.WorldPosition, WorldPosition) < hullUpdateDistanceSqr))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ namespace Barotrauma.Networking
|
|||||||
string orderOption = orderMessageInfo.OrderOption ??
|
string orderOption = orderMessageInfo.OrderOption ??
|
||||||
(orderMessageInfo.OrderOptionIndex == null || orderMessageInfo.OrderOptionIndex < 0 || orderMessageInfo.OrderOptionIndex >= orderPrefab.Options.Length ?
|
(orderMessageInfo.OrderOptionIndex == null || orderMessageInfo.OrderOptionIndex < 0 || orderMessageInfo.OrderOptionIndex >= orderPrefab.Options.Length ?
|
||||||
"" : orderPrefab.Options[orderMessageInfo.OrderOptionIndex.Value]);
|
"" : orderPrefab.Options[orderMessageInfo.OrderOptionIndex.Value]);
|
||||||
orderMsg = new OrderChatMessage(orderPrefab, orderOption, orderMessageInfo.Priority, orderTargetPosition ?? orderTargetEntity as ISpatialEntity, orderTargetCharacter, c.Character)
|
orderMsg = new OrderChatMessage(orderPrefab, orderOption, orderMessageInfo.Priority, orderTargetPosition ?? orderTargetEntity as ISpatialEntity, orderTargetCharacter, c.Character, isNewOrder: orderMessageInfo.IsNewOrder)
|
||||||
{
|
{
|
||||||
WallSectionIndex = wallSectionIndex
|
WallSectionIndex = wallSectionIndex
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3154,11 +3154,11 @@ namespace Barotrauma.Networking
|
|||||||
//too far to hear the msg -> don't send
|
//too far to hear the msg -> don't send
|
||||||
if (!client.Character.CanHearCharacter(message.Sender)) { continue; }
|
if (!client.Character.CanHearCharacter(message.Sender)) { continue; }
|
||||||
}
|
}
|
||||||
SendDirectChatMessage(new OrderChatMessage(message.Order, message.OrderOption, message.OrderPriority, message.TargetEntity, message.TargetCharacter, message.Sender), client);
|
SendDirectChatMessage(new OrderChatMessage(message.Order, message.OrderOption, message.OrderPriority, message.TargetEntity, message.TargetCharacter, message.Sender, isNewOrder: message.IsNewOrder), client);
|
||||||
}
|
}
|
||||||
if (!string.IsNullOrWhiteSpace(message.Text))
|
if (!string.IsNullOrWhiteSpace(message.Text))
|
||||||
{
|
{
|
||||||
AddChatMessage(new OrderChatMessage(message.Order, message.OrderOption, message.OrderPriority, message.Text, message.TargetEntity, message.TargetCharacter, message.Sender));
|
AddChatMessage(new OrderChatMessage(message.Order, message.OrderOption, message.OrderPriority, message.Text, message.TargetEntity, message.TargetCharacter, message.Sender, isNewOrder: message.IsNewOrder));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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.16.1.0</Version>
|
<Version>0.16.2.0</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
<AssemblyName>DedicatedServer</AssemblyName>
|
<AssemblyName>DedicatedServer</AssemblyName>
|
||||||
|
|||||||
@@ -77,8 +77,8 @@
|
|||||||
<Character file="Content/Characters/Coelanth/Coelanth.xml" />
|
<Character file="Content/Characters/Coelanth/Coelanth.xml" />
|
||||||
<Character file="Content/Characters/Crawler/Crawler.xml" />
|
<Character file="Content/Characters/Crawler/Crawler.xml" />
|
||||||
<Character file="Content/Characters/Crawlerhusk/Crawlerhusk.xml" />
|
<Character file="Content/Characters/Crawlerhusk/Crawlerhusk.xml" />
|
||||||
|
<Character file="Content/Characters/Crawlerbroodmother/Crawlerbroodmother.xml" />
|
||||||
<Character file="Content/Characters/Endworm/Endworm.xml" />
|
<Character file="Content/Characters/Endworm/Endworm.xml" />
|
||||||
<Character file="Content/Characters/Variants/Doomworm.xml" />
|
|
||||||
<Character file="Content/Characters/Fractalguardian/Fractalguardian.xml" />
|
<Character file="Content/Characters/Fractalguardian/Fractalguardian.xml" />
|
||||||
<Character file="Content/Characters/Fractalguardian2/Fractalguardian2.xml" />
|
<Character file="Content/Characters/Fractalguardian2/Fractalguardian2.xml" />
|
||||||
<Character file="Content/Characters/Swarmfeeder/Swarmfeeder.xml" />
|
<Character file="Content/Characters/Swarmfeeder/Swarmfeeder.xml" />
|
||||||
@@ -113,6 +113,7 @@
|
|||||||
<Character file="Content/Characters/Terminalcell/Terminalcell.xml" />
|
<Character file="Content/Characters/Terminalcell/Terminalcell.xml" />
|
||||||
<Character file="Content/Characters/Watcher/Watcher.xml" />
|
<Character file="Content/Characters/Watcher/Watcher.xml" />
|
||||||
<Character file="Content/Characters/Spineling/Spineling.xml" />
|
<Character file="Content/Characters/Spineling/Spineling.xml" />
|
||||||
|
<Character file="Content/Characters/Spineling_giant/Spineling_giant.xml" />
|
||||||
<Character file="Content/Characters/Variants/Swarmcrawler.xml" />
|
<Character file="Content/Characters/Variants/Swarmcrawler.xml" />
|
||||||
<Character file="Content/Characters/Variants/Moloch_m.xml" />
|
<Character file="Content/Characters/Variants/Moloch_m.xml" />
|
||||||
<Character file="Content/Characters/Variants/Molochblack_m.xml" />
|
<Character file="Content/Characters/Variants/Molochblack_m.xml" />
|
||||||
@@ -121,7 +122,9 @@
|
|||||||
<Character file="Content/Characters/Variants/Crawler_hatchling/Crawler_hatchling.xml" />
|
<Character file="Content/Characters/Variants/Crawler_hatchling/Crawler_hatchling.xml" />
|
||||||
<Character file="Content/Characters/Variants/Mudraptor_hatchling/Mudraptor_hatchling.xml" />
|
<Character file="Content/Characters/Variants/Mudraptor_hatchling/Mudraptor_hatchling.xml" />
|
||||||
<Character file="Content/Characters/Variants/Mudraptor_passive/Mudraptor_passive.xml" />
|
<Character file="Content/Characters/Variants/Mudraptor_passive/Mudraptor_passive.xml" />
|
||||||
|
<Character file="Content/Characters/Variants/Mudraptor_veteran/Mudraptor_veteran.xml" />
|
||||||
<Character file="Content/Characters/Variants/Tigerthresher_hatchling/Tigerthresher_hatchling.xml" />
|
<Character file="Content/Characters/Variants/Tigerthresher_hatchling/Tigerthresher_hatchling.xml" />
|
||||||
|
<Character file="Content/Characters/Variants/Doomworm.xml" />
|
||||||
<MapCreature file="Content/Items/Gardening/ballastflora.xml" />
|
<MapCreature file="Content/Items/Gardening/ballastflora.xml" />
|
||||||
<Wreck file="Content/Map/Wrecks/Dugong_Wrecked.sub" />
|
<Wreck file="Content/Map/Wrecks/Dugong_Wrecked.sub" />
|
||||||
<Wreck file="Content/Map/Wrecks/Kastrull_Wrecked.sub" />
|
<Wreck file="Content/Map/Wrecks/Kastrull_Wrecked.sub" />
|
||||||
@@ -145,6 +148,8 @@
|
|||||||
<Submarine file="Submarines/Azimuth.sub" />
|
<Submarine file="Submarines/Azimuth.sub" />
|
||||||
<Submarine file="Submarines/R-29.sub" />
|
<Submarine file="Submarines/R-29.sub" />
|
||||||
<Submarine file="Submarines/Barsuk.sub" />
|
<Submarine file="Submarines/Barsuk.sub" />
|
||||||
|
<Submarine file="Submarines/Herja.sub" />
|
||||||
|
<Submarine file="Submarines/Winterhalter.sub" />
|
||||||
<Text file="Content/Texts/English/EnglishVanilla.xml" />
|
<Text file="Content/Texts/English/EnglishVanilla.xml" />
|
||||||
<Text file="Content/Texts/German/GermanVanilla.xml" />
|
<Text file="Content/Texts/German/GermanVanilla.xml" />
|
||||||
<Text file="Content/Texts/French/FrenchVanilla.xml" />
|
<Text file="Content/Texts/French/FrenchVanilla.xml" />
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
karmadecaythreshold="50"
|
karmadecaythreshold="50"
|
||||||
karmaincrease="0.05"
|
karmaincrease="0.05"
|
||||||
karmaincreasethreshold="50"
|
karmaincreasethreshold="50"
|
||||||
structurerepairkarmaincrease="0.01"
|
structurerepairkarmaincrease="0.005"
|
||||||
structuredamagekarmadecrease="0.05"
|
structuredamagekarmadecrease="0.025"
|
||||||
itemrepairkarmaincrease="0.03"
|
itemrepairkarmaincrease="0.03"
|
||||||
reactoroverheatkarmadecrease="0.5"
|
reactoroverheatkarmadecrease="0.5"
|
||||||
reactormeltdownkarmadecrease="30"
|
reactormeltdownkarmadecrease="30"
|
||||||
@@ -35,8 +35,8 @@
|
|||||||
karmadecaythreshold="50"
|
karmadecaythreshold="50"
|
||||||
karmaincrease="0.04"
|
karmaincrease="0.04"
|
||||||
karmaincreasethreshold="45"
|
karmaincreasethreshold="45"
|
||||||
structurerepairkarmaincrease="0.01"
|
structurerepairkarmaincrease="0.005"
|
||||||
structuredamagekarmadecrease="0.2"
|
structuredamagekarmadecrease="0.1"
|
||||||
itemrepairkarmaincrease="0.03"
|
itemrepairkarmaincrease="0.03"
|
||||||
reactoroverheatkarmadecrease="1.0"
|
reactoroverheatkarmadecrease="1.0"
|
||||||
reactormeltdownkarmadecrease="35"
|
reactormeltdownkarmadecrease="35"
|
||||||
|
|||||||
@@ -531,8 +531,7 @@ namespace Barotrauma
|
|||||||
selectedTargetingParams = targetingParams;
|
selectedTargetingParams = targetingParams;
|
||||||
State = targetingParams.State;
|
State = targetingParams.State;
|
||||||
}
|
}
|
||||||
if (SelectedAiTarget?.Entity != null &&
|
if ((LatchOntoAI == null || !LatchOntoAI.IsAttached || wallTarget != null) &&
|
||||||
(LatchOntoAI == null || !LatchOntoAI.IsAttached || wallTarget != null) &&
|
|
||||||
(State == AIState.Attack || State == AIState.Aggressive || State == AIState.PassiveAggressive))
|
(State == AIState.Attack || State == AIState.Aggressive || State == AIState.PassiveAggressive))
|
||||||
{
|
{
|
||||||
UpdateWallTarget(requiredHoleCount);
|
UpdateWallTarget(requiredHoleCount);
|
||||||
@@ -646,7 +645,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
run = isBeingChased ? true : squaredDistance < Math.Pow(halfReactDistance, 2);
|
run = isBeingChased || squaredDistance < Math.Pow(halfReactDistance, 2);
|
||||||
State = AIState.Escape;
|
State = AIState.Escape;
|
||||||
avoidTimer = AIParams.AvoidTime * 0.5f * Rand.Range(0.75f, 1.25f);
|
avoidTimer = AIParams.AvoidTime * 0.5f * Rand.Range(0.75f, 1.25f);
|
||||||
}
|
}
|
||||||
@@ -674,7 +673,8 @@ namespace Barotrauma
|
|||||||
Character c = a.Character;
|
Character c = a.Character;
|
||||||
if (c.IsDead || c.Removed) { return false; }
|
if (c.IsDead || c.Removed) { return false; }
|
||||||
if (!Character.IsFriendly(c)) { return true; }
|
if (!Character.IsFriendly(c)) { return true; }
|
||||||
// Only apply the threshold to friendly characters
|
if (!c.IsPlayer) { return false; }
|
||||||
|
// Only apply the threshold to players
|
||||||
return a.Damage >= selectedTargetingParams.Threshold;
|
return a.Damage >= selectedTargetingParams.Threshold;
|
||||||
}
|
}
|
||||||
Character attacker = targetCharacter.LastAttackers.LastOrDefault(IsValid)?.Character;
|
Character attacker = targetCharacter.LastAttackers.LastOrDefault(IsValid)?.Character;
|
||||||
@@ -686,6 +686,8 @@ namespace Barotrauma
|
|||||||
// Attack the character that attacked the target we are protecting
|
// Attack the character that attacked the target we are protecting
|
||||||
ChangeTargetState(attacker, AIState.Attack, selectedTargetingParams.Priority * 2);
|
ChangeTargetState(attacker, AIState.Attack, selectedTargetingParams.Priority * 2);
|
||||||
SelectTarget(attacker.AiTarget);
|
SelectTarget(attacker.AiTarget);
|
||||||
|
State = AIState.Attack;
|
||||||
|
UpdateWallTarget(requiredHoleCount);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2270,6 +2272,10 @@ namespace Barotrauma
|
|||||||
if (SelectedAiTarget == null || SelectedAiTarget.Entity == null || SelectedAiTarget.Entity.Removed)
|
if (SelectedAiTarget == null || SelectedAiTarget.Entity == null || SelectedAiTarget.Entity.Removed)
|
||||||
{
|
{
|
||||||
State = AIState.Idle;
|
State = AIState.Idle;
|
||||||
|
if (Character.SelectedCharacter != null)
|
||||||
|
{
|
||||||
|
Character.DeselectCharacter();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (SelectedAiTarget.Entity is Character || SelectedAiTarget.Entity is Item)
|
if (SelectedAiTarget.Entity is Character || SelectedAiTarget.Entity is Item)
|
||||||
@@ -2285,7 +2291,16 @@ namespace Barotrauma
|
|||||||
Vector2 attackSimPosition = Character.GetRelativeSimPosition(SelectedAiTarget.Entity);
|
Vector2 attackSimPosition = Character.GetRelativeSimPosition(SelectedAiTarget.Entity);
|
||||||
Vector2 limbDiff = attackSimPosition - mouthPos;
|
Vector2 limbDiff = attackSimPosition - mouthPos;
|
||||||
float extent = Math.Max(mouthLimb.body.GetMaxExtent(), 2);
|
float extent = Math.Max(mouthLimb.body.GetMaxExtent(), 2);
|
||||||
if (limbDiff.LengthSquared() < extent * extent)
|
bool tooFar = Character.InWater ? limbDiff.LengthSquared() > extent * extent : limbDiff.X > extent;
|
||||||
|
if (tooFar)
|
||||||
|
{
|
||||||
|
steeringManager.SteeringSeek(attackSimPosition - (mouthPos - SimPosition), 2);
|
||||||
|
if (Character.InWater)
|
||||||
|
{
|
||||||
|
SteeringManager.SteeringAvoid(deltaTime, lookAheadDistance: avoidLookAheadDistance, weight: 15);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
if (SelectedAiTarget.Entity is Character targetCharacter)
|
if (SelectedAiTarget.Entity is Character targetCharacter)
|
||||||
{
|
{
|
||||||
@@ -2301,11 +2316,9 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
item.body.LinearVelocity *= 0.9f;
|
item.body.LinearVelocity *= 0.9f;
|
||||||
item.body.LinearVelocity -= limbDiff * 0.25f;
|
item.body.LinearVelocity -= limbDiff * 0.25f;
|
||||||
|
|
||||||
bool wasBroken = item.Condition <= 0.0f;
|
bool wasBroken = item.Condition <= 0.0f;
|
||||||
|
item.AddDamage(Character, item.WorldPosition, new Attack(0.0f, 0.0f, 0.0f, 0.0f, 0.02f * Character.Params.EatingSpeed), deltaTime);
|
||||||
item.AddDamage(Character, item.WorldPosition, new Attack(0.0f, 0.0f, 0.0f, 0.0f, 0.1f), deltaTime);
|
Character.ApplyStatusEffects(ActionType.OnEating, deltaTime);
|
||||||
|
|
||||||
if (item.Condition <= 0.0f)
|
if (item.Condition <= 0.0f)
|
||||||
{
|
{
|
||||||
if (!wasBroken) { PetBehavior?.OnEat(item); }
|
if (!wasBroken) { PetBehavior?.OnEat(item); }
|
||||||
@@ -2317,14 +2330,6 @@ namespace Barotrauma
|
|||||||
steeringManager.SteeringManual(deltaTime, Vector2.Normalize(limbDiff) * 3);
|
steeringManager.SteeringManual(deltaTime, Vector2.Normalize(limbDiff) * 3);
|
||||||
Character.AnimController.Collider.ApplyForce(limbDiff * mouthLimb.Mass * 50.0f, mouthPos);
|
Character.AnimController.Collider.ApplyForce(limbDiff * mouthLimb.Mass * 50.0f, mouthPos);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
steeringManager.SteeringSeek(attackSimPosition - (mouthPos - SimPosition), 2);
|
|
||||||
if (Character.AnimController.InWater)
|
|
||||||
{
|
|
||||||
SteeringManager.SteeringAvoid(deltaTime, lookAheadDistance: avoidLookAheadDistance, weight: 15);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -2392,7 +2397,7 @@ namespace Barotrauma
|
|||||||
selectedTargetMemory = null;
|
selectedTargetMemory = null;
|
||||||
targetingParams = null;
|
targetingParams = null;
|
||||||
bool isAnyTargetClose = false;
|
bool isAnyTargetClose = false;
|
||||||
|
bool isBeingChased = IsBeingChased;
|
||||||
foreach (AITarget aiTarget in AITarget.List)
|
foreach (AITarget aiTarget in AITarget.List)
|
||||||
{
|
{
|
||||||
if (aiTarget.InDetectable) { continue; }
|
if (aiTarget.InDetectable) { continue; }
|
||||||
@@ -2515,11 +2520,12 @@ namespace Barotrauma
|
|||||||
// Ignore inner walls when outside (walltargets still work)
|
// Ignore inner walls when outside (walltargets still work)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
valueModifier = 1;
|
|
||||||
if (!Character.AnimController.CanEnterSubmarine && IsWallDisabled(s))
|
if (!Character.AnimController.CanEnterSubmarine && IsWallDisabled(s))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// Prefer weaker walls (200 is the default for normal hull walls)
|
||||||
|
valueModifier = 200f / s.MaxHealth;
|
||||||
for (int i = 0; i < s.Sections.Length; i++)
|
for (int i = 0; i < s.Sections.Length; i++)
|
||||||
{
|
{
|
||||||
var section = s.Sections[i];
|
var section = s.Sections[i];
|
||||||
@@ -2674,6 +2680,10 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (targetParams.State == AIState.Eat && Character.Params.Health.HealthRegenerationWhenEating > 0)
|
||||||
|
{
|
||||||
|
valueModifier *= MathHelper.Lerp(1f, 0.1f, Character.HealthPercentage / 100f);
|
||||||
|
}
|
||||||
valueModifier *= targetParams.Priority;
|
valueModifier *= targetParams.Priority;
|
||||||
if (valueModifier == 0.0f) { continue; }
|
if (valueModifier == 0.0f) { continue; }
|
||||||
if (targetingTag != "decoy")
|
if (targetingTag != "decoy")
|
||||||
@@ -2720,9 +2730,21 @@ namespace Barotrauma
|
|||||||
// Stick to the current target
|
// Stick to the current target
|
||||||
valueModifier *= 1.1f;
|
valueModifier *= 1.1f;
|
||||||
}
|
}
|
||||||
|
if (!isBeingChased)
|
||||||
|
{
|
||||||
|
if (targetParams.State == AIState.Avoid || targetParams.State == AIState.PassiveAggressive || targetParams.State == AIState.Aggressive)
|
||||||
|
{
|
||||||
|
float reactDistance = targetParams.ReactDistance;
|
||||||
|
if (reactDistance > 0 && reactDistance < dist)
|
||||||
|
{
|
||||||
|
// The target is too far and should be ignored.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//if the target is very close, the distance doesn't make much difference
|
//if the target is very close, the distance doesn't make much difference
|
||||||
// -> just ignore the distance and attack whatever has the highest priority
|
// -> just ignore the distance and target whatever has the highest priority
|
||||||
dist = Math.Max(dist, 100.0f);
|
dist = Math.Max(dist, 100.0f);
|
||||||
AITargetMemory targetMemory = GetTargetMemory(aiTarget, addIfNotFound: true);
|
AITargetMemory targetMemory = GetTargetMemory(aiTarget, addIfNotFound: true);
|
||||||
if (Character.Submarine != null && !Character.Submarine.Info.IsRuin && Character.CurrentHull != null)
|
if (Character.Submarine != null && !Character.Submarine.Info.IsRuin && Character.CurrentHull != null)
|
||||||
@@ -2801,9 +2823,9 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
if (Character.CurrentHull != null && targetCharacter.CurrentHull != Character.CurrentHull)
|
if (Character.CurrentHull != null && targetCharacter.CurrentHull != Character.CurrentHull)
|
||||||
{
|
{
|
||||||
if (targetParams.State == AIState.Follow || targetParams.State == AIState.Protect || targetParams.State == AIState.Observe)
|
if (targetParams.State == AIState.Follow || targetParams.State == AIState.Protect || targetParams.State == AIState.Observe || targetParams.State == AIState.Eat)
|
||||||
{
|
{
|
||||||
// Ignore targets that cannot see
|
// Ignore targets that cannot be seen
|
||||||
if (!VisibleHulls.Contains(targetCharacter.CurrentHull))
|
if (!VisibleHulls.Contains(targetCharacter.CurrentHull))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@@ -3295,7 +3317,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
if (priority.HasValue)
|
if (priority.HasValue)
|
||||||
{
|
{
|
||||||
targetParams.Priority = priority.Value;
|
targetParams.Priority = Math.Max(targetParams.Priority, priority.Value);
|
||||||
}
|
}
|
||||||
targetParams.State = state;
|
targetParams.State = state;
|
||||||
if (!modifiedParams.ContainsKey(tag))
|
if (!modifiedParams.ContainsKey(tag))
|
||||||
@@ -3314,6 +3336,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Temporarily changes the predefined state for a target. Eg. Idle -> Attack.
|
/// Temporarily changes the predefined state for a target. Eg. Idle -> Attack.
|
||||||
|
/// Note: does not change the current AIState!
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void ChangeTargetState(Character target, AIState state, float? priority = null)
|
private void ChangeTargetState(Character target, AIState state, float? priority = null)
|
||||||
{
|
{
|
||||||
@@ -3335,14 +3358,14 @@ namespace Barotrauma
|
|||||||
// --> Target the submarine too.
|
// --> Target the submarine too.
|
||||||
if (target.Submarine != null && Character.Submarine == null && (canAttackDoors || canAttackWalls))
|
if (target.Submarine != null && Character.Submarine == null && (canAttackDoors || canAttackWalls))
|
||||||
{
|
{
|
||||||
ChangeParams("room", state, priority * 0.1f);
|
ChangeParams("room", state, priority / 2);
|
||||||
if (canAttackWalls)
|
if (canAttackWalls)
|
||||||
{
|
{
|
||||||
ChangeParams("wall", state, priority * 0.1f);
|
ChangeParams("wall", state, priority / 2);
|
||||||
}
|
}
|
||||||
if (canAttackDoors)
|
if (canAttackDoors)
|
||||||
{
|
{
|
||||||
ChangeParams("door", state, priority * 0.1f);
|
ChangeParams("door", state, priority / 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ChangeParams("provocative", state, priority, onlyExisting: true);
|
ChangeParams("provocative", state, priority, onlyExisting: true);
|
||||||
@@ -3394,9 +3417,15 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private bool CanPerceive(AITarget target, float dist = -1, float distSquared = -1, bool checkVisibility = false)
|
private bool CanPerceive(AITarget target, float dist = -1, float distSquared = -1, bool checkVisibility = false)
|
||||||
{
|
{
|
||||||
|
if (target?.Entity == null) { return false; }
|
||||||
bool insideSightRange;
|
bool insideSightRange;
|
||||||
bool insideSoundRange;
|
bool insideSoundRange;
|
||||||
checkVisibility = checkVisibility && Character.Submarine != null && target.Entity.Submarine == Character.Submarine;
|
if (checkVisibility)
|
||||||
|
{
|
||||||
|
// We only want to check the visibility when the target is in ruins/wreck/similiar place where sneaking should be possible.
|
||||||
|
// When the monsters attack the player sub, they wall hack so that they can be more aggressive.
|
||||||
|
checkVisibility = target.Entity.Submarine != null && target.Entity.Submarine == Character.Submarine && target.Entity.Submarine.TeamID == CharacterTeamType.None;
|
||||||
|
}
|
||||||
if (dist > 0)
|
if (dist > 0)
|
||||||
{
|
{
|
||||||
insideSightRange = IsInRange(dist, target.SightRange, Sight);
|
insideSightRange = IsInRange(dist, target.SightRange, Sight);
|
||||||
|
|||||||
@@ -565,7 +565,7 @@ namespace Barotrauma
|
|||||||
Character.AnimController.HeadInWater ||
|
Character.AnimController.HeadInWater ||
|
||||||
Character.Submarine == null ||
|
Character.Submarine == null ||
|
||||||
(Character.Submarine.TeamID != Character.TeamID && !Character.IsEscorted) ||
|
(Character.Submarine.TeamID != Character.TeamID && !Character.IsEscorted) ||
|
||||||
!ObjectiveManager.IsCurrentObjective<AIObjectiveIdle>() && ObjectiveManager.CurrentOrders.Any(o => o.Objective.KeepDivingGearOn) ||
|
ObjectiveManager.CurrentOrders.Any(o => o.Objective.KeepDivingGearOnAlsoWhenInactive) ||
|
||||||
ObjectiveManager.CurrentObjective.GetSubObjectivesRecursive(true).Any(o => o.KeepDivingGearOn) ||
|
ObjectiveManager.CurrentObjective.GetSubObjectivesRecursive(true).Any(o => o.KeepDivingGearOn) ||
|
||||||
Character.CurrentHull.OxygenPercentage < HULL_LOW_OXYGEN_PERCENTAGE + 10;
|
Character.CurrentHull.OxygenPercentage < HULL_LOW_OXYGEN_PERCENTAGE + 10;
|
||||||
bool IsOrderedToWait() => Character.IsOnPlayerTeam && ObjectiveManager.CurrentOrder is AIObjectiveGoTo goTo && goTo.Target == Character;
|
bool IsOrderedToWait() => Character.IsOnPlayerTeam && ObjectiveManager.CurrentOrder is AIObjectiveGoTo goTo && goTo.Target == Character;
|
||||||
@@ -878,7 +878,7 @@ namespace Barotrauma
|
|||||||
foreach (Character target in Character.CharacterList)
|
foreach (Character target in Character.CharacterList)
|
||||||
{
|
{
|
||||||
if (target.CurrentHull != hull || !target.Enabled) { continue; }
|
if (target.CurrentHull != hull || !target.Enabled) { continue; }
|
||||||
if (AIObjectiveFightIntruders.IsValidTarget(target, Character))
|
if (AIObjectiveFightIntruders.IsValidTarget(target, Character, false))
|
||||||
{
|
{
|
||||||
if (!target.IsArrested && AddTargets<AIObjectiveFightIntruders, Character>(Character, target) && newOrder == null)
|
if (!target.IsArrested && AddTargets<AIObjectiveFightIntruders, Character>(Character, target) && newOrder == null)
|
||||||
{
|
{
|
||||||
@@ -1772,7 +1772,7 @@ namespace Barotrauma
|
|||||||
foreach (var enemy in Character.CharacterList)
|
foreach (var enemy in Character.CharacterList)
|
||||||
{
|
{
|
||||||
if (enemy.CurrentHull != hull) { continue; }
|
if (enemy.CurrentHull != hull) { continue; }
|
||||||
if (AIObjectiveFightIntruders.IsValidTarget(enemy, character))
|
if (AIObjectiveFightIntruders.IsValidTarget(enemy, character, false))
|
||||||
{
|
{
|
||||||
AddTargets<AIObjectiveFightIntruders, Character>(character, enemy);
|
AddTargets<AIObjectiveFightIntruders, Character>(character, enemy);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
class IndoorsSteeringManager : SteeringManager
|
class IndoorsSteeringManager : SteeringManager
|
||||||
{
|
{
|
||||||
private PathFinder pathFinder;
|
private readonly PathFinder pathFinder;
|
||||||
private SteeringPath currentPath;
|
private SteeringPath currentPath;
|
||||||
|
|
||||||
private bool canOpenDoors;
|
private readonly bool canOpenDoors;
|
||||||
public bool CanBreakDoors { get; set; }
|
public bool CanBreakDoors { get; set; }
|
||||||
|
|
||||||
private bool ShouldBreakDoor(Door door) =>
|
private bool ShouldBreakDoor(Door door) =>
|
||||||
@@ -20,7 +20,7 @@ namespace Barotrauma
|
|||||||
!door.Item.Indestructible && !door.Item.InvulnerableToDamage &&
|
!door.Item.Indestructible && !door.Item.InvulnerableToDamage &&
|
||||||
(door.Item.Submarine == null || door.Item.Submarine.TeamID != character.TeamID);
|
(door.Item.Submarine == null || door.Item.Submarine.TeamID != character.TeamID);
|
||||||
|
|
||||||
private Character character;
|
private readonly Character character;
|
||||||
|
|
||||||
private Vector2 currentTarget;
|
private Vector2 currentTarget;
|
||||||
|
|
||||||
@@ -77,8 +77,10 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public IndoorsSteeringManager(ISteerable host, bool canOpenDoors, bool canBreakDoors) : base(host)
|
public IndoorsSteeringManager(ISteerable host, bool canOpenDoors, bool canBreakDoors) : base(host)
|
||||||
{
|
{
|
||||||
pathFinder = new PathFinder(WayPoint.WayPointList.FindAll(wp => wp.SpawnType == SpawnType.Path), true);
|
pathFinder = new PathFinder(WayPoint.WayPointList.FindAll(wp => wp.SpawnType == SpawnType.Path), true)
|
||||||
pathFinder.GetNodePenalty = GetNodePenalty;
|
{
|
||||||
|
GetNodePenalty = GetNodePenalty
|
||||||
|
};
|
||||||
|
|
||||||
this.canOpenDoors = canOpenDoors;
|
this.canOpenDoors = canOpenDoors;
|
||||||
this.CanBreakDoors = canBreakDoors;
|
this.CanBreakDoors = canBreakDoors;
|
||||||
@@ -508,6 +510,16 @@ namespace Barotrauma
|
|||||||
canAccessButtons = true;
|
canAccessButtons = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
foreach (var linked in door.Item.linkedTo)
|
||||||
|
{
|
||||||
|
if (!(linked is Item linkedItem)) { continue; }
|
||||||
|
var button = linkedItem.GetComponent<Controller>();
|
||||||
|
if (button == null) { continue; }
|
||||||
|
if (button.HasAccess(character) && (buttonFilter == null || buttonFilter(button)))
|
||||||
|
{
|
||||||
|
canAccessButtons = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
return canAccessButtons || door.IsOpen || ShouldBreakDoor(door);
|
return canAccessButtons || door.IsOpen || ShouldBreakDoor(door);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ namespace Barotrauma
|
|||||||
public virtual bool ConcurrentObjectives => false;
|
public virtual bool ConcurrentObjectives => false;
|
||||||
|
|
||||||
public virtual bool KeepDivingGearOn => false;
|
public virtual bool KeepDivingGearOn => false;
|
||||||
|
public virtual bool KeepDivingGearOnAlsoWhenInactive => false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// There's a separate property for diving suit and mask: KeepDivingGearOn.
|
/// There's a separate property for diving suit and mask: KeepDivingGearOn.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
+5
-3
@@ -12,10 +12,12 @@ namespace Barotrauma
|
|||||||
|
|
||||||
protected override float TargetUpdateTimeMultiplier => 0.2f;
|
protected override float TargetUpdateTimeMultiplier => 0.2f;
|
||||||
|
|
||||||
|
public bool TargetCharactersInOtherSubs { get; set; }
|
||||||
|
|
||||||
public AIObjectiveFightIntruders(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1)
|
public AIObjectiveFightIntruders(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1)
|
||||||
: base(character, objectiveManager, priorityModifier) { }
|
: base(character, objectiveManager, priorityModifier) { }
|
||||||
|
|
||||||
protected override bool Filter(Character target) => IsValidTarget(target, character);
|
protected override bool Filter(Character target) => IsValidTarget(target, character, TargetCharactersInOtherSubs);
|
||||||
|
|
||||||
protected override IEnumerable<Character> GetList() => Character.CharacterList;
|
protected override IEnumerable<Character> GetList() => Character.CharacterList;
|
||||||
|
|
||||||
@@ -54,7 +56,7 @@ namespace Barotrauma
|
|||||||
protected override void OnObjectiveCompleted(AIObjective objective, Character target)
|
protected override void OnObjectiveCompleted(AIObjective objective, Character target)
|
||||||
=> HumanAIController.RemoveTargets<AIObjectiveFightIntruders, Character>(character, target);
|
=> HumanAIController.RemoveTargets<AIObjectiveFightIntruders, Character>(character, target);
|
||||||
|
|
||||||
public static bool IsValidTarget(Character target, Character character)
|
public static bool IsValidTarget(Character target, Character character, bool targetCharactersInOtherSubs)
|
||||||
{
|
{
|
||||||
if (target == null || target.Removed) { return false; }
|
if (target == null || target.Removed) { return false; }
|
||||||
if (target.IsDead) { return false; }
|
if (target.IsDead) { return false; }
|
||||||
@@ -65,7 +67,7 @@ namespace Barotrauma
|
|||||||
if (target.CurrentHull == null) { return false; }
|
if (target.CurrentHull == null) { return false; }
|
||||||
if (HumanAIController.IsFriendly(character, target)) { return false; }
|
if (HumanAIController.IsFriendly(character, target)) { return false; }
|
||||||
if (!character.Submarine.IsConnectedTo(target.Submarine)) { return false; }
|
if (!character.Submarine.IsConnectedTo(target.Submarine)) { return false; }
|
||||||
if (character.Submarine.TeamID != target.Submarine.TeamID) { return false; }
|
if (!targetCharactersInOtherSubs && character.Submarine.TeamID != target.Submarine.TeamID) { return false; }
|
||||||
if (target.HasAbilityFlag(AbilityFlags.IgnoredByEnemyAI)) { return false; }
|
if (target.HasAbilityFlag(AbilityFlags.IgnoredByEnemyAI)) { return false; }
|
||||||
if (target.IsArrested) { return false; }
|
if (target.IsArrested) { return false; }
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
+12
-10
@@ -629,18 +629,20 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (SteeringManager == PathSteering && PathSteering.CurrentPath != null && !PathSteering.CurrentPath.Finished && PathSteering.IsCurrentNodeLadder)
|
if (character.IsClimbing)
|
||||||
{
|
{
|
||||||
// Climbing a ladder
|
if (SteeringManager == PathSteering && PathSteering.CurrentPath != null && !PathSteering.CurrentPath.Finished && PathSteering.IsCurrentNodeLadder)
|
||||||
if (Target.WorldPosition.Y > character.WorldPosition.Y)
|
|
||||||
{
|
{
|
||||||
// The target is still above us
|
if (Target.WorldPosition.Y > character.WorldPosition.Y)
|
||||||
return false;
|
{
|
||||||
}
|
// The target is still above us
|
||||||
if (!character.AnimController.IsAboveFloor)
|
return false;
|
||||||
{
|
}
|
||||||
// Going through a hatch
|
if (!character.AnimController.IsAboveFloor)
|
||||||
return false;
|
{
|
||||||
|
// Going through a hatch
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!AlwaysUseEuclideanDistance && !character.AnimController.InWater)
|
if (!AlwaysUseEuclideanDistance && !character.AnimController.InWater)
|
||||||
|
|||||||
+7
-1
@@ -422,7 +422,7 @@ namespace Barotrauma
|
|||||||
case "wait":
|
case "wait":
|
||||||
newObjective = new AIObjectiveGoTo(order.TargetSpatialEntity ?? character, character, this, repeat: true, priorityModifier: priorityModifier)
|
newObjective = new AIObjectiveGoTo(order.TargetSpatialEntity ?? character, character, this, repeat: true, priorityModifier: priorityModifier)
|
||||||
{
|
{
|
||||||
AllowGoingOutside = character.Submarine == null || (order.TargetSpatialEntity != null && character.Submarine != order.TargetSpatialEntity.Submarine)
|
AllowGoingOutside = true
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case "return":
|
case "return":
|
||||||
@@ -468,6 +468,12 @@ namespace Barotrauma
|
|||||||
case "fightintruders":
|
case "fightintruders":
|
||||||
newObjective = new AIObjectiveFightIntruders(character, this, priorityModifier);
|
newObjective = new AIObjectiveFightIntruders(character, this, priorityModifier);
|
||||||
break;
|
break;
|
||||||
|
case "assaultenemy":
|
||||||
|
newObjective = new AIObjectiveFightIntruders(character, this, priorityModifier)
|
||||||
|
{
|
||||||
|
TargetCharactersInOtherSubs = true
|
||||||
|
};
|
||||||
|
break;
|
||||||
case "steer":
|
case "steer":
|
||||||
var steering = (order?.TargetEntity as Item)?.GetComponent<Steering>();
|
var steering = (order?.TargetEntity as Item)?.GetComponent<Steering>();
|
||||||
if (steering != null) { steering.PosToMaintain = steering.Item.Submarine?.WorldPosition; }
|
if (steering != null) { steering.PosToMaintain = steering.Item.Submarine?.WorldPosition; }
|
||||||
|
|||||||
+1
@@ -11,6 +11,7 @@ namespace Barotrauma
|
|||||||
public override string Identifier { get; set; } = "prepare";
|
public override string Identifier { get; set; } = "prepare";
|
||||||
public override string DebugTag => $"{Identifier}";
|
public override string DebugTag => $"{Identifier}";
|
||||||
public override bool KeepDivingGearOn => true;
|
public override bool KeepDivingGearOn => true;
|
||||||
|
public override bool KeepDivingGearOnAlsoWhenInactive => true;
|
||||||
public override bool PrioritizeIfSubObjectivesActive => true;
|
public override bool PrioritizeIfSubObjectivesActive => true;
|
||||||
|
|
||||||
private AIObjectiveGetItem getSingleItemObjective;
|
private AIObjectiveGetItem getSingleItemObjective;
|
||||||
|
|||||||
@@ -155,6 +155,9 @@ namespace Barotrauma
|
|||||||
public OrderCategory? Category { get; private set; }
|
public OrderCategory? Category { get; private set; }
|
||||||
|
|
||||||
//legacy support
|
//legacy support
|
||||||
|
/// <summary>
|
||||||
|
/// If defined, the order can only be quick-assigned to characters with these jobs. Or if it's a report, the icon will only be displayed to characters with these jobs.
|
||||||
|
/// </summary>
|
||||||
public readonly string[] AppropriateJobs;
|
public readonly string[] AppropriateJobs;
|
||||||
public readonly string[] Options;
|
public readonly string[] Options;
|
||||||
public readonly string[] HiddenOptions;
|
public readonly string[] HiddenOptions;
|
||||||
@@ -177,6 +180,10 @@ namespace Barotrauma
|
|||||||
public bool IsPrefab { get; private set; }
|
public bool IsPrefab { get; private set; }
|
||||||
public readonly bool MustManuallyAssign;
|
public readonly bool MustManuallyAssign;
|
||||||
public readonly bool AutoDismiss;
|
public readonly bool AutoDismiss;
|
||||||
|
/// <summary>
|
||||||
|
/// If defined, the order will be quick-assigned to characters with these jobs before characters with other jobs.
|
||||||
|
/// </summary>
|
||||||
|
public string[] PreferredJobs { get; }
|
||||||
|
|
||||||
public readonly OrderTarget TargetPosition;
|
public readonly OrderTarget TargetPosition;
|
||||||
|
|
||||||
@@ -327,6 +334,7 @@ namespace Barotrauma
|
|||||||
ControllerTags = orderElement.GetAttributeStringArray("controllertags", new string[0]);
|
ControllerTags = orderElement.GetAttributeStringArray("controllertags", new string[0]);
|
||||||
TargetAllCharacters = orderElement.GetAttributeBool("targetallcharacters", false);
|
TargetAllCharacters = orderElement.GetAttributeBool("targetallcharacters", false);
|
||||||
AppropriateJobs = orderElement.GetAttributeStringArray("appropriatejobs", new string[0]);
|
AppropriateJobs = orderElement.GetAttributeStringArray("appropriatejobs", new string[0]);
|
||||||
|
PreferredJobs = orderElement.GetAttributeStringArray("preferredjobs", new string[0]);
|
||||||
Options = orderElement.GetAttributeStringArray("options", new string[0]);
|
Options = orderElement.GetAttributeStringArray("options", new string[0]);
|
||||||
HiddenOptions = orderElement.GetAttributeStringArray("hiddenoptions", new string[0]);
|
HiddenOptions = orderElement.GetAttributeStringArray("hiddenoptions", new string[0]);
|
||||||
AllOptions = Options.Concat(HiddenOptions).ToArray();
|
AllOptions = Options.Concat(HiddenOptions).ToArray();
|
||||||
@@ -407,7 +415,7 @@ namespace Barotrauma
|
|||||||
MustManuallyAssign = orderElement.GetAttributeBool("mustmanuallyassign", false);
|
MustManuallyAssign = orderElement.GetAttributeBool("mustmanuallyassign", false);
|
||||||
IsIgnoreOrder = Identifier == "ignorethis" || Identifier == "unignorethis";
|
IsIgnoreOrder = Identifier == "ignorethis" || Identifier == "unignorethis";
|
||||||
DrawIconWhenContained = orderElement.GetAttributeBool("displayiconwhencontained", false);
|
DrawIconWhenContained = orderElement.GetAttributeBool("displayiconwhencontained", false);
|
||||||
AutoDismiss = orderElement.GetAttributeBool("autodismiss", Category == OrderCategory.Movement);
|
AutoDismiss = orderElement.GetAttributeBool("autodismiss", Category == OrderCategory.Operate || Category == OrderCategory.Movement);
|
||||||
AssignmentPriority = Math.Clamp(orderElement.GetAttributeInt("assignmentpriority", 100), 0, 100);
|
AssignmentPriority = Math.Clamp(orderElement.GetAttributeInt("assignmentpriority", 100), 0, 100);
|
||||||
ColoredWhenControllingGiver = orderElement.GetAttributeBool("coloredwhencontrollinggiver", false);
|
ColoredWhenControllingGiver = orderElement.GetAttributeBool("coloredwhencontrollinggiver", false);
|
||||||
DisplayGiverInTooltip = orderElement.GetAttributeBool("displaygiverintooltip", false);
|
DisplayGiverInTooltip = orderElement.GetAttributeBool("displaygiverintooltip", false);
|
||||||
@@ -435,6 +443,7 @@ namespace Barotrauma
|
|||||||
ControllerTags = prefab.ControllerTags;
|
ControllerTags = prefab.ControllerTags;
|
||||||
TargetAllCharacters = prefab.TargetAllCharacters;
|
TargetAllCharacters = prefab.TargetAllCharacters;
|
||||||
AppropriateJobs = prefab.AppropriateJobs;
|
AppropriateJobs = prefab.AppropriateJobs;
|
||||||
|
PreferredJobs = prefab.PreferredJobs;
|
||||||
FadeOutTime = prefab.FadeOutTime;
|
FadeOutTime = prefab.FadeOutTime;
|
||||||
MustSetTarget = prefab.MustSetTarget;
|
MustSetTarget = prefab.MustSetTarget;
|
||||||
CanBeGeneralized = prefab.CanBeGeneralized;
|
CanBeGeneralized = prefab.CanBeGeneralized;
|
||||||
@@ -446,8 +455,9 @@ namespace Barotrauma
|
|||||||
Hidden = prefab.Hidden;
|
Hidden = prefab.Hidden;
|
||||||
IgnoreAtOutpost = prefab.IgnoreAtOutpost;
|
IgnoreAtOutpost = prefab.IgnoreAtOutpost;
|
||||||
AssignmentPriority = prefab.AssignmentPriority;
|
AssignmentPriority = prefab.AssignmentPriority;
|
||||||
ColoredWhenControllingGiver = prefab.ColoredWhenControllingGiver;
|
AutoDismiss = prefab.AutoDismiss;
|
||||||
DisplayGiverInTooltip = prefab.DisplayGiverInTooltip;
|
DisplayGiverInTooltip = prefab.DisplayGiverInTooltip;
|
||||||
|
ColoredWhenControllingGiver = prefab.ColoredWhenControllingGiver;
|
||||||
|
|
||||||
OrderGiver = orderGiver;
|
OrderGiver = orderGiver;
|
||||||
TargetEntity = targetEntity;
|
TargetEntity = targetEntity;
|
||||||
@@ -489,29 +499,36 @@ namespace Barotrauma
|
|||||||
TargetType = OrderTargetType.WallSection;
|
TargetType = OrderTargetType.WallSection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasAppropriateJob(Character character)
|
private bool HasSpecifiedJob(Character character, string[] jobs)
|
||||||
{
|
{
|
||||||
if (character.Info == null || character.Info.Job == null) { return false; }
|
if (jobs == null || jobs.Length == 0) { return false; }
|
||||||
if (character.Info.Job.Prefab.AppropriateOrders.Any(appropriateOrderId => Identifier == appropriateOrderId)) { return true; }
|
string jobIdentifier = character?.Info?.Job?.Prefab?.Identifier;
|
||||||
|
if (string.IsNullOrEmpty(jobIdentifier)) { return false; }
|
||||||
if (!JobPrefab.Prefabs.Any(jp => jp.AppropriateOrders.Contains(Identifier)) &&
|
for (int i = 0; i < jobs.Length; i++)
|
||||||
(AppropriateJobs == null || AppropriateJobs.Length == 0))
|
|
||||||
{
|
{
|
||||||
return true;
|
if (jobIdentifier.Equals(jobs[i], StringComparison.OrdinalIgnoreCase)) { return true; }
|
||||||
}
|
|
||||||
for (int i = 0; i < AppropriateJobs.Length; i++)
|
|
||||||
{
|
|
||||||
if (character.Info.Job.Prefab.Identifier.Equals(AppropriateJobs[i], StringComparison.OrdinalIgnoreCase)) { return true; }
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool HasAppropriateJob(Character character) => HasSpecifiedJob(character, AppropriateJobs);
|
||||||
|
|
||||||
|
public bool HasPreferredJob(Character character) => HasSpecifiedJob(character, PreferredJobs);
|
||||||
|
|
||||||
public string GetChatMessage(string targetCharacterName, string targetRoomName, bool givingOrderToSelf, string orderOption = "", bool isNewOrder = true)
|
public string GetChatMessage(string targetCharacterName, string targetRoomName, bool givingOrderToSelf, string orderOption = "", bool isNewOrder = true)
|
||||||
{
|
{
|
||||||
if (!TargetAllCharacters && !isNewOrder && Identifier != "dismissed")
|
if (!TargetAllCharacters && !isNewOrder && Identifier != "dismissed")
|
||||||
{
|
{
|
||||||
// Use special dialogue when we're rearranging character orders
|
// Use special dialogue when we're rearranging character orders
|
||||||
return TextManager.GetWithVariable("rearrangedorders", "[name]", targetCharacterName ?? string.Empty, returnNull: true) ?? string.Empty;
|
if (!givingOrderToSelf)
|
||||||
|
{
|
||||||
|
return TextManager.GetWithVariable("rearrangedorders", "[name]", targetCharacterName ?? string.Empty, returnNull: true) ?? string.Empty;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Say nothing when rearranging the orders of the character you're controlling
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
string messageTag = $"{(givingOrderToSelf && !TargetAllCharacters ? "OrderDialogSelf" : "OrderDialog")}.{Identifier}";
|
string messageTag = $"{(givingOrderToSelf && !TargetAllCharacters ? "OrderDialogSelf" : "OrderDialog")}.{Identifier}";
|
||||||
if (!string.IsNullOrEmpty(orderOption))
|
if (!string.IsNullOrEmpty(orderOption))
|
||||||
|
|||||||
@@ -346,7 +346,12 @@ namespace Barotrauma
|
|||||||
|
|
||||||
Vector2 limbDiff = attackSimPosition - mouthPos;
|
Vector2 limbDiff = attackSimPosition - mouthPos;
|
||||||
float extent = Math.Max(mouthLimb.body.GetMaxExtent(), 1);
|
float extent = Math.Max(mouthLimb.body.GetMaxExtent(), 1);
|
||||||
if (limbDiff.LengthSquared() < extent * extent)
|
bool tooFar = character.InWater ? limbDiff.LengthSquared() > extent * extent : limbDiff.X > extent;
|
||||||
|
if (tooFar)
|
||||||
|
{
|
||||||
|
character.SelectedCharacter = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
//pull the target character to the position of the mouth
|
//pull the target character to the position of the mouth
|
||||||
//(+ make the force fluctuate to waggle the character a bit)
|
//(+ make the force fluctuate to waggle the character a bit)
|
||||||
@@ -383,7 +388,7 @@ namespace Barotrauma
|
|||||||
mouthLimb.body.ApplyTorque(-force * 50);
|
mouthLimb.body.ApplyTorque(-force * 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Character.CanEat)
|
if (Character.CanEat && target.IsDead)
|
||||||
{
|
{
|
||||||
var jaw = GetLimb(LimbType.Jaw);
|
var jaw = GetLimb(LimbType.Jaw);
|
||||||
if (jaw != null)
|
if (jaw != null)
|
||||||
@@ -432,10 +437,6 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
character.SelectedCharacter = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool reverse;
|
public bool reverse;
|
||||||
|
|||||||
+1
-1
@@ -533,7 +533,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
bool onSlope = Math.Abs(movement.X) > 0.01f && Math.Abs(floorNormal.X) > 0.1f && Math.Sign(floorNormal.X) != Math.Sign(movement.X);
|
bool onSlope = Math.Abs(movement.X) > 0.01f && Math.Abs(floorNormal.X) > 0.1f && Math.Sign(floorNormal.X) != Math.Sign(movement.X);
|
||||||
|
|
||||||
bool movingHorizontally = !MathUtils.NearlyEqual(targetMovement.X, 0.0f);
|
bool movingHorizontally = !MathUtils.NearlyEqual(TargetMovement.X, 0.0f);
|
||||||
|
|
||||||
if (Stairs != null || onSlope)
|
if (Stairs != null || onSlope)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -304,7 +304,27 @@ namespace Barotrauma
|
|||||||
public abstract float? TorsoPosition { get; }
|
public abstract float? TorsoPosition { get; }
|
||||||
public abstract float? TorsoAngle { get; }
|
public abstract float? TorsoAngle { get; }
|
||||||
|
|
||||||
public float ImpactTolerance => RagdollParams.ImpactTolerance;
|
float? impactTolerance;
|
||||||
|
public float ImpactTolerance
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (impactTolerance == null)
|
||||||
|
{
|
||||||
|
impactTolerance = RagdollParams.ImpactTolerance;
|
||||||
|
if (character.Params.VariantFile != null)
|
||||||
|
{
|
||||||
|
float? tolerance = character.Params.VariantFile.Root.GetChildElement("ragdoll")?.GetAttributeFloat("impacttolerance", impactTolerance.Value);
|
||||||
|
if (tolerance.HasValue)
|
||||||
|
{
|
||||||
|
impactTolerance = tolerance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return impactTolerance.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool Draggable => RagdollParams.Draggable;
|
public bool Draggable => RagdollParams.Draggable;
|
||||||
public bool CanEnterSubmarine => RagdollParams.CanEnterSubmarine;
|
public bool CanEnterSubmarine => RagdollParams.CanEnterSubmarine;
|
||||||
|
|
||||||
@@ -1833,7 +1853,7 @@ namespace Barotrauma
|
|||||||
float sin = (float)Math.Sin(mouthLimb.Rotation);
|
float sin = (float)Math.Sin(mouthLimb.Rotation);
|
||||||
Vector2 bodySize = mouthLimb.body.GetSize();
|
Vector2 bodySize = mouthLimb.body.GetSize();
|
||||||
Vector2 offset = new Vector2(mouthLimb.MouthPos.X * bodySize.X / 2, mouthLimb.MouthPos.Y * bodySize.Y / 2);
|
Vector2 offset = new Vector2(mouthLimb.MouthPos.X * bodySize.X / 2, mouthLimb.MouthPos.Y * bodySize.Y / 2);
|
||||||
return mouthLimb.SimPosition + new Vector2(offset.X * cos - offset.Y * sin, offset.X * sin + offset.Y * cos) * mouthLimb.Scale * RagdollParams.LimbScale;
|
return mouthLimb.SimPosition + new Vector2(offset.X * cos - offset.Y * sin, offset.X * sin + offset.Y * cos);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector2 GetColliderBottom()
|
public Vector2 GetColliderBottom()
|
||||||
|
|||||||
@@ -101,11 +101,21 @@ namespace Barotrauma
|
|||||||
[Serialize(false, true, description: "Should the AI try to steer away from the target when aiming with this attack? Best combined with PassiveAggressive behavior."), Editable]
|
[Serialize(false, true, description: "Should the AI try to steer away from the target when aiming with this attack? Best combined with PassiveAggressive behavior."), Editable]
|
||||||
public bool Retreat { get; private set; }
|
public bool Retreat { get; private set; }
|
||||||
|
|
||||||
|
private float _range;
|
||||||
[Serialize(0.0f, true, description: "The min distance from the attack limb to the target before the AI tries to attack."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 2000.0f)]
|
[Serialize(0.0f, true, description: "The min distance from the attack limb to the target before the AI tries to attack."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 2000.0f)]
|
||||||
public float Range { get; set; }
|
public float Range
|
||||||
|
{
|
||||||
|
get => _range * RangeMultiplier;
|
||||||
|
set => _range = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float _damageRange;
|
||||||
[Serialize(0.0f, true, description: "The min distance from the attack limb to the target to do damage. In distance-based hit detection, the hit will be registered as soon as the target is within the damage range, unless the attack duration has expired."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 2000.0f)]
|
[Serialize(0.0f, true, description: "The min distance from the attack limb to the target to do damage. In distance-based hit detection, the hit will be registered as soon as the target is within the damage range, unless the attack duration has expired."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 2000.0f)]
|
||||||
public float DamageRange { get; set; }
|
public float DamageRange
|
||||||
|
{
|
||||||
|
get => _damageRange * RangeMultiplier;
|
||||||
|
set => _damageRange = value;
|
||||||
|
}
|
||||||
|
|
||||||
[Serialize(0.25f, true, description: "An approximation of the attack duration. Effectively defines the time window in which the hit can be registered. If set to too low value, it's possible that the attack won't hit the target in time."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10.0f, DecimalCount = 2)]
|
[Serialize(0.25f, true, description: "An approximation of the attack duration. Effectively defines the time window in which the hit can be registered. If set to too low value, it's possible that the attack won't hit the target in time."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10.0f, DecimalCount = 2)]
|
||||||
public float Duration { get; private set; }
|
public float Duration { get; private set; }
|
||||||
@@ -145,10 +155,20 @@ namespace Barotrauma
|
|||||||
public float Penetration { get; private set; }
|
public float Penetration { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Currently only used with variants. Used for multiplying all the damage.
|
/// Used for multiplying all the damage.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float DamageMultiplier { get; set; } = 1;
|
public float DamageMultiplier { get; set; } = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used for multiplying all the ranges.
|
||||||
|
/// </summary>
|
||||||
|
public float RangeMultiplier { get; set; } = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used for multiplying the physics forces.
|
||||||
|
/// </summary>
|
||||||
|
public float ImpactMultiplier { get; set; } = 1;
|
||||||
|
|
||||||
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f)]
|
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f)]
|
||||||
public float LevelWallDamage { get; set; }
|
public float LevelWallDamage { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -1249,6 +1249,10 @@ namespace Barotrauma
|
|||||||
Info.HairElement?.Elements("sprite").ForEach(s => head.OtherWearables.Add(new WearableSprite(s, WearableType.Hair)));
|
Info.HairElement?.Elements("sprite").ForEach(s => head.OtherWearables.Add(new WearableSprite(s, WearableType.Hair)));
|
||||||
|
|
||||||
#if CLIENT
|
#if CLIENT
|
||||||
|
if (info.Head?.HairWithHatElement != null)
|
||||||
|
{
|
||||||
|
head.HairWithHatSprite = new WearableSprite(info.Head?.HairWithHatElement.Element("sprite"), WearableType.Hair);
|
||||||
|
}
|
||||||
head.EnableHuskSprite = Params.Husk;
|
head.EnableHuskSprite = Params.Husk;
|
||||||
head.LoadHerpesSprite();
|
head.LoadHerpesSprite();
|
||||||
head.UpdateWearableTypesToHide();
|
head.UpdateWearableTypesToHide();
|
||||||
@@ -3499,7 +3503,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
Limb limbHit = targetLimb;
|
Limb limbHit = targetLimb;
|
||||||
|
|
||||||
float attackImpulse = attack.TargetImpulse + attack.TargetForce * deltaTime;
|
float attackImpulse = attack.TargetImpulse + attack.TargetForce * attack.ImpactMultiplier * deltaTime;
|
||||||
|
|
||||||
AbilityAttackData attackData = new AbilityAttackData(attack, this);
|
AbilityAttackData attackData = new AbilityAttackData(attack, this);
|
||||||
if (attacker != null)
|
if (attacker != null)
|
||||||
@@ -3537,7 +3541,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (limbHit == null) { return new AttackResult(); }
|
if (limbHit == null) { return new AttackResult(); }
|
||||||
Vector2 forceWorld = attack.TargetImpulseWorld + attack.TargetForceWorld;
|
Vector2 forceWorld = attack.TargetImpulseWorld + attack.TargetForceWorld * attack.ImpactMultiplier;
|
||||||
if (attacker != null)
|
if (attacker != null)
|
||||||
{
|
{
|
||||||
forceWorld.X *= attacker.AnimController.Dir;
|
forceWorld.X *= attacker.AnimController.Dir;
|
||||||
@@ -3845,40 +3849,49 @@ namespace Barotrauma
|
|||||||
targets.AddRange(statusEffect.GetNearbyTargets(WorldPosition, targets));
|
targets.AddRange(statusEffect.GetNearbyTargets(WorldPosition, targets));
|
||||||
statusEffect.Apply(actionType, deltaTime, this, targets);
|
statusEffect.Apply(actionType, deltaTime, this, targets);
|
||||||
}
|
}
|
||||||
else
|
else if (statusEffect.targetLimbs != null)
|
||||||
{
|
{
|
||||||
statusEffect.Apply(actionType, deltaTime, this, this);
|
foreach (var limbType in statusEffect.targetLimbs)
|
||||||
if (statusEffect.targetLimbs != null)
|
|
||||||
{
|
{
|
||||||
foreach (var limbType in statusEffect.targetLimbs)
|
if (statusEffect.HasTargetType(StatusEffect.TargetType.AllLimbs))
|
||||||
{
|
{
|
||||||
if (statusEffect.HasTargetType(StatusEffect.TargetType.AllLimbs))
|
// Target all matching limbs
|
||||||
|
foreach (var limb in AnimController.Limbs)
|
||||||
{
|
{
|
||||||
// Target all matching limbs
|
if (limb.IsSevered) { continue; }
|
||||||
foreach (var limb in AnimController.Limbs)
|
if (limb.type == limbType)
|
||||||
{
|
{
|
||||||
if (limb.IsSevered) { continue; }
|
statusEffect.sourceBody = limb.body;
|
||||||
if (limb.type == limbType)
|
statusEffect.Apply(actionType, deltaTime, this, limb);
|
||||||
{
|
|
||||||
statusEffect.Apply(actionType, deltaTime, this, limb);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (statusEffect.HasTargetType(StatusEffect.TargetType.Limb))
|
}
|
||||||
|
else if (statusEffect.HasTargetType(StatusEffect.TargetType.Limb))
|
||||||
|
{
|
||||||
|
// Target just the first matching limb
|
||||||
|
Limb limb = AnimController.GetLimb(limbType);
|
||||||
|
if (limb != null)
|
||||||
{
|
{
|
||||||
// Target just the first matching limb
|
statusEffect.sourceBody = limb.body;
|
||||||
Limb limb = AnimController.GetLimb(limbType);
|
|
||||||
statusEffect.Apply(actionType, deltaTime, this, limb);
|
statusEffect.Apply(actionType, deltaTime, this, limb);
|
||||||
}
|
}
|
||||||
else if (statusEffect.HasTargetType(StatusEffect.TargetType.LastLimb))
|
}
|
||||||
|
else if (statusEffect.HasTargetType(StatusEffect.TargetType.LastLimb))
|
||||||
|
{
|
||||||
|
// Target just the last matching limb
|
||||||
|
Limb limb = AnimController.Limbs.LastOrDefault(l => l.type == limbType && !l.IsSevered && !l.Hidden);
|
||||||
|
if (limb != null)
|
||||||
{
|
{
|
||||||
// Target just the last matching limb
|
statusEffect.sourceBody = limb.body;
|
||||||
Limb limb = AnimController.Limbs.LastOrDefault(l => l.type == limbType && !l.IsSevered && !l.Hidden);
|
|
||||||
statusEffect.Apply(actionType, deltaTime, this, limb);
|
statusEffect.Apply(actionType, deltaTime, this, limb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (statusEffect.HasTargetType(StatusEffect.TargetType.This) || statusEffect.HasTargetType(StatusEffect.TargetType.Character))
|
||||||
|
{
|
||||||
|
statusEffect.Apply(actionType, deltaTime, this, this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (actionType != ActionType.OnDamaged && actionType != ActionType.OnSevered)
|
if (actionType != ActionType.OnDamaged && actionType != ActionType.OnSevered)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ namespace Barotrauma
|
|||||||
public int FaceAttachmentIndex { get; set; } = -1;
|
public int FaceAttachmentIndex { get; set; } = -1;
|
||||||
|
|
||||||
public XElement HairElement { get; set; }
|
public XElement HairElement { get; set; }
|
||||||
|
public XElement HairWithHatElement { get; set; }
|
||||||
public XElement BeardElement { get; set; }
|
public XElement BeardElement { get; set; }
|
||||||
public XElement MoustacheElement { get; set; }
|
public XElement MoustacheElement { get; set; }
|
||||||
public XElement FaceAttachment { get; set; }
|
public XElement FaceAttachment { get; set; }
|
||||||
@@ -1125,6 +1126,16 @@ namespace Barotrauma
|
|||||||
Head.HairElement = GetRandomElement(hairs);
|
Head.HairElement = GetRandomElement(hairs);
|
||||||
Head.HairIndex = hairs.IndexOf(Head.HairElement);
|
Head.HairIndex = hairs.IndexOf(Head.HairElement);
|
||||||
}
|
}
|
||||||
|
if (Head.HairElement != null)
|
||||||
|
{
|
||||||
|
int thisHairIndex = hairs.IndexOf(head.HairElement);
|
||||||
|
int hairWithHatIndex = head.HairElement.GetAttributeInt("replacewhenwearinghat", thisHairIndex);
|
||||||
|
if (thisHairIndex != hairWithHatIndex && hairWithHatIndex > -1 && hairWithHatIndex < hairs.Count)
|
||||||
|
{
|
||||||
|
head.HairWithHatElement = hairs[hairWithHatIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (IsValidIndex(Head.BeardIndex, beards))
|
if (IsValidIndex(Head.BeardIndex, beards))
|
||||||
{
|
{
|
||||||
Head.BeardElement = beards[Head.BeardIndex];
|
Head.BeardElement = beards[Head.BeardIndex];
|
||||||
|
|||||||
@@ -649,6 +649,8 @@ namespace Barotrauma
|
|||||||
if (attackElement != null)
|
if (attackElement != null)
|
||||||
{
|
{
|
||||||
attack.DamageMultiplier = attackElement.GetAttributeFloat("damagemultiplier", 1f);
|
attack.DamageMultiplier = attackElement.GetAttributeFloat("damagemultiplier", 1f);
|
||||||
|
attack.RangeMultiplier = attackElement.GetAttributeFloat("rangemultiplier", 1f);
|
||||||
|
attack.ImpactMultiplier = attackElement.GetAttributeFloat("impactmultiplier", 1f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
+3
-3
@@ -14,9 +14,9 @@ namespace Barotrauma
|
|||||||
NotDefined = 0,
|
NotDefined = 0,
|
||||||
Walk = 1,
|
Walk = 1,
|
||||||
Run = 2,
|
Run = 2,
|
||||||
Crouch = 3,
|
SwimSlow = 3,
|
||||||
SwimSlow = 4,
|
SwimFast = 4,
|
||||||
SwimFast = 5
|
Crouch = 5
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class GroundedMovementParams : AnimationParams
|
abstract class GroundedMovementParams : AnimationParams
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
|
|
||||||
private float skillIncreasePerRepairedStructureDamage;
|
private float skillIncreasePerRepairedStructureDamage;
|
||||||
[Serialize(0.005f, true)]
|
[Serialize(0.0025f, true)]
|
||||||
public float SkillIncreasePerRepairedStructureDamage
|
public float SkillIncreasePerRepairedStructureDamage
|
||||||
{
|
{
|
||||||
get { return skillIncreasePerRepairedStructureDamage * GetCurrentSkillGainMultiplier(); }
|
get { return skillIncreasePerRepairedStructureDamage * GetCurrentSkillGainMultiplier(); }
|
||||||
|
|||||||
@@ -521,6 +521,7 @@ namespace Barotrauma
|
|||||||
if (targetCharacter == null) { return; }
|
if (targetCharacter == null) { return; }
|
||||||
|
|
||||||
targetCharacter.GodMode = !targetCharacter.GodMode;
|
targetCharacter.GodMode = !targetCharacter.GodMode;
|
||||||
|
NewMessage((targetCharacter.GodMode ? "Enabled godmode on " : "Disabled godmode on " + targetCharacter.Name), Color.White);
|
||||||
},
|
},
|
||||||
() =>
|
() =>
|
||||||
{
|
{
|
||||||
@@ -1042,6 +1043,20 @@ namespace Barotrauma
|
|||||||
throw new Exception("crash command issued");
|
throw new Exception("crash command issued");
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
commands.Add(new Command("fastforward", "fastforward [seconds]: Fast forwards the game by x seconds. Note that large numbers may cause a long freeze.", (string[] args) =>
|
||||||
|
{
|
||||||
|
float seconds = 0;
|
||||||
|
if (args.Length > 0) { float.TryParse(args[0], out seconds); }
|
||||||
|
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
|
||||||
|
sw.Start();
|
||||||
|
for (int i = 0; i < seconds * Timing.FixedUpdateRate; i++)
|
||||||
|
{
|
||||||
|
Screen.Selected?.Update(Timing.Step);
|
||||||
|
}
|
||||||
|
sw.Stop();
|
||||||
|
NewMessage($"Fast-forwarded by {seconds} seconds (took {sw.ElapsedMilliseconds / 1000.0f} s).");
|
||||||
|
}));
|
||||||
|
|
||||||
commands.Add(new Command("removecharacter", "removecharacter [character name]: Immediately deletes the specified character.", (string[] args) =>
|
commands.Add(new Command("removecharacter", "removecharacter [character name]: Immediately deletes the specified character.", (string[] args) =>
|
||||||
{
|
{
|
||||||
if (args.Length == 0) { return; }
|
if (args.Length == 0) { return; }
|
||||||
|
|||||||
@@ -351,7 +351,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<string> GetDebugStatistics(int simulatedRoundCount = 100, Func<MonsterEvent, bool> filter = null)
|
public static List<string> GetDebugStatistics(int simulatedRoundCount = 100, Func<MonsterEvent, bool> filter = null, bool fullLog = false)
|
||||||
{
|
{
|
||||||
List<string> debugLines = new List<string>();
|
List<string> debugLines = new List<string>();
|
||||||
|
|
||||||
@@ -365,7 +365,7 @@ namespace Barotrauma
|
|||||||
stats.Add(newStats);
|
stats.Add(newStats);
|
||||||
}
|
}
|
||||||
debugLines.Add($"Event stats ({eventSet.DebugIdentifier}): ");
|
debugLines.Add($"Event stats ({eventSet.DebugIdentifier}): ");
|
||||||
LogEventStats(stats, debugLines);
|
LogEventStats(stats, debugLines, fullLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
return debugLines;
|
return debugLines;
|
||||||
@@ -415,14 +415,19 @@ namespace Barotrauma
|
|||||||
if (eventPrefab.EventType == typeof(MonsterEvent) && eventPrefab.TryCreateInstance(out MonsterEvent monsterEvent))
|
if (eventPrefab.EventType == typeof(MonsterEvent) && eventPrefab.TryCreateInstance(out MonsterEvent monsterEvent))
|
||||||
{
|
{
|
||||||
if (filter != null && !filter(monsterEvent)) { return; }
|
if (filter != null && !filter(monsterEvent)) { return; }
|
||||||
|
|
||||||
float spawnProbability = monsterEvent.Prefab.Probability;
|
float spawnProbability = monsterEvent.Prefab.Probability;
|
||||||
if (Rand.Value() > spawnProbability) { return; }
|
if (Rand.Value() > spawnProbability) { return; }
|
||||||
|
|
||||||
string character = monsterEvent.speciesName;
|
|
||||||
int count = Rand.Range(monsterEvent.MinAmount, monsterEvent.MaxAmount + 1);
|
int count = Rand.Range(monsterEvent.MinAmount, monsterEvent.MaxAmount + 1);
|
||||||
if (count <= 0) { return; }
|
if (count <= 0) { return; }
|
||||||
if (!stats.MonsterCounts.ContainsKey(character)) { stats.MonsterCounts[character] = 0; }
|
string character = monsterEvent.speciesName;
|
||||||
|
if (stats.MonsterCounts.TryGetValue(character, out int currentCount))
|
||||||
|
{
|
||||||
|
if (currentCount >= monsterEvent.MaxAmountPerLevel) { return; }
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stats.MonsterCounts[character] = 0;
|
||||||
|
}
|
||||||
stats.MonsterCounts[character] += count;
|
stats.MonsterCounts[character] += count;
|
||||||
|
|
||||||
var aiElement = CharacterPrefab.FindBySpeciesName(character)?.XDocument?.Root?.GetChildElement("ai");
|
var aiElement = CharacterPrefab.FindBySpeciesName(character)?.XDocument?.Root?.GetChildElement("ai");
|
||||||
@@ -433,7 +438,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void LogEventStats(List<EventDebugStats> stats, List<string> debugLines)
|
static void LogEventStats(List<EventDebugStats> stats, List<string> debugLines, bool fullLog)
|
||||||
{
|
{
|
||||||
if (stats.Count == 0 || stats.All(s => s.MonsterCounts.Values.Sum() == 0))
|
if (stats.Count == 0 || stats.All(s => s.MonsterCounts.Values.Sum() == 0))
|
||||||
{
|
{
|
||||||
@@ -442,28 +447,42 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
var allMonsters = new Dictionary<string, int>();
|
||||||
|
foreach (var stat in stats)
|
||||||
|
{
|
||||||
|
foreach (var monster in stat.MonsterCounts)
|
||||||
|
{
|
||||||
|
if (!allMonsters.TryAdd(monster.Key, monster.Value))
|
||||||
|
{
|
||||||
|
allMonsters[monster.Key] += monster.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
allMonsters = allMonsters.OrderBy(m => m.Key).ToDictionary(m => m.Key, m => m.Value);
|
||||||
stats.Sort((s1, s2) => s1.MonsterCounts.Values.Sum().CompareTo(s2.MonsterCounts.Values.Sum()));
|
stats.Sort((s1, s2) => s1.MonsterCounts.Values.Sum().CompareTo(s2.MonsterCounts.Values.Sum()));
|
||||||
debugLines.Add($" Minimum monster count: {stats.First().MonsterCounts.Values.Sum()}");
|
debugLines.Add($" Average monster count: {StringFormatter.FormatZeroDecimal((float)stats.Average(s => s.MonsterCounts.Values.Sum()))} (Min: {stats.First().MonsterCounts.Values.Sum()}, Max: {stats.Last().MonsterCounts.Values.Sum()})");
|
||||||
debugLines.Add($" {LogMonsterCounts(stats.First())}");
|
debugLines.Add($" {LogMonsterCounts(allMonsters, divider: stats.Count)}");
|
||||||
debugLines.Add($" Median monster count: {stats[stats.Count / 2].MonsterCounts.Values.Sum()}");
|
if (fullLog)
|
||||||
debugLines.Add($" {LogMonsterCounts(stats[stats.Count / 2])}");
|
{
|
||||||
debugLines.Add($" Maximum monster count: {stats.Last().MonsterCounts.Values.Sum()}");
|
debugLines.Add($" All samples:");
|
||||||
debugLines.Add($" {LogMonsterCounts(stats.Last())}");
|
stats.ForEach(s => debugLines.Add($" {LogMonsterCounts(s.MonsterCounts)}"));
|
||||||
debugLines.Add($" Average monster count: {StringFormatter.FormatZeroDecimal((float)stats.Average(s => s.MonsterCounts.Values.Sum()))}");
|
}
|
||||||
debugLines.Add($" ");
|
|
||||||
|
|
||||||
stats.Sort((s1, s2) => s1.MonsterStrength.CompareTo(s2.MonsterStrength));
|
stats.Sort((s1, s2) => s1.MonsterStrength.CompareTo(s2.MonsterStrength));
|
||||||
debugLines.Add($" Minimum monster strength: {StringFormatter.FormatZeroDecimal(stats.First().MonsterStrength)}");
|
debugLines.Add($" Average monster strength: {StringFormatter.FormatZeroDecimal(stats.Average(s => s.MonsterStrength))} (Min: {StringFormatter.FormatZeroDecimal(stats.First().MonsterStrength)}, Max: {StringFormatter.FormatZeroDecimal(stats.Last().MonsterStrength)})");
|
||||||
debugLines.Add($" Median monster strength: {StringFormatter.FormatZeroDecimal(stats[stats.Count / 2].MonsterStrength)}");
|
|
||||||
debugLines.Add($" Maximum monster strength: {StringFormatter.FormatZeroDecimal(stats.Last().MonsterStrength)}");
|
|
||||||
debugLines.Add($" Average monster strength: {StringFormatter.FormatZeroDecimal(stats.Average(s => s.MonsterStrength))}");
|
|
||||||
debugLines.Add($" ");
|
debugLines.Add($" ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static string LogMonsterCounts(EventDebugStats stats)
|
static string LogMonsterCounts(Dictionary<string, int> stats, float divider = 0)
|
||||||
{
|
{
|
||||||
return string.Join(", ", stats.MonsterCounts.Select(mc => mc.Key + " x " + mc.Value));
|
if (divider > 0)
|
||||||
|
{
|
||||||
|
return string.Join("\n ", stats.Select(mc => mc.Key + " x " + (mc.Value / divider).FormatSingleDecimal()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return string.Join(", ", stats.Select(mc => mc.Key + " x " + mc.Value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Barotrauma.Items.Components;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -58,6 +59,18 @@ namespace Barotrauma
|
|||||||
if (IsClient) { return; }
|
if (IsClient) { return; }
|
||||||
if (!swarmSpawned && level.CheckBeaconActive())
|
if (!swarmSpawned && level.CheckBeaconActive())
|
||||||
{
|
{
|
||||||
|
List<Submarine> connectedSubs = level.BeaconStation.GetConnectedSubs();
|
||||||
|
foreach (Item item in Item.ItemList)
|
||||||
|
{
|
||||||
|
if (!connectedSubs.Contains(item.Submarine)) { continue; }
|
||||||
|
if (item.GetComponent<PowerTransfer>() != null ||
|
||||||
|
item.GetComponent<PowerContainer>() != null ||
|
||||||
|
item.GetComponent<Reactor>() != null)
|
||||||
|
{
|
||||||
|
item.Indestructible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
State = 1;
|
State = 1;
|
||||||
|
|
||||||
Vector2 spawnPos = level.BeaconStation.WorldPosition;
|
Vector2 spawnPos = level.BeaconStation.WorldPosition;
|
||||||
|
|||||||
@@ -60,8 +60,16 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string itemIdentifier = prefab.ConfigElement.GetAttributeString("itemidentifier", "");
|
string itemIdentifier = prefab.ConfigElement.GetAttributeString("itemidentifier", null);
|
||||||
itemPrefab = MapEntityPrefab.Find(null, itemIdentifier) as ItemPrefab;
|
if (itemIdentifier != null)
|
||||||
|
{
|
||||||
|
itemPrefab = MapEntityPrefab.Find(null, itemIdentifier) as ItemPrefab;
|
||||||
|
}
|
||||||
|
if (itemPrefab == null)
|
||||||
|
{
|
||||||
|
string itemTag = prefab.ConfigElement.GetAttributeString("itemtag", "");
|
||||||
|
itemPrefab = MapEntityPrefab.GetRandom(p => p.Tags.Contains(itemTag), Rand.RandSync.Unsynced) as ItemPrefab;
|
||||||
|
}
|
||||||
if (itemPrefab == null)
|
if (itemPrefab == null)
|
||||||
{
|
{
|
||||||
DebugConsole.ThrowError("Error in SalvageMission - couldn't find an item prefab with the identifier " + itemIdentifier);
|
DebugConsole.ThrowError("Error in SalvageMission - couldn't find an item prefab with the identifier " + itemIdentifier);
|
||||||
@@ -150,8 +158,8 @@ namespace Barotrauma
|
|||||||
if (item == null)
|
if (item == null)
|
||||||
{
|
{
|
||||||
item = new Item(itemPrefab, position, null);
|
item = new Item(itemPrefab, position, null);
|
||||||
|
item.body.SetTransformIgnoreContacts(item.body.SimPosition, item.body.Rotation);
|
||||||
item.body.FarseerBody.BodyType = BodyType.Kinematic;
|
item.body.FarseerBody.BodyType = BodyType.Kinematic;
|
||||||
item.FindHull();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < statusEffects.Count; i++)
|
for (int i = 0; i < statusEffects.Count; i++)
|
||||||
@@ -192,7 +200,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
if (validContainers.Any())
|
if (validContainers.Any())
|
||||||
{
|
{
|
||||||
var selectedContainer = validContainers.GetRandom();
|
var selectedContainer = validContainers.GetRandom(Rand.RandSync.Unsynced);
|
||||||
if (selectedContainer.Combine(item, user: null))
|
if (selectedContainer.Combine(item, user: null))
|
||||||
{
|
{
|
||||||
#if SERVER
|
#if SERVER
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private bool spawnPending;
|
private bool spawnPending;
|
||||||
|
|
||||||
private readonly int maxAmountPerLevel = int.MaxValue;
|
public readonly int MaxAmountPerLevel = int.MaxValue;
|
||||||
|
|
||||||
public List<Character> Monsters => monsters;
|
public List<Character> Monsters => monsters;
|
||||||
public Vector2? SpawnPos => spawnPos;
|
public Vector2? SpawnPos => spawnPos;
|
||||||
@@ -74,7 +74,7 @@ namespace Barotrauma
|
|||||||
minAmount = prefab.ConfigElement.GetAttributeInt("minamount", defaultAmount);
|
minAmount = prefab.ConfigElement.GetAttributeInt("minamount", defaultAmount);
|
||||||
maxAmount = Math.Max(prefab.ConfigElement.GetAttributeInt("maxamount", 1), minAmount);
|
maxAmount = Math.Max(prefab.ConfigElement.GetAttributeInt("maxamount", 1), minAmount);
|
||||||
|
|
||||||
maxAmountPerLevel = prefab.ConfigElement.GetAttributeInt("maxamountperlevel", int.MaxValue);
|
MaxAmountPerLevel = prefab.ConfigElement.GetAttributeInt("maxamountperlevel", int.MaxValue);
|
||||||
|
|
||||||
var spawnPosTypeStr = prefab.ConfigElement.GetAttributeString("spawntype", "");
|
var spawnPosTypeStr = prefab.ConfigElement.GetAttributeString("spawntype", "");
|
||||||
if (string.IsNullOrWhiteSpace(spawnPosTypeStr) ||
|
if (string.IsNullOrWhiteSpace(spawnPosTypeStr) ||
|
||||||
@@ -367,9 +367,9 @@ namespace Barotrauma
|
|||||||
|
|
||||||
if (spawnPos == null)
|
if (spawnPos == null)
|
||||||
{
|
{
|
||||||
if (maxAmountPerLevel < int.MaxValue)
|
if (MaxAmountPerLevel < int.MaxValue)
|
||||||
{
|
{
|
||||||
if (Character.CharacterList.Count(c => c.SpeciesName == speciesName) >= maxAmountPerLevel)
|
if (Character.CharacterList.Count(c => c.SpeciesName == speciesName) >= MaxAmountPerLevel)
|
||||||
{
|
{
|
||||||
disallowed = true;
|
disallowed = true;
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -507,20 +507,25 @@ namespace Barotrauma
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var allPackages = GameMain.Config?.AllEnabledPackages.ToList();
|
if (GameMain.Config != null)
|
||||||
if (allPackages?.Count > 0)
|
|
||||||
{
|
{
|
||||||
List<string> packageNames = new List<string>();
|
var allPackages = GameMain.Config.AllEnabledPackages.ToList();
|
||||||
foreach (ContentPackage cp in allPackages)
|
if (allPackages?.Count > 0)
|
||||||
{
|
{
|
||||||
string sanitizedName = cp.Name.Replace(":", "").Replace(" ", "");
|
List<string> packageNames = new List<string>();
|
||||||
sanitizedName = sanitizedName.Substring(0, Math.Min(32, sanitizedName.Length));
|
foreach (ContentPackage cp in allPackages)
|
||||||
packageNames.Add(sanitizedName);
|
{
|
||||||
loadedImplementation?.AddDesignEvent("ContentPackage:" + sanitizedName);
|
string sanitizedName = cp.Name.Replace(":", "").Replace(" ", "");
|
||||||
|
sanitizedName = sanitizedName.Substring(0, Math.Min(32, sanitizedName.Length));
|
||||||
|
packageNames.Add(sanitizedName);
|
||||||
|
loadedImplementation?.AddDesignEvent("ContentPackage:" + sanitizedName);
|
||||||
|
}
|
||||||
|
packageNames.Sort();
|
||||||
|
loadedImplementation?.AddDesignEvent("AllContentPackages:" + string.Join(", ", packageNames));
|
||||||
}
|
}
|
||||||
packageNames.Sort();
|
loadedImplementation?.AddDesignEvent("Language:" + GameMain.Config.Language);
|
||||||
loadedImplementation?.AddDesignEvent("AllContentPackages:" + string.Join(", ", packageNames));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static partial void InitKeys();
|
static partial void InitKeys();
|
||||||
|
|||||||
@@ -27,21 +27,85 @@ namespace Barotrauma
|
|||||||
class SoldItem
|
class SoldItem
|
||||||
{
|
{
|
||||||
public ItemPrefab ItemPrefab { get; }
|
public ItemPrefab ItemPrefab { get; }
|
||||||
public ushort ID { get; }
|
public ushort ID { get; private set; }
|
||||||
public bool Removed { get; set; }
|
public bool Removed { get; set; }
|
||||||
public byte SellerID { get; }
|
public byte SellerID { get; }
|
||||||
|
public SellOrigin Origin { get; }
|
||||||
|
|
||||||
public SoldItem(ItemPrefab itemPrefab, ushort id, bool removed, byte sellerId)
|
public enum SellOrigin
|
||||||
|
{
|
||||||
|
Character,
|
||||||
|
Submarine
|
||||||
|
}
|
||||||
|
|
||||||
|
public SoldItem(ItemPrefab itemPrefab, ushort id, bool removed, byte sellerId, SellOrigin origin)
|
||||||
{
|
{
|
||||||
ItemPrefab = itemPrefab;
|
ItemPrefab = itemPrefab;
|
||||||
ID = id;
|
ID = id;
|
||||||
Removed = removed;
|
Removed = removed;
|
||||||
SellerID = sellerId;
|
SellerID = sellerId;
|
||||||
|
Origin = origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetItemId(ushort id)
|
||||||
|
{
|
||||||
|
if (ID != Entity.NullEntityID)
|
||||||
|
{
|
||||||
|
DebugConsole.ShowError("Error setting SoldItem.ID: ID has already been set and should not be changed.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ID = id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
partial class CargoManager
|
partial class CargoManager
|
||||||
{
|
{
|
||||||
|
private class SoldEntity
|
||||||
|
{
|
||||||
|
public enum SellStatus
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Entity sold in SP. Or, entity sold by client and confirmed by server in MP.
|
||||||
|
/// </summary>
|
||||||
|
Confirmed,
|
||||||
|
/// <summary>
|
||||||
|
/// Entity sold by client in MP. Client has received at least one update from server after selling, but this entity wasn't yet confirmed.
|
||||||
|
/// </summary>
|
||||||
|
Unconfirmed,
|
||||||
|
/// <summary>
|
||||||
|
/// Entity sold by client in MP. Client hasn't yet received an update from server after selling.
|
||||||
|
/// </summary>
|
||||||
|
Local
|
||||||
|
}
|
||||||
|
|
||||||
|
public Item Item { get; private set; }
|
||||||
|
public ItemPrefab ItemPrefab { get; }
|
||||||
|
public SellStatus Status { get; set; }
|
||||||
|
|
||||||
|
public SoldEntity(Item item, SellStatus status)
|
||||||
|
{
|
||||||
|
Item = item;
|
||||||
|
ItemPrefab = item?.Prefab;
|
||||||
|
Status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SoldEntity(ItemPrefab itemPrefab, SellStatus status)
|
||||||
|
{
|
||||||
|
ItemPrefab = itemPrefab;
|
||||||
|
Status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetItem(Item item)
|
||||||
|
{
|
||||||
|
if (Item != null)
|
||||||
|
{
|
||||||
|
DebugConsole.ShowError($"Trying to set SoldEntity.Item, but it's already set!\n{Environment.StackTrace.CleanupStackTrace()}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Item = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public const int MaxQuantity = 100;
|
public const int MaxQuantity = 100;
|
||||||
|
|
||||||
public List<PurchasedItem> ItemsInBuyCrate { get; } = new List<PurchasedItem>();
|
public List<PurchasedItem> ItemsInBuyCrate { get; } = new List<PurchasedItem>();
|
||||||
@@ -92,7 +156,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public void ModifyItemQuantityInBuyCrate(ItemPrefab itemPrefab, int changeInQuantity)
|
public void ModifyItemQuantityInBuyCrate(ItemPrefab itemPrefab, int changeInQuantity)
|
||||||
{
|
{
|
||||||
PurchasedItem itemInCrate = ItemsInBuyCrate.Find(i => i.ItemPrefab == itemPrefab);
|
var itemInCrate = ItemsInBuyCrate.Find(i => i.ItemPrefab == itemPrefab);
|
||||||
if (itemInCrate != null)
|
if (itemInCrate != null)
|
||||||
{
|
{
|
||||||
itemInCrate.Quantity += changeInQuantity;
|
itemInCrate.Quantity += changeInQuantity;
|
||||||
@@ -109,6 +173,25 @@ namespace Barotrauma
|
|||||||
OnItemsInBuyCrateChanged?.Invoke();
|
OnItemsInBuyCrateChanged?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ModifyItemQuantityInSubSellCrate(ItemPrefab itemPrefab, int changeInQuantity)
|
||||||
|
{
|
||||||
|
var itemInCrate = ItemsInSellFromSubCrate.Find(i => i.ItemPrefab == itemPrefab);
|
||||||
|
if (itemInCrate != null)
|
||||||
|
{
|
||||||
|
itemInCrate.Quantity += changeInQuantity;
|
||||||
|
if (itemInCrate.Quantity < 1)
|
||||||
|
{
|
||||||
|
ItemsInSellFromSubCrate.Remove(itemInCrate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (changeInQuantity > 0)
|
||||||
|
{
|
||||||
|
itemInCrate = new PurchasedItem(itemPrefab, changeInQuantity);
|
||||||
|
ItemsInSellFromSubCrate.Add(itemInCrate);
|
||||||
|
}
|
||||||
|
OnItemsInSellFromSubCrateChanged?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
public void PurchaseItems(List<PurchasedItem> itemsToPurchase, bool removeFromCrate)
|
public void PurchaseItems(List<PurchasedItem> itemsToPurchase, bool removeFromCrate)
|
||||||
{
|
{
|
||||||
// Check all the prices before starting the transaction
|
// Check all the prices before starting the transaction
|
||||||
@@ -185,6 +268,82 @@ namespace Barotrauma
|
|||||||
OnPurchasedItemsChanged?.Invoke();
|
OnPurchasedItemsChanged?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Dictionary<ItemPrefab, int> UndeterminedSoldEntities { get; } = new Dictionary<ItemPrefab, int>();
|
||||||
|
|
||||||
|
public IEnumerable<Item> GetSellableItemsFromSub()
|
||||||
|
{
|
||||||
|
if (Submarine.MainSub == null) { return new List<Item>(); }
|
||||||
|
var confirmedSoldEntities = Enumerable.Empty<SoldEntity>();
|
||||||
|
UndeterminedSoldEntities.Clear();
|
||||||
|
#if CLIENT
|
||||||
|
confirmedSoldEntities = GetConfirmedSoldEntities();
|
||||||
|
foreach (var soldEntity in SoldEntities)
|
||||||
|
{
|
||||||
|
if (soldEntity.Item != null) { continue; }
|
||||||
|
if (UndeterminedSoldEntities.TryGetValue(soldEntity.ItemPrefab, out int count))
|
||||||
|
{
|
||||||
|
UndeterminedSoldEntities[soldEntity.ItemPrefab] = count + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UndeterminedSoldEntities.Add(soldEntity.ItemPrefab, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return Submarine.MainSub.GetItems(true).FindAll(item =>
|
||||||
|
{
|
||||||
|
if (!IsItemSellable(item, confirmedSoldEntities)) { return false; }
|
||||||
|
if (item.GetRootInventoryOwner() is Character) { return false; }
|
||||||
|
if (!item.Components.All(c => !(c is Holdable h) || !h.Attachable || !h.Attached)) { return false; }
|
||||||
|
if (!item.Components.All(c => !(c is Wire w) || w.Connections.All(c => c == null))) { return false; }
|
||||||
|
if (!ItemAndAllContainersInteractable(item)) { return false; }
|
||||||
|
if (item.GetRootContainer() is Item rootContainer && rootContainer.HasTag("donttakeitems")) { return false; }
|
||||||
|
return true;
|
||||||
|
}).Distinct();
|
||||||
|
|
||||||
|
static bool ItemAndAllContainersInteractable(Item item)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (!item.IsPlayerTeamInteractable) { return false; }
|
||||||
|
item = item.Container;
|
||||||
|
} while (item != null);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsItemSellable(Item item, IEnumerable<SoldEntity> confirmedItems)
|
||||||
|
{
|
||||||
|
if (item.Removed) { return false; }
|
||||||
|
if (!item.Prefab.CanBeSold) { return false; }
|
||||||
|
if (item.SpawnedInCurrentOutpost) { return false; }
|
||||||
|
if (!item.Prefab.AllowSellingWhenBroken && item.ConditionPercentage < 90.0f) { return false; }
|
||||||
|
if (confirmedItems.Any(ci => ci.Item == item)) { return false; }
|
||||||
|
if (UndeterminedSoldEntities.TryGetValue(item.Prefab, out int count))
|
||||||
|
{
|
||||||
|
int newCount = count - 1;
|
||||||
|
if (newCount > 0)
|
||||||
|
{
|
||||||
|
UndeterminedSoldEntities[item.Prefab] = newCount;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UndeterminedSoldEntities.Remove(item.Prefab);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (item.OwnInventory?.Container is ItemContainer itemContainer)
|
||||||
|
{
|
||||||
|
var containedItems = item.ContainedItems;
|
||||||
|
if (containedItems.None()) { return true; }
|
||||||
|
// Allow selling the item if contained items are unsellable and set to be removed on deconstruct
|
||||||
|
if (itemContainer.RemoveContainedItemsOnDeconstruct && containedItems.All(it => !it.Prefab.CanBeSold)) { return true; }
|
||||||
|
// Otherwise there must be no contained items or the contained items must be confirmed as sold
|
||||||
|
if (!containedItems.All(it => confirmedItems.Any(ci => ci.Item == it))) { return false; }
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public static void CreateItems(List<PurchasedItem> itemsToSpawn, Submarine sub)
|
public static void CreateItems(List<PurchasedItem> itemsToSpawn, Submarine sub)
|
||||||
{
|
{
|
||||||
if (itemsToSpawn.Count == 0) { return; }
|
if (itemsToSpawn.Count == 0) { return; }
|
||||||
|
|||||||
@@ -448,19 +448,21 @@ namespace Barotrauma
|
|||||||
filteredCharacters = filteredCharacters.Union(extraCharacters);
|
filteredCharacters = filteredCharacters.Union(extraCharacters);
|
||||||
}
|
}
|
||||||
return filteredCharacters
|
return filteredCharacters
|
||||||
// 1. Prioritize those who are on the same submarine than the controlled character
|
// Prioritize those who are on the same submarine as the controlled character
|
||||||
.OrderByDescending(c => Character.Controlled == null || c.Submarine == Character.Controlled.Submarine)
|
.OrderByDescending(c => Character.Controlled == null || c.Submarine == Character.Controlled.Submarine)
|
||||||
// 2. Prioritize those who are already ordered to operate the device
|
// Prioritize those who are already ordered to operate the device
|
||||||
.ThenByDescending(c => order.Category == OrderCategory.Operate && c.CurrentOrders.Any(o => o.Order != null && o.Order.Identifier == order.Identifier && o.Order.TargetEntity == order.TargetEntity))
|
.ThenByDescending(c => order.Category == OrderCategory.Operate && c.CurrentOrders.Any(o => o.Order != null && o.Order.Identifier == order.Identifier && o.Order.TargetEntity == order.TargetEntity))
|
||||||
// 3. Prioritize those with the appropriate job for the order
|
// Prioritize those with the appropriate job for the order
|
||||||
.ThenByDescending(c => order.HasAppropriateJob(c))
|
.ThenByDescending(c => order.HasAppropriateJob(c))
|
||||||
// 4. Prioritize those who don't yet have another Operate order of the same kind (which allows quick-assigning multiple Operate orders to different characters)
|
// Prioritize those who don't yet have the same order (which allows quick-assigning the order to different characters)
|
||||||
.ThenByDescending(c => order.Category == OrderCategory.Operate && c.CurrentOrders.None(o => o.Order != null && o.Order.Identifier == order.Identifier))
|
.ThenByDescending(c => c.CurrentOrders.None(o => o.Order != null && o.Order.Identifier == order.Identifier))
|
||||||
// 5. Prioritize bots over player controlled characters
|
// Prioritize those with the preferred job for the order
|
||||||
|
.ThenByDescending(c => order.HasPreferredJob(c))
|
||||||
|
// Prioritize bots over player-controlled characters
|
||||||
.ThenByDescending(c => c.IsBot)
|
.ThenByDescending(c => c.IsBot)
|
||||||
// 6. Use the priority value of the current objective
|
// Prioritize those with a lower current objective priority
|
||||||
.ThenBy(c => c.AIController is HumanAIController humanAI ? humanAI.ObjectiveManager.CurrentObjective?.Priority : 0)
|
.ThenBy(c => c.AIController is HumanAIController humanAI ? humanAI.ObjectiveManager.CurrentObjective?.Priority : 0)
|
||||||
// 7. Prioritize those with the best skill for the order
|
// Prioritize those with a higher order skill level
|
||||||
.ThenByDescending(c => c.GetSkillLevel(order.AppropriateSkill));
|
.ThenByDescending(c => c.GetSkillLevel(order.AppropriateSkill));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -74,6 +74,13 @@ namespace Barotrauma.Items.Components
|
|||||||
set;
|
set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Serialize(true, false, description: "Should the OnUse StatusEffects trigger when docking (on vanilla docking ports these effects emit particles and play a sound).)")]
|
||||||
|
public bool ApplyEffectsOnDocking
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
[Editable, Serialize(DirectionType.None, false, description: "Which direction the port is allowed to dock in. For example, \"Top\" would mean the port can dock to another port above it.\n"+
|
[Editable, Serialize(DirectionType.None, false, description: "Which direction the port is allowed to dock in. For example, \"Top\" would mean the port can dock to another port above it.\n"+
|
||||||
"Normally there's no need to touch this setting, but if you notice the docking position is incorrect (for example due to some unusual docking port configuration without hulls or doors), you can use this to enforce the direction.")]
|
"Normally there's no need to touch this setting, but if you notice the docking position is incorrect (for example due to some unusual docking port configuration without hulls or doors), you can use this to enforce the direction.")]
|
||||||
public DirectionType ForceDockingDirection { get; set; }
|
public DirectionType ForceDockingDirection { get; set; }
|
||||||
@@ -261,7 +268,7 @@ namespace Barotrauma.Items.Components
|
|||||||
DockingDir = GetDir(DockingTarget);
|
DockingDir = GetDir(DockingTarget);
|
||||||
DockingTarget.DockingDir = -DockingDir;
|
DockingTarget.DockingDir = -DockingDir;
|
||||||
|
|
||||||
if (applyEffects)
|
if (applyEffects && ApplyEffectsOnDocking)
|
||||||
{
|
{
|
||||||
ApplyStatusEffects(ActionType.OnUse, 1.0f);
|
ApplyStatusEffects(ActionType.OnUse, 1.0f);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
if (holdable.Attached)
|
if (holdable.Attached)
|
||||||
{
|
{
|
||||||
GameAnalyticsManager.AddDesignEvent("ResourceCollected:" + (GameMain.GameSession?.GameMode?.Name ?? "none") + ":" + item.Prefab.Identifier);
|
GameAnalyticsManager.AddDesignEvent("ResourceCollected:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "none") + ":" + item.Prefab.Identifier);
|
||||||
holdable.DeattachFromWall();
|
holdable.DeattachFromWall();
|
||||||
}
|
}
|
||||||
trigger.Enabled = false;
|
trigger.Enabled = false;
|
||||||
|
|||||||
@@ -841,9 +841,9 @@ namespace Barotrauma.Items.Components
|
|||||||
if (statusEffectLists == null) { return; }
|
if (statusEffectLists == null) { return; }
|
||||||
if (!statusEffectLists.TryGetValue(actionType, out List<StatusEffect> statusEffects)) { return; }
|
if (!statusEffectLists.TryGetValue(actionType, out List<StatusEffect> statusEffects)) { return; }
|
||||||
|
|
||||||
currentTargets.Clear();
|
|
||||||
foreach (StatusEffect effect in statusEffects)
|
foreach (StatusEffect effect in statusEffects)
|
||||||
{
|
{
|
||||||
|
currentTargets.Clear();
|
||||||
effect.SetUser(user);
|
effect.SetUser(user);
|
||||||
if (effect.HasTargetType(StatusEffect.TargetType.UseTarget))
|
if (effect.HasTargetType(StatusEffect.TargetType.UseTarget))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -300,7 +300,7 @@ namespace Barotrauma.Items.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GameAnalyticsManager.AddDesignEvent("ItemDeconstructed:" + (GameMain.GameSession?.GameMode?.Name ?? "none") + ":" + targetItem.prefab.Identifier);
|
GameAnalyticsManager.AddDesignEvent("ItemDeconstructed:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "none") + ":" + targetItem.prefab.Identifier);
|
||||||
|
|
||||||
if (targetItem.AllowDeconstruct && allowRemove)
|
if (targetItem.AllowDeconstruct && allowRemove)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ namespace Barotrauma.Items.Components
|
|||||||
prevVoltage = Voltage;
|
prevVoltage = Voltage;
|
||||||
hasPower = Voltage > MinVoltage;
|
hasPower = Voltage > MinVoltage;
|
||||||
|
|
||||||
Force = MathHelper.Lerp(force, (Voltage < MinVoltage) ? 0.0f : targetForce, 0.1f);
|
Force = MathHelper.Lerp(force, (Voltage < MinVoltage) ? 0.0f : targetForce, deltaTime * 10.0f);
|
||||||
if (Math.Abs(Force) > 1.0f)
|
if (Math.Abs(Force) > 1.0f)
|
||||||
{
|
{
|
||||||
float voltageFactor = MinVoltage <= 0.0f ? 1.0f : Math.Min(Voltage, 1.0f);
|
float voltageFactor = MinVoltage <= 0.0f ? 1.0f : Math.Min(Voltage, 1.0f);
|
||||||
@@ -137,17 +137,18 @@ namespace Barotrauma.Items.Components
|
|||||||
currForce *= MathHelper.Lerp(0.5f, 2.0f, condition);
|
currForce *= MathHelper.Lerp(0.5f, 2.0f, condition);
|
||||||
if (item.Submarine.FlippedX) { currForce *= -1; }
|
if (item.Submarine.FlippedX) { currForce *= -1; }
|
||||||
Vector2 forceVector = new Vector2(currForce, 0);
|
Vector2 forceVector = new Vector2(currForce, 0);
|
||||||
item.Submarine.ApplyForce(forceVector);
|
item.Submarine.ApplyForce(forceVector * deltaTime * Timing.FixedUpdateRate);
|
||||||
UpdatePropellerDamage(deltaTime);
|
UpdatePropellerDamage(deltaTime);
|
||||||
#if CLIENT
|
#if CLIENT
|
||||||
particleTimer -= deltaTime;
|
float particleInterval = 1.0f / particlesPerSec;
|
||||||
if (particleTimer <= 0.0f)
|
particleTimer += deltaTime;
|
||||||
|
while (particleTimer > particleInterval)
|
||||||
{
|
{
|
||||||
Vector2 particleVel = -forceVector.ClampLength(5000.0f) / 5.0f;
|
Vector2 particleVel = -forceVector.ClampLength(5000.0f) / 5.0f;
|
||||||
GameMain.ParticleManager.CreateParticle("bubbles", item.WorldPosition + PropellerPos * item.Scale,
|
GameMain.ParticleManager.CreateParticle("bubbles", item.WorldPosition + PropellerPos * item.Scale,
|
||||||
particleVel * Rand.Range(0.9f, 1.1f),
|
particleVel * Rand.Range(0.8f, 1.1f),
|
||||||
0.0f, item.CurrentHull);
|
0.0f, item.CurrentHull);
|
||||||
particleTimer = 1.0f / particlesPerSec;
|
particleTimer -= particleInterval;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -397,7 +397,7 @@ namespace Barotrauma.Items.Components
|
|||||||
for (int i = 0; i < (int)fabricationitemAmount.Value; i++)
|
for (int i = 0; i < (int)fabricationitemAmount.Value; i++)
|
||||||
{
|
{
|
||||||
float outCondition = fabricatedItem.OutCondition;
|
float outCondition = fabricatedItem.OutCondition;
|
||||||
GameAnalyticsManager.AddDesignEvent("ItemFabricated:" + (GameMain.GameSession?.GameMode?.Name ?? "none") + ":" + fabricatedItem.TargetItem.Identifier);
|
GameAnalyticsManager.AddDesignEvent("ItemFabricated:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "none") + ":" + fabricatedItem.TargetItem.Identifier);
|
||||||
if (i < amountFittingContainer)
|
if (i < amountFittingContainer)
|
||||||
{
|
{
|
||||||
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, outputContainer.Inventory, fabricatedItem.TargetItem.Health * outCondition, quality,
|
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, outputContainer.Inventory, fabricatedItem.TargetItem.Health * outCondition, quality,
|
||||||
|
|||||||
@@ -103,7 +103,20 @@ namespace Barotrauma.Items.Components
|
|||||||
if (TargetLevel != null)
|
if (TargetLevel != null)
|
||||||
{
|
{
|
||||||
float hullPercentage = 0.0f;
|
float hullPercentage = 0.0f;
|
||||||
if (item.CurrentHull != null) { hullPercentage = (item.CurrentHull.WaterVolume / item.CurrentHull.Volume) * 100.0f; }
|
if (item.CurrentHull != null)
|
||||||
|
{
|
||||||
|
float hullWaterVolume = item.CurrentHull.WaterVolume;
|
||||||
|
float totalHullVolume = item.CurrentHull.Volume;
|
||||||
|
foreach (var linked in item.CurrentHull.linkedTo)
|
||||||
|
{
|
||||||
|
if ((linked is Hull linkedHull))
|
||||||
|
{
|
||||||
|
hullWaterVolume += linkedHull.WaterVolume;
|
||||||
|
totalHullVolume += linkedHull.Volume;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hullPercentage = hullWaterVolume / totalHullVolume * 100.0f;
|
||||||
|
}
|
||||||
FlowPercentage = ((float)TargetLevel - hullPercentage) * 10.0f;
|
FlowPercentage = ((float)TargetLevel - hullPercentage) * 10.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,8 +144,8 @@ namespace Barotrauma.Items.Components
|
|||||||
//less effective when in a bad condition
|
//less effective when in a bad condition
|
||||||
currFlow *= MathHelper.Lerp(0.5f, 1.0f, item.Condition / item.MaxCondition);
|
currFlow *= MathHelper.Lerp(0.5f, 1.0f, item.Condition / item.MaxCondition);
|
||||||
|
|
||||||
item.CurrentHull.WaterVolume += currFlow;
|
item.CurrentHull.WaterVolume += currFlow * deltaTime * Timing.FixedUpdateRate;
|
||||||
if (item.CurrentHull.WaterVolume > item.CurrentHull.Volume) { item.CurrentHull.Pressure += 0.5f; }
|
if (item.CurrentHull.WaterVolume > item.CurrentHull.Volume) { item.CurrentHull.Pressure += 30.0f * deltaTime; }
|
||||||
|
|
||||||
Voltage -= deltaTime;
|
Voltage -= deltaTime;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -738,8 +738,9 @@ namespace Barotrauma.Items.Components
|
|||||||
}
|
}
|
||||||
lastTarget = target;
|
lastTarget = target;
|
||||||
|
|
||||||
float projectileNewSpeed = 0.5f;
|
int remainingHits = Math.Max(MaxTargetsToHit - hits.Count, 0);
|
||||||
float projectileDeflectedNewSpeed = 0.1f;
|
float speedMultiplier = Math.Min(0.4f + remainingHits * 0.1f, 1.0f);
|
||||||
|
float deflectedSpeedMultiplier = 0.1f;
|
||||||
|
|
||||||
AttackResult attackResult = new AttackResult();
|
AttackResult attackResult = new AttackResult();
|
||||||
Character character = null;
|
Character character = null;
|
||||||
@@ -755,8 +756,8 @@ namespace Barotrauma.Items.Components
|
|||||||
// when hitting limbs with piercing ammo, don't lose as much speed
|
// when hitting limbs with piercing ammo, don't lose as much speed
|
||||||
if (MaxTargetsToHit > 1)
|
if (MaxTargetsToHit > 1)
|
||||||
{
|
{
|
||||||
projectileNewSpeed = 1f;
|
speedMultiplier = 1f;
|
||||||
projectileDeflectedNewSpeed = 0.8f;
|
deflectedSpeedMultiplier = 0.8f;
|
||||||
}
|
}
|
||||||
if (limb.IsSevered || limb.character == null || limb.character.Removed) { return false; }
|
if (limb.IsSevered || limb.character == null || limb.character.Removed) { return false; }
|
||||||
|
|
||||||
@@ -869,7 +870,7 @@ namespace Barotrauma.Items.Components
|
|||||||
if (attackResult.AppliedDamageModifiers != null &&
|
if (attackResult.AppliedDamageModifiers != null &&
|
||||||
(attackResult.AppliedDamageModifiers.Any(dm => dm.DeflectProjectiles) && !StickToDeflective))
|
(attackResult.AppliedDamageModifiers.Any(dm => dm.DeflectProjectiles) && !StickToDeflective))
|
||||||
{
|
{
|
||||||
item.body.LinearVelocity *= projectileDeflectedNewSpeed;
|
item.body.LinearVelocity *= deflectedSpeedMultiplier;
|
||||||
}
|
}
|
||||||
else if ( // When hitting characters the collision normal seems to sometimes point into wrong direction, resulting in a failed attempt to stick
|
else if ( // When hitting characters the collision normal seems to sometimes point into wrong direction, resulting in a failed attempt to stick
|
||||||
//Vector2.Dot(Vector2.Normalize(velocity), collisionNormal) < 0.0f &&
|
//Vector2.Dot(Vector2.Normalize(velocity), collisionNormal) < 0.0f &&
|
||||||
@@ -901,13 +902,13 @@ namespace Barotrauma.Items.Components
|
|||||||
item.CreateServerEvent(this);
|
item.CreateServerEvent(this);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
item.body.LinearVelocity *= projectileNewSpeed;
|
item.body.LinearVelocity *= speedMultiplier;
|
||||||
|
|
||||||
return Hitscan;
|
return Hitscan;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
item.body.LinearVelocity *= projectileNewSpeed;
|
item.body.LinearVelocity *= speedMultiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
var containedItems = item.OwnInventory?.AllItems;
|
var containedItems = item.OwnInventory?.AllItems;
|
||||||
|
|||||||
@@ -272,7 +272,7 @@ namespace Barotrauma.Items.Components
|
|||||||
ic.ReceiveSignal(signal, connection);
|
ic.ReceiveSignal(signal, connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recipient.Effects != null && signal.value != "0" && !string.IsNullOrEmpty(signal.value))
|
if (recipient.Effects != null && signal.value != "0")
|
||||||
{
|
{
|
||||||
foreach (StatusEffect effect in recipient.Effects)
|
foreach (StatusEffect effect in recipient.Effects)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -241,7 +241,7 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
public override void OnMapLoaded()
|
public override void OnMapLoaded()
|
||||||
{
|
{
|
||||||
if (item.body == null && powerConsumption <= 0.0f && Parent == null && turret == null &&
|
if (item.body == null && powerConsumption <= 0.0f && Parent == null && turret == null && IsOn &&
|
||||||
(statusEffectLists == null || !statusEffectLists.ContainsKey(ActionType.OnActive)) &&
|
(statusEffectLists == null || !statusEffectLists.ContainsKey(ActionType.OnActive)) &&
|
||||||
(IsActiveConditionals == null || IsActiveConditionals.Count == 0))
|
(IsActiveConditionals == null || IsActiveConditionals.Count == 0))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -235,6 +235,13 @@ namespace Barotrauma
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsInteractable(Character character)
|
public bool IsInteractable(Character character)
|
||||||
{
|
{
|
||||||
|
#if CLIENT
|
||||||
|
if (Screen.Selected is EditorScreen)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (character != null && character.IsOnPlayerTeam)
|
if (character != null && character.IsOnPlayerTeam)
|
||||||
{
|
{
|
||||||
return IsPlayerTeamInteractable;
|
return IsPlayerTeamInteractable;
|
||||||
@@ -638,6 +645,10 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private float buoyancySineMagnitude;
|
||||||
|
private float buoyancySineFrequency;
|
||||||
|
private float buoyancyRandomForce;
|
||||||
|
|
||||||
public bool FireProof
|
public bool FireProof
|
||||||
{
|
{
|
||||||
get { return Prefab.FireProof; }
|
get { return Prefab.FireProof; }
|
||||||
@@ -866,9 +877,11 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
body.FarseerBody.AngularDamping = subElement.GetAttributeFloat("angulardamping", 0.2f);
|
||||||
body.FarseerBody.AngularDamping = element.GetAttributeFloat("angulardamping", 0.2f);
|
body.FarseerBody.LinearDamping = subElement.GetAttributeFloat("lineardamping", 0.1f);
|
||||||
body.FarseerBody.LinearDamping = element.GetAttributeFloat("lineardamping", 0.1f);
|
buoyancySineMagnitude = subElement.GetAttributeFloat("buoyancysinemagnitude", 0f);
|
||||||
|
buoyancySineFrequency = subElement.GetAttributeFloat("buoyancysinefrequency", 0f);
|
||||||
|
buoyancyRandomForce = subElement.GetAttributeFloat("buoyancyrandom", 0f);
|
||||||
body.UserData = this;
|
body.UserData = this;
|
||||||
break;
|
break;
|
||||||
case "trigger":
|
case "trigger":
|
||||||
@@ -1716,7 +1729,7 @@ namespace Barotrauma
|
|||||||
UpdateNetPosition(deltaTime);
|
UpdateNetPosition(deltaTime);
|
||||||
if (inWater)
|
if (inWater)
|
||||||
{
|
{
|
||||||
ApplyWaterForces();
|
ApplyWaterForces(deltaTime);
|
||||||
CurrentHull?.ApplyFlowForces(deltaTime, this);
|
CurrentHull?.ApplyFlowForces(deltaTime, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1805,16 +1818,24 @@ namespace Barotrauma
|
|||||||
transformDirty = false;
|
transformDirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private float sineTime;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Applies buoyancy, drag and angular drag caused by water
|
/// Applies buoyancy, drag and angular drag caused by water
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void ApplyWaterForces()
|
private void ApplyWaterForces(float deltaTime)
|
||||||
{
|
{
|
||||||
if (body.Mass <= 0.0f || body.Density <= 0.0f)
|
if (body.Mass <= 0.0f || body.Density <= 0.0f)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (buoyancySineFrequency > 0)
|
||||||
|
{
|
||||||
|
if (sineTime >= float.MaxValue)
|
||||||
|
{
|
||||||
|
sineTime = float.MinValue;
|
||||||
|
}
|
||||||
|
sineTime += deltaTime * buoyancySineFrequency;
|
||||||
|
}
|
||||||
float forceFactor = 1.0f;
|
float forceFactor = 1.0f;
|
||||||
if (CurrentHull != null)
|
if (CurrentHull != null)
|
||||||
{
|
{
|
||||||
@@ -1833,7 +1854,10 @@ namespace Barotrauma
|
|||||||
|
|
||||||
Vector2 drag = body.LinearVelocity * volume;
|
Vector2 drag = body.LinearVelocity * volume;
|
||||||
|
|
||||||
body.ApplyForce((uplift - drag) * 10.0f);
|
float sine = (float)Math.Sin(sineTime) * buoyancySineMagnitude;
|
||||||
|
Vector2 sineForce = Vector2.UnitY * sine * volume;
|
||||||
|
Vector2 randomForce = Vector2.UnitY * Rand.Range(-buoyancyRandomForce, buoyancyRandomForce, Rand.RandSync.Unsynced) * volume;
|
||||||
|
body.ApplyForce((uplift - drag) * 10.0f + sineForce + randomForce);
|
||||||
|
|
||||||
//apply simple angular drag
|
//apply simple angular drag
|
||||||
body.ApplyTorque(body.AngularVelocity * volume * -0.05f);
|
body.ApplyTorque(body.AngularVelocity * volume * -0.05f);
|
||||||
@@ -1971,7 +1995,7 @@ namespace Barotrauma
|
|||||||
return connectedComponents;
|
return connectedComponents;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GetConnectedComponentsRecursive<T>(HashSet<Connection> alreadySearched, List<T> connectedComponents) where T : ItemComponent
|
private void GetConnectedComponentsRecursive<T>(HashSet<Connection> alreadySearched, List<T> connectedComponents, bool ignoreInactiveRelays = false) where T : ItemComponent
|
||||||
{
|
{
|
||||||
ConnectionPanel connectionPanel = GetComponent<ConnectionPanel>();
|
ConnectionPanel connectionPanel = GetComponent<ConnectionPanel>();
|
||||||
if (connectionPanel == null) { return; }
|
if (connectionPanel == null) { return; }
|
||||||
@@ -1980,18 +2004,18 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
if (alreadySearched.Contains(c)) { continue; }
|
if (alreadySearched.Contains(c)) { continue; }
|
||||||
alreadySearched.Add(c);
|
alreadySearched.Add(c);
|
||||||
GetConnectedComponentsRecursive(c, alreadySearched, connectedComponents);
|
GetConnectedComponentsRecursive(c, alreadySearched, connectedComponents, ignoreInactiveRelays);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Note: This function generates garbage and might be a bit too heavy to be used once per frame.
|
/// Note: This function generates garbage and might be a bit too heavy to be used once per frame.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<T> GetConnectedComponentsRecursive<T>(Connection c) where T : ItemComponent
|
public List<T> GetConnectedComponentsRecursive<T>(Connection c, bool ignoreInactiveRelays = false) where T : ItemComponent
|
||||||
{
|
{
|
||||||
List<T> connectedComponents = new List<T>();
|
List<T> connectedComponents = new List<T>();
|
||||||
HashSet<Connection> alreadySearched = new HashSet<Connection>();
|
HashSet<Connection> alreadySearched = new HashSet<Connection>();
|
||||||
GetConnectedComponentsRecursive(c, alreadySearched, connectedComponents);
|
GetConnectedComponentsRecursive(c, alreadySearched, connectedComponents, ignoreInactiveRelays);
|
||||||
|
|
||||||
return connectedComponents;
|
return connectedComponents;
|
||||||
}
|
}
|
||||||
@@ -2008,7 +2032,7 @@ namespace Barotrauma
|
|||||||
("signal_in2", "signal_out")
|
("signal_in2", "signal_out")
|
||||||
};
|
};
|
||||||
|
|
||||||
private void GetConnectedComponentsRecursive<T>(Connection c, HashSet<Connection> alreadySearched, List<T> connectedComponents) where T : ItemComponent
|
private void GetConnectedComponentsRecursive<T>(Connection c, HashSet<Connection> alreadySearched, List<T> connectedComponents, bool ignoreInactiveRelays) where T : ItemComponent
|
||||||
{
|
{
|
||||||
alreadySearched.Add(c);
|
alreadySearched.Add(c);
|
||||||
|
|
||||||
@@ -2033,12 +2057,18 @@ namespace Barotrauma
|
|||||||
foreach (Connection wifiOutput in receiverConnections)
|
foreach (Connection wifiOutput in receiverConnections)
|
||||||
{
|
{
|
||||||
if ((wifiOutput.IsOutput == recipient.IsOutput) || alreadySearched.Contains(wifiOutput)) { continue; }
|
if ((wifiOutput.IsOutput == recipient.IsOutput) || alreadySearched.Contains(wifiOutput)) { continue; }
|
||||||
GetConnectedComponentsRecursive(wifiOutput, alreadySearched, connectedComponents);
|
GetConnectedComponentsRecursive(wifiOutput, alreadySearched, connectedComponents, ignoreInactiveRelays);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
recipient.Item.GetConnectedComponentsRecursive(recipient, alreadySearched, connectedComponents);
|
recipient.Item.GetConnectedComponentsRecursive(recipient, alreadySearched, connectedComponents, ignoreInactiveRelays);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ignoreInactiveRelays)
|
||||||
|
{
|
||||||
|
var relay = GetComponent<RelayComponent>();
|
||||||
|
if (relay != null && !relay.IsOn) { return; }
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ((string input, string output) in connectionPairs)
|
foreach ((string input, string output) in connectionPairs)
|
||||||
@@ -2049,7 +2079,7 @@ namespace Barotrauma
|
|||||||
if (pairedConnection != null)
|
if (pairedConnection != null)
|
||||||
{
|
{
|
||||||
if (alreadySearched.Contains(pairedConnection)) { continue; }
|
if (alreadySearched.Contains(pairedConnection)) { continue; }
|
||||||
GetConnectedComponentsRecursive(pairedConnection, alreadySearched, connectedComponents);
|
GetConnectedComponentsRecursive(pairedConnection, alreadySearched, connectedComponents, ignoreInactiveRelays);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (output == c.Name)
|
else if (output == c.Name)
|
||||||
@@ -2058,7 +2088,7 @@ namespace Barotrauma
|
|||||||
if (pairedConnection != null)
|
if (pairedConnection != null)
|
||||||
{
|
{
|
||||||
if (alreadySearched.Contains(pairedConnection)) { continue; }
|
if (alreadySearched.Contains(pairedConnection)) { continue; }
|
||||||
GetConnectedComponentsRecursive(pairedConnection, alreadySearched, connectedComponents);
|
GetConnectedComponentsRecursive(pairedConnection, alreadySearched, connectedComponents, ignoreInactiveRelays);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -301,7 +301,7 @@ namespace Barotrauma
|
|||||||
Hull hull2 = linkedTo.Count < 2 ? null : (Hull)linkedTo[1];
|
Hull hull2 = linkedTo.Count < 2 ? null : (Hull)linkedTo[1];
|
||||||
if (hull1 == hull2) { return; }
|
if (hull1 == hull2) { return; }
|
||||||
|
|
||||||
UpdateOxygen(hull1, hull2);
|
UpdateOxygen(hull1, hull2, deltaTime);
|
||||||
|
|
||||||
if (linkedTo.Count == 1)
|
if (linkedTo.Count == 1)
|
||||||
{
|
{
|
||||||
@@ -316,7 +316,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
flowForce.X = MathHelper.Clamp(flowForce.X, -MaxFlowForce, MaxFlowForce);
|
flowForce.X = MathHelper.Clamp(flowForce.X, -MaxFlowForce, MaxFlowForce);
|
||||||
flowForce.Y = MathHelper.Clamp(flowForce.Y, -MaxFlowForce, MaxFlowForce);
|
flowForce.Y = MathHelper.Clamp(flowForce.Y, -MaxFlowForce, MaxFlowForce);
|
||||||
if (openedTimer > 0.0f && flowForce.Length() > lerpedFlowForce.Length())
|
if (openedTimer > 0.0f && flowForce.LengthSquared() > lerpedFlowForce.LengthSquared())
|
||||||
{
|
{
|
||||||
//if the gap has just been opened/created, allow it to exert a large force instantly without any smoothing
|
//if the gap has just been opened/created, allow it to exert a large force instantly without any smoothing
|
||||||
lerpedFlowForce = flowForce;
|
lerpedFlowForce = flowForce;
|
||||||
@@ -344,7 +344,7 @@ namespace Barotrauma
|
|||||||
subOffset = hull2.Submarine.Position - Submarine.Position;
|
subOffset = hull2.Submarine.Position - Submarine.Position;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hull1.WaterVolume <= 0.0 && hull2.WaterVolume <= 0.0) return;
|
if (hull1.WaterVolume <= 0.0 && hull2.WaterVolume <= 0.0) { return; }
|
||||||
|
|
||||||
float size = IsHorizontal ? rect.Height : rect.Width;
|
float size = IsHorizontal ? rect.Height : rect.Width;
|
||||||
|
|
||||||
@@ -366,7 +366,7 @@ namespace Barotrauma
|
|||||||
//water flowing from the righthand room to the lefthand room
|
//water flowing from the righthand room to the lefthand room
|
||||||
if (dir == -1)
|
if (dir == -1)
|
||||||
{
|
{
|
||||||
if (!(hull2.WaterVolume > 0.0f)) return;
|
if (!(hull2.WaterVolume > 0.0f)) { return; }
|
||||||
lowerSurface = hull1.Surface - hull1.WaveY[hull1.WaveY.Length - 1];
|
lowerSurface = hull1.Surface - hull1.WaveY[hull1.WaveY.Length - 1];
|
||||||
//delta = Math.Min((room2.water.pressure - room1.water.pressure) * sizeModifier, Math.Min(room2.water.Volume, room2.Volume));
|
//delta = Math.Min((room2.water.pressure - room1.water.pressure) * sizeModifier, Math.Min(room2.water.Volume, room2.Volume));
|
||||||
//delta = Math.Min(delta, room1.Volume - room1.water.Volume + Water.MaxCompress);
|
//delta = Math.Min(delta, room1.Volume - room1.water.Volume + Water.MaxCompress);
|
||||||
@@ -374,10 +374,10 @@ namespace Barotrauma
|
|||||||
flowTargetHull = hull1;
|
flowTargetHull = hull1;
|
||||||
|
|
||||||
//make sure not to move more than what the room contains
|
//make sure not to move more than what the room contains
|
||||||
delta = Math.Min(((hull2.Pressure + subOffset.Y) - hull1.Pressure) * 5.0f * sizeModifier, Math.Min(hull2.WaterVolume, hull2.Volume));
|
delta = Math.Min(((hull2.Pressure + subOffset.Y) - hull1.Pressure) * 300.0f * sizeModifier * deltaTime, Math.Min(hull2.WaterVolume, hull2.Volume));
|
||||||
|
|
||||||
//make sure not to place more water to the target room than it can hold
|
//make sure not to place more water to the target room than it can hold
|
||||||
delta = Math.Min(delta, hull1.Volume * Hull.MaxCompress - (hull1.WaterVolume));
|
delta = Math.Min(delta, hull1.Volume * Hull.MaxCompress - hull1.WaterVolume);
|
||||||
hull1.WaterVolume += delta;
|
hull1.WaterVolume += delta;
|
||||||
hull2.WaterVolume -= delta;
|
hull2.WaterVolume -= delta;
|
||||||
if (hull1.WaterVolume > hull1.Volume)
|
if (hull1.WaterVolume > hull1.Volume)
|
||||||
@@ -389,16 +389,16 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
else if (dir == 1)
|
else if (dir == 1)
|
||||||
{
|
{
|
||||||
if (!(hull1.WaterVolume > 0.0f)) return;
|
if (!(hull1.WaterVolume > 0.0f)) { return; }
|
||||||
lowerSurface = hull2.Surface - hull2.WaveY[hull2.WaveY.Length - 1];
|
lowerSurface = hull2.Surface - hull2.WaveY[hull2.WaveY.Length - 1];
|
||||||
|
|
||||||
flowTargetHull = hull2;
|
flowTargetHull = hull2;
|
||||||
|
|
||||||
//make sure not to move more than what the room contains
|
//make sure not to move more than what the room contains
|
||||||
delta = Math.Min((hull1.Pressure - (hull2.Pressure + subOffset.Y)) * 5.0f * sizeModifier, Math.Min(hull1.WaterVolume, hull1.Volume));
|
delta = Math.Min((hull1.Pressure - (hull2.Pressure + subOffset.Y)) * 300.0f * sizeModifier * deltaTime, Math.Min(hull1.WaterVolume, hull1.Volume));
|
||||||
|
|
||||||
//make sure not to place more water to the target room than it can hold
|
//make sure not to place more water to the target room than it can hold
|
||||||
delta = Math.Min(delta, hull2.Volume * Hull.MaxCompress - (hull2.WaterVolume));
|
delta = Math.Min(delta, hull2.Volume * Hull.MaxCompress - hull2.WaterVolume);
|
||||||
hull1.WaterVolume -= delta;
|
hull1.WaterVolume -= delta;
|
||||||
hull2.WaterVolume += delta;
|
hull2.WaterVolume += delta;
|
||||||
if (hull2.WaterVolume > hull2.Volume)
|
if (hull2.WaterVolume > hull2.Volume)
|
||||||
@@ -409,7 +409,7 @@ namespace Barotrauma
|
|||||||
flowForce = new Vector2(delta, 0.0f);
|
flowForce = new Vector2(delta, 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (delta > 100.0f && subOffset == Vector2.Zero)
|
if (delta > 1.5f && subOffset == Vector2.Zero)
|
||||||
{
|
{
|
||||||
float avg = (hull1.Surface + hull2.Surface) / 2.0f;
|
float avg = (hull1.Surface + hull2.Surface) / 2.0f;
|
||||||
|
|
||||||
@@ -516,7 +516,7 @@ namespace Barotrauma
|
|||||||
delta = Math.Min(delta, hull1.Volume * Hull.MaxCompress - hull1.WaterVolume);
|
delta = Math.Min(delta, hull1.Volume * Hull.MaxCompress - hull1.WaterVolume);
|
||||||
hull1.WaterVolume += delta;
|
hull1.WaterVolume += delta;
|
||||||
|
|
||||||
if (hull1.WaterVolume > hull1.Volume) hull1.Pressure += 0.5f;
|
if (hull1.WaterVolume > hull1.Volume) { hull1.Pressure += 30.0f * deltaTime; }
|
||||||
|
|
||||||
flowTargetHull = hull1;
|
flowTargetHull = hull1;
|
||||||
|
|
||||||
@@ -541,24 +541,24 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
if (rect.X > hull1.Rect.X + hull1.Rect.Width / 2.0f)
|
if (rect.X > hull1.Rect.X + hull1.Rect.Width / 2.0f)
|
||||||
{
|
{
|
||||||
float vel = ((rect.Y - rect.Height / 2) - (hull1.Surface + hull1.WaveY[hull1.WaveY.Length - 1])) * 0.1f;
|
float vel = ((rect.Y - rect.Height / 2) - (hull1.Surface + hull1.WaveY[hull1.WaveY.Length - 1])) * 6.0f;
|
||||||
vel *= Math.Min(Math.Abs(flowForce.X) / 200.0f, 1.0f);
|
vel *= Math.Min(Math.Abs(flowForce.X) / 200.0f, 1.0f);
|
||||||
|
|
||||||
hull1.WaveVel[hull1.WaveY.Length - 1] += vel;
|
hull1.WaveVel[hull1.WaveY.Length - 1] += vel * deltaTime;
|
||||||
hull1.WaveVel[hull1.WaveY.Length - 2] += vel;
|
hull1.WaveVel[hull1.WaveY.Length - 2] += vel * deltaTime;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
float vel = ((rect.Y - rect.Height / 2) - (hull1.Surface + hull1.WaveY[0])) * 0.1f;
|
float vel = ((rect.Y - rect.Height / 2) - (hull1.Surface + hull1.WaveY[0])) * 6.0f;
|
||||||
vel *= Math.Min(Math.Abs(flowForce.X) / 200.0f, 1.0f);
|
vel *= Math.Min(Math.Abs(flowForce.X) / 200.0f, 1.0f);
|
||||||
|
|
||||||
hull1.WaveVel[0] += vel;
|
hull1.WaveVel[0] += vel * deltaTime;
|
||||||
hull1.WaveVel[1] += vel;
|
hull1.WaveVel[1] += vel * deltaTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
hull1.LethalPressure += (Submarine != null && Submarine.AtDamageDepth) ? 100.0f * deltaTime : 10.0f * deltaTime;
|
hull1.LethalPressure += ((Submarine != null && Submarine.AtDamageDepth) ? 100.0f : 10.0f) * deltaTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -573,7 +573,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
if (hull1.WaterVolume >= hull1.Volume / Hull.MaxCompress)
|
if (hull1.WaterVolume >= hull1.Volume / Hull.MaxCompress)
|
||||||
{
|
{
|
||||||
hull1.LethalPressure += (Submarine != null && Submarine.AtDamageDepth) ? 100.0f * deltaTime : 10.0f * deltaTime;
|
hull1.LethalPressure += ((Submarine != null && Submarine.AtDamageDepth) ? 100.0f : 10.0f) * deltaTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -639,7 +639,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateOxygen(Hull hull1, Hull hull2)
|
private void UpdateOxygen(Hull hull1, Hull hull2, float deltaTime)
|
||||||
{
|
{
|
||||||
if (hull1 == null || hull2 == null) { return; }
|
if (hull1 == null || hull2 == null) { return; }
|
||||||
|
|
||||||
@@ -650,10 +650,10 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
|
|
||||||
float totalOxygen = hull1.Oxygen + hull2.Oxygen;
|
float totalOxygen = hull1.Oxygen + hull2.Oxygen;
|
||||||
float totalVolume = (hull1.Volume + hull2.Volume);
|
float totalVolume = hull1.Volume + hull2.Volume;
|
||||||
|
|
||||||
float deltaOxygen = (totalOxygen * hull1.Volume / totalVolume) - hull1.Oxygen;
|
float deltaOxygen = (totalOxygen * hull1.Volume / totalVolume) - hull1.Oxygen;
|
||||||
deltaOxygen = MathHelper.Clamp(deltaOxygen, -Hull.OxygenDistributionSpeed, Hull.OxygenDistributionSpeed);
|
deltaOxygen = MathHelper.Clamp(deltaOxygen, -Hull.OxygenDistributionSpeed * deltaTime, Hull.OxygenDistributionSpeed * deltaTime);
|
||||||
|
|
||||||
hull1.Oxygen += deltaOxygen;
|
hull1.Oxygen += deltaOxygen;
|
||||||
hull2.Oxygen -= deltaOxygen;
|
hull2.Oxygen -= deltaOxygen;
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ namespace Barotrauma
|
|||||||
public static bool ShowHulls = true;
|
public static bool ShowHulls = true;
|
||||||
|
|
||||||
public static bool EditWater, EditFire;
|
public static bool EditWater, EditFire;
|
||||||
public const float OxygenDistributionSpeed = 500.0f;
|
public const float OxygenDistributionSpeed = 30000.0f;
|
||||||
public const float OxygenDeteriorationSpeed = 0.3f;
|
public const float OxygenDeteriorationSpeed = 0.3f;
|
||||||
public const float OxygenConsumptionSpeed = 700.0f;
|
public const float OxygenConsumptionSpeed = 700.0f;
|
||||||
|
|
||||||
@@ -132,7 +132,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private float lethalPressure;
|
private float lethalPressure;
|
||||||
|
|
||||||
private float surface, drawSurface;
|
private float surface;
|
||||||
private float waterVolume;
|
private float waterVolume;
|
||||||
private float pressure;
|
private float pressure;
|
||||||
|
|
||||||
@@ -241,7 +241,10 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
|
|
||||||
OxygenPercentage = prevOxygenPercentage;
|
OxygenPercentage = prevOxygenPercentage;
|
||||||
surface = drawSurface = rect.Y - rect.Height + WaterVolume / rect.Width;
|
surface = rect.Y - rect.Height + WaterVolume / rect.Width;
|
||||||
|
#if CLIENT
|
||||||
|
drawSurface = surface;
|
||||||
|
#endif
|
||||||
Pressure = surface;
|
Pressure = surface;
|
||||||
|
|
||||||
CreateBackgroundSections();
|
CreateBackgroundSections();
|
||||||
@@ -275,17 +278,6 @@ namespace Barotrauma
|
|||||||
get { return surface; }
|
get { return surface; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public float DrawSurface
|
|
||||||
{
|
|
||||||
get { return drawSurface; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (Math.Abs(drawSurface - value) < 0.00001f) return;
|
|
||||||
drawSurface = MathHelper.Clamp(value, rect.Y - rect.Height, rect.Y);
|
|
||||||
update = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public float WorldSurface
|
public float WorldSurface
|
||||||
{
|
{
|
||||||
get { return Submarine == null ? surface : surface + Submarine.Position.Y; }
|
get { return Submarine == null ? surface : surface + Submarine.Position.Y; }
|
||||||
@@ -628,7 +620,10 @@ namespace Barotrauma
|
|||||||
Gap.UpdateHulls();
|
Gap.UpdateHulls();
|
||||||
}
|
}
|
||||||
|
|
||||||
surface = drawSurface = rect.Y - rect.Height + WaterVolume / rect.Width;
|
surface = rect.Y - rect.Height + WaterVolume / rect.Width;
|
||||||
|
#if CLIENT
|
||||||
|
drawSurface = surface;
|
||||||
|
#endif
|
||||||
Pressure = surface;
|
Pressure = surface;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -753,8 +748,6 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public override void Update(float deltaTime, Camera cam)
|
public override void Update(float deltaTime, Camera cam)
|
||||||
{
|
{
|
||||||
base.Update(deltaTime, cam);
|
|
||||||
|
|
||||||
BallastFlora?.Update(deltaTime);
|
BallastFlora?.Update(deltaTime);
|
||||||
|
|
||||||
UpdateProjSpecific(deltaTime, cam);
|
UpdateProjSpecific(deltaTime, cam);
|
||||||
@@ -808,11 +801,6 @@ namespace Barotrauma
|
|||||||
surface,
|
surface,
|
||||||
rect.Y - rect.Height + waterDepth,
|
rect.Y - rect.Height + waterDepth,
|
||||||
deltaTime * 10.0f), rect.Y - rect.Height);
|
deltaTime * 10.0f), rect.Y - rect.Height);
|
||||||
//interpolate the position of the rendered surface towards the "target surface"
|
|
||||||
drawSurface = Math.Max(MathHelper.Lerp(
|
|
||||||
drawSurface,
|
|
||||||
rect.Y - rect.Height + waterDepth,
|
|
||||||
deltaTime * 10.0f), rect.Y - rect.Height);
|
|
||||||
|
|
||||||
for (int i = 0; i < waveY.Length; i++)
|
for (int i = 0; i < waveY.Length; i++)
|
||||||
{
|
{
|
||||||
@@ -900,10 +888,10 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//0.01 increase every ~1000 frames = reaches full dirtiness in ~27 minutes
|
//0.016 increase every ~2000 frames = reaches full dirtiness in ~35 minutes
|
||||||
if (submergedSections.Count > 0 && Submarine != null && Submarine.Info.Type == SubmarineType.Player && Rand.Int(1000) == 1)
|
if (submergedSections.Count > 0 && Submarine != null && Submarine.Info.Type == SubmarineType.Player && Rand.Int(2000) == 1)
|
||||||
{
|
{
|
||||||
DirtySections(submergedSections, 0.01f);
|
DirtySections(submergedSections, deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (waterVolume < Volume)
|
if (waterVolume < Volume)
|
||||||
@@ -911,11 +899,13 @@ namespace Barotrauma
|
|||||||
LethalPressure -= 10.0f * deltaTime;
|
LethalPressure -= 10.0f * deltaTime;
|
||||||
if (WaterVolume <= 0.0f)
|
if (WaterVolume <= 0.0f)
|
||||||
{
|
{
|
||||||
|
#if CLIENT
|
||||||
//wait for the surface to be lerped back to bottom and the waves to settle until disabling update
|
//wait for the surface to be lerped back to bottom and the waves to settle until disabling update
|
||||||
if (drawSurface > rect.Y - rect.Height + 1) return;
|
if (drawSurface > rect.Y - rect.Height + 1) { return; }
|
||||||
|
#endif
|
||||||
for (int i = 1; i < waveY.Length - 1; i++)
|
for (int i = 1; i < waveY.Length - 1; i++)
|
||||||
{
|
{
|
||||||
if (waveY[i] > 0.1f) return;
|
if (waveY[i] > 0.1f) { return; }
|
||||||
}
|
}
|
||||||
|
|
||||||
update = false;
|
update = false;
|
||||||
|
|||||||
@@ -147,8 +147,6 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
List<Vector2> points = new List<Vector2>();
|
List<Vector2> points = new List<Vector2>();
|
||||||
|
|
||||||
var wallPrefabs = StructurePrefab.Prefabs.Where(mp => mp.Body);
|
|
||||||
|
|
||||||
foreach (XElement element in rootElement.Elements())
|
foreach (XElement element in rootElement.Elements())
|
||||||
{
|
{
|
||||||
if (element.Name != "Structure") { continue; }
|
if (element.Name != "Structure") { continue; }
|
||||||
@@ -159,7 +157,11 @@ namespace Barotrauma
|
|||||||
StructurePrefab prefab = Structure.FindPrefab(name, identifier);
|
StructurePrefab prefab = Structure.FindPrefab(name, identifier);
|
||||||
if (prefab == null) { continue; }
|
if (prefab == null) { continue; }
|
||||||
|
|
||||||
|
float scale = element.GetAttributeFloat("scale", prefab.Scale);
|
||||||
|
|
||||||
var rect = element.GetAttributeVector4("rect", Vector4.Zero);
|
var rect = element.GetAttributeVector4("rect", Vector4.Zero);
|
||||||
|
rect.Z *= scale / prefab.Scale;
|
||||||
|
rect.W *= scale / prefab.Scale;
|
||||||
|
|
||||||
points.Add(new Vector2(rect.X, rect.Y));
|
points.Add(new Vector2(rect.X, rect.Y));
|
||||||
points.Add(new Vector2(rect.X + rect.Z, rect.Y));
|
points.Add(new Vector2(rect.X + rect.Z, rect.Y));
|
||||||
|
|||||||
@@ -486,7 +486,8 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
location.LevelData = new LevelData(location)
|
location.LevelData = new LevelData(location)
|
||||||
{
|
{
|
||||||
Difficulty = MathHelper.Clamp(GetLevelDifficulty(location.MapPosition.X / Width), 0.0f, 100.0f)
|
Difficulty = MathHelper.Clamp(location.MapPosition.X / Width * 100, 0.0f, 100.0f)
|
||||||
|
//Difficulty = MathHelper.Clamp(GetLevelDifficulty(location.MapPosition.X / Width), 0.0f, 100.0f)
|
||||||
};
|
};
|
||||||
location.UnlockInitialMissions();
|
location.UnlockInitialMissions();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,10 @@ using FarseerPhysics;
|
|||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
using Barotrauma.Networking;
|
|
||||||
|
|
||||||
namespace Barotrauma
|
namespace Barotrauma
|
||||||
{
|
{
|
||||||
@@ -21,6 +19,9 @@ namespace Barotrauma
|
|||||||
protected List<ushort> linkedToID;
|
protected List<ushort> linkedToID;
|
||||||
public List<ushort> unresolvedLinkedToID;
|
public List<ushort> unresolvedLinkedToID;
|
||||||
|
|
||||||
|
private const int GapUpdateInterval = 4;
|
||||||
|
private static int gapUpdateTimer;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// List of upgrades this item has
|
/// List of upgrades this item has
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -410,6 +411,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
|
|
||||||
//connect clone wires to the clone items and refresh links between doors and gaps
|
//connect clone wires to the clone items and refresh links between doors and gaps
|
||||||
|
List<Wire> orphanedWires = new List<Wire>();
|
||||||
for (int i = 0; i < clones.Count; i++)
|
for (int i = 0; i < clones.Count; i++)
|
||||||
{
|
{
|
||||||
if (!(clones[i] is Item cloneItem)) { continue; }
|
if (!(clones[i] is Item cloneItem)) { continue; }
|
||||||
@@ -442,7 +444,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
|
|
||||||
var connectedItem = originalWire.Connections[n].Item;
|
var connectedItem = originalWire.Connections[n].Item;
|
||||||
if (connectedItem == null) { continue; }
|
if (connectedItem == null || !entitiesToClone.Contains(connectedItem)) { continue; }
|
||||||
|
|
||||||
//index of the item the wire is connected to
|
//index of the item the wire is connected to
|
||||||
int itemIndex = entitiesToClone.IndexOf(connectedItem);
|
int itemIndex = entitiesToClone.IndexOf(connectedItem);
|
||||||
@@ -469,6 +471,20 @@ namespace Barotrauma
|
|||||||
(clones[itemIndex] as Item).Connections[connectionIndex].TryAddLink(cloneWire);
|
(clones[itemIndex] as Item).Connections[connectionIndex].TryAddLink(cloneWire);
|
||||||
cloneWire.Connect((clones[itemIndex] as Item).Connections[connectionIndex], false);
|
cloneWire.Connect((clones[itemIndex] as Item).Connections[connectionIndex], false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cloneWire.Connections[0] == null || cloneWire.Connections[1] == null)
|
||||||
|
{
|
||||||
|
if (!clones.Any(c => (c as Item)?.GetComponent<ConnectionPanel>()?.DisconnectedWires.Contains(cloneWire) ?? false))
|
||||||
|
{
|
||||||
|
orphanedWires.Add(cloneWire);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var orphanedWire in orphanedWires)
|
||||||
|
{
|
||||||
|
orphanedWire.Item.Remove();
|
||||||
|
clones.Remove(orphanedWire.Item);
|
||||||
}
|
}
|
||||||
|
|
||||||
return clones;
|
return clones;
|
||||||
@@ -548,20 +564,27 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
hull.Update(deltaTime, cam);
|
hull.Update(deltaTime, cam);
|
||||||
}
|
}
|
||||||
|
#if CLIENT
|
||||||
|
Hull.UpdateCheats(deltaTime, cam);
|
||||||
|
#endif
|
||||||
|
|
||||||
foreach (Structure structure in Structure.WallList)
|
foreach (Structure structure in Structure.WallList)
|
||||||
{
|
{
|
||||||
structure.Update(deltaTime, cam);
|
structure.Update(deltaTime, cam);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//update gaps in random order, because otherwise in rooms with multiple gaps
|
//update gaps in random order, because otherwise in rooms with multiple gaps
|
||||||
//the water/air will always tend to flow through the first gap in the list,
|
//the water/air will always tend to flow through the first gap in the list,
|
||||||
//which may lead to weird behavior like water draining down only through
|
//which may lead to weird behavior like water draining down only through
|
||||||
//one gap in a room even if there are several
|
//one gap in a room even if there are several
|
||||||
foreach (Gap gap in Gap.GapList.OrderBy(g => Rand.Int(int.MaxValue)))
|
gapUpdateTimer++;
|
||||||
|
if (gapUpdateTimer >= GapUpdateInterval)
|
||||||
{
|
{
|
||||||
gap.Update(deltaTime, cam);
|
foreach (Gap gap in Gap.GapList.OrderBy(g => Rand.Int(int.MaxValue)))
|
||||||
|
{
|
||||||
|
gap.Update(deltaTime * GapUpdateInterval, cam);
|
||||||
|
}
|
||||||
|
gapUpdateTimer = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Powered.UpdatePower(deltaTime);
|
Powered.UpdatePower(deltaTime);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using Barotrauma.Extensions;
|
||||||
|
|
||||||
namespace Barotrauma
|
namespace Barotrauma
|
||||||
{
|
{
|
||||||
@@ -317,6 +318,11 @@ namespace Barotrauma
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static MapEntityPrefab GetRandom(Predicate<MapEntityPrefab> predicate, Rand.RandSync sync)
|
||||||
|
{
|
||||||
|
return List.GetRandom(p => predicate(p), sync);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Find a matching map entity prefab
|
/// Find a matching map entity prefab
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -85,7 +85,9 @@ namespace Barotrauma.Networking
|
|||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (customTextColor != null) { return customTextColor.Value; }
|
if (customTextColor != null) { return customTextColor.Value; }
|
||||||
return MessageColor[(int)Type];
|
int intType = (int)Type;
|
||||||
|
if (intType < 0 || intType >= MessageColor.Length) { return Color.White; }
|
||||||
|
return MessageColor[intType];
|
||||||
}
|
}
|
||||||
|
|
||||||
set
|
set
|
||||||
@@ -230,13 +232,13 @@ namespace Barotrauma.Networking
|
|||||||
break;
|
break;
|
||||||
case ChatMessageType.Radio:
|
case ChatMessageType.Radio:
|
||||||
case ChatMessageType.Order:
|
case ChatMessageType.Order:
|
||||||
if (receiver != null && !receiver.IsDead)
|
if (receiver?.Inventory != null && !receiver.IsDead)
|
||||||
{
|
{
|
||||||
foreach (Item receiverItem in receiver.Inventory?.AllItems.Where(i => i.GetComponent<WifiComponent>()?.LinkToChat ?? false))
|
foreach (Item receiverItem in receiver.Inventory.AllItems.Where(i => i.GetComponent<WifiComponent>()?.LinkToChat ?? false))
|
||||||
{
|
{
|
||||||
if (!receiver.HasEquippedItem(receiverItem)) { continue; }
|
if (sender.Inventory == null || !receiver.HasEquippedItem(receiverItem)) { continue; }
|
||||||
|
|
||||||
foreach (Item senderItem in sender.Inventory?.AllItems.Where(i => i.GetComponent<WifiComponent>()?.LinkToChat ?? false))
|
foreach (Item senderItem in sender.Inventory.AllItems.Where(i => i.GetComponent<WifiComponent>()?.LinkToChat ?? false))
|
||||||
{
|
{
|
||||||
if (!sender.HasEquippedItem(senderItem)) { continue; }
|
if (!sender.HasEquippedItem(senderItem)) { continue; }
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,11 @@ namespace Barotrauma.Networking
|
|||||||
ManageSettings = 0x200,
|
ManageSettings = 0x200,
|
||||||
ManagePermissions = 0x400,
|
ManagePermissions = 0x400,
|
||||||
KarmaImmunity = 0x800,
|
KarmaImmunity = 0x800,
|
||||||
All = 0xFFF
|
BuyItems = 0x1000,
|
||||||
|
SellInventoryItems = 0x2000,
|
||||||
|
SellSubItems = 0x4000,
|
||||||
|
CampaignStore = 0x8000,
|
||||||
|
All = 0xFFFF
|
||||||
}
|
}
|
||||||
|
|
||||||
class PermissionPreset
|
class PermissionPreset
|
||||||
|
|||||||
@@ -1591,8 +1591,10 @@ namespace Barotrauma
|
|||||||
if (rope != null && sourceBody.UserData is Limb sourceLimb)
|
if (rope != null && sourceBody.UserData is Limb sourceLimb)
|
||||||
{
|
{
|
||||||
rope.Attach(sourceLimb, newItem);
|
rope.Attach(sourceLimb, newItem);
|
||||||
|
#if SERVER
|
||||||
|
newItem.CreateServerEvent(rope);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
float spread = MathHelper.ToRadians(Rand.Range(-chosenItemSpawnInfo.AimSpread, chosenItemSpawnInfo.AimSpread));
|
float spread = MathHelper.ToRadians(Rand.Range(-chosenItemSpawnInfo.AimSpread, chosenItemSpawnInfo.AimSpread));
|
||||||
var worldPos = sourceBody.Position;
|
var worldPos = sourceBody.Position;
|
||||||
float rotation = chosenItemSpawnInfo.Rotation;
|
float rotation = chosenItemSpawnInfo.Rotation;
|
||||||
@@ -1625,8 +1627,27 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
newItem.body?.ApplyLinearImpulse(Rand.Vector(1) * chosenItemSpawnInfo.Speed);
|
var body = newItem.body;
|
||||||
newItem.Rotation = chosenItemSpawnInfo.Rotation;
|
if (body != null)
|
||||||
|
{
|
||||||
|
float rotation = MathHelper.ToRadians(chosenItemSpawnInfo.Rotation);
|
||||||
|
if (chosenItemSpawnInfo.RotationType == ItemSpawnInfo.SpawnRotationType.Limb)
|
||||||
|
{
|
||||||
|
if (sourceBody != null)
|
||||||
|
{
|
||||||
|
rotation += sourceBody.Rotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (chosenItemSpawnInfo.RotationType == ItemSpawnInfo.SpawnRotationType.Collider)
|
||||||
|
{
|
||||||
|
if (entity is Character character)
|
||||||
|
{
|
||||||
|
rotation += character.AnimController.Collider.Rotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
body.SetTransform(newItem.SimPosition, rotation);
|
||||||
|
body.ApplyLinearImpulse(Rand.Vector(1) * chosenItemSpawnInfo.Speed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user