Unstable 1.1.14.0

This commit is contained in:
Markus Isberg
2023-10-02 16:43:54 +03:00
parent 94f5a93a0c
commit cf8f0de659
606 changed files with 21906 additions and 11456 deletions
@@ -58,9 +58,13 @@ namespace Barotrauma
}
}
public static void RegenerateLoot(Submarine sub, ItemContainer regeneratedContainer)
/// <summary>
/// Spawns loot in the specified container.
/// </summary>
/// <param name="skipItemProbability">Probability for an individual loot item to be skipped. I.e. a value of 1.0 means nothing spawns, 0.5 means there's about 50% of the normal amount of loot.</param>
public static void RegenerateLoot(Submarine sub, ItemContainer regeneratedContainer, float skipItemProbability = 0.0f)
{
CreateAndPlace(sub.ToEnumerable(), regeneratedContainer: regeneratedContainer);
CreateAndPlace(sub.ToEnumerable(), regeneratedContainer: regeneratedContainer, skipItemProbability);
}
public static Identifier DefaultStartItemSet = new Identifier("normal");
@@ -105,6 +109,7 @@ namespace Barotrauma
DebugConsole.AddWarning($"Cannot find a start item with with the identifier \"{startItem.Item}\"");
continue;
}
if (startItem.MultiPlayerOnly && GameMain.GameSession?.GameMode is { IsSinglePlayer: true }) { continue; }
for (int i = 0; i < startItem.Amount; i++)
{
var item = new Item(itemPrefab, initialSpawnPos.Position, sub, callOnItemLoaded: false);
@@ -136,7 +141,7 @@ namespace Barotrauma
}
}
private static void CreateAndPlace(IEnumerable<Submarine> subs, ItemContainer regeneratedContainer = null)
private static void CreateAndPlace(IEnumerable<Submarine> subs, ItemContainer regeneratedContainer = null, float skipItemProbability = 0.0f)
{
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
{
@@ -249,16 +254,16 @@ namespace Barotrauma
}
}
bool SpawnItems(ItemPrefab itemPrefab)
void SpawnItems(ItemPrefab itemPrefab, float skipItemProbability = 0.0f)
{
if (Rand.Range(0.0f, 1.0f, Rand.RandSync.ServerAndClient) < skipItemProbability) { return; }
if (itemPrefab == null)
{
string errorMsg = "Error in AutoItemPlacer.SpawnItems - itemPrefab was null.\n" + Environment.StackTrace.CleanupStackTrace();
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("AutoItemPlacer.SpawnItems:ItemNull", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
return false;
return;
}
bool success = false;
bool isCampaign = GameMain.GameSession?.GameMode is CampaignMode;
float levelDifficulty = Level.Loaded?.Difficulty ?? 0.0f;
foreach (PreferredContainer preferredContainer in itemPrefab.PreferredContainers)
@@ -278,11 +283,9 @@ namespace Barotrauma
if (newItems.Any())
{
itemsToSpawn.AddRange(newItems);
success = true;
}
}
}
return success;
}
}
@@ -440,7 +440,7 @@ 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("dontsellitems")) { return false; }
if (item.RootContainer is Item rootContainer && rootContainer.HasTag(Tags.DontSellItems)) { return false; }
return true;
}).Distinct();
@@ -498,7 +498,7 @@ namespace Barotrauma
.Distinct();
public static IEnumerable<Item> FilterCargoCrates(IEnumerable<Item> items, Func<Item, bool> conditional = null)
=> items.Where(it => it.HasTag("crate") && !it.NonInteractable && !it.NonPlayerTeamInteractable && !it.HiddenInGame && !it.Removed && (conditional == null || conditional(it)));
=> 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)))
@@ -535,6 +535,12 @@ namespace Barotrauma
DebugConsole.AddWarning($"CargoManager: No ItemContainer component found in {containerItem.Prefab.Identifier}!");
return null;
}
if (!itemContainer.CanBeContained(item))
{
// Can't contain the item in the crate -> let's not create it.
containerItem.Remove();
return null;
}
availableContainers.Add(itemContainer);
#if SERVER
if (GameMain.Server != null)
@@ -607,7 +613,7 @@ namespace Barotrauma
#if SERVER
Entity.Spawner?.CreateNetworkEvent(new EntitySpawner.SpawnEntity(item));
#endif
(itemContainer?.Item ?? item).CampaignInteractionType = CampaignMode.InteractionType.Cargo;
(itemContainer?.Item ?? item).AssignCampaignInteractionType(CampaignMode.InteractionType.Cargo);
static void itemSpawned(PurchasedItem purchased, Item item)
{
Submarine sub = item.Submarine ?? item.RootContainer?.Submarine;
@@ -262,7 +262,7 @@ namespace Barotrauma
while (spawnWaypoints.Any() && spawnWaypoints.Count < characterInfos.Count)
{
spawnWaypoints.Add(spawnWaypoints[Rand.Int(spawnWaypoints.Count)]);
}
}
}
if (spawnWaypoints == null || !spawnWaypoints.Any())
{
@@ -306,16 +306,15 @@ namespace Barotrauma
}
character.LoadTalents();
character.GiveIdCardTags(mainSubWaypoints[i]);
character.GiveIdCardTags(spawnWaypoints[i]);
character.GiveIdCardTags(new List<WayPoint>() { mainSubWaypoints[i], spawnWaypoints[i] });
character.Info.StartItemsGiven = true;
if (character.Info.OrderData != null)
{
character.Info.ApplyOrderData();
}
}
AddCharacter(character, sortCrewList: false);
#if CLIENT
if (IsSinglePlayer && (Character.Controlled == null || character.Info.LastControlled)) { Character.Controlled = character; }
@@ -59,6 +59,8 @@ namespace Barotrauma
public LocalizedString Description { get; }
public LocalizedString ShortDescription { get; }
public Identifier OpposingFaction { get; }
public class HireableCharacter
{
public readonly Identifier NPCSetIdentifier;
@@ -150,6 +152,7 @@ namespace Barotrauma
Name = element.GetAttributeString("name", null) ?? TextManager.Get($"faction.{Identifier}").Fallback("Unnamed");
Description = element.GetAttributeString("description", null) ?? TextManager.Get($"faction.{Identifier}.description").Fallback("");
ShortDescription = element.GetAttributeString("shortdescription", null) ?? TextManager.Get($"faction.{Identifier}.shortdescription").Fallback("");
OpposingFaction = element.GetAttributeIdentifier(nameof(OpposingFaction), Identifier.Empty);
List<HireableCharacter> hireableCharacters = new List<HireableCharacter>();
List<AutomaticMission> automaticMissions = new List<AutomaticMission>();
@@ -95,6 +95,8 @@ namespace Barotrauma
public SubmarineInfo PendingSubmarineSwitch;
public bool TransferItemsOnSubSwitch { get; set; }
public bool SwitchedSubsThisRound { get; private set; }
protected Map map;
public Map Map
{
@@ -293,6 +295,7 @@ namespace Barotrauma
PurchasedLostShuttlesInLatestSave = PurchasedLostShuttles = false;
var connectedSubs = Submarine.MainSub.GetConnectedSubs();
wasDocked = Level.Loaded.StartOutpost != null && connectedSubs.Contains(Level.Loaded.StartOutpost);
SwitchedSubsThisRound = false;
}
public static int GetHullRepairCost()
@@ -590,7 +593,7 @@ namespace Barotrauma
/// </summary>
protected abstract void LoadInitialLevel();
protected abstract IEnumerable<CoroutineStatus> DoLevelTransition(TransitionType transitionType, LevelData newLevel, Submarine leavingSub, bool mirror, List<TraitorMissionResult> traitorResults = null);
protected abstract IEnumerable<CoroutineStatus> DoLevelTransition(TransitionType transitionType, LevelData newLevel, Submarine leavingSub, bool mirror);
/// <summary>
/// Which type of transition between levels is currently possible (if any)
@@ -623,8 +626,7 @@ namespace Barotrauma
}
else if (map.SelectedConnection != null)
{
nextLevel = Level.Loaded.LevelData != map.SelectedConnection?.LevelData || (map.SelectedConnection.Locations[0] == Level.Loaded.EndLocation == Level.Loaded.Mirrored) ?
map.SelectedConnection.LevelData : null;
nextLevel = map.SelectedConnection.LevelData;
return TransitionType.ProgressToNextEmptyLocation;
}
else
@@ -878,6 +880,19 @@ namespace Barotrauma
port.Door.IsOpen = false;
}
}
foreach (Item item in Item.ItemList)
{
if (item.Submarine is not Submarine sub) { continue; }
if (!sub.Info.IsPlayer) { continue; }
if (sub.TeamID != CharacterTeamType.Team1 && sub.TeamID != CharacterTeamType.Team2) { continue; }
if (item.GetComponent<Reactor>() is Reactor reactor && reactor.LastAIUser != null && reactor.LastUser == reactor.LastAIUser)
{
// Reactor managed by an AI crew ->
// Turn auto temp on, so that the reactor won't be unmanaged at beginning of the next round.
reactor.AutoTemp = true;
}
}
}
/// <summary>
@@ -896,7 +911,7 @@ namespace Barotrauma
{
if (c.IsOnPlayerTeam)
{
c.CharacterHealth.RemoveAllAfflictions();
c.CharacterHealth.RemoveNegativeAfflictions();
}
}
foreach (LocationConnection connection in Map.Connections)
@@ -904,13 +919,17 @@ namespace Barotrauma
connection.Difficulty = connection.Biome.AdjustedMaxDifficulty;
connection.LevelData = new LevelData(connection)
{
IsBeaconActive = false
IsBeaconActive = false,
ForceOutpostGenerationParams = connection.LevelData.ForceOutpostGenerationParams
};
connection.LevelData.HasHuntingGrounds = connection.LevelData.OriginallyHadHuntingGrounds;
}
foreach (Location location in Map.Locations)
{
location.LevelData = new LevelData(location, Map, location.Biome.AdjustedMaxDifficulty);
location.LevelData = new LevelData(location, Map, location.Biome.AdjustedMaxDifficulty)
{
ForceOutpostGenerationParams = location.LevelData.ForceOutpostGenerationParams
};
location.Reset(this);
}
Map.ClearLocationHistory();
@@ -1294,7 +1313,7 @@ namespace Barotrauma
foreach (Submarine sub in subsToLeaveBehind)
{
GameMain.GameSession.OwnedSubmarines.RemoveAll(s => s != leavingSub.Info && s.Name == sub.Info.Name);
MapEntity.mapEntityList.RemoveAll(e => e.Submarine == sub && e is LinkedSubmarine);
MapEntity.MapEntityList.RemoveAll(e => e.Submarine == sub && e is LinkedSubmarine);
LinkedSubmarine.CreateDummy(leavingSub, sub);
}
}
@@ -1307,6 +1326,7 @@ namespace Barotrauma
TransferItemsBetweenSubs();
}
RefreshOwnedSubmarines();
SwitchedSubsThisRound = true;
PendingSubmarineSwitch = null;
}
@@ -1368,7 +1388,7 @@ namespace Barotrauma
return;
}
// First move the cargo containers, so that we can reuse them
var cargoContainers = itemsToTransfer.Where(it => it.item.HasTag("crate"));
var cargoContainers = itemsToTransfer.Where(it => it.item.HasTag(Tags.Crate));
foreach (var (item, oldContainer) in cargoContainers)
{
Vector2 simPos = ConvertUnits.ToSimUnits(CargoManager.GetCargoPos(spawnHull, item.Prefab));
@@ -230,6 +230,9 @@ namespace Barotrauma
Bank = new Wallet(Option<Character>.None(), subElement);
break;
#if SERVER
case nameof(TraitorManager):
GameMain.Server?.TraitorManager?.Load(subElement);
break;
case "savedexperiencepoints":
foreach (XElement savedExp in subElement.Elements())
{
@@ -21,6 +21,8 @@ namespace Barotrauma
public enum InfoFrameTab { Crew, Mission, MyCharacter, Traitor };
public Version LastSaveVersion { get; set; } = GameMain.Version;
public readonly EventManager EventManager;
public GameMode? GameMode;
@@ -107,6 +109,10 @@ namespace Barotrauma
public string? SavePath { get; set; }
public bool TraitorsEnabled =>
GameMain.NetworkMember?.ServerSettings != null &&
GameMain.NetworkMember.ServerSettings.TraitorProbability > 0.0f;
partial void InitProjSpecific();
private GameSession(SubmarineInfo submarineInfo)
@@ -148,7 +154,8 @@ namespace Barotrauma
this.SavePath = saveFile;
GameMain.GameSession = this;
XElement rootElement = doc.Root ?? throw new NullReferenceException("Game session XML element is invalid: document is null.");
//selectedSub.Name = doc.Root.GetAttributeString("submarine", selectedSub.Name);
LastSaveVersion = doc.Root.GetAttributeVersion("version", GameMain.Version);
foreach (var subElement in rootElement.Elements())
{
@@ -300,7 +307,7 @@ namespace Barotrauma
public void LoadPreviousSave()
{
Submarine.Unload();
SaveUtil.LoadGame(SavePath);
SaveUtil.LoadGame(SavePath ?? "");
}
/// <summary>
@@ -376,7 +383,10 @@ namespace Barotrauma
missionPrefab.AllowedLocationTypes.Any() &&
!missionPrefab.AllowedConnectionTypes.Any())
{
LocationType? locationType = LocationType.Prefabs.FirstOrDefault(lt => missionPrefab.AllowedLocationTypes.Any(m => m == lt.Identifier));
Random rand = new MTRandom(ToolBox.StringToInt(levelSeed));
LocationType? locationType = LocationType.Prefabs
.Where(lt => missionPrefab.AllowedLocationTypes.Any(m => m == lt.Identifier))
.GetRandom(rand);
dummyLocations = CreateDummyLocations(levelSeed, locationType);
randomLevel = LevelData.CreateRandom(levelSeed, difficulty, levelGenerationParams, requireOutpost: true);
break;
@@ -392,7 +402,7 @@ namespace Barotrauma
DateTime startTime = DateTime.Now;
#endif
RoundDuration = 0.0f;
AfflictionPrefab.LoadAllEffects();
AfflictionPrefab.LoadAllEffectsAndTreatmentSuitabilities();
MirrorLevel = mirrorLevel;
if (SubmarineInfo == null)
@@ -572,11 +582,11 @@ namespace Barotrauma
}
ReadyCheck.ReadyCheckCooldown = DateTime.MinValue;
GUI.PreventPauseMenuToggle = false;
HintManager.OnRoundStarted();
EnableEventLogNotificationIcon(enabled: false);
#endif
EventManager?.EventLog?.Clear();
if (campaignMode is { DivingSuitWarningShown: false } &&
Level.Loaded != null && Level.Loaded.GetRealWorldDepth(0) > 4000)
{
@@ -605,7 +615,8 @@ namespace Barotrauma
foreach (var sub in Submarine.Loaded)
{
if (sub.Info.IsOutpost)
// TODO: Currently there's no need to check these on ruins, but that might change -> Could maybe just check if the body is static?
if (sub.Info.IsOutpost || sub.Info.IsBeacon || sub.Info.IsWreck)
{
sub.DisableObstructedWayPoints();
}
@@ -759,7 +770,7 @@ namespace Barotrauma
// Make sure that linked subs which are NOT docked to the main sub
// (but still close enough to NOT be considered as 'left behind')
// are also moved to keep their relative position to the main sub
var linkedSubs = MapEntity.mapEntityList.FindAll(me => me is LinkedSubmarine);
var linkedSubs = MapEntity.MapEntityList.FindAll(me => me is LinkedSubmarine);
foreach (LinkedSubmarine ls in linkedSubs)
{
if (ls.Sub == null || ls.Submarine != Submarine) { continue; }
@@ -845,7 +856,11 @@ namespace Barotrauma
return characters.ToImmutableHashSet();
}
public void EndRound(string endMessage, List<TraitorMissionResult>? traitorResults = null, CampaignMode.TransitionType transitionType = CampaignMode.TransitionType.None)
#if SERVER
private double LastEndRoundErrorMessageTime;
#endif
public void EndRound(string endMessage, CampaignMode.TransitionType transitionType = CampaignMode.TransitionType.None, TraitorManager.TraitorResults? traitorResults = null)
{
RoundEnding = true;
@@ -854,10 +869,10 @@ namespace Barotrauma
try
{
EventManager?.TriggerOnEndRoundActions();
ImmutableHashSet<Character> crewCharacters = GetSessionCrewCharacters(CharacterType.Both);
int prevMoney = GetAmountOfMoney(crewCharacters);
foreach (Mission mission in missions)
{
mission.End();
@@ -898,11 +913,11 @@ namespace Barotrauma
GUI.PreventPauseMenuToggle = true;
if (!(GameMode is TestGameMode) && Screen.Selected == GameMain.GameScreen && RoundSummary != null && transitionType != CampaignMode.TransitionType.End)
if (GameMode is not TestGameMode && Screen.Selected == GameMain.GameScreen && RoundSummary != null && transitionType != CampaignMode.TransitionType.End)
{
GUI.ClearMessages();
GUIMessageBox.MessageBoxes.RemoveAll(mb => mb.UserData is RoundSummary);
GUIFrame summaryFrame = RoundSummary.CreateSummaryFrame(this, endMessage, traitorResults, transitionType);
GUIFrame summaryFrame = RoundSummary.CreateSummaryFrame(this, endMessage, transitionType, traitorResults);
GUIMessageBox.MessageBoxes.Add(summaryFrame);
RoundSummary.ContinueButton.OnClicked = (_, __) => { GUIMessageBox.MessageBoxes.Remove(summaryFrame); return true; };
}
@@ -915,6 +930,9 @@ namespace Barotrauma
#endif
SteamAchievementManager.OnRoundEnded(this);
#if SERVER
GameMain.Server?.TraitorManager?.EndRound();
#endif
GameMode?.End(transitionType);
EventManager?.EndRound();
StatusEffect.StopAll();
@@ -924,14 +942,16 @@ namespace Barotrauma
#if CLIENT
bool success = CrewManager!.GetCharacters().Any(c => !c.IsDead);
#else
bool success = GameMain.Server.ConnectedClients.Any(c => c.InGame && c.Character != null && !c.Character.IsDead);
bool success =
GameMain.Server != null &&
GameMain.Server.ConnectedClients.Any(c => c.InGame && c.Character != null && !c.Character.IsDead);
#endif
GameAnalyticsManager.AddProgressionEvent(
success ? GameAnalyticsManager.ProgressionStatus.Complete : GameAnalyticsManager.ProgressionStatus.Fail,
GameMode?.Preset.Identifier.Value ?? "none",
RoundDuration);
string eventId = "EndRound:" + (GameMode?.Preset?.Identifier.Value ?? "none") + ":";
LogEndRoundStats(eventId);
LogEndRoundStats(eventId, traitorResults);
if (GameMode is CampaignMode campaignMode)
{
GameAnalyticsManager.AddDesignEvent(eventId + "MoneyEarned", GetAmountOfMoney(crewCharacters) - prevMoney);
@@ -942,6 +962,19 @@ namespace Barotrauma
#endif
missions.Clear();
}
catch (Exception e)
{
string errorMsg = "Unknown error while ending the round.";
DebugConsole.ThrowError(errorMsg, e);
GameAnalyticsManager.AddErrorEventOnce("GameSession.EndRound:UnknownError", GameAnalyticsManager.ErrorSeverity.Error, errorMsg + "\n" + e.StackTrace);
#if SERVER
if (Timing.TotalTime > LastEndRoundErrorMessageTime + 1.0)
{
GameMain.Server?.SendChatMessage(errorMsg + "\n" + e.StackTrace, Networking.ChatMessageType.Error);
LastEndRoundErrorMessageTime = Timing.TotalTime;
}
#endif
}
finally
{
RoundEnding = false;
@@ -949,7 +982,7 @@ namespace Barotrauma
int GetAmountOfMoney(IEnumerable<Character> crew)
{
if (!(GameMode is CampaignMode campaign)) { return 0; }
if (GameMode is not CampaignMode campaign) { return 0; }
return GameMain.NetworkMember switch
{
@@ -959,7 +992,7 @@ namespace Barotrauma
}
}
public void LogEndRoundStats(string eventId)
public void LogEndRoundStats(string eventId, TraitorManager.TraitorResults? traitorResults = null)
{
GameAnalyticsManager.AddDesignEvent(eventId + "Submarine:" + (Submarine.MainSub?.Info?.Name ?? "none"), RoundDuration);
GameAnalyticsManager.AddDesignEvent(eventId + "GameMode:" + (GameMode?.Name.Value ?? "none"), RoundDuration);
@@ -1003,6 +1036,12 @@ namespace Barotrauma
}
}
if (traitorResults.HasValue)
{
GameAnalyticsManager.AddDesignEvent($"TraitorEvent:{traitorResults.Value.TraitorEventIdentifier}:{traitorResults.Value.ObjectiveSuccessful}");
GameAnalyticsManager.AddDesignEvent($"TraitorEvent:{traitorResults.Value.TraitorEventIdentifier}:{(traitorResults.Value.VotedCorrectTraitor ? "TraitorIdentifier" : "TraitorUnidentified")}");
}
foreach (Character c in GetSessionCrewCharacters(CharacterType.Both))
{
foreach (var itemSelectedDuration in c.ItemSelectedDurations)
@@ -1134,6 +1173,7 @@ namespace Barotrauma
#warning TODO: after this gets on main, replace savetime with the commented line
//rootElement.Add(new XAttribute("savetime", SerializableDateTime.LocalNow));
LastSaveVersion = GameMain.Version;
rootElement.Add(new XAttribute("version", GameMain.Version));
if (Submarine?.Info != null && !Submarine.Removed && Campaign != null)
{