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
@@ -3,7 +3,6 @@ using Barotrauma.Items.Components;
using FarseerPhysics; using FarseerPhysics;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -287,6 +287,11 @@ namespace Barotrauma
InputBox.OnDeselected += (gui, Keys) => InputBox.OnDeselected += (gui, Keys) =>
{ {
ChatManager.Clear(); ChatManager.Clear();
if (GUIFrame.IsParentOf(GUI.MouseOn))
{
CloseAfterMessageSent = false;
return;
}
ChatMessage.GetChatMessageCommand(InputBox.Text, out var message); ChatMessage.GetChatMessageCommand(InputBox.Text, out var message);
if (string.IsNullOrEmpty(message)) if (string.IsNullOrEmpty(message))
{ {
@@ -15,7 +15,7 @@ namespace Barotrauma
get { return inventoryTopY; } get { return inventoryTopY; }
set set
{ {
if (value == inventoryTopY) return; if (value == inventoryTopY) { return; }
inventoryTopY = value; inventoryTopY = value;
CreateAreas(); CreateAreas();
} }
@@ -91,8 +91,6 @@ namespace Barotrauma
if (GameMain.Instance != null) if (GameMain.Instance != null)
{ {
GameMain.Instance.ResolutionChanged += CreateAreas; GameMain.Instance.ResolutionChanged += CreateAreas;
#warning TODO: reimplement
//GameSettings.CurrentConfig.OnHUDScaleChanged += CreateAreas;
CreateAreas(); CreateAreas();
CharacterInfo.Init(); CharacterInfo.Init();
} }
@@ -1559,10 +1559,10 @@ namespace Barotrauma
RichString missionReputationString = RichString.Rich(reputationText, wrapMissionText(GUIStyle.Font)); RichString missionReputationString = RichString.Rich(reputationText, wrapMissionText(GUIStyle.Font));
RichString missionDescriptionString = RichString.Rich(descriptionText, wrapMissionText(GUIStyle.Font)); RichString missionDescriptionString = RichString.Rich(descriptionText, wrapMissionText(GUIStyle.Font));
Vector2 missionNameSize = GUIStyle.LargeFont.MeasureString(missionNameString); Vector2 missionNameSize = GUIStyle.LargeFont.MeasureString(missionNameString.SanitizedValue);
Vector2 missionDescriptionSize = GUIStyle.Font.MeasureString(missionDescriptionString); Vector2 missionDescriptionSize = GUIStyle.Font.MeasureString(missionDescriptionString.SanitizedValue);
Vector2 missionRewardSize = GUIStyle.Font.MeasureString(missionRewardString); Vector2 missionRewardSize = GUIStyle.Font.MeasureString(missionRewardString.SanitizedValue);
Vector2 missionReputationSize = GUIStyle.Font.MeasureString(missionReputationString); Vector2 missionReputationSize = GUIStyle.Font.MeasureString(missionReputationString.SanitizedValue);
float ySize = missionNameSize.Y + missionDescriptionSize.Y + missionRewardSize.Y + missionReputationSize.Y + missionTextGroup.AbsoluteSpacing * 4; float ySize = missionNameSize.Y + missionDescriptionSize.Y + missionRewardSize.Y + missionReputationSize.Y + missionTextGroup.AbsoluteSpacing * 4;
bool displayDifficulty = mission.Difficulty.HasValue; bool displayDifficulty = mission.Difficulty.HasValue;
@@ -102,29 +102,11 @@ namespace Barotrauma.Tutorials
radioSpeakerName = TextManager.Get("Tutorial.Radio.Watchman"); radioSpeakerName = TextManager.Get("Tutorial.Radio.Watchman");
GameMain.GameSession.CrewManager.AllowCharacterSwitch = false; GameMain.GameSession.CrewManager.AllowCharacterSwitch = false;
var revolver = FindOrGiveItem(captain, "revolver".ToIdentifier()); foreach (Item item in captain.Inventory.AllItemsMod)
revolver.Unequip(captain);
captain.Inventory.RemoveItem(revolver);
var captainscap =
captain.Inventory.FindItemByIdentifier("captainscap1".ToIdentifier()) ??
captain.Inventory.FindItemByIdentifier("captainscap2".ToIdentifier()) ??
captain.Inventory.FindItemByIdentifier("captainscap3".ToIdentifier());
if (captainscap != null)
{ {
captainscap.Unequip(captain); if (item.HasTag("identitycard") || item.HasTag("headset")) { continue; }
captain.Inventory.RemoveItem(captainscap); item.Unequip(captain);
} captain.Inventory.RemoveItem(item);
var captainsuniform =
captain.Inventory.FindItemByIdentifier("captainsuniform1".ToIdentifier()) ??
captain.Inventory.FindItemByIdentifier("captainsuniform2".ToIdentifier()) ??
captain.Inventory.FindItemByIdentifier("captainsuniform3".ToIdentifier());
if (captainsuniform != null)
{
captainsuniform.Unequip(captain);
captain.Inventory.RemoveItem(captainsuniform);
} }
var steerOrder = OrderPrefab.Prefabs["steer"]; var steerOrder = OrderPrefab.Prefabs["steer"];
@@ -105,21 +105,12 @@ namespace Barotrauma.Tutorials
radioSpeakerName = TextManager.Get("Tutorial.Radio.Speaker"); radioSpeakerName = TextManager.Get("Tutorial.Radio.Speaker");
doctor = Character.Controlled; doctor = Character.Controlled;
var bandages = FindOrGiveItem(doctor, "antibleeding1".ToIdentifier()); foreach (Item item in doctor.Inventory.AllItemsMod)
bandages.Unequip(doctor); {
doctor.Inventory.RemoveItem(bandages); if (item.HasTag("clothing") || item.HasTag("identitycard") || item.HasTag("headset")) { continue; }
item.Unequip(doctor);
var syringegun = FindOrGiveItem(doctor, "syringegun".ToIdentifier()); doctor.Inventory.RemoveItem(item);
syringegun.Unequip(doctor); }
doctor.Inventory.RemoveItem(syringegun);
var antibiotics = FindOrGiveItem(doctor, "antibiotics".ToIdentifier());
antibiotics.Unequip(doctor);
doctor.Inventory.RemoveItem(antibiotics);
var morphine = FindOrGiveItem(doctor, "antidama1".ToIdentifier());
morphine.Unequip(doctor);
doctor.Inventory.RemoveItem(morphine);
doctor_suppliesCabinet = Item.ItemList.Find(i => i.HasTag("doctor_suppliescabinet"))?.GetComponent<ItemContainer>(); doctor_suppliesCabinet = Item.ItemList.Find(i => i.HasTag("doctor_suppliescabinet"))?.GetComponent<ItemContainer>();
doctor_medBayCabinet = Item.ItemList.Find(i => i.HasTag("doctor_medbaycabinet"))?.GetComponent<ItemContainer>(); doctor_medBayCabinet = Item.ItemList.Find(i => i.HasTag("doctor_medbaycabinet"))?.GetComponent<ItemContainer>();
@@ -131,9 +131,12 @@ namespace Barotrauma.Tutorials
radioSpeakerName = TextManager.Get("Tutorial.Radio.Speaker"); radioSpeakerName = TextManager.Get("Tutorial.Radio.Speaker");
engineer = Character.Controlled; engineer = Character.Controlled;
var toolbelt = FindOrGiveItem(engineer, "toolbelt".ToIdentifier()); foreach (Item item in engineer.Inventory.AllItemsMod)
toolbelt.Unequip(engineer); {
engineer.Inventory.RemoveItem(toolbelt); if (item.HasTag("clothing") || item.HasTag("identitycard") || item.HasTag("headset")) { continue; }
item.Unequip(engineer);
engineer.Inventory.RemoveItem(item);
}
var repairOrder = OrderPrefab.Prefabs["repairsystems"]; var repairOrder = OrderPrefab.Prefabs["repairsystems"];
engineer_repairIcon = repairOrder.SymbolSprite; engineer_repairIcon = repairOrder.SymbolSprite;
@@ -160,13 +160,12 @@ namespace Barotrauma.Tutorials
radioSpeakerName = TextManager.Get("Tutorial.Radio.Speaker"); radioSpeakerName = TextManager.Get("Tutorial.Radio.Speaker");
mechanic = Character.Controlled; mechanic = Character.Controlled;
var toolbelt = FindOrGiveItem(mechanic, "toolbelt".ToIdentifier()); foreach (Item item in mechanic.Inventory.AllItemsMod)
toolbelt.Unequip(mechanic); {
mechanic.Inventory.RemoveItem(toolbelt); if (item.HasTag("clothing") || item.HasTag("identitycard") || item.HasTag("headset")) { continue; }
item.Unequip(mechanic);
var crowbar = FindOrGiveItem(mechanic, "crowbar".ToIdentifier()); mechanic.Inventory.RemoveItem(item);
crowbar.Unequip(mechanic); }
mechanic.Inventory.RemoveItem(crowbar);
var repairOrder = OrderPrefab.Prefabs["repairsystems"]; var repairOrder = OrderPrefab.Prefabs["repairsystems"];
mechanic_repairIcon = repairOrder.SymbolSprite; mechanic_repairIcon = repairOrder.SymbolSprite;
@@ -297,7 +296,10 @@ namespace Barotrauma.Tutorials
public override void Update(float deltaTime) public override void Update(float deltaTime)
{ {
mechanic_brokenhull_1.WaterVolume = MathHelper.Clamp(mechanic_brokenhull_1.WaterVolume, 0, mechanic_brokenhull_1.Volume * 0.85f); if (mechanic_brokenhull_1 != null)
{
mechanic_brokenhull_1.WaterVolume = MathHelper.Clamp(mechanic_brokenhull_1.WaterVolume, 0, mechanic_brokenhull_1.Volume * 0.85f);
}
base.Update(deltaTime); base.Update(deltaTime);
} }
@@ -413,7 +415,7 @@ namespace Barotrauma.Tutorials
} }
} while (mechanic_workingPump.FlowPercentage >= 0 || !mechanic_workingPump.IsActive); // Highlight until draining } while (mechanic_workingPump.FlowPercentage >= 0 || !mechanic_workingPump.IsActive); // Highlight until draining
SetHighlight(mechanic_workingPump.Item, false); SetHighlight(mechanic_workingPump.Item, false);
do { yield return null; } while (mechanic_brokenhull_1.WaterPercentage > waterVolumeBeforeOpening); // Unlock door once drained do { yield return null; } while (mechanic_brokenhull_1 != null && mechanic_brokenhull_1.WaterPercentage > waterVolumeBeforeOpening); // Unlock door once drained
RemoveCompletedObjective(3); RemoveCompletedObjective(3);
GameAnalyticsManager.AddDesignEvent("Tutorial:MechanicTutorial:Objective3"); GameAnalyticsManager.AddDesignEvent("Tutorial:MechanicTutorial:Objective3");
@@ -141,36 +141,12 @@ namespace Barotrauma.Tutorials
radioSpeakerName = TextManager.Get("Tutorial.Radio.Speaker"); radioSpeakerName = TextManager.Get("Tutorial.Radio.Speaker");
officer = Character.Controlled; officer = Character.Controlled;
var handcuffs = FindOrGiveItem(officer, "handcuffs".ToIdentifier()); foreach (Item item in officer.Inventory.AllItemsMod)
handcuffs.Unequip(officer); {
officer.Inventory.RemoveItem(handcuffs); if (item.HasTag("clothing") || item.HasTag("identitycard") || item.HasTag("headset")) { continue; }
item.Unequip(officer);
var stunbaton = FindOrGiveItem(officer, "stunbaton".ToIdentifier()); officer.Inventory.RemoveItem(item);
stunbaton.Unequip(officer); }
officer.Inventory.RemoveItem(stunbaton);
var smg = FindOrGiveItem(officer, "smg".ToIdentifier());
smg.Unequip(officer);
officer.Inventory.RemoveItem(smg);
var divingknife = FindOrGiveItem(officer, "divingknife".ToIdentifier());
divingknife.Unequip(officer);
officer.Inventory.RemoveItem(divingknife);
var steroids = FindOrGiveItem(officer, "steroids".ToIdentifier());
steroids.Unequip(officer);
officer.Inventory.RemoveItem(steroids);
var ballistichelmet =
officer.Inventory.FindItemByIdentifier("ballistichelmet1".ToIdentifier()) ??
officer.Inventory.FindItemByIdentifier("ballistichelmet2".ToIdentifier()) ??
FindOrGiveItem(officer, "ballistichelmet3".ToIdentifier());
ballistichelmet.Unequip(officer);
officer.Inventory.RemoveItem(ballistichelmet);
var bodyarmor = FindOrGiveItem(officer, "bodyarmor".ToIdentifier());
bodyarmor.Unequip(officer);
officer.Inventory.RemoveItem(bodyarmor);
var gunOrder = OrderPrefab.Prefabs["operateweapons"]; var gunOrder = OrderPrefab.Prefabs["operateweapons"];
officer_gunIcon = gunOrder.SymbolSprite; officer_gunIcon = gunOrder.SymbolSprite;
@@ -28,7 +28,7 @@ namespace Barotrauma
UserListData = "ReadyUserList", UserListData = "ReadyUserList",
ReadySpriteData = "ReadySprite"; ReadySpriteData = "ReadySprite";
private int lastSecond; private int lastSecond = 1;
private GUIMessageBox? msgBox; private GUIMessageBox? msgBox;
private GUIMessageBox? resultsBox; private GUIMessageBox? resultsBox;
@@ -44,7 +44,7 @@ namespace Barotrauma
msgBox = new GUIMessageBox(readyCheckHeader, readyCheckBody(author), new[] { yesButton, noButton }, relativeSize, minSize, type: GUIMessageBox.Type.Vote) { UserData = PromptData, Draggable = true }; msgBox = new GUIMessageBox(readyCheckHeader, readyCheckBody(author), new[] { yesButton, noButton }, relativeSize, minSize, type: GUIMessageBox.Type.Vote) { UserData = PromptData, Draggable = true };
GUILayoutGroup contentLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.125f), msgBox.Content.RectTransform), childAnchor: Anchor.Center); GUILayoutGroup contentLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.125f), msgBox.Content.RectTransform), childAnchor: Anchor.Center);
new GUIProgressBar(new RectTransform(new Vector2(0.8f, 1f), contentLayout.RectTransform), time / endTime, GUIStyle.Orange) { UserData = TimerData }; new GUIProgressBar(new RectTransform(new Vector2(0.8f, 1f), contentLayout.RectTransform), 0.0f, GUIStyle.Orange) { UserData = TimerData };
// Yes // Yes
msgBox.Buttons[0].OnClicked = delegate msgBox.Buttons[0].OnClicked = delegate
@@ -116,17 +116,18 @@ namespace Barotrauma
private void UpdateBar() private void UpdateBar()
{ {
double elapsedTime = (DateTime.Now - startTime).TotalSeconds;
if (msgBox != null && !msgBox.Closed && GUIMessageBox.MessageBoxes.Contains(msgBox)) if (msgBox != null && !msgBox.Closed && GUIMessageBox.MessageBoxes.Contains(msgBox))
{ {
if (msgBox.FindChild(TimerData, true) is GUIProgressBar bar) if (msgBox.FindChild(TimerData, true) is GUIProgressBar bar)
{ {
bar.BarSize = time / endTime; bar.BarSize = (float)(elapsedTime / (endTime - startTime).TotalSeconds);
} }
} }
// play click sound after a second has passed // play click sound after a second has passed
int second = (int) Math.Ceiling(time); int second = (int)Math.Ceiling(elapsedTime);
if (second < lastSecond) if (second > lastSecond)
{ {
if (msgBox != null && !msgBox.Closed) if (msgBox != null && !msgBox.Closed)
{ {
@@ -156,7 +157,8 @@ namespace Barotrauma
bool isOwn = false; bool isOwn = false;
byte authorId = 0; byte authorId = 0;
float duration = inc.ReadSingle(); long startTime = inc.ReadInt64();
long endTime = inc.ReadInt64();
string author = inc.ReadString(); string author = inc.ReadString();
bool hasAuthor = inc.ReadBoolean(); bool hasAuthor = inc.ReadBoolean();
@@ -173,7 +175,9 @@ namespace Barotrauma
clients.Add(inc.ReadByte()); clients.Add(inc.ReadByte());
} }
ReadyCheck rCheck = new ReadyCheck(clients, duration); ReadyCheck rCheck = new ReadyCheck(clients,
DateTimeOffset.FromUnixTimeSeconds(startTime).LocalDateTime,
DateTimeOffset.FromUnixTimeSeconds(endTime).LocalDateTime);
crewManager.ActiveReadyCheck = rCheck; crewManager.ActiveReadyCheck = rCheck;
if (isOwn) if (isOwn)
@@ -192,12 +196,10 @@ namespace Barotrauma
} }
break; break;
case ReadyCheckState.Update: case ReadyCheckState.Update:
float time = inc.ReadSingle();
ReadyStatus newState = (ReadyStatus) inc.ReadByte(); ReadyStatus newState = (ReadyStatus) inc.ReadByte();
byte targetId = inc.ReadByte(); byte targetId = inc.ReadByte();
if (crewManager.ActiveReadyCheck != null) if (crewManager.ActiveReadyCheck != null)
{ {
crewManager.ActiveReadyCheck.time = time;
crewManager.ActiveReadyCheck?.UpdateState(targetId, newState); crewManager.ActiveReadyCheck?.UpdateState(targetId, newState);
} }
break; break;
@@ -222,7 +222,8 @@ namespace Barotrauma.Items.Components
if (brokenSprite != null && item.Health < item.MaxCondition) if (brokenSprite != null && item.Health < item.MaxCondition)
{ {
Vector2 scale = scaleBrokenSprite ? new Vector2(1.0f, 1.0f - item.Health / item.MaxCondition) : Vector2.One; Vector2 scale = scaleBrokenSprite ? new Vector2(1.0f - item.Health / item.MaxCondition) : Vector2.One;
if (IsHorizontal) { scale.X = 1; } else { scale.Y = 1; }
float alpha = fadeBrokenSprite ? 1.0f - item.Health / item.MaxCondition : 1.0f; float alpha = fadeBrokenSprite ? 1.0f - item.Health / item.MaxCondition : 1.0f;
spriteBatch.Draw(brokenSprite.Texture, pos, spriteBatch.Draw(brokenSprite.Texture, pos,
getSourceRect(brokenSprite, openState, IsHorizontal), getSourceRect(brokenSprite, openState, IsHorizontal),
@@ -405,7 +405,7 @@ namespace Barotrauma.Items.Components
float newVolume; float newVolume;
try try
{ {
newVolume = property.GetFloatValue(this); newVolume = Math.Min(property.GetFloatValue(this), 1.0f);
} }
catch catch
{ {
@@ -58,6 +58,8 @@ namespace Barotrauma.Items.Components
return true; return true;
} }
}; };
layoutGroup.Recalculate();
} }
// Create fillerBlock to cover historyBox so new values appear at the bottom of historyBox // Create fillerBlock to cover historyBox so new values appear at the bottom of historyBox
@@ -33,7 +33,7 @@ namespace Barotrauma
DrawArrow(FlowTargetHull, IsHorizontal ? rect.Height: rect.Width, Math.Abs(lerpedFlowForce.Length()), Color.Red * 0.3f); DrawArrow(FlowTargetHull, IsHorizontal ? rect.Height: rect.Width, Math.Abs(lerpedFlowForce.Length()), Color.Red * 0.3f);
} }
if (outsideCollisionBlocker.Enabled && Submarine != null) if (Submarine != null && outsideCollisionBlocker != null && outsideCollisionBlocker.Enabled)
{ {
var edgeShape = outsideCollisionBlocker.FixtureList[0].Shape as FarseerPhysics.Collision.Shapes.EdgeShape; var edgeShape = outsideCollisionBlocker.FixtureList[0].Shape as FarseerPhysics.Collision.Shapes.EdgeShape;
Vector2 startPos = ConvertUnits.ToDisplayUnits(outsideCollisionBlocker.GetWorldPoint(edgeShape.Vertex1)) + Submarine.Position; Vector2 startPos = ConvertUnits.ToDisplayUnits(outsideCollisionBlocker.GetWorldPoint(edgeShape.Vertex1)) + Submarine.Position;
@@ -1200,7 +1200,14 @@ namespace Barotrauma.Networking
new LocalizedString[] { TextManager.Get("Cancel") }); new LocalizedString[] { TextManager.Get("Cancel") });
reconnectBox.Buttons[0].OnClicked += (btn, userdata) => { CancelConnect(); return true; }; reconnectBox.Buttons[0].OnClicked += (btn, userdata) => { CancelConnect(); return true; };
connected = false; connected = false;
var prevContentPackages = clientPeer.ServerContentPackages;
ConnectToServer(serverEndpoint, serverName); ConnectToServer(serverEndpoint, serverName);
if (clientPeer != null)
{
//restore the previous list of content packages so we can reconnect immediately without having to recheck that the packages match
clientPeer.ServerContentPackages = prevContentPackages;
}
} }
else else
{ {
@@ -9,6 +9,7 @@ namespace Barotrauma.Networking
msg.Write((byte)ClientNetObject.CHAT_MESSAGE); msg.Write((byte)ClientNetObject.CHAT_MESSAGE);
msg.Write(NetStateID); msg.Write(NetStateID);
msg.WriteRangedInteger((int)ChatMessageType.Order, 0, Enum.GetValues(typeof(ChatMessageType)).Length - 1); msg.WriteRangedInteger((int)ChatMessageType.Order, 0, Enum.GetValues(typeof(ChatMessageType)).Length - 1);
msg.WriteRangedInteger((int)ChatMode.None, 0, Enum.GetValues(typeof(ChatMode)).Length - 1);
WriteOrder(msg); WriteOrder(msg);
} }
} }
@@ -50,7 +50,7 @@ namespace Barotrauma.Networking
} }
} }
public ImmutableArray<ServerContentPackage> ServerContentPackages { get; private set; } = public ImmutableArray<ServerContentPackage> ServerContentPackages { get; set; } =
ImmutableArray<ServerContentPackage>.Empty; ImmutableArray<ServerContentPackage>.Empty;
public delegate void MessageCallback(IReadMessage message); public delegate void MessageCallback(IReadMessage message);
@@ -117,8 +117,6 @@ namespace Barotrauma.Networking
PacketHeader packetHeader = (PacketHeader)inc.ReadByte(); PacketHeader packetHeader = (PacketHeader)inc.ReadByte();
//Console.WriteLine(isCompressed + " " + isConnectionInitializationStep + " " + (int)incByte);
if (packetHeader.IsConnectionInitializationStep() && initializationStep != ConnectionInitialization.Success) if (packetHeader.IsConnectionInitializationStep() && initializationStep != ConnectionInitialization.Success)
{ {
ReadConnectionInitializationStep(new ReadWriteMessage(inc.Data, (int)inc.Position, inc.LengthBits, false)); ReadConnectionInitializationStep(new ReadWriteMessage(inc.Data, (int)inc.Position, inc.LengthBits, false));
@@ -110,7 +110,7 @@ namespace Barotrauma
public SettingValue<GameDifficulty> Difficulty; public SettingValue<GameDifficulty> Difficulty;
public SettingValue<Identifier> StartItemSet; public SettingValue<Identifier> StartItemSet;
public CampaignSettings CreateSettings() public readonly CampaignSettings CreateSettings()
{ {
return new CampaignSettings(element: null) return new CampaignSettings(element: null)
{ {
@@ -210,8 +210,17 @@ namespace Barotrauma
{ {
CreateCustomizeWindow(CurrentSettings, settings => CreateCustomizeWindow(CurrentSettings, settings =>
{ {
CampaignSettings prevSettings = CurrentSettings;
CurrentSettings = settings; CurrentSettings = settings;
UpdateSubList(SubmarineInfo.SavedSubmarines); if (prevSettings.InitialMoney != settings.InitialMoney)
{
object selectedData = subList.SelectedData;
UpdateSubList(SubmarineInfo.SavedSubmarines);
if (selectedData is SubmarineInfo selectedSub && selectedSub.Price <= CurrentSettings.InitialMoney)
{
subList.Select(selectedData);
}
}
}); });
return true; return true;
} }
@@ -513,18 +513,18 @@ namespace Barotrauma
var moduleLabel = new GUITextBlock(new RectTransform(new Point(editorContainer.Content.Rect.Width, (int)(70 * GUI.Scale))), TextManager.Get("submarinetype.outpostmodules"), font: GUIStyle.SubHeadingFont); var moduleLabel = new GUITextBlock(new RectTransform(new Point(editorContainer.Content.Rect.Width, (int)(70 * GUI.Scale))), TextManager.Get("submarinetype.outpostmodules"), font: GUIStyle.SubHeadingFont);
outpostParamsEditor.AddCustomContent(moduleLabel, 100); outpostParamsEditor.AddCustomContent(moduleLabel, 100);
foreach (KeyValuePair<Identifier, int> moduleCount in outpostGenerationParams.ModuleCounts) foreach (var moduleCount in outpostGenerationParams.ModuleCounts)
{ {
var moduleCountGroup = new GUILayoutGroup(new RectTransform(new Point(editorContainer.Content.Rect.Width, (int)(25 * GUI.Scale))), isHorizontal: true, childAnchor: Anchor.CenterLeft); var moduleCountGroup = new GUILayoutGroup(new RectTransform(new Point(editorContainer.Content.Rect.Width, (int)(25 * GUI.Scale))), isHorizontal: true, childAnchor: Anchor.CenterLeft);
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), moduleCountGroup.RectTransform), TextManager.Capitalize(moduleCount.Key.Value), textAlignment: Alignment.CenterLeft); new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), moduleCountGroup.RectTransform), TextManager.Capitalize(moduleCount.Identifier.Value), textAlignment: Alignment.CenterLeft);
new GUINumberInput(new RectTransform(new Vector2(0.5f, 1f), moduleCountGroup.RectTransform), NumberType.Int) new GUINumberInput(new RectTransform(new Vector2(0.5f, 1f), moduleCountGroup.RectTransform), NumberType.Int)
{ {
MinValueInt = 0, MinValueInt = 0,
MaxValueInt = 100, MaxValueInt = 100,
IntValue = moduleCount.Value, IntValue = moduleCount.Count,
OnValueChanged = (numInput) => OnValueChanged = (numInput) =>
{ {
outpostGenerationParams.SetModuleCount(moduleCount.Key, numInput.IntValue); outpostGenerationParams.SetModuleCount(moduleCount.Identifier, numInput.IntValue);
if (numInput.IntValue == 0) if (numInput.IntValue == 0)
{ {
outpostParamsList.Select(outpostParamsList.SelectedData); outpostParamsList.Select(outpostParamsList.SelectedData);
@@ -540,7 +540,7 @@ namespace Barotrauma
var addModuleCountGroup = new GUILayoutGroup(new RectTransform(new Point(editorContainer.Content.Rect.Width, (int)(40 * GUI.Scale))), isHorizontal: true, childAnchor: Anchor.Center); var addModuleCountGroup = new GUILayoutGroup(new RectTransform(new Point(editorContainer.Content.Rect.Width, (int)(40 * GUI.Scale))), isHorizontal: true, childAnchor: Anchor.Center);
HashSet<Identifier> availableFlags = new HashSet<Identifier>(); HashSet<Identifier> availableFlags = new HashSet<Identifier>();
foreach (Identifier flag in OutpostGenerationParams.OutpostParams.SelectMany(p => p.ModuleCounts.Select(m => m.Key))) { availableFlags.Add(flag); } foreach (Identifier flag in OutpostGenerationParams.OutpostParams.SelectMany(p => p.ModuleCounts.Select(m => m.Identifier))) { availableFlags.Add(flag); }
foreach (var sub in SubmarineInfo.SavedSubmarines) foreach (var sub in SubmarineInfo.SavedSubmarines)
{ {
if (sub.OutpostModuleInfo == null) { continue; } if (sub.OutpostModuleInfo == null) { continue; }
@@ -551,7 +551,7 @@ namespace Barotrauma
text: TextManager.Get("leveleditor.addmoduletype")); text: TextManager.Get("leveleditor.addmoduletype"));
foreach (Identifier flag in availableFlags) foreach (Identifier flag in availableFlags)
{ {
if (outpostGenerationParams.ModuleCounts.Any(mc => mc.Key == flag)) { continue; } if (outpostGenerationParams.ModuleCounts.Any(mc => mc.Identifier == flag)) { continue; }
moduleTypeDropDown.AddItem(TextManager.Capitalize(flag.Value), flag); moduleTypeDropDown.AddItem(TextManager.Capitalize(flag.Value), flag);
} }
moduleTypeDropDown.OnSelected += (_, userdata) => moduleTypeDropDown.OnSelected += (_, userdata) =>
@@ -1549,6 +1549,11 @@ namespace Barotrauma
MapEntity.DeselectAll(); MapEntity.DeselectAll();
ClearUndoBuffer(); ClearUndoBuffer();
GameMain.DebugDraw = false;
GameMain.LightManager.LightingEnabled = true;
Hull.EditWater = false;
Hull.EditFire = false;
SetMode(Mode.Default); SetMode(Mode.Default);
SoundPlayer.OverrideMusicType = Identifier.Empty; SoundPlayer.OverrideMusicType = Identifier.Empty;
@@ -1586,7 +1591,7 @@ namespace Barotrauma
private void CreateDummyCharacter() private void CreateDummyCharacter()
{ {
if (dummyCharacter != null) RemoveDummyCharacter(); if (dummyCharacter != null) { RemoveDummyCharacter(); }
dummyCharacter = Character.Create(CharacterPrefab.HumanSpeciesName, Vector2.Zero, "", id: Entity.DummyID, hasAi: false); dummyCharacter = Character.Create(CharacterPrefab.HumanSpeciesName, Vector2.Zero, "", id: Entity.DummyID, hasAi: false);
dummyCharacter.Info.Name = "Galldren"; dummyCharacter.Info.Name = "Galldren";
@@ -1876,21 +1881,15 @@ namespace Barotrauma
&& MainSub.Info.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)) && MainSub.Info.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))
{ {
prevSavePath = MainSub.Info.FilePath.CleanUpPath(); prevSavePath = MainSub.Info.FilePath.CleanUpPath();
string prevDir = Path.GetDirectoryName(MainSub.Info.FilePath).CleanUpPath();
ModProject modProject = new ModProject { Name = name };
string fileListPath = null;
ContentPackage contentPackage = GetLocalPackageThatOwnsSub(MainSub.Info); ContentPackage contentPackage = GetLocalPackageThatOwnsSub(MainSub.Info);
if (contentPackage != null) if (contentPackage == null)
{ {
modProject = new ModProject(contentPackage); throw new InvalidOperationException($"Tried to overwrite a submarine ({name}) that's not in a local package!");
fileListPath = contentPackage.Path;
packageToSaveTo = contentPackage;
} }
ModProject modProject = new ModProject(contentPackage);
savePath = Path.Combine(prevDir, savePath).CleanUpPath(); packageToSaveTo = contentPackage;
addSubAndSaveModProject(modProject, savePath, fileListPath ?? Path.Combine(Path.GetDirectoryName(savePath), ContentPackage.FileListFileName)); savePath = prevSavePath;
addSubAndSaveModProject(modProject, savePath, contentPackage.Path);
} }
else else
{ {
@@ -2074,8 +2073,8 @@ namespace Barotrauma
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), outpostModuleGroup.RectTransform), TextManager.Get("outpostmoduletype"), textAlignment: Alignment.CenterLeft); new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), outpostModuleGroup.RectTransform), TextManager.Get("outpostmoduletype"), textAlignment: Alignment.CenterLeft);
HashSet<Identifier> availableFlags = new HashSet<Identifier>(); HashSet<Identifier> availableFlags = new HashSet<Identifier>();
foreach (Identifier flag in OutpostGenerationParams.OutpostParams.SelectMany(p => p.ModuleCounts.Select(m => m.Key))) { availableFlags.Add(flag); } foreach (Identifier flag in OutpostGenerationParams.OutpostParams.SelectMany(p => p.ModuleCounts.Select(m => m.Identifier))) { availableFlags.Add(flag); }
foreach (Identifier flag in RuinGeneration.RuinGenerationParams.RuinParams.SelectMany(p => p.ModuleCounts.Select(m => m.Key))) { availableFlags.Add(flag); } foreach (Identifier flag in RuinGeneration.RuinGenerationParams.RuinParams.SelectMany(p => p.ModuleCounts.Select(m => m.Identifier))) { availableFlags.Add(flag); }
foreach (var sub in SubmarineInfo.SavedSubmarines) foreach (var sub in SubmarineInfo.SavedSubmarines)
{ {
if (sub.OutpostModuleInfo == null) { continue; } if (sub.OutpostModuleInfo == null) { continue; }
@@ -2551,11 +2550,9 @@ namespace Barotrauma
{ {
MainSub.Info.BeaconStationInfo ??= new BeaconStationInfo(MainSub.Info); MainSub.Info.BeaconStationInfo ??= new BeaconStationInfo(MainSub.Info);
} }
previewImageButtonHolder.Children.ForEach(c => c.Enabled = type != SubmarineType.OutpostModule); previewImageButtonHolder.Children.ForEach(c => c.Enabled = MainSub.Info.AllowPreviewImage);
outpostSettingsContainer.Visible = type == SubmarineType.OutpostModule; outpostSettingsContainer.Visible = type == SubmarineType.OutpostModule;
beaconSettingsContainer.Visible = type == SubmarineType.BeaconStation; beaconSettingsContainer.Visible = type == SubmarineType.BeaconStation;
subSettingsContainer.Visible = type == SubmarineType.Player; subSettingsContainer.Visible = type == SubmarineType.Player;
return true; return true;
}; };
@@ -2572,6 +2569,7 @@ namespace Barotrauma
new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), previewImageButtonHolder.RectTransform), TextManager.Get("SubPreviewImageCreate"), style: "GUIButtonSmall") new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), previewImageButtonHolder.RectTransform), TextManager.Get("SubPreviewImageCreate"), style: "GUIButtonSmall")
{ {
Enabled = MainSub?.Info.AllowPreviewImage ?? false,
OnClicked = (btn, userdata) => OnClicked = (btn, userdata) =>
{ {
using (System.IO.MemoryStream imgStream = new System.IO.MemoryStream()) using (System.IO.MemoryStream imgStream = new System.IO.MemoryStream())
@@ -2589,6 +2587,7 @@ namespace Barotrauma
new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), previewImageButtonHolder.RectTransform), TextManager.Get("SubPreviewImageBrowse"), style: "GUIButtonSmall") new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), previewImageButtonHolder.RectTransform), TextManager.Get("SubPreviewImageBrowse"), style: "GUIButtonSmall")
{ {
Enabled = MainSub?.Info.AllowPreviewImage ?? false,
OnClicked = (btn, userdata) => OnClicked = (btn, userdata) =>
{ {
FileSelection.OnFileSelected = (file) => FileSelection.OnFileSelected = (file) =>
@@ -220,7 +220,7 @@ namespace Barotrauma
}; };
} }
private string Percentage(float v) => $"{Round(v * 100)}%"; private string Percentage(float v) => TextManager.GetWithVariable("percentageformat", "[value]", Round(v * 100).ToString()).Value;
private int Round(float v) => (int)MathF.Round(v); private int Round(float v) => (int)MathF.Round(v);
@@ -647,6 +647,8 @@ namespace Barotrauma
{ {
unsavedConfig.InventoryKeyMap = GameSettings.Config.InventoryKeyMapping.GetDefault(); unsavedConfig.InventoryKeyMap = GameSettings.Config.InventoryKeyMapping.GetDefault();
unsavedConfig.KeyMap = GameSettings.Config.KeyMapping.GetDefault(); unsavedConfig.KeyMap = GameSettings.Config.KeyMapping.GetDefault();
Create(mainFrame.Parent.RectTransform);
Instance?.SelectTab(Tab.Controls);
return true; return true;
} }
}; };
@@ -253,12 +253,12 @@ namespace Barotrauma
if (flipHorizontal) if (flipHorizontal)
{ {
float diff = targetSize.X % (sourceRect.Width * scale.X); float diff = targetSize.X % (sourceRect.Width * scale.X);
flippedDrawOffset.X = (int)((sourceRect.Width * scale.X - diff) / scale.X); flippedDrawOffset.X = (int)MathF.Round((sourceRect.Width * scale.X - diff) / scale.X);
} }
if (flipVertical) if (flipVertical)
{ {
float diff = targetSize.Y % (sourceRect.Height * scale.Y); float diff = targetSize.Y % (sourceRect.Height * scale.Y);
flippedDrawOffset.Y = (int)((sourceRect.Height * scale.Y - diff) / scale.Y); flippedDrawOffset.Y = (int)MathF.Round((sourceRect.Height * scale.Y - diff) / scale.Y);
} }
drawOffset += flippedDrawOffset; drawOffset += flippedDrawOffset;
@@ -562,10 +562,10 @@ namespace Barotrauma.Steam
= (parentList == enabledRegularModsList, parentList.AllSelected.Count > 1); = (parentList == enabledRegularModsList, parentList.AllSelected.Count > 1);
Identifier swapLabel = (labelConditions switch Identifier swapLabel = (labelConditions switch
{ {
(true, true) => "EnableSelectedWorkshopMods", (false, true) => "EnableSelectedWorkshopMods",
(true, false) => "EnableWorkshopMod", (false, false) => "EnableWorkshopMod",
(false, true) => "DisableSelectedWorkshopMods", (true, true) => "DisableSelectedWorkshopMods",
(false, false) => "DisableWorkshopMod" (true, false) => "DisableWorkshopMod"
}).ToIdentifier(); }).ToIdentifier();
contextMenuOptions.Add(new ContextMenuOption(swapLabel, contextMenuOptions.Add(new ContextMenuOption(swapLabel,
@@ -595,6 +595,12 @@ namespace Barotrauma.Steam
{ {
ContentPackageManager.WorkshopPackages.Refresh(); ContentPackageManager.WorkshopPackages.Refresh();
ContentPackageManager.EnabledPackages.RefreshUpdatedMods(); ContentPackageManager.EnabledPackages.RefreshUpdatedMods();
var package = ContentPackageManager.WorkshopPackages.FirstOrDefault(p => p.SteamWorkshopId == workshopItem.Id);
if (package is RegularPackage regular)
{
ContentPackageManager.EnabledPackages.EnableRegular(regular);
}
PopulateInstalledModLists(forceRefreshEnabled: true);
}); });
return false; return false;
} }
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.18.6.0</Version> <Version>0.18.7.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright> <Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
+1 -1
View File
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.18.6.0</Version> <Version>0.18.7.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright> <Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.18.6.0</Version> <Version>0.18.7.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright> <Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.18.6.0</Version> <Version>0.18.7.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright> <Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
+1 -1
View File
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.18.6.0</Version> <Version>0.18.7.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright> <Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
@@ -10,6 +10,11 @@ namespace Barotrauma
{ {
private readonly Dictionary<Identifier, float> prevSentSkill = new Dictionary<Identifier, float>(); private readonly Dictionary<Identifier, float> prevSentSkill = new Dictionary<Identifier, float>();
/// <summary>
/// The client opted to create a new character and discard this one
/// </summary>
public bool Discarded;
partial void OnSkillChanged(Identifier skillIdentifier, float prevLevel, float newLevel) partial void OnSkillChanged(Identifier skillIdentifier, float prevLevel, float newLevel)
{ {
if (Character == null || Character.Removed) { return; } if (Character == null || Character.Removed) { return; }
@@ -230,7 +230,7 @@ namespace Barotrauma
if (!matchingCharacterData.HasSpawned) { continue; } if (!matchingCharacterData.HasSpawned) { continue; }
characterInfo ??= matchingCharacterData.CharacterInfo; characterInfo ??= matchingCharacterData.CharacterInfo;
} }
if (characterInfo == null) { continue; } if (characterInfo == null || characterInfo.Discarded) { continue; }
//reduce skills if the character has died //reduce skills if the character has died
if (characterInfo.CauseOfDeath != null && characterInfo.CauseOfDeath.Type != CauseOfDeathType.Disconnected) if (characterInfo.CauseOfDeath != null && characterInfo.CauseOfDeath.Type != CauseOfDeathType.Disconnected)
{ {
@@ -420,6 +420,8 @@ namespace Barotrauma
{ {
discardedCharacters.Add(data); discardedCharacters.Add(data);
} }
DebugConsole.Log($"Client \"{client}\" discarded the character ({data.Name})");
data.CharacterInfo.Discarded = true;
characterData.Remove(data); characterData.Remove(data);
IncrementLastUpdateIdForFlag(NetFlags.CharacterInfo); IncrementLastUpdateIdForFlag(NetFlags.CharacterInfo);
} }
@@ -1,4 +1,5 @@
#nullable enable #nullable enable
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Barotrauma.Networking; using Barotrauma.Networking;
@@ -18,7 +19,8 @@ namespace Barotrauma
IWriteMessage msg = new WriteOnlyMessage(); IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte) ServerPacketHeader.READY_CHECK); msg.Write((byte) ServerPacketHeader.READY_CHECK);
msg.Write((byte) ReadyCheckState.Start); msg.Write((byte) ReadyCheckState.Start);
msg.Write(endTime); msg.Write(new DateTimeOffset(startTime).ToUnixTimeSeconds());
msg.Write(new DateTimeOffset(endTime).ToUnixTimeSeconds());
msg.Write(author); msg.Write(author);
if (sender != null) if (sender != null)
@@ -53,10 +55,9 @@ namespace Barotrauma
foreach (Client client in ActivePlayers) foreach (Client client in ActivePlayers)
{ {
IWriteMessage msg = new WriteOnlyMessage(); IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte) ServerPacketHeader.READY_CHECK); msg.Write((byte)ServerPacketHeader.READY_CHECK);
msg.Write((byte) ReadyCheckState.Update); msg.Write((byte)ReadyCheckState.Update);
msg.Write(time); // sync time msg.Write((byte)state);
msg.Write((byte) state);
msg.Write(otherClient); msg.Write(otherClient);
GameMain.Server.ServerPeer.Send(msg, client.Connection, DeliveryMethod.Reliable); GameMain.Server.ServerPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
} }
@@ -1,13 +1,22 @@
using Barotrauma.Networking; using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Globalization;
using System.Xml.Linq;
namespace Barotrauma.Items.Components namespace Barotrauma.Items.Components
{ {
partial class Pump : Powered, IServerSerializable, IClientSerializable partial class Pump : Powered, IServerSerializable, IClientSerializable
{ {
const float NetworkUpdateInterval = 5.0f;
private float networkUpdateTimer;
partial void UpdateProjSpecific(float deltaTime)
{
networkUpdateTimer -= deltaTime;
if (networkUpdateTimer <= 0.0f)
{
item.CreateServerEvent(this);
networkUpdateTimer = NetworkUpdateInterval;
}
}
public void ServerEventRead(IReadMessage msg, Client c) public void ServerEventRead(IReadMessage msg, Client c)
{ {
float newFlowPercentage = msg.ReadRangedInteger(-10, 10) * 10.0f; float newFlowPercentage = msg.ReadRangedInteger(-10, 10) * 10.0f;
@@ -2,10 +2,7 @@
using Barotrauma.Networking; using Barotrauma.Networking;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using Barotrauma.Extensions;
using Barotrauma.MapCreatures.Behavior;
namespace Barotrauma namespace Barotrauma
{ {
@@ -39,9 +36,9 @@ namespace Barotrauma
return; return;
} }
statusUpdateTimer -= deltaTime; statusUpdateTimer += deltaTime;
decalUpdateTimer -= deltaTime; decalUpdateTimer += deltaTime;
backgroundSectionUpdateTimer -= deltaTime; backgroundSectionUpdateTimer += deltaTime;
//update client hulls if the amount of water has changed by >10% //update client hulls if the amount of water has changed by >10%
//or if oxygen percentage has changed by 5% //or if oxygen percentage has changed by 5%
@@ -49,33 +46,32 @@ namespace Barotrauma
(Math.Abs(lastSentVolume - waterVolume) > Volume * 0.1f (Math.Abs(lastSentVolume - waterVolume) > Volume * 0.1f
|| Math.Abs(lastSentOxygen - OxygenPercentage) > 5f || Math.Abs(lastSentOxygen - OxygenPercentage) > 5f
|| lastSentFireCount != FireSources.Count) || lastSentFireCount != FireSources.Count)
&& statusUpdateTimer <= 0.0f; && (statusUpdateTimer > NetConfig.HullUpdateInterval);
if (shouldSendStatusUpdate) //force an update every 5 seconds even if nothing's changed (in case a client's gotten out of sync somehow)
if (shouldSendStatusUpdate || statusUpdateTimer > NetConfig.SparseHullUpdateInterval)
{ {
GameMain.NetworkMember.CreateEntityEvent(this, new StatusEventData()); GameMain.NetworkMember.CreateEntityEvent(this, new StatusEventData());
lastSentVolume = waterVolume; lastSentVolume = waterVolume;
lastSentOxygen = OxygenPercentage; lastSentOxygen = OxygenPercentage;
lastSentFireCount = FireSources.Count; lastSentFireCount = FireSources.Count;
statusUpdateTimer = 0;
statusUpdateTimer = NetConfig.SparseHullUpdateInterval;
} }
if (decalUpdatePending && decalUpdateTimer <= 0.0f) if (decalUpdatePending && decalUpdateTimer > NetConfig.HullUpdateInterval)
{ {
GameMain.NetworkMember.CreateEntityEvent(this, new DecalEventData()); GameMain.NetworkMember.CreateEntityEvent(this, new DecalEventData());
decalUpdateTimer = NetConfig.HullUpdateInterval; decalUpdateTimer = 0;
decalUpdatePending = false; decalUpdatePending = false;
} }
if (pendingSectionUpdates.Count > 0 && backgroundSectionUpdateTimer <= 0.0f) if (pendingSectionUpdates.Count > 0 && backgroundSectionUpdateTimer > NetConfig.HullUpdateInterval)
{ {
foreach (int pendingSectionUpdate in pendingSectionUpdates) foreach (int pendingSectionUpdate in pendingSectionUpdates)
{ {
GameMain.NetworkMember.CreateEntityEvent(this, new BackgroundSectionsEventData(pendingSectionUpdate)); GameMain.NetworkMember.CreateEntityEvent(this, new BackgroundSectionsEventData(pendingSectionUpdate));
} }
backgroundSectionUpdateTimer = NetConfig.HullUpdateInterval; backgroundSectionUpdateTimer = 0;
pendingSectionUpdates.Clear(); pendingSectionUpdates.Clear();
} }
} }
@@ -73,6 +73,9 @@ namespace Barotrauma.Networking
characterInfo = value; characterInfo = value;
} }
} }
public string PendingName;
public NetworkConnection Connection { get; set; } public NetworkConnection Connection { get; set; }
public bool SpectateOnly; public bool SpectateOnly;
@@ -1046,7 +1046,7 @@ namespace Barotrauma.Networking
c.LastRecvChatMsgID = NetIdUtils.Clamp(inc.ReadUInt16(), c.LastRecvChatMsgID, c.LastChatMsgQueueID); c.LastRecvChatMsgID = NetIdUtils.Clamp(inc.ReadUInt16(), c.LastRecvChatMsgID, c.LastChatMsgQueueID);
c.LastRecvClientListUpdate = NetIdUtils.Clamp(inc.ReadUInt16(), c.LastRecvClientListUpdate, LastClientListUpdateID); c.LastRecvClientListUpdate = NetIdUtils.Clamp(inc.ReadUInt16(), c.LastRecvClientListUpdate, LastClientListUpdateID);
TryChangeClientName(c, inc); ReadClientNameChange(c, inc);
c.LastRecvCampaignSave = inc.ReadUInt16(); c.LastRecvCampaignSave = inc.ReadUInt16();
if (c.LastRecvCampaignSave > 0) if (c.LastRecvCampaignSave > 0)
@@ -2675,7 +2675,7 @@ namespace Barotrauma.Networking
base.AddChatMessage(message); base.AddChatMessage(message);
} }
private bool TryChangeClientName(Client c, IReadMessage inc) private bool ReadClientNameChange(Client c, IReadMessage inc)
{ {
UInt16 nameId = inc.ReadUInt16(); UInt16 nameId = inc.ReadUInt16();
string newName = inc.ReadString(); string newName = inc.ReadString();
@@ -2685,16 +2685,21 @@ namespace Barotrauma.Networking
if (c == null || string.IsNullOrEmpty(newName) || !NetIdUtils.IdMoreRecent(nameId, c.NameID)) { return false; } if (c == null || string.IsNullOrEmpty(newName) || !NetIdUtils.IdMoreRecent(nameId, c.NameID)) { return false; }
c.NameID = nameId; c.NameID = nameId;
newName = Client.SanitizeName(newName);
if (newName == c.Name && newJob == c.PreferredJob && newTeam == c.PreferredTeam) { return false; } if (newName == c.Name && newJob == c.PreferredJob && newTeam == c.PreferredTeam) { return false; }
c.PreferredJob = newJob; c.PreferredJob = newJob;
c.PreferredTeam = newTeam; c.PreferredTeam = newTeam;
return TryChangeClientName(c, newName);
}
public bool TryChangeClientName(Client c, string newName)
{
newName = Client.SanitizeName(newName);
//update client list even if the name cannot be changed to the one sent by the client, //update client list even if the name cannot be changed to the one sent by the client,
//so the client will be informed what their actual name is //so the client will be informed what their actual name is
LastClientListUpdateID++; LastClientListUpdateID++;
if (newName == c.Name) { return false; } if (newName == c.Name || string.IsNullOrEmpty(newName)) { return false; }
if (IsNameValid(c, newName)) if (IsNameValid(c, newName))
{ {
@@ -2710,6 +2715,7 @@ namespace Barotrauma.Networking
} }
} }
private bool IsNameValid(Client c, string newName) private bool IsNameValid(Client c, string newName)
{ {
newName = Client.SanitizeName(newName); newName = Client.SanitizeName(newName);
@@ -3542,6 +3548,10 @@ namespace Barotrauma.Networking
{ {
newName = sender.Name; newName = sender.Name;
} }
else
{
sender.PendingName = newName;
}
} }
int tagCount = message.ReadByte(); int tagCount = message.ReadByte();
@@ -392,6 +392,14 @@ namespace Barotrauma.Networking
bool forceSpawnInMainSub = false; bool forceSpawnInMainSub = false;
if (!bot && campaign != null) if (!bot && campaign != null)
{ {
//the client has opted to change the name of their new character
//when the character spawns, set the client's name to match
if (clients[i].PendingName == characterInfos[i].Name)
{
GameMain.Server?.TryChangeClientName(clients[i], clients[i].PendingName);
clients[i].PendingName = null;
}
var matchingData = campaign?.GetClientCharacterData(clients[i]); var matchingData = campaign?.GetClientCharacterData(clients[i]);
if (matchingData != null) if (matchingData != null)
{ {
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.18.6.0</Version> <Version>0.18.7.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright> <Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
@@ -227,9 +227,13 @@ namespace Barotrauma
{ {
mainLimb = Limbs.FirstOrDefault(l => IsValid(l)); 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();
}
} }
static bool IsValid(Limb limb) => limb != null && !limb.IsSevered && !limb.IgnoreCollisions && !limb.Hidden;
bool IsValid(Limb limb) => limb != null && !limb.IsSevered && !limb.IgnoreCollisions && !limb.Hidden;
return mainLimb; return mainLimb;
} }
} }
@@ -49,6 +49,9 @@ namespace Barotrauma
[Serialize("", IsPropertySaveable.Yes)] [Serialize("", IsPropertySaveable.Yes)]
public Identifier SpawnPointTag { get; set; } 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?")] [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; } public bool RequireSpawnPointTag { get; set; }
@@ -119,7 +122,7 @@ namespace Barotrauma
{ {
if (newCharacter == null) { return; } if (newCharacter == null) { return; }
newCharacter.HumanPrefab = humanPrefab; newCharacter.HumanPrefab = humanPrefab;
newCharacter.TeamID = CharacterTeamType.FriendlyNPC; newCharacter.TeamID = Team;
newCharacter.EnableDespawn = false; newCharacter.EnableDespawn = false;
humanPrefab.GiveItems(newCharacter, newCharacter.Submarine); humanPrefab.GiveItems(newCharacter, newCharacter.Submarine);
if (LootingIsStealing) if (LootingIsStealing)
@@ -13,12 +13,26 @@ namespace Barotrauma
internal partial class ReadyCheck internal partial class ReadyCheck
{ {
private readonly float endTime; private readonly DateTime endTime;
private float time; private readonly DateTime startTime;
public readonly Dictionary<byte, ReadyStatus> Clients; public readonly Dictionary<byte, ReadyStatus> Clients;
public bool IsFinished = false; 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>(); Clients = new Dictionary<byte, ReadyStatus>();
foreach (byte client in clients) foreach (byte client in clients)
@@ -27,24 +41,17 @@ namespace Barotrauma
Clients.Add(client, ReadyStatus.Unanswered); Clients.Add(client, ReadyStatus.Unanswered);
} }
time = duration;
endTime = duration;
#if CLIENT
lastSecond = (int) Math.Ceiling(duration);
#endif
} }
partial void EndReadyCheck(); partial void EndReadyCheck();
public void Update(float deltaTime) public void Update(float deltaTime)
{ {
if (time > 0) if (DateTime.Now < endTime)
{ {
#if CLIENT #if CLIENT
UpdateBar(); UpdateBar();
#endif #endif
time -= deltaTime;
return; return;
} }
@@ -1,10 +1,9 @@
using Barotrauma.Networking; using Barotrauma.MapCreatures.Behavior;
using Barotrauma.Networking;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Xml.Linq;
using Barotrauma.MapCreatures.Behavior;
namespace Barotrauma.Items.Components namespace Barotrauma.Items.Components
{ {
@@ -24,7 +23,10 @@ namespace Barotrauma.Items.Components
if (value == hijacked) { return; } if (value == hijacked) { return; }
hijacked = value; hijacked = value;
#if SERVER #if SERVER
item.CreateServerEvent(this); if (!Submarine.Unloading)
{
item.CreateServerEvent(this);
}
#endif #endif
} }
} }
@@ -68,7 +68,7 @@ namespace Barotrauma.Items.Components
get { return poweredList; } 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>(); 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; } public bool PoweredByTinkering { get; set; }
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "Can the item be damaged by electomagnetic pulses.")] [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() public void SetRecipientsDirty()
{ {
recipientsDirty = true; recipientsDirty = true;
if (IsPower) { Powered.ChangedConnections.Add(this); }
} }
private void RefreshRecipients() private void RefreshRecipients()
@@ -399,6 +399,7 @@ namespace Barotrauma.MapCreatures.Behavior
new XAttribute("pos", XMLExtensions.Vector2ToString(branch.Position)), new XAttribute("pos", XMLExtensions.Vector2ToString(branch.Position)),
new XAttribute("ID", branch.ID), new XAttribute("ID", branch.ID),
new XAttribute("isroot", branch.IsRoot), new XAttribute("isroot", branch.IsRoot),
new XAttribute("isrootgrowth", branch.IsRootGrowth),
new XAttribute("health", branch.Health.ToString("G", CultureInfo.InvariantCulture)), new XAttribute("health", branch.Health.ToString("G", CultureInfo.InvariantCulture)),
new XAttribute("maxhealth", branch.MaxHealth.ToString("G", CultureInfo.InvariantCulture)), new XAttribute("maxhealth", branch.MaxHealth.ToString("G", CultureInfo.InvariantCulture)),
new XAttribute("sides", (int)branch.Sides), new XAttribute("sides", (int)branch.Sides),
@@ -457,9 +458,16 @@ namespace Barotrauma.MapCreatures.Behavior
foreach ((BallastFloraBranch branch, int parentBranchId) in branches) 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); Vector2 pos = branchElement.GetAttributeVector2("pos", Vector2.Zero);
bool isRoot = branchElement.GetAttributeBool("isroot", false); bool isRoot = branchElement.GetAttributeBool("isroot", false);
bool isRootGrowth = branchElement.GetAttributeBool("isrootgrowth", false);
int flowerConfig = getInt("flowerconfig"); int flowerConfig = getInt("flowerconfig");
int leafconfig = getInt("leafconfig"); int leafconfig = getInt("leafconfig");
int id = getInt("ID"); int id = getInt("ID");
@@ -493,7 +502,8 @@ namespace Barotrauma.MapCreatures.Behavior
MaxHealth = maxhealth, MaxHealth = maxhealth,
Sides = (TileSide) sides, Sides = (TileSide) sides,
BlockedSides = (TileSide) blockedSides, BlockedSides = (TileSide) blockedSides,
IsRoot = isRoot IsRoot = isRoot,
IsRootGrowth = isRootGrowth
}; };
branches.Add((newBranch, parentBranchId)); branches.Add((newBranch, parentBranchId));
@@ -658,11 +668,14 @@ namespace Barotrauma.MapCreatures.Behavior
toBeRemoved.Clear(); toBeRemoved.Clear();
foreach (BallastFloraBranch branch in Branches) 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; if (branch.ParentBranch == null || branch.ParentBranch.DisconnectedFromRoot || branch.ParentBranch.Health <= 0.0f)
float speed = MathHelper.Lerp(5.0f, 0.1f, parentHealth); {
DamageBranch(branch, speed * speed * deltaTime, AttackType.CutFromRoot); 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) if (branch.Health <= 0.0f)
{ {
@@ -1197,7 +1210,10 @@ namespace Barotrauma.MapCreatures.Behavior
} }
}); });
#if SERVER #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 #endif
} }
@@ -215,16 +215,19 @@ namespace Barotrauma
saveElement = element saveElement = element
}; };
if (!string.IsNullOrWhiteSpace(levelSeed) && levelData != null && bool levelMatches = string.IsNullOrWhiteSpace(levelSeed) || levelData == null || levelData.Seed == levelSeed;
levelData.Seed != levelSeed && !linkedSub.purchasedLostShuttles)
{ //don't load a sub that was left in this level if we have a submarine switch pending
linkedSub.loadSub = false; //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)
else
{ {
linkedSub.loadSub = true; linkedSub.loadSub = true;
linkedSub.rect.Location = MathUtils.ToPoint(pos); linkedSub.rect.Location = MathUtils.ToPoint(pos);
} }
else
{
linkedSub.loadSub = false;
}
} }
#warning TODO: revise #warning TODO: revise
@@ -308,8 +311,17 @@ namespace Barotrauma
{ {
linkedPort = (FindEntityByID(originalLinkedToID) as Item)?.GetComponent<DockingPort>(); 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; originalLinkedPort = linkedPort;
ushort originalMyId = childRemap.GetOffsetId(originalMyPortID); ushort originalMyId = childRemap.GetOffsetId(originalMyPortID);
@@ -1,11 +1,9 @@
using Barotrauma.Extensions; using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq; using System.Linq;
using System.Xml.Linq;
namespace Barotrauma namespace Barotrauma
{ {
@@ -98,9 +96,29 @@ namespace Barotrauma
[Serialize("", IsPropertySaveable.Yes), Editable] [Serialize("", IsPropertySaveable.Yes), Editable]
public string ReplaceInRadiation { get; set; } 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; } get { return moduleCounts; }
} }
@@ -171,8 +189,7 @@ namespace Barotrauma
switch (subElement.Name.ToString().ToLowerInvariant()) switch (subElement.Name.ToString().ToLowerInvariant())
{ {
case "modulecount": case "modulecount":
Identifier moduleFlag = subElement.GetAttributeIdentifier("flag", subElement.GetAttributeIdentifier("moduletype", "")); moduleCounts.Add(new ModuleCount(subElement));
moduleCounts[moduleFlag] = subElement.GetAttributeInt("count", 0);
break; break;
case "npcs": case "npcs":
var newCollection = new NpcCollection(); var newCollection = new NpcCollection();
@@ -200,7 +217,7 @@ namespace Barotrauma
public int GetModuleCount(Identifier moduleFlag) public int GetModuleCount(Identifier moduleFlag)
{ {
if (moduleFlag == Identifier.Empty || moduleFlag == "none") { return int.MaxValue; } 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) public void SetModuleCount(Identifier moduleFlag, int count)
@@ -208,11 +225,19 @@ namespace Barotrauma
if (moduleFlag == Identifier.Empty || moduleFlag == "none") { return; } if (moduleFlag == Identifier.Empty || moduleFlag == "none") { return; }
if (count <= 0) if (count <= 0)
{ {
moduleCounts.Remove(moduleFlag); moduleCounts.RemoveAll(m => m.Identifier == moduleFlag);
} }
else 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 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()) && 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; continue;
} }
@@ -141,16 +141,11 @@ namespace Barotrauma
selectedModules.Clear(); selectedModules.Clear();
//select which module types the outpost should consist of //select which module types the outpost should consist of
List<Identifier> pendingModuleFlags; List<Identifier> pendingModuleFlags =
using (var md5 = MD5.Create()) onlyEntrance ?
{ generationParams.ModuleCounts.First().Identifier.ToEnumerable().ToList() :
#warning TODO: cursed SelectModules(outpostModules, generationParams);
pendingModuleFlags = onlyEntrance
? generationParams.ModuleCounts
.Keys.OrderBy(k => ToolBox.IdentifierToUint32Hash(k, md5))
.First().ToEnumerable().ToList()
: SelectModules(outpostModules, generationParams);
}
foreach (Identifier flag in pendingModuleFlags) foreach (Identifier flag in pendingModuleFlags)
{ {
if (flag == "none") { continue; } if (flag == "none") { continue; }
@@ -437,31 +432,27 @@ namespace Barotrauma
var pendingModuleFlags = new List<Identifier>(); var pendingModuleFlags = new List<Identifier>();
bool availableModulesFound = true; bool availableModulesFound = true;
Identifier initialModuleFlag = generationParams.ModuleCounts.FirstOrDefault().Key; Identifier initialModuleFlag = generationParams.ModuleCounts.FirstOrDefault().Identifier;
pendingModuleFlags.Add(initialModuleFlag); pendingModuleFlags.Add(initialModuleFlag);
while (pendingModuleFlags.Count < totalModuleCount && availableModulesFound) while (pendingModuleFlags.Count < totalModuleCount && availableModulesFound)
{ {
availableModulesFound = false; availableModulesFound = false;
foreach (var moduleFlag in generationParams.ModuleCounts) 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; 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; continue;
} }
availableModulesFound = true; availableModulesFound = true;
pendingModuleFlags.Add(moduleFlag.Key); pendingModuleFlags.Add(moduleFlag.Identifier);
} }
} }
using (MD5 md5 = MD5.Create()) pendingModuleFlags.OrderBy(f => generationParams.ModuleCounts.First(m => m.Identifier == f)).ThenBy(f => Rand.Value(Rand.RandSync.ServerAndClient));
{
pendingModuleFlags.Sort((i1, i2) => (int)ToolBox.StringToUInt32Hash(i1.Value.ToLowerInvariant(), md5) - (int)ToolBox.StringToUInt32Hash(i2.Value.ToLowerInvariant(), md5));
}
pendingModuleFlags.Shuffle(Rand.RandSync.ServerAndClient);
while (pendingModuleFlags.Count < totalModuleCount) while (pendingModuleFlags.Count < totalModuleCount)
{ {
//don't place "none" modules at the end because //don't place "none" modules at the end because
@@ -610,7 +601,7 @@ namespace Barotrauma
Identifier flagToPlace = "none".ToIdentifier(); Identifier flagToPlace = "none".ToIdentifier();
SubmarineInfo nextModule = null; SubmarineInfo nextModule = null;
foreach (Identifier moduleFlag in pendingModuleFlags) foreach (Identifier moduleFlag in pendingModuleFlags.OrderByDescending(f => currentModule?.Info?.OutpostModuleInfo.AllowAttachToModules.Contains(f) ?? false))
{ {
flagToPlace = moduleFlag; flagToPlace = moduleFlag;
nextModule = GetRandomModule(currentModule?.Info?.OutpostModuleInfo, availableModules, flagToPlace, gapPosition, locationType, allowDifferentLocationType); 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.Left ||
module.ThisGapPosition == OutpostModuleInfo.GapPosition.Right; 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 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 ? MapEntity rightHull = module.ThisGap.Position.X > module.PreviousGap.Position.X ?
module.ThisGap.linkedTo.Count == 1 ? module.ThisGap.linkedTo[0] : module.ThisGap.linkedTo[1] : 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 IsCampaignCompatible => IsPlayer && !HasTag(SubmarineTag.Shuttle) && !HasTag(SubmarineTag.HideInMenus) && SubmarineClass != SubmarineClass.Undefined;
public bool IsCampaignCompatibleIgnoreClass => IsPlayer && !HasTag(SubmarineTag.Shuttle) && !HasTag(SubmarineTag.HideInMenus); public bool IsCampaignCompatibleIgnoreClass => IsPlayer && !HasTag(SubmarineTag.Shuttle) && !HasTag(SubmarineTag.HideInMenus);
public bool AllowPreviewImage => Type == SubmarineType.Player;
public Md5Hash MD5Hash public Md5Hash MD5Hash
{ {
get get
@@ -556,7 +558,7 @@ namespace Barotrauma
XDocument doc = new XDocument(newElement); XDocument doc = new XDocument(newElement);
doc.Root.Add(new XAttribute("name", Name)); doc.Root.Add(new XAttribute("name", Name));
if (previewImage != null) if (previewImage != null && AllowPreviewImage)
{ {
doc.Root.Add(new XAttribute("previewimage", Convert.ToBase64String(previewImage.ToArray()))); 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; } if (parentObject is Powered powered) { value = powered.Voltage; return true; }
} }
break; break;
case nameof(Powered.RelativeVoltage):
{
if (parentObject is Powered powered) { value = powered.RelativeVoltage; return true; }
}
break;
case nameof(Powered.CurrPowerConsumption): case nameof(Powered.CurrPowerConsumption):
{ {
if (parentObject is Powered powered) { value = powered.CurrPowerConsumption; return true; } 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 voiceCaptureChanged = currentConfig.Audio.VoiceCaptureDevice != newConfig.Audio.VoiceCaptureDevice;
bool textScaleChanged = Math.Abs(currentConfig.Graphics.TextScale - newConfig.Graphics.TextScale) > MathF.Pow(2.0f, -7); 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 = bool setGraphicsMode =
resolutionChanged || resolutionChanged ||
currentConfig.Graphics.VSync != newConfig.Graphics.VSync || currentConfig.Graphics.VSync != newConfig.Graphics.VSync ||
@@ -514,6 +516,10 @@ namespace Barotrauma
componentStyle.RefreshSize(); componentStyle.RefreshSize();
} }
} }
if (hudScaleChanged)
{
HUDLayoutSettings.CreateAreas();
}
GameMain.SoundManager?.ApplySettings(); GameMain.SoundManager?.ApplySettings();
#endif #endif
@@ -100,13 +100,23 @@ namespace Barotrauma
} }
#endif #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 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++) for (int i = 0; i < subDirs.Length; i++)
{ {
if (i == subDirs.Length - 1 && string.IsNullOrEmpty(subDirs[i])) if (i == subDirs.Length - 1 && string.IsNullOrEmpty(subDirs[i]))
+38
View File
@@ -1,3 +1,41 @@
---------------------------------------------------------------------------------------------------------
v0.18.7.0
---------------------------------------------------------------------------------------------------------
Unstable only:
- Reduced depth charge scale to fit in loader/racks better.
- Fixed ballast flora dying by itself client-side when transitioning to a new level.
- Disallow laying in vertically flipped normal bunks.
- Fixed broken door sprite stretching on the wrong axis when cutting a door.
- Fixed AI orders not working in multiplayer.
- Fixed tutorial characters starting with more items than they should.
- Fixed chat box closing when clicking any chat box elements after opening it with the keybind.
- Fixed submarine selection resetting when changing server settings in the campaign setup menu.
- Fixed "enable" and "disable" being wrong way around in the mod tab's context menus.
- Fixed sub editor always saving subs from a local mod in the root of that mod's folder instead of the actual location of that sub file.
- Fixed decoys not being containable in wrecked coilgun ammo shelves.
- Fixes and improvements to the new beacon stations and the enemies inside them.
- Fixed an issue during submarine switching when a drone / linked sub has been left behind (Regalis11/Barotrauma#9172): When you were switching back to a submarine whose drone you'd left behind, in the same level where you left the drone, the drone would get loaded and resaved during the item transfer, which caused it to no longer be considered "left behind". It also removed the drone's position from the save, and since it was not docked to anything either, positioning it failed at the beginning of the next round.
Changes:
- Adding preview images to wrecks, beacon stations, outposts or enemy subs isn't allowed in the sub editor (unnecessarily bloats up their file size, as the preview images aren't visible anywhere).
Fixes:
- Fixed server not refreshing the power grid when a client disconnects and reconnects a power wire.
- Fixed hull updates not being sent if the water/oxygen/fire in the hull doesn't change server-side, preventing the hull's status from getting corrected if a client somehow ends up out of sync.
- Fixed keybinds shown in the controls tab not refreshing when resetting the binds.
- Hopefully fixed colonies sometimes not including some modules (most often the armory module).
- Fixed ready checks sometimes ending at a slightly different time client-side compared to the server, allowing you to answer the prompt even though the time to answer already ended server-side.
- Fixed large terminal welcome messages going slightly outside the bounds of the listbox.
- Fixed overlapping in the tab menu's mission tab when there's more than one mission selected.
- Fixed fabricators and deconstructors playing the sounds even if they're out of power.
- Fixed occasional "hash mismatch for downloaded mod" errors on Linux.
- Fixed clients occasionally spawning as the old character after they've opted to create a new one. Only happened if the client hadn't died and was still controlling the old character at the end of the round.
- When a client creates a character with a new name, the client's name is changed to match it after they spawn as that character.
- Fixed enabled mods getting disabled when updating them in the mods menu.
- Fixed a rounding error in Sprite.DrawTiled that sometimes caused an extra 1-pixel line on some scaled and flipped structures (e.g. certain wall pieces scaled to 0.6).
- Fixed Orca 2 still using the old chaingun charge time.
--------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------
v0.18.6.0 v0.18.6.0
--------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------