From 03f569b1612b31e88878dbd3b8dc4465be44ca88 Mon Sep 17 00:00:00 2001 From: Regalis Date: Thu, 7 Jan 2016 23:25:11 +0200 Subject: [PATCH] Traitor probability selection instead of separate traitor mode, stopping water ambience on round end, lobby sync after client has verified connection, WIP shiftsummary in multiplayer, "cleanbuild" command in debugconsole --- .../Source/Characters/AI/CrewCommander.cs | 2 +- Subsurface/Source/Characters/Character.cs | 3 +- Subsurface/Source/DebugConsole.cs | 24 ++++ Subsurface/Source/GameSession/CrewManager.cs | 13 +- .../GameSession/GameModes/GameModePreset.cs | 8 +- .../GameSession/GameModes/SinglePlayerMode.cs | 9 -- .../GameSession/GameModes/TraitorMode.cs | 124 ++++++++---------- Subsurface/Source/GameSession/GameSession.cs | 15 +++ Subsurface/Source/GameSession/ShiftSummary.cs | 48 ++++--- .../Components/Signal/RegExFindComponent.cs | 6 +- Subsurface/Source/Networking/GameClient.cs | 20 ++- Subsurface/Source/Networking/GameServer.cs | 43 ++++-- .../Source/Networking/GameServerSettings.cs | 13 +- Subsurface/Source/Networking/NetworkMember.cs | 2 + Subsurface/Source/Screens/NetLobbyScreen.cs | 62 ++++++++- Subsurface/Source/Screens/Screen.cs | 5 +- Subsurface/Source/Sounds/SoundPlayer.cs | 14 +- Subsurface/Source/Utils/SaveUtil.cs | 2 +- Subsurface_Solution.v12.suo | Bin 846848 -> 846848 bytes 19 files changed, 273 insertions(+), 140 deletions(-) diff --git a/Subsurface/Source/Characters/AI/CrewCommander.cs b/Subsurface/Source/Characters/AI/CrewCommander.cs index a56044d43..fe7ec86f4 100644 --- a/Subsurface/Source/Characters/AI/CrewCommander.cs +++ b/Subsurface/Source/Characters/AI/CrewCommander.cs @@ -187,7 +187,7 @@ namespace Barotrauma new GUIImage(new Rectangle(-5, -5, 0, 0), character.AnimController.Limbs[0].sprite, Alignment.Left, characterButton); var humanAi = character.AIController as HumanAIController; - if (humanAi.CurrentOrder != null) + if (humanAi != null && humanAi.CurrentOrder != null) { CreateCharacterOrderFrame(characterButton, humanAi.CurrentOrder, humanAi.CurrentOrderOption); } diff --git a/Subsurface/Source/Characters/Character.cs b/Subsurface/Source/Characters/Character.cs index 7989068f0..da4b6425a 100644 --- a/Subsurface/Source/Characters/Character.cs +++ b/Subsurface/Source/Characters/Character.cs @@ -1161,6 +1161,7 @@ namespace Barotrauma GameMain.NetworkMember.AddChatMessage(chatMessage, ChatMessageType.Dead); GameMain.LightManager.LosEnabled = false; + controlled = null; new NetworkEvent(NetworkEventType.KillCharacter, ID, true, causeOfDeath); } @@ -1169,7 +1170,7 @@ namespace Barotrauma { new NetworkEvent(NetworkEventType.KillCharacter, ID, true, causeOfDeath); } - //otherwise don't kill the Character unless received a message about the Character dying + //don't kill the Character unless received a message about the Character dying else if (!isNetworkMessage) { return; diff --git a/Subsurface/Source/DebugConsole.cs b/Subsurface/Source/DebugConsole.cs index 4aec8f413..56392aa8f 100644 --- a/Subsurface/Source/DebugConsole.cs +++ b/Subsurface/Source/DebugConsole.cs @@ -423,6 +423,30 @@ namespace Barotrauma GameMain.Server.ShowNetStats = !GameMain.Server.ShowNetStats; break; + case "cleanbuild": + GameMain.Config.MusicVolume = 0.5f; + GameMain.Config.SoundVolume = 0.5f; + GameMain.Config.Save("config.xml"); + DebugConsole.NewMessage("Set music and sound volume to 0.5", Color.Green); + + var saveFiles = System.IO.Directory.GetFiles(SaveUtil.SaveFolder); + + foreach (string saveFile in saveFiles) + { + System.IO.File.Delete(saveFile); + DebugConsole.NewMessage("Deleted "+saveFile, Color.Green); + } + + if (System.IO.File.Exists("filelist.xml")) + { + System.IO.File.Delete("filelist.xml"); + } + + if (!System.IO.File.Exists("Content/Map/TutorialSub.sub")) + { + DebugConsole.ThrowError("TutorialSub.sub not found!"); + } + break; default: NewMessage("Command not found", Color.Red); break; diff --git a/Subsurface/Source/GameSession/CrewManager.cs b/Subsurface/Source/GameSession/CrewManager.cs index 2039166b3..4852d1fc0 100644 --- a/Subsurface/Source/GameSession/CrewManager.cs +++ b/Subsurface/Source/GameSession/CrewManager.cs @@ -86,7 +86,11 @@ namespace Barotrauma characterInfos.Add(character.Info); } - commander.UpdateCharacters(); + if (character is AICharacter) + { + commander.UpdateCharacters(); + } + character.Info.CreateCharacterFrame(listBox, character.Info.Name.Replace(' ', '\n'), character); @@ -128,9 +132,10 @@ namespace Barotrauma GUIComponent characterBlock = listBox.GetChild(killedCharacter) as GUIComponent; if (characterBlock != null) characterBlock.Color = Color.DarkRed * 0.5f; - - commander.UpdateCharacters(); - + if (killedCharacter is AICharacter) + { + commander.UpdateCharacters(); + } //if (characters.Find(c => !c.IsDead)==null) //{ // Game1.GameSession.EndShift(null, null); diff --git a/Subsurface/Source/GameSession/GameModes/GameModePreset.cs b/Subsurface/Source/GameSession/GameModes/GameModePreset.cs index 5acdf3392..f5906c7c9 100644 --- a/Subsurface/Source/GameSession/GameModes/GameModePreset.cs +++ b/Subsurface/Source/GameSession/GameModes/GameModePreset.cs @@ -43,10 +43,10 @@ namespace Barotrauma var mode = new GameModePreset("SandBox", typeof(GameMode), false); mode.Description = "A game mode with no specific objectives."; - mode = new GameModePreset("Traitor", typeof(TraitorMode), false); - mode.Description = "One of the players is selected as a traitor and given a secret objective. " - + "The rest of the crew will win if they reach the end of the level or kill the traitor " - + "before the objective is completed."; + //mode = new GameModePreset("Traitor", typeof(TraitorMode), false); + //mode.Description = "One of the players is selected as a traitor and given a secret objective. " + // + "The rest of the crew will win if they reach the end of the level or kill the traitor " + // + "before the objective is completed."; mode = new GameModePreset("Mission", typeof(MissionMode), false); mode.Description = "The crew must work together to complete a specific task, such as retrieving " diff --git a/Subsurface/Source/GameSession/GameModes/SinglePlayerMode.cs b/Subsurface/Source/GameSession/GameModes/SinglePlayerMode.cs index 189e8609f..c9a1cfd63 100644 --- a/Subsurface/Source/GameSession/GameModes/SinglePlayerMode.cs +++ b/Subsurface/Source/GameSession/GameModes/SinglePlayerMode.cs @@ -18,8 +18,6 @@ namespace Barotrauma private GUIButton endShiftButton; public readonly CargoManager CargoManager; - - private ShiftSummary shiftSummary; public Map Map; @@ -119,8 +117,6 @@ namespace Barotrauma isRunning = true; CrewManager.StartShift(); - - shiftSummary = new ShiftSummary(GameMain.GameSession); } public bool TryHireCharacter(HireManager hireManager, CharacterInfo characterInfo) @@ -195,11 +191,6 @@ namespace Barotrauma //if (endMessage != "" || this.endMessage == null) this.endMessage = endMessage; - GUIFrame summaryFrame = shiftSummary.CreateSummaryFrame(); - GUIMessageBox.MessageBoxes.Enqueue(summaryFrame); - var okButton = new GUIButton(new Rectangle(0,0,100,30), "Ok", Alignment.BottomRight, GUI.Style, summaryFrame.children[0]); - okButton.OnClicked = (GUIButton button, object obj) => { GUIMessageBox.MessageBoxes.Dequeue(); return true; }; - bool success = CrewManager.characters.Any(c => !c.IsDead); if (success) diff --git a/Subsurface/Source/GameSession/GameModes/TraitorMode.cs b/Subsurface/Source/GameSession/GameModes/TraitorMode.cs index 6ac1a4851..4c981882f 100644 --- a/Subsurface/Source/GameSession/GameModes/TraitorMode.cs +++ b/Subsurface/Source/GameSession/GameModes/TraitorMode.cs @@ -4,84 +4,66 @@ using Barotrauma.Networking; namespace Barotrauma { - class TraitorMode : GameMode + class TraitorManager { private Character traitorCharacter, targetCharacter; - public TraitorMode(GameModePreset preset) - : base(preset) + public TraitorManager(GameServer server) { + server.NewTraitor(out traitorCharacter, out targetCharacter); + } + + public string GetEndMessage() + { + if (GameMain.Server == null || traitorCharacter == null || targetCharacter == null) return ""; + if (targetCharacter == null || targetCharacter.IsDead) + { + string 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."; + //End(endMessage); + } + else if (traitorCharacter == null || traitorCharacter.IsDead) + { + string 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."; + + return endMessage; + } + else if (Submarine.Loaded.AtEndPosition) + { + string endMessage = traitorCharacter.Name + " was a traitor! "; + endMessage += (traitorCharacter.Info.Gender == Gender.Male) ? "His" : "Her"; + endMessage += " task was to assassinate " + targetCharacter.Name + ". "; + endMessage += "The task was unsuccessful - the has submarine reached its destination."; + + return endMessage; + } + + return ""; + + } - public override void Start() - { - base.Start(); + //public void CharacterLeft(Character character) + //{ + // if (character != traitorCharacter && character != targetCharacter) return; - traitorCharacter = null; - targetCharacter = null; - } - - public override void Update(float deltaTime) - { - if (GameMain.Server == null) return; - - base.Update(deltaTime); - - if (!isRunning) return; - - - if (traitorCharacter == null || targetCharacter == null) - { - GameMain.Server.NewTraitor(out traitorCharacter, out targetCharacter); - } - else - { - if (targetCharacter == null || targetCharacter.IsDead) - { - string 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."; - End(endMessage); - } - else if (traitorCharacter == null || traitorCharacter.IsDead) - { - string 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."; - End(endMessage); - return; - } - else if (Submarine.Loaded.AtEndPosition) - { - string endMessage = traitorCharacter.Name + " was a traitor! "; - endMessage += (traitorCharacter.Info.Gender == Gender.Male) ? "His" : "Her"; - endMessage += " task was to assassinate " + targetCharacter.Name + ". "; - endMessage += "The task was unsuccessful - the has submarine reached its destination."; - End(endMessage); - return; - } - - } - } - - public void CharacterLeft(Character character) - { - if (character != traitorCharacter && character != targetCharacter) return; - - if (character == traitorCharacter) - { - string endMessage = "The traitor has disconnected from the server."; - End(endMessage); - } - else if (character == targetCharacter) - { - string endMessage = "The traitor's target has disconnected from the server."; - End(endMessage); - } - } + // if (character == traitorCharacter) + // { + // string endMessage = "The traitor has disconnected from the server."; + // End(endMessage); + // } + // else if (character == targetCharacter) + // { + // string endMessage = "The traitor's target has disconnected from the server."; + // End(endMessage); + // } + //} } } diff --git a/Subsurface/Source/GameSession/GameSession.cs b/Subsurface/Source/GameSession/GameSession.cs index 623b12904..650e4cb4d 100644 --- a/Subsurface/Source/GameSession/GameSession.cs +++ b/Subsurface/Source/GameSession/GameSession.cs @@ -18,6 +18,8 @@ namespace Barotrauma private Submarine submarine; public CrewManager CrewManager; + + private ShiftSummary shiftSummary; public Mission Mission { @@ -54,6 +56,11 @@ namespace Barotrauma get { return saveFile; } } + public ShiftSummary ShiftSummary + { + get { return shiftSummary; } + } + public GameSession(Submarine submarine, string saveFile, GameModePreset gameModePreset = null) { GameMain.GameSession = this; @@ -119,6 +126,8 @@ namespace Barotrauma if (Mission!=null) Mission.Start(Level.Loaded); + shiftSummary = new ShiftSummary(this); + if (gameMode!=null) gameMode.Start(); TaskManager.StartShift(level); @@ -139,6 +148,12 @@ namespace Barotrauma GameMain.LobbyScreen.Select(); } + GUIFrame summaryFrame = shiftSummary.CreateSummaryFrame(endMessage); + GUIMessageBox.MessageBoxes.Enqueue(summaryFrame); + var okButton = new GUIButton(new Rectangle(0, 0, 100, 30), "Ok", Alignment.BottomRight, GUI.Style, summaryFrame.children[0]); + okButton.OnClicked = (GUIButton button, object obj) => { GUIMessageBox.MessageBoxes.Dequeue(); return true; }; + + TaskManager.EndShift(); //gameMode.End(); diff --git a/Subsurface/Source/GameSession/ShiftSummary.cs b/Subsurface/Source/GameSession/ShiftSummary.cs index 7788aeb6f..7921f9186 100644 --- a/Subsurface/Source/GameSession/ShiftSummary.cs +++ b/Subsurface/Source/GameSession/ShiftSummary.cs @@ -37,8 +37,8 @@ namespace Barotrauma { this.gameSession = gameSession; - startLocation = gameSession.Map.CurrentLocation; - endLocation = gameSession.Map.SelectedLocation; + startLocation = gameSession.Map==null ? null : gameSession.Map.CurrentLocation; + endLocation = gameSession.Map==null ? null : gameSession.Map.SelectedLocation; casualties = new List(); @@ -48,7 +48,6 @@ namespace Barotrauma } selectedMission = gameSession.Mission; - } @@ -57,24 +56,36 @@ namespace Barotrauma casualties.Add(new Casualty(character.Info, causeOfDeath, "")); } - public GUIFrame CreateSummaryFrame() + public GUIFrame CreateSummaryFrame(string endMessage) { + bool singleplayer = GameMain.NetworkMember == null; + bool gameOver = !gameSession.CrewManager.characters.Any(c => !c.IsDead); bool progress = Submarine.Loaded.AtEndPosition; - + GUIFrame frame = new GUIFrame(new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.Black * 0.8f); - - + int width = 760, height = 400; GUIFrame innerFrame = new GUIFrame(new Rectangle(0, 0, width, height), null, Alignment.Center, GUI.Style, frame); - - + int y = 0; - string summaryText = InfoTextManager.GetInfoText(gameOver ? "gameover" : - (progress ? "progress" : "return")); + + if (singleplayer) + { + string summaryText = InfoTextManager.GetInfoText(gameOver ? "gameover" : + (progress ? "progress" : "return")); - var infoText = new GUITextBlock(new Rectangle(0, y, 0, 50), summaryText, GUI.Style, innerFrame, true); - y += infoText.Rect.Height; + var infoText = new GUITextBlock(new Rectangle(0, y, 0, 50), summaryText, GUI.Style, innerFrame, true); + y += infoText.Rect.Height; + } + + + if (!string.IsNullOrWhiteSpace(endMessage)) + { + var endText = new GUITextBlock(new Rectangle(0, y, 0, 30), endMessage, GUI.Style, innerFrame, true); + + y += 30 + endText.Text.Split('\n').Length * 20; + } new GUITextBlock(new Rectangle(0, y, 0, 20), "Crew status:", GUI.Style, innerFrame, GUI.LargeFont); y += 30; @@ -87,8 +98,11 @@ namespace Barotrauma var characterFrame = new GUIFrame(new Rectangle(x, y, 170, 70), character.IsDead ? Color.DarkRed * 0.7f : Color.Transparent, GUI.Style, listBox); characterFrame.OutlineColor = Color.Transparent; characterFrame.Padding = new Vector4(5.0f, 5.0f, 5.0f, 5.0f); + characterFrame.CanBeFocused = false; + character.Info.CreateCharacterFrame(characterFrame, character.Info.Job != null ? (character.Info.Name + '\n' + "(" + character.Info.Job.Name + ")") : character.Info.Name, null); + string statusText; Color statusColor; @@ -103,7 +117,7 @@ namespace Barotrauma else { statusText = (character.Health / character.MaxHealth > 0.8f) ? "OK" : "Injured"; - statusColor = Color.DarkGreen; + statusColor = (character.Health / character.MaxHealth > 0.8f) ? Color.DarkGreen : Color.DarkOrange; } new GUITextBlock(new Rectangle(0, 0, 0, 20), statusText, @@ -122,10 +136,12 @@ namespace Barotrauma new GUITextBlock(new Rectangle(0, y, 0, 30), (GameMain.GameSession.Mission.Completed) ? GameMain.GameSession.Mission.SuccessMessage : GameMain.GameSession.Mission.FailureMessage, GUI.Style, innerFrame); + y += 40; - if (GameMain.GameSession.Mission.Completed) + if (GameMain.GameSession.Mission.Completed && singleplayer) { - new GUITextBlock(new Rectangle(0, y + 40, 0, 30), "Reward: " + GameMain.GameSession.Mission.Reward, GUI.Style, innerFrame); + new GUITextBlock(new Rectangle(0, y, 0, 30), "Reward: " + GameMain.GameSession.Mission.Reward, GUI.Style, innerFrame); + y += 30; } } diff --git a/Subsurface/Source/Items/Components/Signal/RegExFindComponent.cs b/Subsurface/Source/Items/Components/Signal/RegExFindComponent.cs index c5eb192aa..75797a326 100644 --- a/Subsurface/Source/Items/Components/Signal/RegExFindComponent.cs +++ b/Subsurface/Source/Items/Components/Signal/RegExFindComponent.cs @@ -30,7 +30,8 @@ namespace Barotrauma.Items.Components set { if (expression == value) return; - expression = value; + expression = value; + previousReceivedSignal = ""; try { @@ -55,12 +56,13 @@ namespace Barotrauma.Items.Components { if (string.IsNullOrWhiteSpace(expression) || regex==null) return; - if (receivedSignal!=previousReceivedSignal) + if (receivedSignal != previousReceivedSignal) { try { Match match = regex.Match(receivedSignal); previousResult = match.Success; + previousReceivedSignal = receivedSignal; } catch diff --git a/Subsurface/Source/Networking/GameClient.cs b/Subsurface/Source/Networking/GameClient.cs index 55dd52e89..0f9e97991 100644 --- a/Subsurface/Source/Networking/GameClient.cs +++ b/Subsurface/Source/Networking/GameClient.cs @@ -243,6 +243,10 @@ namespace Barotrauma.Networking GameMain.NetLobbyScreen.AddPlayer(name); CanStart = true; + + NetOutgoingMessage lobbyUpdateRequest = client.CreateMessage(); + lobbyUpdateRequest.Write((byte)PacketTypes.RequestNetLobbyUpdate); + client.SendMessage(lobbyUpdateRequest, NetDeliveryMethod.ReliableUnordered); } else if (packetType == (byte)PacketTypes.KickedOut) { @@ -588,15 +592,18 @@ namespace Barotrauma.Networking public IEnumerable EndGame(string endMessage) { - var messageBox = new GUIMessageBox("The round has ended", endMessage, 400, 300); + GameMain.GameSession.gameMode.End(endMessage); + + + //var messageBox = new GUIMessageBox("The round has ended", endMessage, 400, 300); if (!gameStarted) yield return CoroutineStatus.Success; gameStarted = false; Character.Controlled = null; + GameMain.GameScreen.Cam.TargetPos = Vector2.Zero; GameMain.LightManager.LosEnabled = false; - float endPreviewLength = 10.0f; var cinematic = new TransitionCinematic(Submarine.Loaded, GameMain.GameScreen.Cam, endPreviewLength); @@ -615,7 +622,7 @@ namespace Barotrauma.Networking //GameMain.GameScreen.Cam.TargetPos = Submarine.Loaded.Position + offset * 0.8f; //Game1.GameScreen.Cam.MoveCamera((float)deltaTime); - messageBox.Text = endMessage + "\nReturning to lobby in " + (int)secondsLeft + " s"; + //messageBox.Text = endMessage + "\nReturning to lobby in " + (int)secondsLeft + " s"; yield return CoroutineStatus.Running; } while (secondsLeft > 0.0f); @@ -641,13 +648,13 @@ namespace Barotrauma.Networking // yield return CoroutineStatus.Running; //} while (secondsLeft > 0.0f); - messageBox.Close(null,null); + //messageBox.Close(null,null); Submarine.Unload(); GameMain.NetLobbyScreen.Select(); - if (GameMain.GameSession!=null) GameMain.GameSession.EndShift(""); + //if (GameMain.GameSession!=null) GameMain.GameSession.EndShift(""); myCharacter = null; foreach (Client c in otherClients) @@ -776,6 +783,9 @@ namespace Barotrauma.Networking } Character character = Character.Create(ch, position, !isMyCharacter, false); + GameMain.GameSession.CrewManager.characters.Add(character); + character.OnDeath = GameMain.GameSession.ShiftSummary.AddCasualty; + character.ID = ID; diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index c0e98f219..cf26ec95b 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -32,6 +32,8 @@ namespace Barotrauma.Networking private bool masterServerResponded; + public TraitorManager TraitorManager; + public GameServer(string name, int port, bool isPublic = false, string password = "", bool attemptUPnP = false, int maxPlayers = 10) { var endRoundButton = new GUIButton(new Rectangle(GameMain.GraphicsWidth - 170, 20, 150, 25), "End round", Alignment.TopLeft, GUI.Style, inGameHUD); @@ -401,8 +403,6 @@ namespace Barotrauma.Networking SendMessage(outmsg, NetDeliveryMethod.ReliableUnordered, inc.SenderConnection); AddChatMessage(sender.name + " has joined the server", ChatMessageType.Server); - - UpdateNetLobby(null, null); } } else if (inc.SenderConnection.Status == NetConnectionStatus.Disconnected) @@ -470,6 +470,9 @@ namespace Barotrauma.Networking case (byte)PacketTypes.Vote: Voting.RegisterVote(inc, ConnectedClients); break; + case (byte)PacketTypes.RequestNetLobbyUpdate: + UpdateNetLobby(null, null); + break; case (byte)PacketTypes.SpectateRequest: if (gameStarted && allowSpectating) { @@ -772,6 +775,9 @@ namespace Barotrauma.Networking ConnectedClients[i].Character = Character.Create( ConnectedClients[i].characterInfo, assignedWayPoints[i].WorldPosition, true, false); ConnectedClients[i].Character.GiveJobItems(assignedWayPoints[i]); + + GameMain.GameSession.CrewManager.characters.Add(ConnectedClients[i].Character); + ConnectedClients[i].Character.OnDeath = GameMain.GameSession.ShiftSummary.AddCasualty; } if (characterInfo != null) @@ -780,6 +786,9 @@ namespace Barotrauma.Networking Character.Controlled = myCharacter; myCharacter.GiveJobItems(assignedWayPoints[assignedWayPoints.Length - 1]); + + GameMain.GameSession.CrewManager.characters.Add(myCharacter); + myCharacter.OnDeath = GameMain.GameSession.ShiftSummary.AddCasualty; } var startMessage = CreateStartMessage(roundStartSeed, Submarine.Loaded, GameMain.GameSession.gameMode.Preset); @@ -790,6 +799,11 @@ namespace Barotrauma.Networking UpdateCrewFrame(); + if (TraitorsEnabled == YesNoMaybe.Yes || (TraitorsEnabled == YesNoMaybe.Maybe && Rand.Range(0.0f, 1.0f)<0.5f)) + { + TraitorManager = new TraitorManager(this); + } + //give some time for the clients to load the map yield return new WaitForSeconds(2.0f); @@ -838,7 +852,14 @@ namespace Barotrauma.Networking private bool EndButtonHit(GUIButton button, object obj) { - GameMain.GameSession.gameMode.End("Server admin has ended the round"); + string endMessage = "The round has ended." + '\n'; + + if (TraitorManager!=null) + { + endMessage += TraitorManager.GetEndMessage(); + } + + GameMain.GameSession.gameMode.End(endMessage); if (autoRestart) AutoRestartTimer = 20.0f; @@ -847,7 +868,7 @@ namespace Barotrauma.Networking public IEnumerable EndGame(string endMessage) { - var messageBox = new GUIMessageBox("The round has ended", endMessage, 400, 300); + //var messageBox = new GUIMessageBox("The round has ended", endMessage, 400, 300); Character.Controlled = null; myCharacter = null; @@ -893,14 +914,14 @@ namespace Barotrauma.Networking //GameMain.GameScreen.Cam.TargetPos = Submarine.Loaded.Position + offset * 0.8f; //Game1.GameScreen.Cam.MoveCamera((float)deltaTime); - messageBox.Text = endMessage + "\nReturning to lobby in " + (int)secondsLeft + " s"; + //messageBox.Text = endMessage + "\nReturning to lobby in " + (int)secondsLeft + " s"; yield return CoroutineStatus.Running; } while (secondsLeft > 0.0f); Submarine.Unload(); - messageBox.Close(null, null); + //messageBox.Close(null, null); GameMain.NetLobbyScreen.Select(); @@ -922,11 +943,11 @@ namespace Barotrauma.Networking { if (GameMain.GameSession!=null && GameMain.GameSession.gameMode!=null) { - TraitorMode traitorMode = GameMain.GameSession.gameMode as TraitorMode; - if (traitorMode!=null) - { - traitorMode.CharacterLeft(client.Character); - } + //TraitorMode traitorMode = GameMain.GameSession.gameMode as TraitorMode; + //if (traitorMode!=null) + //{ + // traitorMode.CharacterLeft(client.Character); + //} } client.Character.ClearInputs(); diff --git a/Subsurface/Source/Networking/GameServerSettings.cs b/Subsurface/Source/Networking/GameServerSettings.cs index ee30ec613..01d334f85 100644 --- a/Subsurface/Source/Networking/GameServerSettings.cs +++ b/Subsurface/Source/Networking/GameServerSettings.cs @@ -6,11 +6,16 @@ using System.Text; namespace Barotrauma.Networking { - enum SelectionMode + enum SelectionMode : int { Manual = 0, Random = 1, Vote = 2 } + enum YesNoMaybe : int + { + No = 0, Maybe = 1, Yes = 2 + } + partial class GameServer : NetworkMember { public bool ShowNetStats; @@ -47,6 +52,12 @@ namespace Barotrauma.Networking } } + public YesNoMaybe TraitorsEnabled + { + get; + set; + } + public SelectionMode SubSelectionMode { get { return subSelectionMode; } diff --git a/Subsurface/Source/Networking/NetworkMember.cs b/Subsurface/Source/Networking/NetworkMember.cs index 8e2b01754..df376175e 100644 --- a/Subsurface/Source/Networking/NetworkMember.cs +++ b/Subsurface/Source/Networking/NetworkMember.cs @@ -15,6 +15,8 @@ namespace Barotrauma.Networking PlayerJoined, PlayerLeft, KickedOut, + RequestNetLobbyUpdate, + StartGame, EndGame, CharacterInfo, diff --git a/Subsurface/Source/Screens/NetLobbyScreen.cs b/Subsurface/Source/Screens/NetLobbyScreen.cs index 32f10f94a..81cda10d4 100644 --- a/Subsurface/Source/Screens/NetLobbyScreen.cs +++ b/Subsurface/Source/Screens/NetLobbyScreen.cs @@ -18,6 +18,9 @@ namespace Barotrauma private GUIListBox playerList; private GUIListBox subList, modeList, chatBox; + + private GUIButton[] traitorProbabilityButtons; + private GUITextBlock traitorProbabilityText; private GUIListBox jobList; @@ -31,8 +34,6 @@ namespace Barotrauma private GUITickBox autoRestartBox; - private float camAngle; - public bool IsServer; public string ServerName, ServerMessage; @@ -260,6 +261,21 @@ namespace Barotrauma var restartText = new GUITextBlock(new Rectangle(columnX, 210, 20, 20), "", GUI.Style, infoFrame); restartText.TextGetter = AutoRestartText; + //traitor probability ------------------------------------------------------------------ + + var traitorText = new GUITextBlock(new Rectangle(columnX, 230, 20, 20), "Traitors:", GUI.Style, infoFrame); + + traitorProbabilityButtons = new GUIButton[2]; + + traitorProbabilityButtons[0] = new GUIButton(new Rectangle(columnX, 260, 20, 20), "<", GUI.Style, infoFrame); + traitorProbabilityButtons[0].UserData = -1; + + traitorProbabilityText = new GUITextBlock(new Rectangle(columnX+20, 260, 150, 20), "No", null,null, Alignment.TopCenter, GUI.Style, infoFrame); + + traitorProbabilityButtons[1] = new GUIButton(new Rectangle(columnX + 150, 260, 20, 20), ">", GUI.Style, infoFrame); + traitorProbabilityButtons[1].UserData = 1; + + //server info ------------------------------------------------------------------ var serverName = new GUITextBox(new Rectangle(0, 0, 200, 20), null, null, Alignment.TopLeft, Alignment.TopLeft, GUI.Style, infoFrame); @@ -296,6 +312,10 @@ namespace Barotrauma seedBox.Enabled = GameMain.Server != null; serverMessage.Enabled = GameMain.Server != null; autoRestartBox.Enabled = GameMain.Server != null; + + traitorProbabilityButtons[0].Enabled = GameMain.Server != null; + traitorProbabilityButtons[1].Enabled = GameMain.Server != null; + ServerName = (GameMain.Server==null) ? "Server" : GameMain.Server.Name; infoFrame.RemoveChild(infoFrame.children.Find(c => c.UserData as string == "startButton")); @@ -307,9 +327,12 @@ namespace Barotrauma if (IsServer && GameMain.Server != null) { modeList.OnSelected = VotableClicked; - modeList.OnSelected += SelectMode; + modeList.OnSelected = SelectMode; subList.OnSelected = VotableClicked; - subList.OnSelected += SelectMap; + subList.OnSelected = SelectMap; + + traitorProbabilityButtons[0].OnClicked = ToggleTraitorsEnabled; + traitorProbabilityButtons[1].OnClicked = ToggleTraitorsEnabled; GUIButton startButton = new GUIButton(new Rectangle(0, 0, 80, 30), "Start", Alignment.BottomRight, GUI.Style, infoFrame); startButton.OnClicked = GameMain.Server.StartGameClicked; @@ -461,6 +484,29 @@ namespace Barotrauma return true; } + public bool ToggleTraitorsEnabled(GUIButton button, object userData) + { + if (GameMain.Server == null) return false; + + int dir = (int)userData; + + int index = (int)GameMain.Server.TraitorsEnabled + dir; + if (index < 0) index = 2; + if (index > 2) index = 0; + + SetTraitorsEnabled((YesNoMaybe)index); + + return true; + } + + private void SetTraitorsEnabled(YesNoMaybe enabled) + { + + if (GameMain.Server != null) GameMain.Server.TraitorsEnabled = enabled; + (traitorProbabilityText as GUITextBlock).Text = enabled.ToString(); + + } + private bool SelectMap(GUIComponent component, object obj) { if (GameMain.Server != null) GameMain.Server.UpdateNetLobby(obj); @@ -884,6 +930,8 @@ namespace Barotrauma msg.Write(ServerName); msg.Write(ServerMessage); + msg.WriteRangedInteger(0, 2, (int)GameMain.Server.TraitorsEnabled); + //msg.Write(AllowSubVoting); //msg.Write(AllowModeVoting); @@ -914,6 +962,8 @@ namespace Barotrauma float restartTimer = 0.0f; + YesNoMaybe traitorsEnabled; + try { mapName = msg.ReadString(); @@ -922,6 +972,8 @@ namespace Barotrauma ServerName = msg.ReadString(); ServerMessage = msg.ReadString(); + traitorsEnabled = (YesNoMaybe)msg.ReadRangedInteger(0, 2); + //AllowSubVoting = msg.ReadBoolean(); //AllowModeVoting = msg.ReadBoolean(); @@ -952,6 +1004,8 @@ namespace Barotrauma if (!GameMain.NetworkMember.Voting.AllowModeVoting) modeList.Select(modeIndex, true); + SetTraitorsEnabled(traitorsEnabled); + autoRestartBox.Selected = autoRestart; autoRestartTimer = restartTimer; diff --git a/Subsurface/Source/Screens/Screen.cs b/Subsurface/Source/Screens/Screen.cs index be828a21a..57f959397 100644 --- a/Subsurface/Source/Screens/Screen.cs +++ b/Subsurface/Source/Screens/Screen.cs @@ -1,4 +1,5 @@ -using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; namespace Barotrauma { @@ -19,6 +20,8 @@ namespace Barotrauma { if (selected != null && selected!=this) selected.Deselect(); selected = this; + + GUI.ScreenOverlayColor = Color.Transparent; } public virtual void Update(double deltaTime) diff --git a/Subsurface/Source/Sounds/SoundPlayer.cs b/Subsurface/Source/Sounds/SoundPlayer.cs index bc9d7529f..683090a97 100644 --- a/Subsurface/Source/Sounds/SoundPlayer.cs +++ b/Subsurface/Source/Sounds/SoundPlayer.cs @@ -173,17 +173,13 @@ namespace Barotrauma if (Submarine.Loaded==null) { - - - if (waterAmbienceIndexes[0] > 0) + for (int i = 0; i < waterAmbienceIndexes.Length; i++) { - SoundManager.Stop(waterAmbienceIndexes[0]); - SoundManager.Stop(waterAmbienceIndexes[1]); - - waterAmbienceIndexes[0] = 0; - waterAmbienceIndexes[1] = 0; - } + if (waterAmbienceIndexes[i] <= 0) continue; + SoundManager.Stop(waterAmbienceIndexes[i]); + waterAmbienceIndexes[i] = 0; + } return; } diff --git a/Subsurface/Source/Utils/SaveUtil.cs b/Subsurface/Source/Utils/SaveUtil.cs index 8140c3ed8..dda2aa11a 100644 --- a/Subsurface/Source/Utils/SaveUtil.cs +++ b/Subsurface/Source/Utils/SaveUtil.cs @@ -10,7 +10,7 @@ namespace Barotrauma { public class SaveUtil { - private static string SaveFolder = "Data"+Path.DirectorySeparatorChar+"Saves"; + public static string SaveFolder = "Data"+Path.DirectorySeparatorChar+"Saves"; public delegate void ProgressDelegate(string sMessage); diff --git a/Subsurface_Solution.v12.suo b/Subsurface_Solution.v12.suo index 3f194edd7cd30f39f8e2ca1ac2b0fc5078b378d3..1d908405fe4ad6d2f310b57db5ac1cf6882c15af 100644 GIT binary patch delta 14334 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 delta 14673 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 R(B<*Cp{6l<_YqUr{{TMr