Unstable 0.1300.1.11

This commit is contained in:
Markus Isberg
2021-04-26 21:07:23 +03:00
parent 841d755394
commit 2c750282ec
76 changed files with 671 additions and 396 deletions

View File

@@ -21,6 +21,11 @@ namespace Barotrauma
throw new System.Exception("Error in CargoMission.ClientReadInitial: item count does not match the server count (" + itemCount + " != " + items.Count + ", mission: " + Prefab.Identifier + ")");
}
if (requiredDeliveryAmount == 0) { requiredDeliveryAmount = items.Count; }
if (requiredDeliveryAmount > items.Count)
{
DebugConsole.AddWarning($"Error in mission \"{Prefab.Identifier}\". Required delivery amount is {requiredDeliveryAmount} but there's only {items.Count} items to deliver.");
requiredDeliveryAmount = items.Count;
}
}
}
}

View File

@@ -1274,6 +1274,7 @@ namespace Barotrauma
private static void UpdateSavingIndicator(float deltaTime)
{
if (Style.SavingIndicator == null) { return; }
lock (mutex)
{
if (timeUntilSavingIndicatorDisabled.HasValue)
@@ -1651,7 +1652,7 @@ namespace Barotrauma
private static void DrawSavingIndicator(SpriteBatch spriteBatch)
{
if (!IsSavingIndicatorVisible) { return; }
if (!IsSavingIndicatorVisible || Style.SavingIndicator == null) { return; }
var sheet = Style.SavingIndicator;
Vector2 pos = new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight) - new Vector2(HUDLayoutSettings.Padding) - 2 * Scale * sheet.FrameSize.ToVector2();
sheet.Draw(spriteBatch, (int)Math.Floor(savingIndicatorSpriteIndex), pos, savingIndicatorColor, origin: Vector2.Zero, rotate: 0.0f, scale: new Vector2(Scale));

View File

@@ -1319,17 +1319,26 @@ namespace Barotrauma
// When using Deselect to close the interface, make sure it's not a seconday mouse button click on a node
// That should be reserved for opening manual assignment
var hitDeselect = PlayerInput.KeyHit(InputType.Deselect) && (!PlayerInput.SecondaryMouseButtonClicked() ||
(optionNodes.None(n => GUI.IsMouseOn(n.Item1)) && shortcutNodes.None(n => GUI.IsMouseOn(n))));
bool isMouseOnOptionNode = optionNodes.Any(n => GUI.IsMouseOn(n.Item1));
bool isMouseOnShortcutNode = !isMouseOnOptionNode && shortcutNodes.Any(n => GUI.IsMouseOn(n));
bool hitDeselect = PlayerInput.KeyHit(InputType.Deselect) &&
(!PlayerInput.SecondaryMouseButtonClicked() || (!isMouseOnOptionNode && !isMouseOnShortcutNode));
bool isBoundToPrimaryMouse = GameMain.Config.KeyBind(InputType.Command).MouseButton is MouseButton mouseButton &&
(mouseButton == MouseButton.PrimaryMouse || mouseButton == (PlayerInput.MouseButtonsSwapped() ? MouseButton.RightMouse : MouseButton.LeftMouse));
bool canToggleInterface = !isBoundToPrimaryMouse ||
(!isMouseOnOptionNode && !isMouseOnShortcutNode && extraOptionNodes.None(n => GUI.IsMouseOn(n)) && !GUI.IsMouseOn(returnNode));
// TODO: Consider using HUD.CloseHUD() instead of KeyHit(Escape), the former method is also used for health UI
if (hitDeselect || PlayerInput.KeyHit(Keys.Escape) || !CanIssueOrders ||
(PlayerInput.KeyHit(InputType.Command) && selectedNode == null && !clicklessSelectionActive))
(canToggleInterface && PlayerInput.KeyHit(InputType.Command) && selectedNode == null && !clicklessSelectionActive))
{
DisableCommandUI();
}
else if (PlayerInput.KeyUp(InputType.Command))
{
if (!isOpeningClick && clicklessSelectionActive && timeSelected < 0.15f)
// Clickless selection behavior
if (canToggleInterface && !isOpeningClick && clicklessSelectionActive && timeSelected < 0.15f)
{
DisableCommandUI();
}
@@ -1344,6 +1353,7 @@ namespace Barotrauma
}
else if (PlayerInput.KeyDown(InputType.Command) && (targetFrame == null || !targetFrame.Visible))
{
// Clickless selection behavior
if (!GUI.IsMouseOn(centerNode))
{
clicklessSelectionActive = true;
@@ -1914,6 +1924,7 @@ namespace Barotrauma
private bool NavigateForward(GUIButton node, object userData)
{
if (commandFrame == null) { return false; }
if (!(optionNodes.Find(n => n.Item1 == node) is Tuple<GUIComponent, Keys> optionNode) || !optionNodes.Remove(optionNode))
{
shortcutNodes.Remove(node);
@@ -1953,6 +1964,7 @@ namespace Barotrauma
private bool NavigateBackward(GUIButton node, object userData)
{
if (commandFrame == null) { return false; }
RemoveOptionNodes();
if (targetFrame != null) { targetFrame.Visible = false; }
// TODO: Center node could move to option node instead of being removed

View File

@@ -474,8 +474,7 @@ namespace Barotrauma
public override void Update(float deltaTime, Camera cam, bool isSubInventory = false)
{
// Need to update the infiltrator's inventory because they use id cards to access the sub. TODO: We don't probably need to update everything.
if (!AccessibleWhenAlive && !character.IsDead && (character.Params.AI == null || !character.Params.AI.Infiltrate))
if (!AccessibleWhenAlive && !character.IsDead)
{
syncItemsDelay = Math.Max(syncItemsDelay - deltaTime, 0.0f);
return;

View File

@@ -898,6 +898,22 @@ namespace Barotrauma.Items.Components
signalWarningText.Visible = false;
}
foreach (AITarget aiTarget in AITarget.List)
{
if (!aiTarget.Enabled) { continue; }
if (string.IsNullOrEmpty(aiTarget.SonarLabel) || aiTarget.SoundRange <= 0.0f) { continue; }
if (Vector2.DistanceSquared(aiTarget.WorldPosition, transducerCenter) < aiTarget.SoundRange * aiTarget.SoundRange)
{
DrawMarker(spriteBatch,
aiTarget.SonarLabel,
aiTarget.SonarIconIdentifier,
aiTarget,
aiTarget.WorldPosition, transducerCenter,
displayScale, center, DisplayRadius * 0.975f);
}
}
if (GameMain.GameSession == null || Level.Loaded == null) { return; }
if (Level.Loaded.StartLocation != null)
@@ -931,23 +947,7 @@ namespace Barotrauma.Items.Components
cave.StartPos.ToVector2(), transducerCenter,
displayScale, center, DisplayRadius);
}
foreach (AITarget aiTarget in AITarget.List)
{
if (!aiTarget.Enabled) { continue; }
if (string.IsNullOrEmpty(aiTarget.SonarLabel) || aiTarget.SoundRange <= 0.0f) { continue; }
if (Vector2.DistanceSquared(aiTarget.WorldPosition, transducerCenter) < aiTarget.SoundRange * aiTarget.SoundRange)
{
DrawMarker(spriteBatch,
aiTarget.SonarLabel,
aiTarget.SonarIconIdentifier,
aiTarget,
aiTarget.WorldPosition, transducerCenter,
displayScale, center, DisplayRadius * 0.975f);
}
}
foreach (Mission mission in GameMain.GameSession.Missions)
{
if (!string.IsNullOrWhiteSpace(mission.SonarLabel))

View File

@@ -220,6 +220,8 @@ namespace Barotrauma
public int tooltipDisplayedCondition;
public bool ForceTooltipRefresh;
public SlotReference(Inventory parentInventory, VisualSlot slot, int slotIndex, bool isSubSlot, Inventory subInventory = null)
{
ParentInventory = parentInventory;
@@ -234,12 +236,14 @@ namespace Barotrauma
public bool TooltipNeedsRefresh()
{
if (ForceTooltipRefresh) { return true; }
if (Item == null) { return false; }
return (int)Item.ConditionPercentage != tooltipDisplayedCondition;
}
public void RefreshTooltip()
{
ForceTooltipRefresh = false;
if (Item == null) { return; }
IEnumerable<Item> itemsInSlot = null;
if (ParentInventory != null && Item != null)

View File

@@ -160,17 +160,6 @@ namespace Barotrauma
public void Update(float deltaTime)
{
if (ParticleEmitters != null)
{
for (int i = 0; i < ParticleEmitters.Length; i++)
{
if (ParticleEmitterTriggers[i] != null && !ParticleEmitterTriggers[i].IsTriggered) continue;
Vector2 emitterPos = LocalToWorld(Prefab.EmitterPositions[i]);
ParticleEmitters[i].Emit(deltaTime, emitterPos, hullGuess: null,
angle: ParticleEmitters[i].Prefab.CopyEntityAngle ? Rotation : 0.0f);
}
}
CurrentRotation = Rotation;
if (ActivePrefab.SwingFrequency > 0.0f)
{
@@ -214,6 +203,17 @@ namespace Barotrauma
UpdateDeformations(deltaTime);
}
if (ParticleEmitters != null)
{
for (int i = 0; i < ParticleEmitters.Length; i++)
{
if (ParticleEmitterTriggers[i] != null && !ParticleEmitterTriggers[i].IsTriggered) { continue; }
Vector2 emitterPos = LocalToWorld(Prefab.EmitterPositions[i]);
ParticleEmitters[i].Emit(deltaTime, emitterPos, hullGuess: null,
angle: ParticleEmitters[i].Prefab.CopyEntityAngle ? -CurrentRotation + MathHelper.PiOver2 : 0.0f);
}
}
for (int i = 0; i < Sounds.Length; i++)
{
if (Sounds[i] == null) { continue; }

View File

@@ -573,7 +573,7 @@ namespace Barotrauma
if (!spriteRecorder.ReadyToRender)
{
string waitText = !loadTask.IsCompleted ?
"Generating preview..." :
TextManager.Get("generatingsubmarinepreview", fallBackTag: "loading") :
(loadTask.Exception?.ToString() ?? "Task completed without marking as ready to render");
Vector2 origin = (GUI.Font.MeasureString(waitText) * 0.5f);
origin.X = MathF.Round(origin.X);

View File

@@ -12,9 +12,10 @@ namespace Barotrauma.Networking
{
protected class ServerContentPackage
{
public string Name;
public string Hash;
public UInt64 WorkshopId;
public readonly string Name;
public readonly string Hash;
public readonly UInt64 WorkshopId;
public readonly DateTime InstallTime;
public ContentPackage RegularPackage
{
@@ -32,11 +33,12 @@ namespace Barotrauma.Networking
}
}
public ServerContentPackage(string name, string hash, UInt64 workshopId)
public ServerContentPackage(string name, string hash, UInt64 workshopId, DateTime installTime)
{
Name = name;
Hash = hash;
WorkshopId = workshopId;
InstallTime = installTime;
}
}
@@ -129,7 +131,10 @@ namespace Barotrauma.Networking
string name = inc.ReadString();
string hash = inc.ReadString();
UInt64 workshopId = inc.ReadUInt64();
var pkg = new ServerContentPackage(name, hash, workshopId);
UInt32 installTimeDiffSeconds = inc.ReadUInt32();
DateTime installTime = DateTime.UtcNow + TimeSpan.FromSeconds(installTimeDiffSeconds);
var pkg = new ServerContentPackage(name, hash, workshopId, installTime);
if (pkg.CorePackage != null)
{
corePackage = pkg;
@@ -147,16 +152,24 @@ namespace Barotrauma.Networking
if (missingPackages.Count > 0)
{
var nonDownloadable = missingPackages.Where(p => p.WorkshopId == 0);
var mismatchedButDownloaded = missingPackages.Where(p =>
var mismatchedButDownloaded = missingPackages.Where(remote =>
{
var localMatching = ContentPackage.RegularPackages.Find(l => l.SteamWorkshopId != 0 && p.WorkshopId == l.SteamWorkshopId);
localMatching ??= ContentPackage.CorePackages.Find(l => l.SteamWorkshopId != 0 && p.WorkshopId == l.SteamWorkshopId);
return localMatching != null;
return ContentPackage.AllPackages.Any(local =>
local.SteamWorkshopId != 0 && /* is a Workshop item */
remote.WorkshopId == local.SteamWorkshopId /* ids match */);
});
if (GameMain.ServerListScreen.LastAutoConnectEndpoint != ServerConnection.EndPointString)
{
mismatchedButDownloaded = mismatchedButDownloaded.Where(remote =>
{
return ContentPackage.AllPackages.Any(local =>
remote.InstallTime < local.InstallTime/* remote is older than local */);
});
}
if (mismatchedButDownloaded.Any())
{
GameMain.ServerListScreen.LastAutoConnectEndpoint = null;
string disconnectMsg;
if (mismatchedButDownloaded.Count() == 1)
{

View File

@@ -1,4 +1,5 @@
using Barotrauma.IO;
using Barotrauma.Extensions;
using Barotrauma.IO;
using Barotrauma.Networking;
using RestSharp;
using System;
@@ -921,10 +922,6 @@ namespace Barotrauma.Steam
return null;
}
#if DEBUG
item = item?.WithPrivateVisibility();
#endif
contentPackage.GameVersion = GameMain.Version;
contentPackage.Save(contentPackage.Path);
@@ -969,6 +966,9 @@ namespace Barotrauma.Steam
}
else
{
//nuke the existing steamworks cache for the item we just published
ForceRedownload(task.Result.FileId);
workshopPublishStatus.Success = true;
workshopPublishStatus.Result = task.Result;
DebugConsole.NewMessage("Published workshop item " + item?.Title + " successfully.", Microsoft.Xna.Framework.Color.LightGreen);
@@ -986,41 +986,61 @@ namespace Barotrauma.Steam
yield return CoroutineStatus.Success;
}
/// <summary>
/// Forces a Workshop item to redownload.
/// </summary>
public static void ForceRedownload(Steamworks.Data.PublishedFileId itemId, Action onDownloadFinished = null)
{
Steamworks.Ugc.Item itemToNuke = new Steamworks.Ugc.Item(itemId);
string directory = itemToNuke.Directory;
if (Directory.Exists(directory))
{
try
{
Directory.Delete(directory, true);
}
catch (Exception e) { DebugConsole.ThrowError("Failed to delete Workshop item cache", e); }
}
itemToNuke.Download(onDownloadFinished, highPriority: true);
}
/// <summary>
/// Installs a workshop item by moving it to the game folder.
/// </summary>
public static bool InstallWorkshopItem(Steamworks.Ugc.Item? item, out string errorMsg, bool enableContentPackage = false, bool suppressInstallNotif = false)
public static bool InstallWorkshopItem(Steamworks.Ugc.Item? itemOrNull, out string errorMsg, bool enableContentPackage = false, bool suppressInstallNotif = false)
{
if (!(item?.IsInstalled ?? false))
errorMsg = "Item is null";
if (!itemOrNull.TryGetValue(out Steamworks.Ugc.Item item)) { return false; }
if (!item.IsInstalled)
{
errorMsg = TextManager.GetWithVariable("WorkshopErrorInstallRequiredToEnable", "[itemname]", item?.Title ?? "[NULL]");
errorMsg = TextManager.GetWithVariable("WorkshopErrorInstallRequiredToEnable", "[itemname]", item.Title);
DebugConsole.NewMessage(errorMsg, Color.Red);
return false;
}
string metaDataFilePath = Path.Combine(item?.Directory, MetadataFileName);
string metaDataFilePath = Path.Combine(item.Directory, MetadataFileName);
if (!File.Exists(metaDataFilePath))
{
errorMsg = TextManager.GetWithVariable("WorkshopErrorInstallRequiredToEnable", "[itemname]", item?.Title);
errorMsg = TextManager.GetWithVariable("WorkshopErrorInstallRequiredToEnable", "[itemname]", item.Title);
DebugConsole.ThrowError(errorMsg);
return false;
}
ContentPackage contentPackage = new ContentPackage(metaDataFilePath)
{
SteamWorkshopId = item?.Id ?? 0
SteamWorkshopId = item.Id
};
string newContentPackagePath = GetWorkshopItemContentPackagePath(contentPackage);
List<ContentPackage> existingPackages = ContentPackage.AllPackages.Where(cp => cp.Path.CleanUpPath() == newContentPackagePath.CleanUpPath()).ToList();
if (existingPackages.Any())
{
if (item?.Owner.Id != Steamworks.SteamClient.SteamId)
if (item.Owner.Id != Steamworks.SteamClient.SteamId)
{
errorMsg = TextManager.GetWithVariables("WorkshopErrorSamePathInstalled",
new string[] { "[itemname]", "[itempath]" },
new string[] { item?.Title, Path.GetDirectoryName(newContentPackagePath) });
new string[] { item.Title, Path.GetDirectoryName(newContentPackagePath) });
return false;
}
else
@@ -1041,12 +1061,12 @@ namespace Barotrauma.Steam
lock (modCopiesInProgress)
{
if (modCopiesInProgress.ContainsKey(item.Value.Id))
if (modCopiesInProgress.ContainsKey(item.Id))
{
errorMsg = ""; return true;
}
newTask = CopyWorkShopItemAsync(item, contentPackage, newContentPackagePath, metaDataFilePath);
modCopiesInProgress.Add(item.Value.Id, newTask);
modCopiesInProgress.Add(item.Id, newTask);
}
TaskPool.Add("CopyWorkShopItemAsync",
@@ -1058,14 +1078,14 @@ namespace Barotrauma.Steam
{
if (task.IsFaulted || task.IsCanceled)
{
DebugConsole.ThrowError($"Failed to copy \"{item?.Title}\"", task.Exception);
DebugConsole.ThrowError($"Failed to copy \"{item.Title}\"", task.Exception);
GameMain.SteamWorkshopScreen?.SetReinstallButtonStatus(item, true, GUI.Style.Red);
return;
}
string errorMsg = ((Task<string>)task).Result;
if (!string.IsNullOrWhiteSpace(errorMsg))
{
DebugConsole.ThrowError($"Failed to copy \"{item?.Title}\": {errorMsg}");
DebugConsole.ThrowError($"Failed to copy \"{item.Title}\": {errorMsg}");
GameMain.SteamWorkshopScreen?.SetReinstallButtonStatus(item, true, GUI.Style.Red);
return;
}
@@ -1074,8 +1094,8 @@ namespace Barotrauma.Steam
var newPackage = new ContentPackage(cp.Path, newContentPackagePath)
{
SteamWorkshopId = item?.Id ?? 0,
InstallTime = item?.Updated > item?.Created ? item?.Updated : item?.Created
SteamWorkshopId = item.Id,
InstallTime = item.Updated > item.Created ? item.Updated : item.Created
};
foreach (ContentFile contentFile in newPackage.Files)
@@ -1124,7 +1144,6 @@ namespace Barotrauma.Steam
GameMain.Config.SuppressModFolderWatcher = false;
GameMain.SteamWorkshopScreen?.SetReinstallButtonStatus(item, true, GUI.Style.Green);
}
catch
{
@@ -1132,7 +1151,7 @@ namespace Barotrauma.Steam
}
finally
{
modCopiesInProgress.Remove(item.Value.Id);
modCopiesInProgress.Remove(item.Id);
}
});
@@ -1144,9 +1163,27 @@ namespace Barotrauma.Steam
/// Asynchronously copies a Workshop item into the Mods folder.
/// </summary>
/// <returns>Returns an empty string on success, otherwise returns an error message.</returns>
private async static Task<string> CopyWorkShopItemAsync(Steamworks.Ugc.Item? item, ContentPackage contentPackage, string newContentPackagePath, string metaDataFilePath)
private async static Task<string> CopyWorkShopItemAsync(Steamworks.Ugc.Item? itemOrNull, ContentPackage contentPackage, string newContentPackagePath, string metaDataFilePath)
{
await Task.Yield();
if (!itemOrNull.TryGetValue(out Steamworks.Ugc.Item item)) { return "Item is null"; }
if (item.NeedsUpdate)
{
item.Download(highPriority: true);
await Task.Delay(1000);
}
while (item.NeedsUpdate && !item.IsDownloading && !item.IsDownloadPending && !item.IsInstalled)
{
if (!item.IsDownloading && !item.IsDownloadPending)
{
if (!item.Download())
{
return TextManager.GetWithVariable("WorkshopErrorEnableFailed", "[itemname]", item.Title);
}
}
await Task.Delay(1000);
}
string targetPath = Path.GetDirectoryName(GetWorkshopItemContentPackagePath(contentPackage));
string copyingPath = Path.Combine(targetPath, CopyIndicatorFileName);
@@ -1157,18 +1194,18 @@ namespace Barotrauma.Steam
Directory.CreateDirectory(targetPath);
File.WriteAllText(copyingPath, "TEMPORARY FILE");
SaveUtil.CopyFolder(item?.Directory, targetPath, copySubDirs: true, overwriteExisting: false);
SaveUtil.CopyFolder(item.Directory, targetPath, copySubDirs: true, overwriteExisting: false);
File.Delete(copyingPath);
return "";
}
var allPackageFiles = Directory.GetFiles(item?.Directory, "*", System.IO.SearchOption.AllDirectories);
var allPackageFiles = Directory.GetFiles(item.Directory, "*", System.IO.SearchOption.AllDirectories);
List<string> nonContentFiles = new List<string>();
foreach (string file in allPackageFiles)
{
if (file == metaDataFilePath) { continue; }
string relativePath = UpdaterUtil.GetRelativePath(file, item?.Directory);
string relativePath = UpdaterUtil.GetRelativePath(file, item.Directory);
string fullPath = Path.GetFullPath(relativePath);
if (contentPackage.Files.Any(f => { string fp = Path.GetFullPath(f.Path); return fp == fullPath; })) { continue; }
nonContentFiles.Add(relativePath);
@@ -1198,14 +1235,14 @@ namespace Barotrauma.Steam
foreach (ContentFile contentFile in contentPackage.Files)
{
contentFile.Path = contentFile.Path.CleanUpPathCrossPlatform(correctFilenameCase: true, item?.Directory);
string sourceFile = Path.Combine(item?.Directory, contentFile.Path);
contentFile.Path = contentFile.Path.CleanUpPathCrossPlatform(correctFilenameCase: true, item.Directory);
string sourceFile = Path.Combine(item.Directory, contentFile.Path);
if (!File.Exists(sourceFile))
{
string[] splitPath = contentFile.Path.Split('/');
if (splitPath.Length >= 2 && splitPath[0] == "Mods")
{
sourceFile = Path.Combine(item?.Directory, string.Join("/", splitPath.Skip(2)));
sourceFile = Path.Combine(item.Directory, string.Join("/", splitPath.Skip(2)));
}
}
@@ -1225,7 +1262,7 @@ namespace Barotrauma.Steam
//if the external file doesn't exist, we cannot enable the package
else if (!File.Exists(contentFile.Path))
{
errorMsg = TextManager.GetWithVariable("WorkshopErrorEnableFailed", "[itemname]", item?.Title) + " " + TextManager.GetWithVariable("WorkshopFileNotFound", "[path]", "\"" + contentFile.Path + "\"");
errorMsg = TextManager.GetWithVariable("WorkshopErrorEnableFailed", "[itemname]", item.Title) + " " + TextManager.GetWithVariable("WorkshopFileNotFound", "[path]", "\"" + contentFile.Path + "\"");
return errorMsg;
}
continue;
@@ -1240,7 +1277,7 @@ namespace Barotrauma.Steam
else
{
//file not present in either the mod or the game folder -> cannot enable the package
errorMsg = TextManager.GetWithVariable("WorkshopErrorEnableFailed", "[itemname]", item?.Title) + " " + TextManager.GetWithVariable("WorkshopFileNotFound", "[path]", "\"" + contentFile.Path + "\"");
errorMsg = TextManager.GetWithVariable("WorkshopErrorEnableFailed", "[itemname]", item.Title) + " " + TextManager.GetWithVariable("WorkshopFileNotFound", "[path]", "\"" + contentFile.Path + "\"");
return errorMsg;
}
}
@@ -1252,7 +1289,7 @@ namespace Barotrauma.Steam
foreach (string nonContentFile in nonContentFiles)
{
string sourceFile = Path.Combine(item?.Directory, nonContentFile);
string sourceFile = Path.Combine(item.Directory, nonContentFile);
if (!File.Exists(sourceFile)) { continue; }
string destinationPath = CorrectContentFilePath(nonContentFile, ContentType.None, contentPackage, false);
Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));
@@ -1351,43 +1388,50 @@ namespace Barotrauma.Steam
string metaDataPath = Path.Combine(item?.Directory, MetadataFileName);
if (!File.Exists(metaDataPath))
{
throw new System.IO.FileNotFoundException("Metadata file for the Workshop item \"" + item?.Title + "\" not found. The file may be corrupted.");
DebugConsole.ThrowError("Metadata file for the Workshop item \"" + item?.Title + "\" not found. The file may be corrupted.", appendStackTrace: true);
return null;
}
ContentPackage contentPackage = new ContentPackage(metaDataPath);
return contentPackage.IsCompatible();
}
public static bool CheckWorkshopItemInstalled(Steamworks.Ugc.Item? item)
public static bool CheckWorkshopItemInstalled(Steamworks.Ugc.Item? itemOrNull)
{
if (!(item?.IsInstalled ?? false)) { return false; }
if (!itemOrNull.TryGetValue(out Steamworks.Ugc.Item item)) { return false; }
if (!item.IsInstalled) { return false; }
lock (modCopiesInProgress)
{
if (modCopiesInProgress.ContainsKey(item.Value.Id))
if (modCopiesInProgress.ContainsKey(item.Id))
{
return true;
}
}
if (!Directory.Exists(item?.Directory))
if (item.NeedsUpdate && !item.IsDownloading && !item.IsDownloadPending)
{
DebugConsole.ThrowError("Workshop item \"" + item?.Title + "\" has been installed but the install directory cannot be found. Attempting to redownload...");
item?.Download();
return false;
item.Download();
return false;
}
if (!Directory.Exists(item.Directory))
{
DebugConsole.ThrowError("Workshop item \"" + item.Title + "\" has been installed but the install directory cannot be found. Attempting to redownload...");
item.Download();
return false;
}
string metaDataPath = "";
try
{
metaDataPath = Path.Combine(item?.Directory, MetadataFileName);
metaDataPath = Path.Combine(item.Directory, MetadataFileName);
}
catch (ArgumentException)
{
string errorMessage = "Metadata file for the Workshop item \"" + item?.Title +
"\" not found. Could not combine path (" + (item?.Directory ?? "directory name empty") + ").";
string errorMessage = "Metadata file for the Workshop item \"" + item.Title +
"\" not found. Could not combine path (" + (item.Directory ?? "directory name empty") + ").";
DebugConsole.ThrowError(errorMessage);
GameAnalyticsManager.AddErrorEventOnce("SteamManager.CheckWorkshopItemInstalled:PathCombineException" + item?.Title,
GameAnalyticsManager.AddErrorEventOnce("SteamManager.CheckWorkshopItemInstalled:PathCombineException" + item.Title,
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
errorMessage);
return false;
@@ -1395,13 +1439,13 @@ namespace Barotrauma.Steam
if (!File.Exists(metaDataPath))
{
DebugConsole.ThrowError("Metadata file for the Workshop item \"" + item?.Title + "\" not found. The file may be corrupted.");
DebugConsole.ThrowError("Metadata file for the Workshop item \"" + item.Title + "\" not found. The file may be corrupted.");
return false;
}
ContentPackage contentPackage = new ContentPackage(metaDataPath)
{
SteamWorkshopId = item?.Id ?? 0
SteamWorkshopId = item.Id
};
//make sure the contentpackage file is present
if (!File.Exists(GetWorkshopItemContentPackagePath(contentPackage)) ||
@@ -1414,20 +1458,21 @@ namespace Barotrauma.Steam
return true;
}
public static bool CheckWorkshopItemUpToDate(Steamworks.Ugc.Item? item)
public static bool CheckWorkshopItemUpToDate(Steamworks.Ugc.Item? itemOrNull)
{
if (!(item?.IsInstalled ?? false)) return false;
if (!itemOrNull.TryGetValue(out Steamworks.Ugc.Item item)) { return false; }
if (!item.IsInstalled || item.NeedsUpdate || item.IsDownloading || item.IsDownloadPending) { return false; }
string metaDataPath = Path.Combine(item?.Directory, MetadataFileName);
string metaDataPath = Path.Combine(item.Directory, MetadataFileName);
if (!File.Exists(metaDataPath))
{
DebugConsole.ThrowError("Metadata file for the Workshop item \"" + item?.Title + "\" not found. The file may be corrupted.");
DebugConsole.ThrowError("Metadata file for the Workshop item \"" + item.Title + "\" not found. The file may be corrupted.");
return false;
}
ContentPackage steamPackage = new ContentPackage(metaDataPath)
{
SteamWorkshopId = item?.Id ?? 0
SteamWorkshopId = item.Id
};
ContentPackage myPackage = ContentPackage.AllPackages.FirstOrDefault(cp => cp.SteamWorkshopId == steamPackage.SteamWorkshopId);
@@ -1435,7 +1480,7 @@ namespace Barotrauma.Steam
{
return false;
}
DateTime latestTime = item.Value.Updated > item.Value.Created ? item.Value.Updated : item.Value.Created;
DateTime latestTime = item.Updated > item.Created ? item.Updated : item.Created;
bool upToDate = latestTime <= myPackage.InstallTime.Value;
return upToDate;
}

View File

@@ -1,4 +1,5 @@
using Barotrauma.Sounds;
using Concentus.Structs;
using Microsoft.Xna.Framework;
using OpenAL;
using System;
@@ -23,6 +24,8 @@ namespace Barotrauma.Networking
private bool capturing;
private OpusEncoder encoder;
public double LastdB
{
get;
@@ -79,7 +82,7 @@ namespace Barotrauma.Networking
{
Disconnected = false;
VoipConfig.SetupEncoding();
encoder = VoipConfig.CreateEncoder();
//set up capture device
captureDevice = Alc.CaptureOpenDevice(deviceName, VoipConfig.FREQUENCY, Al.FormatMono16, VoipConfig.BUFFER_SIZE * 5);
@@ -265,10 +268,10 @@ namespace Barotrauma.Networking
{
if (!prevCaptured) //enqueue the previous buffer if not sent to avoid cutoff
{
int compressedCountPrev = VoipConfig.Encoder.Encode(prevUncompressedBuffer, 0, VoipConfig.BUFFER_SIZE, BufferToQueue, 0, VoipConfig.MAX_COMPRESSED_SIZE);
int compressedCountPrev = encoder.Encode(prevUncompressedBuffer, 0, VoipConfig.BUFFER_SIZE, BufferToQueue, 0, VoipConfig.MAX_COMPRESSED_SIZE);
EnqueueBuffer(compressedCountPrev);
}
int compressedCount = VoipConfig.Encoder.Encode(uncompressedBuffer, 0, VoipConfig.BUFFER_SIZE, BufferToQueue, 0, VoipConfig.MAX_COMPRESSED_SIZE);
int compressedCount = encoder.Encode(uncompressedBuffer, 0, VoipConfig.BUFFER_SIZE, BufferToQueue, 0, VoipConfig.MAX_COMPRESSED_SIZE);
EnqueueBuffer(compressedCount);
}
captureTimer -= (VoipConfig.BUFFER_SIZE * 1000) / VoipConfig.FREQUENCY;

View File

@@ -10,35 +10,22 @@ namespace Barotrauma.Networking
{
static partial class VoipConfig
{
public static bool Ready = false;
public const int FREQUENCY = 48000; //48Khz
public const int BITRATE = 16000; //16Kbps
public const int BUFFER_SIZE = (8 * MAX_COMPRESSED_SIZE * FREQUENCY) / BITRATE; //20ms window
public const int FREQUENCY = 48000;
public const int BUFFER_SIZE = 960; //20ms window
public static OpusEncoder CreateEncoder()
{
var encoder = new OpusEncoder(FREQUENCY, 1, OpusApplication.OPUS_APPLICATION_VOIP);
encoder.Bandwidth = OpusBandwidth.OPUS_BANDWIDTH_AUTO;
encoder.Bitrate = BITRATE;
encoder.SignalType = OpusSignal.OPUS_SIGNAL_VOICE;
return encoder;
}
public static OpusEncoder Encoder
public static OpusDecoder CreateDecoder()
{
get;
private set;
}
public static OpusDecoder Decoder
{
get;
private set;
}
public static void SetupEncoding()
{
if (!Ready)
{
Encoder = new OpusEncoder(FREQUENCY, 1, OpusApplication.OPUS_APPLICATION_VOIP);
Encoder.Bandwidth = OpusBandwidth.OPUS_BANDWIDTH_AUTO;
Encoder.Bitrate = 8 * MAX_COMPRESSED_SIZE * FREQUENCY / BUFFER_SIZE;
Encoder.SignalType = OpusSignal.OPUS_SIGNAL_VOICE;
Decoder = new OpusDecoder(FREQUENCY, 1);
Ready = true;
}
return new OpusDecoder(FREQUENCY, 1);
}
}
}

View File

@@ -407,7 +407,7 @@ namespace Barotrauma
ToolTip = TextManager.Get(connection.LevelData.IsBeaconActive ? "BeaconStationActiveTooltip" : "BeaconStationInactiveTooltip")
};
new GUITextBlock(new RectTransform(Vector2.One, beaconStationContent.RectTransform),
TextManager.Get("submarinetype.beaconstation"), font: GUI.SubHeadingFont, textAlignment: Alignment.CenterLeft)
TextManager.Get("submarinetype.beaconstation", fallBackTag: "beaconstationsonarlabel"), font: GUI.SubHeadingFont, textAlignment: Alignment.CenterLeft)
{
Padding = Vector4.Zero,
ToolTip = icon.ToolTip

View File

@@ -78,7 +78,7 @@ namespace Barotrauma
private readonly GUIFrame playerInfoContainer;
private GUILayoutGroup infoContainer;
private GUITextBlock changesPendingText;
private GUIComponent changesPendingText;
public GUIButton PlayerFrame;
private readonly GUIComponent subPreviewContainer;
@@ -417,7 +417,7 @@ namespace Barotrauma
{
if (!(FileTransferFrame.UserData is FileReceiver.FileTransferIn transfer)) { return false; }
GameMain.Client?.CancelFileTransfer(transfer);
GameMain.Client.FileReceiver.StopTransfer(transfer);
GameMain.Client?.FileReceiver.StopTransfer(transfer);
return true;
}
};
@@ -1632,9 +1632,17 @@ namespace Barotrauma
private void CreateChangesPendingText()
{
if (changesPendingText != null || infoContainer == null) return;
changesPendingText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.065f), infoContainer.Parent.RectTransform, Anchor.BottomCenter, Pivot.TopCenter) { RelativeOffset = new Vector2(0f, -0.065f) },
TextManager.Get("tabmenu.characterchangespending"), textColor: GUI.Style.Orange, textAlignment: Alignment.Center, style: null) { IgnoreLayoutGroups = true };
if (changesPendingText != null || infoContainer == null) { return; }
changesPendingText = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.065f), infoContainer.Parent.Parent.RectTransform, Anchor.BottomCenter, Pivot.TopCenter) { RelativeOffset = new Vector2(0f, -0.03f) },
style: "OuterGlow")
{
Color = Color.Black,
IgnoreLayoutGroups = true
};
var text = new GUITextBlock(new RectTransform(Vector2.One, changesPendingText.RectTransform, Anchor.Center),
TextManager.Get("tabmenu.characterchangespending"), textColor: GUI.Style.Orange, textAlignment: Alignment.Center, style: null);
changesPendingText.RectTransform.MinSize = new Point((int)(text.TextSize.X * 1.2f), (int)(text.TextSize.Y * 2.0f));
}
private void CreateJobVariantTooltip(JobPrefab jobPrefab, int variant, GUIComponent parentSlot)

View File

@@ -189,7 +189,7 @@ namespace Barotrauma
var particlePrefabs = GameMain.ParticleManager.GetPrefabList();
foreach (ParticlePrefab particlePrefab in particlePrefabs)
{
var prefabText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), prefabList.Content.RectTransform) { MinSize = new Point(0, 20) },
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), prefabList.Content.RectTransform) { MinSize = new Point(0, 20) },
particlePrefab.DisplayName)
{
Padding = Vector4.Zero,
@@ -231,6 +231,7 @@ namespace Barotrauma
private void SerializeAll()
{
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
foreach (ContentFile configFile in GameMain.Instance.GetFilesOfType(ContentType.Particles))
{
XDocument doc = XMLExtensions.TryLoadXml(configFile.Path);
@@ -259,6 +260,7 @@ namespace Barotrauma
writer.Flush();
}
}
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
}
private void SerializeToClipboard(ParticlePrefab prefab)

View File

@@ -41,7 +41,9 @@ namespace Barotrauma
private GUIFrame workshopDownloadsFrame = null;
private Steamworks.Ugc.Item? currentlyDownloadingWorkshopItem = null;
private Dictionary<ulong, Steamworks.Ugc.Item?> pendingWorkshopDownloads = null;
private string autoConnectName; private string autoConnectEndpoint;
private string autoConnectName;
public string AutoConnectEndpoint { get; private set; }
public string LastAutoConnectEndpoint;
private enum TernaryOption
{
@@ -1037,7 +1039,7 @@ namespace Barotrauma
}
}
if (currentlyDownloadingWorkshopItem?.IsInstalled ?? true)
if (currentlyDownloadingWorkshopItem == null)
{
if (pendingWorkshopDownloads?.Any() ?? false)
{
@@ -1046,15 +1048,19 @@ namespace Barotrauma
{
ulong itemId = item.Value.Id;
currentlyDownloadingWorkshopItem = item;
SteamManager.SubscribeToWorkshopItem(itemId, () =>
SteamManager.ForceRedownload(item.Value.Id, () =>
{
if (!(item?.IsSubscribed ?? false))
{
TaskPool.Add("SubscribeToServerMod", item?.Subscribe(), (t) => { });
}
pendingWorkshopDownloads.Remove(itemId);
currentlyDownloadingWorkshopItem = null;
if (SteamManager.CheckWorkshopItemInstalled(item))
{
SteamManager.UninstallWorkshopItem(item, false, out _);
}
if (SteamManager.InstallWorkshopItem(item, out string errorMsg, enableContentPackage: false, suppressInstallNotif: true))
{
workshopDownloadsFrame?.FindChild((c) => c.UserData is ulong l && l == itemId, true)?.Flash(GUI.Style.Green);
@@ -1067,10 +1073,11 @@ namespace Barotrauma
});
}
}
else if (!string.IsNullOrEmpty(autoConnectEndpoint))
else if (!string.IsNullOrEmpty(AutoConnectEndpoint))
{
JoinServer(autoConnectEndpoint, autoConnectName);
autoConnectEndpoint = null;
LastAutoConnectEndpoint = AutoConnectEndpoint;
JoinServer(AutoConnectEndpoint, autoConnectName);
AutoConnectEndpoint = null;
}
}
}
@@ -2132,9 +2139,10 @@ namespace Barotrauma
if (workshopDownloadsFrame != null) { return; }
int rowCount = ids.Count() + 2;
autoConnectName = serverName; autoConnectEndpoint = endPointString;
autoConnectName = serverName; AutoConnectEndpoint = endPointString;
workshopDownloadsFrame = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas), null, Color.Black * 0.5f);
currentlyDownloadingWorkshopItem = null;
pendingWorkshopDownloads = new Dictionary<ulong, Steamworks.Ugc.Item?>();
var innerFrame = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.1f + 0.03f * rowCount), workshopDownloadsFrame.RectTransform, Anchor.Center, Pivot.Center));
@@ -2191,9 +2199,11 @@ namespace Barotrauma
{
OnClicked = (btn, obj) =>
{
autoConnectEndpoint = null;
AutoConnectEndpoint = null;
LastAutoConnectEndpoint = null;
autoConnectName = null;
pendingWorkshopDownloads.Clear();
currentlyDownloadingWorkshopItem = null;
workshopDownloadsFrame = null;
return true;
}

View File

@@ -682,12 +682,21 @@ namespace Barotrauma
try
{
bool reselect = GameMain.Config.AllEnabledPackages.Any(cp => cp.SteamWorkshopId != 0 && cp.SteamWorkshopId == item?.Id);
if (!SteamManager.UninstallWorkshopItem(item, false, out string errorMsg) ||
!SteamManager.InstallWorkshopItem(item, out errorMsg, reselect, true))
if (!SteamManager.UninstallWorkshopItem(item, false, out string errorMsg))
{
DebugConsole.ThrowError($"Failed to reinstall \"{item?.Title}\": {errorMsg}", null, true);
elem.Flash(GUI.Style.Red);
return true;
}
SteamManager.ForceRedownload(item?.Id ?? 0, () =>
{
if (!SteamManager.InstallWorkshopItem(item, out string errorMsg, reselect, true))
{
DebugConsole.ThrowError($"Failed to reinstall \"{item?.Title}\": {errorMsg}", null, true);
elem.Flash(GUI.Style.Red);
}
});
}
catch (Exception e)
{

View File

@@ -2815,13 +2815,17 @@ namespace Barotrauma
foreach (GUIComponent child in categorizedEntityList.Content.Children)
{
child.Visible = !entityCategory.HasValue || (MapEntityCategory)child.UserData == entityCategory;
if (child.Visible && dummyCharacter?.SelectedConstruction?.OwnInventory != null)
var innerList = child.GetChild<GUIListBox>();
foreach (GUIComponent grandChild in innerList.Content.Children)
{
child.Visible = child.UserData is MapEntityPrefab item && IsItemPrefab(item);
grandChild.Visible = true;
}
}
if (!string.IsNullOrEmpty(entityFilterBox.Text)) { FilterEntities(entityFilterBox.Text); }
if (!string.IsNullOrEmpty(entityFilterBox.Text) || dummyCharacter?.SelectedConstruction?.OwnInventory != null)
{
FilterEntities(entityFilterBox.Text);
}
categorizedEntityList.UpdateScrollBarSize();
categorizedEntityList.BarScroll = 0.0f;
@@ -2831,7 +2835,7 @@ namespace Barotrauma
private void FilterEntities(string filter)
{
if (string.IsNullOrWhiteSpace(filter))
if (string.IsNullOrWhiteSpace(filter) && dummyCharacter?.SelectedConstruction?.OwnInventory == null)
{
allEntityList.Visible = false;
categorizedEntityList.Visible = true;
@@ -2844,10 +2848,6 @@ namespace Barotrauma
foreach (GUIComponent grandChild in innerList.Content.Children)
{
grandChild.Visible = ((MapEntityPrefab)grandChild.UserData).Name.ToLower().Contains(filter);
if (grandChild.Visible && dummyCharacter?.SelectedConstruction?.OwnInventory != null)
{
grandChild.Visible = grandChild.UserData is MapEntityPrefab item && IsItemPrefab(item);
}
}
};
categorizedEntityList.UpdateScrollBarSize();

View File

@@ -1,5 +1,6 @@
using Barotrauma.IO;
using Barotrauma.Networking;
using Concentus.Structs;
using Microsoft.Xna.Framework;
using OpenAL;
using System;
@@ -30,6 +31,8 @@ namespace Barotrauma.Sounds
private SoundChannel soundChannel;
private OpusDecoder decoder;
public bool UseRadioFilter;
public bool UseMuffleFilter;
@@ -65,7 +68,7 @@ namespace Barotrauma.Sounds
public VoipSound(string name, SoundManager owner, VoipQueue q) : base(owner, $"VoIP ({name})", true, true, getFullPath: false)
{
VoipConfig.SetupEncoding();
decoder = VoipConfig.CreateDecoder();
ALFormat = Al.FormatMono16;
SampleRate = VoipConfig.FREQUENCY;
@@ -152,7 +155,7 @@ namespace Barotrauma.Sounds
{
if (compressedSize > 0)
{
VoipConfig.Decoder.Decode(compressedBuffer, 0, compressedSize, buffer, 0, VoipConfig.BUFFER_SIZE);
decoder.Decode(compressedBuffer, 0, compressedSize, buffer, 0, VoipConfig.BUFFER_SIZE);
bufferID++;
return VoipConfig.BUFFER_SIZE;
}

View File

@@ -5,6 +5,7 @@ using Barotrauma.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Linq;
using System.Globalization;
namespace Barotrauma
{
@@ -217,7 +218,7 @@ namespace Barotrauma
$"<PersonalityTrait " +
$"{GetVariable("name", split[1])}" +
$"{GetVariable("alloweddialogtags", string.Join(",", NPCPersonalityTrait.List[i].AllowedDialogTags))}" +
$"{GetVariable("commonness", NPCPersonalityTrait.List[i].Commonness.ToString())}/>");
$"{GetVariable("commonness", NPCPersonalityTrait.List[i].Commonness.ToString(CultureInfo.InvariantCulture))}/>");
}
xmlContent.Add(string.Empty);

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product>
<Version>0.13.0.11</Version>
<Version>0.1300.1.11</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product>
<Version>0.13.0.11</Version>
<Version>0.1300.1.11</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>

View File

@@ -61,3 +61,9 @@
/processorParam:DebugMode=Auto
/build:watershader.fx
#begin grainshader.fx
/importer:EffectImporter
/processor:EffectProcessor
/processorParam:DebugMode=Auto
/build:grainshader.fx

View File

@@ -61,3 +61,9 @@
/processorParam:DebugMode=Auto
/build:watershader_opengl.fx
#begin grainshader_opengl.fx
/importer:EffectImporter
/processor:EffectProcessor
/processorParam:DebugMode=Auto
/build:grainshader_opengl.fx

View File

@@ -0,0 +1,28 @@
// vim:ft=hlsl
//float4 baseColor;
float seed;
float nrand(float2 uv)
{
return frac(sin(dot(uv, float2(12.9898, 78.233) * seed)) * 43758.5453);
}
float4 grain(float4 position : SV_POSITION, float4 clr : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
{
float4 baseColor = { 1, 1, 1, 0.25 };
float4 color = baseColor * nrand(texCoord);
float2 center = { 0.5, 0.5 };
float2 diff = texCoord - center;
float alpha = diff.x * diff.x + diff.y * diff.y;
color.a = alpha;
return clr * color;
}
technique Grain
{
pass Pass1
{
PixelShader = compile ps_4_0_level_9_1 grain();
}
}

View File

@@ -0,0 +1,28 @@
// vim:ft=hlsl
//float4 baseColor;
float seed;
float nrand(float2 uv)
{
return frac(sin(dot(uv, float2(12.9898, 78.233) * seed)) * 43758.5453);
}
float4 grain(float4 position : SV_POSITION, float4 clr : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
{
float4 baseColor = { 1, 1, 1, 0.25 };
float4 color = baseColor * nrand(texCoord);
float2 center = { 0.5, 0.5 };
float2 diff = texCoord - center;
float alpha = diff.x * diff.x + diff.y * diff.y;
color.a = alpha;
return clr * color;
}
technique Grain
{
pass Pass1
{
PixelShader = compile ps_3_0 grain();
}
}

View File

@@ -26,6 +26,8 @@ sampler TextureSampler : register (s0) = sampler_state { Texture = <xTexture>; }
Texture2D xLosTexture;
sampler LosSampler = sampler_state { Texture = <xLosTexture>; };
float xLosAlpha;
float4 xColor;
float4 mainPS(VertexShaderOutput input) : COLOR0
@@ -39,7 +41,7 @@ float4 mainPS(VertexShaderOutput input) : COLOR0
sampleColor.r * xColor.r,
sampleColor.g * xColor.g,
sampleColor.b * xColor.b,
obscureAmount);
obscureAmount * xLosAlpha);
return outColor;
}

View File

@@ -26,6 +26,8 @@ sampler TextureSampler : register (s0) = sampler_state { Texture = <xTexture>; }
Texture xLosTexture;
sampler LosSampler = sampler_state { Texture = <xLosTexture>; };
float xLosAlpha;
float4 xColor;
float4 mainPS(VertexShaderOutput input) : COLOR0
@@ -39,7 +41,7 @@ float4 mainPS(VertexShaderOutput input) : COLOR0
sampleColor.r * xColor.r,
sampleColor.g * xColor.g,
sampleColor.b * xColor.b,
obscureAmount);
obscureAmount * xLosAlpha);
return outColor;
}

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product>
<Version>0.13.0.11</Version>
<Version>0.1300.1.11</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product>
<Version>0.13.0.11</Version>
<Version>0.1300.1.11</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName>

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product>
<Version>0.13.0.11</Version>
<Version>0.1300.1.11</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName>

