Unstable 1.8.4.0

This commit is contained in:
Markus Isberg
2025-03-12 12:56:27 +00:00
parent a4c3e868e4
commit a4a3427e4e
627 changed files with 29860 additions and 10018 deletions
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Barotrauma.Networking;
namespace Barotrauma
{
@@ -19,7 +20,9 @@ namespace Barotrauma
protected GUIButton loadGameButton;
public Action<SubmarineInfo, string, string, CampaignSettings> StartNewGame;
public Action<string> LoadGame;
public delegate void LoadGameDelegate(string loadPath, Option<uint> backupIndex);
public LoadGameDelegate LoadGame;
protected enum CategoryFilter { All = 0, Vanilla = 1, Custom = 2 }
protected CategoryFilter subFilter = CategoryFilter.All;
@@ -142,6 +145,7 @@ namespace Barotrauma
public SettingValue<float> OxygenMultiplier;
public SettingValue<float> FuelMultiplier;
public SettingValue<float> MissionRewardMultiplier;
public SettingValue<float> ExperienceRewardMultiplier;
public SettingValue<float> ShopPriceMultiplier;
public SettingValue<float> ShipyardPriceMultiplier;
public SettingValue<float> RepairFailMultiplier;
@@ -164,6 +168,7 @@ namespace Barotrauma
OxygenMultiplier = OxygenMultiplier.GetValue(),
FuelMultiplier = FuelMultiplier.GetValue(),
MissionRewardMultiplier = MissionRewardMultiplier.GetValue(),
ExperienceRewardMultiplier = ExperienceRewardMultiplier.GetValue(),
ShopPriceMultiplier = ShopPriceMultiplier.GetValue(),
ShipyardPriceMultiplier = ShipyardPriceMultiplier.GetValue(),
RepairFailMultiplier = RepairFailMultiplier.GetValue(),
@@ -341,6 +346,19 @@ namespace Barotrauma
verticalSize,
OnValuesChanged);
// Experience reward multiplier
CampaignSettings.MultiplierSettings experienceMultiplierSettings = CampaignSettings.GetMultiplierSettings("ExperienceRewardMultiplier");
SettingValue<float> experienceMultiplier = CreateGUIFloatInputCarousel(
settingsList.Content,
TextManager.Get("campaignoption.experiencerewardmultiplier"),
TextManager.Get("campaignoption.experiencerewardmultiplier.tooltip"),
prevSettings.ExperienceRewardMultiplier,
valueStep: experienceMultiplierSettings.Step,
minValue: experienceMultiplierSettings.Min,
maxValue: experienceMultiplierSettings.Max,
verticalSize,
OnValuesChanged);
// Shop buying prices multiplier
CampaignSettings.MultiplierSettings shopPriceMultiplierSettings = CampaignSettings.GetMultiplierSettings("ShopPriceMultiplier");
SettingValue<float> shopPriceMultiplier = CreateGUIFloatInputCarousel(
@@ -498,6 +516,7 @@ namespace Barotrauma
oxygenMultiplier.SetValue(settings.OxygenMultiplier);
fuelMultiplier.SetValue(settings.FuelMultiplier);
rewardMultiplier.SetValue(settings.MissionRewardMultiplier);
experienceMultiplier.SetValue(settings.ExperienceRewardMultiplier);
shopPriceMultiplier.SetValue(settings.ShopPriceMultiplier);
shipyardPriceMultiplier.SetValue(settings.ShipyardPriceMultiplier);
repairFailMultiplier.SetValue(settings.RepairFailMultiplier);
@@ -527,6 +546,7 @@ namespace Barotrauma
OxygenMultiplier = oxygenMultiplier,
FuelMultiplier = fuelMultiplier,
MissionRewardMultiplier = rewardMultiplier,
ExperienceRewardMultiplier = experienceMultiplier,
ShopPriceMultiplier = shopPriceMultiplier,
ShipyardPriceMultiplier = shipyardPriceMultiplier,
RepairFailMultiplier = repairFailMultiplier,
@@ -821,5 +841,119 @@ namespace Barotrauma
return true;
}
protected void CreateBackupMenu(IEnumerable<SaveUtil.BackupIndexData> indexData, Action<SaveUtil.BackupIndexData> loadBackup)
{
var backupPopup = new GUIMessageBox("", "", new[] { TextManager.Get("Load"), TextManager.Get("Cancel") }, new Vector2(0.3f, 0.5f), minSize: new Point(500, 500));
GUILayoutGroup campaignSettingContent = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.8f), backupPopup.Content.RectTransform, Anchor.TopCenter));
GUIListBox backupList = new GUIListBox(new RectTransform(Vector2.One, campaignSettingContent.RectTransform));
bool isIronman = GameMain.NetworkMember?.ServerSettings is { IronmanModeActive: true };
if (!indexData.Any() || isIronman)
{
LocalizedString errorMsg = isIronman
? TextManager.Get("ironmanmodebackupdisclaimer")
: TextManager.Get("nobackups");
var errorBlock = new GUITextBlock(new RectTransform(Vector2.One, campaignSettingContent.RectTransform), errorMsg, font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Center)
{
TextColor = GUIStyle.Red,
IgnoreLayoutGroups = true
};
if (errorBlock.Font.MeasureString(errorMsg).X > campaignSettingContent.Rect.Width)
{
errorBlock.Wrap = true;
errorBlock.SetTextPos();
}
}
if (!isIronman)
{
foreach (var data in indexData.OrderByDescending(static i => i.SaveTime))
{
GUIFrame indexFrame = new GUIFrame(
new RectTransform(new Vector2(1.0f, 1f / SaveUtil.MaxBackupCount), backupList.Content.RectTransform), style: "ListBoxElement")
{
UserData = data
};
GUILayoutGroup indexLayout = new GUILayoutGroup(
new RectTransform(Vector2.One, indexFrame.RectTransform),
isHorizontal: true,
childAnchor: Anchor.CenterLeft)
{
RelativeSpacing = 0.05f,
Stretch = true
};
GUILayoutGroup leftLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 0.8f), indexLayout.RectTransform), childAnchor: Anchor.TopCenter)
{
RelativeSpacing = 0.05f,
Stretch = true
};
LocalizedString locationName = data.LocationType.IsEmpty || data.LocationNameIdentifier.IsEmpty ?
TextManager.Get("unknown") :
Location.GetName(data.LocationType, data.LocationNameFormatIndex, data.LocationNameIdentifier);
var locationNameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), leftLayout.RectTransform), locationName, textAlignment: Alignment.CenterLeft)
{
TextColor = Color.White
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), leftLayout.RectTransform), TextManager.Get($"savestate.{data.LevelType}"), textAlignment: Alignment.CenterLeft);
GUILayoutGroup rightLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 0.8f), indexLayout.RectTransform), childAnchor: Anchor.TopCenter)
{
RelativeSpacing = 0.05f,
Stretch = true
};
TimeSpan difference = SerializableDateTime.UtcNow - data.SaveTime;
double totalMinutes = difference.TotalMinutes;
LocalizedString timeFormat = totalMinutes switch
{
< 1 => TextManager.Get("subeditor.savedjustnow"),
> 60 => TextManager.GetWithVariable("saveagehours", "[hours]", ((int)Math.Floor(difference.TotalHours)).ToString()),
_ => TextManager.GetWithVariable("subeditor.saveageminutes", "[minutes]", difference.Minutes.ToString())
};
new GUITextBlock(new RectTransform(Vector2.One, rightLayout.RectTransform), timeFormat, textAlignment: Alignment.CenterRight);
locationNameBlock.Text = ToolBox.LimitString(locationName, locationNameBlock.Font, locationNameBlock.Rect.Width);
}
}
backupList.AfterSelected = (selected, _) =>
{
// to my understanding, there's no way to unselect an item in a GUIListBox
// so no need to check if selected is null
backupPopup.Buttons[0].Enabled = true;
return true;
};
backupPopup.Buttons[1].OnClicked += (button, o) =>
{
backupPopup.Close();
return true;
};
backupPopup.Buttons[0].Enabled = false;
backupPopup.Buttons[0].OnClicked += (button, o) =>
{
if (backupList.SelectedComponent?.UserData is not SaveUtil.BackupIndexData selectedIndexData) { return false; }
backupPopup.Close();
loadBackup?.Invoke(selectedIndexData);
return true;
};
}
}
}
@@ -1,12 +1,16 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Barotrauma.Networking;
namespace Barotrauma
{
class MultiPlayerCampaignSetupUI : CampaignSetupUI
{
private GUIButton rollbackSaveButton;
private GUIButton deleteMpSaveButton;
private int prevInitialMoney;
@@ -232,34 +236,140 @@ namespace Barotrauma
{
if (saveList.SelectedData is not CampaignMode.SaveInfo saveInfo) { return false; }
if (string.IsNullOrWhiteSpace(saveInfo.FilePath)) { return false; }
LoadGame?.Invoke(saveInfo.FilePath);
CoroutineManager.StartCoroutine(WaitForCampaignSetup(), "WaitForCampaignSetup");
if (saveInfo.RespawnMode != RespawnMode.None && saveInfo.RespawnMode != GameMain.NetworkMember?.ServerSettings?.RespawnMode)
{
var msgBox = new GUIMessageBox(TextManager.Get("Warning"),
TextManager.GetWithVariables("RespawnModeMismatch",
("[currentrespawnmode]", TextManager.Get($"respawnmode.{GameMain.NetworkMember?.ServerSettings?.RespawnMode}")),
("[savedrespawnmode]", TextManager.Get($"respawnmode.{saveInfo.RespawnMode}"))),
new LocalizedString[] { TextManager.Get("RespawnModeMismatch.GoBack"), TextManager.Get("RespawnModeMismatch.LoadAnyway") });
msgBox.Buttons[0].OnClicked = (button, obj) =>
{
msgBox.Close();
return true;
};
msgBox.Buttons[1].OnClicked = (button, obj) =>
{
msgBox.Close();
LoadSaveGame();
return true;
};
return false;
}
LoadSaveGame();
return true;
void LoadSaveGame()
{
LoadGame?.Invoke(saveInfo.FilePath, backupIndex: Option.None);
CoroutineManager.StartCoroutine(WaitForCampaignSetup(), "WaitForCampaignSetup");
}
},
Enabled = false
};
deleteMpSaveButton = new GUIButton(new RectTransform(new Vector2(0.45f, 0.12f), loadGameContainer.RectTransform, Anchor.BottomLeft),
TextManager.Get("Delete"), style: "GUIButtonSmall")
GUILayoutGroup leftButtonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.45f, 0.15f), loadGameContainer.RectTransform, Anchor.BottomLeft))
{
RelativeSpacing = 0.05f,
Stretch = true
};
rollbackSaveButton = new GUIButton(new RectTransform(new Vector2(1f, 0.5f), leftButtonContainer.RectTransform), TextManager.Get("rollbackbutton"), style: "GUIButtonSmallFreeScale")
{
Visible = false,
ToolTip = TextManager.Get("backuptooltip"),
OnClicked = ViewBackupSaveMenu
};
deleteMpSaveButton = new GUIButton(new RectTransform(new Vector2(1f, 0.5f), leftButtonContainer.RectTransform),
TextManager.Get("Delete"), style: "GUIButtonSmallFreeScale")
{
OnClicked = DeleteSave,
Visible = false
};
}
}
private bool ViewBackupSaveMenu(GUIButton button, object obj)
{
if (obj is not CampaignMode.SaveInfo saveInfo) { return false; }
if (string.IsNullOrWhiteSpace(saveInfo.FilePath)) { return false; }
if (GameMain.Client.IsServerOwner)
{
CreateBackupMenu(SaveUtil.GetIndexData(saveInfo.FilePath), index =>
{
LoadGame(saveInfo.FilePath, backupIndex: Option.Some(index.Index));
});
}
else
{
RequestBackupIndexData(saveInfo.FilePath);
}
return true;
}
private const string PleaseWaitUserData = "PleaseWaitPopup";
private void RequestBackupIndexData(string savePath)
{
if (GameMain.Client == null) { return; }
GUI.SetCursorWaiting();
var msgBox = new GUIMessageBox(TextManager.Get("CampaignStartingPleaseWait"), TextManager.Get("CampaignStarting"), new[] { TextManager.Get("Cancel") })
{
UserData = PleaseWaitUserData
};
msgBox.Buttons[0].OnClicked = (btn, obj) =>
{
GUI.ClearCursorWait();
return true;
};
IWriteMessage msg = new WriteOnlyMessage().WithHeader(ClientPacketHeader.REQUEST_BACKUP_INDICES);
msg.WriteString(savePath);
GameMain.Client?.ClientPeer?.Send(msg, DeliveryMethod.Reliable);
}
public void OnBackupIndicesReceived(IReadMessage message)
{
GUI.ClearCursorWait();
foreach (GUIComponent component in GUIMessageBox.MessageBoxes.Where(static mb => mb.UserData is PleaseWaitUserData).ToArray())
{
if (component is GUIMessageBox msgBox)
{
msgBox.Close();
}
}
string path = message.ReadString();
var indexData = INetSerializableStruct.Read<NetCollection<SaveUtil.BackupIndexData>>(message);
CreateBackupMenu(indexData, selectedIndex =>
{
LoadGame?.Invoke(path, backupIndex: Option.Some(selectedIndex.Index));
});
}
private bool SelectSaveFile(GUIComponent component, object obj)
{
if (obj is not CampaignMode.SaveInfo saveInfo) { return true; }
string fileName = saveInfo.FilePath;
loadGameButton.Enabled = true;
rollbackSaveButton.Visible = true;
deleteMpSaveButton.Visible = deleteMpSaveButton.Enabled = GameMain.Client.IsServerOwner;
deleteMpSaveButton.Enabled = GameMain.GameSession?.SavePath != fileName;
rollbackSaveButton.Enabled = deleteMpSaveButton.Enabled = GameMain.GameSession?.DataPath.LoadPath != fileName;
if (deleteMpSaveButton.Visible)
{
deleteMpSaveButton.UserData = saveInfo;
}
if (rollbackSaveButton.Visible)
{
rollbackSaveButton.UserData = saveInfo;
}
return true;
}
}
@@ -291,7 +291,7 @@ namespace Barotrauma
if (characterInfos.Count >= 3) { break; }
}
}
characterInfos.Sort((a, b) => Math.Sign(b.Job.MinKarma - a.Job.MinKarma));
characterInfos.Sort((a, b) => Math.Sign(a.Job.CampaignSetupUIOrder - b.Job.CampaignSetupUIOrder));
characterInfoColumns.ClearChildren();
CharacterMenus?.ForEach(m => m.Dispose());
@@ -632,7 +632,7 @@ namespace Barotrauma
saveFrame.GetChild<GUITextBlock>().TextColor = GUIStyle.Red;
continue;
}
if (docRoot.GetChildElement("multiplayercampaign") != null)
if (docRoot.GetAttributeBool("ismultiplayer", false))
{
//multiplayer campaign save in the wrong folder -> don't show the save
saveList.Content.RemoveChild(saveFrame);
@@ -653,8 +653,7 @@ namespace Barotrauma
{
if (saveList.SelectedData is not CampaignMode.SaveInfo saveInfo) { return false; }
if (string.IsNullOrWhiteSpace(saveInfo.FilePath)) { return false; }
LoadGame?.Invoke(saveInfo.FilePath);
LoadGame?.Invoke(saveInfo.FilePath, backupIndex: Option.None);
return true;
},
Enabled = false
@@ -685,6 +684,15 @@ namespace Barotrauma
string mapseed = docRoot.GetAttributeString("mapseed", "unknown");
Identifier locationNameIdentifier = docRoot.GetAttributeIdentifier("currentlocation", Identifier.Empty);
int locationNameFormatIndex = docRoot.GetAttributeInt("currentlocationnameformatindex", -1);
Identifier locationType = docRoot.GetAttributeIdentifier("locationtype", Identifier.Empty);
LevelData.LevelType levelType = docRoot.GetAttributeEnum("nextleveltype", LevelData.LevelType.LocationConnection);
LocalizedString locationName = locationType.IsEmpty || locationNameIdentifier.IsEmpty ?
LocalizedString.EmptyString :
Location.GetName(locationType, locationNameFormatIndex, locationNameIdentifier);
var saveFileFrame = new GUIFrame(
new RectTransform(new Vector2(0.45f, 0.6f), loadGameContainer.RectTransform, Anchor.TopRight)
{
@@ -708,6 +716,15 @@ namespace Barotrauma
RelativeOffset = new Vector2(0, 0.1f)
});
if (!locationName.IsNullOrEmpty())
{
new GUITextBlock(new RectTransform(new Vector2(1, 0), layoutGroup.RectTransform),
locationName, font: GUIStyle.SmallFont);
new GUITextBlock(new RectTransform(new Vector2(1, 0), layoutGroup.RectTransform),
TextManager.Get($"savestate.{levelType}"), font: GUIStyle.SmallFont);
//spacing
new GUIFrame(new RectTransform(new Vector2(0.0f, 0.05f), layoutGroup.RectTransform), style: null);
}
new GUITextBlock(new RectTransform(new Vector2(1, 0), layoutGroup.RectTransform),
$"{TextManager.Get("Submarine")} : {subName}", font: GUIStyle.SmallFont);
new GUITextBlock(new RectTransform(new Vector2(1, 0), layoutGroup.RectTransform),
@@ -715,15 +732,40 @@ namespace Barotrauma
new GUITextBlock(new RectTransform(new Vector2(1, 0), layoutGroup.RectTransform),
$"{TextManager.Get("MapSeed")} : {mapseed}", font: GUIStyle.SmallFont);
new GUIButton(new RectTransform(new Vector2(0.4f, 0.15f), saveFileFrame.RectTransform, Anchor.BottomCenter)
GUILayoutGroup buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.85f, 0.15f), saveFileFrame.RectTransform, Anchor.BottomCenter)
{
RelativeOffset = new Vector2(0, 0.1f)
}, TextManager.Get("Delete"), style: "GUIButtonSmall")
}, isHorizontal: true)
{
RelativeSpacing = 0.05f,
Stretch = true
};
new GUIButton(new RectTransform(new Vector2(0.5f, 1f), buttonContainer.RectTransform, Anchor.CenterLeft), TextManager.Get("Delete"), style: "GUIButtonSmall")
{
UserData = saveInfo,
OnClicked = DeleteSave
};
new GUIButton(new RectTransform(new Vector2(0.5f, 1f), buttonContainer.RectTransform, Anchor.CenterLeft), TextManager.Get("rollbackbutton"), style: "GUIButtonSmall")
{
UserData = saveInfo,
ToolTip = TextManager.Get("backuptooltip"),
OnClicked = ViewBackupMenu
};
return true;
}
private bool ViewBackupMenu(GUIButton btn, object obj)
{
if (obj is not CampaignMode.SaveInfo saveInfo) { return false; }
var indexData = SaveUtil.GetIndexData(saveInfo.FilePath);
CreateBackupMenu(indexData, index =>
{
LoadGame(saveInfo.FilePath, Option.Some(index.Index));
});
return true;
}
@@ -259,7 +259,7 @@ namespace Barotrauma
{
AutoScaleHorizontal = true
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), location.Type.Name, font: GUIStyle.SubHeadingFont);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), location.GetLocationTypeToDisplay().Name, font: GUIStyle.SubHeadingFont);
Sprite portrait = location.Type.GetPortrait(location.PortraitId);
portrait.EnsureLazyLoaded();
@@ -443,20 +443,22 @@ namespace Barotrauma
GUILayoutGroup difficultyIndicatorGroup = null;
if (mission.Difficulty.HasValue)
{
difficultyIndicatorGroup = new GUILayoutGroup(new RectTransform(Vector2.One * 0.9f, missionName.RectTransform, anchor: Anchor.CenterRight, scaleBasis: ScaleBasis.Smallest) { AbsoluteOffset = new Point((int)missionName.Padding.Z, 0) },
difficultyIndicatorGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 0.9f), missionName.RectTransform, anchor: Anchor.CenterRight) { AbsoluteOffset = new Point((int)missionName.Padding.Z, 0) },
isHorizontal: true, childAnchor: Anchor.CenterRight)
{
AbsoluteSpacing = 1,
UserData = "difficulty"
UserData = "difficulty",
};
difficultyIndicatorGroup.SetAsFirstChild();
var difficultyColor = mission.GetDifficultyColor();
for (int i = 0; i < mission.Difficulty; i++)
{
new GUIImage(new RectTransform(Vector2.One, difficultyIndicatorGroup.RectTransform, scaleBasis: ScaleBasis.Smallest) { IsFixedSize = true }, "DifficultyIndicator", scaleToFit: true)
new GUIImage(new RectTransform(Vector2.One * 0.9f, difficultyIndicatorGroup.RectTransform, scaleBasis: ScaleBasis.Smallest) { IsFixedSize = true }, "DifficultyIndicator", scaleToFit: true)
{
Color = difficultyColor,
SelectedColor = difficultyColor,
HoverColor = difficultyColor
HoverColor = difficultyColor,
ToolTip = mission.GetDifficultyToolTipText()
};
}
}
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Extensions;
using Barotrauma.Sounds;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
#if DEBUG
@@ -115,7 +116,7 @@ namespace Barotrauma.CharacterEditor
{
base.Select();
GameMain.SoundManager.SetCategoryGainMultiplier("waterambience", 0.0f, 0);
GameMain.SoundManager.SetCategoryGainMultiplier(SoundManager.SoundCategoryWaterAmbience, 0.0f, 0);
GUI.ForceMouseOn(null);
if (Submarine.MainSub == null)
@@ -236,7 +237,7 @@ namespace Barotrauma.CharacterEditor
protected override void DeselectEditorSpecific()
{
SoundPlayer.OverrideMusicType = Identifier.Empty;
GameMain.SoundManager.SetCategoryGainMultiplier("waterambience", GameSettings.CurrentConfig.Audio.SoundVolume, 0);
GameMain.SoundManager.SetCategoryGainMultiplier(SoundManager.SoundCategoryWaterAmbience, GameSettings.CurrentConfig.Audio.SoundVolume, 0);
GUI.ForceMouseOn(null);
if (isEndlessRunner)
{
@@ -746,6 +747,12 @@ namespace Barotrauma.CharacterEditor
minorModesToggle?.UpdateOpenState((float)deltaTime, new Vector2(-minorModesPanel.Rect.Width - leftArea.RectTransform.AbsoluteOffset.X, 0), minorModesPanel.RectTransform);
modesToggle?.UpdateOpenState((float)deltaTime, new Vector2(-modesPanel.Rect.Width - leftArea.RectTransform.AbsoluteOffset.X, 0), modesPanel.RectTransform);
buttonsPanelToggle?.UpdateOpenState((float)deltaTime, new Vector2(-buttonsPanel.Rect.Width - leftArea.RectTransform.AbsoluteOffset.X, 0), buttonsPanel.RectTransform);
totalMassText.Text = GetTotalMassText();
}
private LocalizedString GetTotalMassText()
{
return TextManager.GetWithVariable($"{screenTextTag}totalmass", "[mass]", character?.AnimController?.Mass.FormatZeroDecimal() ?? "0");
}
public CursorState GetMouseCursorState()
@@ -1089,9 +1096,12 @@ namespace Barotrauma.CharacterEditor
}
else if (PlayerInput.PrimaryMouseButtonClicked())
{
jointStartLimb = GetClosestLimbOnSpritesheet(PlayerInput.MousePosition, l => selectedLimbs.Contains(l) && !l.Hidden);
anchor1Pos = GetLimbSpritesheetRect(jointStartLimb).Center.ToVector2() - PlayerInput.MousePosition;
jointCreationMode = JointCreationMode.Create;
jointStartLimb = GetClosestLimbOnSpritesheet(PlayerInput.MousePosition, l => selectedLimbs.Contains(l));
if (jointStartLimb != null)
{
anchor1Pos = GetLimbSpritesheetRect(jointStartLimb).Center.ToVector2() - PlayerInput.MousePosition;
jointCreationMode = JointCreationMode.Create;
}
}
}
else
@@ -1110,8 +1120,11 @@ namespace Barotrauma.CharacterEditor
else if (PlayerInput.PrimaryMouseButtonClicked())
{
jointStartLimb = GetClosestLimbOnRagdoll(PlayerInput.MousePosition, l => selectedLimbs.Contains(l) && !l.Hidden);
anchor1Pos = ConvertUnits.ToDisplayUnits(jointStartLimb.body.FarseerBody.GetLocalPoint(ScreenToSim(PlayerInput.MousePosition)));
jointCreationMode = JointCreationMode.Create;
if (jointStartLimb != null)
{
anchor1Pos = ConvertUnits.ToDisplayUnits(jointStartLimb.body.FarseerBody.GetLocalPoint(ScreenToSim(PlayerInput.MousePosition)));
jointCreationMode = JointCreationMode.Create;
}
}
}
}
@@ -1494,10 +1507,10 @@ namespace Barotrauma.CharacterEditor
{
DebugConsole.NewMessage(GetCharacterEditorTranslation("TryingToSpawnCharacter").Replace("[config]", speciesName.ToString()), Color.HotPink);
OnPreSpawn();
bool dontFollowCursor = true;
bool followCursor = false;
if (character != null)
{
dontFollowCursor = character.dontFollowCursor;
followCursor = character.FollowCursor;
RagdollParams.ClearHistory();
CurrentAnimation.ClearHistory();
if (!character.Removed)
@@ -1510,7 +1523,7 @@ namespace Barotrauma.CharacterEditor
{
var characterInfo = new CharacterInfo(speciesName, jobOrJobPrefab: JobPrefab.Prefabs[selectedJob.Value]);
character = Character.Create(speciesName, spawnPosition, ToolBox.RandomSeed(8), characterInfo, hasAi: false, ragdoll: ragdoll);
character.GiveJobItems();
character.GiveJobItems(isPvPMode: false);
HideWearables();
if (displayWearables)
{
@@ -1525,7 +1538,7 @@ namespace Barotrauma.CharacterEditor
}
if (character != null)
{
character.dontFollowCursor = dontFollowCursor;
character.FollowCursor = followCursor;
}
if (character == null)
{
@@ -1693,8 +1706,8 @@ namespace Barotrauma.CharacterEditor
}
else
{
config.SetAttributeValue("speciesname", name, StringComparison.OrdinalIgnoreCase);
config.SetAttributeValue("humanoid", isHumanoid, StringComparison.OrdinalIgnoreCase);
config.TrySetAttributeValue("speciesname", name);
config.TrySetAttributeValue("humanoid", isHumanoid);
var ragdollElement = config.GetChildElement("ragdolls");
if (ragdollElement == null)
{
@@ -1758,7 +1771,7 @@ namespace Barotrauma.CharacterEditor
// Ragdoll
RagdollParams.ClearCache();
string ragdollPath = RagdollParams.GetDefaultFile(name, contentPackage);
string ragdollPath = RagdollParams.GetDefaultFile(name);
RagdollParams ragdollParams = isHumanoid
? RagdollParams.CreateDefault<HumanRagdollParams>(ragdollPath, name, ragdoll)
: RagdollParams.CreateDefault<FishRagdollParams>(ragdollPath, name, ragdoll);
@@ -1777,7 +1790,7 @@ namespace Barotrauma.CharacterEditor
XElement element = animation.MainElement;
if (element == null) { continue; }
element.SetAttributeValue("type", name);
string fullPath = AnimationParams.GetDefaultFile(name, animation.AnimationType);
string fullPath = AnimationParams.GetDefaultFilePath(name, animation.AnimationType);
element.Name = AnimationParams.GetDefaultFileName(name, animation.AnimationType);
#if DEBUG
element.Save(fullPath);
@@ -1805,7 +1818,7 @@ namespace Barotrauma.CharacterEditor
default: continue;
}
Type type = AnimationParams.GetParamTypeFromAnimType(animType, isHumanoid);
string fullPath = AnimationParams.GetDefaultFile(name, animType);
string fullPath = AnimationParams.GetDefaultFilePath(name, animType);
AnimationParams.Create(fullPath, name, animType, type);
}
}
@@ -1845,6 +1858,7 @@ namespace Barotrauma.CharacterEditor
private GUILayoutGroup rightArea, leftArea;
private GUIFrame centerArea;
private GUITextBlock totalMassText;
private GUIFrame characterSelectionPanel;
private GUIFrame fileEditPanel;
private GUIFrame modesPanel;
@@ -1920,16 +1934,13 @@ namespace Barotrauma.CharacterEditor
centerArea = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.95f), parent: Frame.RectTransform, anchor: Anchor.TopRight)
{
AbsoluteOffset = new Point((int)(rightArea.RectTransform.ScaledSize.X + rightArea.RectTransform.RelativeOffset.X * rightArea.RectTransform.Parent.ScaledSize.X + (int)(20 * GUI.xScale)), (int)(20 * GUI.yScale))
}, style: null)
{ CanBeFocused = false };
leftArea = new GUILayoutGroup(new RectTransform(new Vector2(0.15f, 0.95f), parent: Frame.RectTransform, anchor: Anchor.CenterLeft), childAnchor: Anchor.BottomLeft)
{
RelativeSpacing = 0.02f
};
Vector2 toggleSize = new Vector2(1.0f, 0.03f);
CreateFileEditPanel();
CreateOptionsPanel(toggleSize);
CreateCharacterSelectionPanel();
@@ -1941,12 +1952,11 @@ namespace Barotrauma.CharacterEditor
optionsPanel.RectTransform.MinSize = new Point(0, (int)(optionsPanel.GetChild<GUILayoutGroup>().RectTransform.Children.Sum(c => c.Rect.Height) / innerScale.Y));
rightArea.Recalculate();
}
CreateButtonsPanel();
CreateModesPanel(toggleSize);
CreateMinorModesPanel(toggleSize);
CreateContextualControls();
totalMassText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.01f), rightArea.RectTransform), GetTotalMassText());
}
private void CreateMinorModesPanel(Vector2 toggleSize)
@@ -2209,10 +2219,10 @@ namespace Barotrauma.CharacterEditor
};
new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("FollowCursor"))
{
Selected = !character.dontFollowCursor,
Selected = character.FollowCursor,
OnSelected = box =>
{
character.dontFollowCursor = !box.Selected;
character.FollowCursor = box.Selected;
return true;
}
};
@@ -2710,7 +2720,7 @@ namespace Barotrauma.CharacterEditor
}, elementCount: 8, style: null);
jobDropDown.ListBox.Color = new Color(jobDropDown.ListBox.Color.R, jobDropDown.ListBox.Color.G, jobDropDown.ListBox.Color.B, byte.MaxValue);
jobDropDown.AddItem("None");
JobPrefab.Prefabs.ForEach(j => jobDropDown.AddItem(j.Name, j.Identifier));
JobPrefab.Prefabs.Where(j => !j.HiddenJob).ForEach(j => jobDropDown.AddItem(j.Name, j.Identifier));
jobDropDown.SelectItem(selectedJob);
jobDropDown.OnSelected = (component, data) =>
{
@@ -3112,7 +3122,7 @@ namespace Barotrauma.CharacterEditor
{
CharacterParams.Reset(true);
AnimParams.ForEach(p => p.Reset(true));
character.AnimController.ResetRagdoll(forceReload: true);
character.AnimController.ResetRagdoll();
RecreateRagdoll();
jointCreationMode = JointCreationMode.None;
isDrawingLimb = false;
@@ -3566,20 +3576,23 @@ namespace Barotrauma.CharacterEditor
int offsetX = spriteSheetOffsetX;
int offsetY = spriteSheetOffsetY;
Rectangle rect = Rectangle.Empty;
for (int i = 0; i < Textures.Count; i++)
if (Textures != null)
{
if (limb.ActiveSprite.FilePath != texturePaths[i])
for (int i = 0; i < Textures.Count; i++)
{
offsetY += (int)(Textures[i].Height * spriteSheetZoom);
}
else
{
rect = limb.ActiveSprite.SourceRect;
rect.Size = rect.MultiplySize(spriteSheetZoom);
rect.Location = rect.Location.Multiply(spriteSheetZoom);
rect.X += offsetX;
rect.Y += offsetY;
break;
if (limb.ActiveSprite.FilePath != texturePaths[i])
{
offsetY += (int)(Textures[i].Height * spriteSheetZoom);
}
else
{
rect = limb.ActiveSprite.SourceRect;
rect.Size = rect.MultiplySize(spriteSheetZoom);
rect.Location = rect.Location.Multiply(spriteSheetZoom);
rect.X += offsetX;
rect.Y += offsetY;
break;
}
}
}
return rect;
@@ -300,7 +300,7 @@ namespace Barotrauma.CharacterEditor
string destinationDir = Path.GetDirectoryName(destinationPath);
if (!Directory.Exists(destinationDir))
{
Directory.CreateDirectory(destinationDir);
Directory.CreateDirectory(destinationDir, catchUnauthorizedAccessExceptions: true);
}
if (!File.Exists(destinationPath))
@@ -198,7 +198,7 @@ namespace Barotrauma
msgBox.Close();
string path = Path.Combine(exportPath, $"{nameInput.Text}.xml");
File.WriteAllText(path, save.ToString());
File.WriteAllText(path, save.ToString(), catchUnauthorizedAccessExceptions: false);
AskForConfirmation(TextManager.Get("EventEditor.OpenTextHeader"), TextManager.Get("EventEditor.OpenTextBody"), () =>
{
ToolBox.OpenFileWithShell(path);
@@ -942,7 +942,7 @@ namespace Barotrauma
return false;
}
GameSession gameSession = new GameSession(subInfo, "", GameModePreset.TestMode, CampaignSettings.Empty, null);
GameSession gameSession = new GameSession(subInfo, Option.None, CampaignDataPath.Empty, GameModePreset.TestMode, CampaignSettings.Empty, null);
TestGameMode gameMode = ((TestGameMode?)gameSession.GameMode) ?? throw new InvalidCastException();
gameMode.SpawnOutpost = true;
@@ -5,6 +5,7 @@ using Microsoft.Xna.Framework.Graphics;
using System;
using System.Diagnostics;
using System.Linq;
using System.Transactions;
namespace Barotrauma
{
@@ -15,6 +16,8 @@ namespace Barotrauma
private RenderTarget2D renderTargetWater;
private RenderTarget2D renderTargetFinal;
private RenderTarget2D renderTargetDamageable;
public readonly Effect DamageEffect;
private readonly Texture2D damageStencil;
private readonly Texture2D distortTexture;
@@ -65,6 +68,7 @@ namespace Barotrauma
renderTargetBackground = new RenderTarget2D(graphics, GameMain.GraphicsWidth, GameMain.GraphicsHeight);
renderTargetWater = new RenderTarget2D(graphics, GameMain.GraphicsWidth, GameMain.GraphicsHeight);
renderTargetFinal = new RenderTarget2D(graphics, GameMain.GraphicsWidth, GameMain.GraphicsHeight, false, SurfaceFormat.Color, DepthFormat.None);
renderTargetDamageable = new RenderTarget2D(graphics, GameMain.GraphicsWidth, GameMain.GraphicsHeight, false, SurfaceFormat.Color, DepthFormat.None);
}
public override void AddToGUIUpdateList()
@@ -133,19 +137,7 @@ namespace Barotrauma
if (Character.Controlled == null && !GUI.DisableHUD)
{
for (int i = 0; i < Submarine.MainSubs.Length; i++)
{
if (Submarine.MainSubs[i] == null) continue;
if (Level.Loaded != null && Submarine.MainSubs[i].WorldPosition.Y < Level.MaxEntityDepth) { continue; }
Vector2 position = Submarine.MainSubs[i].SubBody != null ? Submarine.MainSubs[i].WorldPosition : Submarine.MainSubs[i].HiddenSubPosition;
Color indicatorColor = i == 0 ? Color.LightBlue * 0.5f : GUIStyle.Red * 0.5f;
GUI.DrawIndicator(
spriteBatch, position, cam,
Math.Max(Submarine.MainSub.Borders.Width, Submarine.MainSub.Borders.Height),
GUIStyle.SubmarineLocationIcon.Value.Sprite, indicatorColor);
}
DrawPositionIndicators(spriteBatch);
}
if (!GUI.DisableHUD)
@@ -164,7 +156,118 @@ namespace Barotrauma
GameMain.PerformanceCounter.AddElapsedTicks("Draw:HUD", sw.ElapsedTicks);
sw.Restart();
}
private void DrawPositionIndicators(SpriteBatch spriteBatch)
{
Sprite subLocationSprite = GUIStyle.SubLocationIcon.Value?.Sprite;
Sprite shuttleSprite = GUIStyle.ShuttleIcon.Value?.Sprite;
Sprite wreckSprite = GUIStyle.WreckIcon.Value?.Sprite;
Sprite caveSprite = GUIStyle.CaveIcon.Value?.Sprite;
Sprite outpostSprite = GUIStyle.OutpostIcon.Value?.Sprite;
Sprite ruinSprite = GUIStyle.RuinIcon.Value?.Sprite;
Sprite enemySprite = GUIStyle.EnemyIcon.Value?.Sprite;
Sprite corpseSprite = GUIStyle.CorpseIcon.Value?.Sprite;
Sprite beaconSprite = GUIStyle.BeaconIcon.Value?.Sprite;
for (int i = 0; i < Submarine.MainSubs.Length; i++)
{
if (Submarine.MainSubs[i] == null) { continue; }
if (Level.Loaded != null && Submarine.MainSubs[i].WorldPosition.Y < Level.MaxEntityDepth) { continue; }
Vector2 position = Submarine.MainSubs[i].SubBody != null ? Submarine.MainSubs[i].WorldPosition : Submarine.MainSubs[i].HiddenSubPosition;
Color indicatorColor = i == 0 ? Color.LightBlue * 0.5f : GUIStyle.Red * 0.5f;
Sprite displaySprite = Submarine.MainSubs[i].Info.HasTag(SubmarineTag.Shuttle) ? shuttleSprite : subLocationSprite;
if (displaySprite != null)
{
GUI.DrawIndicator(
spriteBatch, position, cam,
Math.Max(Submarine.MainSubs[i].Borders.Width, Submarine.MainSubs[i].Borders.Height),
displaySprite, indicatorColor);
}
}
if (!GameMain.DevMode) { return;}
if (Level.Loaded != null)
{
foreach (Level.Cave cave in Level.Loaded.Caves)
{
Vector2 position = cave.StartPos.ToVector2();
Color indicatorColor = Color.Yellow * 0.5f;
if (caveSprite != null)
{
GUI.DrawIndicator(
spriteBatch, position, cam, hideDist: 3000f,
caveSprite, indicatorColor);
}
}
}
foreach (Submarine submarine in Submarine.Loaded)
{
if (Submarine.MainSubs.Contains(submarine)) { continue; }
Vector2 position = submarine.WorldPosition;
Color teamColorIndicator = submarine.TeamID switch
{
CharacterTeamType.Team1 => Color.LightBlue * 0.5f,
CharacterTeamType.Team2 => GUIStyle.Red * 0.5f,
CharacterTeamType.FriendlyNPC => GUIStyle.Yellow * 0.5f,
_ => Color.Green * 0.5f
};
Color indicatorColor = submarine.Info.Type switch
{
SubmarineType.Outpost => Color.LightGreen,
SubmarineType.Wreck => Color.SaddleBrown,
SubmarineType.BeaconStation => Color.Azure,
SubmarineType.Ruin => Color.Purple,
_ => teamColorIndicator
};
Sprite displaySprite = submarine.Info.Type switch
{
SubmarineType.Outpost => outpostSprite,
SubmarineType.Wreck => wreckSprite,
SubmarineType.BeaconStation => beaconSprite,
SubmarineType.Ruin => ruinSprite,
_ => subLocationSprite
};
// use a little dimmer color for transports
if (submarine.Info.SubmarineClass == SubmarineClass.Transport) { indicatorColor *= 0.75f; }
if (displaySprite != null)
{
GUI.DrawIndicator(
spriteBatch, position, cam, hideDist: Math.Max(submarine.Borders.Width, submarine.Borders.Height),
displaySprite, indicatorColor);
}
}
// markers for all enemies and corpses
foreach (Character character in Character.CharacterList)
{
Vector2 position = character.WorldPosition;
Color indicatorColor = Color.DarkRed * 0.5f;
if (character.IsDead) { indicatorColor = Color.DarkGray * 0.5f; }
if (character.TeamID != CharacterTeamType.None) { continue;}
Sprite displaySprite = character.IsDead ? corpseSprite : enemySprite;
if (displaySprite != null)
{
GUI.DrawIndicator(
spriteBatch, position, cam, hideDist: 3000f,
displaySprite, indicatorColor);
}
}
}
public void DrawMap(GraphicsDevice graphics, SpriteBatch spriteBatch, double deltaTime)
{
foreach (Submarine sub in Submarine.Loaded)
@@ -203,7 +306,7 @@ namespace Barotrauma
//Draw background structures and wall background sprites
//(= the background texture that's revealed when a wall is destroyed) into the background render target
//These will be visible through the LOS effect.
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform);
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, depthStencilState: DepthStencilState.None, transformMatrix: cam.Transform);
Submarine.DrawBack(spriteBatch, false, e => e is Structure s && (e.SpriteDepth >= 0.9f || s.Prefab.BackgroundSprite != null) && !IsFromOutpostDrawnBehindSubs(e));
Submarine.DrawPaintedColors(spriteBatch, false);
spriteBatch.End();
@@ -212,8 +315,25 @@ namespace Barotrauma
GameMain.PerformanceCounter.AddElapsedTicks("Draw:Map:BackStructures", sw.ElapsedTicks);
sw.Restart();
graphics.SetRenderTarget(renderTargetDamageable);
graphics.Clear(Color.Transparent);
DamageEffect.CurrentTechnique = DamageEffect.Techniques["StencilShader"];
DamageEffect.CurrentTechnique.Passes[0].Apply();
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, SamplerState.LinearWrap, effect: DamageEffect, transformMatrix: cam.Transform);
Submarine.DrawDamageable(spriteBatch, DamageEffect, false);
DamageEffect.Parameters["aCutoff"].SetValue(0.0f);
DamageEffect.Parameters["cCutoff"].SetValue(0.0f);
Submarine.DamageEffectCutoff = 0.0f;
DamageEffect.CurrentTechnique.Passes[0].Apply();
spriteBatch.End();
sw.Stop();
GameMain.PerformanceCounter.AddElapsedTicks("Draw:Map:FrontDamageable", sw.ElapsedTicks);
sw.Restart();
graphics.SetRenderTarget(null);
GameMain.LightManager.RenderLightMap(graphics, spriteBatch, cam, renderTarget);
GameMain.LightManager.RenderLightMap(graphics, spriteBatch, cam, renderTargetDamageable);
sw.Stop();
GameMain.PerformanceCounter.AddElapsedTicks("Draw:Map:Lighting", sw.ElapsedTicks);
@@ -231,24 +351,24 @@ namespace Barotrauma
Level.Loaded.DrawBack(graphics, spriteBatch, cam);
}
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform);
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, depthStencilState: DepthStencilState.None, transformMatrix: cam.Transform);
Submarine.DrawBack(spriteBatch, false, e => e is Structure s && (e.SpriteDepth >= 0.9f || s.Prefab.BackgroundSprite != null) && IsFromOutpostDrawnBehindSubs(e));
spriteBatch.End();
//draw alpha blended particles that are in water and behind subs
#if LINUX || OSX
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform);
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, depthStencilState: DepthStencilState.None, transformMatrix: cam.Transform);
#else
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform);
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, depthStencilState: DepthStencilState.None, transformMatrix: cam.Transform);
#endif
GameMain.ParticleManager.Draw(spriteBatch, true, false, Particles.ParticleBlendState.AlphaBlend);
spriteBatch.End();
//draw additive particles that are in water and behind subs
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, null, DepthStencilState.None, null, null, cam.Transform);
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, depthStencilState: DepthStencilState.None, transformMatrix: cam.Transform);
GameMain.ParticleManager.Draw(spriteBatch, true, false, Particles.ParticleBlendState.Additive);
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.None);
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, depthStencilState: DepthStencilState.None);
spriteBatch.Draw(renderTarget, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White);
spriteBatch.End();
@@ -267,8 +387,8 @@ namespace Barotrauma
GraphicsQuad.Render();
//Draw the rest of the structures, characters and front structures
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform);
Submarine.DrawBack(spriteBatch, false, e => !(e is Structure) || e.SpriteDepth < 0.9f);
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, depthStencilState: DepthStencilState.None, transformMatrix: cam.Transform);
Submarine.DrawBack(spriteBatch, false, e => e is not Structure || e.SpriteDepth < 0.9f);
DrawCharacters(deformed: false, firstPass: true);
spriteBatch.End();
@@ -276,7 +396,7 @@ namespace Barotrauma
GameMain.PerformanceCounter.AddElapsedTicks("Draw:Map:BackCharactersItems", sw.ElapsedTicks);
sw.Restart();
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform);
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, depthStencilState: DepthStencilState.None, transformMatrix: cam.Transform);
DrawCharacters(deformed: true, firstPass: true);
DrawCharacters(deformed: true, firstPass: false);
DrawCharacters(deformed: false, firstPass: false);
@@ -321,19 +441,19 @@ namespace Barotrauma
GraphicsQuad.Render();
//draw alpha blended particles that are inside a sub
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.DepthRead, null, null, cam.Transform);
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, depthStencilState: DepthStencilState.DepthRead, transformMatrix: cam.Transform);
GameMain.ParticleManager.Draw(spriteBatch, true, true, Particles.ParticleBlendState.AlphaBlend);
spriteBatch.End();
graphics.SetRenderTarget(renderTarget);
//draw alpha blended particles that are not in water
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.DepthRead, null, null, cam.Transform);
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, depthStencilState: DepthStencilState.DepthRead, transformMatrix: cam.Transform);
GameMain.ParticleManager.Draw(spriteBatch, false, null, Particles.ParticleBlendState.AlphaBlend);
spriteBatch.End();
//draw additive particles that are not in water
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, null, DepthStencilState.None, null, null, cam.Transform);
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, depthStencilState: DepthStencilState.None, transformMatrix: cam.Transform);
GameMain.ParticleManager.Draw(spriteBatch, false, null, Particles.ParticleBlendState.Additive);
spriteBatch.End();
@@ -350,20 +470,10 @@ namespace Barotrauma
GameMain.PerformanceCounter.AddElapsedTicks("Draw:Map:FrontParticles", sw.ElapsedTicks);
sw.Restart();
DamageEffect.CurrentTechnique = DamageEffect.Techniques["StencilShader"];
spriteBatch.Begin(SpriteSortMode.Immediate,
BlendState.NonPremultiplied, SamplerState.LinearWrap,
null, null,
DamageEffect,
cam.Transform);
Submarine.DrawDamageable(spriteBatch, DamageEffect, false);
spriteBatch.End();
GraphicsQuad.UseBasicEffect(renderTargetDamageable);
GraphicsQuad.Render();
sw.Stop();
GameMain.PerformanceCounter.AddElapsedTicks("Draw:Map:FrontDamageable", sw.ElapsedTicks);
sw.Restart();
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform);
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, depthStencilState: DepthStencilState.None, transformMatrix: cam.Transform);
Submarine.DrawFront(spriteBatch, false, null);
spriteBatch.End();
@@ -372,7 +482,7 @@ namespace Barotrauma
sw.Restart();
//draw additive particles that are inside a sub
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, null, DepthStencilState.Default, null, null, cam.Transform);
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, depthStencilState: DepthStencilState.Default, transformMatrix: cam.Transform);
GameMain.ParticleManager.Draw(spriteBatch, true, true, Particles.ParticleBlendState.Additive);
foreach (var discharger in Items.Components.ElectricalDischarger.List)
{
@@ -388,7 +498,7 @@ namespace Barotrauma
GraphicsQuad.Render();
}
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.LinearWrap, DepthStencilState.None, null, null, cam.Transform);
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.LinearWrap, depthStencilState: DepthStencilState.None, transformMatrix: cam.Transform);
foreach (Character c in Character.CharacterList)
{
c.DrawFront(spriteBatch, cam);
@@ -7,6 +7,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Microsoft.Xna.Framework.Input;
#if DEBUG
using System.IO;
using System.Xml;
@@ -20,23 +21,29 @@ namespace Barotrauma
{
public override Camera Cam { get; }
private readonly GUIFrame leftPanel, rightPanel, bottomPanel, topPanel;
private GUIFrame leftPanel, rightPanel, bottomPanel, topPanel;
private Point prevResolution;
private LevelGenerationParams selectedParams;
private RuinGenerationParams selectedRuinGenerationParams;
private OutpostGenerationParams selectedOutpostGenerationParams;
private LevelObjectPrefab selectedLevelObject;
private BackgroundCreaturePrefab selectedBackgroundCreature;
private readonly GUIListBox paramsList, ruinParamsList, caveParamsList, outpostParamsList, levelObjectList;
private readonly GUIListBox editorContainer;
private GUIListBox paramsList, ruinParamsList, caveParamsList, outpostParamsList, levelObjectList, backgroundCreatureList;
private GUIListBox editorContainer;
private readonly GUIButton spriteEditDoneButton;
private GUIButton spriteEditDoneButton;
private readonly GUITextBox seedBox;
private GUITextBox seedBox;
private readonly GUITickBox lightingEnabled, cursorLightEnabled, allowInvalidOutpost, mirrorLevel;
private GUITickBox lightingEnabled, cursorLightEnabled, allowInvalidOutpost, mirrorLevel;
private readonly GUIDropDown selectedSubDropDown;
private readonly GUIDropDown selectedBeaconStationDropdown;
private readonly GUIDropDown selectedWreckDropdown;
private GUIDropDown selectedSubDropDown;
private GUIDropDown selectedBeaconStationDropdown;
private GUIDropDown selectedWreckDropdown;
private GUINumberInput forceDifficultyInput;
private Sprite editingSprite;
@@ -45,15 +52,34 @@ namespace Barotrauma
private readonly Color[] tunnelDebugColors = new Color[] { Color.White, Color.Cyan, Color.LightGreen, Color.Red, Color.LightYellow, Color.LightSeaGreen };
private LevelData currentLevelData;
public LevelEditorScreen()
private void RefreshUI(bool forceCreate = false)
{
Cam = new Camera()
if (forceCreate)
{
MinZoom = 0.01f,
MaxZoom = 1.0f
};
CreateUI();
}
GUI.PreventPauseMenuToggle = false;
pointerLightSource = new LightSource(Vector2.Zero, 1000.0f, Color.White, submarine: null);
GameMain.LightManager.AddLight(pointerLightSource);
topPanel.ClearChildren();
new SerializableEntityEditor(topPanel.RectTransform, pointerLightSource.LightSourceParams, false, true);
editingSprite = null;
UpdateParamsList();
UpdateRuinParamsList();
UpdateCaveParamsList();
UpdateOutpostParamsList();
UpdateLevelObjectsList();
UpdateBackgroundCreatureList();
}
private void CreateUI()
{
Frame.ClearChildren();
leftPanel?.ClearChildren();
rightPanel?.ClearChildren();
leftPanel = new GUIFrame(new RectTransform(new Vector2(0.125f, 0.8f), Frame.RectTransform) { MinSize = new Point(150, 0) });
var paddedLeftPanel = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.95f), leftPanel.RectTransform, Anchor.CenterLeft) { RelativeOffset = new Vector2(0.02f, 0.0f) })
{
@@ -71,7 +97,9 @@ namespace Barotrauma
currentLevelData = LevelData.CreateRandom(seedBox.Text, generationParams: selectedParams);
editorContainer.ClearChildren();
SortLevelObjectsList(currentLevelData);
SortBackgroundCreaturesList(currentLevelData);
new SerializableEntityEditor(editorContainer.Content.RectTransform, selectedParams, inGame: false, showName: true, elementHeight: 20, titleFont: GUIStyle.LargeFont);
forceDifficultyInput.FloatValue = (selectedParams.MinLevelDifficulty + selectedParams.MaxLevelDifficulty) / 2f;
return true;
};
@@ -83,7 +111,30 @@ namespace Barotrauma
};
ruinParamsList.OnSelected += (GUIComponent component, object obj) =>
{
CreateOutpostGenerationParamsEditor(obj as OutpostGenerationParams);
if (selectedRuinGenerationParams == obj)
{
// need to wait a frame before deselecting or the highlight on the list item gets left on
CoroutineManager.StartCoroutine(DeselectRuinParams());
IEnumerable<CoroutineStatus> DeselectRuinParams()
{
if (Screen.Selected != this)
{
yield break;
}
yield return null;
selectedRuinGenerationParams = null;
CreateOutpostGenerationParamsEditor(null);
ruinParamsList.Deselect();
}
}
else
{
selectedRuinGenerationParams = obj as RuinGenerationParams;
CreateOutpostGenerationParamsEditor(selectedRuinGenerationParams);
}
return true;
};
@@ -108,7 +159,8 @@ namespace Barotrauma
};
outpostParamsList.OnSelected += (GUIComponent component, object obj) =>
{
CreateOutpostGenerationParamsEditor(obj as OutpostGenerationParams);
selectedOutpostGenerationParams = obj as OutpostGenerationParams;
CreateOutpostGenerationParamsEditor(selectedOutpostGenerationParams);
return true;
};
@@ -218,15 +270,31 @@ namespace Barotrauma
selectedWreckDropdown.AddItem(wreck.DisplayName, userData: wreck);
}
wreckDropDownContainer.RectTransform.MinSize = new Point(0, selectedWreckDropdown.RectTransform.MinSize.Y);
var forceDifficultyContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.02f), paddedRightPanel.RectTransform), isHorizontal: true);
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), forceDifficultyContainer.RectTransform), TextManager.Get("leveldifficulty"));
forceDifficultyInput = new GUINumberInput(new RectTransform(new Vector2(0.5f, 1.0f), forceDifficultyContainer.RectTransform), NumberType.Float)
{
MinValueFloat = 0,
MaxValueFloat = 100,
FloatValue = Level.ForcedDifficulty ?? selectedParams?.MinLevelDifficulty ?? 0f,
OnValueChanged = (numberInput) =>
{
if (Level.ForcedDifficulty == null) { return; }
Level.ForcedDifficulty = numberInput.FloatValue;
}
};
forceDifficultyContainer.RectTransform.MinSize = new Point(0, forceDifficultyInput.RectTransform.MinSize.Y);
var tickBoxContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.04f), paddedRightPanel.RectTransform), isHorizontal: true);
mirrorLevel = new GUITickBox(new RectTransform(new Vector2(0.5f, 0.02f), tickBoxContainer.RectTransform), TextManager.Get("mirrorentityx"));
mirrorLevel = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.02f), paddedRightPanel.RectTransform), TextManager.Get("mirrorentityx"));
allowInvalidOutpost = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.025f), paddedRightPanel.RectTransform),
allowInvalidOutpost = new GUITickBox(new RectTransform(new Vector2(0.5f, 0.025f), tickBoxContainer.RectTransform),
TextManager.Get("leveleditor.allowinvalidoutpost"))
{
ToolTip = TextManager.Get("leveleditor.allowinvalidoutpost.tooltip")
};
new GUIButton(new RectTransform(new Vector2(1.0f, 0.05f), paddedRightPanel.RectTransform),
TextManager.Get("leveleditor.generate"))
{
@@ -240,13 +308,15 @@ namespace Barotrauma
Submarine.MainSub = new Submarine(subInfo);
}
GameMain.LightManager.ClearLights();
currentLevelData = LevelData.CreateRandom(seedBox.Text, generationParams: selectedParams);
currentLevelData = LevelData.CreateRandom(seedBox.Text, difficulty: forceDifficultyInput.FloatValue, generationParams: selectedParams);
currentLevelData.ForceOutpostGenerationParams = outpostParamsList.SelectedData as OutpostGenerationParams;
currentLevelData.ForceBeaconStation = selectedBeaconStationDropdown.SelectedData as SubmarineInfo;
currentLevelData.ForceWreck = selectedWreckDropdown.SelectedData as SubmarineInfo;
currentLevelData.ForceRuinGenerationParams = selectedRuinGenerationParams;
currentLevelData.AllowInvalidOutpost = allowInvalidOutpost.Selected;
var dummyLocations = GameSession.CreateDummyLocations(currentLevelData);
Level.Generate(currentLevelData, mirror: mirrorLevel.Selected, startLocation: dummyLocations[0], endLocation: dummyLocations[1]);
UpdateBackgroundCreatureList();
if (Submarine.MainSub != null)
{
@@ -272,7 +342,6 @@ namespace Barotrauma
}
};
new GUIButton(new RectTransform(new Vector2(1.0f, 0.05f), paddedRightPanel.RectTransform),
TextManager.Get("leveleditor.test"))
{
@@ -300,8 +369,8 @@ namespace Barotrauma
subInfo ??= SubmarineInfo.SavedSubmarines.GetRandomUnsynced(s =>
s.IsPlayer && !s.HasTag(SubmarineTag.Shuttle) &&
!nonPlayerFiles.Any(f => f.Path == s.FilePath));
GameSession gameSession = new GameSession(subInfo, "", GameModePreset.TestMode, CampaignSettings.Empty, null);
gameSession.StartRound(Level.Loaded.LevelData);
GameSession gameSession = new GameSession(subInfo, Option.None, CampaignDataPath.Empty, GameModePreset.TestMode, CampaignSettings.Empty, null);
gameSession.StartRound(Level.Loaded.LevelData, mirrorLevel.Selected);
(gameSession.GameMode as TestGameMode).OnRoundEnd = () =>
{
GameMain.LevelEditorScreen.Select();
@@ -323,9 +392,42 @@ namespace Barotrauma
};
bottomPanel = new GUIFrame(new RectTransform(new Vector2(0.75f, 0.22f), Frame.RectTransform, Anchor.BottomLeft)
{ MaxSize = new Point(GameMain.GraphicsWidth - rightPanel.Rect.Width, 1000) }, style: "GUIFrameBottom");
{ MaxSize = new Point(GameMain.GraphicsWidth - rightPanel.Rect.Width, 1000) }, style: "GUIFrameBottom");
levelObjectList = new GUIListBox(new RectTransform(new Vector2(0.99f, 0.85f), bottomPanel.RectTransform, Anchor.Center))
var bottomPanelContents = new GUILayoutGroup(new RectTransform(new Vector2(0.98f, 0.9f), bottomPanel.RectTransform, Anchor.Center))
{
Stretch = true
};
var bottomPanelButtons = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), bottomPanelContents.RectTransform), isHorizontal: true);
new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), bottomPanelButtons.RectTransform), TextManager.Get("leveleditor.levelobjects"), style: "GUITabButton")
{
Selected = true,
OnClicked = (btn, __) =>
{
bottomPanelButtons.Children.ForEach(c => c.Selected = c == btn);
levelObjectList.Visible = true;
levelObjectList.IgnoreLayoutGroups = false;
backgroundCreatureList.Visible = false;
backgroundCreatureList.IgnoreLayoutGroups = true;
return true;
}
};
new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), bottomPanelButtons.RectTransform), TextManager.Get("leveleditor.backgroundcreatures"), style: "GUITabButton")
{
OnClicked = (btn, __) =>
{
bottomPanelButtons.Children.ForEach(c => c.Selected = c == btn);
backgroundCreatureList.Visible = true;
backgroundCreatureList.IgnoreLayoutGroups = false;
levelObjectList.Visible = false;
levelObjectList.IgnoreLayoutGroups = true;
return true;
}
};
bottomPanelButtons.RectTransform.NonScaledSize = new Point(bottomPanelButtons.Rect.Width, bottomPanelButtons.Children.First().Rect.Height);
levelObjectList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.85f), bottomPanelContents.RectTransform))
{
PlaySoundOnSelect = true,
UseGridLayout = true
@@ -337,6 +439,20 @@ namespace Barotrauma
return true;
};
backgroundCreatureList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.85f), bottomPanelContents.RectTransform))
{
PlaySoundOnSelect = true,
UseGridLayout = true,
Visible = false,
IgnoreLayoutGroups = true
};
backgroundCreatureList.OnSelected += (GUIComponent component, object obj) =>
{
selectedBackgroundCreature = obj as BackgroundCreaturePrefab;
CreateBackgroundCreatureEditor(selectedBackgroundCreature);
return true;
};
spriteEditDoneButton = new GUIButton(new RectTransform(new Point(200, 30), anchor: Anchor.BottomRight) { AbsoluteOffset = new Point(20, 20) },
TextManager.Get("leveleditor.spriteeditdone"))
{
@@ -349,6 +465,19 @@ namespace Barotrauma
topPanel = new GUIFrame(new RectTransform(new Point(400, 100), GUI.Canvas)
{ RelativeOffset = new Vector2(leftPanel.RectTransform.RelativeSize.X * 2, 0.0f) }, style: "GUIFrameTop");
prevResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
}
public LevelEditorScreen()
{
Cam = new Camera()
{
MinZoom = 0.01f,
MaxZoom = 1.0f
};
RefreshUI(forceCreate: true);
}
public void TestLevelGenerationForErrors(int amountOfLevelsToGenerate)
@@ -394,28 +523,13 @@ namespace Barotrauma
yield return CoroutineStatus.Running;
}
}
}
public override void Select()
{
base.Select();
GUI.PreventPauseMenuToggle = false;
pointerLightSource = new LightSource(Vector2.Zero, 1000.0f, Color.White, submarine: null);
GameMain.LightManager.AddLight(pointerLightSource);
topPanel.ClearChildren();
new SerializableEntityEditor(topPanel.RectTransform, pointerLightSource.LightSourceParams, false, true);
editingSprite = null;
UpdateParamsList();
UpdateRuinParamsList();
UpdateCaveParamsList();
UpdateOutpostParamsList();
UpdateLevelObjectsList();
RefreshUI(forceCreate: false);
}
protected override void DeselectEditorSpecific()
@@ -464,7 +578,7 @@ namespace Barotrauma
foreach (RuinGenerationParams genParams in RuinGenerationParams.RuinParams.OrderBy(p => p.Identifier))
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), ruinParamsList.Content.RectTransform) { MinSize = new Point(0, 20) },
genParams.Name)
genParams.Identifier.Value)
{
Padding = Vector4.Zero,
UserData = genParams
@@ -502,9 +616,13 @@ namespace Barotrauma
new Vector2(relWidth, relWidth * ((float)levelObjectList.Content.Rect.Width / levelObjectList.Content.Rect.Height)),
levelObjectList.Content.RectTransform) { MinSize = new Point(0, 60) }, style: "ListBoxElementSquare")
{
UserData = levelObjPrefab
UserData = levelObjPrefab,
ToolTip = levelObjPrefab.Name
};
var paddedFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), frame.RectTransform, Anchor.Center), style: null)
{
CanBeFocused = false
};
var paddedFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), frame.RectTransform, Anchor.Center), style: null);
GUITextBlock textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform, Anchor.BottomCenter),
text: ToolBox.LimitString(levelObjPrefab.Name, GUIStyle.SmallFont, paddedFrame.Rect.Width), textAlignment: Alignment.Center, font: GUIStyle.SmallFont)
@@ -523,6 +641,44 @@ namespace Barotrauma
}
}
private void UpdateBackgroundCreatureList()
{
editorContainer.ClearChildren();
backgroundCreatureList.Content.ClearChildren();
int objectsPerRow = (int)Math.Ceiling(backgroundCreatureList.Content.Rect.Width / Math.Max(100 * GUI.Scale, 100));
float relWidth = 1.0f / objectsPerRow;
foreach (BackgroundCreaturePrefab backgroundCreaturePrefab in BackgroundCreaturePrefab.Prefabs)
{
var frame = new GUIFrame(new RectTransform(
new Vector2(relWidth, relWidth * ((float)backgroundCreatureList.Content.Rect.Width / backgroundCreatureList.Content.Rect.Height)),
backgroundCreatureList.Content.RectTransform)
{ MinSize = new Point(0, 60) }, style: "ListBoxElementSquare")
{
UserData = backgroundCreaturePrefab
};
var paddedFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), frame.RectTransform, Anchor.Center), style: null);
GUITextBlock textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform, Anchor.BottomCenter),
text: ToolBox.LimitString(backgroundCreaturePrefab.Name, GUIStyle.SmallFont, paddedFrame.Rect.Width), textAlignment: Alignment.Center, font: GUIStyle.SmallFont)
{
CanBeFocused = false,
ToolTip = backgroundCreaturePrefab.Name
};
Sprite sprite = backgroundCreaturePrefab.Sprite ?? backgroundCreaturePrefab.DeformableSprite?.Sprite;
new GUIImage(new RectTransform(new Point(paddedFrame.Rect.Height, paddedFrame.Rect.Height - textBlock.Rect.Height),
paddedFrame.RectTransform, Anchor.TopCenter), sprite, scaleToFit: true)
{
LoadAsynchronously = true,
CanBeFocused = false
};
}
SortBackgroundCreaturesList(currentLevelData);
}
private void CreateCaveParamsEditor(CaveGenerationParams caveGenerationParams)
{
editorContainer.ClearChildren();
@@ -556,6 +712,7 @@ namespace Barotrauma
private void CreateOutpostGenerationParamsEditor(OutpostGenerationParams outpostGenerationParams)
{
editorContainer.ClearChildren();
if (outpostGenerationParams == null) { return; }
var outpostParamsEditor = new SerializableEntityEditor(editorContainer.Content.RectTransform, outpostGenerationParams, false, true, elementHeight: 20);
// location type -------------------------
@@ -584,7 +741,7 @@ namespace Barotrauma
locationTypeDropDown.SelectItem("any");
}
locationTypeDropDown.OnSelected += (_, __) =>
locationTypeDropDown.AfterSelected += (_, __) =>
{
outpostGenerationParams.SetAllowedLocationTypes(locationTypeDropDown.SelectedDataMultiple.Cast<Identifier>());
locationTypeDropDown.Text = ToolBox.LimitString(locationTypeDropDown.Text, locationTypeDropDown.Font, locationTypeDropDown.Rect.Width);
@@ -596,29 +753,29 @@ namespace Barotrauma
// module count -------------------------
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);
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);
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)
var editor = new SerializableEntityEditor(editorContainer.Content.RectTransform, moduleCount, inGame: false, showName: true, elementHeight: 20, titleFont: GUIStyle.Font);
foreach (var componentList in editor.Fields.Values)
{
MinValueInt = 0,
MaxValueInt = 100,
IntValue = moduleCount.Count,
OnValueChanged = (numInput) =>
foreach (var component in componentList)
{
outpostGenerationParams.SetModuleCount(moduleCount.Identifier, numInput.IntValue);
if (numInput.IntValue == 0)
if (component is GUINumberInput numberInput)
{
outpostParamsList.Select(outpostParamsList.SelectedData);
numberInput.OnValueChanged += (numInput) =>
{
if (moduleCount.Count == 0)
{
//refresh to remove this module count from the editor
outpostParamsList.Select(outpostParamsList.SelectedData);
}
};
}
}
};
moduleCountGroup.RectTransform.MinSize = new Point(moduleCountGroup.Rect.Width, moduleCountGroup.RectTransform.Children.Max(c => c.MinSize.Y));
outpostParamsEditor.AddCustomContent(moduleCountGroup, 100);
}
editor.RectTransform.MaxSize = new Point(int.MaxValue, editor.Rect.Height);
outpostParamsEditor.AddCustomContent(editor, 100);
editor.Recalculate();
}
// add module count -------------------------
@@ -648,7 +805,7 @@ namespace Barotrauma
};
addModuleCountGroup.RectTransform.MinSize = new Point(addModuleCountGroup.Rect.Width, addModuleCountGroup.RectTransform.Children.Max(c => c.MinSize.Y));
outpostParamsEditor.AddCustomContent(addModuleCountGroup, 100);
outpostParamsEditor.Recalculate();
}
private void CreateLevelObjectEditor(LevelObjectPrefab levelObjectPrefab)
@@ -736,7 +893,7 @@ namespace Barotrauma
dropdown.AddItem(objPrefab.Name, objPrefab);
if (childObj.AllowedNames.Contains(objPrefab.Name)) { dropdown.SelectItem(objPrefab); }
}
dropdown.OnSelected = (selected, obj) =>
dropdown.AfterSelected = (selected, obj) =>
{
childObj.AllowedNames = dropdown.SelectedDataMultiple.Select(d => ((LevelObjectPrefab)d).Name).ToList();
return true;
@@ -817,9 +974,22 @@ namespace Barotrauma
{
var levelObj = levelObjFrame.UserData as LevelObjectPrefab;
float commonness = levelObj.GetCommonness(levelData);
levelObjFrame.Color = commonness > 0.0f ? GUIStyle.Green * 0.4f : Color.Transparent;
levelObjFrame.SelectedColor = commonness > 0.0f ? GUIStyle.Green * 0.6f : Color.White * 0.5f;
levelObjFrame.HoverColor = commonness > 0.0f ? GUIStyle.Green * 0.7f : Color.White * 0.6f;
Color color = GUIStyle.Green;
if (commonness > 0.0f && levelData?.GenerationParams != null)
{
if (levelObj.MinSurfaceWidth > levelData.GenerationParams.CellSubdivisionLength &&
levelObj.SpawnPos.HasFlag(LevelObjectPrefab.SpawnPosType.Wall))
{
color = Color.Orange;
levelObjFrame.ToolTip = $"Potential issue: the level walls in \"{levelData.GenerationParams.Identifier}\" are set to be subdivided every {levelData.GenerationParams.CellSubdivisionLength} pixels, but the level object requires wall segments of at least {levelObj.MinSurfaceWidth} px. The object may be rarer than intended (or fail to spawn at all) in the level.";
}
}
levelObjFrame.Color = commonness > 0.0f ? color * 0.4f : Color.Transparent;
levelObjFrame.SelectedColor = commonness > 0.0f ? color * 0.6f : Color.White * 0.5f;
levelObjFrame.HoverColor = commonness > 0.0f ? color * 0.7f : Color.White * 0.6f;
levelObjFrame.GetAnyChild<GUIImage>().Color = commonness > 0.0f ? Color.White : Color.DarkGray;
if (commonness <= 0.0f)
@@ -837,6 +1007,99 @@ namespace Barotrauma
});
}
private void SortBackgroundCreaturesList(LevelData levelData)
{
if (levelData == null) { return; }
//fade out levelobjects that don't spawn in this type of level
foreach (GUIComponent child in backgroundCreatureList.Content.Children)
{
if (child.UserData is not BackgroundCreaturePrefab creature) { continue; }
SetElementColorBasedOnCommonness(child, creature.GetCommonness(levelData));
}
//sort the levelobjects according to commonness in this level
backgroundCreatureList.Content.RectTransform.SortChildren((c1, c2) =>
{
var creature1 = c1.GUIComponent.UserData as BackgroundCreaturePrefab;
var creature2 = c2.GUIComponent.UserData as BackgroundCreaturePrefab;
return Math.Sign(creature2.GetCommonness(levelData) - creature1.GetCommonness(levelData));
});
}
private static void SetElementColorBasedOnCommonness(GUIComponent element, float commonness)
{
element.Color = commonness > 0.0f ? GUIStyle.Green * 0.4f : Color.Transparent;
element.SelectedColor = commonness > 0.0f ? GUIStyle.Green * 0.6f : Color.White * 0.5f;
element.HoverColor = commonness > 0.0f ? GUIStyle.Green * 0.7f : Color.White * 0.6f;
element.GetAnyChild<GUIImage>().Color = commonness > 0.0f ? Color.White : Color.DarkGray;
if (commonness <= 0.0f)
{
element.GetAnyChild<GUITextBlock>().TextColor = Color.DarkGray;
}
}
private void CreateBackgroundCreatureEditor(BackgroundCreaturePrefab backgroundCreaturePrefab)
{
editorContainer.ClearChildren();
var editor = new SerializableEntityEditor(editorContainer.Content.RectTransform, backgroundCreaturePrefab, false, true, elementHeight: 20, titleFont: GUIStyle.LargeFont);
if (selectedParams != null)
{
List<Identifier> availableIdentifiers = new List<Identifier>() { selectedParams.Identifier };
foreach (Identifier paramsId in availableIdentifiers)
{
var commonnessContainer = new GUILayoutGroup(new RectTransform(new Point(editor.Rect.Width, 70)) { IsFixedSize = true },
isHorizontal: false, childAnchor: Anchor.TopCenter)
{
AbsoluteSpacing = 5,
Stretch = true
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.4f), commonnessContainer.RectTransform),
TextManager.GetWithVariable("leveleditor.levelobjcommonness", "[leveltype]", paramsId.Value), textAlignment: Alignment.Center);
new GUINumberInput(new RectTransform(new Vector2(0.5f, 0.4f), commonnessContainer.RectTransform), NumberType.Float)
{
MinValueFloat = 0,
MaxValueFloat = 100,
FloatValue = backgroundCreaturePrefab.GetCommonness(currentLevelData),
OnValueChanged = (numberInput) =>
{
backgroundCreaturePrefab.OverrideCommonness[paramsId] = numberInput.FloatValue;
}
};
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.2f), commonnessContainer.RectTransform), style: null);
editor.AddCustomContent(commonnessContainer, 1);
}
}
Sprite sprite = backgroundCreaturePrefab.Sprite ?? backgroundCreaturePrefab.DeformableSprite?.Sprite;
if (sprite != null)
{
editor.AddCustomContent(new GUIButton(new RectTransform(new Point(editor.Rect.Width / 2, (int)(25 * GUI.Scale))) { IsFixedSize = true },
TextManager.Get("leveleditor.editsprite"))
{
OnClicked = (btn, userdata) =>
{
editingSprite = sprite;
GameMain.SpriteEditorScreen.SelectSprite(editingSprite);
return true;
}
}, 1);
}
if (backgroundCreaturePrefab.DeformableSprite != null)
{
var deformEditor = backgroundCreaturePrefab.DeformableSprite.CreateEditor(editor, backgroundCreaturePrefab.SpriteDeformations, backgroundCreaturePrefab.Name);
deformEditor.GetChild<GUIDropDown>().OnSelected += (selected, userdata) =>
{
CreateBackgroundCreatureEditor(backgroundCreaturePrefab);
return true;
};
editor.AddCustomContent(deformEditor, editor.ContentCount);
}
}
public override void AddToGUIUpdateList()
{
base.AddToGUIUpdateList();
@@ -965,11 +1228,17 @@ namespace Barotrauma
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
if (Level.Loaded != null)
{
float crushDepthScreen = Cam.WorldToScreen(new Vector2(0.0f, -Level.Loaded.CrushDepth)).Y;
if (crushDepthScreen > 0.0f && crushDepthScreen < GameMain.GraphicsHeight)
float hullUpgradePrcIncrease = UpgradePrefab.CrushDepthUpgradePrc / 100f;
for (int upgradeLevel = 0; upgradeLevel <= UpgradePrefab.IncreaseWallHealthMaxLevel; upgradeLevel++)
{
GUI.DrawLine(spriteBatch, new Vector2(0, crushDepthScreen), new Vector2(GameMain.GraphicsWidth, crushDepthScreen), GUIStyle.Red * 0.25f, width: 5);
GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2, crushDepthScreen), "Crush depth", GUIStyle.Red, backgroundColor: Color.Black);
float upgradeLevelCrushDepth = Level.DefaultRealWorldCrushDepth + (Level.DefaultRealWorldCrushDepth * upgradeLevel * hullUpgradePrcIncrease);
float subCrushDepth = (upgradeLevelCrushDepth / Physics.DisplayToRealWorldRatio) - Level.Loaded.LevelData.InitialDepth;
string labelText = $"Crush depth (upgrade level {upgradeLevel})";
if (upgradeLevel == 0)
{
labelText = $"Crush depth (no upgrade)";
}
DrawCrushDepth(subCrushDepth, labelText, Color.Red);
}
float abyssStartScreen = Cam.WorldToScreen(new Vector2(0.0f, Level.Loaded.AbyssArea.Bottom)).Y;
@@ -987,11 +1256,25 @@ namespace Barotrauma
}
GUI.Draw(Cam, spriteBatch);
spriteBatch.End();
void DrawCrushDepth(float crushDepth, string labelText, Color color)
{
float crushDepthScreen = Cam.WorldToScreen(new Vector2(0.0f, -crushDepth)).Y;
if (crushDepthScreen > 0.0f && crushDepthScreen < GameMain.GraphicsHeight)
{
GUI.DrawLine(spriteBatch, new Vector2(0, crushDepthScreen), new Vector2(GameMain.GraphicsWidth, crushDepthScreen), color * 0.25f, width: 5);
GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2, crushDepthScreen), labelText, GUIStyle.Red, backgroundColor: Color.Black);
}
}
}
public override void Update(double deltaTime)
{
if (GameMain.GraphicsWidth != prevResolution.X || GameMain.GraphicsHeight != prevResolution.Y)
{
RefreshUI(forceCreate: true);
}
if (lightingEnabled.Selected)
{
foreach (Item item in Item.ItemList)
@@ -1016,6 +1299,19 @@ namespace Barotrauma
{
GameMain.SpriteEditorScreen.Update(deltaTime);
}
// in case forced difficulty was changed by console command or such
if (Level.ForcedDifficulty != null && MathHelper.Distance((float)Level.ForcedDifficulty, forceDifficultyInput.FloatValue) > 0.001f)
{
forceDifficultyInput.FloatValue = (float)Level.ForcedDifficulty;
}
#if DEBUG
if (PlayerInput.KeyDown(Keys.LeftControl) && PlayerInput.KeyHit(Keys.R))
{
RefreshUI(forceCreate: true);
}
#endif
}
private void SerializeAll()
@@ -512,6 +512,22 @@ namespace Barotrauma
return true;
}
};
new GUIButton(new RectTransform(new Point(300, 30), Frame.RectTransform, Anchor.TopRight) { AbsoluteOffset = new Point(40, 230) },
"Local MP Quickstart", style: "GUIButtonLarge", color: GUIStyle.Red)
{
IgnoreLayoutGroups = true,
UserData = Tab.Empty,
ToolTip = "Starts a server and another client and connects both to localhost, using names 'client1' and 'client2'.",
OnClicked = (tb, userdata) =>
{
SelectTab(tb, userdata);
DebugConsole.StartLocalMPSession(numClients: 2);
return true;
}
};
#endif
var minButtonSize = new Point(120, 20);
var maxButtonSize = new Point(480, 80);
@@ -906,6 +922,7 @@ namespace Barotrauma
}
var gamesession = new GameSession(
selectedSub,
Option.None,
GameModePreset.DevSandbox,
missionPrefabs: null);
//(gamesession.GameMode as SinglePlayerCampaign).GenerateMap(ToolBox.RandomSeed(8));
@@ -928,6 +945,7 @@ namespace Barotrauma
DebugConsole.ThrowError("Failed to find the job \"" + job + "\"!");
}
gamesession.CrewManager.AddCharacterInfo(characterInfo);
characterInfo.SetNameBasedOnJob();
}
gamesession.CrewManager.InitSinglePlayerRound();
}
@@ -1251,12 +1269,12 @@ namespace Barotrauma
if (!Directory.Exists(SaveUtil.TempPath))
{
Directory.CreateDirectory(SaveUtil.TempPath);
Directory.CreateDirectory(SaveUtil.TempPath, catchUnauthorizedAccessExceptions: true);
}
try
{
File.Copy(selectedSub.FilePath, Path.Combine(SaveUtil.TempPath, selectedSub.Name + ".sub"), true);
File.Copy(selectedSub.FilePath, Path.Combine(SaveUtil.TempPath, selectedSub.Name + ".sub"), overwrite: true, catchUnauthorizedAccessExceptions: false);
}
catch (System.IO.IOException e)
{
@@ -1270,7 +1288,7 @@ namespace Barotrauma
selectedSub = new SubmarineInfo(Path.Combine(SaveUtil.TempPath, selectedSub.Name + ".sub"));
GameMain.GameSession = new GameSession(selectedSub, savePath, GameModePreset.SinglePlayerCampaign, settings, mapSeed);
GameMain.GameSession = new GameSession(selectedSub, Option.None, CampaignDataPath.CreateRegular(savePath), GameModePreset.SinglePlayerCampaign, settings, mapSeed);
GameMain.GameSession.CrewManager.ClearCharacterInfos();
foreach (var characterInfo in campaignSetupUI.CharacterMenus.Select(m => m.CharacterInfo))
{
@@ -1279,22 +1297,25 @@ namespace Barotrauma
((SinglePlayerCampaign)GameMain.GameSession.GameMode).LoadNewLevel();
}
private void LoadGame(string saveFile)
private void LoadGame(string path, Option<uint> backupIndex)
{
if (string.IsNullOrWhiteSpace(saveFile)) return;
if (string.IsNullOrWhiteSpace(path)) return;
try
{
SaveUtil.LoadGame(saveFile);
CampaignDataPath dataPath =
backupIndex.TryUnwrap(out uint index)
? new CampaignDataPath(loadPath: SaveUtil.GetBackupPath(path, index), path)
: CampaignDataPath.CreateRegular(path);
SaveUtil.LoadGame(dataPath);
}
catch (Exception e)
{
DebugConsole.ThrowError("Loading save \"" + saveFile + "\" failed", e);
DebugConsole.ThrowError("Loading save \"" + path + "\" failed", e);
GameMain.GameSession = null;
return;
}
//TODO
//GameMain.LobbyScreen.Select();
}
#region UI Methods
@@ -1327,7 +1348,7 @@ namespace Barotrauma
var serverSettings = XMLExtensions.TryLoadXml(ServerSettings.SettingsFile, out _)?.Root ?? new XElement("serversettings");
var name = serverSettings.GetAttributeString("name", "");
var name = serverSettings.GetAttributeString(nameof(ServerSettings.ServerName), "");
var password = serverSettings.GetAttributeString("password", "");
var isPublic = serverSettings.GetAttributeBool("IsPublic", true);
var banAfterWrongPassword = serverSettings.GetAttributeBool("banafterwrongpassword", false);
File diff suppressed because it is too large Load Diff
@@ -465,33 +465,38 @@ namespace Barotrauma
bool noneSelected = langTickboxes.All(tb => !tb.Selected);
bool allSelected = langTickboxes.All(tb => tb.Selected);
if (allSelected != allTickbox.Selected)
{
allTickbox.Selected = allSelected;
}
if (allSelected)
{
languageDropdown.Text = TextManager.Get(allLanguagesKey);
}
else if (noneSelected)
{
languageDropdown.Text = TextManager.Get("None");
}
var languages = languageDropdown.SelectedDataMultiple.OfType<LanguageIdentifier>();
ServerListFilters.Instance.SetAttribute(languageKey, string.Join(", ", languages));
GameSettings.SaveCurrentConfig();
return true;
}
finally
{
inSelectedCall = false;
FilterServers();
}
};
languageDropdown.AfterSelected = (_, userData) =>
{
bool noneSelected = langTickboxes.All(tb => !tb.Selected);
bool allSelected = langTickboxes.All(tb => tb.Selected);
if (allSelected)
{
languageDropdown.Text = TextManager.Get(allLanguagesKey);
}
else if (noneSelected)
{
languageDropdown.Text = TextManager.Get("None");
}
var languages = languageDropdown.SelectedDataMultiple.OfType<LanguageIdentifier>();
ServerListFilters.Instance.SetAttribute(languageKey, string.Join(", ", languages));
GameSettings.SaveCurrentConfig();
FilterServers();
return true;
};
}
// Filter Tags
@@ -998,6 +1003,8 @@ namespace Barotrauma
private bool ShouldShowServer(ServerInfo serverInfo)
{
if (serverInfo == null) { return false; }
#if !DEBUG
//never show newer versions
//(ignore revision number, it doesn't affect compatibility)
@@ -503,8 +503,18 @@ namespace Barotrauma
{
if (PlayerInput.ScrollWheelSpeed != 0)
{
zoom = MathHelper.Clamp(zoom + PlayerInput.ScrollWheelSpeed * (float)deltaTime * 0.05f * zoom, MinZoom, MaxZoom);
float newZoom = MathHelper.Clamp(zoom + PlayerInput.ScrollWheelSpeed * (float)deltaTime * 0.05f * zoom, MinZoom, MaxZoom);
float zoomDeltaPrc = ((newZoom - zoom) / zoom);
zoom = newZoom;
zoomBar.BarScroll = GetBarScrollValue();
// modify view area offset as well when zooming, to zoom into mouse cursor position
Point mouseToViewAreaScreenCenterDelta = (GetViewArea.Center - viewAreaOffset) - PlayerInput.MousePosition.ToPoint();
Vector2 mouseDelta = mouseToViewAreaScreenCenterDelta.ToVector2();
Vector2 newViewAreaOffset = viewAreaOffset.ToVector2();
newViewAreaOffset += (mouseDelta + newViewAreaOffset) * zoomDeltaPrc;
viewAreaOffset = newViewAreaOffset.ToPoint();
}
widgets.Values.ForEach(w => w.Update((float)deltaTime));
if (PlayerInput.MidButtonHeld())
@@ -516,6 +526,36 @@ namespace Barotrauma
}
if (GUI.KeyboardDispatcher.Subscriber == null)
{
if (PlayerInput.KeyDown(Keys.LeftControl) && PlayerInput.KeyHit(Keys.C))
{
string text = "";
if (selectedSprites.Count == 1)
{
var selectedSprite = selectedSprites.First();
if (selectedSprite.SourceElement != null)
{
string sourceRectText = $"sourcerect=\"{XMLExtensions.RectToString(selectedSprite.SourceRect)}\"";
text += $"{sourceRectText}";
}
}
else
{
foreach (var selectedSprite in selectedSprites)
{
if (selectedSprite.SourceElement == null) { continue; }
XElement xElement = new XElement(selectedSprite.SourceElement.Element);
xElement.SetAttributeValue("sourcerect", XMLExtensions.RectToString(selectedSprite.SourceRect));
xElement.SetAttributeValue("origin", XMLExtensions.Vector2ToString(selectedSprite.RelativeOrigin));
text += $"{xElement}";
if (selectedSprites.Last() != selectedSprite)
{
text += Environment.NewLine;
}
}
}
Clipboard.SetText(text);
}
if (PlayerInput.KeyHit(Keys.Left))
{
Nudge(Keys.Left);
@@ -568,6 +608,28 @@ namespace Barotrauma
{
holdTimer = 0;
}
float moveSpeed = 600f * zoom;
float moveSpeedDeltaTime = (float)(moveSpeed * deltaTime);
Vector2 viewOffsetMove = Vector2.Zero;
if (PlayerInput.KeyDown(Keys.W))
{
viewOffsetMove.Y += moveSpeedDeltaTime;
}
if (PlayerInput.KeyDown(Keys.S))
{
viewOffsetMove.Y -= moveSpeedDeltaTime;
}
if (PlayerInput.KeyDown(Keys.A))
{
viewOffsetMove.X += moveSpeedDeltaTime;
}
if (PlayerInput.KeyDown(Keys.D))
{
viewOffsetMove.X -= moveSpeedDeltaTime;
}
viewAreaOffset += viewOffsetMove.ToPoint();
}
}
@@ -963,8 +1025,14 @@ namespace Barotrauma
//foreach (Sprite sprite in loadedSprites.OrderBy(s => GetSpriteName(s)))
foreach (Sprite sprite in loadedSprites.OrderBy(s => s.SourceElement.GetAttributeContentPath("texture")?.Value ?? string.Empty))
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), spriteList.Content.RectTransform) { MinSize = new Point(0, 20) },
GetSpriteName(sprite) + " (" + sprite.SourceRect.X + ", " + sprite.SourceRect.Y + ", " + sprite.SourceRect.Width + ", " + sprite.SourceRect.Height + ")")
string elementLocalName = sprite.SourceElement.Element.Name.LocalName;
string text = $"{GetSpriteName(sprite)} ({sprite.SourceRect.X}, {sprite.SourceRect.Y}, {sprite.SourceRect.Width}, {sprite.SourceRect.Height}) [{elementLocalName}]";
if (string.Equals(elementLocalName, "sprite", StringComparison.InvariantCultureIgnoreCase))
{
text = $"{GetSpriteName(sprite)} ({sprite.SourceRect.X}, {sprite.SourceRect.Y}, {sprite.SourceRect.Width}, {sprite.SourceRect.Height})";
}
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), spriteList.Content.RectTransform) { MinSize = new Point(0, 20) }, text: text)
{
UserData = sprite
};
@@ -1005,11 +1073,11 @@ namespace Barotrauma
string name = sprite.Name;
if (string.IsNullOrWhiteSpace(name))
{
name = sourceElement.Parent.GetAttributeString("identifier", string.Empty);
name = sourceElement.Parent?.GetAttributeString("identifier", string.Empty);
}
if (string.IsNullOrEmpty(name))
{
name = sourceElement.Parent.GetAttributeString("name", string.Empty);
name = sourceElement.Parent?.GetAttributeString("name", string.Empty);
}
return string.IsNullOrEmpty(name) ? Path.GetFileNameWithoutExtension(sprite.FilePath.Value) : name;
}
@@ -11,6 +11,7 @@ using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Xml.Linq;
using Barotrauma.Sounds;
namespace Barotrauma
{
@@ -20,7 +21,7 @@ namespace Barotrauma
public const int MaxWalls = 500;
public const int MaxItems = 5000;
public const int MaxLights = 600;
public const int MaxShadowCastingLights = 60;
public const int MaxShadowCastingLights = 100;
private static Submarine MainSub
{
@@ -46,6 +47,7 @@ namespace Barotrauma
NoBallastTag,
NonLinkedGaps,
NoHiddenContainers,
InsufficientFreeConnectionsWarning,
StructureCount,
WallCount,
ItemCount,
@@ -54,7 +56,8 @@ namespace Barotrauma
WaterInHulls,
LowOxygenOutputWarning,
TooLargeForEndGame,
NotEnoughContainers
NotEnoughContainers,
NoSuitableBrainRooms
}
public static Vector2 MouseDragStart = Vector2.Zero;
@@ -1071,9 +1074,16 @@ namespace Barotrauma
backedUpSubInfo = new SubmarineInfo(MainSub);
GameSession gameSession = new GameSession(backedUpSubInfo, Option.None, CampaignDataPath.Empty, GameModePreset.TestMode, CampaignSettings.Empty, null);
// if testing an outpost module, we will generate an entire outpost in place of the main submarine down the line
if (backedUpSubInfo.OutpostModuleInfo != null)
{
gameSession.ForceOutpostModule = new SubmarineInfo(MainSub);
}
GameMain.GameScreen.Select();
GameSession gameSession = new GameSession(backedUpSubInfo, "", GameModePreset.TestMode, CampaignSettings.Empty, null);
gameSession.StartRound(null, false);
foreach ((string layerName, LayerData layerData) in Layers)
@@ -1299,7 +1309,7 @@ namespace Barotrauma
}
GUITextBlock textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform, Anchor.BottomCenter),
text: name, textAlignment: Alignment.Center, font: GUIStyle.SmallFont)
text: RichString.Rich(name), textAlignment: Alignment.Center, font: GUIStyle.SmallFont)
{
CanBeFocused = false
};
@@ -1311,7 +1321,7 @@ namespace Barotrauma
textBlock.Text = frame.ToolTip = ep.Identifier.Value;
textBlock.TextColor = GUIStyle.Red;
}
textBlock.Text = ToolBox.LimitString(textBlock.Text, textBlock.Font, textBlock.Rect.Width);
textBlock.Text = ToolBox.LimitString(textBlock.Text.SanitizedString, textBlock.Font, textBlock.Rect.Width);
if (ep.Category == MapEntityCategory.ItemAssembly
&& ep.ContentPackage?.Files.Length == 1
@@ -1382,10 +1392,12 @@ namespace Barotrauma
GUI.PreventPauseMenuToggle = false;
if (!Directory.Exists(autoSavePath))
{
System.IO.DirectoryInfo e = Directory.CreateDirectory(autoSavePath);
e.Attributes = System.IO.FileAttributes.Directory | System.IO.FileAttributes.Hidden;
if (!e.Exists)
if (Directory.CreateDirectory(autoSavePath, catchUnauthorizedAccessExceptions: true) is { Exists: true } directoryInfo)
{
directoryInfo.Attributes = System.IO.FileAttributes.Directory | System.IO.FileAttributes.Hidden;
}
else
{
DebugConsole.ThrowError("Failed to create auto save directory!");
}
}
@@ -1395,7 +1407,7 @@ namespace Barotrauma
try
{
AutoSaveInfo = new XDocument(new XElement("AutoSaves"));
IO.SafeXML.SaveSafe(AutoSaveInfo, autoSaveInfoPath);
AutoSaveInfo.SaveSafe(autoSaveInfoPath, throwExceptions: true);
}
catch (Exception e)
{
@@ -1456,8 +1468,8 @@ namespace Barotrauma
MainSub.UpdateTransform(interpolate: false);
cam.Position = MainSub.Position + MainSub.HiddenSubPosition;
GameMain.SoundManager.SetCategoryGainMultiplier("default", 0.0f);
GameMain.SoundManager.SetCategoryGainMultiplier("waterambience", 0.0f);
GameMain.SoundManager.SetCategoryGainMultiplier(SoundManager.SoundCategoryDefault, 0.0f);
GameMain.SoundManager.SetCategoryGainMultiplier(SoundManager.SoundCategoryWaterAmbience, 0.0f);
string downloadFolder = Path.GetFullPath(SaveUtil.SubmarineDownloadFolder);
linkedSubBox.ClearChildren();
@@ -1617,8 +1629,8 @@ namespace Barotrauma
SetMode(Mode.Default);
SoundPlayer.OverrideMusicType = Identifier.Empty;
GameMain.SoundManager.SetCategoryGainMultiplier("default", GameSettings.CurrentConfig.Audio.SoundVolume);
GameMain.SoundManager.SetCategoryGainMultiplier("waterambience", GameSettings.CurrentConfig.Audio.SoundVolume);
GameMain.SoundManager.SetCategoryGainMultiplier(SoundManager.SoundCategoryDefault, GameSettings.CurrentConfig.Audio.SoundVolume);
GameMain.SoundManager.SetCategoryGainMultiplier(SoundManager.SoundCategoryWaterAmbience, GameSettings.CurrentConfig.Audio.SoundVolume);
if (CoroutineManager.IsCoroutineRunning("SubEditorAutoSave"))
{
@@ -1788,6 +1800,21 @@ namespace Barotrauma
nameBox.Flash();
return false;
}
// when creating a new local package, prevent name clashes with existing packages
if (packageToSaveTo == null)
{
var subFiles = ContentPackageManager.EnabledPackages.All
.SelectMany(p => p.GetFiles<SubmarineFile>());
var nameConflictFile = subFiles.FirstOrDefault(file => Path.GetFileNameWithoutExtension(file.Path.Value).Equals(nameBox.Text, StringComparison.InvariantCultureIgnoreCase));
if (nameConflictFile != null)
{
new GUIMessageBox(TextManager.Get("error"), TextManager.GetWithVariable("subeditor.duplicatefilenameerror", "[packagename]", nameConflictFile.ContentPackage.Name));
return false;
}
}
if (MainSub.Info.Type != SubmarineType.Player)
{
@@ -2060,7 +2087,7 @@ namespace Barotrauma
saveFrame = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), style: "GUIBackgroundBlocker");
var innerFrame = new GUIFrame(new RectTransform(new Vector2(0.55f, 0.65f), saveFrame.RectTransform, Anchor.Center) { MinSize = new Point(750, 500) });
var innerFrame = new GUIFrame(new RectTransform(new Vector2(0.6f, 0.7f), saveFrame.RectTransform, Anchor.Center) { MinSize = new Point(750, 500) });
var paddedSaveFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), innerFrame.RectTransform, Anchor.Center)) { Stretch = true, RelativeSpacing = 0.02f };
var columnArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.9f), paddedSaveFrame.RectTransform), isHorizontal: true) { RelativeSpacing = 0.02f, Stretch = true };
@@ -2162,17 +2189,15 @@ namespace Barotrauma
layerVisibilityDropDown.SelectItem(layerName);
}
}
layerVisibilityDropDown.OnSelected += (button, obj) =>
layerVisibilityDropDown.AfterSelected += (button, _) =>
{
string layerName = (string)obj;
bool isVisible = layerVisibilityDropDown.SelectedDataMultiple.Contains(obj);
if (isVisible)
MainSub.Info.LayersHiddenByDefault.Clear();
foreach (var layer in Layers)
{
MainSub.Info.LayersHiddenByDefault.Remove(layerName.ToIdentifier());
}
else
{
MainSub.Info.LayersHiddenByDefault.Add(layerName.ToIdentifier());
if (!layerVisibilityDropDown.SelectedDataMultiple.Contains(layer.Key))
{
MainSub.Info.LayersHiddenByDefault.Add(layer.Key.ToIdentifier());
}
}
UpdateLayerPanel();
layerVisibilityDropDown.Text = ToolBox.LimitString(layerVisibilityDropDown.Text.Value, layerVisibilityDropDown.Font, layerVisibilityDropDown.Rect.Width);
@@ -2183,9 +2208,9 @@ namespace Barotrauma
//---------------------------------------
var subTypeDependentSettingFrame = new GUIFrame(new RectTransform((1.0f, 0.5f), leftColumn.RectTransform), style: "InnerFrame");
var subTypeDependentSettingFrame = new GUIFrame(new RectTransform((1.0f, 0.6f), leftColumn.RectTransform), style: "InnerFrame");
var outpostSettingsContainer = new GUILayoutGroup(new RectTransform(Vector2.One, subTypeDependentSettingFrame.RectTransform))
var outpostModuleSettingsContainer = new GUILayoutGroup(new RectTransform(Vector2.One, subTypeDependentSettingFrame.RectTransform))
{
CanBeFocused = true,
Visible = false,
@@ -2194,7 +2219,7 @@ namespace Barotrauma
// module flags ---------------------
var outpostModuleGroup = new GUILayoutGroup(new RectTransform(new Vector2(.975f, 0.1f), outpostSettingsContainer.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
var outpostModuleGroup = new GUILayoutGroup(new RectTransform(new Vector2(.975f, 0.1f), outpostModuleSettingsContainer.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), outpostModuleGroup.RectTransform), TextManager.Get("outpostmoduletype"), textAlignment: Alignment.CenterLeft);
HashSet<Identifier> availableFlags = new HashSet<Identifier>();
@@ -2209,7 +2234,13 @@ namespace Barotrauma
availableFlags.Add(flag);
}
}
if (MainSub?.Info?.OutpostModuleInfo is { } moduleInfo)
{
foreach (var moduleType in moduleInfo.ModuleFlags)
{
availableFlags.Add(moduleType);
}
}
var moduleTypeDropDown = new GUIDropDown(new RectTransform(new Vector2(0.5f, 1f), outpostModuleGroup.RectTransform),
text: LocalizedString.Join(", ", MainSub?.Info?.OutpostModuleInfo?.ModuleFlags.Select(s => TextManager.Capitalize(s.Value)) ?? ((LocalizedString)"None").ToEnumerable()), selectMultiple: true);
foreach (Identifier flag in availableFlags.OrderBy(f => f.Value, StringComparer.InvariantCultureIgnoreCase))
@@ -2221,7 +2252,7 @@ namespace Barotrauma
moduleTypeDropDown.SelectItem(flag);
}
}
moduleTypeDropDown.OnSelected += (_, __) =>
moduleTypeDropDown.AfterSelected += (_, __) =>
{
if (MainSub?.Info?.OutpostModuleInfo == null) { return false; }
MainSub.Info.OutpostModuleInfo.SetFlags(moduleTypeDropDown.SelectedDataMultiple.Cast<Identifier>());
@@ -2232,9 +2263,34 @@ namespace Barotrauma
};
outpostModuleGroup.RectTransform.MinSize = new Point(0, outpostModuleGroup.RectTransform.Children.Max(c => c.MinSize.Y));
var addTypeGroup = new GUILayoutGroup(new RectTransform(new Vector2(.975f, 0.1f), outpostModuleSettingsContainer.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), addTypeGroup.RectTransform), TextManager.Get("leveleditor.addmoduletype"), textAlignment: Alignment.CenterLeft);
var textBox = new GUITextBox(new RectTransform(new Vector2(0.4f, 1f), addTypeGroup.RectTransform));
new GUIButton(new RectTransform(new Vector2(0.1f, 0.9f), addTypeGroup.RectTransform), text: "+", style: "GUIButtonSmallFreeScale")
{
OnClicked = (btn, _) =>
{
if (textBox.Text.IsNullOrEmpty())
{
textBox.Flash();
return false;
}
if (MainSub?.Info?.OutpostModuleInfo is { } moduleInfo)
{
moduleInfo.SetFlags(moduleInfo.ModuleFlags.Append(textBox.Text.ToIdentifier()).ToList());
//refresh
saveFrame = null;
CreateSaveScreen();
}
return true;
}
};
addTypeGroup.RectTransform.MinSize = new Point(0, addTypeGroup.RectTransform.Children.Max(c => c.MinSize.Y));
// module flags ---------------------
var allowAttachGroup = new GUILayoutGroup(new RectTransform(new Vector2(.975f, 0.1f), outpostSettingsContainer.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
var allowAttachGroup = new GUILayoutGroup(new RectTransform(new Vector2(.975f, 0.1f), outpostModuleSettingsContainer.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), allowAttachGroup.RectTransform), TextManager.Get("outpostmoduleallowattachto"), textAlignment: Alignment.CenterLeft);
@@ -2257,7 +2313,7 @@ namespace Barotrauma
allowAttachDropDown.SelectItem(flag);
}
}
allowAttachDropDown.OnSelected += (_, __) =>
allowAttachDropDown.AfterSelected += (_, __) =>
{
if (MainSub?.Info?.OutpostModuleInfo == null) { return false; }
MainSub.Info.OutpostModuleInfo.SetAllowAttachTo(allowAttachDropDown.SelectedDataMultiple.Cast<Identifier>());
@@ -2270,7 +2326,7 @@ namespace Barotrauma
// location types ---------------------
var locationTypeGroup = new GUILayoutGroup(new RectTransform(new Vector2(.975f, 0.1f), outpostSettingsContainer.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
var locationTypeGroup = new GUILayoutGroup(new RectTransform(new Vector2(.975f, 0.1f), outpostModuleSettingsContainer.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), locationTypeGroup.RectTransform), TextManager.Get("outpostmoduleallowedlocationtypes"), textAlignment: Alignment.CenterLeft);
HashSet<Identifier> availableLocationTypes = new HashSet<Identifier>();
@@ -2290,7 +2346,7 @@ namespace Barotrauma
}
if (!MainSub.Info?.OutpostModuleInfo?.AllowedLocationTypes?.Any() ?? true) { locationTypeDropDown.SelectItem("any".ToIdentifier()); }
locationTypeDropDown.OnSelected += (_, __) =>
locationTypeDropDown.AfterSelected += (_, __) =>
{
MainSub?.Info?.OutpostModuleInfo?.SetAllowedLocationTypes(locationTypeDropDown.SelectedDataMultiple.Cast<Identifier>());
locationTypeDropDown.Text = ToolBox.LimitString(locationTypeDropDown.Text.Value, locationTypeDropDown.Font, locationTypeDropDown.Rect.Width);
@@ -2300,7 +2356,7 @@ namespace Barotrauma
// gap positions ---------------------
var gapPositionGroup = new GUILayoutGroup(new RectTransform(new Vector2(.975f, 0.1f), outpostSettingsContainer.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
var gapPositionGroup = new GUILayoutGroup(new RectTransform(new Vector2(.975f, 0.1f), outpostModuleSettingsContainer.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), gapPositionGroup.RectTransform), TextManager.Get("outpostmodulegappositions"), textAlignment: Alignment.CenterLeft);
var gapPositionDropDown = new GUIDropDown(new RectTransform(new Vector2(0.5f, 1f), gapPositionGroup.RectTransform),
text: "", selectMultiple: true);
@@ -2323,7 +2379,7 @@ namespace Barotrauma
}
}
gapPositionDropDown.OnSelected += (_, __) =>
gapPositionDropDown.AfterSelected += (_, __) =>
{
if (MainSub.Info?.OutpostModuleInfo == null) { return false; }
MainSub.Info.OutpostModuleInfo.GapPositions = OutpostModuleInfo.GapPosition.None;
@@ -2345,7 +2401,7 @@ namespace Barotrauma
};
gapPositionGroup.RectTransform.MinSize = new Point(0, gapPositionGroup.RectTransform.Children.Max(c => c.MinSize.Y));
var canAttachToPrevGroup = new GUILayoutGroup(new RectTransform(new Vector2(.975f, 0.1f), outpostSettingsContainer.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
var canAttachToPrevGroup = new GUILayoutGroup(new RectTransform(new Vector2(.975f, 0.1f), outpostModuleSettingsContainer.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), canAttachToPrevGroup.RectTransform), TextManager.Get("canattachtoprevious"), textAlignment: Alignment.CenterLeft)
{
ToolTip = TextManager.Get("canattachtoprevious.tooltip")
@@ -2365,7 +2421,7 @@ namespace Barotrauma
}
}
canAttachToPrevDropDown.OnSelected += (_, __) =>
canAttachToPrevDropDown.AfterSelected += (_, __) =>
{
if (Submarine.MainSub.Info?.OutpostModuleInfo == null) { return false; }
Submarine.MainSub.Info.OutpostModuleInfo.CanAttachToPrevious = OutpostModuleInfo.GapPosition.None;
@@ -2390,7 +2446,7 @@ namespace Barotrauma
// -------------------
var maxModuleCountGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), outpostSettingsContainer.RectTransform), isHorizontal: true)
var maxModuleCountGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), outpostModuleSettingsContainer.RectTransform), isHorizontal: true)
{
Stretch = true
};
@@ -2410,8 +2466,9 @@ namespace Barotrauma
MainSub.Info.OutpostModuleInfo.MaxCount = numberInput.IntValue;
}
};
maxModuleCountGroup.RectTransform.MinSize = new Point(0, maxModuleCountGroup.RectTransform.Children.Max(c => c.MinSize.Y));
var commonnessGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), outpostSettingsContainer.RectTransform), isHorizontal: true)
var commonnessGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), outpostModuleSettingsContainer.RectTransform), isHorizontal: true)
{
Stretch = true
};
@@ -2427,7 +2484,9 @@ namespace Barotrauma
MainSub.Info.OutpostModuleInfo.Commonness = numberInput.FloatValue;
}
};
outpostSettingsContainer.RectTransform.MinSize = new Point(0, outpostSettingsContainer.RectTransform.Children.Sum(c => c.Children.Any() ? c.Children.Max(c2 => c2.MinSize.Y) : 0));
commonnessGroup.RectTransform.MinSize = new Point(0, commonnessGroup.RectTransform.Children.Max(c => c.MinSize.Y));
outpostModuleSettingsContainer.RectTransform.MinSize = new Point(0, outpostModuleSettingsContainer.RectTransform.Children.Sum(c => c.Children.Any() ? c.Children.Max(c2 => c2.MinSize.Y) : 0));
//---------------------------------------
@@ -2476,6 +2535,133 @@ namespace Barotrauma
//---------------------------------------
var outpostSettingsContainer = new GUILayoutGroup(new RectTransform(Vector2.One, subTypeDependentSettingFrame.RectTransform))
{
CanBeFocused = true,
Visible = false,
Stretch = true
};
var outpostTagsGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), outpostSettingsContainer.RectTransform), isHorizontal: true)
{
Stretch = true
};
new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), outpostTagsGroup.RectTransform),
TextManager.Get("sp.item.tags.name"), textAlignment: Alignment.CenterLeft, wrap: true);
var outpostTagsBox = new GUITextBox(new RectTransform(new Vector2(0.4f, 1.0f), outpostTagsGroup.RectTransform))
{
OnEnterPressed = (GUITextBox textBox, string text) =>
{
MainSub.Info.OutpostTags = text.ToIdentifiers().ToImmutableHashSet();
return true;
},
OverflowClip = true,
Text = "default"
};
outpostTagsBox.OnDeselected += (textbox, _) =>
{
MainSub.Info.OutpostTags = outpostTagsBox.Text.ToIdentifiers().ToImmutableHashSet();
};
if (MainSub.Info.OutpostTags != null)
{
outpostTagsBox.Text = MainSub.Info.OutpostTags.ConvertToString();
}
outpostTagsGroup.RectTransform.MaxSize = outpostTagsBox.RectTransform.MaxSize;
var triggerMissionTagsGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), outpostSettingsContainer.RectTransform), isHorizontal: true)
{
Stretch = true
};
new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), triggerMissionTagsGroup.RectTransform),
TextManager.Get("outpost.triggeroutpostmissionevents"), textAlignment: Alignment.CenterLeft, wrap: true);
var triggerMissionTagsBox = new GUITextBox(new RectTransform(new Vector2(0.4f, 1.0f), triggerMissionTagsGroup.RectTransform))
{
OnEnterPressed = (GUITextBox textBox, string text) =>
{
MainSub.Info.TriggerOutpostMissionEvents = text.ToIdentifiers().ToImmutableHashSet();
return true;
},
ToolTip = TextManager.Get("outpost.triggeroutpostmissionevents.tooltip"),
OverflowClip = true,
Text = "default"
};
triggerMissionTagsBox.OnDeselected += (textbox, _) =>
{
MainSub.Info.TriggerOutpostMissionEvents = triggerMissionTagsBox.Text.ToIdentifiers().ToImmutableHashSet();
};
if (MainSub.Info.TriggerOutpostMissionEvents != null)
{
triggerMissionTagsBox.Text = MainSub.Info.TriggerOutpostMissionEvents.ConvertToString();
}
triggerMissionTagsGroup.RectTransform.MaxSize = triggerMissionTagsBox.RectTransform.MaxSize;
//---------------------------------------
var enemySubmarineSettingsContainer = new GUILayoutGroup(new RectTransform(Vector2.One, subTypeDependentSettingFrame.RectTransform))
{
CanBeFocused = true,
Visible = false,
Stretch = true
};
// -------------------
var enemySubmarineRewardGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), enemySubmarineSettingsContainer.RectTransform), isHorizontal: true)
{
Stretch = true
};
new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), enemySubmarineRewardGroup.RectTransform),
TextManager.Get("enemysub.reward"), textAlignment: Alignment.CenterLeft, wrap: true);
numInput = new GUINumberInput(new RectTransform(new Vector2(0.4f, 1.0f), enemySubmarineRewardGroup.RectTransform), NumberType.Int, buttonVisibility: GUINumberInput.ButtonVisibility.ForceHidden)
{
IntValue = (int)(MainSub?.Info?.EnemySubmarineInfo?.Reward ?? 4000),
MinValueInt = 0,
MaxValueInt = 999999,
OnValueChanged = (numberInput) =>
{
MainSub.Info.EnemySubmarineInfo.Reward = numberInput.IntValue;
}
};
enemySubmarineRewardGroup.RectTransform.MaxSize = numInput.TextBox.RectTransform.MaxSize;
var enemySubmarineDifficultyGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), enemySubmarineSettingsContainer.RectTransform), isHorizontal: true)
{
Stretch = true
};
new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), enemySubmarineDifficultyGroup.RectTransform),
TextManager.Get("preferreddifficulty"), textAlignment: Alignment.CenterLeft, wrap: true);
numInput = new GUINumberInput(new RectTransform(new Vector2(0.4f, 1.0f), enemySubmarineDifficultyGroup.RectTransform), NumberType.Int)
{
IntValue = (int)(MainSub?.Info?.EnemySubmarineInfo?.PreferredDifficulty ?? 50),
MinValueInt = 0,
MaxValueInt = 100,
OnValueChanged = (numberInput) =>
{
MainSub.Info.EnemySubmarineInfo.PreferredDifficulty = numberInput.IntValue;
}
};
enemySubmarineDifficultyGroup.RectTransform.MaxSize = numInput.TextBox.RectTransform.MaxSize;
var enemySubmarineTagsGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), enemySubmarineSettingsContainer.RectTransform), isHorizontal: true)
{
Stretch = true
};
new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), enemySubmarineTagsGroup.RectTransform),
TextManager.Get("sp.item.tags.name"), textAlignment: Alignment.CenterLeft, wrap: true);
var tagsBox = new GUITextBox(new RectTransform(new Vector2(0.4f, 1.0f), enemySubmarineTagsGroup.RectTransform))
{
OnEnterPressed = ChangeEnemySubTags,
OverflowClip = true,
Text = "default"
};
tagsBox.OnDeselected += (textbox, _) => ChangeEnemySubTags(textbox, textbox.Text);
if (MainSub?.Info?.EnemySubmarineInfo?.MissionTags != null)
{
tagsBox.Text = string.Join(',', MainSub.Info.EnemySubmarineInfo.MissionTags);
}
enemySubmarineTagsGroup.RectTransform.MaxSize = tagsBox.RectTransform.MaxSize;
enemySubmarineSettingsContainer.RectTransform.MinSize = new Point(0, enemySubmarineSettingsContainer.RectTransform.Children.Sum(c => c.Children.Any() ? c.Children.Max(c2 => c2.MinSize.Y) : 0));
//--------------------------------------------------------
var beaconSettingsContainer = new GUILayoutGroup(new RectTransform(Vector2.One, extraSettingsContainer.RectTransform))
{
CanBeFocused = true,
@@ -2530,7 +2716,7 @@ namespace Barotrauma
Stretch = true
};
var priceGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), subSettingsContainer.RectTransform), isHorizontal: true)
var priceGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), subSettingsContainer.RectTransform), isHorizontal: true)
{
Stretch = true
};
@@ -2554,7 +2740,7 @@ namespace Barotrauma
MainSub.Info.Price = Math.Max(MainSub.Info.Price, basePrice);
}
var classGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), subSettingsContainer.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
var classGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), subSettingsContainer.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
Stretch = true
};
@@ -2587,7 +2773,7 @@ namespace Barotrauma
};
classDropDown.SelectItem(!MainSub.Info.HasTag(SubmarineTag.Shuttle) ? MainSub.Info.SubmarineClass : (object)SubmarineTag.Shuttle);
var tierGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), subSettingsContainer.RectTransform), isHorizontal: true)
var tierGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), subSettingsContainer.RectTransform), isHorizontal: true)
{
Stretch = true
};
@@ -2612,14 +2798,14 @@ namespace Barotrauma
MainSub.Info.Tier = Math.Clamp(MainSub.Info.Tier, 1, SubmarineInfo.HighestTier);
}
var crewSizeArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), subSettingsContainer.RectTransform), isHorizontal: true)
var crewSizeArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), subSettingsContainer.RectTransform), isHorizontal: true)
{
Stretch = true,
AbsoluteSpacing = 5
};
new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), crewSizeArea.RectTransform),
TextManager.Get("RecommendedCrewSize"), textAlignment: Alignment.CenterLeft, wrap: true, font: GUIStyle.SmallFont);
TextManager.Get("RecommendedCrewSize"), textAlignment: Alignment.CenterLeft, wrap: true);
var crewSizeMin = new GUINumberInput(new RectTransform(new Vector2(0.17f, 1.0f), crewSizeArea.RectTransform), NumberType.Int, relativeButtonAreaWidth: 0.25f)
{
MinValueInt = 1,
@@ -2646,14 +2832,14 @@ namespace Barotrauma
MainSub.Info.RecommendedCrewSizeMax = crewSizeMax.IntValue;
};
var crewExpArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), subSettingsContainer.RectTransform), isHorizontal: true)
var crewExpArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), subSettingsContainer.RectTransform), isHorizontal: true)
{
Stretch = true,
AbsoluteSpacing = 5
};
new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), crewExpArea.RectTransform),
TextManager.Get("RecommendedCrewExperience"), textAlignment: Alignment.CenterLeft, wrap: true, font: GUIStyle.SmallFont);
TextManager.Get("RecommendedCrewExperience"), textAlignment: Alignment.CenterLeft, wrap: true);
var toggleExpLeft = new GUIButton(new RectTransform(new Vector2(0.05f, 1.0f), crewExpArea.RectTransform), style: "GUIButtonToggleLeft");
var experienceText = new GUITextBlock(new RectTransform(new Vector2(0.3f, 1.0f), crewExpArea.RectTransform),
@@ -2682,13 +2868,13 @@ namespace Barotrauma
return true;
};
var hideInMenusArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), subSettingsContainer.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
var hideInMenusArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), subSettingsContainer.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
Stretch = true,
AbsoluteSpacing = 5
};
new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), hideInMenusArea.RectTransform),
TextManager.Get("HideInMenus"), textAlignment: Alignment.CenterLeft, wrap: true, font: GUIStyle.SmallFont);
TextManager.Get("HideInMenus"), textAlignment: Alignment.CenterLeft, wrap: true);
new GUITickBox(new RectTransform((0.4f, 1.0f), hideInMenusArea.RectTransform), "")
{
@@ -2707,13 +2893,13 @@ namespace Barotrauma
}
};
var outFittingArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), subSettingsContainer.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
var outFittingArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), subSettingsContainer.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
Stretch = true,
AbsoluteSpacing = 5
};
new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), outFittingArea.RectTransform),
TextManager.Get("ManuallyOutfitted"), textAlignment: Alignment.CenterLeft, wrap: true, font: GUIStyle.SmallFont)
TextManager.Get("ManuallyOutfitted"), textAlignment: Alignment.CenterLeft, wrap: true)
{
ToolTip = TextManager.Get("manuallyoutfittedtooltip")
};
@@ -2757,15 +2943,27 @@ namespace Barotrauma
{
MainSub.Info.WreckInfo ??= new WreckInfo(MainSub.Info);
}
else if (type == SubmarineType.EnemySubmarine)
{
MainSub.Info.EnemySubmarineInfo ??= new EnemySubmarineInfo(MainSub.Info);
}
previewImageButtonHolder.Children.ForEach(c => c.Enabled = MainSub.Info.AllowPreviewImage);
outpostSettingsContainer.Visible = type == SubmarineType.OutpostModule;
outpostModuleSettingsContainer.Visible = type == SubmarineType.OutpostModule;
extraSettingsContainer.Visible = type == SubmarineType.BeaconStation || type == SubmarineType.Wreck;
beaconSettingsContainer.Visible = type == SubmarineType.BeaconStation;
enemySubmarineSettingsContainer.Visible = type == SubmarineType.EnemySubmarine;
subSettingsContainer.Visible = type == SubmarineType.Player;
outpostSettingsContainer.Visible = type == SubmarineType.Outpost;
return true;
};
subSettingsContainer.RectTransform.MinSize = new Point(0, subSettingsContainer.RectTransform.Children.Sum(c => c.Children.Any() ? c.Children.Max(c2 => c2.MinSize.Y) : 0));
int minHeight = subSettingsContainer.Children.First().Children.Max(c => c.RectTransform.MinSize.Y);
foreach (var child in subSettingsContainer.Children)
{
child.RectTransform.MinSize = new Point(0, minHeight);
}
// right column ---------------------------------------------------
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), rightColumn.RectTransform), TextManager.Get("SubPreviewImage"), font: GUIStyle.SubHeadingFont);
@@ -3036,11 +3234,12 @@ namespace Barotrauma
paddedSaveFrame.Recalculate();
leftColumn.Recalculate();
subSettingsContainer.RectTransform.MinSize = outpostSettingsContainer.RectTransform.MinSize = beaconSettingsContainer.RectTransform.MinSize =
new Point(0, Math.Max(subSettingsContainer.Rect.Height, outpostSettingsContainer.Rect.Height));
subSettingsContainer.RectTransform.MinSize = outpostModuleSettingsContainer.RectTransform.MinSize = beaconSettingsContainer.RectTransform.MinSize =
new Point(0, Math.Max(subSettingsContainer.Rect.Height, outpostModuleSettingsContainer.Rect.Height));
subSettingsContainer.Recalculate();
outpostSettingsContainer.Recalculate();
outpostModuleSettingsContainer.Recalculate();
beaconSettingsContainer.Recalculate();
enemySubmarineSettingsContainer.Recalculate();
descriptionBox.Text = MainSub == null ? "" : MainSub.Info.Description.Value;
submarineDescriptionCharacterCount.Text = descriptionBox.Text.Length + " / " + submarineDescriptionLimit;
@@ -3344,7 +3543,7 @@ namespace Barotrauma
};
searchBox.OnSelected += (sender, userdata) => { searchTitle.Visible = false; };
searchBox.OnDeselected += (sender, userdata) => { searchTitle.Visible = true; };
searchBox.OnDeselected += (sender, userdata) => { searchTitle.Visible = sender.Text.IsNullOrEmpty(); };
searchBox.OnTextChanged += (textBox, text) => { FilterSubs(subList, text); return true; };
var sortedSubs = GetLoadableSubs()
@@ -3542,7 +3741,7 @@ namespace Barotrauma
/// </summary>
private void LoadAutoSave(object userData)
{
if (!(userData is XElement element)) { return; }
if (userData is not XElement element) { return; }
#warning TODO: revise
string filePath = element.GetAttributeStringUnrestricted("file", "");
@@ -3566,6 +3765,8 @@ namespace Barotrauma
MainSub.Info.Name = loadedSub.Info.Name;
subNameLabel.Text = ToolBox.LimitString(loadedSub.Info.Name, subNameLabel.Font, subNameLabel.Rect.Width);
ReconstructLayers();
CreateDummyCharacter();
cam.Position = MainSub.Position + MainSub.HiddenSubPosition;
@@ -3769,10 +3970,10 @@ namespace Barotrauma
{
if (subPackage != null)
{
File.Delete(sub.FilePath);
File.Delete(sub.FilePath, catchUnauthorizedAccessExceptions: false);
ModProject modProject = new ModProject(subPackage);
modProject.RemoveFile(modProject.Files.First(f => ContentPath.FromRaw(subPackage, f.Path) == sub.FilePath));
modProject.Save(subPackage.Path);
modProject.Save(subPackage.Path, catchUnauthorizedAccessExceptions: true);
ReloadModifiedPackage(subPackage);
if (MainSub?.Info != null && MainSub.Info.FilePath == sub.FilePath)
{
@@ -4458,7 +4659,7 @@ namespace Barotrauma
private bool SelectLinkedSub(GUIComponent selected, object userData)
{
if (!(selected.UserData is SubmarineInfo submarine)) return false;
if (userData is not SubmarineInfo submarine) { return false; }
var prefab = new LinkedSubmarinePrefab(submarine);
MapEntityPrefab.SelectPrefab(prefab);
return true;
@@ -4573,11 +4774,32 @@ namespace Barotrauma
return false;
}
if (MainSub != null) MainSub.Info.Name = text;
if (MainSub != null) { MainSub.Info.Name = text; }
textBox.Deselect();
textBox.Text = text;
textBox.Flash(GUIStyle.Green);
return true;
}
private bool ChangeEnemySubTags(GUITextBox textBox, string text)
{
if (string.IsNullOrWhiteSpace(text))
{
textBox.Flash(GUIStyle.Red);
return false;
}
if (MainSub.Info.EnemySubmarineInfo is { } enemySubInfo)
{
enemySubInfo.MissionTags.Clear();
string[] tags = text.Split(',');
foreach (string tag in tags)
{
enemySubInfo.MissionTags.Add(tag.ToIdentifier());
}
}
textBox.Text = text;
textBox.Flash(GUIStyle.Green);
return true;
@@ -5595,12 +5817,14 @@ namespace Barotrauma
{
foreach (LightComponent lightComponent in item.GetComponents<LightComponent>())
{
lightComponent.Light.Color =
(item.body == null || item.body.Enabled || item.ParentInventory is ItemInventory { Container.HideItems: true }) &&
bool visibleInContainer = item.FindParentInventory(static it => it is ItemInventory { Container.HideItems: true }) == null;
lightComponent.Light.Color =
((item.body == null || !item.body.Enabled) && !visibleInContainer) ||
/*the light is only visible when worn -> can't be visible in the editor*/
lightComponent.Parent is not Wearable ?
lightComponent.LightColor :
Color.Transparent;
lightComponent.Parent is Wearable ?
Color.Transparent :
lightComponent.LightColor;
lightComponent.Light.LightSpriteEffect = lightComponent.Item.SpriteEffects;
}
}
@@ -5741,7 +5965,7 @@ namespace Barotrauma
newItem.Remove();
}
}
else if (itemContainer != null && itemContainer.Inventory.CanBePut(itemPrefab))
else if (itemContainer != null && itemContainer.Inventory.CanProbablyBePut(itemPrefab))
{
bool placedItem = itemContainer.Inventory.TryPutItem(newItem, dummyCharacter);
spawnedItem |= placedItem;
@@ -1,4 +1,4 @@
#nullable enable
#nullable enable
using System.Linq;
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
@@ -49,7 +49,7 @@ namespace Barotrauma
}
dummyCharacter = Character.Create(CharacterPrefab.HumanSpeciesName, Vector2.Zero, "", id: Entity.DummyID, hasAi: false);
dummyCharacter.Info.Job = new Job(JobPrefab.Prefabs.FirstOrDefault(static jp => jp.Identifier == "captain"));
dummyCharacter.Info.Job = new Job(JobPrefab.Prefabs.FirstOrDefault(static jp => jp.Identifier == "captain"), isPvP: false);
dummyCharacter.Info.Name = "Galldren";
dummyCharacter.Inventory.CreateSlots();
dummyCharacter.Info.GiveExperience(999999);
@@ -59,6 +59,9 @@ namespace Barotrauma
Character.Controlled = dummyCharacter;
GameMain.World.ProcessChanges();
dummyCharacter.Info.TalentRefundPoints = 2;
TabMenu = new TabMenu();
}
public override void AddToGUIUpdateList()