using Barotrauma.Networking; using System; using System.Collections.Generic; using System.Linq; namespace Barotrauma { partial class Traitor { public class Objective { public Traitor Traitor { get; private set; } private int shuffleGoalsCount; private readonly List allGoals = new List(); private readonly List activeGoals = new List(); private readonly List pendingGoals = new List(); private readonly List completedGoals = new List(); public bool IsCompleted => pendingGoals.Count <= 0; public bool IsPartiallyCompleted => completedGoals.Count > 0; public bool IsStarted { get; private set; } = false; public bool CanBeStarted(ICollection traitors) => !IsStarted && allGoals.Any(goal => goal.CanBeCompleted(traitors)); public bool CanBeCompleted => !IsStarted || pendingGoals.All(goal => goal.CanBeCompleted(goal.Traitors)); public bool IsEnemy(Character character) => pendingGoals.Any(goal => goal.IsEnemy(character)); public bool IsAllowedToDamage(Structure structure) => pendingGoals.Any(goal => goal.IsAllowedToDamage(structure)); public readonly HashSet Roles = new HashSet(); public string InfoText { get; private set; } public virtual string GoalInfoFormatId { get; set; } = "TraitorObjectiveGoalInfoFormat"; public string GoalInfos => string.Join("/", string.Join("/", activeGoals.Select((goal, index) => { var statusText = goal.StatusText(Traitor); var startIndex = statusText.LastIndexOf('/') + 1; return $"{statusText.Substring(0, startIndex)}[{index}.st]={statusText.Substring(startIndex)}/[{index}.sl]={TextManager.FormatServerMessage(GoalInfoFormatId, ("[statustext]", $"[{index}.st]"))}"; }).ToArray()), string.Join("", activeGoals.Select((goal, index) => $"[{index}.sl]").ToArray())); public string AllGoalInfos => string.Join("/", string.Join("/", allGoals.Select((goal, index) => { var statusText = goal.StatusText(Traitor); var startIndex = statusText.LastIndexOf('/') + 1; return $"{statusText.Substring(0, startIndex)}[{index}.st]={statusText.Substring(startIndex)}/[{index}.sl]={TextManager.FormatServerMessage(GoalInfoFormatId, ("[statustext]", $"[{index}.st]"))}"; }).ToArray()), string.Join("", allGoals.Select((goal, index) => $"[{index}.sl]").ToArray())); public virtual string StartMessageTextId { get; set; } = "TraitorObjectiveStartMessage"; public virtual IEnumerable StartMessageKeys => new string[] { "[traitorgoalinfos]" }; public virtual IEnumerable StartMessageValues => new string[] { GoalInfos }; public virtual LocalizedString StartMessageText => TextManager.FormatServerMessageWithPronouns(Traitor.Character.Info, StartMessageTextId, StartMessageKeys.Zip(StartMessageValues, (k,v) => (k,v)).ToArray()); public virtual string StartMessageServerTextId { get; set; } = "TraitorObjectiveStartMessageServer"; public virtual IEnumerable StartMessageServerKeys => StartMessageKeys.Concat(new string[] { "[traitorname]" }); public virtual IEnumerable StartMessageServerValues => StartMessageValues.Concat(new string[] { Traitor?.Character?.Name ?? "(unknown)" }); public virtual LocalizedString StartMessageServerText => TextManager.FormatServerMessageWithPronouns(Traitor.Character.Info, StartMessageServerTextId, StartMessageServerKeys.Zip(StartMessageServerValues, (k,v) => (k,v)).ToArray()); public virtual string EndMessageSuccessTextId { get; set; } = "TraitorObjectiveEndMessageSuccess"; public virtual string EndMessageSuccessDeadTextId { get; set; } = "TraitorObjectiveEndMessageSuccessDead"; public virtual string EndMessageSuccessDetainedTextId { get; set; } = "TraitorObjectiveEndMessageSuccessDetained"; public virtual string EndMessageFailureTextId { get; set; } = "TraitorObjectiveEndMessageFailure"; public virtual string EndMessageFailureDeadTextId { get; set; } = "TraitorObjectiveEndMessageFailureDead"; public virtual string EndMessageFailureDetainedTextId { get; set; } = "TraitorObjectiveEndMessageFailureDetained"; public virtual IEnumerable EndMessageKeys => new string[] { "[traitorname]", "[traitorgoalinfos]" }; public virtual IEnumerable EndMessageValues => new string[] { Traitor?.Character?.Name ?? "(unknown)", GoalInfos }; public virtual string EndMessageText { get { var traitorIsDead = Traitor.Character.IsDead; var traitorIsDetained = Traitor.Character.LockHands; var messageId = IsCompleted ? (traitorIsDead ? EndMessageSuccessDeadTextId : traitorIsDetained ? EndMessageSuccessDetainedTextId : EndMessageSuccessTextId) : (traitorIsDead ? EndMessageFailureDeadTextId : traitorIsDetained ? EndMessageFailureDetainedTextId : EndMessageFailureTextId); return TextManager.FormatServerMessageWithPronouns(Traitor.Character.Info, messageId, EndMessageKeys.Zip(EndMessageValues, (k,v)=>(k,v)).ToArray()); } } public bool Start(Traitor traitor) { Traitor = traitor; activeGoals.Clear(); pendingGoals.Clear(); completedGoals.Clear(); var allGoalsCount = allGoals.Count; var indices = allGoals.Select((goal, index) => index).ToArray(); if (shuffleGoalsCount > 0) { for (var i = allGoalsCount; i > 1;) { int j = TraitorManager.RandomInt(i--); var temp = indices[j]; indices[j] = indices[i]; indices[i] = temp; } } for (var i = 0; i < allGoalsCount; ++i) { var goal = allGoals[indices[i]]; if (goal.Start(traitor)) { activeGoals.Add(goal); pendingGoals.Add(goal); if (shuffleGoalsCount > 0 && pendingGoals.Count >= shuffleGoalsCount) { break; } } else { completedGoals.Add(goal); } } if (pendingGoals.Count <= 0 && completedGoals.Count < allGoals.Count) { return false; } IsStarted = true; traitor.SendChatMessageBox(StartMessageText.Value, traitor.Mission.Identifier); traitor.UpdateCurrentObjective(GoalInfos, traitor.Mission.Identifier); return true; } public void StartMessage() { Traitor.SendChatMessage(StartMessageText.Value, Traitor.Mission.Identifier); } public void EndMessage() { Traitor.SendChatMessageBox(EndMessageText, Traitor.Mission.Identifier); Traitor.SendChatMessage(EndMessageText, Traitor.Mission.Identifier); } public void Update(float deltaTime) { if (!IsStarted) { return; } for (int i = 0; i < pendingGoals.Count;) { var goal = pendingGoals[i]; goal.Update(deltaTime); if (!goal.IsCompleted) { ++i; } else { completedGoals.Add(goal); pendingGoals.RemoveAt(i); if (GameMain.Server != null) { Traitor.SendChatMessage(goal.CompletedText(Traitor), Traitor.Mission.Identifier); if (pendingGoals.Count > 0) { Traitor.SendChatMessageBox(goal.CompletedText(Traitor), Traitor.Mission.Identifier); } Traitor.UpdateCurrentObjective(GoalInfos, Traitor.Mission.Identifier); } } } } public Objective(string infoText, int shuffleGoalsCount, ICollection roles, ICollection goals) { InfoText = infoText; this.shuffleGoalsCount = shuffleGoalsCount; Roles.UnionWith(roles); allGoals.AddRange(goals); } } } }