View File

@@ -101,9 +101,7 @@ namespace Barotrauma.Networking
partial void InitProjSpecific()
{
var jobs = JobPrefab.Prefabs.ToList();
// TODO: modding support?
JobPreferences = new List<Pair<JobPrefab, int>>(jobs.GetRange(0, Math.Min(jobs.Count, 3)).Select(j => new Pair<JobPrefab, int>(j, 0)));
JobPreferences = new List<Pair<JobPrefab, int>>();
VoipQueue = new VoipQueue(ID, true, true);
GameMain.Server.VoipServer.RegisterQueue(VoipQueue);

View File

@@ -3482,8 +3482,6 @@ namespace Barotrauma.Networking
sender.CharacterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, sender.Name);
sender.CharacterInfo.RecreateHead(headSpriteId, race, gender, hairIndex, beardIndex, moustacheIndex, faceAttachmentIndex);
//if the client didn't provide job preferences, we'll use the preferences that are randomly assigned in the Client constructor
Debug.Assert(sender.JobPreferences.Count > 0);
if (jobPreferences.Count > 0)
{
sender.JobPreferences = jobPreferences;
@@ -3534,7 +3532,7 @@ namespace Barotrauma.Networking
for (int i = unassigned.Count - 1; i >= 0; i--)
{
if (unassigned[i].JobPreferences.Count == 0) { continue; }
if (!unassigned[i].JobPreferences[0].First.AllowAlways) { continue; }
if (!unassigned[i].JobPreferences.Any() || !unassigned[i].JobPreferences[0].First.AllowAlways) { continue; }
unassigned[i].AssignedJob = unassigned[i].JobPreferences[0];
unassigned.RemoveAt(i);
}

View File

@@ -253,6 +253,8 @@ namespace Barotrauma.Networking
outMsg.Write(mpContentPackages[i].Name);
outMsg.Write(mpContentPackages[i].MD5hash.Hash);
outMsg.Write(mpContentPackages[i].SteamWorkshopId);
UInt32 installTimeDiffSeconds = (UInt32)((mpContentPackages[i].InstallTime ?? DateTime.UtcNow) - DateTime.UtcNow).TotalSeconds;
outMsg.Write(installTimeDiffSeconds);
}
break;
case ConnectionInitialization.Password:

