Build 0.18.7.0

This commit is contained in:
Markus Isberg
2022-06-08 21:52:38 +09:00
parent 5a10b444ee
commit 4f5a3bf8b9
56 changed files with 401 additions and 245 deletions
@@ -227,9 +227,13 @@ namespace Barotrauma
{
mainLimb = Limbs.FirstOrDefault(l => IsValid(l));
}
if (mainLimb == null)
{
DebugConsole.ThrowError("Couldn't find a valid main limb. The limb can't be hidden nor be set to ignore collisions!");
mainLimb = Limbs.FirstOrDefault();
}
}
bool IsValid(Limb limb) => limb != null && !limb.IsSevered && !limb.IgnoreCollisions && !limb.Hidden;
static bool IsValid(Limb limb) => limb != null && !limb.IsSevered && !limb.IgnoreCollisions && !limb.Hidden;
return mainLimb;
}
}
@@ -49,6 +49,9 @@ namespace Barotrauma
[Serialize("", IsPropertySaveable.Yes)]
public Identifier SpawnPointTag { get; set; }
[Serialize(CharacterTeamType.FriendlyNPC, IsPropertySaveable.Yes)]
public CharacterTeamType Team { get; protected set; }
[Serialize(false, IsPropertySaveable.Yes, description: "Should we spawn the entity even when no spawn points with matching tags were found?")]
public bool RequireSpawnPointTag { get; set; }
@@ -119,7 +122,7 @@ namespace Barotrauma
{
if (newCharacter == null) { return; }
newCharacter.HumanPrefab = humanPrefab;
newCharacter.TeamID = CharacterTeamType.FriendlyNPC;
newCharacter.TeamID = Team;
newCharacter.EnableDespawn = false;
humanPrefab.GiveItems(newCharacter, newCharacter.Submarine);
if (LootingIsStealing)
@@ -13,12 +13,26 @@ namespace Barotrauma
internal partial class ReadyCheck
{
private readonly float endTime;
private float time;
private readonly DateTime endTime;
private readonly DateTime startTime;
public readonly Dictionary<byte, ReadyStatus> Clients;
public bool IsFinished = false;
public ReadyCheck(List<byte> clients, float duration = 30)
public ReadyCheck(List<byte> clients, DateTime startTime, DateTime endTime)
: this(clients)
{
this.startTime = startTime;
this.endTime = endTime;
}
public ReadyCheck(List<byte> clients, float duration)
: this(clients)
{
startTime = DateTime.Now;
endTime = startTime + new TimeSpan(0, 0, 0, 0, (int)(duration * 1000));
}
private ReadyCheck(List<byte> clients)
{
Clients = new Dictionary<byte, ReadyStatus>();
foreach (byte client in clients)
@@ -27,24 +41,17 @@ namespace Barotrauma
Clients.Add(client, ReadyStatus.Unanswered);
}
time = duration;
endTime = duration;
#if CLIENT
lastSecond = (int) Math.Ceiling(duration);
#endif
}
partial void EndReadyCheck();
public void Update(float deltaTime)
{
if (time > 0)
if (DateTime.Now < endTime)
{
#if CLIENT
UpdateBar();
#endif
time -= deltaTime;
return;
}
@@ -1,10 +1,9 @@
using Barotrauma.Networking;
using Barotrauma.MapCreatures.Behavior;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.MapCreatures.Behavior;
namespace Barotrauma.Items.Components
{
@@ -24,7 +23,10 @@ namespace Barotrauma.Items.Components
if (value == hijacked) { return; }
hijacked = value;
#if SERVER
item.CreateServerEvent(this);
if (!Submarine.Unloading)
{
item.CreateServerEvent(this);
}
#endif
}
}
@@ -68,7 +68,7 @@ namespace Barotrauma.Items.Components
get { return poweredList; }
}
public static readonly List<Connection> ChangedConnections = new List<Connection>();
public static readonly HashSet<Connection> ChangedConnections = new HashSet<Connection>();
public readonly static Dictionary<int, GridInfo> Grids = new Dictionary<int, GridInfo>();
@@ -158,6 +158,12 @@ namespace Barotrauma.Items.Components
}
}
/// <summary>
/// Essentially Voltage / MinVoltage (= how much of the minimum required voltage has been satisfied), clamped between 0 and 1.
/// Can be used by status effects or sounds to check if the item has enough power to run
/// </summary>
public float RelativeVoltage => minVoltage <= 0.0f ? 1.0f : MathHelper.Clamp(Voltage / minVoltage, 0.0f, 1.0f);
public bool PoweredByTinkering { get; set; }
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "Can the item be damaged by electomagnetic pulses.")]
@@ -170,6 +170,7 @@ namespace Barotrauma.Items.Components
public void SetRecipientsDirty()
{
recipientsDirty = true;
if (IsPower) { Powered.ChangedConnections.Add(this); }
}
private void RefreshRecipients()
@@ -399,6 +399,7 @@ namespace Barotrauma.MapCreatures.Behavior
new XAttribute("pos", XMLExtensions.Vector2ToString(branch.Position)),
new XAttribute("ID", branch.ID),
new XAttribute("isroot", branch.IsRoot),
new XAttribute("isrootgrowth", branch.IsRootGrowth),
new XAttribute("health", branch.Health.ToString("G", CultureInfo.InvariantCulture)),
new XAttribute("maxhealth", branch.MaxHealth.ToString("G", CultureInfo.InvariantCulture)),
new XAttribute("sides", (int)branch.Sides),
@@ -457,9 +458,16 @@ namespace Barotrauma.MapCreatures.Behavior
foreach ((BallastFloraBranch branch, int parentBranchId) in branches)
{
if (parentBranchId > -1 && parentBranchId < Branches.Count)
if (parentBranchId > -1)
{
branch.ParentBranch = Branches[parentBranchId];
if (parentBranchId < Branches.Count)
{
branch.ParentBranch = Branches[parentBranchId];
}
else
{
DebugConsole.AddWarning($"Error while loading ballast flora: parent branch ID {parentBranchId} out of range (total {Branches.Count} branches)");
}
}
}
@@ -476,6 +484,7 @@ namespace Barotrauma.MapCreatures.Behavior
{
Vector2 pos = branchElement.GetAttributeVector2("pos", Vector2.Zero);
bool isRoot = branchElement.GetAttributeBool("isroot", false);
bool isRootGrowth = branchElement.GetAttributeBool("isrootgrowth", false);
int flowerConfig = getInt("flowerconfig");
int leafconfig = getInt("leafconfig");
int id = getInt("ID");
@@ -493,7 +502,8 @@ namespace Barotrauma.MapCreatures.Behavior
MaxHealth = maxhealth,
Sides = (TileSide) sides,
BlockedSides = (TileSide) blockedSides,
IsRoot = isRoot
IsRoot = isRoot,
IsRootGrowth = isRootGrowth
};
branches.Add((newBranch, parentBranchId));
@@ -658,11 +668,14 @@ namespace Barotrauma.MapCreatures.Behavior
toBeRemoved.Clear();
foreach (BallastFloraBranch branch in Branches)
{
if (branch.ParentBranch == null || branch.ParentBranch.DisconnectedFromRoot || branch.ParentBranch.Health <= 0.0f)
if (!branch.IsRoot)
{
float parentHealth = branch.ParentBranch == null ? 0.0f : branch.ParentBranch.Health / branch.ParentBranch.MaxHealth;
float speed = MathHelper.Lerp(5.0f, 0.1f, parentHealth);
DamageBranch(branch, speed * speed * deltaTime, AttackType.CutFromRoot);
if (branch.ParentBranch == null || branch.ParentBranch.DisconnectedFromRoot || branch.ParentBranch.Health <= 0.0f)
{
float parentHealth = branch.ParentBranch == null ? 0.0f : branch.ParentBranch.Health / branch.ParentBranch.MaxHealth;
float speed = MathHelper.Lerp(5.0f, 0.1f, parentHealth);
DamageBranch(branch, speed * speed * deltaTime, AttackType.CutFromRoot);
}
}
if (branch.Health <= 0.0f)
{
@@ -1197,7 +1210,10 @@ namespace Barotrauma.MapCreatures.Behavior
}
});
#if SERVER
CreateNetworkMessage(new InfectEventData(item, InfectEventData.InfectState.No, null));
if (!item.Removed && Parent != null && !Parent.Removed)
{
CreateNetworkMessage(new InfectEventData(item, InfectEventData.InfectState.No, null));
}
#endif
}
@@ -215,16 +215,19 @@ namespace Barotrauma
saveElement = element
};
if (!string.IsNullOrWhiteSpace(levelSeed) && levelData != null &&
levelData.Seed != levelSeed && !linkedSub.purchasedLostShuttles)
{
linkedSub.loadSub = false;
}
else
bool levelMatches = string.IsNullOrWhiteSpace(levelSeed) || levelData == null || levelData.Seed == levelSeed;
//don't load a sub that was left in this level if we have a submarine switch pending
//to make sure it gets ignored during the submarine switch and item transfer (reloading and saving it during the switch makes it not considered "left behind")
if ((levelMatches || linkedSub.purchasedLostShuttles) && GameMain.GameSession?.Campaign?.PendingSubmarineSwitch == null)
{
linkedSub.loadSub = true;
linkedSub.rect.Location = MathUtils.ToPoint(pos);
}
else
{
linkedSub.loadSub = false;
}
}
#warning TODO: revise
@@ -279,14 +282,14 @@ namespace Barotrauma
if (worldPos != Vector2.Zero)
{
if (GameMain.GameSession != null && GameMain.GameSession.MirrorLevel)
{
{
worldPos.X = GameMain.GameSession.LevelData.Size.X - worldPos.X;
}
sub.SetPosition(worldPos);
}
else
{
sub.SetPosition(WorldPosition);
sub.SetPosition(WorldPosition);
}
DockingPort linkedPort = null;
@@ -308,8 +311,17 @@ namespace Barotrauma
{
linkedPort = (FindEntityByID(originalLinkedToID) as Item)?.GetComponent<DockingPort>();
}
if (linkedPort == null) { return; }
}
if (linkedPort == null)
{
if (worldPos == Vector2.Zero)
{
DebugConsole.ThrowError("Something went wrong when loading a linked submarine - the save didn't include either a world position or a linked port for the submarine.");
}
return;
}
originalLinkedPort = linkedPort;
ushort originalMyId = childRemap.GetOffsetId(originalMyPortID);
@@ -1,11 +1,9 @@
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
@@ -98,9 +96,29 @@ namespace Barotrauma
[Serialize("", IsPropertySaveable.Yes), Editable]
public string ReplaceInRadiation { get; set; }
private readonly Dictionary<Identifier, int> moduleCounts = new Dictionary<Identifier, int>();
public class ModuleCount
{
public Identifier Identifier;
public int Count;
public int Order;
public IReadOnlyDictionary<Identifier, int> ModuleCounts
public ModuleCount(ContentXElement element)
{
Identifier = element.GetAttributeIdentifier("flag", element.GetAttributeIdentifier("moduletype", ""));
Count = element.GetAttributeInt("count", 0);
Order = element.GetAttributeInt("order", 0);
}
public ModuleCount(Identifier id, int count)
{
Identifier = id;
Count = count;
}
}
private readonly List<ModuleCount> moduleCounts = new List<ModuleCount>();
public IReadOnlyList<ModuleCount> ModuleCounts
{
get { return moduleCounts; }
}
@@ -171,8 +189,7 @@ namespace Barotrauma
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "modulecount":
Identifier moduleFlag = subElement.GetAttributeIdentifier("flag", subElement.GetAttributeIdentifier("moduletype", ""));
moduleCounts[moduleFlag] = subElement.GetAttributeInt("count", 0);
moduleCounts.Add(new ModuleCount(subElement));
break;
case "npcs":
var newCollection = new NpcCollection();
@@ -200,7 +217,7 @@ namespace Barotrauma
public int GetModuleCount(Identifier moduleFlag)
{
if (moduleFlag == Identifier.Empty || moduleFlag == "none") { return int.MaxValue; }
return moduleCounts.ContainsKey(moduleFlag) ? moduleCounts[moduleFlag] : 0;
return moduleCounts.FirstOrDefault(m => m.Identifier == moduleFlag)?.Count ?? 0;
}
public void SetModuleCount(Identifier moduleFlag, int count)
@@ -208,11 +225,19 @@ namespace Barotrauma
if (moduleFlag == Identifier.Empty || moduleFlag == "none") { return; }
if (count <= 0)
{
moduleCounts.Remove(moduleFlag);
moduleCounts.RemoveAll(m => m.Identifier == moduleFlag);
}
else
{
moduleCounts[moduleFlag] = count;
var moduleCount = moduleCounts.FirstOrDefault(m => m.Identifier == moduleFlag);
if (moduleCount == null)
{
moduleCounts.Add(new ModuleCount(moduleFlag, count));
}
else
{
moduleCount.Count = count;
}
}
}
@@ -99,7 +99,7 @@ namespace Barotrauma
{
//if the module doesn't have the ruin flag or any other flag used in the generation params, don't use it in ruins
if (!subInfo.OutpostModuleInfo.ModuleFlags.Contains("ruin".ToIdentifier()) &&
!generationParams.ModuleCounts.Any(m => subInfo.OutpostModuleInfo.ModuleFlags.Contains(m.Key)))
!generationParams.ModuleCounts.Any(m => subInfo.OutpostModuleInfo.ModuleFlags.Contains(m.Identifier)))
{
continue;
}
@@ -141,16 +141,11 @@ namespace Barotrauma
selectedModules.Clear();
//select which module types the outpost should consist of
List<Identifier> pendingModuleFlags;
using (var md5 = MD5.Create())
{
#warning TODO: cursed
pendingModuleFlags = onlyEntrance
? generationParams.ModuleCounts
.Keys.OrderBy(k => ToolBox.IdentifierToUint32Hash(k, md5))
.First().ToEnumerable().ToList()
: SelectModules(outpostModules, generationParams);
}
List<Identifier> pendingModuleFlags =
onlyEntrance ?
generationParams.ModuleCounts.First().Identifier.ToEnumerable().ToList() :
SelectModules(outpostModules, generationParams);
foreach (Identifier flag in pendingModuleFlags)
{
if (flag == "none") { continue; }
@@ -437,31 +432,27 @@ namespace Barotrauma
var pendingModuleFlags = new List<Identifier>();
bool availableModulesFound = true;
Identifier initialModuleFlag = generationParams.ModuleCounts.FirstOrDefault().Key;
Identifier initialModuleFlag = generationParams.ModuleCounts.FirstOrDefault().Identifier;
pendingModuleFlags.Add(initialModuleFlag);
while (pendingModuleFlags.Count < totalModuleCount && availableModulesFound)
{
availableModulesFound = false;
foreach (var moduleFlag in generationParams.ModuleCounts)
{
if (pendingModuleFlags.Count(m => m == moduleFlag.Key) >= generationParams.GetModuleCount(moduleFlag.Key))
if (pendingModuleFlags.Count(m => m == moduleFlag.Identifier) >= generationParams.GetModuleCount(moduleFlag.Identifier))
{
continue;
}
if (!modules.Any(m => m.OutpostModuleInfo.ModuleFlags.Contains(moduleFlag.Key)))
if (!modules.Any(m => m.OutpostModuleInfo.ModuleFlags.Contains(moduleFlag.Identifier)))
{
DebugConsole.ThrowError($"Failed to add a module to the outpost (no modules with the flag \"{moduleFlag.Key}\" found).");
DebugConsole.ThrowError($"Failed to add a module to the outpost (no modules with the flag \"{moduleFlag.Identifier}\" found).");
continue;
}
availableModulesFound = true;
pendingModuleFlags.Add(moduleFlag.Key);
pendingModuleFlags.Add(moduleFlag.Identifier);
}
}
using (MD5 md5 = MD5.Create())
{
pendingModuleFlags.Sort((i1, i2) => (int)ToolBox.StringToUInt32Hash(i1.Value.ToLowerInvariant(), md5) - (int)ToolBox.StringToUInt32Hash(i2.Value.ToLowerInvariant(), md5));
}
pendingModuleFlags.Shuffle(Rand.RandSync.ServerAndClient);
pendingModuleFlags.OrderBy(f => generationParams.ModuleCounts.First(m => m.Identifier == f)).ThenBy(f => Rand.Value(Rand.RandSync.ServerAndClient));
while (pendingModuleFlags.Count < totalModuleCount)
{
//don't place "none" modules at the end because
@@ -610,7 +601,7 @@ namespace Barotrauma
Identifier flagToPlace = "none".ToIdentifier();
SubmarineInfo nextModule = null;
foreach (Identifier moduleFlag in pendingModuleFlags)
foreach (Identifier moduleFlag in pendingModuleFlags.OrderByDescending(f => currentModule?.Info?.OutpostModuleInfo.AllowAttachToModules.Contains(f) ?? false))
{
flagToPlace = moduleFlag;
nextModule = GetRandomModule(currentModule?.Info?.OutpostModuleInfo, availableModules, flagToPlace, gapPosition, locationType, allowDifferentLocationType);
@@ -1048,6 +1039,17 @@ namespace Barotrauma
module.ThisGapPosition == OutpostModuleInfo.GapPosition.Left ||
module.ThisGapPosition == OutpostModuleInfo.GapPosition.Right;
if (!module.ThisGap.linkedTo.Any())
{
DebugConsole.ThrowError($"Error during outpost generation: {module.ThisGapPosition} gap in module \"{module.Info.Name}\" was not linked to any hulls.");
continue;
}
if (!module.PreviousGap.linkedTo.Any())
{
DebugConsole.ThrowError($"Error during outpost generation: {GetOpposingGapPosition(module.ThisGapPosition)} gap in module \"{module.PreviousModule.Info.Name}\" was not linked to any hulls.");
continue;
}
MapEntity leftHull = module.ThisGap.Position.X < module.PreviousGap.Position.X ? module.ThisGap.linkedTo[0] : module.PreviousGap.linkedTo[0];
MapEntity rightHull = module.ThisGap.Position.X > module.PreviousGap.Position.X ?
module.ThisGap.linkedTo.Count == 1 ? module.ThisGap.linkedTo[0] : module.ThisGap.linkedTo[1] :
@@ -109,6 +109,8 @@ namespace Barotrauma
public bool IsCampaignCompatible => IsPlayer && !HasTag(SubmarineTag.Shuttle) && !HasTag(SubmarineTag.HideInMenus) && SubmarineClass != SubmarineClass.Undefined;
public bool IsCampaignCompatibleIgnoreClass => IsPlayer && !HasTag(SubmarineTag.Shuttle) && !HasTag(SubmarineTag.HideInMenus);
public bool AllowPreviewImage => Type == SubmarineType.Player;
public Md5Hash MD5Hash
{
get
@@ -556,7 +558,7 @@ namespace Barotrauma
XDocument doc = new XDocument(newElement);
doc.Root.Add(new XAttribute("name", Name));
if (previewImage != null)
if (previewImage != null && AllowPreviewImage)
{
doc.Root.Add(new XAttribute("previewimage", Convert.ToBase64String(previewImage.ToArray())));
}
@@ -619,6 +619,11 @@ namespace Barotrauma
if (parentObject is Powered powered) { value = powered.Voltage; return true; }
}
break;
case nameof(Powered.RelativeVoltage):
{
if (parentObject is Powered powered) { value = powered.RelativeVoltage; return true; }
}
break;
case nameof(Powered.CurrPowerConsumption):
{
if (parentObject is Powered powered) { value = powered.CurrPowerConsumption; return true; }
@@ -480,6 +480,8 @@ namespace Barotrauma
bool voiceCaptureChanged = currentConfig.Audio.VoiceCaptureDevice != newConfig.Audio.VoiceCaptureDevice;
bool textScaleChanged = Math.Abs(currentConfig.Graphics.TextScale - newConfig.Graphics.TextScale) > MathF.Pow(2.0f, -7);
bool hudScaleChanged = !MathUtils.NearlyEqual(currentConfig.Graphics.HUDScale, newConfig.Graphics.HUDScale);
bool setGraphicsMode =
resolutionChanged ||
currentConfig.Graphics.VSync != newConfig.Graphics.VSync ||
@@ -514,6 +516,10 @@ namespace Barotrauma
componentStyle.RefreshSize();
}
}
if (hudScaleChanged)
{
HUDLayoutSettings.CreateAreas();
}
GameMain.SoundManager?.ApplySettings();
#endif
@@ -100,13 +100,23 @@ namespace Barotrauma
}
#endif
if (Path.IsPathRooted(originalFilename))
string startPath = directory ?? "";
string saveFolder = SaveUtil.SaveFolder.Replace('\\', '/');
if (originalFilename.Replace('\\', '/').StartsWith(saveFolder))
{
//paths that lead to the save folder might have incorrect case,
//mainly if they come from a filelist
startPath = saveFolder.EndsWith('/') ? saveFolder : $"{saveFolder}/";
filename = startPath;
subDirs = subDirs.Skip(saveFolder.Split('/').Length).ToArray();
}
else if (Path.IsPathRooted(originalFilename))
{
#warning TODO: incorrect assumption or...? Figure out what this was actually supposed to fix, if anything. Might've been a perf thing.
return originalFilename; //assume that rooted paths have correct case since these are generated by the game
}
string startPath = directory ?? "";
for (int i = 0; i < subDirs.Length; i++)
{
if (i == subDirs.Length - 1 && string.IsNullOrEmpty(subDirs[i]))