v0.14.6.0
This commit is contained in:
@@ -207,7 +207,8 @@ namespace Barotrauma
|
||||
SpawnedInOutpost = validContainer.Key.Item.SpawnedInOutpost,
|
||||
AllowStealing = validContainer.Key.Item.AllowStealing,
|
||||
OriginalModuleIndex = validContainer.Key.Item.OriginalModuleIndex,
|
||||
OriginalContainerID = validContainer.Key.Item.ID
|
||||
OriginalContainerIndex =
|
||||
Item.ItemList.Where(it => it.Submarine == validContainer.Key.Item.Submarine && it.OriginalModuleIndex == validContainer.Key.Item.OriginalModuleIndex).ToList().IndexOf(validContainer.Key.Item)
|
||||
};
|
||||
foreach (WifiComponent wifiComponent in item.GetComponents<WifiComponent>())
|
||||
{
|
||||
|
||||
@@ -46,6 +46,7 @@ namespace Barotrauma
|
||||
|
||||
public List<PurchasedItem> ItemsInBuyCrate { get; } = new List<PurchasedItem>();
|
||||
public List<PurchasedItem> ItemsInSellCrate { get; } = new List<PurchasedItem>();
|
||||
public List<PurchasedItem> ItemsInSellFromSubCrate { get; } = new List<PurchasedItem>();
|
||||
public List<PurchasedItem> PurchasedItems { get; } = new List<PurchasedItem>();
|
||||
public List<SoldItem> SoldItems { get; } = new List<SoldItem>();
|
||||
|
||||
@@ -55,6 +56,7 @@ namespace Barotrauma
|
||||
|
||||
public Action OnItemsInBuyCrateChanged;
|
||||
public Action OnItemsInSellCrateChanged;
|
||||
public Action OnItemsInSellFromSubCrateChanged;
|
||||
public Action OnPurchasedItemsChanged;
|
||||
public Action OnSoldItemsChanged;
|
||||
|
||||
@@ -75,6 +77,12 @@ namespace Barotrauma
|
||||
OnItemsInSellCrateChanged?.Invoke();
|
||||
}
|
||||
|
||||
public void ClearItemsInSellFromSubCrate()
|
||||
{
|
||||
ItemsInSellFromSubCrate.Clear();
|
||||
OnItemsInSellFromSubCrateChanged?.Invoke();
|
||||
}
|
||||
|
||||
public void SetPurchasedItems(List<PurchasedItem> items)
|
||||
{
|
||||
PurchasedItems.Clear();
|
||||
@@ -246,42 +254,21 @@ namespace Barotrauma
|
||||
continue;
|
||||
}
|
||||
availableContainers.Add(itemContainer);
|
||||
#if SERVER
|
||||
#if SERVER
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
Entity.Spawner.CreateNetworkEvent(itemContainer.Item, false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (itemContainer == null)
|
||||
{
|
||||
//no container, place at the waypoint
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
Entity.Spawner.AddToSpawnQueue(pi.ItemPrefab, position, wp.Submarine, onSpawned: itemSpawned);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
var item = new Item(pi.ItemPrefab, position, wp.Submarine);
|
||||
itemSpawned(item);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
//place in the container
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
Entity.Spawner.AddToSpawnQueue(pi.ItemPrefab, itemContainer.Inventory, onSpawned: itemSpawned);
|
||||
}
|
||||
else
|
||||
{
|
||||
var item = new Item(pi.ItemPrefab, position, wp.Submarine);
|
||||
itemContainer.Inventory.TryPutItem(item, null);
|
||||
itemSpawned(item);
|
||||
}
|
||||
|
||||
var item = new Item(pi.ItemPrefab, position, wp.Submarine);
|
||||
itemContainer?.Inventory.TryPutItem(item, null);
|
||||
itemSpawned(item);
|
||||
#if SERVER
|
||||
Entity.Spawner?.CreateNetworkEvent(item, false);
|
||||
#endif
|
||||
static void itemSpawned(Item item)
|
||||
{
|
||||
Submarine sub = item.Submarine ?? item.GetRootContainer()?.Submarine;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -30,6 +31,8 @@ namespace Barotrauma
|
||||
|
||||
public ReadyCheck ActiveReadyCheck;
|
||||
|
||||
public XElement ActiveOrdersElement { get; set; }
|
||||
|
||||
public CrewManager(bool isSinglePlayer)
|
||||
{
|
||||
IsSinglePlayer = isSinglePlayer;
|
||||
@@ -111,6 +114,9 @@ namespace Barotrauma
|
||||
case "health":
|
||||
characterInfo.HealthData = subElement;
|
||||
break;
|
||||
case "orders":
|
||||
characterInfo.OrderData = subElement;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,7 +195,7 @@ namespace Barotrauma
|
||||
spawnWaypoints = WayPoint.WayPointList.FindAll(wp =>
|
||||
wp.SpawnType == SpawnType.Human &&
|
||||
wp.Submarine == Level.Loaded.StartOutpost &&
|
||||
wp.CurrentHull?.OutpostModuleTags != null &&
|
||||
wp.CurrentHull != null &&
|
||||
wp.CurrentHull.OutpostModuleTags.Contains("airlock"));
|
||||
while (spawnWaypoints.Count > characterInfos.Count)
|
||||
{
|
||||
@@ -229,10 +235,14 @@ namespace Barotrauma
|
||||
}
|
||||
if (character.Info.HealthData != null)
|
||||
{
|
||||
character.Info.ApplyHealthData(character, character.Info.HealthData);
|
||||
CharacterInfo.ApplyHealthData(character, character.Info.HealthData);
|
||||
}
|
||||
character.GiveIdCardTags(spawnWaypoints[i]);
|
||||
character.Info.StartItemsGiven = true;
|
||||
if (character.Info.OrderData != null)
|
||||
{
|
||||
character.Info.ApplyOrderData();
|
||||
}
|
||||
}
|
||||
|
||||
AddCharacter(character);
|
||||
@@ -265,6 +275,14 @@ namespace Barotrauma
|
||||
RemoveCharacterInfo(characterInfo);
|
||||
}
|
||||
|
||||
public void ClearCurrentOrders()
|
||||
{
|
||||
foreach (var characterInfo in characterInfos)
|
||||
{
|
||||
characterInfo?.ClearCurrentOrders();
|
||||
}
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
foreach (Pair<Order, float?> order in ActiveOrders)
|
||||
@@ -392,6 +410,88 @@ namespace Barotrauma
|
||||
|
||||
#endregion
|
||||
|
||||
public static Character GetCharacterForQuickAssignment(Order order, Character controlledCharacter, IEnumerable<Character> characters, bool includeSelf = false)
|
||||
{
|
||||
bool isControlledCharacterNull = controlledCharacter == null;
|
||||
#if !DEBUG
|
||||
if (isControlledCharacterNull) { return null; }
|
||||
#endif
|
||||
if (order.Category == OrderCategory.Operate && HumanAIController.IsItemTargetedBySomeone(order.TargetItemComponent, controlledCharacter != null ? controlledCharacter.TeamID : CharacterTeamType.Team1, out Character operatingCharacter) &&
|
||||
(isControlledCharacterNull || operatingCharacter.CanHearCharacter(controlledCharacter)))
|
||||
{
|
||||
return operatingCharacter;
|
||||
}
|
||||
return GetCharactersSortedForOrder(order, characters, controlledCharacter, includeSelf).FirstOrDefault(c => isControlledCharacterNull || c.CanHearCharacter(controlledCharacter)) ?? controlledCharacter;
|
||||
}
|
||||
|
||||
public static IEnumerable<Character> GetCharactersSortedForOrder(Order order, IEnumerable<Character> characters, Character controlledCharacter, bool includeSelf, IEnumerable<Character> extraCharacters = null)
|
||||
{
|
||||
var filteredCharacters = characters.Where(c => controlledCharacter == null || ((includeSelf || c != controlledCharacter) && c.TeamID == controlledCharacter.TeamID));
|
||||
if (extraCharacters != null)
|
||||
{
|
||||
filteredCharacters = filteredCharacters.Union(extraCharacters);
|
||||
}
|
||||
return filteredCharacters
|
||||
// 1. Prioritize those who are on the same submarine than the controlled character
|
||||
.OrderByDescending(c => Character.Controlled == null || c.Submarine == Character.Controlled.Submarine)
|
||||
// 2. Prioritize those who have been given the same maintenance or operate order as now issued
|
||||
.ThenByDescending(c => c.CurrentOrders.Any(o =>
|
||||
o.Order != null && o.Order.Identifier == order.Identifier &&
|
||||
(order.Category == OrderCategory.Maintenance || order.Category == OrderCategory.Operate)))
|
||||
// 3. Prioritize those with the appropriate job for the order
|
||||
.ThenByDescending(c => order.HasAppropriateJob(c))
|
||||
// 4. Prioritize bots over player controlled characters
|
||||
.ThenByDescending(c => c.IsBot)
|
||||
// 5. Use the priority value of the current objective
|
||||
.ThenBy(c => c.AIController is HumanAIController humanAI ? humanAI.ObjectiveManager.CurrentObjective?.Priority : 0)
|
||||
// 6. Prioritize those with the best skill for the order
|
||||
.ThenByDescending(c => c.GetSkillLevel(order.AppropriateSkill));
|
||||
}
|
||||
|
||||
partial void UpdateProjectSpecific(float deltaTime);
|
||||
|
||||
private void SaveActiveOrders(XElement parentElement)
|
||||
{
|
||||
ActiveOrdersElement = new XElement("activeorders");
|
||||
// Only save orders with no fade out time (e.g. ignore orders)
|
||||
var ordersToSave = new List<OrderInfo>();
|
||||
foreach (var activeOrder in ActiveOrders)
|
||||
{
|
||||
var order = activeOrder?.First;
|
||||
if (order == null || activeOrder.Second.HasValue) { continue; }
|
||||
ordersToSave.Add(new OrderInfo(order, null, CharacterInfo.HighestManualOrderPriority));
|
||||
}
|
||||
CharacterInfo.SaveOrders(ActiveOrdersElement, ordersToSave.ToArray());
|
||||
parentElement?.Add(ActiveOrdersElement);
|
||||
}
|
||||
|
||||
public void LoadActiveOrders()
|
||||
{
|
||||
if (ActiveOrdersElement == null) { return; }
|
||||
foreach (var orderInfo in CharacterInfo.LoadOrders(ActiveOrdersElement))
|
||||
{
|
||||
IIgnorable ignoreTarget = null;
|
||||
if (orderInfo.Order.IsIgnoreOrder)
|
||||
{
|
||||
switch (orderInfo.Order.TargetType)
|
||||
{
|
||||
case Order.OrderTargetType.Entity:
|
||||
ignoreTarget = orderInfo.Order.TargetEntity as IIgnorable;
|
||||
break;
|
||||
case Order.OrderTargetType.WallSection when orderInfo.Order.TargetEntity is Structure s && orderInfo.Order.WallSectionIndex.HasValue:
|
||||
ignoreTarget = s.GetSection(orderInfo.Order.WallSectionIndex.Value) as IIgnorable;
|
||||
break;
|
||||
default:
|
||||
DebugConsole.ThrowError("Error loading an ignore order - can't find a proper ignore target");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (ignoreTarget != null)
|
||||
{
|
||||
ignoreTarget.OrderedToBeIgnored = true;
|
||||
}
|
||||
AddOrder(orderInfo.Order, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,25 +17,33 @@ namespace Barotrauma
|
||||
// Anything that uses this field I wasn't sure if actually needed the proper campaign settings to be passed down
|
||||
public static CampaignSettings Unsure = Empty;
|
||||
public bool RadiationEnabled { get; set; }
|
||||
public int MaxMissionCount { get; set; }
|
||||
|
||||
public const int DefaultMaxMissionCount = 2;
|
||||
public const int MaxMissionCountLimit = 10;
|
||||
public const int MinMissionCountLimit = 1;
|
||||
|
||||
public CampaignSettings(IReadMessage inc)
|
||||
{
|
||||
RadiationEnabled = inc.ReadBoolean();
|
||||
MaxMissionCount = inc.ReadInt32();
|
||||
}
|
||||
|
||||
public CampaignSettings(XElement element)
|
||||
{
|
||||
RadiationEnabled = element.GetAttributeBool(nameof(RadiationEnabled).ToLower(), true);
|
||||
MaxMissionCount = element.GetAttributeInt(nameof(MaxMissionCount).ToLower(), DefaultMaxMissionCount);
|
||||
}
|
||||
|
||||
public void Serialize(IWriteMessage msg)
|
||||
{
|
||||
msg.Write(RadiationEnabled);
|
||||
msg.Write(MaxMissionCount);
|
||||
}
|
||||
|
||||
public XElement Save()
|
||||
{
|
||||
return new XElement(nameof(CampaignSettings), new XAttribute(nameof(RadiationEnabled).ToLower(), RadiationEnabled));
|
||||
return new XElement(nameof(CampaignSettings), new XAttribute(nameof(RadiationEnabled).ToLower(), RadiationEnabled), new XAttribute(nameof(MaxMissionCount).ToLower().ToLower(), MaxMissionCount));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,9 +57,9 @@ namespace Barotrauma
|
||||
//duration of the camera transition at the end of a round
|
||||
protected const float EndTransitionDuration = 5.0f;
|
||||
//there can be no events before this time has passed during the 1st campaign round
|
||||
const float FirstRoundEventDelay = 30.0f;
|
||||
const float FirstRoundEventDelay = 0.0f;
|
||||
|
||||
public enum InteractionType { None, Talk, Map, Crew, Store, Repair, Upgrade, PurchaseSub }
|
||||
public enum InteractionType { None, Talk, Examine, Map, Crew, Store, Repair, Upgrade, PurchaseSub }
|
||||
|
||||
public readonly CargoManager CargoManager;
|
||||
public UpgradeManager UpgradeManager;
|
||||
@@ -113,12 +121,15 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Map.CurrentLocation?.SelectedMission != null)
|
||||
if (Map.CurrentLocation != null)
|
||||
{
|
||||
if (Map.CurrentLocation.SelectedMission.Locations[0] == Map.CurrentLocation.SelectedMission.Locations[1] ||
|
||||
Map.CurrentLocation.SelectedMission.Locations.Contains(Map.SelectedLocation))
|
||||
foreach (Mission mission in map.CurrentLocation.SelectedMissions)
|
||||
{
|
||||
yield return Map.CurrentLocation.SelectedMission;
|
||||
if (mission.Locations[0] == mission.Locations[1] ||
|
||||
mission.Locations.Contains(Map.SelectedLocation))
|
||||
{
|
||||
yield return mission;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (Mission mission in extraMissions)
|
||||
@@ -169,7 +180,7 @@ namespace Barotrauma
|
||||
return Submarine.Loaded.FindAll(sub =>
|
||||
sub != leavingSub &&
|
||||
!leavingSub.DockedTo.Contains(sub) &&
|
||||
sub.Info.Type == SubmarineType.Player &&
|
||||
sub.Info.Type == SubmarineType.Player && sub.TeamID == CharacterTeamType.Team1 && // pirate subs are currently tagged as player subs as well
|
||||
sub != GameMain.NetworkMember?.RespawnManager?.RespawnShuttle &&
|
||||
(sub.AtEndExit != leavingSub.AtEndExit || sub.AtStartExit != leavingSub.AtStartExit));
|
||||
}
|
||||
@@ -240,23 +251,24 @@ namespace Barotrauma
|
||||
if (levelData.Type == LevelData.LevelType.Outpost)
|
||||
{
|
||||
//if there's an available mission that takes place in the outpost, select it
|
||||
var availableMissionsInLocation = currentLocation.AvailableMissions.Where(m => m.Locations[0] == currentLocation && m.Locations[1] == currentLocation);
|
||||
if (availableMissionsInLocation.Any())
|
||||
foreach (var availableMission in currentLocation.AvailableMissions)
|
||||
{
|
||||
currentLocation.SelectedMission = availableMissionsInLocation.FirstOrDefault();
|
||||
}
|
||||
else
|
||||
{
|
||||
currentLocation.SelectedMission = null;
|
||||
if (availableMission.Locations[0] == currentLocation && availableMission.Locations[1] == currentLocation)
|
||||
{
|
||||
currentLocation.SelectMission(availableMission);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//if we had selected a mission that takes place in the outpost, deselect it when leaving the outpost
|
||||
if (currentLocation.SelectedMission?.Locations[0] == currentLocation &&
|
||||
currentLocation.SelectedMission?.Locations[1] == currentLocation)
|
||||
foreach (Mission mission in currentLocation.SelectedMissions.ToList())
|
||||
{
|
||||
currentLocation.SelectedMission = null;
|
||||
//if we had selected a mission that takes place in the outpost, deselect it when leaving the outpost
|
||||
if (mission.Locations[0] == currentLocation &&
|
||||
mission.Locations[1] == currentLocation)
|
||||
{
|
||||
currentLocation.DeselectMission(mission);
|
||||
}
|
||||
}
|
||||
|
||||
if (levelData.HasBeaconStation && !levelData.IsBeaconActive)
|
||||
@@ -268,7 +280,7 @@ namespace Barotrauma
|
||||
var beaconMissionPrefab = ToolBox.SelectWeightedRandom(beaconMissionPrefabs, beaconMissionPrefabs.Select(p => (float)p.Commonness).ToList(), rand);
|
||||
if (!Missions.Any(m => m.Prefab.Type == beaconMissionPrefab.Type))
|
||||
{
|
||||
extraMissions.Add(beaconMissionPrefab.Instantiate(Map.SelectedConnection.Locations));
|
||||
extraMissions.Add(beaconMissionPrefab.Instantiate(Map.SelectedConnection.Locations, Submarine.MainSub));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -282,10 +294,10 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
Random rand = new MTRandom(ToolBox.StringToInt(levelData.Seed));
|
||||
var huntingGroundsMissionPrefab = ToolBox.SelectWeightedRandom(huntingGroundsMissionPrefabs, huntingGroundsMissionPrefabs.Select(p => (float)p.Commonness).ToList(), rand);
|
||||
var huntingGroundsMissionPrefab = ToolBox.SelectWeightedRandom(huntingGroundsMissionPrefabs, huntingGroundsMissionPrefabs.Select(p => (float)Math.Max(p.Commonness, 0.1f)).ToList(), rand);
|
||||
if (!Missions.Any(m => m.Prefab.Tags.Any(t => t.Equals("huntinggrounds", StringComparison.OrdinalIgnoreCase))))
|
||||
{
|
||||
extraMissions.Add(huntingGroundsMissionPrefab.Instantiate(Map.SelectedConnection.Locations));
|
||||
extraMissions.Add(huntingGroundsMissionPrefab.Instantiate(Map.SelectedConnection.Locations, Submarine.MainSub));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -490,7 +502,7 @@ namespace Barotrauma
|
||||
if (Level.Loaded.StartOutpost.DockedTo.Any())
|
||||
{
|
||||
var dockedSub = Level.Loaded.StartOutpost.DockedTo.FirstOrDefault();
|
||||
if (dockedSub == GameMain.NetworkMember?.RespawnManager?.RespawnShuttle) { return null; }
|
||||
if (dockedSub == GameMain.NetworkMember?.RespawnManager?.RespawnShuttle || dockedSub.TeamID != leavingPlayers.FirstOrDefault()?.TeamID) { return null; }
|
||||
return dockedSub.DockedTo.Contains(Submarine.MainSub) ? Submarine.MainSub : dockedSub;
|
||||
}
|
||||
|
||||
@@ -518,7 +530,7 @@ namespace Barotrauma
|
||||
if (Level.Loaded.EndOutpost.DockedTo.Any())
|
||||
{
|
||||
var dockedSub = Level.Loaded.EndOutpost.DockedTo.FirstOrDefault();
|
||||
if (dockedSub == GameMain.NetworkMember?.RespawnManager?.RespawnShuttle) { return null; }
|
||||
if (dockedSub == GameMain.NetworkMember?.RespawnManager?.RespawnShuttle || dockedSub.TeamID != leavingPlayers.FirstOrDefault()?.TeamID) { return null; }
|
||||
return dockedSub.DockedTo.Contains(Submarine.MainSub) ? Submarine.MainSub : dockedSub;
|
||||
}
|
||||
|
||||
@@ -554,12 +566,14 @@ namespace Barotrauma
|
||||
{
|
||||
CargoManager.ClearItemsInBuyCrate();
|
||||
CargoManager.ClearItemsInSellCrate();
|
||||
CargoManager.ClearItemsInSellFromSubCrate();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
CargoManager?.ClearItemsInBuyCrate();
|
||||
// TODO: CargoManager?.ClearItemsInSellFromSubCrate();
|
||||
}
|
||||
else if (GameMain.NetworkMember.IsClient)
|
||||
{
|
||||
@@ -585,7 +599,7 @@ namespace Barotrauma
|
||||
if (c.IsDead)
|
||||
{
|
||||
CrewManager.RemoveCharacterInfo(c.Info);
|
||||
c.DespawnNow();
|
||||
c.DespawnNow(createNetworkEvents: false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -595,7 +609,6 @@ namespace Barotrauma
|
||||
{
|
||||
CrewManager.RemoveCharacterInfo(ci);
|
||||
}
|
||||
ci?.ClearCurrentOrders();
|
||||
}
|
||||
|
||||
foreach (DockingPort port in DockingPort.List)
|
||||
@@ -637,6 +650,7 @@ namespace Barotrauma
|
||||
location.CreateStore(force: true);
|
||||
location.ClearMissions();
|
||||
location.Discovered = false;
|
||||
location.LevelData?.EventHistory?.Clear();
|
||||
}
|
||||
Map.SetLocation(Map.Locations.IndexOf(Map.StartLocation));
|
||||
Map.SelectLocation(-1);
|
||||
@@ -851,11 +865,14 @@ namespace Barotrauma
|
||||
DebugConsole.NewMessage(" " + i + ". " + destination.Name, Color.White);
|
||||
}
|
||||
}
|
||||
|
||||
if (map.CurrentLocation?.SelectedMission != null)
|
||||
|
||||
if (map.CurrentLocation != null)
|
||||
{
|
||||
DebugConsole.NewMessage(" Selected mission: " + map.CurrentLocation.SelectedMission.Name, Color.White);
|
||||
DebugConsole.NewMessage("\n" + map.CurrentLocation.SelectedMission.Description, Color.White);
|
||||
foreach (Mission mission in map.CurrentLocation.SelectedMissions)
|
||||
{
|
||||
DebugConsole.NewMessage(" Selected mission: " + mission.Name, Color.White);
|
||||
DebugConsole.NewMessage("\n" + mission.Description, Color.White);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -865,5 +882,25 @@ namespace Barotrauma
|
||||
map?.Remove();
|
||||
map = null;
|
||||
}
|
||||
|
||||
public int NumberOfMissionsAtLocation(Location location)
|
||||
{
|
||||
return Map.CurrentLocation.SelectedMissions.Count(m => m.Locations.Contains(location));
|
||||
}
|
||||
|
||||
public void CheckTooManyMissions(Location currentLocation, Client sender)
|
||||
{
|
||||
foreach (Location location in currentLocation.Connections.Select(c => c.OtherLocation(currentLocation)))
|
||||
{
|
||||
if (NumberOfMissionsAtLocation(location) > Settings.MaxMissionCount)
|
||||
{
|
||||
DebugConsole.AddWarning($"Client {sender.Name} had too many missions selected for location {location.Name}! Count was {NumberOfMissionsAtLocation(location)}. Deselecting extra missions.");
|
||||
foreach (Mission mission in currentLocation.SelectedMissions.Where(m => m.Locations[1] == location).Skip(Settings.MaxMissionCount).ToList())
|
||||
{
|
||||
currentLocation.DeselectMission(mission);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
-2
@@ -26,6 +26,7 @@ namespace Barotrauma
|
||||
|
||||
private XElement itemData;
|
||||
private XElement healthData;
|
||||
public XElement OrderData { get; private set; }
|
||||
|
||||
partial void InitProjSpecific(Client client);
|
||||
public CharacterCampaignData(Client client)
|
||||
@@ -38,8 +39,10 @@ namespace Barotrauma
|
||||
if (client.Character.Inventory != null)
|
||||
{
|
||||
itemData = new XElement("inventory");
|
||||
client.Character.SaveInventory(client.Character.Inventory, itemData);
|
||||
Character.SaveInventory(client.Character.Inventory, itemData);
|
||||
}
|
||||
OrderData = new XElement("orders");
|
||||
CharacterInfo.SaveOrderData(client.Character.Info, OrderData);
|
||||
}
|
||||
|
||||
public CharacterCampaignData(XElement element)
|
||||
@@ -67,6 +70,9 @@ namespace Barotrauma
|
||||
case "health":
|
||||
healthData = subElement;
|
||||
break;
|
||||
case "orders":
|
||||
OrderData = subElement;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,8 +84,10 @@ namespace Barotrauma
|
||||
if (character.Inventory != null)
|
||||
{
|
||||
itemData = new XElement("inventory");
|
||||
character.SaveInventory(character.Inventory, itemData);
|
||||
Character.SaveInventory(character.Inventory, itemData);
|
||||
}
|
||||
OrderData = new XElement("orders");
|
||||
CharacterInfo.SaveOrderData(character.Info, OrderData);
|
||||
}
|
||||
|
||||
public XElement Save()
|
||||
@@ -92,6 +100,7 @@ namespace Barotrauma
|
||||
CharacterInfo?.Save(element);
|
||||
if (itemData != null) { element.Add(itemData); }
|
||||
if (healthData != null) { element.Add(healthData); }
|
||||
if (OrderData != null) { element.Add(OrderData); }
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Barotrauma
|
||||
Location[] locations = { GameMain.GameSession.StartLocation, GameMain.GameSession.EndLocation };
|
||||
foreach (MissionPrefab missionPrefab in missionPrefabs)
|
||||
{
|
||||
missions.Add(missionPrefab.Instantiate(locations));
|
||||
missions.Add(missionPrefab.Instantiate(locations, Submarine.MainSub));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -149,12 +149,14 @@ namespace Barotrauma
|
||||
case "metadata":
|
||||
CampaignMetadata = new CampaignMetadata(this, subElement);
|
||||
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);
|
||||
CrewManager.ActiveOrdersElement = subElement.GetChildElement("activeorders");
|
||||
break;
|
||||
case "cargo":
|
||||
CargoManager?.LoadPurchasedItems(subElement);
|
||||
|
||||
@@ -162,6 +162,18 @@ namespace Barotrauma
|
||||
|
||||
private GameMode InstantiateGameMode(GameModePreset gameModePreset, string seed, SubmarineInfo selectedSub, CampaignSettings settings, IEnumerable<MissionPrefab> missionPrefabs = null, MissionType missionType = MissionType.None)
|
||||
{
|
||||
if (gameModePreset.GameModeType == typeof(CoOpMode) || gameModePreset.GameModeType == typeof(PvPMode))
|
||||
{
|
||||
//don't allow hidden mission types (e.g. GoTo) in single mission modes
|
||||
var missionTypes = (MissionType[])Enum.GetValues(typeof(MissionType));
|
||||
for (int i = 0; i < missionTypes.Length; i++)
|
||||
{
|
||||
if (MissionPrefab.HiddenMissionClasses.Contains(missionTypes[i]))
|
||||
{
|
||||
missionType &= ~missionTypes[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (gameModePreset.GameModeType == typeof(CoOpMode))
|
||||
{
|
||||
return missionPrefabs != null ?
|
||||
@@ -353,9 +365,20 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
if (GameMode is PvPMode && Submarine.MainSubs[1] == null)
|
||||
|
||||
foreach (Mission mission in GameMode.Missions)
|
||||
{
|
||||
Submarine.MainSubs[1] = new Submarine(SubmarineInfo, true);
|
||||
// setting difficulty for missions that may involve difficulty-related submarine creation
|
||||
mission.SetDifficulty(levelData?.Difficulty ?? 0f);
|
||||
}
|
||||
|
||||
if (Submarine.MainSubs[1] == null)
|
||||
{
|
||||
var enemySubmarineInfo = GameMode is PvPMode ? SubmarineInfo : GameMode.Missions.FirstOrDefault(m => m.EnemySubmarineInfo != null)?.EnemySubmarineInfo;
|
||||
if (enemySubmarineInfo != null)
|
||||
{
|
||||
Submarine.MainSubs[1] = new Submarine(enemySubmarineInfo, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (GameMain.NetworkMember?.ServerSettings?.LockAllDefaultWires ?? false)
|
||||
@@ -526,7 +549,10 @@ namespace Barotrauma
|
||||
if (port.IsHorizontal || port.Docked) { continue; }
|
||||
if (port.Item.Submarine == level.StartOutpost)
|
||||
{
|
||||
outPostPort = port;
|
||||
if (port.DockingTarget == null)
|
||||
{
|
||||
outPostPort = port;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (port.Item.Submarine != Submarine) { continue; }
|
||||
@@ -614,6 +640,18 @@ namespace Barotrauma
|
||||
return missions.IndexOf(mission);
|
||||
}
|
||||
|
||||
public void EnforceMissionOrder(List<string> missionIdentifiers)
|
||||
{
|
||||
List<Mission> sortedMissions = new List<Mission>();
|
||||
foreach (string missionId in missionIdentifiers)
|
||||
{
|
||||
var matchingMission = missions.Find(m => m.Prefab.Identifier == missionId);
|
||||
sortedMissions.Add(matchingMission);
|
||||
missions.Remove(matchingMission);
|
||||
}
|
||||
missions.AddRange(sortedMissions);
|
||||
}
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime);
|
||||
|
||||
public void EndRound(string endMessage, List<TraitorMissionResult> traitorResults = null, CampaignMode.TransitionType transitionType = CampaignMode.TransitionType.None)
|
||||
|
||||
@@ -28,6 +28,18 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
internal class PurchasedItemSwap
|
||||
{
|
||||
public readonly Item ItemToRemove;
|
||||
public readonly ItemPrefab ItemToInstall;
|
||||
|
||||
public PurchasedItemSwap(Item itemToRemove, ItemPrefab itemToInstall)
|
||||
{
|
||||
ItemToRemove = itemToRemove;
|
||||
ItemToInstall = itemToInstall;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This class handles all upgrade logic.
|
||||
/// Storing, applying, checking and validation of upgrades.
|
||||
@@ -75,9 +87,10 @@ namespace Barotrauma
|
||||
|
||||
public readonly List<PurchasedUpgrade> PendingUpgrades = new List<PurchasedUpgrade>();
|
||||
|
||||
public readonly List<PurchasedItemSwap> PurchasedItemSwaps = new List<PurchasedItemSwap>();
|
||||
|
||||
private CampaignMetadata Metadata => Campaign.CampaignMetadata;
|
||||
private readonly CampaignMode Campaign;
|
||||
private int spentMoney;
|
||||
|
||||
public event Action? OnUpgradesChanged;
|
||||
|
||||
@@ -90,7 +103,67 @@ namespace Barotrauma
|
||||
public UpgradeManager(CampaignMode campaign, XElement element, bool isSingleplayer) : this(campaign)
|
||||
{
|
||||
DebugConsole.Log($"Restored upgrade manager from save file, ({element.Elements().Count()} pending upgrades).");
|
||||
LoadPendingUpgrades(element, isSingleplayer);
|
||||
|
||||
//backwards compatibility:
|
||||
//upgrades used to be saved to a <pendingupgrades> element, now upgrades and item swaps are saved separately under a <upgrademanager> element
|
||||
if (element.Name.LocalName.Equals("pendingupgrades", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
LoadPendingUpgrades(element, isSingleplayer);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "pendingupgrades":
|
||||
LoadPendingUpgrades(subElement, isSingleplayer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int DetermineItemSwapCost(Item item, ItemPrefab? replacement)
|
||||
{
|
||||
if (replacement == null)
|
||||
{
|
||||
replacement = ItemPrefab.Find("", item.Prefab.SwappableItem.ReplacementOnUninstall);
|
||||
if (replacement == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to determine swap cost for item \"{}\". Trying to uninstall the item but no replacement item found.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int price = 0;
|
||||
if (replacement == item.Prefab)
|
||||
{
|
||||
if (item.PendingItemSwap != null)
|
||||
{
|
||||
//refund the pending swap
|
||||
price -= item.PendingItemSwap.SwappableItem.GetPrice(Campaign?.Map?.CurrentLocation);
|
||||
//buy back the current item
|
||||
price += item.Prefab.SwappableItem.GetPrice(Campaign?.Map?.CurrentLocation);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
price = replacement.SwappableItem.GetPrice(Campaign?.Map?.CurrentLocation);
|
||||
if (item.PendingItemSwap != null)
|
||||
{
|
||||
//refund the pending swap
|
||||
price -= item.PendingItemSwap.SwappableItem.GetPrice(Campaign?.Map?.CurrentLocation);
|
||||
//buy back the current item
|
||||
price += item.Prefab.SwappableItem.GetPrice(Campaign?.Map?.CurrentLocation);
|
||||
}
|
||||
//refund the current item
|
||||
if (replacement != item.prefab)
|
||||
{
|
||||
price -= item.Prefab.SwappableItem.GetPrice(Campaign?.Map?.CurrentLocation);
|
||||
}
|
||||
}
|
||||
return price;
|
||||
}
|
||||
|
||||
private DateTime lastUpgradeSpeak, lastErrorSpeak;
|
||||
@@ -102,9 +175,6 @@ 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>
|
||||
/// <param name="prefab"></param>
|
||||
/// <param name="category"></param>
|
||||
/// <param name="force"></param>
|
||||
public void PurchaseUpgrade(UpgradePrefab prefab, UpgradeCategory category, bool force = false)
|
||||
{
|
||||
if (!CanUpgradeSub())
|
||||
@@ -142,7 +212,7 @@ namespace Barotrauma
|
||||
price = 0;
|
||||
}
|
||||
|
||||
if (Campaign.Money > price)
|
||||
if (Campaign.Money >= price)
|
||||
{
|
||||
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
@@ -155,7 +225,6 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
Campaign.Money -= price;
|
||||
spentMoney += price;
|
||||
|
||||
PurchasedUpgrade? upgrade = FindMatchingUpgrade(prefab, category);
|
||||
|
||||
@@ -184,6 +253,159 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Purchases an item swap and handles logic for deducting the credit.
|
||||
/// </summary>
|
||||
public void PurchaseItemSwap(Item itemToRemove, ItemPrefab itemToInstall, bool force = false)
|
||||
{
|
||||
if (!CanUpgradeSub())
|
||||
{
|
||||
DebugConsole.ThrowError("Cannot swap items when switching to another submarine.");
|
||||
return;
|
||||
}
|
||||
if (itemToRemove == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Cannot swap null item!");
|
||||
return;
|
||||
}
|
||||
if (itemToRemove.HiddenInGame)
|
||||
{
|
||||
DebugConsole.ThrowError($"Cannot swap item \"{itemToRemove.Name}\" because it's set to be hidden in-game.");
|
||||
return;
|
||||
}
|
||||
if (!itemToRemove.AllowSwapping)
|
||||
{
|
||||
DebugConsole.ThrowError($"Cannot swap item \"{itemToRemove.Name}\" because it's configured to be non-swappable.");
|
||||
return;
|
||||
}
|
||||
if (!UpgradeCategory.Categories.Any(c => c.ItemTags.Any(t => itemToRemove.HasTag(t)) && c.ItemTags.Any(t => itemToInstall.Tags.Contains(t))))
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to swap item \"{itemToRemove.Name}\" with \"{itemToInstall.Name}\" (not in the same upgrade category).");
|
||||
return;
|
||||
}
|
||||
|
||||
if (itemToRemove.prefab == itemToInstall)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to swap item \"{itemToRemove.Name}\" (trying to swap with the same item!).");
|
||||
return;
|
||||
}
|
||||
SwappableItem? swappableItem = itemToRemove.Prefab.SwappableItem;
|
||||
if (swappableItem == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to swap item \"{itemToRemove.Name}\" (not configured as a swappable item).");
|
||||
return;
|
||||
}
|
||||
|
||||
int price = 0;
|
||||
if (!itemToRemove.AvailableSwaps.Contains(itemToInstall))
|
||||
{
|
||||
price = itemToInstall.SwappableItem.GetPrice(Campaign.Map?.CurrentLocation);
|
||||
}
|
||||
|
||||
if (force)
|
||||
{
|
||||
price = 0;
|
||||
}
|
||||
|
||||
if (Campaign.Money >= price)
|
||||
{
|
||||
PurchasedItemSwaps.RemoveAll(p => p.ItemToRemove == itemToRemove);
|
||||
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
// only make the NPC speak if more than 5 minutes have passed since the last purchased service
|
||||
if (lastUpgradeSpeak == DateTime.MinValue || lastUpgradeSpeak.AddMinutes(5) < DateTime.Now)
|
||||
{
|
||||
UpgradeNPCSpeak(TextManager.Get("Dialog.UpgradePurchased"), Campaign.IsSinglePlayer);
|
||||
lastUpgradeSpeak = DateTime.Now;
|
||||
}
|
||||
}
|
||||
|
||||
Campaign.Money -= price;
|
||||
|
||||
itemToRemove.AvailableSwaps.Add(itemToRemove.Prefab);
|
||||
if (itemToInstall != null && !itemToRemove.AvailableSwaps.Contains(itemToInstall))
|
||||
{
|
||||
itemToRemove.PurchasedNewSwap = true;
|
||||
itemToRemove.AvailableSwaps.Add(itemToInstall);
|
||||
}
|
||||
|
||||
if (itemToRemove.Prefab != itemToInstall && itemToInstall != null)
|
||||
{
|
||||
itemToRemove.PendingItemSwap = itemToInstall;
|
||||
PurchasedItemSwaps.Add(new PurchasedItemSwap(itemToRemove, itemToInstall));
|
||||
DebugLog($"CLIENT: Swapped item \"{itemToRemove.Name}\" with \"{itemToInstall.Name}\".", Color.Orange);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugLog($"CLIENT: Cancelled swapping the item \"{itemToRemove.Name}\" with \"{(itemToRemove.PendingItemSwap?.Name ?? null)}\".", Color.Orange);
|
||||
}
|
||||
OnUpgradesChanged?.Invoke();
|
||||
}
|
||||
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}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
if (!CanUpgradeSub())
|
||||
{
|
||||
DebugConsole.ThrowError("Cannot swap items when switching to another submarine.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (itemToRemove?.PendingItemSwap == null && string.IsNullOrEmpty(itemToRemove?.Prefab.SwappableItem?.ReplacementOnUninstall))
|
||||
{
|
||||
DebugConsole.ThrowError($"Cannot uninstall item \"{itemToRemove?.Name}\" (no replacement item configured).");
|
||||
return;
|
||||
}
|
||||
|
||||
SwappableItem? swappableItem = itemToRemove.Prefab.SwappableItem;
|
||||
if (swappableItem == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to uninstall item \"{itemToRemove.Name}\" (not configured as a swappable item).");
|
||||
return;
|
||||
}
|
||||
|
||||
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
// only make the NPC speak if more than 5 minutes have passed since the last purchased service
|
||||
if (lastUpgradeSpeak == DateTime.MinValue || lastUpgradeSpeak.AddMinutes(5) < DateTime.Now)
|
||||
{
|
||||
UpgradeNPCSpeak(TextManager.Get("Dialog.UpgradePurchased"), Campaign.IsSinglePlayer);
|
||||
lastUpgradeSpeak = DateTime.Now;
|
||||
}
|
||||
}
|
||||
|
||||
if (itemToRemove.PendingItemSwap == null)
|
||||
{
|
||||
var replacement = MapEntityPrefab.Find("", swappableItem.ReplacementOnUninstall) as ItemPrefab;
|
||||
if (replacement == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to uninstall item \"{itemToRemove.Name}\". Could not find the replacement item \"{swappableItem.ReplacementOnUninstall}\".");
|
||||
return;
|
||||
}
|
||||
PurchasedItemSwaps.RemoveAll(p => p.ItemToRemove == itemToRemove);
|
||||
PurchasedItemSwaps.Add(new PurchasedItemSwap(itemToRemove, replacement));
|
||||
DebugLog($"Uninstalled item item \"{itemToRemove.Name}\".", Color.Orange);
|
||||
itemToRemove.PendingItemSwap = replacement;
|
||||
}
|
||||
else
|
||||
{
|
||||
PurchasedItemSwaps.RemoveAll(p => p.ItemToRemove == itemToRemove);
|
||||
DebugLog($"Cancelled swapping the item \"{itemToRemove.Name}\" with \"{itemToRemove.PendingItemSwap.Name}\".", Color.Orange);
|
||||
itemToRemove.PendingItemSwap = null;
|
||||
}
|
||||
#if CLIENT
|
||||
OnUpgradesChanged?.Invoke();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies all our pending upgrades to the submarine.
|
||||
/// </summary>
|
||||
@@ -201,24 +423,22 @@ namespace Barotrauma
|
||||
public void ApplyUpgrades()
|
||||
{
|
||||
PurchasedUpgrades.Clear();
|
||||
PurchasedItemSwaps.Clear();
|
||||
if (Submarine.MainSub == null) { return; }
|
||||
|
||||
List<PurchasedUpgrade> pendingUpgrades = PendingUpgrades;
|
||||
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
|
||||
if (Level.Loaded is { Type: LevelData.LevelType.Outpost })
|
||||
{
|
||||
if (Level.Loaded?.Type != LevelData.LevelType.Outpost)
|
||||
return;
|
||||
}
|
||||
|
||||
if (GameMain.NetworkMember is { IsClient: true })
|
||||
{
|
||||
if (loadedUpgrades != null)
|
||||
{
|
||||
if (loadedUpgrades != null)
|
||||
{
|
||||
// client receives pending upgrades from the save file
|
||||
pendingUpgrades = loadedUpgrades;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// prevent the client from applying pending upgrades at an outpost when joining mid round
|
||||
return;
|
||||
// client receives pending upgrades from the save file
|
||||
pendingUpgrades = loadedUpgrades;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,36 +447,12 @@ namespace Barotrauma
|
||||
{
|
||||
int newLevel = BuyUpgrade(prefab, category, Submarine.MainSub, level);
|
||||
DebugConsole.Log($" - {category.Identifier}.{prefab.Identifier} lvl. {level}, new: ({newLevel})");
|
||||
if (newLevel > 0)
|
||||
{
|
||||
SetUpgradeLevel(prefab, category, Math.Clamp(newLevel, 0, prefab.MaxLevel));
|
||||
}
|
||||
SetUpgradeLevel(prefab, category, Math.Clamp(GetRealUpgradeLevel(prefab, category) + level, 0, prefab.MaxLevel));
|
||||
}
|
||||
|
||||
PendingUpgrades.Clear();
|
||||
loadedUpgrades?.Clear();
|
||||
loadedUpgrades = null;
|
||||
spentMoney = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancels the pending upgrades and refunds the money spent
|
||||
/// </summary>
|
||||
private void RefundUpgrades()
|
||||
{
|
||||
DebugConsole.Log($"Refunded {spentMoney} marks in pending upgrades.");
|
||||
if (spentMoney > 0)
|
||||
{
|
||||
#if CLIENT
|
||||
GUIMessageBox msgBox = new GUIMessageBox(TextManager.Get("UpgradeRefundTitle"), TextManager.Get("UpgradeRefundBody"), new[] { TextManager.Get("Ok") });
|
||||
msgBox.Buttons[0].OnClicked += msgBox.Close;
|
||||
#endif
|
||||
}
|
||||
|
||||
Campaign.Money += spentMoney;
|
||||
spentMoney = 0;
|
||||
PendingUpgrades.Clear();
|
||||
PurchasedUpgrades.Clear();
|
||||
}
|
||||
|
||||
public void CreateUpgradeErrorMessage(string text, bool isSinglePlayer, Character character)
|
||||
@@ -314,7 +510,7 @@ namespace Barotrauma
|
||||
|
||||
if (upgrade == null || upgrade.Level != level || isOverMax)
|
||||
{
|
||||
DebugConsole.AddWarning($"{wall.prefab.Name} has incorrect \"{prefab.Name}\" level! Expected {level} but got {upgrade?.Level ?? 0}. Fixing...");
|
||||
DebugLog($"{wall.prefab.Name} has incorrect \"{prefab.Name}\" level! Expected {level} but got {upgrade?.Level ?? 0}. Fixing...");
|
||||
FixUpgradeOnItem(wall, prefab, level);
|
||||
}
|
||||
}
|
||||
@@ -343,7 +539,7 @@ namespace Barotrauma
|
||||
|
||||
if (upgrade == null || upgrade.Level != level || isOverMax)
|
||||
{
|
||||
DebugConsole.AddWarning($"{item.prefab.Name} has incorrect \"{prefab.Name}\" level! Expected {level} but got {upgrade?.Level ?? 0}{(isOverMax ? " (Over max level!)" : string.Empty)}. Fixing...");
|
||||
DebugLog($"{item.prefab.Name} has incorrect \"{prefab.Name}\" level! Expected {level} but got {upgrade?.Level ?? 0}{(isOverMax ? " (Over max level!)" : string.Empty)}. Fixing...");
|
||||
FixUpgradeOnItem(item, prefab, level);
|
||||
}
|
||||
}
|
||||
@@ -486,113 +682,17 @@ namespace Barotrauma
|
||||
return Campaign.PendingSubmarineSwitch == null;
|
||||
}
|
||||
|
||||
public void RefundResetAndReload(SubmarineInfo newSubmarine, bool notifyClients = false)
|
||||
public void Save(XElement? parent)
|
||||
{
|
||||
RefundUpgrades();
|
||||
ResetUpgrades();
|
||||
Dictionary<string, int> newUpgrades = ReloadUpgradeValues(newSubmarine);
|
||||
#if SERVER
|
||||
if (notifyClients)
|
||||
{
|
||||
SendUpgradeResetMessage(newUpgrades);
|
||||
}
|
||||
#endif
|
||||
if (parent == null) { return; }
|
||||
|
||||
var upgradeManagerElement = new XElement("upgrademanager");
|
||||
parent.Add(upgradeManagerElement);
|
||||
|
||||
SavePendingUpgrades(upgradeManagerElement, PendingUpgrades);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a SubmarineInfo and sets the store values accordingly.
|
||||
/// Used when reloading a previously saved submarine.
|
||||
/// </summary>
|
||||
/// <param name="info"></param>
|
||||
private Dictionary<string, int> ReloadUpgradeValues(SubmarineInfo info)
|
||||
{
|
||||
Dictionary<string, int> newValues = new Dictionary<string, int>();
|
||||
IEnumerable<XElement> linkedSubElements = info.SubmarineElement.Elements().Where(element => element.Name.ToString().Equals("LinkedSubmarine", StringComparison.OrdinalIgnoreCase)).SelectMany(element => element.Elements());
|
||||
IEnumerable<XElement> mainSubElements = info.SubmarineElement.Elements().Where(Predicate);
|
||||
List<XElement> elements = mainSubElements.Concat(linkedSubElements.Where(Predicate)).ToList();
|
||||
foreach (UpgradeCategory category in UpgradeCategory.Categories)
|
||||
{
|
||||
foreach (UpgradePrefab prefab in UpgradePrefab.Prefabs)
|
||||
{
|
||||
if (!prefab.UpgradeCategories.Contains(category)) { continue; }
|
||||
|
||||
List<int> levels = GetUpgradeFromXML(elements, category, prefab);
|
||||
if (levels.Any())
|
||||
{
|
||||
int level = (int) levels.Average(i => i);
|
||||
newValues.Add(FormatIdentifier(prefab, category), level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (dataIdentifier, level) in newValues)
|
||||
{
|
||||
Campaign.CampaignMetadata.SetValue(dataIdentifier, level);
|
||||
}
|
||||
|
||||
return newValues;
|
||||
|
||||
static List<int> GetUpgradeFromXML(List<XElement> elements, UpgradeCategory category, UpgradePrefab prefab)
|
||||
{
|
||||
List<int> levels = new List<int>();
|
||||
foreach (XElement subElement in elements)
|
||||
{
|
||||
if (!category.CanBeApplied(subElement, prefab)) { continue; }
|
||||
|
||||
foreach (XElement component in subElement.Elements())
|
||||
{
|
||||
if (string.Equals(component.Name.ToString(), "upgrade", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string identifier = component.GetAttributeString("identifier", string.Empty);
|
||||
int level = component.GetAttributeInt("level", -1);
|
||||
if (string.IsNullOrWhiteSpace(identifier) || level <= 0) { continue; }
|
||||
|
||||
UpgradePrefab? matchingPrefab = UpgradePrefab.Find(identifier);
|
||||
if (matchingPrefab == null || matchingPrefab != prefab) { continue; }
|
||||
|
||||
if (matchingPrefab.UpgradeCategories.Contains(category)) { levels.Add(level); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return levels;
|
||||
}
|
||||
|
||||
static bool Predicate(XElement element) => element.HasElements && element.Elements().Any(e => e.Name.ToString().Equals("upgrade", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Resets our upgrade progress and prices.
|
||||
/// This does not actually remove the upgrades from the submarine but resets the store interface.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method works by iterating thru all upgrade categories and prefabs and checking if they have a
|
||||
/// valid key stored in the metadata, if they do set it to 0, upgrades without a key stored are always
|
||||
/// assumed to be 0 so they don't need to be reset.
|
||||
///
|
||||
/// Should initially be called server side as we can't trust clients with such a simple notification.
|
||||
/// </remarks>
|
||||
private void ResetUpgrades()
|
||||
{
|
||||
foreach (UpgradeCategory category in UpgradeCategory.Categories)
|
||||
{
|
||||
foreach (UpgradePrefab prefab in UpgradePrefab.Prefabs)
|
||||
{
|
||||
if (!prefab.UpgradeCategories.Contains(category)) { continue; }
|
||||
|
||||
string dataIdentifier = FormatIdentifier(prefab, category);
|
||||
if (Metadata.HasKey(dataIdentifier))
|
||||
{
|
||||
Metadata.SetValue(dataIdentifier, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OnUpgradesChanged?.Invoke();
|
||||
}
|
||||
|
||||
public void SavePendingUpgrades(XElement? parent, List<PurchasedUpgrade> upgrades)
|
||||
private void SavePendingUpgrades(XElement? parent, List<PurchasedUpgrade> upgrades)
|
||||
{
|
||||
if (parent == null) { return; }
|
||||
|
||||
@@ -612,7 +712,7 @@ namespace Barotrauma
|
||||
|
||||
private void LoadPendingUpgrades(XElement? element, bool isSingleplayer = true)
|
||||
{
|
||||
if (element == null || !element.HasElements) { return; }
|
||||
if (!(element is { HasElements: true })) { return; }
|
||||
|
||||
List<PurchasedUpgrade> pendingUpgrades = new List<PurchasedUpgrade>();
|
||||
|
||||
@@ -647,7 +747,7 @@ namespace Barotrauma
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void LogError(string text, Dictionary<string, object?> data, Exception e = null)
|
||||
public static void LogError(string text, Dictionary<string, object?> data, Exception? e = null)
|
||||
{
|
||||
string error = $"{text}\n";
|
||||
foreach (var (label, value) in data)
|
||||
@@ -658,34 +758,10 @@ namespace Barotrauma
|
||||
DebugConsole.ThrowError(error.TrimEnd('\n'), e);
|
||||
}
|
||||
|
||||
public static Dictionary<string, int> GetMetadataLevels(CampaignMetadata? metadata)
|
||||
{
|
||||
Dictionary<string, int> values = new Dictionary<string, int>();
|
||||
|
||||
if (metadata == null) { return values; }
|
||||
|
||||
foreach (UpgradeCategory category in UpgradeCategory.Categories)
|
||||
{
|
||||
foreach (UpgradePrefab prefab in UpgradePrefab.Prefabs)
|
||||
{
|
||||
string identifier = FormatIdentifier(prefab, category);
|
||||
if (metadata.HasKey(identifier) && !values.ContainsKey(identifier))
|
||||
{
|
||||
values.Add(identifier, metadata.GetInt(identifier));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to sync the pending upgrades list in multiplayer.
|
||||
/// </summary>
|
||||
/// <param name="upgrades"></param>
|
||||
/// <remarks>
|
||||
/// In singleplayer this is not used and should not be.
|
||||
/// </remarks>
|
||||
public void SetPendingUpgrades(List<PurchasedUpgrade> upgrades)
|
||||
{
|
||||
PendingUpgrades.Clear();
|
||||
|
||||
Reference in New Issue
Block a user