View File

@@ -205,6 +205,7 @@ namespace Barotrauma.Networking
GameMain.Server.CreateEntityEvent(this);
RespawnCountdownStarted = false;
ReturnCountdownStarted = false;
}
}

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product>
<Version>0.13.0.11</Version>
<Version>0.1300.1.11</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName>

View File

@@ -245,6 +245,12 @@ namespace Barotrauma
{
IsCompleted = true;
}
else if (Enemy.IsKnockedDown &&
!objectiveManager.IsCurrentObjective<AIObjectiveFightIntruders>() &&
!HumanAIController.HasItem(character, "handlocker", out _, requireEquipped: false))
{
IsCompleted = true;
}
break;
}
}
@@ -738,7 +744,7 @@ namespace Barotrauma
},
onAbandon: () => Abandon = true);
if (followTargetObjective == null) { return; }
if (Mode == CombatMode.Arrest && (Enemy.Stun > 1 || Enemy.IsKnockedDown))
if (Mode == CombatMode.Arrest && Enemy.IsKnockedDown)
{
if (HumanAIController.HasItem(character, "handlocker", out _))
{
@@ -786,6 +792,23 @@ namespace Barotrauma
private void OnArrestTargetReached()
{
if (!Enemy.IsKnockedDown)
{
RemoveFollowTarget();
return;
}
if (character.TeamID == CharacterTeamType.FriendlyNPC)
{
// Confiscate stolen goods.
foreach (var item in Enemy.Inventory.AllItemsMod)
{
if (item.StolenDuringRound)
{
item.Drop(character);
character.Inventory.TryPutItem(item, character, CharacterInventory.anySlot);
}
}
}
if (HumanAIController.HasItem(character, "handlocker", out IEnumerable<Item> matchingItems) && !Enemy.IsUnconscious && Enemy.IsKnockedDown && character.CanInteractWith(Enemy))
{
var handCuffs = matchingItems.First();
@@ -794,20 +817,18 @@ namespace Barotrauma
#if DEBUG
DebugConsole.NewMessage($"{character.Name}: Failed to handcuff the target.", Color.Red);
#endif
}
// Confiscate stolen goods.
foreach (var item in Enemy.Inventory.AllItemsMod)
{
if (item == handCuffs) { continue; }
if (item.StolenDuringRound)
if (objectiveManager.IsCurrentObjective<AIObjectiveFightIntruders>())
{
item.Drop(character);
character.Inventory.TryPutItem(item, character, CharacterInventory.anySlot);
Abandon = true;
return;
}
}
character.Speak(TextManager.Get("DialogTargetArrested"), null, 3.0f, "targetarrested", 30.0f);
}
IsCompleted = true;
if (!objectiveManager.IsCurrentObjective<AIObjectiveFightIntruders>())
{
IsCompleted = true;
}
}
/// <summary>
@@ -941,14 +962,6 @@ namespace Barotrauma
return;
}
if (reloadTimer > 0) { return; }
if (Mode == CombatMode.Arrest)
{
// If the target is arrested or if it's stunned and we can't lock the target up, consider the objective done.
if (Enemy.IsKnockedDown && !HumanAIController.HasItem(character, "handlocker", out _, requireEquipped: false) || HumanAIController.HasItem(Enemy, "handlocker", out _, requireEquipped: true))
{
IsCompleted = true;
}
}
if (holdFireCondition != null && holdFireCondition()) { return; }
float sqrDist = Vector2.DistanceSquared(character.Position, Enemy.Position);
if (WeaponComponent is MeleeWeapon meleeWeapon)

