Unstable 1.8.4.0
This commit is contained in:
@@ -57,9 +57,9 @@ namespace Barotrauma
|
||||
/// Spawns loot in the specified container.
|
||||
/// </summary>
|
||||
/// <param name="skipItemProbability">Probability for an individual loot item to be skipped. I.e. a value of 1.0 means nothing spawns, 0.5 means there's about 50% of the normal amount of loot.</param>
|
||||
public static void RegenerateLoot(Submarine sub, ItemContainer regeneratedContainer, float skipItemProbability = 0.0f)
|
||||
public static IEnumerable<Item> RegenerateLoot(Submarine sub, ItemContainer regeneratedContainer, float skipItemProbability = 0.0f)
|
||||
{
|
||||
CreateAndPlace(sub.ToEnumerable(), regeneratedContainer: regeneratedContainer, skipItemProbability);
|
||||
return CreateAndPlace(sub.ToEnumerable(), regeneratedContainer: regeneratedContainer, skipItemProbability);
|
||||
}
|
||||
|
||||
public static Identifier DefaultStartItemSet = new Identifier("normal");
|
||||
@@ -136,12 +136,12 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateAndPlace(IEnumerable<Submarine> subs, ItemContainer regeneratedContainer = null, float skipItemProbability = 0.0f)
|
||||
private static IEnumerable<Item> CreateAndPlace(IEnumerable<Submarine> subs, ItemContainer regeneratedContainer = null, float skipItemProbability = 0.0f)
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
|
||||
{
|
||||
DebugConsole.ThrowError("Clients are not allowed to use AutoItemPlacer.\n" + Environment.StackTrace.CleanupStackTrace());
|
||||
return;
|
||||
return Enumerable.Empty<Item>();
|
||||
}
|
||||
|
||||
List<Item> itemsToSpawn = new List<Item>(100);
|
||||
@@ -199,12 +199,12 @@ namespace Barotrauma
|
||||
{
|
||||
var itemPrefab = prefabsItemsCanSpawnIn[i];
|
||||
if (itemPrefab == null) { continue; }
|
||||
SpawnItems(itemPrefab);
|
||||
SpawnItems(itemPrefab, skipItemProbability);
|
||||
}
|
||||
|
||||
// Spawn items that nothing can spawn in last
|
||||
singlePrefabs.Shuffle(Rand.RandSync.ServerAndClient);
|
||||
singlePrefabs.ForEach(i => SpawnItems(i));
|
||||
singlePrefabs.ForEach(i => SpawnItems(i, skipItemProbability));
|
||||
|
||||
if (OutputDebugInfo)
|
||||
{
|
||||
@@ -249,7 +249,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
void SpawnItems(ItemPrefab itemPrefab, float skipItemProbability = 0.0f)
|
||||
return itemsToSpawn;
|
||||
|
||||
void SpawnItems(ItemPrefab itemPrefab, float skipItemProbability)
|
||||
{
|
||||
if (Rand.Range(0.0f, 1.0f, Rand.RandSync.ServerAndClient) < skipItemProbability) { return; }
|
||||
if (itemPrefab == null)
|
||||
@@ -260,11 +262,13 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
bool isCampaign = GameMain.GameSession?.GameMode is CampaignMode;
|
||||
bool isPvP = GameMain.GameSession?.GameMode is PvPMode;
|
||||
float levelDifficulty = Level.Loaded?.Difficulty ?? 0.0f;
|
||||
foreach (PreferredContainer preferredContainer in itemPrefab.PreferredContainers)
|
||||
{
|
||||
if (preferredContainer.CampaignOnly && !isCampaign) { continue; }
|
||||
if (preferredContainer.NotCampaign && isCampaign) { continue; }
|
||||
if (preferredContainer.NotPvP && isPvP) { continue; }
|
||||
if (levelDifficulty < preferredContainer.MinLevelDifficulty || levelDifficulty > preferredContainer.MaxLevelDifficulty) { continue; }
|
||||
if (preferredContainer.SpawnProbability <= 0.0f || preferredContainer.MaxAmount <= 0 && preferredContainer.Amount <= 0) { continue; }
|
||||
validContainers = GetValidContainers(preferredContainer, containers, validContainers, primary: true);
|
||||
@@ -329,7 +333,7 @@ namespace Barotrauma
|
||||
}
|
||||
var existingItem = validContainer.Key.Inventory.AllItems.FirstOrDefault(it => it.Prefab == itemPrefab);
|
||||
int quality = existingItem?.Quality ?? Quality.GetSpawnedItemQuality(validContainer.Key.Item.Submarine, Level.Loaded, Rand.RandSync.ServerAndClient);
|
||||
if (!validContainer.Key.Inventory.CanBePut(itemPrefab, quality: quality)) { break; }
|
||||
if (!validContainer.Key.Inventory.CanProbablyBePut(itemPrefab, quality: quality)) { break; }
|
||||
var item = new Item(itemPrefab, validContainer.Key.Item.Position, validContainer.Key.Item.Submarine, callOnItemLoaded: false)
|
||||
{
|
||||
SpawnedInCurrentOutpost = validContainer.Key.Item.SpawnedInCurrentOutpost,
|
||||
|
||||
@@ -510,6 +510,17 @@ namespace Barotrauma
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//can't sell items in hidden inventories
|
||||
Item rootContainer = item.Container;
|
||||
while (rootContainer != null)
|
||||
{
|
||||
if (rootContainer.OwnInventory?.Container is { } containerComponent)
|
||||
{
|
||||
if (!containerComponent.DrawInventory) { return false; }
|
||||
if (!containerComponent.IsAccessible()) { return false; }
|
||||
}
|
||||
rootContainer = rootContainer.Container;
|
||||
}
|
||||
if (item.OwnInventory?.Container is ItemContainer itemContainer)
|
||||
{
|
||||
var containedItems = item.ContainedItems;
|
||||
@@ -546,7 +557,7 @@ namespace Barotrauma
|
||||
if (!string.IsNullOrEmpty(item.CargoContainerIdentifier))
|
||||
{
|
||||
itemContainer = availableContainers.Find(ac =>
|
||||
ac.Inventory.CanBePut(item) &&
|
||||
ac.Inventory.CanProbablyBePut(item) &&
|
||||
(ac.Item.Prefab.Identifier == item.CargoContainerIdentifier ||
|
||||
ac.Item.Prefab.Tags.Contains(item.CargoContainerIdentifier)));
|
||||
|
||||
@@ -588,7 +599,7 @@ namespace Barotrauma
|
||||
return itemContainer;
|
||||
}
|
||||
|
||||
public static void DeliverItemsToSub(IEnumerable<PurchasedItem> itemsToSpawn, Submarine sub, CargoManager cargoManager)
|
||||
public static void DeliverItemsToSub(IEnumerable<PurchasedItem> itemsToSpawn, Submarine sub, CargoManager cargoManager, bool showNotification = true)
|
||||
{
|
||||
if (!itemsToSpawn.Any()) { return; }
|
||||
|
||||
@@ -606,7 +617,7 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
if (sub == Submarine.MainSub && itemsToSpawn.Any(it => !it.Delivered && it.Quantity > 0))
|
||||
if (sub == Submarine.MainSub && itemsToSpawn.Any(it => !it.Delivered && it.Quantity > 0) && showNotification)
|
||||
{
|
||||
#if CLIENT
|
||||
new GUIMessageBox("",
|
||||
@@ -620,11 +631,14 @@ namespace Barotrauma
|
||||
#else
|
||||
foreach (Client client in GameMain.Server.ConnectedClients)
|
||||
{
|
||||
ChatMessage msg = ChatMessage.Create("",
|
||||
TextManager.ContainsTag(cargoRoom.RoomName) ? $"CargoSpawnNotification~[roomname]=§{cargoRoom.RoomName}" : $"CargoSpawnNotification~[roomname]={cargoRoom.RoomName}",
|
||||
ChatMessageType.ServerMessageBoxInGame, null);
|
||||
msg.IconStyle = "StoreShoppingCrateIcon";
|
||||
GameMain.Server.SendDirectChatMessage(msg, client);
|
||||
if (client.TeamID == CharacterTeamType.None || client.TeamID == sub.TeamID)
|
||||
{
|
||||
ChatMessage msg = ChatMessage.Create("",
|
||||
TextManager.ContainsTag(cargoRoom.RoomName) ? $"CargoSpawnNotification~[roomname]=§{cargoRoom.RoomName}" : $"CargoSpawnNotification~[roomname]={cargoRoom.RoomName}",
|
||||
ChatMessageType.ServerMessageBoxInGame, null);
|
||||
msg.IconStyle = "StoreShoppingCrateIcon";
|
||||
GameMain.Server.SendDirectChatMessage(msg, client);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -638,7 +652,7 @@ namespace Barotrauma
|
||||
{
|
||||
var item = new Item(pi.ItemPrefab, position, wp.Submarine);
|
||||
var itemContainer = GetOrCreateCargoContainerFor(pi.ItemPrefab, cargoRoom, ref availableContainers);
|
||||
itemContainer?.Inventory.TryPutItem(item, null);
|
||||
itemContainer?.Inventory.TryPutItem(item, user: null);
|
||||
ItemSpawned(pi, item, cargoManager);
|
||||
#if SERVER
|
||||
Entity.Spawner?.CreateNetworkEvent(new EntitySpawner.SpawnEntity(item));
|
||||
@@ -669,7 +683,8 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (Item containedItem in character.Inventory.AllItemsMod)
|
||||
{
|
||||
if (containedItem.OwnInventory != null &&
|
||||
//only put into containers that draw the inventory (not ones with a hidden inventory like circuit boxes!)
|
||||
if (containedItem.OwnInventory?.Container is { DrawInventory: true } &&
|
||||
containedItem.OwnInventory.TryPutItem(item, user: null, item.AllowedSlots))
|
||||
{
|
||||
break;
|
||||
@@ -695,14 +710,28 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
Submarine sub = item.Submarine ?? item.RootContainer?.Submarine;
|
||||
if (sub != null)
|
||||
ItemSpawned(item);
|
||||
}
|
||||
|
||||
public static void ItemSpawned(Item item)
|
||||
{
|
||||
CharacterTeamType teamID = CharacterTeamType.Team1;
|
||||
if (item.ParentInventory?.Owner is Character character)
|
||||
{
|
||||
foreach (WifiComponent wifiComponent in item.GetComponents<WifiComponent>())
|
||||
teamID = character.TeamID;
|
||||
}
|
||||
else
|
||||
{
|
||||
Submarine sub = item.Submarine ?? item.RootContainer?.Submarine;
|
||||
if (sub != null)
|
||||
{
|
||||
wifiComponent.TeamID = sub.TeamID;
|
||||
teamID = sub.TeamID;
|
||||
}
|
||||
}
|
||||
foreach (WifiComponent wifiComponent in item.GetComponents<WifiComponent>())
|
||||
{
|
||||
wifiComponent.TeamID = teamID;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<(PurchasedItem purchaseInfo, IdCard idCard)> purchasedIDCards = new List<(PurchasedItem purchaseInfo, IdCard idCard)>();
|
||||
|
||||
@@ -5,9 +5,9 @@ using Barotrauma.Tutorials;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -27,19 +27,29 @@ namespace Barotrauma
|
||||
|
||||
private readonly List<CharacterInfo> characterInfos = new List<CharacterInfo>();
|
||||
private readonly List<Character> characters = new List<Character>();
|
||||
|
||||
private readonly List<CharacterInfo> reserveBench = new List<CharacterInfo>();
|
||||
|
||||
public IEnumerable<Character> GetCharacters()
|
||||
{
|
||||
return characters;
|
||||
}
|
||||
/// <summary>
|
||||
/// Note: this only returns AI characters' infos in multiplayer. The infos are used to manage hiring/firing/renaming, which only applies to AI characters.
|
||||
/// NOTE: When called from client code, this method will include players, but NOT when called from server code.
|
||||
/// CrewManager is used for dealing with things relevant to AI characters, like hiring, firing, renaming, and the reserve bench.
|
||||
/// In single player/client code, player CharacterInfos are still stored in it but only for displaying crew listings in the GUI correctly.
|
||||
/// Use <see cref="GetSessionCrewCharacters"/> to get all the characters regardless if they're player or AI controlled.
|
||||
/// </summary>
|
||||
public IEnumerable<CharacterInfo> GetCharacterInfos()
|
||||
/// <param name="includeReserveBench">Should characters on the reserve be included? Defaults to false.</param>
|
||||
public IEnumerable<CharacterInfo> GetCharacterInfos(bool includeReserveBench = false)
|
||||
{
|
||||
if (includeReserveBench)
|
||||
{
|
||||
return characterInfos.Concat(reserveBench);
|
||||
}
|
||||
return characterInfos;
|
||||
}
|
||||
|
||||
public IEnumerable<CharacterInfo> GetReserveBenchInfos()
|
||||
{
|
||||
return reserveBench;
|
||||
}
|
||||
|
||||
private Character welcomeMessageNPC;
|
||||
|
||||
@@ -81,6 +91,7 @@ namespace Barotrauma
|
||||
|
||||
// Ignore orders work a bit differently since the "unignore" order counters the "ignore" order
|
||||
var isUnignoreOrder = order.Identifier == Tags.UnignoreThis;
|
||||
var isIgnoreOrder = order.Identifier == Tags.IgnoreThis;
|
||||
var orderPrefab = !isUnignoreOrder ? order.Prefab : OrderPrefab.Prefabs[Tags.IgnoreThis];
|
||||
ActiveOrder existingOrder = ActiveOrders.Find(o =>
|
||||
o.Order.Prefab == orderPrefab && MatchesTarget(o.Order.TargetEntity, order.TargetEntity) &&
|
||||
@@ -96,6 +107,14 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
ActiveOrders.Remove(existingOrder);
|
||||
if (isIgnoreOrder && order.TargetEntity is Item targetItem)
|
||||
{
|
||||
foreach (var stackedItem in targetItem.GetStackedItems())
|
||||
{
|
||||
ActiveOrders.RemoveAll(o => o.Order.Prefab == orderPrefab && o.Order.TargetEntity == stackedItem);
|
||||
stackedItem.OrderedToBeIgnored = false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -124,7 +143,18 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
ActiveOrders.Add(new ActiveOrder(order, fadeOutTime));
|
||||
if (isIgnoreOrder && order.TargetEntity is Item targetItem)
|
||||
{
|
||||
foreach (var stackedItem in targetItem.GetStackedItems())
|
||||
{
|
||||
ActiveOrders.Add(new ActiveOrder(order.WithTargetEntity(stackedItem), fadeOutTime));
|
||||
stackedItem.OrderedToBeIgnored = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ActiveOrders.Add(new ActiveOrder(order, fadeOutTime));
|
||||
}
|
||||
#if CLIENT
|
||||
HintManager.OnActiveOrderAdded(order);
|
||||
#endif
|
||||
@@ -154,7 +184,14 @@ namespace Barotrauma
|
||||
if (characterElement.GetAttributeBool("lastcontrolled", false)) { characterInfo.LastControlled = true; }
|
||||
characterInfo.CrewListIndex = characterElement.GetAttributeInt("crewlistindex", -1);
|
||||
#endif
|
||||
characterInfos.Add(characterInfo);
|
||||
if (characterElement.GetAttributeBool(nameof(CharacterInfo.IsOnReserveBench), false))
|
||||
{
|
||||
reserveBench.Add(characterInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
characterInfos.Add(characterInfo);
|
||||
}
|
||||
foreach (var subElement in characterElement.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
@@ -179,10 +216,17 @@ namespace Barotrauma
|
||||
/// <param name="characterInfo"></param>
|
||||
public void RemoveCharacterInfo(CharacterInfo characterInfo)
|
||||
{
|
||||
if (characterInfo is { IsOnReserveBench: true })
|
||||
{
|
||||
reserveBench.Remove(characterInfo);
|
||||
}
|
||||
characterInfos.Remove(characterInfo);
|
||||
#if CLIENT
|
||||
GameMain.GameSession?.DeathPrompt?.UpdateBotList();
|
||||
#endif
|
||||
}
|
||||
|
||||
public void AddCharacter(Character character, bool sortCrewList = true)
|
||||
public void AddCharacter(Character character)
|
||||
{
|
||||
if (character.Removed)
|
||||
{
|
||||
@@ -216,10 +260,6 @@ namespace Barotrauma
|
||||
}
|
||||
#if CLIENT
|
||||
var characterComponent = AddCharacterToCrewList(character);
|
||||
if (sortCrewList)
|
||||
{
|
||||
SortCrewList();
|
||||
}
|
||||
if (character.CurrentOrders != null)
|
||||
{
|
||||
foreach (var order in character.CurrentOrders)
|
||||
@@ -273,6 +313,15 @@ namespace Barotrauma
|
||||
|
||||
public void AddCharacterInfo(CharacterInfo characterInfo)
|
||||
{
|
||||
if (GameMain.GameSession?.Campaign is MultiPlayerCampaign)
|
||||
{
|
||||
Debug.Assert(characterInfo.BotStatus == BotStatus.ActiveService);
|
||||
if (characterInfo.BotStatus != BotStatus.ActiveService)
|
||||
{
|
||||
DebugConsole.ThrowError($"CrewManager.AddCharacterInfo called on a bot ({characterInfo.DisplayName}) with the wrong status ({characterInfo.BotStatus})");
|
||||
}
|
||||
}
|
||||
|
||||
if (characterInfos.Contains(characterInfo))
|
||||
{
|
||||
DebugConsole.ThrowError("Tried to add the same character info to CrewManager twice.\n" + Environment.StackTrace.CleanupStackTrace());
|
||||
@@ -280,11 +329,15 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
characterInfos.Add(characterInfo);
|
||||
#if CLIENT
|
||||
GameMain.GameSession?.DeathPrompt?.UpdateBotList();
|
||||
#endif
|
||||
}
|
||||
|
||||
public void ClearCharacterInfos()
|
||||
{
|
||||
characterInfos.Clear();
|
||||
reserveBench.Clear();
|
||||
}
|
||||
|
||||
public void InitRound()
|
||||
@@ -297,7 +350,7 @@ namespace Barotrauma
|
||||
|
||||
List<WayPoint> spawnWaypoints = null;
|
||||
List<WayPoint> mainSubWaypoints = WayPoint.SelectCrewSpawnPoints(characterInfos, Submarine.MainSub).ToList();
|
||||
|
||||
|
||||
if (Level.Loaded != null && Level.Loaded.ShouldSpawnCrewInsideOutpost())
|
||||
{
|
||||
spawnWaypoints = GetOutpostSpawnpoints();
|
||||
@@ -308,7 +361,7 @@ namespace Barotrauma
|
||||
while (spawnWaypoints.Any() && spawnWaypoints.Count < characterInfos.Count)
|
||||
{
|
||||
spawnWaypoints.Add(spawnWaypoints[Rand.Int(spawnWaypoints.Count)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (spawnWaypoints == null || !spawnWaypoints.Any())
|
||||
{
|
||||
@@ -324,7 +377,7 @@ namespace Barotrauma
|
||||
Character character = Character.Create(info, spawnWaypoints[i].WorldPosition, info.Name);
|
||||
InitializeCharacter(character, mainSubWaypoints[i], spawnWaypoints[i]);
|
||||
|
||||
AddCharacter(character, sortCrewList: false);
|
||||
AddCharacter(character);
|
||||
#if CLIENT
|
||||
if (IsSinglePlayer && (Character.Controlled == null || character.Info.LastControlled)) { Character.Controlled = character; }
|
||||
#endif
|
||||
@@ -364,7 +417,7 @@ namespace Barotrauma
|
||||
}
|
||||
else if (!character.Info.StartItemsGiven)
|
||||
{
|
||||
character.GiveJobItems(mainSubWaypoint);
|
||||
character.GiveJobItems(isPvPMode: GameMain.GameSession?.GameMode is PvPMode, mainSubWaypoint);
|
||||
foreach (Item item in character.Inventory.AllItems)
|
||||
{
|
||||
//if the character is loaded from a human prefab with preconfigured items, its ID card gets assigned to the sub it spawns in
|
||||
@@ -558,7 +611,7 @@ namespace Barotrauma
|
||||
|
||||
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));
|
||||
var filteredCharacters = characters.Where(c => c.Info != null && (controlledCharacter == null || ((includeSelf || c != controlledCharacter) && c.TeamID == controlledCharacter.TeamID)));
|
||||
if (extraCharacters != null)
|
||||
{
|
||||
filteredCharacters = filteredCharacters.Union(extraCharacters);
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace Barotrauma
|
||||
string FilePath,
|
||||
Option<SerializableDateTime> SaveTime,
|
||||
string SubmarineName,
|
||||
RespawnMode RespawnMode,
|
||||
ImmutableArray<string> EnabledContentPackageNames) : INetSerializableStruct;
|
||||
|
||||
public const int MaxMoney = int.MaxValue / 2; //about 1 billion
|
||||
@@ -33,6 +34,20 @@ namespace Barotrauma
|
||||
|
||||
public enum InteractionType { None, Talk, Examine, Map, Crew, Store, Upgrade, PurchaseSub, MedicalClinic, Cargo }
|
||||
|
||||
/// <summary>
|
||||
/// Should the interaction be disabled if the character's faction is hostile towards the players?
|
||||
/// </summary>
|
||||
public static bool HostileFactionDisablesInteraction(InteractionType interactionType)
|
||||
{
|
||||
return
|
||||
interactionType != InteractionType.None &&
|
||||
//allow interacting with stores, otherwise you could get softlocked
|
||||
//(no way to get enough resources from a hostile outpost to make it to the next one?)
|
||||
interactionType != InteractionType.Store &&
|
||||
//examining is triggered by events, and there may be events that are intended to allow interaction with a hostile NPC.
|
||||
interactionType != InteractionType.Examine;
|
||||
}
|
||||
|
||||
public static bool BlocksInteraction(InteractionType interactionType)
|
||||
{
|
||||
return interactionType != InteractionType.None && interactionType != InteractionType.Cargo;
|
||||
@@ -103,6 +118,9 @@ namespace Barotrauma
|
||||
get { return map; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Which missions have been selected for the current round?
|
||||
/// </summary>
|
||||
public override IEnumerable<Mission> Missions
|
||||
{
|
||||
get
|
||||
@@ -112,10 +130,13 @@ namespace Barotrauma
|
||||
{
|
||||
if (Map.CurrentLocation != null)
|
||||
{
|
||||
var currentLevelData = Level.Loaded?.LevelData ?? GameMain.GameSession?.LevelData;
|
||||
foreach (Mission mission in map.CurrentLocation.SelectedMissions)
|
||||
{
|
||||
if (mission.Locations[0] == mission.Locations[1] ||
|
||||
mission.Locations.Contains(Map.SelectedLocation))
|
||||
if (//mission takes place in current location
|
||||
mission.Locations[0] == mission.Locations[1] ||
|
||||
//mission takes place between two locations, and we're in the level between those locations
|
||||
mission.Locations.Contains(Map.SelectedLocation) && currentLevelData is { Type: LevelData.LevelType.LocationConnection })
|
||||
{
|
||||
yield return mission;
|
||||
}
|
||||
@@ -156,10 +177,28 @@ namespace Barotrauma
|
||||
|
||||
if (GameMain.NetworkMember.GameStarted)
|
||||
{
|
||||
//allow managing if no-one with permissions is alive and in-game
|
||||
return GameMain.NetworkMember.ConnectedClients.None(c =>
|
||||
c.InGame && c.Character is { IsIncapacitated: false, IsDead: false } &&
|
||||
(IsOwner(c) || c.HasPermission(permissions)));
|
||||
bool someOneHasPermissions = GameMain.NetworkMember.ConnectedClients.Any(c => IsOwner(c) || c.HasPermission(permissions));
|
||||
if (someOneHasPermissions)
|
||||
{
|
||||
if (GameMain.GameSession != null && GameMain.GameSession.RoundDuration < 60.0f)
|
||||
{
|
||||
//round has been going on for less than a minute, don't allow anyone to manage just yet,
|
||||
//the people with permissions might still be loading or doing something in the lobby
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
//allow managing if the round has been going on for a while, and no-one with permissions is alive and in-game
|
||||
return GameMain.NetworkMember.ConnectedClients.None(c =>
|
||||
c.InGame && c.Character is { IsIncapacitated: false, IsDead: false } &&
|
||||
(IsOwner(c) || c.HasPermission(permissions)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//no-one in the server with permissions, allow anyone to manage
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -170,6 +209,7 @@ namespace Barotrauma
|
||||
protected CampaignMode(GameModePreset preset, CampaignSettings settings)
|
||||
: base(preset)
|
||||
{
|
||||
Settings = settings;
|
||||
Bank = new Wallet(Option<Character>.None())
|
||||
{
|
||||
Balance = settings.InitialMoney
|
||||
@@ -246,7 +286,7 @@ namespace Barotrauma
|
||||
sub != leavingSub &&
|
||||
!leavingSub.DockedTo.Contains(sub) &&
|
||||
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.IsRespawnShuttle &&
|
||||
(sub.AtEndExit != leavingSub.AtEndExit || sub.AtStartExit != leavingSub.AtStartExit));
|
||||
}
|
||||
|
||||
@@ -401,9 +441,9 @@ namespace Barotrauma
|
||||
currentLocation.DeselectMission(mission);
|
||||
}
|
||||
}
|
||||
if (levelData.HasBeaconStation && !levelData.IsBeaconActive && Missions.None(m => m.Prefab.Type == MissionType.Beacon))
|
||||
if (levelData.HasBeaconStation && !levelData.IsBeaconActive && Missions.None(m => m.Prefab.Type == Tags.MissionTypeBeacon))
|
||||
{
|
||||
var beaconMissionPrefabs = MissionPrefab.Prefabs.Where(m => m.IsSideObjective && m.Type == MissionType.Beacon);
|
||||
var beaconMissionPrefabs = MissionPrefab.Prefabs.Where(m => m.IsSideObjective && m.Type == Tags.MissionTypeBeacon);
|
||||
if (beaconMissionPrefabs.Any())
|
||||
{
|
||||
var filteredMissions = beaconMissionPrefabs.Where(m => levelData.Difficulty >= m.MinLevelDifficulty && levelData.Difficulty <= m.MaxLevelDifficulty);
|
||||
@@ -490,7 +530,7 @@ namespace Barotrauma
|
||||
if (missionPrefabs.Any())
|
||||
{
|
||||
var missionPrefab = ToolBox.SelectWeightedRandom(missionPrefabs, p => p.Commonness, rand);
|
||||
if (missionPrefab.Type == MissionType.Pirate && Missions.Any(m => m.Prefab.Type == MissionType.Pirate))
|
||||
if (missionPrefab.Type == Tags.MissionTypePirate && Missions.Any(m => m.Prefab.Type == Tags.MissionTypePirate))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -769,7 +809,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (var dockedSub in Level.Loaded.StartOutpost.DockedTo)
|
||||
{
|
||||
if (dockedSub == GameMain.NetworkMember?.RespawnManager?.RespawnShuttle || dockedSub.TeamID != submarineTeam) { continue; }
|
||||
if (dockedSub.IsRespawnShuttle || dockedSub.TeamID != submarineTeam) { continue; }
|
||||
return dockedSub.DockedTo.Contains(Submarine.MainSub) ? Submarine.MainSub : dockedSub;
|
||||
}
|
||||
}
|
||||
@@ -809,7 +849,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (var dockedSub in Level.Loaded.EndOutpost.DockedTo)
|
||||
{
|
||||
if (dockedSub == GameMain.NetworkMember?.RespawnManager?.RespawnShuttle || dockedSub.TeamID != submarineTeam) { continue; }
|
||||
if (dockedSub.IsRespawnShuttle || dockedSub.TeamID != submarineTeam) { continue; }
|
||||
return dockedSub.DockedTo.Contains(Submarine.MainSub) ? Submarine.MainSub : dockedSub;
|
||||
}
|
||||
}
|
||||
@@ -941,7 +981,9 @@ namespace Barotrauma
|
||||
{
|
||||
UpdateStoreStock();
|
||||
}
|
||||
GameMain.GameSession.EventManager?.RegisterEventHistory(registerFinishedOnly: true);
|
||||
|
||||
GameMain.GameSession.EndMissions();
|
||||
GameMain.GameSession.EventManager?.StoreEventDataAtRoundEnd(registerFinishedOnly: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1067,13 +1109,24 @@ namespace Barotrauma
|
||||
return false;
|
||||
}
|
||||
}
|
||||
var price = buyingNewCharacter ? NewCharacterCost(characterInfo) : HireManager.GetSalaryFor(characterInfo);
|
||||
int price = buyingNewCharacter ? NewCharacterCost(characterInfo) : HireManager.GetSalaryFor(characterInfo);
|
||||
if (takeMoney && !TryPurchase(client, price)) { return false; }
|
||||
|
||||
characterInfo.IsNewHire = true;
|
||||
characterInfo.Title = null;
|
||||
location.RemoveHireableCharacter(characterInfo);
|
||||
CrewManager.AddCharacterInfo(characterInfo);
|
||||
|
||||
if (GameMain.GameSession?.Campaign is MultiPlayerCampaign)
|
||||
{
|
||||
#if SERVER
|
||||
CrewManager.ToggleReserveBenchStatus(characterInfo, client, pendingHire: true, confirmPendingHire: true, sendUpdate: false);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
CrewManager.AddCharacterInfo(characterInfo);
|
||||
}
|
||||
|
||||
GameAnalyticsManager.AddMoneySpentEvent(characterInfo.Salary, GameAnalyticsManager.MoneySink.Crew, characterInfo.Job?.Prefab.Identifier.Value ?? "unknown");
|
||||
return true;
|
||||
}
|
||||
@@ -1092,6 +1145,7 @@ namespace Barotrauma
|
||||
private void NPCInteract(Character npc, Character interactor)
|
||||
{
|
||||
if (!npc.AllowCustomInteract) { return; }
|
||||
if (npc.AIController is HumanAIController humanAi && !humanAi.AllowCampaignInteraction()) { return; }
|
||||
NPCInteractProjSpecific(npc, interactor);
|
||||
string coroutineName = "DoCharacterWait." + (npc?.ID ?? Entity.NullEntityID);
|
||||
if (!CoroutineManager.IsCoroutineRunning(coroutineName))
|
||||
@@ -1287,7 +1341,7 @@ namespace Barotrauma
|
||||
return Faction.GetPlayerAffiliationStatus(faction);
|
||||
}
|
||||
|
||||
public abstract void Save(XElement element);
|
||||
public abstract void Save(XElement element, bool isSavingOnLoading);
|
||||
|
||||
protected void LoadStats(XElement element)
|
||||
{
|
||||
|
||||
@@ -140,13 +140,14 @@ namespace Barotrauma
|
||||
|
||||
private static readonly Dictionary<string, MultiplierSettings> _multiplierSettings = new Dictionary<string, MultiplierSettings>
|
||||
{
|
||||
{ "default", new MultiplierSettings { Min = 0.2f, Max = 2.0f, Step = 0.1f } },
|
||||
{ nameof(CrewVitalityMultiplier), new MultiplierSettings { Min = 0.5f, Max = 2.0f, Step = 0.1f } },
|
||||
{ nameof(NonCrewVitalityMultiplier),new MultiplierSettings { Min = 0.5f, Max = 3.0f, Step = 0.1f } },
|
||||
{ nameof(MissionRewardMultiplier), new MultiplierSettings { Min = 0.5f, Max = 2.0f, Step = 0.1f } },
|
||||
{ nameof(RepairFailMultiplier), new MultiplierSettings { Min = 0.5f, Max = 5.0f, Step = 0.5f } },
|
||||
{ nameof(ShopPriceMultiplier), new MultiplierSettings { Min = 0.1f, Max = 3.0f, Step = 0.1f } },
|
||||
{ nameof(ShipyardPriceMultiplier), new MultiplierSettings { Min = 0.1f, Max = 3.0f, Step = 0.1f } }
|
||||
{ "default", new MultiplierSettings { Min = 0.2f, Max = 2.0f, Step = 0.1f } },
|
||||
{ nameof(CrewVitalityMultiplier), new MultiplierSettings { Min = 0.5f, Max = 2.0f, Step = 0.1f } },
|
||||
{ nameof(NonCrewVitalityMultiplier), new MultiplierSettings { Min = 0.5f, Max = 3.0f, Step = 0.1f } },
|
||||
{ nameof(MissionRewardMultiplier), new MultiplierSettings { Min = 0.5f, Max = 2.0f, Step = 0.1f } },
|
||||
{ nameof(ExperienceRewardMultiplier), new MultiplierSettings { Min = 0.5f, Max = 2.0f, Step = 0.1f } },
|
||||
{ nameof(RepairFailMultiplier), new MultiplierSettings { Min = 0.5f, Max = 5.0f, Step = 0.5f } },
|
||||
{ nameof(ShopPriceMultiplier), new MultiplierSettings { Min = 0.1f, Max = 3.0f, Step = 0.1f } },
|
||||
{ nameof(ShipyardPriceMultiplier), new MultiplierSettings { Min = 0.1f, Max = 3.0f, Step = 0.1f } }
|
||||
// Add overrides for default values here
|
||||
};
|
||||
|
||||
@@ -165,6 +166,9 @@ namespace Barotrauma
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes), NetworkSerialize]
|
||||
public float MissionRewardMultiplier { get; set; }
|
||||
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes), NetworkSerialize]
|
||||
public float ExperienceRewardMultiplier { get; set; }
|
||||
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes), NetworkSerialize]
|
||||
public float ShopPriceMultiplier { get; set; }
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -7,6 +6,6 @@ namespace Barotrauma
|
||||
{
|
||||
public CoOpMode(GameModePreset preset, IEnumerable<MissionPrefab> missionPrefabs) : base(preset, ValidateMissionPrefabs(missionPrefabs, MissionPrefab.CoOpMissionClasses)) { }
|
||||
|
||||
public CoOpMode(GameModePreset preset, MissionType missionType, string seed) : base(preset, ValidateMissionType(missionType, MissionPrefab.CoOpMissionClasses), seed) { }
|
||||
public CoOpMode(GameModePreset preset, IEnumerable<Identifier> missionTypes, string seed) : base(preset, ValidateMissionTypes(missionTypes, MissionPrefab.CoOpMissionClasses), seed) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -25,42 +26,37 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public MissionMode(GameModePreset preset, MissionType missionType, string seed)
|
||||
public MissionMode(GameModePreset preset, IEnumerable<Identifier> missionTypes, string seed)
|
||||
: base(preset)
|
||||
{
|
||||
Location[] locations = { GameMain.GameSession.StartLocation, GameMain.GameSession.EndLocation };
|
||||
var mission = Mission.LoadRandom(locations, seed, requireCorrectLocationType: false, missionType, difficultyLevel: GameMain.NetworkMember.ServerSettings.SelectedLevelDifficulty);
|
||||
var mission = Mission.LoadRandom(locations, seed, requireCorrectLocationType: false, missionTypes, difficultyLevel: GameMain.NetworkMember.ServerSettings.SelectedLevelDifficulty);
|
||||
if (mission != null)
|
||||
{
|
||||
missions.Add(mission);
|
||||
}
|
||||
}
|
||||
|
||||
protected static IEnumerable<MissionPrefab> ValidateMissionPrefabs(IEnumerable<MissionPrefab> missionPrefabs, Dictionary<MissionType, Type> missionClasses)
|
||||
protected static IEnumerable<MissionPrefab> ValidateMissionPrefabs(IEnumerable<MissionPrefab> missionPrefabs, Dictionary<Identifier, Type> missionClasses)
|
||||
{
|
||||
foreach (MissionPrefab missionPrefab in missionPrefabs)
|
||||
{
|
||||
if (ValidateMissionType(missionPrefab.Type, missionClasses) != missionPrefab.Type)
|
||||
if (!missionClasses.ContainsValue(missionPrefab.MissionClass))
|
||||
{
|
||||
throw new InvalidOperationException("Cannot start gamemode with mission type " + missionPrefab.Type);
|
||||
throw new InvalidOperationException($"Cannot start gamemode with a {missionPrefab.MissionClass} mission.");
|
||||
}
|
||||
}
|
||||
return missionPrefabs;
|
||||
}
|
||||
|
||||
protected static MissionType ValidateMissionType(MissionType missionType, Dictionary<MissionType, Type> missionClasses)
|
||||
/// <summary>
|
||||
/// Returns the mission types that are valid for the given mission classes (e.g. all mission types suitable for the PvP mission classes).
|
||||
/// </summary>
|
||||
public static IEnumerable<Identifier> ValidateMissionTypes(IEnumerable<Identifier> missionTypes, Dictionary<Identifier, Type> missionClasses)
|
||||
{
|
||||
var missionTypes = (MissionType[])Enum.GetValues(typeof(MissionType));
|
||||
for (int i = 0; i < missionTypes.Length; i++)
|
||||
{
|
||||
var type = missionTypes[i];
|
||||
if (type == MissionType.None || type == MissionType.All) { continue; }
|
||||
if (!missionClasses.ContainsKey(type))
|
||||
{
|
||||
missionType &= ~(type);
|
||||
}
|
||||
}
|
||||
return missionType;
|
||||
return missionTypes.Where(type =>
|
||||
MissionPrefab.Prefabs.OrderBy(missionPrefab => missionPrefab.UintIdentifier)
|
||||
.Any(missionPrefab => missionPrefab.Type == type && missionClasses.ContainsValue(missionPrefab.MissionClass)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+21
-9
@@ -92,6 +92,11 @@ namespace Barotrauma
|
||||
get; set;
|
||||
}
|
||||
|
||||
public byte RoundID
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
private MultiPlayerCampaign(CampaignSettings settings) : base(GameModePreset.MultiPlayerCampaign, settings)
|
||||
{
|
||||
currentCampaignID++;
|
||||
@@ -116,7 +121,6 @@ namespace Barotrauma
|
||||
//only the server generates the map, the clients load it from a save file
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
campaign.Settings = settings;
|
||||
campaign.map = new Map(campaign, mapSeed);
|
||||
}
|
||||
campaign.InitProjSpecific();
|
||||
@@ -133,16 +137,24 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
partial void InitProjSpecific();
|
||||
|
||||
public static string GetCharacterDataSavePath(string savePath)
|
||||
|
||||
public static string GetCharacterDataSavePath(string loadPath)
|
||||
{
|
||||
return Path.Combine(Path.GetDirectoryName(savePath), Path.GetFileNameWithoutExtension(savePath) + "_CharacterData.xml");
|
||||
string directory = Path.GetDirectoryName(loadPath);
|
||||
string fileName = Path.GetFileNameWithoutExtension(loadPath);
|
||||
|
||||
if (CampaignDataPath.IsBackupPath(loadPath, out uint backupIndex))
|
||||
{
|
||||
string trimmedFileName = Path.GetFileNameWithoutExtension(fileName);
|
||||
return Path.Combine(directory, $"{trimmedFileName}_CharacterData{SaveUtil.BackupCharacterDataExtensionStart}{backupIndex}");
|
||||
}
|
||||
return Path.Combine(directory, $"{fileName}_CharacterData.xml");
|
||||
}
|
||||
|
||||
public static string GetCharacterDataSavePath()
|
||||
{
|
||||
return GetCharacterDataSavePath(GameMain.GameSession.SavePath);
|
||||
}
|
||||
public static string GetCharacterDataPathForLoading()
|
||||
=> GetCharacterDataSavePath(GameMain.GameSession.DataPath.LoadPath);
|
||||
public static string GetCharacterDataPathForSaving()
|
||||
=> GetCharacterDataSavePath(GameMain.GameSession.DataPath.SavePath);
|
||||
|
||||
/// <summary>
|
||||
/// Loads the campaign from an XML element. Creates the map if it hasn't been created yet, otherwise updates the state of the map.
|
||||
@@ -254,7 +266,7 @@ namespace Barotrauma
|
||||
|
||||
#if SERVER
|
||||
characterData.Clear();
|
||||
string characterDataPath = GetCharacterDataSavePath();
|
||||
string characterDataPath = GetCharacterDataPathForLoading();
|
||||
if (!File.Exists(characterDataPath))
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to load the character data for the campaign. Could not find the file \"{characterDataPath}\".");
|
||||
|
||||
@@ -1,50 +1,27 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class PvPMode : MissionMode
|
||||
partial class PvPMode : MissionMode
|
||||
{
|
||||
public PvPMode(GameModePreset preset, IEnumerable<MissionPrefab> missionPrefabs) : base(preset, ValidateMissionPrefabs(missionPrefabs, MissionPrefab.PvPMissionClasses)) { }
|
||||
|
||||
public PvPMode(GameModePreset preset, MissionType missionType, string seed) : base(preset, ValidateMissionType(missionType, MissionPrefab.PvPMissionClasses), seed) { }
|
||||
|
||||
public void AssignTeamIDs(IEnumerable<Client> clients)
|
||||
public PvPMode(GameModePreset preset, IEnumerable<MissionPrefab> missionPrefabs) :
|
||||
base(preset, ValidateMissionPrefabs(missionPrefabs, MissionPrefab.PvPMissionClasses))
|
||||
{
|
||||
int team1Count = 0, team2Count = 0;
|
||||
//if a client has a preference, assign them to that team
|
||||
List<Client> unassignedClients = new List<Client>(clients);
|
||||
for (int i = 0; i < unassignedClients.Count; i++)
|
||||
if (Missions.None())
|
||||
{
|
||||
if (unassignedClients[i].PreferredTeam == CharacterTeamType.Team1 ||
|
||||
unassignedClients[i].PreferredTeam == CharacterTeamType.Team2)
|
||||
{
|
||||
assignTeam(unassignedClients[i], unassignedClients[i].PreferredTeam);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
//assign the rest of the clients to the team that has the least players
|
||||
while (unassignedClients.Any())
|
||||
{
|
||||
var randomClient = unassignedClients.GetRandom(Rand.RandSync.Unsynced);
|
||||
assignTeam(randomClient, team1Count < team2Count ? CharacterTeamType.Team1 : CharacterTeamType.Team2);
|
||||
throw new System.Exception($"Attempted to start {nameof(PvPMode)} without a mission.");
|
||||
}
|
||||
}
|
||||
|
||||
void assignTeam(Client client, CharacterTeamType team)
|
||||
public PvPMode(GameModePreset preset, IEnumerable<Identifier> missionTypes, string seed) :
|
||||
base(preset, ValidateMissionTypes(missionTypes, MissionPrefab.PvPMissionClasses), seed)
|
||||
{
|
||||
if (Missions.None())
|
||||
{
|
||||
client.TeamID = team;
|
||||
unassignedClients.Remove(client);
|
||||
if (team == CharacterTeamType.Team1)
|
||||
{
|
||||
team1Count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
team2Count++;
|
||||
}
|
||||
throw new System.Exception($"Attempted to start {nameof(PvPMode)} without a mission.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class TestGameMode : GameMode
|
||||
{
|
||||
public TestGameMode(GameModePreset preset) : base(preset)
|
||||
{
|
||||
foreach (JobPrefab jobPrefab in JobPrefab.Prefabs.OrderBy(p => p.Identifier))
|
||||
{
|
||||
for (int i = 0; i < jobPrefab.InitialCount; i++)
|
||||
{
|
||||
var variant = Rand.Range(0, jobPrefab.Variants);
|
||||
CrewManager.AddCharacterInfo(new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: jobPrefab, variant: variant));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -58,7 +58,8 @@ namespace Barotrauma
|
||||
void AddCharacter(JobPrefab job)
|
||||
{
|
||||
if (job == null) { return; }
|
||||
int variant = Rand.Range(0, job.Variants, Rand.RandSync.ServerAndClient);
|
||||
//no need for synced rand, these only generate ones and are then included in the campaign save
|
||||
int variant = Rand.Range(0, job.Variants, Rand.RandSync.Unsynced);
|
||||
AvailableCharacters.Add(new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: job, variant: variant));
|
||||
}
|
||||
}
|
||||
@@ -73,7 +74,8 @@ namespace Barotrauma
|
||||
DebugConsole.ThrowError($"Couldn't create a hireable for the location: character prefab \"{character.NPCIdentifier}\" not found in the NPC set \"{character.NPCSetIdentifier}\".");
|
||||
continue;
|
||||
}
|
||||
var characterInfo = humanPrefab.CreateCharacterInfo(Rand.RandSync.ServerAndClient);
|
||||
//no need for synced rand, these only generate ones and are then included in the campaign save
|
||||
var characterInfo = humanPrefab.CreateCharacterInfo(Rand.RandSync.Unsynced);
|
||||
characterInfo.MinReputationToHire = (faction.Identifier, character.MinReputation);
|
||||
AvailableCharacters.Add(characterInfo);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user