Build 0.18.4.0

This commit is contained in:
Markus Isberg
2022-05-31 23:13:05 +09:00
parent 077917fa5d
commit 64db1a6a44
175 changed files with 4916 additions and 2393 deletions
@@ -10,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]
@@ -149,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
@@ -189,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);
@@ -596,6 +542,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
@@ -729,7 +676,6 @@ namespace Barotrauma
}
}
public void EndCampaign()
{
foreach (Character c in Character.CharacterList)
@@ -741,7 +687,7 @@ namespace Barotrauma
}
foreach (LocationConnection connection in Map.Connections)
{
connection.Difficulty = MathHelper.Lerp(connection.Difficulty, 100.0f, 0.25f);
connection.Difficulty = connection.Biome.MaxDifficulty;
connection.LevelData = new LevelData(connection)
{
IsBeaconActive = false
@@ -750,6 +696,7 @@ namespace Barotrauma
}
foreach (Location location in Map.Locations)
{
location.LevelData = new LevelData(location, location.Biome.MaxDifficulty);
location.Reset();
}
Map.SetLocation(Map.Locations.IndexOf(Map.StartLocation));
@@ -873,7 +820,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();
@@ -1058,7 +1005,10 @@ namespace Barotrauma
public SubmarineInfo SwitchSubs()
{
TransferItemsBetweenSubs();
if (TransferItemsOnSubSwitch)
{
TransferItemsBetweenSubs();
}
RefreshOwnedSubmarines();
PendingSubmarineSwitch = null;
return GameMain.GameSession.SubmarineInfo;
@@ -0,0 +1,82 @@
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)
{
return values.TryGetValue(identifier, out Either<int, float> value) && value.TryGet(out float range) ? range : 0.0f;
}
public int GetInt(Identifier identifier)
{
return values.TryGetValue(identifier, out Either<int, float> value) && value.TryGet(out int integer) ? integer : 0;
}
}
}
@@ -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)
@@ -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
{