diff --git a/Barotrauma/BarotraumaShared/BarotraumaShared.projitems b/Barotrauma/BarotraumaShared/BarotraumaShared.projitems
index b2beaca84..0f3ac76c9 100644
--- a/Barotrauma/BarotraumaShared/BarotraumaShared.projitems
+++ b/Barotrauma/BarotraumaShared/BarotraumaShared.projitems
@@ -222,6 +222,9 @@
PreserveNewest
+
+ PreserveNewest
+
PreserveNewest
diff --git a/Barotrauma/BarotraumaShared/Content/CodeWords.txt b/Barotrauma/BarotraumaShared/Content/CodeWords.txt
new file mode 100644
index 000000000..4ebcad52f
--- /dev/null
+++ b/Barotrauma/BarotraumaShared/Content/CodeWords.txt
@@ -0,0 +1,76 @@
+carrier
+charybdis
+coelanth
+crawler
+endworm
+guardian
+husk
+mantis
+moloch
+thresher
+watcher
+water
+submarine
+red
+green
+blue
+yellow
+toxic
+explosive
+combustible
+crate
+renegade
+ally
+teamwork
+railgun
+nonsense
+abyss
+artifact
+party
+signal
+airlock
+scooter
+ruins
+C4
+Compound-N
+hyperzine
+plasma
+morbusine
+oxygenite
+revolver
+harpoon
+ocean
+wrench
+door
+man
+comrade
+mask
+diving
+monitor
+lights
+battery
+junction
+button
+locker
+crate
+honk
+clown
+horn
+grenade
+stun
+baton
+circuit
+death
+life
+fish
+shark
+damage
+ruins
+shaft
+aft
+starboard
+ice
+cold
+hot
+fire
+extinguisher
\ No newline at end of file
diff --git a/Barotrauma/BarotraumaShared/Source/DebugConsole.cs b/Barotrauma/BarotraumaShared/Source/DebugConsole.cs
index ce339de4c..9c1fd0d0c 100644
--- a/Barotrauma/BarotraumaShared/Source/DebugConsole.cs
+++ b/Barotrauma/BarotraumaShared/Source/DebugConsole.cs
@@ -102,6 +102,17 @@ namespace Barotrauma
NewMessage("***************", Color.Cyan);
}));
+ commands.Add(new Command("traitorlist", "traitorlist: List all the traitors and their targets.", (string[] args) =>
+ {
+ if (GameMain.Server == null) return;
+ TraitorManager traitorManager = GameMain.Server.TraitorManager;
+ if (traitorManager == null) return;
+ foreach (Traitor t in traitorManager.TraitorList)
+ {
+ NewMessage("- Traitor " + t.Character.Name + "'s target is " + t.TargetCharacter.Name + ".", Color.Cyan);
+ }
+ NewMessage("The code words are: " + traitorManager.codeWords + ", response: " + traitorManager.codeResponse + ".", Color.Cyan);
+ }));
commands.Add(new Command("createfilelist", "", (string[] args) =>
{
diff --git a/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/TraitorManager.cs b/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/TraitorManager.cs
index 26db8d153..027561f23 100644
--- a/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/TraitorManager.cs
+++ b/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/TraitorManager.cs
@@ -1,106 +1,203 @@
using Barotrauma.Networking;
using System.Collections.Generic;
+using System.IO;
namespace Barotrauma
{
- partial class TraitorManager
+ partial class Traitor
{
- public Character TraitorCharacter
+ public readonly Character Character;
+ public Character TargetCharacter; //TODO: make a modular objective system (similar to crew missions) that allows for things OTHER than assasinations.
+
+ public Traitor(Character character)
{
- get { return traitorCharacter; }
+ Character = character;
}
- public Character TargetCharacter
+ public void Greet(GameServer server, string codeWords, string codeResponse)
{
- get { return targetCharacter; }
- }
+ //Greeting messages TODO: Move this to a function in Traitor class
+ string greetingMessage = "You are the Traitor! Your secret task is to assassinate " + TargetCharacter.Name + "! Discretion is an utmost concern; sinking the submarine and killing the entire crew "
+ + "will arouse suspicion amongst the Fleet. If possible, make the death look like an accident.";
+ string moreAgentsMessage = "It is possible that there are other agents on this submarine. You don't know their names, but you do have a method of communication. "
+ + "Use the code words to greet the agent and code response to respond. Disguise such words in a normal-looking phrase so the crew doesn't suspect anything.";
+ moreAgentsMessage += "\nThe code words are: " + codeWords + ".";
+ moreAgentsMessage += "\nThe code response is: " + codeResponse + ".\n";
- private Character traitorCharacter, targetCharacter;
-
- public TraitorManager(GameServer server)
- {
- Start(server);
- }
-
- private void Start(GameServer server)
- {
- if (server == null) return;
-
- List characters = new List();
- foreach (Client client in server.ConnectedClients)
+ if (server.Character != Character)
{
- if (client.Character != null)
- characters.Add(client.Character);
+ var chatMsg = ChatMessage.Create(
+ null,
+ greetingMessage + "\n" + moreAgentsMessage,
+ (ChatMessageType)ChatMessageType.Server,
+ null);
+
+ var msgBox = ChatMessage.Create(
+ null,
+ "There might be other agents. Use these to communicate with them." +
+ "\nThe code words are: " + codeWords + "." +
+ "\nThe code response is: " + codeResponse + ".",
+ (ChatMessageType)ChatMessageType.MessageBox,
+ null);
+
+ Client client = server.ConnectedClients.Find(c => c.Character == Character);
+ GameMain.Server.SendChatMessage(chatMsg, client);
+ GameMain.Server.SendChatMessage(msgBox, client);
}
- if (server.Character!= null) characters.Add(server.Character);
-
- if (characters.Count < 2)
- {
- traitorCharacter = null;
- targetCharacter = null;
- return;
- }
-
- int traitorIndex = Rand.Range(0, characters.Count);
-
- int targetIndex = Rand.Range(0, characters.Count);
- while (targetIndex == traitorIndex)
- {
- targetIndex = Rand.Range(0, characters.Count);
- }
-
- traitorCharacter = characters[traitorIndex];
- targetCharacter = characters[targetIndex];
-
#if CLIENT
if (server.Character == null)
{
- new GUIMessageBox("New traitor", traitorCharacter.Name + " is the traitor and the target is " + targetCharacter.Name+".");
+ new GUIMessageBox("New traitor", Character.Name + " is the traitor and the target is " + TargetCharacter.Name+".");
}
- else if (server.Character == traitorCharacter)
+ else if (server.Character == Character)
{
- CreateStartPopUp(targetCharacter.Name);
+ TraitorManager.CreateStartPopUp(TargetCharacter.Name);
+ GameMain.NetworkMember.AddChatMessage(moreAgentsMessage, ChatMessageType.Server);
return;
}
#endif
}
+ }
+
+ partial class TraitorManager
+ {
+ private static string wordsTxt = Path.Combine("Content", "CodeWords.txt");
+
+ public List TraitorList
+ {
+ get { return traitorList; }
+ }
+
+ private List traitorList = new List();
+
+ public string codeWords, codeResponse;
+
+ public TraitorManager(GameServer server, int traitorCount)
+ {
+ if (traitorCount < 1) //what why how
+ {
+ traitorCount = 1;
+ DebugConsole.ThrowError("Traitor Manager: TraitorCount somehow ended up less than 1, setting it to 1.");
+ }
+ Start(server, traitorCount);
+ }
+
+ private void Start(GameServer server, int traitorCount)
+ {
+ if (server == null) return;
+
+ List characters = new List(); //ANYONE can be a target.
+ List traitorCandidates = new List(); //Keep this to not re-pick traitors twice
+ foreach (Client client in server.ConnectedClients)
+ {
+ if (client.Character != null)
+ {
+ characters.Add(client.Character);
+ traitorCandidates.Add(client.Character);
+ }
+ }
+
+ if (server.Character != null)
+ {
+ characters.Add(server.Character); //Add host character
+ traitorCandidates.Add(server.Character);
+ }
+
+ if (characters.Count < 2)
+ {
+ return;
+ }
+
+ codeWords = ToolBox.GetRandomLine(wordsTxt) + ", " + ToolBox.GetRandomLine(wordsTxt);
+ codeResponse = ToolBox.GetRandomLine(wordsTxt) + ", " + ToolBox.GetRandomLine(wordsTxt);
+
+ while (traitorCount-- >= 0)
+ {
+ if (traitorCandidates.Count <= 0)
+ break;
+
+ int traitorIndex = Rand.Int(traitorCandidates.Count);
+ Character traitorCharacter = traitorCandidates[traitorIndex];
+ traitorCandidates.Remove(traitorCharacter);
+
+ //Add them to the list
+ traitorList.Add(new Traitor(traitorCharacter));
+ }
+
+ //Now that traitors have been decided, let's do objectives in post for deciding things like Document Exchange.
+ foreach (Traitor traitor in traitorList)
+ {
+ Character traitorCharacter = traitor.Character;
+ int targetIndex = Rand.Int(characters.Count);
+ while (characters[targetIndex] == traitorCharacter) //Cannot target self
+ {
+ targetIndex = Rand.Int(characters.Count);
+ }
+
+ Character targetCharacter = characters[targetIndex];
+ traitor.TargetCharacter = targetCharacter;
+ traitor.Greet(server, codeWords, codeResponse);
+ }
+ }
public string GetEndMessage()
{
- if (GameMain.Server == null || traitorCharacter == null || targetCharacter == null) return "";
+ if (GameMain.Server == null || traitorList.Count <= 0) return "";
string endMessage = "";
- if (targetCharacter.IsDead && !traitorCharacter.IsDead)
+ foreach (Traitor traitor in traitorList)
{
- endMessage = traitorCharacter.Name + " was a traitor! ";
+ Character traitorCharacter = traitor.Character;
+ Character targetCharacter = traitor.TargetCharacter;
+ endMessage += traitorCharacter.Name + " was a traitor! ";
endMessage += (traitorCharacter.Info.Gender == Gender.Male) ? "His" : "Her";
- endMessage += " task was to assassinate " + targetCharacter.Name + ". The task was successful.";
- }
- else if (targetCharacter.IsDead && traitorCharacter.IsDead)
- {
- endMessage = traitorCharacter.Name + " was a traitor! ";
- endMessage += (traitorCharacter.Info.Gender == Gender.Male) ? "His" : "Her";
- endMessage += " task was to assassinate " + targetCharacter.Name + ". The task was successful, but luckily the bastard didn't make it out alive either.";
- }
- else if (traitorCharacter.IsDead)
- {
- endMessage = traitorCharacter.Name + " was a traitor! ";
- endMessage += (traitorCharacter.Info.Gender == Gender.Male) ? "His" : "Her";
- endMessage += " task was to assassinate " + targetCharacter.Name + ", but ";
- endMessage += (traitorCharacter.Info.Gender == Gender.Male) ? "he" : "she";
- endMessage += " got " + ((traitorCharacter.Info.Gender == Gender.Male) ? "himself" : "herself");
- endMessage += " killed before completing it.";
- }
- else
- {
- endMessage = traitorCharacter.Name + " was a traitor! ";
- endMessage += (traitorCharacter.Info.Gender == Gender.Male) ? "His" : "Her";
- endMessage += " task was to assassinate " + targetCharacter.Name + ". ";
- endMessage += (Submarine.MainSub.AtEndPosition) ?
- "The task was unsuccessful - the submarine has reached its destination." :
- "The task was unsuccessful.";
+ endMessage += " task was to assassinate " + targetCharacter.Name;
+
+ if (targetCharacter.IsDead) //Partial or complete mission success
+ {
+ endMessage += ". The task was successful";
+ if (traitorCharacter.IsDead)
+ {
+ endMessage += ", but luckily the bastard didn't make it out alive either.";
+ }
+ else if (traitorCharacter.LockHands)
+ {
+ endMessage += ", but ";
+ endMessage += (traitorCharacter.Info.Gender == Gender.Male) ? "he" : "she";
+ endMessage += " was successfuly detained.";
+ }
+ else
+ endMessage += ".";
+ }
+ else //Partial or complete failure
+ {
+ if (traitorCharacter.IsDead)
+ {
+ endMessage += ", but ";
+ endMessage += (traitorCharacter.Info.Gender == Gender.Male) ? "he" : "she";
+ endMessage += " got " + ((traitorCharacter.Info.Gender == Gender.Male) ? "himself" : "herself");
+ endMessage += " killed before completing it.";
+ }
+ else
+ {
+ endMessage += ". The task was unsuccessful";
+ if (traitorCharacter.LockHands)
+ {
+ endMessage += " - ";
+ endMessage += (traitorCharacter.Info.Gender == Gender.Male) ? "he" : "she";
+ endMessage += " was successfuly detained";
+ }
+ if (Submarine.MainSub.AtEndPosition)
+ {
+ endMessage += (traitorCharacter.LockHands ? " and " : " - ");
+ endMessage += "the submarine has reached its destination";
+ }
+ endMessage += ".";
+ }
+ }
+ endMessage += "\n";
}
return endMessage;
diff --git a/Barotrauma/BarotraumaShared/Source/Networking/GameServer.cs b/Barotrauma/BarotraumaShared/Source/Networking/GameServer.cs
index 8c6b56561..cec645624 100644
--- a/Barotrauma/BarotraumaShared/Source/Networking/GameServer.cs
+++ b/Barotrauma/BarotraumaShared/Source/Networking/GameServer.cs
@@ -1326,11 +1326,22 @@ namespace Barotrauma.Networking
if (TraitorsEnabled == YesNoMaybe.Yes ||
(TraitorsEnabled == YesNoMaybe.Maybe && Rand.Range(0.0f, 1.0f) < 0.5f))
{
- TraitorManager = new TraitorManager(this);
-
- if (TraitorManager.TraitorCharacter!=null && TraitorManager.TargetCharacter != null)
+ List characters = new List();
+ foreach (Client client in ConnectedClients)
{
- Log(TraitorManager.TraitorCharacter.Name + " is the traitor and the target is " + TraitorManager.TargetCharacter.Name, ServerLog.MessageType.ServerMessage);
+ if (client.Character != null)
+ characters.Add(client.Character);
+ }
+ var max = (int)Math.Round(characters.Count * 0.2f, 1);
+ var traitorCount = Math.Max(1, TraitorsEnabled == YesNoMaybe.Maybe ? Rand.Int(max) + 1 : max);
+ TraitorManager = new TraitorManager(this, traitorCount);
+
+ if (TraitorManager.TraitorList.Count > 0)
+ {
+ for (int i = 0; i < TraitorManager.TraitorList.Count; i++)
+ {
+ Log(TraitorManager.TraitorList[i].Character.Name + " is the traitor and the target is " + TraitorManager.TraitorList[i].TargetCharacter.Name, ServerLog.MessageType.ServerMessage);
+ }
}
}
@@ -1389,13 +1400,13 @@ namespace Barotrauma.Networking
msg.Write(AllowRespawn && missionAllowRespawn);
msg.Write(Submarine.MainSubs[1] != null); //loadSecondSub
- if (TraitorManager != null &&
- TraitorManager.TraitorCharacter != null &&
- TraitorManager.TargetCharacter != null &&
- TraitorManager.TraitorCharacter == client.Character)
+ Traitor traitor = null;
+ if (TraitorManager != null && TraitorManager.TraitorList.Count > 0)
+ traitor = TraitorManager.TraitorList.Find(t => t.Character == client.Character);
+ if (traitor != null)
{
msg.Write(true);
- msg.Write(TraitorManager.TargetCharacter.Name);
+ msg.Write(traitor.TargetCharacter.Name);
}
else
{