Unstable 1.2.1.0

This commit is contained in:
Markus Isberg
2023-11-10 17:45:19 +02:00
parent 2ea58c58a7
commit 8a2e2ea0ae
268 changed files with 4076 additions and 1843 deletions
@@ -26,6 +26,13 @@ namespace Barotrauma
public readonly int BuyerCharacterInfoIdentifier;
/// <summary>
/// Should the items be given to the buyer immediately, as opposed to spawning them in the sub the next round?
/// </summary>
public bool DeliverImmediately { get; set; }
public bool Delivered;
public PurchasedItem(ItemPrefab itemPrefab, int quantity, int buyerCharacterInfoId)
{
ItemPrefabIdentifier = itemPrefab.Identifier;
@@ -216,9 +223,11 @@ namespace Barotrauma
public List<PurchasedItem> GetPurchasedItems(Location.StoreInfo store, bool create = false) => GetPurchasedItems(store?.Identifier ?? Identifier.Empty, create);
public PurchasedItem GetPurchasedItem(Identifier identifier, ItemPrefab prefab) => GetPurchasedItems(identifier)?.FirstOrDefault(i => i.ItemPrefab == prefab);
public int GetPurchasedItemCount(Location.StoreInfo store, ItemPrefab prefab) =>
GetPurchasedItemCount(store?.Identifier ?? Identifier.Empty, prefab);
public PurchasedItem GetPurchasedItem(Location.StoreInfo store, ItemPrefab prefab) => GetPurchasedItem(store?.Identifier ?? Identifier.Empty, prefab);
public int GetPurchasedItemCount(Identifier identifier, ItemPrefab prefab) =>
GetPurchasedItems(identifier)?.Where(i => i.ItemPrefab == prefab).Sum(it => it.Quantity) ?? 0;
public List<SoldItem> GetSoldItems(Identifier identifier, bool create = false) => GetItems(identifier, SoldItems, create);
@@ -287,23 +296,6 @@ namespace Barotrauma
OnItemsInSellFromSubCrateChanged?.Invoke(this);
}
#if SERVER
public void OnNewItemsPurchased(Identifier storeIdentifier, List<PurchasedItem> newItems, Client client)
{
StringBuilder sb = new StringBuilder();
int price = 0;
Dictionary<ItemPrefab, int> buyValues = GetBuyValuesAtCurrentLocation(storeIdentifier, newItems.Select(i => i.ItemPrefab));
foreach (PurchasedItem item in newItems)
{
int itemValue = item.Quantity * buyValues[item.ItemPrefab];
GameAnalyticsManager.AddMoneySpentEvent(itemValue, GameAnalyticsManager.MoneySink.Store, item.ItemPrefab.Identifier.Value);
sb.Append($"\n - {item.ItemPrefab.Name} x{item.Quantity}");
price += itemValue;
}
GameServer.Log($"{NetworkMember.ClientLogName(client, client?.Name ?? "Unknown")} purchased {newItems.Count} item(s) for {TextManager.FormatCurrency(price)}{sb.ToString()}", ServerLog.MessageType.Money);
}
#endif
public void PurchaseItems(Identifier storeIdentifier, List<PurchasedItem> itemsToPurchase, bool removeFromCrate, Client client = null)
{
var store = Location.GetStore(storeIdentifier);
@@ -314,27 +306,58 @@ namespace Barotrauma
var itemsInStoreCrate = GetBuyCrateItems(storeIdentifier, create: true);
foreach (PurchasedItem item in itemsToPurchase)
{
if (item.Quantity <= 0) { continue; }
// Exchange money
int itemValue = item.Quantity * buyValues[item.ItemPrefab];
if (!campaign.TryPurchase(client, itemValue)) { continue; }
// Add to the purchased items
var purchasedItem = itemsPurchasedFromStore.Find(pi => pi.ItemPrefab == item.ItemPrefab);
var purchasedItem = itemsPurchasedFromStore.Find(pi => pi.ItemPrefab == item.ItemPrefab && pi.DeliverImmediately == item.DeliverImmediately);
if (purchasedItem != null)
{
purchasedItem.Quantity += item.Quantity;
}
else
{
purchasedItem = new PurchasedItem(item.ItemPrefab, item.Quantity, client);
purchasedItem = new PurchasedItem(item.ItemPrefab, item.Quantity, client) { DeliverImmediately = item.DeliverImmediately };
itemsPurchasedFromStore.Add(purchasedItem);
}
purchasedItem.Delivered = item.DeliverImmediately;
if (GameMain.IsSingleplayer)
{
GameAnalyticsManager.AddMoneySpentEvent(itemValue, GameAnalyticsManager.MoneySink.Store, item.ItemPrefab.Identifier.Value);
}
store.Balance += itemValue;
if (removeFromCrate)
}
if (GameMain.NetworkMember is not { IsClient: true })
{
Character targetCharacter;
#if CLIENT
targetCharacter = Character.Controlled;
if (targetCharacter == null)
{
DebugConsole.ThrowError("Failed to deliver items directly to a character (not controlling a character).");
}
#else
targetCharacter = client?.Character;
if (targetCharacter == null)
{
DebugConsole.ThrowError($"Failed to deliver items directly to a character ({(client == null ? "client was null" : $"client {client.Name} is not controlling a character")}).");
}
#endif
if (targetCharacter == null)
{
DeliverItemsToSub(itemsToPurchase.Where(it => it.DeliverImmediately), Submarine.MainSub, this);
}
else
{
DeliverItemsToCharacter(itemsToPurchase.Where(it => it.DeliverImmediately), targetCharacter, this);
}
}
if (removeFromCrate)
{
foreach (PurchasedItem item in itemsToPurchase)
{
// Remove from the shopping crate
if (itemsInStoreCrate.Find(pi => pi.ItemPrefab == item.ItemPrefab) is { } crateItem)
@@ -387,9 +410,9 @@ namespace Barotrauma
var items = new List<PurchasedItem>();
foreach (var storeSpecificItems in PurchasedItems)
{
items.AddRange(storeSpecificItems.Value);
items.AddRange(storeSpecificItems.Value.Where(it => !it.DeliverImmediately));
}
CreateItems(items, Submarine.MainSub, this);
DeliverItemsToSub(items, Submarine.MainSub, this);
PurchasedItems.Clear();
OnPurchasedItemsChanged?.Invoke(this);
}
@@ -440,10 +463,22 @@ namespace Barotrauma
if (!item.Components.All(static c => c is not Holdable { Attachable: true, Attached: true })) { return false; }
if (!item.Components.All(static c => c is not Wire w || w.Connections.All(static c => c is null))) { return false; }
if (!ItemAndAllContainersInteractable(item)) { return false; }
if (item.RootContainer is Item rootContainer && rootContainer.HasTag(Tags.DontSellItems)) { return false; }
if (!AllContainersAllowSellingItems(item)) { return false; }
return true;
}).Distinct();
static bool AllContainersAllowSellingItems(Item item)
{
do
{
item = item.Container;
if (item is null) { return true; }
if (item.HasTag(Tags.DontSellItems)) { return false; }
if (item.Components.Any(static c => c.DisallowSellingItemsFromContainer)) { return false; }
} while (item != null);
return true;
}
static bool ItemAndAllContainersInteractable(Item item)
{
do
@@ -501,7 +536,7 @@ namespace Barotrauma
=> items.Where(it => it.HasTag(Tags.Crate) && !it.NonInteractable && !it.NonPlayerTeamInteractable && !it.HiddenInGame && !it.Removed && (conditional == null || conditional(it)));
public static IEnumerable<ItemContainer> FindReusableCargoContainers(IEnumerable<Submarine> subs, IEnumerable<Hull> cargoRooms = null) =>
FilterCargoCrates(Item.ItemList, it => subs.Contains(it.Submarine) && (cargoRooms == null || cargoRooms.Contains(it.CurrentHull)))
FilterCargoCrates(Item.ItemList, it => subs.Contains(it.Submarine) && !it.HasTag(Tags.CargoMissionItem) && (cargoRooms == null || cargoRooms.Contains(it.CurrentHull)))
.Select(it => it.GetComponent<ItemContainer>())
.Where(c => c != null);
@@ -553,9 +588,9 @@ namespace Barotrauma
return itemContainer;
}
public static void CreateItems(List<PurchasedItem> itemsToSpawn, Submarine sub, CargoManager cargoManager)
public static void DeliverItemsToSub(IEnumerable<PurchasedItem> itemsToSpawn, Submarine sub, CargoManager cargoManager)
{
if (itemsToSpawn.Count == 0) { return; }
if (!itemsToSpawn.Any()) { return; }
WayPoint wp = WayPoint.GetRandom(SpawnType.Cargo, null, sub);
if (wp == null)
@@ -571,7 +606,7 @@ namespace Barotrauma
return;
}
if (sub == Submarine.MainSub)
if (sub == Submarine.MainSub && itemsToSpawn.Any(it => !it.Delivered && it.Quantity > 0))
{
#if CLIENT
new GUIMessageBox("",
@@ -597,37 +632,66 @@ namespace Barotrauma
List<ItemContainer> availableContainers = FindReusableCargoContainers(connectedSubs, FindCargoRooms(connectedSubs)).ToList();
foreach (PurchasedItem pi in itemsToSpawn)
{
pi.Delivered = true;
Vector2 position = GetCargoPos(cargoRoom, pi.ItemPrefab);
for (int i = 0; i < pi.Quantity; i++)
{
var item = new Item(pi.ItemPrefab, position, wp.Submarine);
var itemContainer = GetOrCreateCargoContainerFor(pi.ItemPrefab, cargoRoom, ref availableContainers);
itemContainer?.Inventory.TryPutItem(item, null);
var idCard = item.GetComponent<IdCard>();
if (cargoManager != null && idCard != null && pi.BuyerCharacterInfoIdentifier != 0)
{
cargoManager.purchasedIDCards.Add((pi, idCard));
}
itemSpawned(pi, item);
ItemSpawned(pi, item, cargoManager);
#if SERVER
Entity.Spawner?.CreateNetworkEvent(new EntitySpawner.SpawnEntity(item));
#endif
(itemContainer?.Item ?? item).AssignCampaignInteractionType(CampaignMode.InteractionType.Cargo);
static void itemSpawned(PurchasedItem purchased, Item item)
{
Submarine sub = item.Submarine ?? item.RootContainer?.Submarine;
if (sub != null)
{
foreach (WifiComponent wifiComponent in item.GetComponents<WifiComponent>())
{
wifiComponent.TeamID = sub.TeamID;
}
}
}
(itemContainer?.Item ?? item).AssignCampaignInteractionType(CampaignMode.InteractionType.Cargo);
}
}
}
public static void DeliverItemsToCharacter(IEnumerable<PurchasedItem> itemsToSpawn, Character character, CargoManager cargoManager)
{
if (!itemsToSpawn.Any()) { return; }
foreach (PurchasedItem pi in itemsToSpawn)
{
pi.Delivered = true;
for (int i = 0; i < pi.Quantity; i++)
{
var item = new Item(pi.ItemPrefab, character.Position, character.Submarine);
#if SERVER
Entity.Spawner?.CreateNetworkEvent(new EntitySpawner.SpawnEntity(item));
#endif
if (!character.Inventory.TryPutItem(item, user: null, item.AllowedSlots))
{
foreach (Item containedItem in character.Inventory.AllItemsMod)
{
if (containedItem.OwnInventory != null &&
containedItem.OwnInventory.TryPutItem(item, user: null, item.AllowedSlots))
{
break;
}
}
}
ItemSpawned(pi, item, cargoManager);
}
}
}
private static void ItemSpawned(PurchasedItem purchased, Item item, CargoManager cargoManager)
{
var idCard = item.GetComponent<IdCard>();
if (cargoManager != null && idCard != null && purchased.BuyerCharacterInfoIdentifier != 0)
{
cargoManager.purchasedIDCards.Add((purchased, idCard));
}
Submarine sub = item.Submarine ?? item.RootContainer?.Submarine;
if (sub != null)
{
foreach (WifiComponent wifiComponent in item.GetComponents<WifiComponent>())
{
wifiComponent.TeamID = sub.TeamID;
}
}
itemsToSpawn.Clear();
}
private readonly List<(PurchasedItem purchaseInfo, IdCard idCard)> purchasedIDCards = new List<(PurchasedItem purchaseInfo, IdCard idCard)>();
@@ -685,6 +749,7 @@ namespace Barotrauma
new XAttribute("id", item.ItemPrefab.Identifier),
new XAttribute("qty", item.Quantity),
new XAttribute("storeid", storeSpecificItems.Key),
new XAttribute("deliverimmediately", item.DeliverImmediately),
new XAttribute("buyer", item.BuyerCharacterInfoIdentifier)));
}
}
@@ -700,17 +765,22 @@ namespace Barotrauma
{
string prefabId = itemElement.GetAttributeString("id", null);
if (string.IsNullOrWhiteSpace(prefabId)) { continue; }
var prefab = ItemPrefab.Prefabs.Find(p => p.Identifier == prefabId);
if (prefab == null) { continue; }
if (!ItemPrefab.Prefabs.TryGet(prefabId.ToIdentifier(), out var prefab)) { continue; }
int qty = itemElement.GetAttributeInt("qty", 0);
Identifier storeId = itemElement.GetAttributeIdentifier("storeid", "merchant");
bool deliverImmediately = itemElement.GetAttributeBool("deliverimmediately", false);
int buyerId = itemElement.GetAttributeInt("buyer", 0);
if (!purchasedItems.TryGetValue(storeId, out var storeItems))
{
storeItems = new List<PurchasedItem>();
purchasedItems.Add(storeId, storeItems);
}
storeItems.Add(new PurchasedItem(prefab, qty, buyerId));
storeItems.Add(new PurchasedItem(prefab, qty, buyerId)
{
DeliverImmediately = deliverImmediately,
//must have already been delivered if we had opted for immediate delivery
Delivered = deliverImmediately
});
}
}
SetPurchasedItems(purchasedItems);
@@ -120,7 +120,7 @@ namespace Barotrauma
foreach (var characterElement in element.Elements())
{
if (!characterElement.Name.ToString().Equals("character", StringComparison.OrdinalIgnoreCase)) { continue; }
CharacterInfo characterInfo = new CharacterInfo(characterElement);
CharacterInfo characterInfo = new CharacterInfo(new ContentXElement(contentPackage: null, characterElement));
#if CLIENT
if (characterElement.GetAttributeBool("lastcontrolled", false)) { characterInfo.LastControlled = true; }
characterInfo.CrewListIndex = characterElement.GetAttributeInt("crewlistindex", -1);
@@ -382,22 +382,28 @@ namespace Barotrauma
currentLocation.DeselectMission(mission);
}
}
if (levelData.HasBeaconStation && !levelData.IsBeaconActive)
if (levelData.HasBeaconStation && !levelData.IsBeaconActive && Missions.None(m => m.Prefab.Type == MissionType.Beacon))
{
var beaconMissionPrefabs = MissionPrefab.Prefabs.Where(m => m.Tags.Contains("beaconnoreward")).OrderBy(m => m.UintIdentifier);
var beaconMissionPrefabs = MissionPrefab.Prefabs.Where(m => m.IsSideObjective && m.Type == MissionType.Beacon);
if (beaconMissionPrefabs.Any())
{
Random rand = new MTRandom(ToolBox.StringToInt(levelData.Seed));
var beaconMissionPrefab = ToolBox.SelectWeightedRandom(beaconMissionPrefabs, p => (float)p.Commonness, rand);
if (!Missions.Any(m => m.Prefab.Type == beaconMissionPrefab.Type))
var filteredMissions = beaconMissionPrefabs.Where(m => levelData.Difficulty >= m.MinLevelDifficulty && levelData.Difficulty <= m.MaxLevelDifficulty);
if (filteredMissions.None())
{
extraMissions.Add(beaconMissionPrefab.Instantiate(Map.SelectedConnection.Locations, Submarine.MainSub));
DebugConsole.AddWarning($"No suitable beacon mission found matching the level difficulty {levelData.Difficulty}. Ignoring the restriction.");
}
else
{
beaconMissionPrefabs = filteredMissions;
}
Random rand = new MTRandom(ToolBox.StringToInt(levelData.Seed));
var beaconMissionPrefab = ToolBox.SelectWeightedRandom(beaconMissionPrefabs, p => p.Commonness, rand);
extraMissions.Add(beaconMissionPrefab.Instantiate(Map.SelectedConnection.Locations, Submarine.MainSub));
}
}
if (levelData.HasHuntingGrounds)
{
var huntingGroundsMissionPrefabs = MissionPrefab.Prefabs.Where(m => m.Tags.Contains("huntinggrounds")).OrderBy(m => m.UintIdentifier);
var huntingGroundsMissionPrefabs = MissionPrefab.Prefabs.Where(m => m.IsSideObjective && m.Tags.Contains("huntinggrounds")).OrderBy(m => m.UintIdentifier);
if (!huntingGroundsMissionPrefabs.Any())
{
DebugConsole.AddWarning("Could not find a hunting grounds mission for the level. No mission with the tag \"huntinggrounds\" found.");
@@ -862,6 +868,16 @@ namespace Barotrauma
}
}
//remove ID cards left in duffel bags
foreach (var item in Item.ItemList.ToList())
{
if (item.HasTag(Tags.IdCardTag) &&
(item.Container?.HasTag(Tags.DespawnContainer) ?? false))
{
item.Remove();
}
}
foreach (CharacterInfo ci in CrewManager.CharacterInfos.ToList())
{
if (ci.CauseOfDeath != null)
@@ -1353,6 +1369,7 @@ namespace Barotrauma
if (item.HiddenInGame) { continue; }
if (!connectedSubs.Contains(item.Submarine)) { continue; }
if (item.Prefab.DontTransferBetweenSubs) { continue; }
if (AnyParentInventoryDisableTransfer(item)) { continue; }
var rootOwner = item.GetRootInventoryOwner();
if (rootOwner is Character) { continue; }
if (rootOwner is Item ownerItem && (ownerItem.NonInteractable || item.NonPlayerTeamInteractable || ownerItem.HiddenInGame)) { continue; }
@@ -1362,6 +1379,15 @@ namespace Barotrauma
if (item.Components.Any(c => c is Wire w && w.Connections.Any(c => c != null))) { continue; }
itemsToTransfer.Add((item, item.Container));
item.Submarine = null;
static bool AnyParentInventoryDisableTransfer(Item item)
{
if (item.ParentInventory?.Owner is not Item parentOwner) { return false; }
return HasProblematicComponent(parentOwner) || AnyParentInventoryDisableTransfer(parentOwner);
static bool HasProblematicComponent(Item it)
=> it.Components.Any(static c => c.DontTransferInventoryBetweenSubs);
}
}
foreach (var (item, container) in itemsToTransfer)
{
@@ -1369,8 +1395,15 @@ namespace Barotrauma
{
// Drop the item if it's not inside another item set to be transferred.
item.Drop(null, createNetworkEvent: false, setTransform: false);
//dropping items sets the sub, set it back to null
item.Submarine = null;
foreach (var itemContainer in item.GetComponents<ItemContainer>())
{
itemContainer.Inventory.FindAllItems((_) => true, recursive: true).ForEach(it => it.Submarine = null);
}
}
}
System.Diagnostics.Debug.Assert(itemsToTransfer.None(it => it.item.Submarine != null), "Item that was set to be transferred was not removed from the sub!");
currentSub.Info.NoItems = true;
}
// Serialize the current sub
@@ -1408,6 +1441,7 @@ namespace Barotrauma
{
newContainer = newSub.FindContainerFor(item, onlyPrimary: true, checkTransferConditions: true, allowConnectedSubs: true);
}
string newContainerName = newContainer == null ? "(null)" : $"{newContainer.Prefab.Identifier} ({newContainer.Tags})";
if (item.Container == null && (newContainer == null || !newContainer.OwnInventory.TryPutItem(item, user: null, createNetworkEvent: false)))
{
var cargoContainer = CargoManager.GetOrCreateCargoContainerFor(item.Prefab, spawnHull, ref availableContainers);
@@ -1416,13 +1450,16 @@ namespace Barotrauma
Vector2 simPos = ConvertUnits.ToSimUnits(CargoManager.GetCargoPos(spawnHull, item.Prefab));
item.SetTransform(simPos, 0.0f, findNewHull: false, setPrevTransform: false);
}
else if (cargoContainer.Item.Submarine is Submarine containerSub)
else
{
// Use the item's sub in case the sub consists of multiple linked subs.
item.Submarine = containerSub;
if (cargoContainer.Item.Submarine is Submarine containerSub)
{
// Use the item's sub in case the sub consists of multiple linked subs.
item.Submarine = containerSub;
}
newContainerName = cargoContainer.Item.Prefab.Identifier.ToString();
}
}
string newContainerName = newContainer == null ? "(null)" : $"{newContainer.Prefab.Identifier} ({newContainer.Tags})";
string msg = "Item transfer log error.";
if (oldContainer != null)
{
@@ -83,6 +83,12 @@ namespace Barotrauma
}
}
[Serialize(0.2f, IsPropertySaveable.Yes, description: "How likely it is for security to inspect player characters for stolen items when your reputation is high?")]
public float MinStolenItemInspectionProbability { get; set; }
[Serialize(0.9f, IsPropertySaveable.Yes, description: "How likely it is for security to inspect player characters for stolen items when your reputation is low?")]
public float MaxStolenItemInspectionProbability { get; set; }
public const int DefaultMaxMissionCount = 2;
public const int MaxMissionCountLimit = 10;
public const int MinMissionCountLimit = 1;
@@ -319,6 +319,7 @@ namespace Barotrauma
foreach (var item in storeItems.Value)
{
msg.WriteIdentifier(item.ItemPrefabIdentifier);
msg.WriteBoolean(item.DeliverImmediately);
msg.WriteRangedInteger(item.Quantity, 0, CargoManager.MaxQuantity);
}
}
@@ -336,8 +337,12 @@ namespace Barotrauma
for (int j = 0; j < itemCount; j++)
{
Identifier itemId = msg.ReadIdentifier();
bool deliverImmediately = msg.ReadBoolean();
#if SERVER
if (!AllowImmediateItemDelivery(sender)) { deliverImmediately = false; }
#endif
int quantity = msg.ReadRangedInteger(0, CargoManager.MaxQuantity);
items[storeId].Add(new PurchasedItem(itemId, quantity, sender));
items[storeId].Add(new PurchasedItem(itemId, quantity, sender) { DeliverImmediately = deliverImmediately });
}
}
return items;
@@ -177,7 +177,7 @@ namespace Barotrauma
return;
}
int price = prefab.Price.GetBuyPrice(GetUpgradeLevel(prefab, category), Campaign.Map?.CurrentLocation);
int price = prefab.Price.GetBuyPrice(prefab, GetUpgradeLevel(prefab, category), Campaign.Map?.CurrentLocation);
int currentLevel = GetUpgradeLevel(prefab, category);
int newLevel = currentLevel + 1;
@@ -198,20 +198,23 @@ namespace Barotrauma
return result;
}
switch (GameMain.NetworkMember)
if (!force)
{
case null when Character.Controlled is { } controlled: // singleplayer
if (!TryTakeResources(controlled)) { return; }
break;
case { IsClient: true }:
if (!prefab.HasResourcesToUpgrade(Character.Controlled, newLevel)) { return; }
break;
case { IsServer: true } when client?.Character is { } character:
if (!TryTakeResources(character)) { return; }
break;
default:
DebugConsole.ThrowError($"Tried to purchase \"{prefab.Name}\" without a player.");
return;
switch (GameMain.NetworkMember)
{
case null when Character.Controlled is { } controlled: // singleplayer
if (!TryTakeResources(controlled)) { return; }
break;
case { IsClient: true }:
if (!prefab.HasResourcesToUpgrade(Character.Controlled, newLevel)) { return; }
break;
case { IsServer: true } when client?.Character is { } character:
if (!TryTakeResources(character)) { return; }
break;
default:
DebugConsole.ThrowError($"Tried to purchase \"{prefab.Name}\" without a player.");
return;
}
}
if (price < 0)
@@ -683,7 +686,8 @@ namespace Barotrauma
{
// automatically fix this if it ever happens?
DebugConsole.AddWarning($"The upgrade {newUpgrade.Prefab.Name} in {target.Name} has a different level compared to other items! \n" +
$"Expected level was ${newLevel} but got {newUpgrade.Level} instead.");
$"Expected level was ${newLevel} but got {newUpgrade.Level} instead.",
newUpgrade.Prefab.ContentPackage);
}
}
}