Build 0.18.0.0
This commit is contained in:
@@ -3,36 +3,32 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
#warning TODO: This class needs some changes:
|
||||
// - We shouldn't be iterating over MapEntityPrefab.List. It has no guarantee of any sort of order and becomes entirely unpredictable once you start adding mods.
|
||||
// - Note: iterating over ItemPrefab.Prefabs would also be incorrect. Sorting by UintIdentifier is necessary for determinism.
|
||||
// - SpawnItems and SpawnItem are named incorrectly.
|
||||
static class AutoItemPlacer
|
||||
{
|
||||
public static bool OutputDebugInfo = false;
|
||||
|
||||
/// <summary>
|
||||
/// If we are spawning in an area where difficulty should not be a factor, assume difficulty is at the exact "middle"
|
||||
/// </summary>
|
||||
public const float DefaultDifficultyModifier = 0f;
|
||||
|
||||
public static void PlaceIfNeeded()
|
||||
public static void SpawnItems()
|
||||
{
|
||||
if (GameMain.NetworkMember != null && !GameMain.NetworkMember.IsServer) { return; }
|
||||
|
||||
for (int i = 0; i < Submarine.MainSubs.Length; i++)
|
||||
bool skipMainSubs = GameMain.GameSession.GameMode is CampaignMode { IsFirstRound: false };
|
||||
if (!skipMainSubs)
|
||||
{
|
||||
if (Submarine.MainSubs[i] == null || Submarine.MainSubs[i].Info.InitialSuppliesSpawned) { continue; }
|
||||
List<Submarine> subs = new List<Submarine>() { Submarine.MainSubs[i] };
|
||||
subs.AddRange(Submarine.MainSubs[i].DockedTo.Where(d => !d.Info.IsOutpost));
|
||||
Place(subs);
|
||||
subs.ForEach(s => s.Info.InitialSuppliesSpawned = true);
|
||||
for (int i = 0; i < Submarine.MainSubs.Length; i++)
|
||||
{
|
||||
var sub = Submarine.MainSubs[i];
|
||||
if (sub == null || sub.Info.InitialSuppliesSpawned) { continue; }
|
||||
SpawnStartItems(sub);
|
||||
var subs = sub.GetConnectedSubs().Where(s => s.TeamID == sub.TeamID);
|
||||
CreateAndPlace(subs);
|
||||
subs.ForEach(s => s.Info.InitialSuppliesSpawned = true);
|
||||
}
|
||||
}
|
||||
|
||||
float difficultyModifier = GetLevelDifficultyModifier();
|
||||
foreach (var sub in Submarine.Loaded)
|
||||
{
|
||||
if (sub.Info.Type == SubmarineType.Player ||
|
||||
@@ -42,33 +38,93 @@ namespace Barotrauma
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Place(sub.ToEnumerable(), difficultyModifier: difficultyModifier);
|
||||
if (sub.Info.InitialSuppliesSpawned) { continue; }
|
||||
CreateAndPlace(sub.ToEnumerable());
|
||||
sub.Info.InitialSuppliesSpawned = true;
|
||||
}
|
||||
|
||||
if (Level.Loaded?.StartOutpost != null && Level.Loaded.Type == LevelData.LevelType.Outpost)
|
||||
{
|
||||
Rand.SetSyncedSeed(ToolBox.StringToInt(Level.Loaded.StartOutpost.Info.Name));
|
||||
Place(Level.Loaded.StartOutpost.ToEnumerable());
|
||||
var sub = Level.Loaded.StartOutpost;
|
||||
if (!sub.Info.InitialSuppliesSpawned)
|
||||
{
|
||||
Rand.SetSyncedSeed(ToolBox.StringToInt(sub.Info.Name));
|
||||
CreateAndPlace(sub.ToEnumerable());
|
||||
sub.Info.InitialSuppliesSpawned = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const float MaxDifficultyModifier = 0.2f;
|
||||
|
||||
/// <summary>
|
||||
/// Spawn probability of loot is modified by difficulty, -20% less loot at 0% difficulty and +20% loot at 100% difficulty.
|
||||
/// </summary>
|
||||
private static float GetLevelDifficultyModifier()
|
||||
{
|
||||
return Math.Clamp(Level.Loaded?.Difficulty is float difficulty ? (difficulty / 100f) * (MaxDifficultyModifier * 2) - MaxDifficultyModifier : DefaultDifficultyModifier, -MaxDifficultyModifier, MaxDifficultyModifier);
|
||||
}
|
||||
|
||||
public static void RegenerateLoot(Submarine sub, ItemContainer regeneratedContainer)
|
||||
{
|
||||
// Level difficulty currently doesn't affect regenerated loot for the sake of simplicity
|
||||
Place(sub.ToEnumerable(), regeneratedContainer: regeneratedContainer);
|
||||
CreateAndPlace(sub.ToEnumerable(), regeneratedContainer: regeneratedContainer);
|
||||
}
|
||||
|
||||
private static void Place(IEnumerable<Submarine> subs, ItemContainer regeneratedContainer = null, float difficultyModifier = DefaultDifficultyModifier)
|
||||
public static Identifier StartItemSet = new Identifier("normal");
|
||||
private static void SpawnStartItems(Submarine sub)
|
||||
{
|
||||
if (!Barotrauma.StartItemSet.Sets.TryGet(StartItemSet, out StartItemSet itemSet))
|
||||
{
|
||||
DebugConsole.AddWarning($"Couldn't find a start item set matching the identifier \"{StartItemSet}\"!");
|
||||
return;
|
||||
}
|
||||
WayPoint wp = WayPoint.GetRandom(SpawnType.Cargo, null, sub);
|
||||
ISpatialEntity initialSpawnPos;
|
||||
if (wp?.CurrentHull == null)
|
||||
{
|
||||
var spawnHull = Hull.HullList.Where(h => h.Submarine == sub && !h.IsWetRoom).GetRandomUnsynced();
|
||||
if (spawnHull == null)
|
||||
{
|
||||
DebugConsole.AddWarning($"Failed to spawn start items in the sub. No cargo waypoint or dry hulls found to spawn the items in.");
|
||||
return;
|
||||
}
|
||||
initialSpawnPos = spawnHull;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
initialSpawnPos = wp;
|
||||
}
|
||||
var newItems = new List<Item>();
|
||||
foreach (var startItem in itemSet.Items)
|
||||
{
|
||||
if (!ItemPrefab.Prefabs.TryGet(startItem.Item, out ItemPrefab itemPrefab))
|
||||
{
|
||||
DebugConsole.AddWarning($"Cannot find a start item with with the identifier \"{startItem.Item}\"");
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < startItem.Amount; i++)
|
||||
{
|
||||
var item = new Item(itemPrefab, initialSpawnPos.Position, sub, callOnItemLoaded: false);
|
||||
// Is this necessary?
|
||||
foreach (WifiComponent wifiComponent in item.GetComponents<WifiComponent>())
|
||||
{
|
||||
wifiComponent.TeamID = sub.TeamID;
|
||||
}
|
||||
newItems.Add(item);
|
||||
}
|
||||
}
|
||||
var cargoContainers = new List<ItemContainer>();
|
||||
foreach (var item in newItems)
|
||||
{
|
||||
#if SERVER
|
||||
Entity.Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(item));
|
||||
#endif
|
||||
foreach (ItemComponent ic in item.Components)
|
||||
{
|
||||
ic.OnItemLoaded();
|
||||
}
|
||||
var container = sub.FindContainerFor(item, onlyPrimary: true);
|
||||
if (container == null)
|
||||
{
|
||||
var cargoContainer = CargoManager.GetOrCreateCargoContainerFor(item.Prefab, initialSpawnPos, ref cargoContainers);
|
||||
container = cargoContainer?.Item;
|
||||
}
|
||||
container?.OwnInventory.TryPutItem(item, user: null);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateAndPlace(IEnumerable<Submarine> subs, ItemContainer regeneratedContainer = null)
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
|
||||
{
|
||||
@@ -76,7 +132,7 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
List<Item> spawnedItems = new List<Item>(100);
|
||||
List<Item> itemsToSpawn = new List<Item>(100);
|
||||
|
||||
int itemCountApprox = MapEntityPrefab.List.Count() / 3;
|
||||
var containers = new List<ItemContainer>(70 + 30 * subs.Count());
|
||||
@@ -100,11 +156,11 @@ namespace Barotrauma
|
||||
containers.Shuffle(Rand.RandSync.ServerAndClient);
|
||||
}
|
||||
|
||||
foreach (ItemPrefab ip in ItemPrefab.Prefabs)
|
||||
var itemPrefabs = ItemPrefab.Prefabs.OrderBy(p => p.UintIdentifier);
|
||||
foreach (ItemPrefab ip in itemPrefabs)
|
||||
{
|
||||
if (!ip.PreferredContainers.Any()) { continue; }
|
||||
if (ip.ConfigElement.Elements().Any(e => string.Equals(e.Name.ToString(), typeof(ItemContainer).Name.ToString(), StringComparison.OrdinalIgnoreCase)) &&
|
||||
ItemPrefab.Prefabs.Any(ip2 => CanSpawnIn(ip2, ip)))
|
||||
if (ip.ConfigElement.Elements().Any(e => string.Equals(e.Name.ToString(), typeof(ItemContainer).Name.ToString(), StringComparison.OrdinalIgnoreCase)) && itemPrefabs.Any(ip2 => CanSpawnIn(ip2, ip)))
|
||||
{
|
||||
prefabsItemsCanSpawnIn.Add(ip);
|
||||
}
|
||||
@@ -141,9 +197,9 @@ namespace Barotrauma
|
||||
{
|
||||
var subNames = subs.Select(s => s.Info.Name).ToList();
|
||||
DebugConsole.NewMessage($"Automatically placed items in { string.Join(", ", subNames) }:");
|
||||
foreach (string itemName in spawnedItems.Select(it => it.Name).Distinct())
|
||||
foreach (string itemName in itemsToSpawn.Select(it => it.Name).Distinct())
|
||||
{
|
||||
DebugConsole.NewMessage(" - " + itemName + " x" + spawnedItems.Count(it => it.Name == itemName));
|
||||
DebugConsole.NewMessage(" - " + itemName + " x" + itemsToSpawn.Count(it => it.Name == itemName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,24 +209,28 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (Location.TakenItem takenItem in GameMain.GameSession.StartLocation.TakenItems)
|
||||
{
|
||||
var matchingItem = spawnedItems.Find(it => takenItem.Matches(it));
|
||||
var matchingItem = itemsToSpawn.Find(it => takenItem.Matches(it));
|
||||
if (matchingItem == null) { continue; }
|
||||
var containedItems = spawnedItems.FindAll(it => it.ParentInventory?.Owner == matchingItem);
|
||||
if (OutputDebugInfo)
|
||||
{
|
||||
DebugConsole.NewMessage($"Removing the stolen item: {matchingItem.Prefab.Identifier} ({matchingItem.ID})");
|
||||
}
|
||||
var containedItems = itemsToSpawn.FindAll(it => it.ParentInventory?.Owner == matchingItem);
|
||||
matchingItem.Remove();
|
||||
spawnedItems.Remove(matchingItem);
|
||||
itemsToSpawn.Remove(matchingItem);
|
||||
foreach (Item containedItem in containedItems)
|
||||
{
|
||||
containedItem.Remove();
|
||||
spawnedItems.Remove(containedItem);
|
||||
itemsToSpawn.Remove(containedItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (Item spawnedItem in spawnedItems)
|
||||
foreach (Item item in itemsToSpawn)
|
||||
{
|
||||
#if SERVER
|
||||
Entity.Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(spawnedItem));
|
||||
Entity.Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(item));
|
||||
#endif
|
||||
foreach (ItemComponent ic in spawnedItem.Components)
|
||||
foreach (ItemComponent ic in item.Components)
|
||||
{
|
||||
ic.OnItemLoaded();
|
||||
}
|
||||
@@ -186,9 +246,12 @@ namespace Barotrauma
|
||||
return false;
|
||||
}
|
||||
bool success = false;
|
||||
bool isCampaign = GameMain.GameSession?.GameMode is CampaignMode;
|
||||
foreach (PreferredContainer preferredContainer in itemPrefab.PreferredContainers)
|
||||
{
|
||||
if (preferredContainer.SpawnProbability <= 0.0f || preferredContainer.MaxAmount <= 0) { continue; }
|
||||
if (preferredContainer.CampaignOnly && !isCampaign) { continue; }
|
||||
if (preferredContainer.NotCampaign && isCampaign) { continue; }
|
||||
if (preferredContainer.SpawnProbability <= 0.0f || preferredContainer.MaxAmount <= 0 && preferredContainer.Amount <= 0) { continue; }
|
||||
validContainers = GetValidContainers(preferredContainer, containers, validContainers, primary: true);
|
||||
if (validContainers.None())
|
||||
{
|
||||
@@ -196,10 +259,10 @@ namespace Barotrauma
|
||||
}
|
||||
foreach (var validContainer in validContainers)
|
||||
{
|
||||
var newItems = SpawnItem(itemPrefab, containers, validContainer, difficultyModifier);
|
||||
var newItems = CreateItems(itemPrefab, containers, validContainer);
|
||||
if (newItems.Any())
|
||||
{
|
||||
spawnedItems.AddRange(newItems);
|
||||
itemsToSpawn.AddRange(newItems);
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
@@ -238,16 +301,20 @@ namespace Barotrauma
|
||||
(3, 0.0f),
|
||||
};
|
||||
|
||||
private static List<Item> SpawnItem(ItemPrefab itemPrefab, List<ItemContainer> containers, KeyValuePair<ItemContainer, PreferredContainer> validContainer, float difficultyModifier)
|
||||
private static List<Item> CreateItems(ItemPrefab itemPrefab, List<ItemContainer> containers, KeyValuePair<ItemContainer, PreferredContainer> validContainer)
|
||||
{
|
||||
List<Item> spawnedItems = new List<Item>();
|
||||
if (Rand.Value(Rand.RandSync.ServerAndClient) > validContainer.Value.SpawnProbability * (1f + difficultyModifier)) { return spawnedItems; }
|
||||
List<Item> newItems = new List<Item>();
|
||||
if (Rand.Value(Rand.RandSync.ServerAndClient) > validContainer.Value.SpawnProbability) { return newItems; }
|
||||
// Don't add dangerously reactive materials in thalamus wrecks
|
||||
if (validContainer.Key.Item.Submarine.WreckAI != null && itemPrefab.Tags.Contains("explodesinwater"))
|
||||
{
|
||||
return spawnedItems;
|
||||
return newItems;
|
||||
}
|
||||
int amount = validContainer.Value.Amount;
|
||||
if (amount == 0)
|
||||
{
|
||||
amount = Rand.Range(validContainer.Value.MinAmount, validContainer.Value.MaxAmount + 1, Rand.RandSync.ServerAndClient);
|
||||
}
|
||||
int amount = Rand.Range(validContainer.Value.MinAmount, validContainer.Value.MaxAmount + 1, Rand.RandSync.ServerAndClient);
|
||||
for (int i = 0; i < amount; i++)
|
||||
{
|
||||
if (validContainer.Key.Inventory.IsFull(takeStacksIntoAccount: true))
|
||||
@@ -255,14 +322,12 @@ namespace Barotrauma
|
||||
containers.Remove(validContainer.Key);
|
||||
break;
|
||||
}
|
||||
|
||||
var existingItem = validContainer.Key.Inventory.AllItems.FirstOrDefault(it => it.Prefab == itemPrefab);
|
||||
int quality =
|
||||
existingItem?.Quality ??
|
||||
ToolBox.SelectWeightedRandom(
|
||||
qualityCommonnesses.Select(q => q.quality).ToList(),
|
||||
qualityCommonnesses.Select(q => q.commonness).ToList(),
|
||||
Rand.RandSync.ServerAndClient);
|
||||
qualityCommonnesses.Select(q => q.commonness).ToList(), Rand.RandSync.ServerAndClient);
|
||||
if (!validContainer.Key.Inventory.CanBePut(itemPrefab, quality: quality)) { break; }
|
||||
var item = new Item(itemPrefab, validContainer.Key.Item.Position, validContainer.Key.Item.Submarine, callOnItemLoaded: false)
|
||||
{
|
||||
@@ -277,11 +342,11 @@ namespace Barotrauma
|
||||
{
|
||||
wifiComponent.TeamID = validContainer.Key.Item.Submarine.TeamID;
|
||||
}
|
||||
spawnedItems.Add(item);
|
||||
newItems.Add(item);
|
||||
validContainer.Key.Inventory.TryPutItem(item, null, createNetworkEvent: false);
|
||||
containers.AddRange(item.GetComponents<ItemContainer>());
|
||||
}
|
||||
return spawnedItems;
|
||||
return newItems;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user