View File

@@ -334,9 +334,15 @@ namespace Barotrauma
return (int)Math.Round(MathHelper.Lerp(minValue, maxValue, Level.Loaded.Difficulty * 0.01f * Config.AgentSpawnCountDifficultyMultiplier));
}
private float GetSpawnTime() =>
Math.Max(Config.AgentSpawnDelay * Rand.Range(Config.AgentSpawnDelayRandomFactor, 1 + Config.AgentSpawnDelayRandomFactor)
/ (Math.Max(Level.Loaded.Difficulty, 1) * 0.01f * Config.AgentSpawnDelayDifficultyMultiplier), Config.AgentSpawnDelay);
private float GetSpawnTime()
{
float randomFactor = Config.AgentSpawnDelayRandomFactor;
float delay = Config.AgentSpawnDelay;
float min = delay;
float max = delay * 6;
float t = Level.Loaded.Difficulty * Config.AgentSpawnDelayDifficultyMultiplier * Rand.Range(1 - randomFactor, 1 + randomFactor);
return MathHelper.Lerp(max, min, MathUtils.InverseLerp(0, 100, t));
}
void UpdateReinforcements(float deltaTime)
{

View File

@@ -3414,7 +3414,7 @@ namespace Barotrauma
/// Is the character knocked down regardless whether the technical state is dead, unconcious, paralyzed, or stunned.
/// With stunning, the parameter uses a half a second delay before the character is treated as knocked down. The purpose of this is to ignore minor stunning. If you don't want to to ignore any stun, use the Stun property.
/// </summary>
public bool IsKnockedDown => IsDead || IsIncapacitated || CharacterHealth.StunTimer > 0.5f;
public bool IsKnockedDown => IsDead || IsIncapacitated || CharacterHealth.StunTimer > 0.5f || IsRagdolled;
public void SetStun(float newStun, bool allowStunDecrease = false, bool isNetworkMessage = false)
{

View File

@@ -131,7 +131,12 @@ namespace Barotrauma
public static string GetFolder(XDocument doc, string filePath)
{
var folder = doc.Root?.Element("animations")?.GetAttributeString("folder", string.Empty);
var root = doc.Root;
if (root?.IsOverride() ?? false)
{
root = root.FirstElement();
}
var folder = root?.Element("animations")?.GetAttributeString("folder", string.Empty);
if (string.IsNullOrEmpty(folder) || folder.Equals("default", StringComparison.OrdinalIgnoreCase))
{
folder = Path.Combine(Path.GetDirectoryName(filePath), "Animations");

View File

@@ -105,7 +105,12 @@ namespace Barotrauma
public static string GetFolder(XDocument doc, string filePath)
{
var folder = doc.Root?.Element("ragdolls")?.GetAttributeString("folder", string.Empty);
var root = doc.Root;
if (root?.IsOverride() ?? false)
{
root = root.FirstElement();
}
var folder = root?.Element("ragdolls")?.GetAttributeString("folder", string.Empty);
if (string.IsNullOrEmpty(folder) || folder.Equals("default", StringComparison.OrdinalIgnoreCase))
{
folder = Path.Combine(Path.GetDirectoryName(filePath), "Ragdolls") + Path.DirectorySeparatorChar;

View File

@@ -23,9 +23,9 @@ namespace Barotrauma
{
if (isFinished) { return; }
if (!string.IsNullOrWhiteSpace(Tag) && ParentEvent.Targets.ContainsKey(Tag))
if (!string.IsNullOrWhiteSpace(Tag))
{
ParentEvent.Targets.Remove(Tag);
ParentEvent.RemoveTag(Tag);
}
isFinished = true;
}

View File

@@ -42,6 +42,11 @@ namespace Barotrauma
}
if (requiredDeliveryAmount == 0) { requiredDeliveryAmount = items.Count; }
if (requiredDeliveryAmount > items.Count)
{
DebugConsole.AddWarning($"Error in mission \"{Prefab.Identifier}\". Required delivery amount is {requiredDeliveryAmount} but there's only {items.Count} items to deliver.");
requiredDeliveryAmount = items.Count;
}
}
private void LoadItemAsChild(XElement element, Item parent)

View File

@@ -138,6 +138,14 @@ namespace Barotrauma
return targetsToReturn;
}
public void RemoveTag(string tag)
{
if (string.IsNullOrWhiteSpace(tag)) { return; }
if (Targets.ContainsKey(tag)) { Targets.Remove(tag); }
if (cachedTargets.ContainsKey(tag)) { cachedTargets.Remove(tag); }
if (targetPredicates.ContainsKey(tag)) { targetPredicates.Remove(tag); }
}
public override void Update(float deltaTime)
{
int botCount = 0;

View File

@@ -0,0 +1,16 @@
namespace Barotrauma.Extensions
{
public static class StructExtensions
{
public static bool TryGetValue<T>(this T? nullableStruct, out T nonNullable) where T : struct
{
if (nullableStruct.HasValue)
{
nonNullable = nullableStruct.Value;
return true;
}
nonNullable = default(T);
return false;
}
}
}

View File

@@ -265,7 +265,7 @@ namespace Barotrauma
if (beaconMissionPrefabs.Any())
{
Random rand = new MTRandom(ToolBox.StringToInt(levelData.Seed));
var beaconMissionPrefab = beaconMissionPrefabs.GetRandom(rand);
var beaconMissionPrefab = ToolBox.SelectWeightedRandom(beaconMissionPrefabs, beaconMissionPrefabs.Select(p => (float)p.Commonness).ToList(), rand);
if (!Missions.Any(m => m.Prefab.Type == beaconMissionPrefab.Type))
{
extraMissions.Add(beaconMissionPrefab.Instantiate(Map.SelectedConnection.Locations));
@@ -282,7 +282,7 @@ namespace Barotrauma
else
{
Random rand = new MTRandom(ToolBox.StringToInt(levelData.Seed));
var huntingGroundsMissionPrefab = huntingGroundsMissionPrefabs.GetRandom(rand);
var huntingGroundsMissionPrefab = ToolBox.SelectWeightedRandom(huntingGroundsMissionPrefabs, huntingGroundsMissionPrefabs.Select(p => (float)p.Commonness).ToList(), rand);
if (!Missions.Any(m => m.Prefab.Tags.Any(t => t.Equals("huntinggrounds", StringComparison.OrdinalIgnoreCase))))
{
extraMissions.Add(huntingGroundsMissionPrefab.Instantiate(Map.SelectedConnection.Locations));
@@ -613,6 +613,13 @@ namespace Barotrauma
public void EndCampaign()
{
foreach (Character c in Character.CharacterList)
{
if (c.IsOnPlayerTeam)
{
c.CharacterHealth.RemoveAllAfflictions();
}
}
foreach (LocationConnection connection in Map.Connections)
{
connection.Difficulty = MathHelper.Lerp(connection.Difficulty, 100.0f, 0.25f);

View File

@@ -32,6 +32,8 @@ namespace Barotrauma
public Level Level { get; private set; }
public LevelData LevelData { get; private set; }
public bool MirrorLevel { get; private set; }
public Map Map
{
get
@@ -318,6 +320,7 @@ namespace Barotrauma
public void StartRound(LevelData levelData, bool mirrorLevel = false, SubmarineInfo startOutpost = null, SubmarineInfo endOutpost = null)
{
MirrorLevel = mirrorLevel;
if (SubmarineInfo == null)
{
DebugConsole.ThrowError("Couldn't start game session, submarine not selected.");
@@ -583,6 +586,7 @@ namespace Barotrauma
if (ls.Sub == null || ls.Submarine != Submarine) { continue; }
if (!ls.LoadSub || ls.Sub.DockedTo.Contains(Submarine)) { continue; }
if (Submarine.Info.LeftBehindDockingPortIDs.Contains(ls.OriginalLinkedToID)) { continue; }
if (ls.Sub.Info.SubmarineElement.Attribute("location") != null) { continue; }
ls.Sub.SetPosition(ls.Sub.WorldPosition + (Submarine.WorldPosition - originalSubPos));
}
}
@@ -743,7 +747,8 @@ namespace Barotrauma
doc.Root.Add(new XAttribute("savetime", ToolBox.Epoch.NowLocal));
doc.Root.Add(new XAttribute("version", GameMain.Version));
doc.Root.Add(new XAttribute("submarine", SubmarineInfo == null ? "" : SubmarineInfo.Name));
var submarineInfo = Campaign?.PendingSubmarineSwitch ?? SubmarineInfo;
doc.Root.Add(new XAttribute("submarine", submarineInfo == null ? "" : submarineInfo.Name));
if (OwnedSubmarines != null)
{
List<string> ownedSubmarineNames = new List<string>();

View File

@@ -311,8 +311,6 @@ namespace Barotrauma
public bool ModBreakerMode { get; set; }
#endif
private System.IO.FileSystemWatcher modsFolderWatcher;
private static int ContentFileLoadOrder(ContentFile a)
{
switch (a.Type)
@@ -805,87 +803,6 @@ namespace Barotrauma
}
LoadPlayerConfig();
modsFolderWatcher = new System.IO.FileSystemWatcher("Mods");
modsFolderWatcher.Filter = "*";
modsFolderWatcher.NotifyFilter = System.IO.NotifyFilters.LastWrite | System.IO.NotifyFilters.FileName | System.IO.NotifyFilters.DirectoryName;
modsFolderWatcher.Created += OnModFolderUpdate;
modsFolderWatcher.Deleted += OnModFolderUpdate;
modsFolderWatcher.Renamed += OnModFolderUpdate;
modsFolderWatcher.EnableRaisingEvents = true;
}
private void OnModFolderUpdate(object sender, System.IO.FileSystemEventArgs e)
{
if (SuppressModFolderWatcher || (GameMain.NetworkMember?.IsClient ?? false)) { return; }
switch (e.ChangeType)
{
case System.IO.WatcherChangeTypes.Created:
{
string cpPath = Path.GetFullPath(Path.Combine(e.FullPath, Steam.SteamManager.MetadataFileName)).CleanUpPath();
if (File.Exists(cpPath) &&
!ContentPackage.AllPackages.Any(cp => Path.GetFullPath(cp.Path).CleanUpPath() == cpPath))
{
var newPackage = new ContentPackage(cpPath);
if (!newPackage.IsCorrupt) { ContentPackage.AddPackage(newPackage); }
}
}
break;
case System.IO.WatcherChangeTypes.Deleted:
{
string cpPath = Path.GetFullPath(Path.Combine(e.FullPath, Steam.SteamManager.MetadataFileName)).CleanUpPath();
var toRemove = ContentPackage.RegularPackages.Where(cp => Path.GetFullPath(cp.Path).CleanUpPath() == cpPath).ToList();
foreach (var cp in toRemove)
{
if (enabledRegularPackages.Contains(cp)) { DisableRegularPackage(cp); }
}
toRemove.AddRange(ContentPackage.CorePackages.Where(cp => Path.GetFullPath(cp.Path).CleanUpPath() == cpPath));
bool reselectCore = false;
foreach (var cp in toRemove)
{
ContentPackage.RemovePackage(cp);
if (cp.IsCorePackage)
{
reselectCore = true;
}
}
if (reselectCore) { AutoSelectCorePackage(null); }
}
break;
case System.IO.WatcherChangeTypes.Renamed:
{
System.IO.RenamedEventArgs renameArgs = e as System.IO.RenamedEventArgs;
string cpPath = Path.GetFullPath(Path.Combine(e.FullPath, Steam.SteamManager.MetadataFileName)).CleanUpPath();
var toRemove = ContentPackage.RegularPackages.Where(cp => Path.GetFullPath(cp.Path).CleanUpPath() == cpPath).ToList();
foreach (var cp in toRemove)
{
if (enabledRegularPackages.Contains(cp)) { DisableRegularPackage(cp); }
}
toRemove.AddRange(ContentPackage.CorePackages.Where(cp => Path.GetFullPath(cp.Path).CleanUpPath() == cpPath));
bool reselectCore = false;
foreach (var cp in toRemove)
{
ContentPackage.RemovePackage(cp);
if (cp.IsCorePackage)
{
reselectCore = true;
}
}
cpPath = Path.GetFullPath(Path.Combine(renameArgs.FullPath, Steam.SteamManager.MetadataFileName)).CleanUpPath();
if (File.Exists(cpPath) &&
!ContentPackage.AllPackages.Any(cp => Path.GetFullPath(cp.Path).CleanUpPath() == cpPath))
{
var newPackage = new ContentPackage(cpPath);
if (!newPackage.IsCorrupt) { ContentPackage.AddPackage(newPackage); }
}
if (reselectCore) { AutoSelectCorePackage(null); }
}
break;
}
}
private void LoadDefaultConfig(bool setLanguage = true, bool loadContentPackages = true)

View File

@@ -37,7 +37,7 @@ namespace Barotrauma.Items.Components
: base(item,element)
{
}
public override bool Use(float deltaTime, Character character = null)
{
if (character == null || character.Removed) return false;
@@ -61,7 +61,7 @@ namespace Barotrauma.Items.Components
Vector2 propulsion = dir * Force * character.PropulsionSpeedMultiplier;
if (character.AnimController.InWater) character.AnimController.TargetMovement = dir;
if (character.AnimController.InWater && Force > 0.0f) { character.AnimController.TargetMovement = dir; }
foreach (Limb limb in character.AnimController.Limbs)
{

View File

@@ -757,7 +757,7 @@ namespace Barotrauma.Items.Components
bool reducesCondition = false;
foreach (StatusEffect effect in statusEffects)
{
if (broken && effect.type != ActionType.OnBroken) { continue; }
if (broken && !effect.AllowWhenBroken && effect.type != ActionType.OnBroken) { continue; }
if (user != null) { effect.SetUser(user); }
item.ApplyStatusEffect(effect, type, deltaTime, character, targetLimb, useTarget, false, false, worldPosition);
reducesCondition |= effect.ReducesItemCondition();

View File

@@ -153,6 +153,7 @@ namespace Barotrauma.Items.Components
if (IsToggle)
{
item.SendSignal(State ? "1" : "0", "signal_out");
item.SendSignal(State ? "1" : "0", "trigger_out");
}
if (user == null
@@ -263,6 +264,8 @@ namespace Barotrauma.Items.Components
}
}
private double lastUsed;
public override bool Use(float deltaTime, Character activator = null)
{
if (activator != user)
@@ -277,7 +280,22 @@ namespace Barotrauma.Items.Components
return false;
}
item.SendSignal(new Signal("1", sender: user), "trigger_out");
if (IsToggle && (activator == null || lastUsed < Timing.TotalTime - 0.1))
{
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
{
State = !State;
#if SERVER
item.CreateServerEvent(this);
#endif
}
}
else
{
item.SendSignal(new Signal("1", sender: user), "trigger_out");
}
lastUsed = Timing.TotalTime;
ApplyStatusEffects(ActionType.OnUse, 1.0f, activator);

View File

@@ -308,7 +308,15 @@ namespace Barotrauma.Items.Components
if (AutoPilot)
{
UpdateAutoPilot(deltaTime);
TargetVelocity = TargetVelocity.ClampLength(MathHelper.Lerp(AutoPilotMaxSpeed, AIPilotMaxSpeed, userSkill) * 100.0f);
float throttle = 1.0f;
if (controlledSub != null)
{
//if the sub is heading in the correct direction, throttle the speed according to the user's skill
//if it's e.g. sinking due to extra water, don't throttle, but allow emptying up the ballast completely
throttle = MathHelper.Clamp(Vector2.Dot(controlledSub.Velocity, TargetVelocity) / 100.0f, 0.0f, 1.0f);
}
float maxSpeed = MathHelper.Lerp(AutoPilotMaxSpeed, AIPilotMaxSpeed, userSkill) * 100.0f;
TargetVelocity = TargetVelocity.ClampLength(MathHelper.Lerp(100.0f, maxSpeed, throttle));
}
else
{

View File

@@ -372,7 +372,7 @@ namespace Barotrauma.Items.Components
hits = hits.OrderBy(h => h.Fraction).ToList();
foreach (HitscanResult h in hits)
{
item.body.SetTransform(h.Point, rotation);
item.SetTransform(h.Point, rotation);
if (HandleProjectileCollision(h.Fixture, h.Normal, Vector2.Zero))
{
hitSomething = true;

View File

@@ -23,6 +23,17 @@ namespace Barotrauma.Items.Components
}
}
private int maxOutputLength;
[Editable, Serialize(200, false, description: "The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")]
public int MaxOutputLength
{
get { return maxOutputLength; }
set
{
maxOutputLength = Math.Max(value, 0);
}
}
[InGameEditable, Serialize("1", true, description: "The signal sent when the condition is met.", alwaysUseInstanceValues: true)]
public string Output
{
@@ -53,17 +64,6 @@ namespace Barotrauma.Items.Components
}
}
private int maxOutputLength;
[Editable, Serialize(200, false, description: "The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")]
public int MaxOutputLength
{
get { return maxOutputLength; }
set
{
maxOutputLength = Math.Max(value, 0);
}
}
public AndComponent(Item item, XElement element)
: base(item, element)
{

View File

@@ -15,6 +15,17 @@ namespace Barotrauma.Items.Components
//the output is sent if both inputs have received a signal within the timeframe
protected float timeFrame;
private int maxOutputLength;
[Editable, Serialize(200, false, description: "The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")]
public int MaxOutputLength
{
get { return maxOutputLength; }
set
{
maxOutputLength = Math.Max(value, 0);
}
}
[InGameEditable, Serialize("1", true, description: "The signal sent when the condition is met.", alwaysUseInstanceValues: true)]
public string Output
{
@@ -45,17 +56,6 @@ namespace Barotrauma.Items.Components
}
}
private int maxOutputLength;
[Editable, Serialize(200, false, description: "The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")]
public int MaxOutputLength
{
get { return maxOutputLength; }
set
{
maxOutputLength = Math.Max(value, 0);
}
}
[InGameEditable(DecimalCount = 2), Serialize(0.0f, true, description: "The maximum amount of time between the received signals. If set to 0, the signals must be received at the same time.", alwaysUseInstanceValues: true)]
public float TimeFrame
{

View File

@@ -6,6 +6,17 @@ namespace Barotrauma.Items.Components
{
partial class MemoryComponent : ItemComponent, IServerSerializable
{
private int maxValueLength;
[Editable, Serialize(200, false, description: "The maximum length of the stored value. Warning: Large values can lead to large memory usage or networking issues.")]
public int MaxValueLength
{
get { return maxValueLength; }
set
{
maxValueLength = Math.Max(value, 0);
}
}
private string value;
[InGameEditable, Serialize("", true, description: "The currently stored signal the item outputs.", alwaysUseInstanceValues: true)]
@@ -23,17 +34,6 @@ namespace Barotrauma.Items.Components
}
}
private int maxValueLength;
[Editable, Serialize(200, false, description: "The maximum length of the stored value. Warning: Large values can lead to large memory usage or networking issues.")]
public int MaxValueLength
{
get { return maxValueLength; }
set
{
maxValueLength = Math.Max(value, 0);
}
}
protected bool writeable = true;
public MemoryComponent(Item item, XElement element)

View File

@@ -74,6 +74,17 @@ namespace Barotrauma.Items.Components
}
}
private int maxOutputLength;
[Editable, Serialize(200, false, description: "The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")]
public int MaxOutputLength
{
get { return maxOutputLength; }
set
{
maxOutputLength = Math.Max(value, 0);
}
}
private string output;
[InGameEditable, Serialize("1", true, description: "The signal the item outputs when it has detected movement.", alwaysUseInstanceValues: true)]
public string Output
@@ -106,17 +117,6 @@ namespace Barotrauma.Items.Components
}
}
private int maxOutputLength;
[Editable, Serialize(200, false, description: "The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")]
public int MaxOutputLength
{
get { return maxOutputLength; }
set
{
maxOutputLength = Math.Max(value, 0);
}
}
[Editable(DecimalCount = 3), Serialize(0.01f, true, description: "How fast the objects within the detector's range have to be moving (in m/s).", alwaysUseInstanceValues: true)]
public float MinimumVelocity
{

View File

@@ -18,6 +18,17 @@ namespace Barotrauma.Items.Components
private bool nonContinuousOutputSent;
private int maxOutputLength;
[Editable, Serialize(200, false, description: "The maximum length of the output string. Warning: Large values can lead to large memory usage or networking issues.")]
public int MaxOutputLength
{
get { return maxOutputLength; }
set
{
maxOutputLength = Math.Max(value, 0);
}
}
private string output;
[InGameEditable, Serialize("1", true, description: "The signal this item outputs when the received signal matches the regular expression.", alwaysUseInstanceValues: true)]
@@ -61,23 +72,11 @@ namespace Barotrauma.Items.Components
catch
{
item.SendSignal("ERROR", "signal_out");
return;
}
}
}
private int maxOutputLength;
[Editable, Serialize(200, false, description: "The maximum length of the output string. Warning: Large values can lead to large memory usage or networking issues.")]
public int MaxOutputLength
{
get { return maxOutputLength; }
set
{
maxOutputLength = Math.Max(value, 0);
}
}
public RegExFindComponent(Item item, XElement element)
: base(item, element)
{

View File

@@ -5,6 +5,17 @@ namespace Barotrauma.Items.Components
{
class SignalCheckComponent : ItemComponent
{
private int maxOutputLength;
[Editable, Serialize(200, false, description: "The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")]
public int MaxOutputLength
{
get { return maxOutputLength; }
set
{
maxOutputLength = Math.Max(value, 0);
}
}
private string output;
[InGameEditable, Serialize("1", true, description: "The signal this item outputs when the received signal matches the target signal.", alwaysUseInstanceValues: true)]
public string Output
@@ -40,17 +51,6 @@ namespace Barotrauma.Items.Components
[InGameEditable, Serialize("", true, description: "The value to compare the received signals against.", alwaysUseInstanceValues: true)]
public string TargetSignal { get; set; }
private int maxOutputLength;
[Editable, Serialize(200, false, description: "The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")]
public int MaxOutputLength
{
get { return maxOutputLength; }
set
{
maxOutputLength = Math.Max(value, 0);
}
}
public SignalCheckComponent(Item item, XElement element)
: base(item, element)
{

View File

@@ -10,6 +10,17 @@ namespace Barotrauma.Items.Components
private bool fireInRange;
private int maxOutputLength;
[Editable, Serialize(200, false, description: "The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")]
public int MaxOutputLength
{
get { return maxOutputLength; }
set
{
maxOutputLength = Math.Max(value, 0);
}
}
private string output;
[InGameEditable, Serialize("1", true, description: "The signal the item outputs when it has detected a fire.", alwaysUseInstanceValues: true)]
public string Output
@@ -42,17 +53,6 @@ namespace Barotrauma.Items.Components
}
}
private int maxOutputLength;
[Editable, Serialize(200, false, description: "The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")]
public int MaxOutputLength
{
get { return maxOutputLength; }
set
{
maxOutputLength = Math.Max(value, 0);
}
}
public SmokeDetector(Item item, XElement element)
: base(item, element)
{

View File

@@ -12,6 +12,17 @@ namespace Barotrauma.Items.Components
private bool isInWater;
private float stateSwitchDelay;
private int maxOutputLength;
[Editable, Serialize(200, false, description: "The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")]
public int MaxOutputLength
{
get { return maxOutputLength; }
set
{
maxOutputLength = Math.Max(value, 0);
}
}
private string output;
[InGameEditable, Serialize("1", true, description: "The signal the item sends out when it's underwater.", alwaysUseInstanceValues: true)]
public string Output
@@ -44,17 +55,6 @@ namespace Barotrauma.Items.Components
}
}
private int maxOutputLength;
[Editable, Serialize(200, false, description: "The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")]
public int MaxOutputLength
{
get { return maxOutputLength; }
set
{
maxOutputLength = Math.Max(value, 0);
}
}
public WaterDetector(Item item, XElement element)
: base(item, element)
{

View File

@@ -507,7 +507,8 @@ namespace Barotrauma
#if CLIENT
if (visualSlots != null)
{
visualSlots[i]?.ShowBorderHighlight(Color.White, 0.1f, 0.4f);
visualSlots[i].ShowBorderHighlight(Color.White, 0.1f, 0.4f);
if (selectedSlot?.Inventory == this) { selectedSlot.ForceTooltipRefresh = true; }
}
#endif
@@ -625,7 +626,10 @@ namespace Barotrauma
else
{
existingItems.Add(slots[index].FirstOrDefault());
slots[index].RemoveItem(existingItems.First());
for (int j = 0; j < capacity; j++)
{
if (existingItems.Any(existingItem => slots[j].Contains(existingItem))) { slots[j].RemoveItem(existingItems.First()); }
}
}
List<Item> stackedItems = new List<Item>();
@@ -831,7 +835,14 @@ namespace Barotrauma
if (!slots[n].Contains(item)) { continue; }
slots[n].RemoveItem(item);
item.ParentInventory = null;
item.ParentInventory = null;
#if CLIENT
if (visualSlots != null)
{
visualSlots[n].ShowBorderHighlight(Color.White, 0.1f, 0.4f);
if (selectedSlot?.Inventory == this) { selectedSlot.ForceTooltipRefresh = true; }
}
#endif
}
}

View File

@@ -1356,11 +1356,11 @@ namespace Barotrauma
{
if (!isNetworkEvent && checkCondition)
{
if (condition == 0.0f && effect.type != ActionType.OnBroken) return;
if (condition == 0.0f && !effect.AllowWhenBroken && effect.type != ActionType.OnBroken) { return; }
}
if (effect.type != type) return;
if (effect.type != type) { return; }
bool hasTargets = (effect.TargetIdentifiers == null);
bool hasTargets = effect.TargetIdentifiers == null;
targets.Clear();
@@ -2611,7 +2611,7 @@ namespace Barotrauma
foreach (XAttribute attribute in element.Attributes())
{
if (!item.SerializableProperties.TryGetValue(attribute.Name.ToString(), out SerializableProperty property)) continue;
if (!item.SerializableProperties.TryGetValue(attribute.Name.ToString(), out SerializableProperty property)) { continue; }
bool shouldBeLoaded = false;
foreach (var propertyAttribute in property.Attributes.OfType<Serialize>())
{
@@ -2622,7 +2622,31 @@ namespace Barotrauma
}
}
if (shouldBeLoaded) { property.TrySetValue(item, attribute.Value); }
if (shouldBeLoaded)
{
object prevValue = property.GetValue(item);
property.TrySetValue(item, attribute.Value);
//create network events for properties that differ from the prefab values
//(e.g. if a character has an item with modified colors in their inventory)
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer && property.Attributes.OfType<Editable>().Any() &&
(submarine == null || !submarine.Loading ))
{
switch (property.Name)
{
case "Tags":
case "Condition":
case "Description":
//these can be ignored, they're always written in the spawn data
break;
default:
if (!(property.GetValue(item)?.Equals(prevValue) ?? true))
{
GameMain.NetworkMember.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ChangeProperty, property });
}
break;
}
}
}
}
item.ParseLinks(element, idRemap);

View File

@@ -424,13 +424,7 @@ namespace Barotrauma
}
foreach (var edge in cell.Edges)
{
if (!MathUtils.GetLineIntersection(worldPosition, cell.Center, edge.Point1 + cell.Translation, edge.Point2 + cell.Translation, out Vector2 intersection))
{
continue;
}
float wallDist = Vector2.DistanceSquared(worldPosition, intersection);
if (wallDist < worldRange * worldRange)
if (MathUtils.LineSegmentToPointDistanceSquared((edge.Point1 + cell.Translation).ToPoint(), (edge.Point2 + cell.Translation).ToPoint(), worldPosition.ToPoint()) < worldRange * worldRange)
{
destructibleWall.AddDamage(damage, worldPosition);
break;

View File

@@ -501,7 +501,7 @@ namespace Barotrauma
EntityGrids.Add(newGrid);
foreach (Hull hull in hullList)
{
if (hull.Submarine == submarine) newGrid.InsertEntity(hull);
if (hull.Submarine == submarine && !hull.IdFreed) { newGrid.InsertEntity(hull); }
}
return newGrid;
}

View File

@@ -3663,7 +3663,7 @@ namespace Barotrauma
}
//break powered items
foreach (Item item in beaconItems.Where(it => it.Components.Any(c => c is Powered)))
foreach (Item item in beaconItems.Where(it => it.Components.Any(c => c is Powered) && it.Components.Any(c => c is Repairable)))
{
if (item.NonInteractable) { continue; }
if (Rand.Range(0f, 1f, Rand.RandSync.Unsynced) < 0.5f)

View File

@@ -252,6 +252,10 @@ namespace Barotrauma
Vector2 worldPos = saveElement.GetAttributeVector2("worldpos", Vector2.Zero);
if (worldPos != Vector2.Zero)
{
if (GameMain.GameSession != null && GameMain.GameSession.MirrorLevel)
{
worldPos.X = GameMain.GameSession.LevelData.Size.X - worldPos.X;
}
sub.SetPosition(worldPos);
}
else
@@ -417,7 +421,12 @@ namespace Barotrauma
if (leaveBehind)
{
saveElement.SetAttributeValue("location", Level.Loaded.Seed);
saveElement.SetAttributeValue("worldpos", XMLExtensions.Vector2ToString(sub.SubBody.Position));
Vector2 position = sub.SubBody.Position;
if (Level.Loaded.Mirrored)
{
position.X = Level.Loaded.Size.X - position.X;
}
saveElement.SetAttributeValue("worldpos", XMLExtensions.Vector2ToString(position));
}
else
{

View File

@@ -975,7 +975,14 @@ namespace Barotrauma
private void ChangeLocationType(Location location, LocationTypeChange change)
{
string prevName = location.Name;
location.ChangeType(LocationType.List.Find(lt => lt.Identifier.Equals(change.ChangeToType, StringComparison.OrdinalIgnoreCase)));
var newType = LocationType.List.Find(lt => lt.Identifier.Equals(change.ChangeToType, StringComparison.OrdinalIgnoreCase));
if (newType.OutpostTeam != location.Type.OutpostTeam ||
newType.HasOutpost != location.Type.HasOutpost)
{
location.ClearMissions();
}
location.ChangeType(newType);
ChangeLocationTypeProjSpecific(location, prevName, change);
foreach (var requirement in change.Requirements)
{

View File

@@ -146,7 +146,7 @@ namespace Barotrauma
foreach (Hull hull in Hull.hullList)
{
if (hull.Submarine != submarine) { continue; }
if (hull.Submarine != submarine || hull.IdFreed) { continue; }
Rectangle rect = hull.Rect;
farseerBody.CreateRectangle(

View File

@@ -284,6 +284,11 @@ namespace Barotrauma
// Currently only used for OnDamaged. TODO: is there a better, more generic way to do this?
public readonly bool OnlyPlayerTriggered;
/// <summary>
/// Can the StatusEffect be applied when the item applying it is broken
/// </summary>
public readonly bool AllowWhenBroken = false;
public HashSet<string> TargetIdentifiers
{
get { return targetIdentifiers; }
@@ -356,6 +361,7 @@ namespace Barotrauma
OnlyInside = element.GetAttributeBool("onlyinside", false);
OnlyOutside = element.GetAttributeBool("onlyoutside", false);
OnlyPlayerTriggered = element.GetAttributeBool("onlyplayertriggered", false);
AllowWhenBroken = element.GetAttributeBool("allowwhenbroken", false);
Range = element.GetAttributeFloat("range", 0.0f);
Offset = element.GetAttributeVector2("offset", Vector2.Zero);

View File

@@ -1,3 +1,37 @@
---------------------------------------------------------------------------------------------------------
v0.13.1.11
---------------------------------------------------------------------------------------------------------
Changes:
- Adjusted autopilot logic to make it better at keeping the sub afloat when there's extra water on board. The maximum velocity of the autopilot is limited, which previously prevented it from emptying the ballast fully. Now it's only limited if the submarine is heading in the correct direction with enough speed, so if the sub starts sinking due to extra water, the autopilot can compensate and fully empty the ballast.
Fixes:
- Fixes to issues that prevented mods installed from the Workshop from getting automatically updated.
- Fixed inability to drag players who've ragdolled themselves with space bar.
- Fixed status monitor being messed up on mirrored subs that contain shuttles.
- Fixed an issue in the voice chat that caused audio crackling when multiple people were speaking at the same time.
- Fixed inability to place oxygenite tanks in oxygen tank shelves.
- Fixed railgun payloads not exploding.
- Fixed server sometimes assigning players who haven't set any job preferences as the captain, even if someone else wants to be the captain.
- Fixed ancient weapon propelling the character in an incorrect direction when using it underwater.
- Fixed sonar beacons not appearing on the sonar in the sub editor's test mode.
- Fixed "easterbunny" traitor mission failing after the mudraptor hatches.
- Fixed "changes to your character will be applied after the round ends" texts getting drawn behind the tab menu elements.
- Fixed "IsToggle" setting not working on periscopes.
- Fixed "Praise the Honkmother" mission being impossible to complete because it required more items to be delivered than the crate contains.
- Fixed humanhusk's inventory not being visible while controlling one.
- Fixed sub editor's entity list getting cleared when changing the entity category while a container/cabinet is selected.
- Fixed items that have been edited in the sub editor (e.g. recolored clothing) reverting back to default when starting a new campaign round while the item is in a character's inventory.
- Fixed clicking on command interface nodes crashing the game if the key is rebound to primary mouse button.
- Fixed tiling issues in some of the legacy background wall sprites.
- Fixed Spinelings attacking Leucocytes. Spinelings should avoid Leucocytes if they get close.
- Fixed "beacon station" text and "generating preview..." in the submarine preview window not being translated when playing in a language other than English.
Modding:
- Fixed crashing if the current style doesn't define a saving indicator.
- Fixed inability to load ragdoll/animation definitions from mods that override a character.
- Fixed ClearTagAction not properly clearing all the tags assigned by a scripted event.
---------------------------------------------------------------------------------------------------------
v0.13.0.11
---------------------------------------------------------------------------------------------------------

View File

@@ -12,4 +12,9 @@
<PackageProjectUrl>https://github.com/lostromb/concentus</PackageProjectUrl>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
</Project>