Unstable 0.17.1.0

This commit is contained in:
Markus Isberg
2022-03-17 01:25:04 +09:00
parent 3974067915
commit 6d410cc1b7
302 changed files with 5878 additions and 3317 deletions
@@ -168,7 +168,7 @@ namespace Barotrauma
foreach (Item spawnedItem in spawnedItems)
{
#if SERVER
Entity.Spawner.CreateNetworkEvent(spawnedItem, remove: false);
Entity.Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(spawnedItem));
#endif
foreach (ItemComponent ic in spawnedItem.Components)
{
@@ -6,6 +6,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Networking;
#if SERVER
using Barotrauma.Networking;
#endif
@@ -18,12 +19,34 @@ namespace Barotrauma
public int Quantity { get; set; }
public bool? IsStoreComponentEnabled { get; set; }
public PurchasedItem(ItemPrefab itemPrefab, int quantity)
public readonly int BuyerCharacterInfoId;
public PurchasedItem(ItemPrefab itemPrefab, int quantity, int buyerCharacterInfoId)
{
ItemPrefab = itemPrefab;
Quantity = quantity;
IsStoreComponentEnabled = null;
BuyerCharacterInfoId = buyerCharacterInfoId;
}
#if CLIENT
public PurchasedItem(ItemPrefab itemPrefab, int quantity, Client buyer = null)
{
ItemPrefab = itemPrefab;
Quantity = quantity;
IsStoreComponentEnabled = null;
BuyerCharacterInfoId = buyer?.Character?.Info?.ID ?? Character.Controlled?.Info?.ID ?? 0;
}
#elif SERVER
public PurchasedItem(ItemPrefab itemPrefab, int quantity, Client buyer)
{
ItemPrefab = itemPrefab;
Quantity = quantity;
IsStoreComponentEnabled = null;
BuyerCharacterInfoId = buyer?.Character?.Info?.ID ?? 0;
}
#endif
}
class SoldItem
@@ -156,7 +179,7 @@ namespace Barotrauma
OnPurchasedItemsChanged?.Invoke();
}
public void ModifyItemQuantityInBuyCrate(ItemPrefab itemPrefab, int changeInQuantity)
public void ModifyItemQuantityInBuyCrate(ItemPrefab itemPrefab, int changeInQuantity, Client client = null)
{
var itemInCrate = ItemsInBuyCrate.Find(i => i.ItemPrefab == itemPrefab);
if (itemInCrate != null)
@@ -167,15 +190,15 @@ namespace Barotrauma
ItemsInBuyCrate.Remove(itemInCrate);
}
}
else if(changeInQuantity > 0)
else if (changeInQuantity > 0)
{
itemInCrate = new PurchasedItem(itemPrefab, changeInQuantity);
itemInCrate = new PurchasedItem(itemPrefab, changeInQuantity, client);
ItemsInBuyCrate.Add(itemInCrate);
}
OnItemsInBuyCrateChanged?.Invoke();
}
public void ModifyItemQuantityInSubSellCrate(ItemPrefab itemPrefab, int changeInQuantity)
public void ModifyItemQuantityInSubSellCrate(ItemPrefab itemPrefab, int changeInQuantity, Client client = null)
{
var itemInCrate = ItemsInSellFromSubCrate.Find(i => i.ItemPrefab == itemPrefab);
if (itemInCrate != null)
@@ -188,13 +211,13 @@ namespace Barotrauma
}
else if (changeInQuantity > 0)
{
itemInCrate = new PurchasedItem(itemPrefab, changeInQuantity);
itemInCrate = new PurchasedItem(itemPrefab, changeInQuantity, client);
ItemsInSellFromSubCrate.Add(itemInCrate);
}
OnItemsInSellFromSubCrateChanged?.Invoke();
}
public void PurchaseItems(List<PurchasedItem> itemsToPurchase, bool removeFromCrate)
public void PurchaseItems(List<PurchasedItem> itemsToPurchase, bool removeFromCrate, Client client = null)
{
// Check all the prices before starting the transaction
// to make sure the modifiers stay the same for the whole transaction
@@ -210,13 +233,13 @@ namespace Barotrauma
}
else
{
purchasedItem = new PurchasedItem(item.ItemPrefab, item.Quantity);
purchasedItem = new PurchasedItem(item.ItemPrefab, item.Quantity, client);
PurchasedItems.Add(purchasedItem);
}
// Exchange money
var itemValue = item.Quantity * buyValues[item.ItemPrefab];
campaign.Money -= itemValue;
campaign.GetWallet(client).TryDeduct(itemValue);
GameAnalyticsManager.AddMoneySpentEvent(itemValue, GameAnalyticsManager.MoneySink.Store, item.ItemPrefab.Identifier.Value);
Location.StoreCurrentBalance += itemValue;
@@ -427,7 +450,7 @@ namespace Barotrauma
#if SERVER
if (GameMain.Server != null)
{
Entity.Spawner.CreateNetworkEvent(itemContainer.Item, false);
Entity.Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(itemContainer.Item));
}
#endif
}
@@ -438,7 +461,7 @@ namespace Barotrauma
itemSpawned(item);
#if SERVER
Entity.Spawner?.CreateNetworkEvent(item, false);
Entity.Spawner?.CreateNetworkEvent(new EntitySpawner.SpawnEntity(item));
#endif
(itemContainer?.Item ?? item).CampaignInteractionType = CampaignMode.InteractionType.Cargo;
static void itemSpawned(Item item)
@@ -491,7 +514,8 @@ namespace Barotrauma
if (item?.ItemPrefab == null) { continue; }
itemsElement.Add(new XElement("item",
new XAttribute("id", item.ItemPrefab.Identifier),
new XAttribute("qty", item.Quantity)));
new XAttribute("qty", item.Quantity),
new XAttribute("buyer", item.BuyerCharacterInfoId)));
}
parentElement.Add(itemsElement);
}
@@ -503,12 +527,15 @@ namespace Barotrauma
{
foreach (XElement itemElement in element.GetChildElements("item"))
{
var id = itemElement.GetAttributeString("id", null);
string id = itemElement.GetAttributeString("id", null);
if (string.IsNullOrWhiteSpace(id)) { continue; }
var prefab = ItemPrefab.Prefabs.Find(p => p.Identifier == id);
if (prefab == null) { continue; }
var qty = itemElement.GetAttributeInt("qty", 0);
purchasedItems.Add(new PurchasedItem(prefab, qty));
int qty = itemElement.GetAttributeInt("qty", 0);
int buyerId = itemElement.GetAttributeInt("buyer", 0);
purchasedItems.Add(new PurchasedItem(prefab, qty, buyerId));
}
}
SetPurchasedItems(purchasedItems);
@@ -20,6 +20,16 @@ namespace Barotrauma
private readonly List<CharacterInfo> characterInfos = new List<CharacterInfo>();
private readonly List<Character> characters = new List<Character>();
public IEnumerable<Character> GetCharacters()
{
return characters;
}
public IEnumerable<CharacterInfo> GetCharacterInfos()
{
return characterInfos;
}
private Character welcomeMessageNPC;
public List<CharacterInfo> CharacterInfos => characterInfos;
@@ -1,8 +1,6 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using Microsoft.Xna.Framework;
using System;
namespace Barotrauma
{
@@ -27,6 +25,8 @@ namespace Barotrauma
public LocalizedString Description { get; }
public LocalizedString ShortDescription { get; }
public int MenuOrder { get; }
/// <summary>
/// How low the reputation can drop on this faction
/// </summary>
@@ -52,6 +52,7 @@ namespace Barotrauma
public FactionPrefab(ContentXElement element, FactionsFile file) : base(file, element.GetAttributeIdentifier("identifier", string.Empty))
{
MenuOrder = element.GetAttributeInt("menuorder", 0);
MinReputation = element.GetAttributeInt("minreputation", -100);
MaxReputation = element.GetAttributeInt("maxreputation", 100);
InitialReputation = element.GetAttributeInt("initialreputation", 0);
@@ -164,7 +164,7 @@ namespace Barotrauma
("[reputationvalue]", ((int)Math.Round(value)).ToString()));
if (addColorTags)
{
formattedReputation = $"‖color:{XMLExtensions.ColorToString(GetReputationColor(normalizedValue))}‖"+ formattedReputation+"‖end‖";
formattedReputation = $"‖color:{XMLExtensions.ToStringHex(GetReputationColor(normalizedValue))}‖{formattedReputation}‖end‖";
}
return formattedReputation;
}
@@ -0,0 +1,193 @@
using System;
using System.Xml.Linq;
using Barotrauma.Networking;
namespace Barotrauma
{
internal readonly struct WalletChangedEvent
{
public readonly Wallet Wallet;
public readonly WalletInfo Info;
public readonly WalletChangedData ChangedData;
public WalletChangedEvent(Wallet wallet, WalletChangedData changedData, WalletInfo info)
{
Wallet = wallet;
Info = info;
ChangedData = changedData;
}
}
[NetworkSerialize]
internal struct WalletInfo : INetSerializableStruct
{
public int RewardDistribution;
public int Balance;
}
internal struct NetWalletUpdate : INetSerializableStruct
{
[NetworkSerialize(ArrayMaxSize = NetConfig.MaxPlayers + 1)]
public NetWalletTransaction[] Transactions;
}
[NetworkSerialize]
internal struct NetWalletTransfer : INetSerializableStruct
{
public Option<ushort> Sender;
public Option<ushort> Receiver;
public int Amount;
}
internal struct NetWalletSalaryUpdate : INetSerializableStruct
{
[NetworkSerialize]
public ushort Target;
[NetworkSerialize(MinValueInt = 0, MaxValueInt = 100)]
public int NewRewardDistribution;
}
[NetworkSerialize]
internal struct WalletChangedData : INetSerializableStruct
{
public Option<int> RewardDistributionChanged;
public Option<int> BalanceChanged;
public WalletChangedData MergeInto(WalletChangedData other)
{
other.BalanceChanged = AddOptionalInt(other.BalanceChanged, BalanceChanged);
other.RewardDistributionChanged = AddOptionalInt(other.RewardDistributionChanged, RewardDistributionChanged);
return other;
static Option<int> AddOptionalInt(Option<int> a, Option<int> b)
{
return a switch
{
Some<int> some1 => b switch
{
Some<int> some2 => Option<int>.Some(some1.Value + some2.Value),
None<int> _ => Option<int>.Some(some1.Value),
_ => throw new ArgumentOutOfRangeException(nameof(b))
},
None<int> _ => b switch
{
Some<int> some1 => Option<int>.Some(some1.Value),
None<int> _ => Option<int>.None(),
_ => throw new ArgumentOutOfRangeException(nameof(b))
},
_ => throw new ArgumentOutOfRangeException(nameof(a))
};
}
}
}
[NetworkSerialize]
internal struct NetWalletTransaction : INetSerializableStruct
{
public Option<ushort> CharacterID;
public WalletChangedData ChangedData;
public WalletInfo Info;
}
// ReSharper disable ValueParameterNotUsed
internal sealed class InvalidWallet : Wallet
{
public override int Balance
{
get => 0;
set => new InvalidOperationException("Tried to set the balance on an invalid wallet");
}
public override int RewardDistribution
{
get => 0;
set => new InvalidOperationException("Tried to set the reward distribution on an invalid wallet");
}
}
internal partial class Wallet
{
public static readonly Wallet Invalid = new InvalidWallet();
public const string LowerCaseSaveElementName = "wallet";
private const string AttributeNameBalance = "balance",
AttrubuteNameRewardDistribution = "rewarddistribution",
SaveElementName = "Wallet";
private int balance;
public virtual int Balance
{
get => balance;
set => balance = ClampBalance(value);
}
private int rewardDistribution;
public virtual int RewardDistribution
{
get => rewardDistribution;
set => rewardDistribution = ClampRewardDistribution(value);
}
public Wallet() { }
public Wallet(XElement element)
{
balance = ClampBalance(element.GetAttributeInt(AttributeNameBalance, 0));
rewardDistribution = ClampBalance(element.GetAttributeInt(AttrubuteNameRewardDistribution, 0));
}
public XElement Save()
{
XElement element = new XElement(SaveElementName, new XAttribute(AttributeNameBalance, Balance), new XAttribute(AttrubuteNameRewardDistribution, RewardDistribution));
return element;
}
public bool TryDeduct(int price)
{
if (!CanAfford(price)) { return false; }
Deduct(price);
return true;
}
public bool CanAfford(int price) => Balance >= price;
public void Refund(int price) => Give(price);
public void Give(int amount)
{
Balance += amount;
SettingsChanged(balanceChanged: Option<int>.Some(amount), rewardChanged: Option<int>.None());
}
public void Deduct(int price)
{
Balance -= price;
SettingsChanged(balanceChanged: Option<int>.Some(-price), rewardChanged: Option<int>.None());
}
public void SetRewardDistrubiton(int value)
{
int oldValue = RewardDistribution;
RewardDistribution = value;
SettingsChanged(balanceChanged: Option<int>.None(), rewardChanged: Option<int>.Some(RewardDistribution - oldValue));
}
public WalletInfo CreateWalletInfo()
{
return new WalletInfo
{
Balance = Balance,
RewardDistribution = RewardDistribution
};
}
partial void SettingsChanged(Option<int> balanceChanged, Option<int> rewardChanged);
private static int ClampBalance(int value) => Math.Clamp(value, 0, CampaignMode.MaxMoney);
private static int ClampRewardDistribution(int value) => Math.Clamp(value, 0, 100);
}
}
@@ -68,7 +68,7 @@ namespace Barotrauma
abstract partial class CampaignMode : GameMode
{
const int MaxMoney = int.MaxValue / 2; //about 1 billion
public const int MaxMoney = int.MaxValue / 2; //about 1 billion
public const int InitialMoney = 8500;
//duration of the cinematic + credits at the end of the campaign
@@ -102,6 +102,8 @@ namespace Barotrauma
private readonly List<Mission> extraMissions = new List<Mission>();
public readonly NamedEvent<WalletChangedEvent> OnMoneyChanged = new NamedEvent<WalletChangedEvent>();
public enum TransitionType
{
None,
@@ -167,12 +169,7 @@ namespace Barotrauma
}
}
private int money;
public int Money
{
get { return money; }
set { money = MathHelper.Clamp(value, 0, MaxMoney); }
}
public Wallet Bank;
public LevelData NextLevel
{
@@ -183,11 +180,20 @@ namespace Barotrauma
protected CampaignMode(GameModePreset preset)
: base(preset)
{
Money = InitialMoney;
Bank = new Wallet
{
Balance = InitialMoney
};
CargoManager = new CargoManager(this);
MedicalClinic = new MedicalClinic(this);
}
public virtual Wallet GetWallet(Client client = null)
{
return Bank;
}
/// <summary>
/// The location that's displayed as the "current one" in the map screen. Normally the current outpost or the location at the start of the level,
/// but when selecting the next destination at the end of the level at an uninhabited location we use the location at the end
@@ -200,7 +206,7 @@ namespace Barotrauma
{
return Level.Loaded.EndLocation;
}
return Level.Loaded?.StartLocation ?? Map.CurrentLocation;
return Level.Loaded?.StartLocation ?? Map.CurrentLocation;
}
public List<Submarine> GetSubsToLeaveBehind(Submarine leavingSub)
@@ -255,8 +261,6 @@ namespace Barotrauma
PurchasedLostShuttles = false;
var connectedSubs = Submarine.MainSub.GetConnectedSubs();
wasDocked = Level.Loaded.StartOutpost != null && connectedSubs.Contains(Level.Loaded.StartOutpost);
ResetTalentData();
}
public void InitCampaignData()
@@ -702,21 +706,20 @@ namespace Barotrauma
string eventId = "FinishCampaign:";
GameAnalyticsManager.AddDesignEvent(eventId + "Submarine:" + (Submarine.MainSub?.Info?.Name ?? "none"));
GameAnalyticsManager.AddDesignEvent(eventId + "CrewSize:" + (CrewManager?.CharacterInfos?.Count() ?? 0));
GameAnalyticsManager.AddDesignEvent(eventId + "Money", Money);
GameAnalyticsManager.AddDesignEvent(eventId + "Money", Bank.Balance);
GameAnalyticsManager.AddDesignEvent(eventId + "Playtime", TotalPlayTime);
GameAnalyticsManager.AddDesignEvent(eventId + "PassedLevels", TotalPassedLevels);
}
protected virtual void EndCampaignProjSpecific() { }
public bool TryHireCharacter(Location location, CharacterInfo characterInfo)
public bool TryHireCharacter(Location location, CharacterInfo characterInfo, Client client = null)
{
if (characterInfo == null) { return false; }
if (Money < characterInfo.Salary) { return false; }
if (!GetWallet(client).TryDeduct(characterInfo.Salary)) { return false; }
characterInfo.IsNewHire = true;
location.RemoveHireableCharacter(characterInfo);
CrewManager.AddCharacterInfo(characterInfo);
Money -= characterInfo.Salary;
GameAnalyticsManager.AddMoneySpentEvent(characterInfo.Salary, GameAnalyticsManager.MoneySink.Crew, characterInfo.Job?.Prefab.Identifier.Value ?? "unknown");
return true;
}
@@ -740,8 +743,7 @@ namespace Barotrauma
HumanAIController humanAI = npc.AIController as HumanAIController;
if (humanAI == null) { yield return CoroutineStatus.Success; }
var waitOrderPrefab = OrderPrefab.Prefabs["wait"];
var waitOrder = new Order(waitOrderPrefab, Identifier.Empty, null, orderGiver: null);
var waitOrder = OrderPrefab.Prefabs["wait"].CreateInstance(OrderPrefab.OrderTargetType.Entity);
humanAI.SetForcedOrder(waitOrder);
var waitObjective = humanAI.ObjectiveManager.ForcedOrder;
humanAI.FaceTarget(interactor);
@@ -856,7 +858,7 @@ namespace Barotrauma
{
GameMain.Server.SendDirectChatMessage(Networking.ChatMessage.Create(
TextManager.Get("RadioAnnouncerName").Value,
TextManager.Get("RadioAnnouncerName").Value,
TextManager.Get("TooFarFromOutpostWarning").Value, Networking.ChatMessageType.Default, null), c);
}
#endif
@@ -906,7 +908,7 @@ namespace Barotrauma
public void LogState()
{
DebugConsole.NewMessage("********* CAMPAIGN STATUS *********", Color.White);
DebugConsole.NewMessage(" Money: " + Money, Color.White);
DebugConsole.NewMessage(" Money: " + Bank.Balance, Color.White);
DebugConsole.NewMessage(" Current location: " + map.CurrentLocation.Name, Color.White);
DebugConsole.NewMessage(" Available destinations: ", Color.White);
@@ -960,13 +962,5 @@ namespace Barotrauma
}
}
// Talent relevant data, only stored for the duration of the mission
private void ResetTalentData()
{
CrewHasDied = false;
}
public bool CrewHasDied { get; set; }
}
}
@@ -26,33 +26,6 @@ namespace Barotrauma
private XElement itemData;
private XElement healthData;
public XElement OrderData { get; private set; }
public void Refresh(Character character)
{
healthData = new XElement("health");
character.CharacterHealth.Save(healthData);
if (character.Inventory != null)
{
itemData = new XElement("inventory");
Character.SaveInventory(character.Inventory, itemData);
}
OrderData = new XElement("orders");
CharacterInfo.SaveOrderData(character.Info, OrderData);
}
public XElement Save()
{
XElement element = new XElement("CharacterCampaignData",
new XAttribute("name", Name),
new XAttribute("endpoint", ClientEndPoint),
new XAttribute("steamid", SteamID));
CharacterInfo?.Save(element);
if (itemData != null) { element.Add(itemData); }
if (healthData != null) { element.Add(healthData); }
if (OrderData != null) { element.Add(OrderData); }
return element;
}
public XElement WalletData;
}
}
@@ -99,7 +99,6 @@ namespace Barotrauma
/// </summary>
private void Load(XElement element)
{
Money = element.GetAttributeInt("money", 0);
PurchasedLostShuttles = element.GetAttributeBool("purchasedlostshuttles", false);
PurchasedHullRepairs = element.GetAttributeBool("purchasedhullrepairs", false);
PurchasedItemRepairs = element.GetAttributeBool("purchaseditemrepairs", false);
@@ -166,6 +165,9 @@ namespace Barotrauma
case "stats":
LoadStats(subElement);
break;
case Wallet.LowerCaseSaveElementName:
Bank = new Wallet(subElement);
break;
#if SERVER
case "savedexperiencepoints":
foreach (XElement savedExp in subElement.Elements())
@@ -177,6 +179,15 @@ namespace Barotrauma
}
}
int oldMoney = element.GetAttributeInt("money", 0);
if (oldMoney > 0)
{
Bank = new Wallet
{
Balance = oldMoney
};
}
CampaignMetadata ??= new CampaignMetadata(this);
UpgradeManager ??= new UpgradeManager(this);
@@ -8,6 +8,7 @@ using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Networking;
namespace Barotrauma
{
@@ -30,10 +31,14 @@ namespace Barotrauma
private readonly List<Mission> missions = new List<Mission>();
public IEnumerable<Mission> Missions { get { return missions; } }
private readonly HashSet<Character> casualties = new HashSet<Character>();
public IEnumerable<Character> Casualties { get { return casualties; } }
public CharacterTeamType? WinningTeam;
public bool IsRunning { get; private set; }
public bool RoundEnding { get; private set; }
public Level? Level { get; private set; }
@@ -201,7 +206,8 @@ namespace Barotrauma
var campaign = MultiPlayerCampaign.StartNew(seed ?? ToolBox.RandomSeed(8), selectedSub, settings);
if (selectedSub != null)
{
campaign.Money = Math.Max(MultiPlayerCampaign.MinimumInitialMoney, campaign.Money - selectedSub.Price);
campaign.Bank.TryDeduct(selectedSub.Price);
campaign.Bank.Balance = Math.Max(campaign.Bank.Balance, MultiPlayerCampaign.MinimumInitialMoney);
}
return campaign;
}
@@ -211,7 +217,8 @@ namespace Barotrauma
var campaign = SinglePlayerCampaign.StartNew(seed ?? ToolBox.RandomSeed(8), selectedSub, settings);
if (selectedSub != null)
{
campaign.Money = Math.Max(SinglePlayerCampaign.MinimumInitialMoney, campaign.Money - selectedSub.Price);
campaign.Bank.TryDeduct(selectedSub.Price);
campaign.Bank.Balance = Math.Max(campaign.Bank.Balance, MultiPlayerCampaign.MinimumInitialMoney);
}
return campaign;
}
@@ -264,7 +271,7 @@ namespace Barotrauma
/// <summary>
/// Switch to another submarine. The sub is loaded when the next round starts.
/// </summary>
public SubmarineInfo SwitchSubmarine(SubmarineInfo newSubmarine, int cost)
public SubmarineInfo SwitchSubmarine(SubmarineInfo newSubmarine, int cost, Client? client = null)
{
if (!OwnedSubmarines.Any(s => s.Name == newSubmarine.Name))
{
@@ -283,19 +290,21 @@ namespace Barotrauma
}
}
Campaign!.Money -= cost;
if ((GameMain.NetworkMember is null || GameMain.NetworkMember is { IsServer: true }) && cost > 0)
{
Campaign!.GetWallet(client).TryDeduct(cost);
}
GameAnalyticsManager.AddMoneySpentEvent(cost, GameAnalyticsManager.MoneySink.SubmarineSwitch, newSubmarine.Name);
return newSubmarine;
}
public void PurchaseSubmarine(SubmarineInfo newSubmarine)
public void PurchaseSubmarine(SubmarineInfo newSubmarine, Client? client = null)
{
if (Campaign is null) { return; }
if (Campaign.Money < newSubmarine.Price) { return; }
if ((GameMain.NetworkMember is null || GameMain.NetworkMember is { IsServer: true }) && !Campaign.GetWallet(client).TryDeduct(newSubmarine.Price)) { return; }
if (!OwnedSubmarines.Any(s => s.Name == newSubmarine.Name))
{
Campaign.Money -= newSubmarine.Price;
GameAnalyticsManager.AddMoneySpentEvent(newSubmarine.Price, GameAnalyticsManager.MoneySink.SubmarinePurchase, newSubmarine.Name);
OwnedSubmarines.Add(newSubmarine);
}
@@ -346,7 +355,7 @@ namespace Barotrauma
public void StartRound(LevelData? levelData, bool mirrorLevel = false, SubmarineInfo? startOutpost = null, SubmarineInfo? endOutpost = null)
{
AfflictionPrefab.LoadAllEffects();
MirrorLevel = mirrorLevel;
if (SubmarineInfo == null)
{
@@ -411,9 +420,6 @@ namespace Barotrauma
}
}
//Clear out the stored grids
Powered.Grids.Clear();
Level? level = null;
if (levelData != null)
{
@@ -422,6 +428,11 @@ namespace Barotrauma
InitializeLevel(level);
//Clear out the cached grids and force update
Powered.Grids.Clear();
casualties.Clear();
GameAnalyticsManager.AddProgressionEvent(
GameAnalyticsManager.ProgressionStatus.Start,
GameMode?.Preset?.Identifier.Value ?? "none");
@@ -480,7 +491,7 @@ namespace Barotrauma
existingRoundSummary.ContinueButton.Visible = true;
}
RoundSummary = new RoundSummary(Submarine.Info, GameMode, Missions, StartLocation, EndLocation);
RoundSummary = new RoundSummary(GameMode, Missions, StartLocation, EndLocation);
if (!(GameMode is TutorialMode) && !(GameMode is TestGameMode))
{
@@ -723,7 +734,7 @@ namespace Barotrauma
}
partial void UpdateProjSpecific(float deltaTime);
public static IEnumerable<Character> GetSessionCrewCharacters()
{
#if SERVER
@@ -745,7 +756,7 @@ namespace Barotrauma
{
IEnumerable<Character> crewCharacters = GetSessionCrewCharacters();
int prevMoney = (GameMode as CampaignMode)?.Money ?? 0;
int prevMoney = (GameMode as CampaignMode)?.Bank.Balance ?? 0; // FIXME personal wallets - reward distribution
foreach (Mission mission in missions)
{
@@ -759,14 +770,13 @@ namespace Barotrauma
if (missions.Any())
{
if (missions.Any())
if (missions.Any(m => m.Completed))
{
foreach (Character character in crewCharacters)
{
character.CheckTalents(AbilityEffectType.OnAnyMissionCompleted);
}
}
if (missions.All(m => m.Completed))
{
foreach (Character character in crewCharacters)
@@ -818,7 +828,7 @@ namespace Barotrauma
LogEndRoundStats(eventId);
if (GameMode is CampaignMode campaignMode)
{
GameAnalyticsManager.AddDesignEvent(eventId + "MoneyEarned", campaignMode.Money - prevMoney);
GameAnalyticsManager.AddDesignEvent(eventId + "MoneyEarned", campaignMode.Bank.Balance - prevMoney); // FIXME personal wallets - reward distrubiton
campaignMode.TotalPlayTime += roundDuration;
}
#if CLIENT
@@ -910,6 +920,10 @@ namespace Barotrauma
public void KillCharacter(Character character)
{
if (CrewManager != null && CrewManager.GetCharacters().Contains(character))
{
casualties.Add(character);
}
#if CLIENT
CrewManager?.KillCharacter(character);
#endif
@@ -917,6 +931,7 @@ namespace Barotrauma
public void ReviveCharacter(Character character)
{
casualties.Remove(character);
#if CLIENT
CrewManager?.ReviveCharacter(character);
#endif
@@ -939,7 +954,7 @@ namespace Barotrauma
List<string> excessPackages = new List<string>();
foreach (ContentPackage cp in ContentPackageManager.EnabledPackages.All)
{
//if (!cp.HasMultiplayerIncompatibleContent) { continue; }
if (!cp.HasMultiplayerSyncedContent) { continue; }
if (!contentPackagePaths.Any(p => p == cp.Path))
{
excessPackages.Add(cp.Name);
@@ -949,7 +964,7 @@ namespace Barotrauma
bool orderMismatch = false;
if (missingPackages.Count == 0 && missingPackages.Count == 0)
{
var enabledPackages = ContentPackageManager.EnabledPackages.All/*.Where(cp => cp.HasMultiplayerIncompatibleContent)*/.ToImmutableArray();
var enabledPackages = ContentPackageManager.EnabledPackages.All.Where(cp => cp.HasMultiplayerSyncedContent).ToImmutableArray();
for (int i = 0; i < contentPackagePaths.Count && i < enabledPackages.Length; i++)
{
if (contentPackagePaths[i] != enabledPackages[i].Path)
@@ -1015,7 +1030,7 @@ namespace Barotrauma
}
if (Map != null) { rootElement.Add(new XAttribute("mapseed", Map.Seed)); }
rootElement.Add(new XAttribute("selectedcontentpackages",
string.Join("|", ContentPackageManager.EnabledPackages.All.Where(cp => cp.HasMultiplayerIncompatibleContent).Select(cp => cp.Path))));
string.Join("|", ContentPackageManager.EnabledPackages.All.Where(cp => cp.HasMultiplayerSyncedContent).Select(cp => cp.Path))));
((CampaignMode)GameMode).Save(doc.Root);
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Barotrauma.Extensions;
using Barotrauma.Networking;
namespace Barotrauma
{
@@ -177,17 +178,20 @@ namespace Barotrauma
}
}
public readonly List<NetCrewMember> PendingHeals = new List<NetCrewMember>();
public Action? OnUpdate;
private readonly CampaignMode? campaign;
public MedicalClinic(CampaignMode campaign)
{
this.campaign = campaign;
#if CLIENT
campaign.OnMoneyChanged.RegisterOverwriteExisting(nameof(MedicalClinic).ToIdentifier(), OnMoneyChanged);
#endif
}
public readonly List<NetCrewMember> PendingHeals = new List<NetCrewMember>();
public Action? OnUpdate;
private static bool IsOutpostInCombat()
{
if (!(Level.Loaded is { Type: LevelData.LevelType.Outpost })) { return false; }
@@ -203,14 +207,13 @@ namespace Barotrauma
return false;
}
private HealRequestResult HealAllPending(bool force = false)
private HealRequestResult HealAllPending(bool force = false, Client? client = null)
{
int totalCost = GetTotalCost();
if (!force)
{
if (GetMoney() < totalCost) { return HealRequestResult.InsufficientFunds; }
if (IsOutpostInCombat()) { return HealRequestResult.Refused; }
if (!GetWallet(client).TryDeduct(totalCost)) { return HealRequestResult.InsufficientFunds; }
}
ImmutableArray<CharacterInfo> crew = GetCrewCharacters();
@@ -225,11 +228,6 @@ namespace Barotrauma
}
}
if (campaign != null)
{
campaign.Money -= totalCost;
}
ClearPendingHeals();
return HealRequestResult.Success;
@@ -316,7 +314,10 @@ namespace Barotrauma
private int GetAdjustedPrice(int price) => campaign?.Map?.CurrentLocation is { Type: { HasOutpost: true } } currentLocation ? currentLocation.GetAdjustedHealCost(price) : int.MaxValue;
public int GetMoney() => campaign?.Money ?? 0;
public Wallet GetWallet(Client? c = null)
{
return campaign?.GetWallet(c) ?? Wallet.Invalid;
}
public static ImmutableArray<CharacterInfo> GetCrewCharacters()
{
@@ -3,6 +3,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Networking;
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
@@ -178,7 +179,7 @@ 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)
public void PurchaseUpgrade(UpgradePrefab prefab, UpgradeCategory category, bool force = false, Client? client = null)
{
if (!CanUpgradeSub())
{
@@ -215,7 +216,7 @@ namespace Barotrauma
price = 0;
}
if (Campaign.Money >= price)
if (Campaign.GetWallet(client).TryDeduct(price)) // FIXME personal wallets
{
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
{
@@ -227,7 +228,6 @@ namespace Barotrauma
}
}
Campaign.Money -= price;
GameAnalyticsManager.AddMoneySpentEvent(price, GameAnalyticsManager.MoneySink.SubmarineUpgrade, prefab.Identifier.Value);
PurchasedUpgrade? upgrade = FindMatchingUpgrade(prefab, category);
@@ -253,14 +253,14 @@ namespace Barotrauma
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.Money}");
$"Upgrade: {prefab.Name}, Cost: {price}, Have: {Campaign.GetWallet(client).Balance}");
}
}
/// <summary>
/// Purchases an item swap and handles logic for deducting the credit.
/// </summary>
public void PurchaseItemSwap(Item itemToRemove, ItemPrefab itemToInstall, bool force = false)
public void PurchaseItemSwap(Item itemToRemove, ItemPrefab itemToInstall, bool force = false, Client? client = null)
{
if (!CanUpgradeSub())
{
@@ -313,7 +313,7 @@ namespace Barotrauma
price = 0;
}
if (Campaign.Money >= price)
if (Campaign.GetWallet(client).TryDeduct(price))
{
PurchasedItemSwaps.RemoveAll(p => linkedItems.Contains(p.ItemToRemove));
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
@@ -326,7 +326,6 @@ namespace Barotrauma
}
}
Campaign.Money -= price;
GameAnalyticsManager.AddMoneySpentEvent(price, GameAnalyticsManager.MoneySink.SubmarineWeapon, itemToInstall.Identifier.Value);
foreach (Item itemToSwap in linkedItems)
@@ -355,7 +354,7 @@ namespace Barotrauma
else
{
DebugConsole.ThrowError("Tried to swap an item with insufficient funds, the transaction has not been completed.\n" +
$"Item to remove: {itemToRemove.Name}, Item to install: {itemToInstall.Name}, Cost: {price}, Have: {Campaign.Money}");
$"Item to remove: {itemToRemove.Name}, Item to install: {itemToInstall.Name}, Cost: {price}, Have: {Campaign.GetWallet(client).Balance}");
}
}