Merge remote-tracking branch 'upstream/dev' into develop

This commit is contained in:
EvilFactory
2022-12-09 17:33:44 -03:00
416 changed files with 12674 additions and 5862 deletions
@@ -163,6 +163,7 @@ namespace Barotrauma
{
if (!subs.Contains(item.Submarine)) { continue; }
if (item.GetRootInventoryOwner() is Character) { continue; }
if (item.NonInteractable) { continue; }
containers.AddRange(item.GetComponents<ItemContainer>());
}
containers.Shuffle(Rand.RandSync.ServerAndClient);
@@ -305,14 +306,6 @@ namespace Barotrauma
return validContainers;
}
private static readonly (int quality, float commonness)[] qualityCommonnesses = new (int quality, float commonness)[Quality.MaxQuality + 1]
{
(0, 1.0f),
(1, 0.0f),
(2, 0.0f),
(3, 0.0f),
};
private static List<Item> CreateItems(ItemPrefab itemPrefab, List<ItemContainer> containers, KeyValuePair<ItemContainer, PreferredContainer> validContainer)
{
List<Item> newItems = new List<Item>();
@@ -335,11 +328,7 @@ namespace Barotrauma
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);
int quality = existingItem?.Quality ?? Quality.GetSpawnedItemQuality(validContainer.Key.Item.Submarine, Level.Loaded, 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)
{
@@ -158,6 +158,16 @@ namespace Barotrauma
this.campaign = campaign;
}
public static bool HasUnlockedStoreItem(ItemPrefab prefab)
{
foreach (Character character in GameSession.GetSessionCrewCharacters(CharacterType.Both))
{
if (character.HasStoreAccessForItem(prefab)) { return true; }
}
return false;
}
private List<T> GetItems<T>(Identifier identifier, Dictionary<Identifier, List<T>> items, bool create = false)
{
if (items.TryGetValue(identifier, out var storeSpecificItems) && storeSpecificItems != null)
@@ -62,6 +62,7 @@ namespace Barotrauma
if (!data.ContainsKey(identifier))
{
data.Add(identifier, value);
SteamAchievementManager.OnCampaignMetadataSet(identifier, value);
return;
}
@@ -4,6 +4,12 @@ using System;
namespace Barotrauma
{
public enum FactionAffiliation
{
Affiliated,
Neutral
}
class Faction
{
public Reputation Reputation { get; }
@@ -14,6 +20,27 @@ namespace Barotrauma
Prefab = prefab;
Reputation = new Reputation(metadata, this, prefab.MinReputation, prefab.MaxReputation, prefab.InitialReputation);
}
/// <summary>
/// Get what kind of affiliation this faction has towards the player depending on who they chose to side with via talents
/// </summary>
/// <returns></returns>
public FactionAffiliation GetPlayerAffiliationStatus()
{
float affiliation = 1f;
foreach (Character character in GameSession.GetSessionCrewCharacters(CharacterType.Both))
{
if (character.Info is not { } info) { continue; }
affiliation *= 1f + info.GetSavedStatValue(StatTypes.Affiliation, Prefab.Identifier);
}
return affiliation switch
{
>= 1f => FactionAffiliation.Affiliated,
_ => FactionAffiliation.Neutral
};
}
}
internal class FactionPrefab : Prefab
@@ -70,6 +70,15 @@ namespace Barotrauma
}
reputationChange *= reputationGainMultiplier;
}
else if (reputationChange < 0f)
{
float reputationLossMultiplier = 1f;
foreach (Character character in GameSession.GetSessionCrewCharacters(CharacterType.Both))
{
reputationLossMultiplier += character.GetStatValue(StatTypes.ReputationLossMultiplier);
}
reputationChange *= reputationLossMultiplier;
}
Value += reputationChange;
}
@@ -80,7 +80,7 @@ namespace Barotrauma
public bool DisableEvents
{
get { return IsFirstRound && Timing.TotalTime < GameMain.GameSession.RoundStartTime + FirstRoundEventDelay; }
get { return IsFirstRound && GameMain.GameSession.RoundDuration < FirstRoundEventDelay; }
}
public bool CheatsEnabled;
@@ -139,6 +139,15 @@ namespace Barotrauma
public virtual bool PurchasedLostShuttles { get; set; }
public virtual bool PurchasedItemRepairs { get; set; }
private static bool AnyOneAllowedToManageCampaign(ClientPermissions permissions)
{
if (GameMain.NetworkMember == null) { return true; }
//allow managing if no-one with permissions is alive
return
GameMain.NetworkMember.ConnectedClients.Count == 1 ||
GameMain.NetworkMember.ConnectedClients.None(c => c.InGame && c.Character is { IsIncapacitated: false, IsDead: false } && (IsOwner(c) || c.HasPermission(permissions)));
}
protected CampaignMode(GameModePreset preset, CampaignSettings settings)
: base(preset)
{
@@ -211,7 +220,7 @@ namespace Barotrauma
return Level.Loaded?.StartLocation ?? Map.CurrentLocation;
}
public List<Submarine> GetSubsToLeaveBehind(Submarine leavingSub)
public static List<Submarine> GetSubsToLeaveBehind(Submarine leavingSub)
{
//leave subs behind if they're not docked to the leaving sub and not at the same exit
return Submarine.Loaded.FindAll(sub =>
@@ -240,7 +249,7 @@ namespace Barotrauma
{
for (int i = 0; i < wall.SectionCount; i++)
{
wall.SetDamage(i, 0, createNetworkEvent: false);
wall.SetDamage(i, 0, createNetworkEvent: false, createExplosionEffect: false);
}
}
}
@@ -266,7 +275,7 @@ namespace Barotrauma
wasDocked = Level.Loaded.StartOutpost != null && connectedSubs.Contains(Level.Loaded.StartOutpost);
}
public int GetHullRepairCost()
public static int GetHullRepairCost()
{
float totalDamage = 0;
foreach (Structure wall in Structure.WallList)
@@ -283,7 +292,7 @@ namespace Barotrauma
return (int)Math.Min(totalDamage * HullRepairCostPerDamage, MaxHullRepairCost);
}
public int GetItemRepairCost()
public static int GetItemRepairCost()
{
float totalRepairDuration = 0.0f;
foreach (Item item in Item.ItemList)
@@ -316,9 +325,18 @@ namespace Barotrauma
public override void AddExtraMissions(LevelData levelData)
{
if (levelData == null)
{
throw new ArgumentException("Level data was null.");
}
extraMissions.Clear();
var currentLocation = Map.CurrentLocation;
if (currentLocation == null)
{
throw new InvalidOperationException("Current location was null.");
}
if (levelData.Type == LevelData.LevelType.Outpost)
{
//if there's an available mission that takes place in the outpost, select it
@@ -551,7 +569,7 @@ namespace Barotrauma
/// <summary>
/// Which submarine is at a position where it can leave the level and enter another one (if any).
/// </summary>
private Submarine GetLeavingSub()
private static Submarine GetLeavingSub()
{
if (Level.IsLoadedOutpost)
{
@@ -652,9 +670,10 @@ namespace Barotrauma
if (map != null && CargoManager != null)
{
map.CurrentLocation.RegisterTakenItems(takenItems);
map.CurrentLocation.AddStock(CargoManager.SoldItems);
CargoManager.ClearSoldItemsProjSpecific();
map.CurrentLocation.RemoveStock(CargoManager.PurchasedItems);
if (transitionType != TransitionType.None)
{
UpdateStoreStock();
}
}
if (GameMain.NetworkMember == null)
{
@@ -717,6 +736,16 @@ namespace Barotrauma
}
}
/// <summary>
/// Updates store stock before saving the game
/// </summary>
public void UpdateStoreStock()
{
Map?.CurrentLocation?.AddStock(CargoManager.SoldItems);
CargoManager?.ClearSoldItemsProjSpecific();
Map?.CurrentLocation?.RemoveStock(CargoManager.PurchasedItems);
}
public void EndCampaign()
{
foreach (Character c in Character.CharacterList)
@@ -740,6 +769,7 @@ namespace Barotrauma
location.LevelData = new LevelData(location, location.Biome.AdjustedMaxDifficulty);
location.Reset();
}
Map.ClearLocationHistory();
Map.SetLocation(Map.Locations.IndexOf(Map.StartLocation));
Map.SelectLocation(-1);
if (Map.Radiation != null)
@@ -1025,7 +1055,7 @@ namespace Barotrauma
}
}
protected void LeaveUnconnectedSubs(Submarine leavingSub)
protected static void LeaveUnconnectedSubs(Submarine leavingSub)
{
if (leavingSub != Submarine.MainSub && !leavingSub.DockedTo.Contains(Submarine.MainSub))
{
@@ -1084,6 +1114,7 @@ namespace Barotrauma
if (item.Components.None(c => c is Pickable)) { continue; }
if (item.Components.Any(c => c is Pickable p && p.IsAttached)) { continue; }
if (item.Components.Any(c => c is Wire w && w.Connections.Any(c => c != null))) { continue; }
if (item.Container?.GetComponent<ItemContainer>() is { DrawInventory: false }) { continue; }
itemsToTransfer.Add((item, item.Container));
item.Submarine = null;
}
@@ -2,6 +2,7 @@
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
@@ -17,6 +18,9 @@ namespace Barotrauma
[Serialize("", IsPropertySaveable.Yes)]
public string PresetName { get; set; } = string.Empty;
[Serialize(true, IsPropertySaveable.Yes)]
public bool TutorialEnabled { get; set; }
[Serialize(false, IsPropertySaveable.Yes), NetworkSerialize]
public bool RadiationEnabled { get; set; }
@@ -103,12 +107,9 @@ namespace Barotrauma
private static int GetAddedMissionCount()
{
int count = 0;
foreach (Character character in GameSession.GetSessionCrewCharacters(CharacterType.Both))
{
count += (int)character.GetStatValue(StatTypes.ExtraMissionCount);
}
return count;
var characters = GameSession.GetSessionCrewCharacters(CharacterType.Both);
if (!characters.Any()) { return 0; }
return characters.Max(static character => (int)character.GetStatValue(StatTypes.ExtraMissionCount));
}
}
}
@@ -133,13 +133,13 @@ namespace Barotrauma
}
partial void InitProjSpecific();
public static string GetCharacterDataSavePath(string savePath)
{
return Path.Combine(SaveUtil.MultiplayerSaveFolder, Path.GetFileNameWithoutExtension(savePath) + "_CharacterData.xml");
return Path.Combine(Path.GetDirectoryName(savePath), Path.GetFileNameWithoutExtension(savePath) + "_CharacterData.xml");
}
public string GetCharacterDataSavePath()
public static string GetCharacterDataSavePath()
{
return GetCharacterDataSavePath(GameMain.GameSession.SavePath);
}
@@ -29,6 +29,14 @@ namespace Barotrauma
public readonly Sprite Banner;
public readonly EndMessageInfo EndMessage;
public enum EndType { None, Continue, Restart }
public readonly record struct EndMessageInfo(
EndType EndType,
Identifier NextTutorialIdentifier);
public TutorialPrefab(ContentFile file, ContentXElement element) : base(file, element.GetAttributeIdentifier("identifier", ""))
{
Order = element.GetAttributeInt("order", int.MaxValue);
@@ -59,6 +67,13 @@ namespace Barotrauma
}
EventIdentifier = element.GetChildElement("scriptedevent")?.GetAttributeIdentifier("identifier", "") ?? Identifier.Empty;
if (element.GetChildElement("endmessage") is ContentXElement endMessageElement)
{
EndMessage = new EndMessageInfo(
EndType: endMessageElement.GetAttributeEnum("type", EndType.None),
NextTutorialIdentifier: endMessageElement.GetAttributeIdentifier("nexttutorial", Identifier.Empty));
}
}
public CharacterInfo GetTutorialCharacterInfo()
@@ -28,7 +28,10 @@ namespace Barotrauma
private Location[]? dummyLocations;
public CrewManager? CrewManager;
public double RoundStartTime;
public float RoundDuration
{
get; private set;
}
public double TimeSpentCleaning, TimeSpentPainting;
@@ -353,6 +356,7 @@ namespace Barotrauma
#if DEBUG
DateTime startTime = DateTime.Now;
#endif
RoundDuration = 0.0f;
AfflictionPrefab.LoadAllEffects();
MirrorLevel = mirrorLevel;
@@ -503,7 +507,7 @@ namespace Barotrauma
RoundSummary = new RoundSummary(GameMode, Missions, StartLocation, EndLocation);
if (!(GameMode is TutorialMode) && !(GameMode is TestGameMode))
if (GameMode is not TutorialMode && GameMode is not TestGameMode)
{
GUI.AddMessage("", Color.Transparent, 3.0f, playSound: false);
if (EndLocation != null && levelData != null)
@@ -573,9 +577,9 @@ namespace Barotrauma
GameMode.Start();
foreach (Mission mission in missions)
{
int prevEntityCount = Entity.GetEntities().Count();
int prevEntityCount = Entity.GetEntities().Count;
mission.Start(Level.Loaded);
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient && Entity.GetEntities().Count() != prevEntityCount)
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient && Entity.GetEntities().Count != prevEntityCount)
{
DebugConsole.ThrowError(
$"Entity count has changed after starting a mission ({mission.Prefab.Identifier}) as a client. " +
@@ -584,6 +588,9 @@ namespace Barotrauma
}
}
#if CLIENT
ObjectiveManager.ResetObjectives();
#endif
EventManager?.StartRound(Level.Loaded);
SteamAchievementManager.OnStartRound();
@@ -611,7 +618,7 @@ namespace Barotrauma
CreatureMetrics.Instance.RecentlyEncountered.Clear();
GameMain.GameScreen.Cam.Position = Character.Controlled?.WorldPosition ?? Submarine.MainSub.WorldPosition;
RoundStartTime = Timing.TotalTime;
RoundDuration = 0.0f;
GameMain.ResetFrameTime();
IsRunning = true;
}
@@ -712,6 +719,7 @@ namespace Barotrauma
public void Update(float deltaTime)
{
RoundDuration += deltaTime;
EventManager?.Update(deltaTime);
GameMode?.Update(deltaTime);
//backwards for loop because the missions may get completed and removed from the list in Update()
@@ -761,7 +769,7 @@ namespace Barotrauma
var result = GameMain.LuaCs.Hook.Call<Character[]?>("getSessionCrewCharacters", type);
if (result != null) return ImmutableHashSet.Create(result);
if (!(GameMain.GameSession.CrewManager is { } crewManager)) { return ImmutableHashSet<Character>.Empty; }
if (GameMain.GameSession?.CrewManager is not { } crewManager) { return ImmutableHashSet<Character>.Empty; }
IEnumerable<Character> players;
IEnumerable<Character> bots;
@@ -771,8 +779,8 @@ namespace Barotrauma
players = GameMain.Server.ConnectedClients.Select(c => c.Character).Where(c => c?.Info != null && !c.IsDead);
bots = crewManager.GetCharacters().Where(c => !c.IsRemotePlayer);
#elif CLIENT
players = crewManager.GetCharacters().Where(c => c.IsPlayer);
bots = crewManager.GetCharacters().Where(c => c.IsBot);
players = crewManager.GetCharacters().Where(static c => c.IsPlayer);
bots = crewManager.GetCharacters().Where(static c => c.IsBot);
#endif
if (type.HasFlag(CharacterType.Bot))
{
@@ -857,6 +865,7 @@ namespace Barotrauma
if (GameMain.NetLobbyScreen != null) { GameMain.NetLobbyScreen.OnRoundEnded(); }
TabMenu.OnRoundEnded();
GUIMessageBox.MessageBoxes.RemoveAll(mb => mb.UserData as string == "ConversationAction" || ReadyCheck.IsReadyCheck(mb));
ObjectiveManager.ResetUI();
#endif
SteamAchievementManager.OnRoundEnded(this);
@@ -871,17 +880,16 @@ namespace Barotrauma
#else
bool success = GameMain.Server.ConnectedClients.Any(c => c.InGame && c.Character != null && !c.Character.IsDead);
#endif
double roundDuration = Timing.TotalTime - RoundStartTime;
GameAnalyticsManager.AddProgressionEvent(
success ? GameAnalyticsManager.ProgressionStatus.Complete : GameAnalyticsManager.ProgressionStatus.Fail,
GameMode?.Preset.Identifier.Value ?? "none",
roundDuration);
RoundDuration);
string eventId = "EndRound:" + (GameMode?.Preset?.Identifier.Value ?? "none") + ":";
LogEndRoundStats(eventId);
if (GameMode is CampaignMode campaignMode)
{
GameAnalyticsManager.AddDesignEvent(eventId + "MoneyEarned", GetAmountOfMoney(crewCharacters) - prevMoney);
campaignMode.TotalPlayTime += roundDuration;
campaignMode.TotalPlayTime += RoundDuration;
}
#if CLIENT
HintManager.OnRoundEnded();
@@ -907,21 +915,20 @@ namespace Barotrauma
public void LogEndRoundStats(string eventId)
{
double roundDuration = Timing.TotalTime - RoundStartTime;
GameAnalyticsManager.AddDesignEvent(eventId + "Submarine:" + (Submarine.MainSub?.Info?.Name ?? "none"), roundDuration);
GameAnalyticsManager.AddDesignEvent(eventId + "GameMode:" + (GameMode?.Name.Value ?? "none"), roundDuration);
GameAnalyticsManager.AddDesignEvent(eventId + "CrewSize:" + (CrewManager?.CharacterInfos?.Count() ?? 0), roundDuration);
GameAnalyticsManager.AddDesignEvent(eventId + "Submarine:" + (Submarine.MainSub?.Info?.Name ?? "none"), RoundDuration);
GameAnalyticsManager.AddDesignEvent(eventId + "GameMode:" + (GameMode?.Name.Value ?? "none"), RoundDuration);
GameAnalyticsManager.AddDesignEvent(eventId + "CrewSize:" + (CrewManager?.CharacterInfos?.Count ?? 0), RoundDuration);
foreach (Mission mission in missions)
{
GameAnalyticsManager.AddDesignEvent(eventId + "MissionType:" + (mission.Prefab.Type.ToString() ?? "none") + ":" + mission.Prefab.Identifier + ":" + (mission.Completed ? "Completed" : "Failed"), roundDuration);
GameAnalyticsManager.AddDesignEvent(eventId + "MissionType:" + (mission.Prefab.Type.ToString() ?? "none") + ":" + mission.Prefab.Identifier + ":" + (mission.Completed ? "Completed" : "Failed"), RoundDuration);
}
if (Level.Loaded != null)
{
Identifier levelId = (Level.Loaded.Type == LevelData.LevelType.Outpost ?
Level.Loaded.StartOutpost?.Info?.OutpostGenerationParams?.Identifier :
Level.Loaded.GenerationParams?.Identifier) ?? "null".ToIdentifier();
GameAnalyticsManager.AddDesignEvent(eventId + "LevelType:" + (Level.Loaded?.Type.ToString() ?? "none" + ":" + levelId), roundDuration);
GameAnalyticsManager.AddDesignEvent(eventId + "Biome:" + (Level.Loaded?.LevelData?.Biome?.Identifier.Value ?? "none"), roundDuration);
GameAnalyticsManager.AddDesignEvent(eventId + "LevelType:" + (Level.Loaded?.Type.ToString() ?? "none" + ":" + levelId), RoundDuration);
GameAnalyticsManager.AddDesignEvent(eventId + "Biome:" + (Level.Loaded?.LevelData?.Biome?.Identifier.Value ?? "none"), RoundDuration);
}
if (Submarine.MainSub != null)
@@ -9,7 +9,7 @@ using Barotrauma.Networking;
namespace Barotrauma
{
internal partial class MedicalClinic
internal sealed partial class MedicalClinic
{
public enum NetworkHeader
{
@@ -18,7 +18,8 @@ namespace Barotrauma
ADD_PENDING,
REMOVE_PENDING,
CLEAR_PENDING,
HEAL_PENDING
HEAL_PENDING,
ADD_EVERYTHING_TO_PENDING
}
public enum AfflictionSeverity
@@ -43,23 +44,10 @@ namespace Barotrauma
}
[NetworkSerialize]
public struct NetHealRequest : INetSerializableStruct
{
public HealRequestResult Result;
}
public readonly record struct NetHealRequest(HealRequestResult Result) : INetSerializableStruct;
[NetworkSerialize]
public struct NetRemovedAffliction : INetSerializableStruct
{
public NetCrewMember CrewMember;
public NetAffliction Affliction;
}
public struct NetPendingCrew : INetSerializableStruct
{
[NetworkSerialize(ArrayMaxSize = CrewManager.MaxCrewSize)]
public NetCrewMember[] CrewMembers;
}
public readonly record struct NetRemovedAffliction(NetCrewMember CrewMember, NetAffliction Affliction) : INetSerializableStruct;
public struct NetAffliction : INetSerializableStruct
{
@@ -69,42 +57,18 @@ namespace Barotrauma
[NetworkSerialize]
public ushort Strength;
[NetworkSerialize]
public int VitalityDecrease;
[NetworkSerialize]
public ushort Price;
public AfflictionSeverity AfflictionSeverity
public void SetAffliction(Affliction affliction, CharacterHealth characterHealth)
{
get
{
if (Prefab is null) { return AfflictionSeverity.Low; }
float normalizedStrength = Strength / Prefab.MaxStrength;
// lesser than 0.1
if (normalizedStrength <= 0.1)
{
return AfflictionSeverity.Low;
}
// between 0.1 and 0.5
if (normalizedStrength > 0.1f && normalizedStrength < 0.5f)
{
return AfflictionSeverity.Medium;
}
// greater than 0.5
return AfflictionSeverity.High;
}
}
public Affliction Affliction
{
set
{
Identifier = value.Identifier;
Strength = (ushort)Math.Ceiling(value.Strength);
Price = (ushort)(value.Prefab.BaseHealCost + Strength * value.Prefab.HealCostMultiplier);
}
Identifier = affliction.Identifier;
Strength = (ushort)Math.Ceiling(affliction.Strength);
Price = (ushort)(affliction.Prefab.BaseHealCost + Strength * affliction.Prefab.HealCostMultiplier);
VitalityDecrease = (int)affliction.GetVitalityDecrease(characterHealth);
}
private AfflictionPrefab? cachedPrefab;
@@ -146,17 +110,23 @@ namespace Barotrauma
}
}
public struct NetCrewMember : INetSerializableStruct
public record struct NetCrewMember : INetSerializableStruct
{
[NetworkSerialize]
public int CharacterInfoID;
[NetworkSerialize]
public NetAffliction[] Afflictions;
public ImmutableArray<NetAffliction> Afflictions;
public CharacterInfo CharacterInfo
public NetCrewMember(CharacterInfo info)
{
set => CharacterInfoID = value.GetIdentifierUsingOriginalName();
CharacterInfoID = info.GetIdentifierUsingOriginalName();
Afflictions = ImmutableArray<NetAffliction>.Empty;
}
public NetCrewMember(CharacterInfo info, ImmutableArray<NetAffliction> afflictions): this(info)
{
Afflictions = afflictions;
}
public readonly CharacterInfo? FindCharacterInfo(ImmutableArray<CharacterInfo> crew)
@@ -194,11 +164,11 @@ namespace Barotrauma
private static bool IsOutpostInCombat()
{
if (!(Level.Loaded is { Type: LevelData.LevelType.Outpost })) { return false; }
if (Level.Loaded is not { Type: LevelData.LevelType.Outpost }) { return false; }
IEnumerable<Character> crew = GetCrewCharacters().Where(c => c.Character != null).Select(c => c.Character).ToImmutableHashSet();
IEnumerable<Character> crew = GetCrewCharacters().Where(static c => c.Character != null).Select(static c => c.Character).ToImmutableHashSet();
foreach (Character npc in Character.CharacterList.Where(c => c.TeamID == CharacterTeamType.FriendlyNPC))
foreach (Character npc in Character.CharacterList.Where(static c => c.TeamID == CharacterTeamType.FriendlyNPC))
{
bool isInCombatWithCrew = !npc.IsInstigator && npc.AIController is HumanAIController { ObjectiveManager: { CurrentObjective: AIObjectiveCombat combatObjective } } && crew.Contains(combatObjective.Enemy);
if (isInCombatWithCrew) { return true; }
@@ -238,6 +208,20 @@ namespace Barotrauma
PendingHeals.Clear();
}
private void AddEverythingToPending()
{
foreach (CharacterInfo info in GetCrewCharacters())
{
if (info.Character?.CharacterHealth is not { } health) { continue; }
var afflictions = GetAllAfflictions(health);
if (afflictions.Length is 0) { continue; }
InsertPendingCrewMember(new NetCrewMember(info, afflictions));
}
}
private void RemovePendingAffliction(NetCrewMember crewMember, NetAffliction affliction)
{
foreach (NetCrewMember listMember in PendingHeals.ToList())
@@ -255,7 +239,7 @@ namespace Barotrauma
newAfflictions.Add(pendingAffliction);
}
pendingMember.Afflictions = newAfflictions.ToArray();
pendingMember.Afflictions = newAfflictions.ToImmutableArray();
}
if (!pendingMember.Afflictions.Any()) { continue; }
@@ -280,9 +264,9 @@ namespace Barotrauma
static float GetShowTreshold(Affliction affliction) => Math.Max(0, Math.Min(affliction.Prefab.ShowIconToOthersThreshold, affliction.Prefab.ShowInHealthScannerThreshold));
}
private NetAffliction[] GetAllAfflictions(CharacterHealth health)
private ImmutableArray<NetAffliction> GetAllAfflictions(CharacterHealth health)
{
IEnumerable<Affliction> rawAfflictions = health.GetAllAfflictions().Where(a => IsHealable(a));
IEnumerable<Affliction> rawAfflictions = health.GetAllAfflictions().Where(IsHealable);
List<NetAffliction> afflictions = new List<NetAffliction>();
@@ -298,19 +282,20 @@ namespace Barotrauma
}
else
{
newAffliction = new NetAffliction { Affliction = affliction };
newAffliction = new NetAffliction();
newAffliction.SetAffliction(affliction, health);
newAffliction.Price = (ushort)GetAdjustedPrice(newAffliction.Price);
}
afflictions.Add(newAffliction);
}
return afflictions.ToArray();
return afflictions.ToImmutableArray();
static int GetHealPrice(Affliction affliction) => (int)(affliction.Prefab.BaseHealCost + (affliction.Prefab.HealCostMultiplier * affliction.Strength));
}
public int GetTotalCost() => PendingHeals.SelectMany(h => h.Afflictions).Aggregate(0, (current, affliction) => current + affliction.Price);
public int GetTotalCost() => PendingHeals.SelectMany(static h => h.Afflictions).Aggregate(0, static (current, affliction) => current + affliction.Price);
private int GetAdjustedPrice(int price) => campaign?.Map?.CurrentLocation is { Type: { HasOutpost: true } } currentLocation ? currentLocation.GetAdjustedHealCost(price) : int.MaxValue;
@@ -325,7 +310,7 @@ namespace Barotrauma
}
#endif
return Character.CharacterList.Where(c => c.Info != null && c.TeamID == CharacterTeamType.Team1).Select(c => c.Info).ToImmutableArray();
return Character.CharacterList.Where(static c => c.Info != null && c.TeamID == CharacterTeamType.Team1).Select(static c => c.Info).ToImmutableArray();
}
#if DEBUG && CLIENT
@@ -56,6 +56,10 @@ namespace Barotrauma
}
EndReadyCheck();
#if CLIENT
msgBox?.Close();
#endif
}
}
}
@@ -207,7 +207,7 @@ namespace Barotrauma
price = 0;
}
if (Campaign.TryPurchase(client, price))
if (force || Campaign.TryPurchase(client, price))
{
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
{
@@ -574,6 +574,7 @@ namespace Barotrauma
}
}
private readonly static HashSet<Submarine> upgradedSubs = new HashSet<Submarine>();
/// <summary>
/// Applies an upgrade on the submarine, should be called by <see cref="ApplyUpgrades"/> when the round starts.
/// </summary>
@@ -584,6 +585,12 @@ namespace Barotrauma
/// <returns>New level that was applied, -1 if no upgrades were applied.</returns>
private static int BuyUpgrade(UpgradePrefab prefab, UpgradeCategory category, Submarine submarine, int level = 1, Submarine? parentSub = null)
{
if (parentSub == null)
{
upgradedSubs.Clear();
}
upgradedSubs.Add(submarine);
int? newLevel = null;
if (category.IsWallUpgrade)
{
@@ -619,9 +626,12 @@ namespace Barotrauma
}
}
foreach (Submarine loadedSub in Submarine.Loaded.Where(sub => sub != submarine))
foreach (Submarine loadedSub in Submarine.Loaded)
{
if (loadedSub == parentSub) { continue; }
if (loadedSub == parentSub || loadedSub == submarine) { continue; }
if (loadedSub.Info?.Type != SubmarineType.Player) { continue; }
if (upgradedSubs.Contains(loadedSub)) { continue; }
XElement? root = loadedSub.Info?.SubmarineElement;
if (root == null) { continue; }
@@ -630,8 +640,8 @@ namespace Barotrauma
if (root.Attribute("location") == null) { continue; }
// Check if this is our linked submarine
ushort dockingPortID = (ushort) root.GetAttributeInt("originallinkedto", 0);
if (dockingPortID > 0 && submarine.GetItems(true).Any(item => item.ID == dockingPortID))
ushort dockingPortID = (ushort)root.GetAttributeInt("originallinkedto", 0);
if (dockingPortID > 0 && submarine.GetItems(alsoFromConnectedSubs: true).Any(item => item.ID == dockingPortID))
{
BuyUpgrade(prefab, category, loadedSub, level, submarine);
}