Merge remote-tracking branch 'upstream/dev' into develop
This commit is contained in:
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -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);
|
||||
}
|
||||
|
||||
+15
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user