Release 1.9.7.0 - Summer Update 2025

This commit is contained in:
Regalis11
2025-06-17 16:38:11 +03:00
parent 22227f13e5
commit ea5a2bc693
297 changed files with 7344 additions and 2421 deletions
@@ -206,7 +206,9 @@ namespace Barotrauma
LocalizedString reputationName = GetReputationName(normalizedValue);
LocalizedString formattedReputation = TextManager.GetWithVariables("reputationformat",
("[reputationname]", reputationName),
("[reputationvalue]", ((int)Math.Round(value)).ToString()));
//simply cast to float (dropping the decimals)
//we don't want any rounding here, otherwise it might look like you have enough rep to reach some threshold when you're actually some fraction off
("[reputationvalue]", ((int)value).ToString()));
if (addColorTags)
{
formattedReputation = $"‖color:{XMLExtensions.ToStringHex(GetReputationColor(normalizedValue))}‖{formattedReputation}‖end‖";
@@ -160,10 +160,34 @@ namespace Barotrauma
protected set;
}
public bool PurchasedLostShuttlesInLatestSave, PurchasedHullRepairsInLatestSave, PurchasedItemRepairsInLatestSave;
/// <summary>
/// Has recovery of lost shuttles been purchased in the latest save? Determines whether the shuttles should be recovered when loading into the round.
/// </summary>
public bool PurchasedLostShuttlesInLatestSave;
/// <summary>
/// Have hull repairs been purchased in the latest save? Determines whether the walls will be repaired when loading into the round.
/// </summary>
public bool PurchasedHullRepairsInLatestSave;
/// <summary>
/// Has repairing damaged items been purchased in the latest save? Determines whether the items will be repaired when loading into the round.
/// </summary>
public bool PurchasedItemRepairsInLatestSave;
/// <summary>
/// Have hull repairs been purchased on the current round?
/// </summary>
public virtual bool PurchasedHullRepairs { get; set; }
/// <summary>
/// Has recovery of lost shuttles been purchased on the current round?
/// </summary>
public virtual bool PurchasedLostShuttles { get; set; }
/// <summary>
/// Has repairing damaged items been purchased on the current round?
/// </summary>
public virtual bool PurchasedItemRepairs { get; set; }
public bool DivingSuitWarningShown;
@@ -441,6 +465,16 @@ namespace Barotrauma
currentLocation.DeselectMission(mission);
}
}
foreach (var mission in currentLocation.AvailableMissions)
{
//if the mission isn't shown in menus, it cannot be selected by the player -> must be something that is supposed to be automatically selected
if (!mission.Prefab.ShowInMenus)
{
currentLocation.SelectMission(mission);
}
}
if (levelData.HasBeaconStation && !levelData.IsBeaconActive && Missions.None(m => m.Prefab.Type == Tags.MissionTypeBeacon))
{
var beaconMissionPrefabs = MissionPrefab.Prefabs.Where(m => m.IsSideObjective && m.Type == Tags.MissionTypeBeacon);
@@ -1632,6 +1666,70 @@ namespace Barotrauma
parentElement?.Add(petsElement);
}
/// <summary>
/// Loads the parts of a campaign save that are the same between single player and multiplayer saves.
/// </summary>
public void LoadSaveSharedSingleAndMultiplayer(XElement element)
{
PurchasedLostShuttlesInLatestSave = element.GetAttributeBool("purchasedlostshuttles", false);
PurchasedHullRepairsInLatestSave = element.GetAttributeBool("purchasedhullrepairs", false);
PurchasedItemRepairsInLatestSave = element.GetAttributeBool("purchaseditemrepairs", false);
CheatsEnabled = element.GetAttributeBool("cheatsenabled", false);
if (CheatsEnabled)
{
DebugConsole.CheatsEnabled = true;
if (!AchievementManager.CheatsEnabled)
{
AchievementManager.CheatsEnabled = true;
#if CLIENT
new GUIMessageBox("Cheats enabled", "Cheat commands have been enabled on the server. You will not receive achievements until you restart the game.");
#else
DebugConsole.NewMessage("Cheat commands have been enabled.", Color.Red);
#endif
}
}
//backwards compatibility for saves made prior to the addition of personal wallets
int oldMoney = element.GetAttributeInt("money", 0);
if (oldMoney > 0)
{
Bank = new Wallet(Option<Character>.None())
{
Balance = oldMoney
};
}
foreach (var subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "cargo":
CargoManager.LoadPurchasedItems(subElement);
break;
case "pendingupgrades": //backwards compatibility
case "upgrademanager":
UpgradeManager = new UpgradeManager(this, subElement, isSingleplayer: IsSinglePlayer);
break;
case "pets":
petsElement = subElement;
break;
case Wallet.LowerCaseSaveElementName:
Bank = new Wallet(Option<Character>.None(), subElement);
break;
case "stats":
LoadStats(subElement);
break;
case "eventmanager":
GameMain.GameSession.EventManager.Load(subElement);
break;
case "unlockedrecipe":
GameMain.GameSession.UnlockRecipe(subElement.GetAttributeIdentifier("identifier", Identifier.Empty), showNotifications: false);
break;
}
}
}
public void LoadPets()
{
if (petsElement != null)
@@ -161,23 +161,7 @@ namespace Barotrauma
/// </summary>
private void Load(XElement element)
{
PurchasedLostShuttlesInLatestSave = element.GetAttributeBool("purchasedlostshuttles", false);
PurchasedHullRepairsInLatestSave = element.GetAttributeBool("purchasedhullrepairs", false);
PurchasedItemRepairsInLatestSave = element.GetAttributeBool("purchaseditemrepairs", false);
CheatsEnabled = element.GetAttributeBool("cheatsenabled", false);
if (CheatsEnabled)
{
DebugConsole.CheatsEnabled = true;
if (!AchievementManager.CheatsEnabled)
{
AchievementManager.CheatsEnabled = true;
#if CLIENT
new GUIMessageBox("Cheats enabled", "Cheat commands have been enabled on the server. You will not receive achievements until you restart the game.");
#else
DebugConsole.NewMessage("Cheat commands have been enabled.", Color.Red);
#endif
}
}
LoadSaveSharedSingleAndMultiplayer(element);
foreach (var subElement in element.Elements())
{
@@ -215,30 +199,11 @@ namespace Barotrauma
}
}
break;
case "upgrademanager":
case "pendingupgrades":
UpgradeManager = new UpgradeManager(this, subElement, isSingleplayer: false);
break;
case "bots" when GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer:
CrewManager.HasBots = subElement.GetAttributeBool("hasbots", false);
CrewManager.AddCharacterElements(subElement);
ActiveOrdersElement = subElement.GetChildElement("activeorders");
break;
case "cargo":
CargoManager?.LoadPurchasedItems(subElement);
break;
case "pets":
petsElement = subElement;
break;
case "stats":
LoadStats(subElement);
break;
case "eventmanager":
GameMain.GameSession.EventManager.Load(subElement);
break;
case Wallet.LowerCaseSaveElementName:
Bank = new Wallet(Option<Character>.None(), subElement);
break;
#if SERVER
case "traitormanager":
GameMain.Server?.TraitorManager?.Load(subElement);
@@ -253,15 +218,6 @@ namespace Barotrauma
}
}
int oldMoney = element.GetAttributeInt("money", 0);
if (oldMoney > 0)
{
Bank = new Wallet(Option<Character>.None())
{
Balance = oldMoney
};
}
UpgradeManager ??= new UpgradeManager(this);
#if SERVER
@@ -170,6 +170,9 @@ namespace Barotrauma
public Submarine? Submarine { get; set; }
private readonly HashSet<Identifier> unlockedRecipes = new HashSet<Identifier>();
public IEnumerable<Identifier> UnlockedRecipes => unlockedRecipes;
public CampaignDataPath DataPath { get; set; }
public bool TraitorsEnabled =>
@@ -275,6 +278,10 @@ namespace Barotrauma
}
}
break;
//NOTE: if you're adding something that's supposed to load something that's persistent in a campaign,
//this is probably not the correct place! This is where the GameSession itself is initialized,
//and if you let's say quit to the server lobby and reload, this method won't be called again.
//You should probably add it to CampaignMode.LoadSaveSharedSingleAndMultiplayer
}
}
}
@@ -385,7 +392,7 @@ namespace Barotrauma
var dummyLocations = new Location[2];
for (int i = 0; i < 2; i++)
{
dummyLocations[i] = Location.CreateRandom(new Vector2((float)rand.NextDouble() * 10000.0f, (float)rand.NextDouble() * 10000.0f), null, rand, requireOutpost: true, forceLocationType);
dummyLocations[i] = Location.CreateRandom(new Vector2((float)rand.NextDouble() * 10000.0f, (float)rand.NextDouble() * 10000.0f), zone: null, biomeId: null, rand, requireOutpost: true, forceLocationType);
}
return dummyLocations;
}
@@ -401,6 +408,7 @@ namespace Barotrauma
public void LoadPreviousSave()
{
AchievementManager.OnRoundEnded(this, roundInterrupted: true);
Submarine.Unload();
SaveUtil.LoadGame(DataPath);
}
@@ -1491,6 +1499,25 @@ namespace Barotrauma
#endif
}
public void UnlockRecipe(Identifier identifier, bool showNotifications)
{
if (unlockedRecipes.Add(identifier))
{
#if CLIENT
if (showNotifications)
{
foreach (var character in GetSessionCrewCharacters(CharacterType.Both))
{
LocalizedString recipeName = TextManager.Get($"entityname.{identifier}").Fallback(identifier.Value);
character.AddMessage(TextManager.GetWithVariable("recipeunlockednotification", "[name]", recipeName).Value, GUIStyle.Yellow, playSound: true);
}
}
#else
GameMain.Server.UnlockRecipe(identifier);
#endif
}
}
public static bool IsCompatibleWithEnabledContentPackages(IList<string> contentPackageNames, out LocalizedString errorMsg)
{
errorMsg = "";
@@ -1602,9 +1629,11 @@ namespace Barotrauma
ownedSubsElement.Add(new XElement("sub", new XAttribute("name", ownedSub.Name)));
}
}
if (Map != null) { rootElement.Add(new XAttribute("mapseed", Map.Seed)); }
rootElement.Add(new XAttribute("selectedcontentpackagenames",
string.Join("|", ContentPackageManager.EnabledPackages.All.Where(cp => cp.HasMultiplayerSyncedContent).Select(cp => cp.Name.Replace("|", @"\|")))));
XElement permadeathsElement = new XElement("permadeaths");
foreach (var kvp in permadeathsPerAccount)
@@ -169,12 +169,14 @@ namespace Barotrauma
/// Purchased upgrades are temporarily stored in <see cref="PendingUpgrades"/> and they are applied
/// after the next round starts similarly how items are spawned in the stowage room after the round starts.
/// </remarks>
public void PurchaseUpgrade(UpgradePrefab prefab, UpgradeCategory category, bool force = false, Client? client = null)
public bool TryPurchaseUpgrade(UpgradePrefab prefab, UpgradeCategory category, bool force = false, Client? client = null)
{
if (!HasPermissionToManageUpgrades(client)) { return false; }
if (!CanUpgradeSub())
{
DebugConsole.ThrowError("Cannot upgrade when switching to another submarine.");
return;
return false;
}
int price = prefab.Price.GetBuyPrice(prefab, GetUpgradeLevel(prefab, category), Campaign.Map?.CurrentLocation);
@@ -185,7 +187,7 @@ namespace Barotrauma
if (currentLevel + 1 > maxLevel)
{
DebugConsole.ThrowError($"Tried to purchase \"{prefab.Name}\" over the max level! ({newLevel} > {maxLevel}). The transaction has been cancelled.");
return;
return false;
}
bool TryTakeResources(Character character)
@@ -203,17 +205,17 @@ namespace Barotrauma
switch (GameMain.NetworkMember)
{
case null when Character.Controlled is { } controlled: // singleplayer
if (!TryTakeResources(controlled)) { return; }
if (!TryTakeResources(controlled)) { return false; }
break;
case { IsClient: true }:
if (!prefab.HasResourcesToUpgrade(Character.Controlled, newLevel)) { return; }
if (!prefab.HasResourcesToUpgrade(Character.Controlled, newLevel)) { return false; }
break;
case { IsServer: true } when client?.Character is { } character:
if (!TryTakeResources(character)) { return; }
if (!TryTakeResources(character)) { return false; }
break;
default:
DebugConsole.ThrowError($"Tried to purchase \"{prefab.Name}\" without a player.");
return;
return false;
}
}
@@ -270,11 +272,15 @@ namespace Barotrauma
PurchasedUpgrades.Add(new PurchasedUpgrade(prefab, category));
#endif
OnUpgradesChanged?.Invoke(this);
return true;
}
else
{
DebugConsole.ThrowError("Tried to purchase an upgrade with insufficient funds, the transaction has not been completed.\n" +
$"Upgrade: {prefab.Name}, Cost: {price}, Have: {Campaign.GetWallet(client).Balance}");
return false;
}
}
@@ -297,6 +303,8 @@ namespace Barotrauma
/// </summary>
public void PurchaseItemSwap(Item itemToRemove, ItemPrefab itemToInstall, bool isNetworkMessage = false, Client? client = null)
{
if (!HasPermissionToManageUpgrades(client)) { return; }
if (!CanUpgradeSub())
{
DebugConsole.ThrowError("Cannot swap items when switching to another submarine.");
@@ -398,8 +406,10 @@ namespace Barotrauma
/// <summary>
/// Cancels the currently pending item swap, or uninstalls the item if there's no swap pending
/// </summary>
public void CancelItemSwap(Item itemToRemove, bool force = false)
public void CancelItemSwap(Item itemToRemove, bool force = false, Client? client = null)
{
if (!HasPermissionToManageUpgrades(client)) { return; }
if (!CanUpgradeSub())
{
DebugConsole.ThrowError("Cannot swap items when switching to another submarine.");
@@ -757,6 +767,18 @@ namespace Barotrauma
Campaign.PendingSubmarineSwitch.Name == Submarine.MainSub.Info.Name;
}
public bool HasPermissionToManageUpgrades(Client? client = null)
{
if (!GameMain.IsMultiplayer) { return true; }
#if SERVER
if (client is null) { return false; }
return CampaignMode.AllowedToManageCampaign(client, ClientPermissions.ManageCampaign);
#elif CLIENT
return CampaignMode.AllowedToManageCampaign(ClientPermissions.ManageCampaign);
#endif
}
public void Save(XElement? parent)
{
if (parent == null) { return; }