Files
2025-03-12 12:56:27 +00:00

178 lines
9.3 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using Barotrauma.Networking;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
partial class CrewManager
{
partial void CreateRandomConversation()
{
List<Character> availableSpeakers = Character.CharacterList.FindAll(c =>
c.AIController is HumanAIController &&
!c.IsDead &&
c.SpeechImpediment <= 100.0f);
foreach (Client client in GameMain.Server.ConnectedClients)
{
if (client.Character != null) availableSpeakers.Remove(client.Character);
}
pendingConversationLines.AddRange(NPCConversation.CreateRandom(availableSpeakers));
}
/// <summary>
/// Saves bots in multiplayer
/// </summary>
public XElement SaveMultiplayer(XElement parentElement)
{
var element = new XElement("bots", new XAttribute("hasbots", HasBots));
foreach (CharacterInfo info in GetCharacterInfos(includeReserveBench: true))
{
if (Level.Loaded != null)
{
//new hires and reserve benched CharacterInfos should be saved even though the Character doesn't exist
if (!info.IsNewHire && !info.IsOnReserveBench)
{
//character being null either means the character has been removed, or that it hasn't spawn yet
if (info.Character == null && !info.PendingSpawnToActiveService) { continue; }
if (info.Character is { IsDead: true }) { continue; }
}
}
XElement characterElement = info.Save(element);
if (info.InventoryData != null) { characterElement.Add(info.InventoryData); }
if (info.HealthData != null) { characterElement.Add(info.HealthData); }
if (info.OrderData != null) { characterElement.Add(info.OrderData); }
}
parentElement?.Add(element);
return element;
}
public void ServerWriteActiveOrders(IWriteMessage msg)
{
ushort count = (ushort)ActiveOrders.Count(o => o.Order != null && !o.FadeOutTime.HasValue);
msg.WriteUInt16(count);
if (count > 0)
{
foreach (var activeOrder in ActiveOrders)
{
if (!(activeOrder?.Order is Order order) || activeOrder.FadeOutTime.HasValue) { continue; }
OrderChatMessage.WriteOrder(msg, order, null, isNewOrder: true);
bool hasOrderGiver = order.OrderGiver != null;
msg.WriteBoolean(hasOrderGiver);
if (hasOrderGiver)
{
msg.WriteUInt16(order.OrderGiver.ID);
}
}
}
}
public void ReadToggleReserveBenchMessage(IReadMessage inc, Client sender)
{
UInt16 botId = inc.ReadUInt16();
bool pendingHire = inc.ReadBoolean();
if (GameMain.GameSession?.GameMode is not MultiPlayerCampaign mpCampaign) { return; }
if (!CampaignMode.AllowedToManageCampaign(sender, ClientPermissions.ManageHires))
{
DebugConsole.NewMessage($"Client {sender.Name} is not allowed to modify the reserve bench status of bots (requires ManageHires)");
return;
}
if (pendingHire && mpCampaign.Map.CurrentLocation?.HireManager.PendingHires.FirstOrDefault(ci => ci.ID == botId) is CharacterInfo pendingCharacterInfo)
{
ToggleReserveBenchStatus(pendingCharacterInfo, sender, pendingHire: true);
}
else if (GameMain.GameSession.CrewManager?.GetCharacterInfos(includeReserveBench: true)?.FirstOrDefault(i => i.ID == botId) is CharacterInfo characterInfo)
{
ToggleReserveBenchStatus(characterInfo, sender);
}
}
/// <summary>
/// Used to correctly handle (and document) transitions between the different possible statuses (BotStatus) bots might have
/// relating to the reserve bench, assigning them the correct new status and into the right CrewManager lists.
/// This will only take care of things relevant to the CrewManager (like maximum crew size), and will assume requirements
/// to hiring (money, permissions) have already been handled.
/// </summary>
/// <param name="characterInfo">CharacterInfo of the bot</param>
/// <param name="client">Which client requested changing the reserve bench status?</param>
/// <param name="pendingHire">Is the bot a pending hire?</param>
/// <param name="confirmPendingHire">Has the hire been confirmed now? This will store the bot in the CrewManager.</param>
/// <param name="sendUpdate">By default, the method will trigger sending updated crew data to the clients, but this may not always be useful eg. if this method is called as part of a longer procedure that will send the update in the end anyway.</param>
public void ToggleReserveBenchStatus(CharacterInfo characterInfo, Client client, bool pendingHire = false, bool confirmPendingHire = false, bool sendUpdate = true)
{
if (GameMain.GameSession?.GameMode is not MultiPlayerCampaign mpCampaign) { return; }
if (confirmPendingHire && !pendingHire)
{
DebugConsole.ThrowError($"ToggleReserveBenchStatus: cannot confirm a hire that is not pending (bot {characterInfo.DisplayName})");
}
BotStatus currentStatus = characterInfo.BotStatus;
if (pendingHire && !confirmPendingHire)
{
if (!(mpCampaign.Map.CurrentLocation?.HireManager.PendingHires.Contains(characterInfo) ?? false))
{
DebugConsole.ThrowError($"ToggleReserveBenchStatus: bot {characterInfo.DisplayName} is supposed to be in the pending hires list, but can't be found there");
}
if (currentStatus == BotStatus.PendingHireToActiveService)
{
characterInfo.BotStatus = BotStatus.PendingHireToReserveBench;
GameServer.Log($"Client \"{client.Name}\" moved the pending hire \"{characterInfo.DisplayName}\" to the reserve bench.", ServerLog.MessageType.ServerMessage);
}
else if (currentStatus == BotStatus.PendingHireToReserveBench)
{
if (GetCharacterInfos().Count() >= MaxCrewSize)
{
DebugConsole.NewMessage($"ToggleReserveBenchStatus: Tried moving pending hire {characterInfo.DisplayName} to active service, but MaxCrewSize has already been reached");
return;
}
characterInfo.BotStatus = BotStatus.PendingHireToActiveService;
GameServer.Log($"Client \"{client.Name}\" moved the pending hire \"{characterInfo.DisplayName}\" from the reserve bench to active service.", ServerLog.MessageType.ServerMessage);
}
}
else if (GetCharacterInfos(includeReserveBench: true).Contains(characterInfo) || confirmPendingHire)
{
if (currentStatus == BotStatus.ActiveService || (confirmPendingHire && currentStatus == BotStatus.PendingHireToReserveBench))
{
if (reserveBench.Contains(characterInfo))
{
DebugConsole.ThrowError($"ToggleReserveBenchStatus: Tried to add the same CharacterInfo ({characterInfo.DisplayName}) to reserve bench twice");
}
RemoveCharacterInfo(characterInfo);
characterInfo.BotStatus = BotStatus.ReserveBench;
GameServer.Log($"Client \"{client.Name}\" moved the bot \"{characterInfo.DisplayName}\" from active service to the reserve bench.", ServerLog.MessageType.ServerMessage);
reserveBench.Add(characterInfo);
}
else if (currentStatus == BotStatus.ReserveBench || (confirmPendingHire && currentStatus == BotStatus.PendingHireToActiveService))
{
if (GetCharacterInfos().Count() >= MaxCrewSize)
{
DebugConsole.NewMessage($"ToggleReserveBenchStatus: Tried moving {characterInfo.DisplayName} to active service, but MaxCrewSize has already been reached");
return;
}
RemoveCharacterInfo(characterInfo);
characterInfo.BotStatus = BotStatus.ActiveService;
GameServer.Log($"Client \"{client.Name}\" moved the bot \"{characterInfo.DisplayName}\" from the reserve bench to active service.", ServerLog.MessageType.ServerMessage);
AddCharacterInfo(characterInfo);
}
}
else
{
DebugConsole.ThrowError($"ToggleReserveBenchStatus: bot {characterInfo.DisplayName} not found from CrewManager");
}
if (sendUpdate)
{
mpCampaign.SendCrewState();
}
}
}
}