Merge branch 'master' of https://github.com/Regalis11/Barotrauma.git
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Networking;
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
@@ -9,63 +10,6 @@ using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal struct CampaignSettings
|
||||
{
|
||||
public static CampaignSettings Empty => new CampaignSettings();
|
||||
|
||||
// Anything that uses this field I wasn't sure if actually needed the proper campaign settings to be passed down
|
||||
public static CampaignSettings Unsure => Empty;
|
||||
public bool RadiationEnabled { get; set; }
|
||||
|
||||
public int TotalMaxMissionCount => MaxMissionCount + GetAddedMissionCount();
|
||||
|
||||
private int maxMissionCount;
|
||||
public int MaxMissionCount
|
||||
{
|
||||
get { return maxMissionCount; }
|
||||
set { maxMissionCount = MathHelper.Clamp(value, MinMissionCountLimit, MaxMissionCountLimit); }
|
||||
}
|
||||
|
||||
public const int DefaultMaxMissionCount = 2;
|
||||
public const int MaxMissionCountLimit = 10;
|
||||
public const int MinMissionCountLimit = 1;
|
||||
|
||||
public CampaignSettings(IReadMessage inc)
|
||||
{
|
||||
maxMissionCount = DefaultMaxMissionCount;
|
||||
RadiationEnabled = inc.ReadBoolean();
|
||||
MaxMissionCount = inc.ReadRangedInteger(MinMissionCountLimit, MaxMissionCountLimit);
|
||||
}
|
||||
|
||||
public CampaignSettings(XElement element)
|
||||
{
|
||||
maxMissionCount = DefaultMaxMissionCount;
|
||||
RadiationEnabled = element.GetAttributeBool(nameof(RadiationEnabled).ToLowerInvariant(), true);
|
||||
MaxMissionCount = element.GetAttributeInt(nameof(MaxMissionCount).ToLowerInvariant(), DefaultMaxMissionCount);
|
||||
}
|
||||
|
||||
public void Serialize(IWriteMessage msg)
|
||||
{
|
||||
msg.Write(RadiationEnabled);
|
||||
msg.WriteRangedInteger(MaxMissionCount, MinMissionCountLimit, MaxMissionCountLimit);
|
||||
}
|
||||
|
||||
public int GetAddedMissionCount()
|
||||
{
|
||||
int count = 0;
|
||||
foreach (Character character in GameSession.GetSessionCrewCharacters(CharacterType.Both))
|
||||
{
|
||||
count += (int)character.GetStatValue(StatTypes.ExtraMissionCount);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public XElement Save()
|
||||
{
|
||||
return new XElement(nameof(CampaignSettings), new XAttribute(nameof(RadiationEnabled).ToLowerInvariant(), RadiationEnabled), new XAttribute(nameof(MaxMissionCount).ToLowerInvariant(), MaxMissionCount));
|
||||
}
|
||||
}
|
||||
|
||||
abstract partial class CampaignMode : GameMode
|
||||
{
|
||||
[NetworkSerialize]
|
||||
@@ -107,6 +51,8 @@ namespace Barotrauma
|
||||
|
||||
protected XElement petsElement;
|
||||
|
||||
protected XElement ActiveOrdersElement { get; set; }
|
||||
|
||||
public CampaignSettings Settings;
|
||||
|
||||
private readonly List<Mission> extraMissions = new List<Mission>();
|
||||
@@ -146,9 +92,8 @@ namespace Barotrauma
|
||||
//key = dialog flag, double = Timing.TotalTime when the line was last said
|
||||
private readonly Dictionary<string, double> dialogLastSpoken = new Dictionary<string, double>();
|
||||
|
||||
public bool PurchasedHullRepairs, PurchasedLostShuttles, PurchasedItemRepairs;
|
||||
|
||||
public SubmarineInfo PendingSubmarineSwitch;
|
||||
public bool TransferItemsOnSubSwitch { get; set; }
|
||||
|
||||
protected Map map;
|
||||
public Map Map
|
||||
@@ -186,12 +131,16 @@ namespace Barotrauma
|
||||
protected set;
|
||||
}
|
||||
|
||||
protected CampaignMode(GameModePreset preset)
|
||||
public virtual bool PurchasedHullRepairs { get; set; }
|
||||
public virtual bool PurchasedLostShuttles { get; set; }
|
||||
public virtual bool PurchasedItemRepairs { get; set; }
|
||||
|
||||
protected CampaignMode(GameModePreset preset, CampaignSettings settings)
|
||||
: base(preset)
|
||||
{
|
||||
Bank = new Wallet(Option<Character>.None())
|
||||
{
|
||||
Balance = InitialMoney
|
||||
Balance = settings.InitialMoney
|
||||
};
|
||||
|
||||
CargoManager = new CargoManager(this);
|
||||
@@ -558,6 +507,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public TransitionType GetAvailableTransition() => GetAvailableTransition(out _, out _);
|
||||
|
||||
/// <summary>
|
||||
/// Which submarine is at a position where it can leave the level and enter another one (if any).
|
||||
/// </summary>
|
||||
@@ -593,6 +544,7 @@ namespace Barotrauma
|
||||
if (Level.Loaded.StartOutpost == null)
|
||||
{
|
||||
Submarine closestSub = Submarine.FindClosest(Level.Loaded.StartExitPosition, ignoreOutposts: true, ignoreRespawnShuttle: true, teamType: leavingPlayers.FirstOrDefault()?.TeamID);
|
||||
if (closestSub == null) { return null; }
|
||||
return closestSub.DockedTo.Contains(Submarine.MainSub) ? Submarine.MainSub : closestSub;
|
||||
}
|
||||
else
|
||||
@@ -726,7 +678,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void EndCampaign()
|
||||
{
|
||||
foreach (Character c in Character.CharacterList)
|
||||
@@ -738,13 +689,16 @@ namespace Barotrauma
|
||||
}
|
||||
foreach (LocationConnection connection in Map.Connections)
|
||||
{
|
||||
connection.Difficulty = MathHelper.Lerp(connection.Difficulty, 100.0f, 0.25f);
|
||||
connection.LevelData.Difficulty = connection.Difficulty;
|
||||
connection.LevelData.IsBeaconActive = false;
|
||||
connection.Difficulty = connection.Biome.MaxDifficulty;
|
||||
connection.LevelData = new LevelData(connection)
|
||||
{
|
||||
IsBeaconActive = false
|
||||
};
|
||||
connection.LevelData.HasHuntingGrounds = connection.LevelData.OriginallyHadHuntingGrounds;
|
||||
}
|
||||
foreach (Location location in Map.Locations)
|
||||
{
|
||||
location.LevelData = new LevelData(location, location.Biome.MaxDifficulty);
|
||||
location.Reset();
|
||||
}
|
||||
Map.SetLocation(Map.Locations.IndexOf(Map.StartLocation));
|
||||
@@ -868,7 +822,7 @@ namespace Barotrauma
|
||||
const float MaxDist = 3000.0f;
|
||||
const float MinDist = 2500.0f;
|
||||
|
||||
if (!Level.IsLoadedOutpost) { return; }
|
||||
if (!Level.IsLoadedFriendlyOutpost) { return; }
|
||||
|
||||
Rectangle worldBorders = Submarine.MainSub.GetDockedBorders();
|
||||
worldBorders.Location += Submarine.MainSub.WorldPosition.ToPoint();
|
||||
@@ -1032,5 +986,190 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
protected void LeaveUnconnectedSubs(Submarine leavingSub)
|
||||
{
|
||||
if (leavingSub != Submarine.MainSub && !leavingSub.DockedTo.Contains(Submarine.MainSub))
|
||||
{
|
||||
Submarine.MainSub = leavingSub;
|
||||
GameMain.GameSession.Submarine = leavingSub;
|
||||
GameMain.GameSession.SubmarineInfo = leavingSub.Info;
|
||||
leavingSub.Info.FilePath = System.IO.Path.Combine(SaveUtil.TempPath, leavingSub.Info.Name + ".sub");
|
||||
var subsToLeaveBehind = GetSubsToLeaveBehind(leavingSub);
|
||||
GameMain.GameSession.OwnedSubmarines.Add(leavingSub.Info);
|
||||
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);
|
||||
LinkedSubmarine.CreateDummy(leavingSub, sub);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SubmarineInfo SwitchSubs()
|
||||
{
|
||||
if (TransferItemsOnSubSwitch)
|
||||
{
|
||||
TransferItemsBetweenSubs();
|
||||
}
|
||||
RefreshOwnedSubmarines();
|
||||
PendingSubmarineSwitch = null;
|
||||
return GameMain.GameSession.SubmarineInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Also serializes the current sub.
|
||||
/// </summary>
|
||||
protected void TransferItemsBetweenSubs()
|
||||
{
|
||||
Submarine currentSub = GameMain.GameSession.Submarine;
|
||||
if (currentSub == null || currentSub.Removed)
|
||||
{
|
||||
DebugConsole.ThrowError("Cannot transfer items between subs, because the current sub is null or removed!");
|
||||
return;
|
||||
}
|
||||
var itemsToTransfer = new List<(Item item, Item container)>();
|
||||
if (PendingSubmarineSwitch != null)
|
||||
{
|
||||
var connectedSubs = currentSub.GetConnectedSubs().Where(s => s.Info.Type == SubmarineType.Player).ToHashSet();
|
||||
// Remove items from the old sub
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
if (item.Removed) { continue; }
|
||||
if (item.NonInteractable) { continue; }
|
||||
if (item.HiddenInGame) { continue; }
|
||||
if (!connectedSubs.Contains(item.Submarine)) { continue; }
|
||||
if (item.Prefab.DontTransferBetweenSubs) { continue; }
|
||||
if (item.GetRootInventoryOwner() is Character) { continue; }
|
||||
if (item.GetComponent<Holdable>() == null && item.GetComponent<Wearable>() == null && item.GetComponent<Projectile>() == null) { continue; }
|
||||
if (item.Components.Any(c => c is Holdable h && h.Attached)) { continue; }
|
||||
if (item.Components.Any(c => c is Wire w && w.Connections.Any(c => c != null))) { continue; }
|
||||
itemsToTransfer.Add((item, item.Container));
|
||||
item.Submarine = null;
|
||||
}
|
||||
foreach (var (item, container) in itemsToTransfer)
|
||||
{
|
||||
if (container?.Submarine != null)
|
||||
{
|
||||
// Drop the item if it's not inside another item set to be transferred.
|
||||
item.Drop(null, createNetworkEvent: false, setTransform: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Serialize the current sub
|
||||
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(currentSub);
|
||||
if (PendingSubmarineSwitch != null && itemsToTransfer.Any())
|
||||
{
|
||||
// Load the new sub
|
||||
var newSub = new Submarine(PendingSubmarineSwitch);
|
||||
var connectedSubs = newSub.GetConnectedSubs().Where(s => s.Info.Type == SubmarineType.Player).ToHashSet();
|
||||
// Move the transferred items
|
||||
List<ItemContainer> availableContainers = Item.ItemList
|
||||
.Where(it => connectedSubs.Contains(it.Submarine) && it.HasTag("crate") && !it.NonInteractable && !it.HiddenInGame && !it.Removed)
|
||||
.Select(it => it.GetComponent<ItemContainer>())
|
||||
.Where(c => c != null)
|
||||
.ToList();
|
||||
foreach (var (item, oldContainer) in itemsToTransfer)
|
||||
{
|
||||
Item newContainer = null;
|
||||
item.Submarine = newSub;
|
||||
if (item.Container == null)
|
||||
{
|
||||
newContainer = newSub.FindContainerFor(item, onlyPrimary: true, checkTransferConditions: true, allowConnectedSubs: true);
|
||||
}
|
||||
if (item.Container == null && (newContainer == null || !newContainer.OwnInventory.TryPutItem(item, user: null, createNetworkEvent: false)))
|
||||
{
|
||||
WayPoint wp = WayPoint.GetRandom(SpawnType.Cargo, null, newSub);
|
||||
Hull spawnHull = wp?.CurrentHull ?? Hull.HullList.Where(h => h.Submarine == newSub && !h.IsWetRoom).GetRandomUnsynced();
|
||||
if (spawnHull == null)
|
||||
{
|
||||
DebugConsole.AddWarning($"Failed to transfer items between subs. No cargo waypoint or dry hulls found in the new sub.");
|
||||
return;
|
||||
}
|
||||
if (spawnHull != null)
|
||||
{
|
||||
var cargoContainer = CargoManager.GetOrCreateCargoContainerFor(item.Prefab, spawnHull, ref availableContainers);
|
||||
if (cargoContainer == null || !cargoContainer.Inventory.TryPutItem(item, user: null, createNetworkEvent: false))
|
||||
{
|
||||
Vector2 simPos = ConvertUnits.ToSimUnits(CargoManager.GetCargoPos(spawnHull, item.Prefab));
|
||||
item.SetTransform(simPos, 0.0f, findNewHull: false, setPrevTransform: false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.AddWarning($"Failed to transfer item {item.Prefab.Identifier} ({item.ID}), because no cargo spawn point could be found!");
|
||||
}
|
||||
}
|
||||
string newContainerName = newContainer == null ? "(null)" : $"{newContainer.Prefab.Identifier} ({newContainer.Tags})";
|
||||
string msg = "Item transfer log error.";
|
||||
if (oldContainer != null)
|
||||
{
|
||||
if (newContainer == null && oldContainer == item.Container)
|
||||
{
|
||||
msg = $"Transferred {item.Prefab.Identifier} ({item.ID}) contained inside {oldContainer.Prefab.Identifier} ({oldContainer.ID})";
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = $"Transferred {item.Prefab.Identifier} ({item.ID}) from {oldContainer.Prefab.Identifier} ({oldContainer.Tags}) to {newContainerName}";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = $"Transferred {item.Prefab.Identifier} ({item.ID}) to {newContainerName}";
|
||||
}
|
||||
#if DEBUG
|
||||
DebugConsole.NewMessage(msg);
|
||||
#else
|
||||
DebugConsole.Log(msg);
|
||||
#endif
|
||||
}
|
||||
// Serialize the new sub
|
||||
PendingSubmarineSwitch = new SubmarineInfo(newSub);
|
||||
}
|
||||
}
|
||||
|
||||
protected void RefreshOwnedSubmarines()
|
||||
{
|
||||
if (PendingSubmarineSwitch != null)
|
||||
{
|
||||
SubmarineInfo previousSub = GameMain.GameSession.SubmarineInfo;
|
||||
GameMain.GameSession.SubmarineInfo = PendingSubmarineSwitch;
|
||||
|
||||
for (int i = 0; i < GameMain.GameSession.OwnedSubmarines.Count; i++)
|
||||
{
|
||||
if (GameMain.GameSession.OwnedSubmarines[i].Name == previousSub.Name)
|
||||
{
|
||||
GameMain.GameSession.OwnedSubmarines[i] = previousSub;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SavePets(XElement parentElement = null)
|
||||
{
|
||||
petsElement = new XElement("pets");
|
||||
PetBehavior.SavePets(petsElement);
|
||||
parentElement?.Add(petsElement);
|
||||
}
|
||||
|
||||
public void LoadPets()
|
||||
{
|
||||
if (petsElement != null)
|
||||
{
|
||||
PetBehavior.LoadPets(petsElement);
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveActiveOrders(XElement parentElement = null)
|
||||
{
|
||||
ActiveOrdersElement = new XElement("activeorders");
|
||||
CrewManager?.SaveActiveOrders(ActiveOrdersElement);
|
||||
parentElement?.Add(ActiveOrdersElement);
|
||||
}
|
||||
|
||||
public void LoadActiveOrders()
|
||||
{
|
||||
CrewManager?.LoadActiveOrders(ActiveOrdersElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal static class CampaignModePresets
|
||||
{
|
||||
public static readonly ImmutableArray<CampaignSettings> List;
|
||||
public static readonly ImmutableDictionary<Identifier, CampaignSettingDefinitions> Definitions;
|
||||
|
||||
private static readonly string fileListPath = Path.Combine("Data", "campaignsettings.xml");
|
||||
|
||||
static CampaignModePresets()
|
||||
{
|
||||
if (!File.Exists(fileListPath) || !(XMLExtensions.TryLoadXml(fileListPath)?.Root is { } docRoot))
|
||||
{
|
||||
List = ImmutableArray<CampaignSettings>.Empty;
|
||||
return;
|
||||
}
|
||||
|
||||
List<CampaignSettings> list = new List<CampaignSettings>();
|
||||
Dictionary<Identifier, CampaignSettingDefinitions> definitions = new Dictionary<Identifier, CampaignSettingDefinitions>();
|
||||
|
||||
foreach (XElement element in docRoot.Elements())
|
||||
{
|
||||
Identifier name = element.NameAsIdentifier();
|
||||
|
||||
if (name == CampaignSettings.LowerCaseSaveElementName)
|
||||
{
|
||||
list.Add(new CampaignSettings(element));
|
||||
}
|
||||
else if (name == nameof(CampaignSettingDefinitions))
|
||||
{
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
definitions.Add(subElement.NameAsIdentifier(), new CampaignSettingDefinitions(subElement));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List = list.ToImmutableArray();
|
||||
Definitions = definitions.ToImmutableDictionary();
|
||||
}
|
||||
}
|
||||
|
||||
internal readonly struct CampaignSettingDefinitions
|
||||
{
|
||||
// Definitely not the best way to do this
|
||||
private readonly ImmutableDictionary<Identifier, Either<int, float>> values;
|
||||
|
||||
public CampaignSettingDefinitions(XElement element)
|
||||
{
|
||||
var definitions = new Dictionary<Identifier, Either<int, float>>();
|
||||
foreach (XAttribute attribute in element.Attributes())
|
||||
{
|
||||
Identifier name = attribute.NameAsIdentifier();
|
||||
if (attribute.Value.Contains('.'))
|
||||
{
|
||||
definitions.Add(name, element.GetAttributeFloat(name.Value, 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
definitions.Add(name, element.GetAttributeInt(name.Value, 0));
|
||||
}
|
||||
}
|
||||
|
||||
values = definitions.ToImmutableDictionary();
|
||||
}
|
||||
|
||||
public float GetFloat(Identifier identifier)
|
||||
{
|
||||
float range = 0;
|
||||
if (!values.TryGetValue(identifier, out Either<int, float> value) || !value.TryGet(out range))
|
||||
{
|
||||
DebugConsole.ThrowError($"CampaignSettings: Can't find value for {identifier}");
|
||||
}
|
||||
return range;
|
||||
}
|
||||
|
||||
public int GetInt(Identifier identifier)
|
||||
{
|
||||
int integer = 0;
|
||||
if (!values.TryGetValue(identifier, out Either<int, float> value) || !value.TryGet(out integer))
|
||||
{
|
||||
DebugConsole.ThrowError($"CampaignSettings: Can't find value for {identifier}");
|
||||
}
|
||||
return integer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
#nullable enable
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal class CampaignSettings : INetSerializableStruct, ISerializableEntity
|
||||
{
|
||||
public static CampaignSettings Empty => new CampaignSettings(element: null);
|
||||
|
||||
public string Name => "CampaignSettings";
|
||||
|
||||
public const string LowerCaseSaveElementName = "campaignsettings";
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public string PresetName { get; set; } = string.Empty;
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes), NetworkSerialize]
|
||||
public bool RadiationEnabled { get; set; }
|
||||
|
||||
private int maxMissionCount;
|
||||
|
||||
[Serialize(DefaultMaxMissionCount, IsPropertySaveable.Yes), NetworkSerialize(MinValueInt = MinMissionCountLimit, MaxValueInt = MaxMissionCountLimit)]
|
||||
public int MaxMissionCount
|
||||
{
|
||||
get => maxMissionCount;
|
||||
set => maxMissionCount = MathHelper.Clamp(value, MinMissionCountLimit, MaxMissionCountLimit);
|
||||
}
|
||||
|
||||
public int TotalMaxMissionCount => MaxMissionCount + GetAddedMissionCount();
|
||||
|
||||
[Serialize(StartingBalanceAmount.Medium, IsPropertySaveable.Yes), NetworkSerialize]
|
||||
public StartingBalanceAmount StartingBalanceAmount { get; set; }
|
||||
|
||||
[Serialize(GameDifficulty.Medium, IsPropertySaveable.Yes), NetworkSerialize]
|
||||
public GameDifficulty Difficulty { get; set; }
|
||||
|
||||
[Serialize("normal", IsPropertySaveable.Yes), NetworkSerialize]
|
||||
public Identifier StartItemSet { get; set; }
|
||||
|
||||
public int InitialMoney
|
||||
{
|
||||
get
|
||||
{
|
||||
if (CampaignModePresets.Definitions.TryGetValue(nameof(StartingBalanceAmount).ToIdentifier(), out var definition))
|
||||
{
|
||||
return definition.GetInt(StartingBalanceAmount.ToIdentifier());
|
||||
}
|
||||
return 8000;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public float ExtraEventManagerDifficulty
|
||||
{
|
||||
get
|
||||
{
|
||||
if (CampaignModePresets.Definitions.TryGetValue(nameof(ExtraEventManagerDifficulty).ToIdentifier(), out var definition))
|
||||
{
|
||||
return definition.GetFloat(Difficulty.ToIdentifier());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public float LevelDifficultyMultiplier
|
||||
{
|
||||
get
|
||||
{
|
||||
if (CampaignModePresets.Definitions.TryGetValue(nameof(LevelDifficultyMultiplier).ToIdentifier(), out var definition))
|
||||
{
|
||||
return definition.GetFloat(Difficulty.ToIdentifier());
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
public const int DefaultMaxMissionCount = 2;
|
||||
public const int MaxMissionCountLimit = 10;
|
||||
public const int MinMissionCountLimit = 1;
|
||||
|
||||
public Dictionary<Identifier, SerializableProperty> SerializableProperties { get; private set; }
|
||||
|
||||
// required for INetSerializableStruct
|
||||
public CampaignSettings()
|
||||
{
|
||||
SerializableProperties = SerializableProperty.GetProperties(this);
|
||||
}
|
||||
|
||||
public CampaignSettings(XElement? element = null)
|
||||
{
|
||||
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
|
||||
}
|
||||
|
||||
public XElement Save()
|
||||
{
|
||||
XElement saveElement = new XElement(LowerCaseSaveElementName);
|
||||
SerializableProperty.SerializeProperties(this, saveElement, saveIfDefault: true);
|
||||
return saveElement;
|
||||
}
|
||||
|
||||
private static int GetAddedMissionCount()
|
||||
{
|
||||
int count = 0;
|
||||
foreach (Character character in GameSession.GetSessionCrewCharacters(CharacterType.Both))
|
||||
{
|
||||
count += (int)character.GetStatValue(StatTypes.ExtraMissionCount);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,11 @@ namespace Barotrauma
|
||||
: base(preset)
|
||||
{
|
||||
Location[] locations = { GameMain.GameSession.StartLocation, GameMain.GameSession.EndLocation };
|
||||
missions.Add(Mission.LoadRandom(locations, seed, false, missionType));
|
||||
var mission = Mission.LoadRandom(locations, seed, false, missionType);
|
||||
if (mission != null)
|
||||
{
|
||||
missions.Add(mission);
|
||||
}
|
||||
}
|
||||
|
||||
protected static IEnumerable<MissionPrefab> ValidateMissionPrefabs(IEnumerable<MissionPrefab> missionPrefabs, Dictionary<MissionType, Type> missionClasses)
|
||||
|
||||
+72
-22
@@ -12,19 +12,60 @@ namespace Barotrauma
|
||||
{
|
||||
public const int MinimumInitialMoney = 500;
|
||||
|
||||
private UInt16 lastUpdateID;
|
||||
public UInt16 LastUpdateID
|
||||
[Flags]
|
||||
public enum NetFlags : UInt16
|
||||
{
|
||||
get
|
||||
{
|
||||
#if SERVER
|
||||
if (GameMain.Server != null && lastUpdateID < 1) { lastUpdateID++; }
|
||||
#endif
|
||||
return lastUpdateID;
|
||||
}
|
||||
set { lastUpdateID = value; }
|
||||
Misc = 0x1,
|
||||
MapAndMissions = 0x2,
|
||||
UpgradeManager = 0x4,
|
||||
SubList = 0x8,
|
||||
ItemsInBuyCrate = 0x10,
|
||||
ItemsInSellFromSubCrate = 0x20,
|
||||
PurchasedItems = 0x80,
|
||||
SoldItems = 0x100,
|
||||
Reputation = 0x200,
|
||||
CharacterInfo = 0x800
|
||||
}
|
||||
|
||||
private readonly Dictionary<NetFlags, UInt16> lastUpdateID;
|
||||
|
||||
public UInt16 GetLastUpdateIdForFlag(NetFlags flag)
|
||||
{
|
||||
if (!ValidateFlag(flag)) { return 0; }
|
||||
return lastUpdateID[flag];
|
||||
}
|
||||
public void SetLastUpdateIdForFlag(NetFlags flag, UInt16 id)
|
||||
{
|
||||
if (!ValidateFlag(flag)) { return; }
|
||||
lastUpdateID[flag] = id;
|
||||
}
|
||||
|
||||
public void IncrementLastUpdateIdForFlag(NetFlags flag)
|
||||
{
|
||||
if (!ValidateFlag(flag)) { return; }
|
||||
if (!lastUpdateID.ContainsKey(flag)) { lastUpdateID[flag] = 0; }
|
||||
lastUpdateID[flag]++;
|
||||
}
|
||||
public void IncrementAllLastUpdateIds()
|
||||
{
|
||||
foreach (NetFlags flag in Enum.GetValues(typeof(NetFlags)))
|
||||
{
|
||||
if (!lastUpdateID.ContainsKey(flag)) { lastUpdateID[flag] = 0; }
|
||||
lastUpdateID[flag]++;
|
||||
}
|
||||
}
|
||||
|
||||
private bool ValidateFlag(NetFlags flag)
|
||||
{
|
||||
if (MathHelper.IsPowerOfTwo((int)flag)) { return true; }
|
||||
#if DEBUG
|
||||
throw new InvalidOperationException($"\"{flag}\" is not a valid campaign update flag.");
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
private UInt16 lastSaveID;
|
||||
public UInt16 LastSaveID
|
||||
{
|
||||
@@ -35,11 +76,11 @@ namespace Barotrauma
|
||||
#endif
|
||||
return lastSaveID;
|
||||
}
|
||||
set
|
||||
set
|
||||
{
|
||||
#if SERVER
|
||||
//trigger a campaign update to notify the clients of the changed save ID
|
||||
lastUpdateID++;
|
||||
IncrementLastUpdateIdForFlag(NetFlags.Misc);
|
||||
#endif
|
||||
lastSaveID = value;
|
||||
}
|
||||
@@ -52,23 +93,33 @@ namespace Barotrauma
|
||||
get; set;
|
||||
}
|
||||
|
||||
private MultiPlayerCampaign() : base(GameModePreset.MultiPlayerCampaign)
|
||||
private MultiPlayerCampaign(CampaignSettings settings) : base(GameModePreset.MultiPlayerCampaign, settings)
|
||||
{
|
||||
currentCampaignID++;
|
||||
lastUpdateID = new Dictionary<NetFlags, ushort>();
|
||||
foreach (NetFlags flag in Enum.GetValues(typeof(NetFlags)))
|
||||
{
|
||||
#if SERVER
|
||||
//server starts from a higher ID to ensure we send the initial state
|
||||
lastUpdateID[flag] = 1;
|
||||
#else
|
||||
lastUpdateID[flag] = 0;
|
||||
#endif
|
||||
}
|
||||
CampaignID = currentCampaignID;
|
||||
CampaignMetadata = new CampaignMetadata(this);
|
||||
UpgradeManager = new UpgradeManager(this);
|
||||
InitCampaignData();
|
||||
}
|
||||
|
||||
public static MultiPlayerCampaign StartNew(string mapSeed, SubmarineInfo selectedSub, CampaignSettings settings)
|
||||
public static MultiPlayerCampaign StartNew(string mapSeed, CampaignSettings settings)
|
||||
{
|
||||
MultiPlayerCampaign campaign = new MultiPlayerCampaign();
|
||||
MultiPlayerCampaign campaign = new MultiPlayerCampaign(settings);
|
||||
//only the server generates the map, the clients load it from a save file
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
campaign.map = new Map(campaign, mapSeed, settings);
|
||||
campaign.Settings = settings;
|
||||
campaign.map = new Map(campaign, mapSeed);
|
||||
}
|
||||
campaign.InitProjSpecific();
|
||||
return campaign;
|
||||
@@ -76,7 +127,7 @@ namespace Barotrauma
|
||||
|
||||
public static MultiPlayerCampaign LoadNew(XElement element)
|
||||
{
|
||||
MultiPlayerCampaign campaign = new MultiPlayerCampaign();
|
||||
MultiPlayerCampaign campaign = new MultiPlayerCampaign(CampaignSettings.Empty);
|
||||
campaign.Load(element);
|
||||
campaign.InitProjSpecific();
|
||||
campaign.IsFirstRound = false;
|
||||
@@ -124,18 +175,17 @@ namespace Barotrauma
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "campaignsettings":
|
||||
case CampaignSettings.LowerCaseSaveElementName:
|
||||
Settings = new CampaignSettings(subElement);
|
||||
#if CLIENT
|
||||
GameMain.NetworkMember.ServerSettings.MaxMissionCount = Settings.MaxMissionCount;
|
||||
GameMain.NetworkMember.ServerSettings.RadiationEnabled = Settings.RadiationEnabled;
|
||||
GameMain.NetworkMember.ServerSettings.CampaignSettings = Settings;
|
||||
#endif
|
||||
break;
|
||||
case "map":
|
||||
if (map == null)
|
||||
{
|
||||
//map not created yet, loading this campaign for the first time
|
||||
map = Map.Load(this, subElement, Settings);
|
||||
map = Map.Load(this, subElement);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -155,7 +205,7 @@ namespace Barotrauma
|
||||
case "bots" when GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer:
|
||||
CrewManager.HasBots = subElement.GetAttributeBool("hasbots", false);
|
||||
CrewManager.AddCharacterElements(subElement);
|
||||
CrewManager.ActiveOrdersElement = subElement.GetChildElement("activeorders");
|
||||
ActiveOrdersElement = subElement.GetChildElement("activeorders");
|
||||
break;
|
||||
case "cargo":
|
||||
CargoManager?.LoadPurchasedItems(subElement);
|
||||
|
||||
Reference in New Issue
Block a user