diff --git a/Subsurface/Barotrauma.csproj b/Subsurface/Barotrauma.csproj index 507d69e0c..60434ea5e 100644 --- a/Subsurface/Barotrauma.csproj +++ b/Subsurface/Barotrauma.csproj @@ -104,6 +104,7 @@ + diff --git a/Subsurface/Content/Missions.xml b/Subsurface/Content/Missions.xml index ce39fc913..3ba323863 100644 --- a/Subsurface/Content/Missions.xml +++ b/Subsurface/Content/Missions.xml @@ -174,4 +174,15 @@ + + + + + \ No newline at end of file diff --git a/Subsurface/Source/Characters/Character.cs b/Subsurface/Source/Characters/Character.cs index b37b47ee2..aa7555608 100644 --- a/Subsurface/Source/Characters/Character.cs +++ b/Subsurface/Source/Characters/Character.cs @@ -78,7 +78,9 @@ namespace Barotrauma private Item selectedConstruction; private Item[] selectedItems; - + + public byte TeamID = 0; + public AnimController AnimController; private Vector2 cursorPosition; diff --git a/Subsurface/Source/CoroutineManager.cs b/Subsurface/Source/CoroutineManager.cs index ce3191fa8..710edba33 100644 --- a/Subsurface/Source/CoroutineManager.cs +++ b/Subsurface/Source/CoroutineManager.cs @@ -86,7 +86,7 @@ namespace Barotrauma } catch (Exception e) { - DebugConsole.ThrowError("Coroutine " + handle.Name + " threw an exception: " + e.Message); + DebugConsole.ThrowError("Coroutine " + handle.Name + " threw an exception: " + e.Message + "\n" + e.StackTrace.ToString()); return true; } } diff --git a/Subsurface/Source/Events/Missions/CargoMission.cs b/Subsurface/Source/Events/Missions/CargoMission.cs index ab41c4b04..17bfe3783 100644 --- a/Subsurface/Source/Events/Missions/CargoMission.cs +++ b/Subsurface/Source/Events/Missions/CargoMission.cs @@ -8,9 +8,8 @@ using System.Xml.Linq; namespace Barotrauma { - class CargoMission : Mission + class CargoMission : Mission, SinglePlayerMission { - private XElement itemConfig; private List items; diff --git a/Subsurface/Source/Events/Missions/CombatMission.cs b/Subsurface/Source/Events/Missions/CombatMission.cs new file mode 100644 index 000000000..70002f234 --- /dev/null +++ b/Subsurface/Source/Events/Missions/CombatMission.cs @@ -0,0 +1,218 @@ +using System; +using Barotrauma; + +using Barotrauma.Networking; +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; +using System.Xml.Linq; + +namespace Barotrauma +{ + class CombatMission : Mission + { + public Submarine TeamASub = null; + public List TeamACrew = new List(); + public Submarine TeamBSub = null; + public List TeamBCrew = new List(); + + int state = 0; + string winner; string loser; + + public override string SuccessMessage + { + get { return successMessage.Replace("[loser]",loser).Replace("[winner]",winner); } + } + + public CombatMission(XElement element) + : base(element) + { + + } + + public override bool AssignClientIDs(List clients) + { + List randList = new List(clients); + for (int i = 0; i < randList.Count; i++) + { + Client a = randList[i]; + int oi = Rand.Range(0, randList.Count - 1); + Client b = randList[oi]; + randList[i] = b; + randList[oi] = a; + } + int halfPlayers = randList.Count / 2; + for (int i = 0; i < randList.Count; i++) + { + if (i < halfPlayers) + { + randList[i].TeamID = 0; + } + else + { + randList[i].TeamID = 1; + } + } + + return true; + } + + public override void Start(Level level) + { + if (GameMain.Server != null) + { + GameMain.Server.AllowRespawn = false; + } + + Items.Components.Radar.StartMarker = Locations[0]; + Items.Components.Radar.EndMarker = Locations[1]; + TeamASub = Submarine.MainSubs[0]; + TeamBSub = Submarine.MainSubs[1]; + TeamBSub.SetPosition(Level.Loaded.EndPosition - new Vector2(0.0f, 2000.0f)); + + for (int i = 0; i < Character.CharacterList.Count; i++) + { + if (Character.CharacterList[i].TeamID==0) + { + TeamACrew.Add(Character.CharacterList[i]); + } + else + { + TeamBCrew.Add(Character.CharacterList[i]); + } + } + } + + public override void Update(float deltaTime) + { + if (TeamACrew.Count == 0 && TeamBCrew.Count == 0) + { + for (int i = 0; i < Character.CharacterList.Count; i++) + { + if (Character.CharacterList[i].TeamID == 0) + { + TeamACrew.Add(Character.CharacterList[i]); + } + else + { + TeamBCrew.Add(Character.CharacterList[i]); + } + } + } + + if (GameMain.Server != null) + { + GameMain.Server.AllowRespawn = false; + } + + bool ADead = true; + foreach (Character c in TeamACrew) + { + if (!c.IsDead) + { + ADead = false; break; + } + } + bool BDead = true; + foreach (Character c in TeamBCrew) + { + if (!c.IsDead) + { + BDead = false; break; + } + } + + if (BDead && !ADead) + { + winner = Locations[0]; + loser = Locations[1]; + if (state==0) + { + ShowMessage(1); + state = 1; + } + } + if (ADead && !BDead) + { + winner = Locations[1]; + loser = Locations[0]; + if (state == 0) + { + ShowMessage(0); + state = 1; + } + } + + if ((TeamBSub != null && TeamBSub.AtEndPosition) || (TeamASub != null && TeamASub.AtEndPosition)) + { + if (ADead && !BDead) + { + //team B wins! + if (GameMain.Server!=null) GameMain.Server.EndGame(); + } + } + + if ((TeamASub != null && TeamASub.AtStartPosition) || (TeamBSub != null && TeamBSub.AtStartPosition)) + { + if (BDead && !ADead) + { + //team A wins! + if (GameMain.Server != null) GameMain.Server.EndGame(); + } + } + } + + public override void End() + { + bool ADead = true; + foreach (Character c in TeamACrew) + { + if (!c.IsDead && !c.IsUnconscious) + { + ADead = false; break; + } + } + bool BDead = true; + foreach (Character c in TeamBCrew) + { + if (!c.IsDead && !c.IsUnconscious) + { + BDead = false; break; + } + } + + if (BDead && !ADead) + { + winner = Locations[0]; + loser = Locations[1]; + } + if (ADead && !BDead) + { + winner = Locations[1]; + loser = Locations[0]; + } + + if ((TeamBSub != null && TeamBSub.AtEndPosition) || (TeamASub != null && TeamASub.AtEndPosition)) + { + if (ADead && !BDead) + { + //team B wins! + GiveReward(); + + completed = true; + } + } + + if ((TeamASub != null && TeamASub.AtStartPosition) || (TeamBSub != null && TeamBSub.AtStartPosition)) + { + if (BDead && !ADead) + { + //team A wins! + GiveReward(); + + completed = true; + } + } + } + } +} diff --git a/Subsurface/Source/Events/Missions/Mission.cs b/Subsurface/Source/Events/Missions/Mission.cs index 39f5f0cd2..dfce01879 100644 --- a/Subsurface/Source/Events/Missions/Mission.cs +++ b/Subsurface/Source/Events/Missions/Mission.cs @@ -27,6 +27,8 @@ namespace Barotrauma private int reward; + protected string[] Locations = new string[2]; + public string Name { get { return name; } @@ -57,7 +59,7 @@ namespace Barotrauma get { return Vector2.Zero; } } - public string SuccessMessage + virtual public string SuccessMessage { get { return successMessage; } } @@ -111,7 +113,7 @@ namespace Barotrauma } } - public static Mission LoadRandom(Location[] locations, MTRandom rand, string missionType = "") + public static Mission LoadRandom(Location[] locations, MTRandom rand, string missionType = "", bool isSinglePlayer = false) { missionType = missionType.ToLowerInvariant(); @@ -182,22 +184,29 @@ namespace Barotrauma DebugConsole.ThrowError("Error in " + configFile + "! Could not find a mission class of the type \"" + type + "\"."); continue; } + + if (isSinglePlayer && t.GetInterface("Barotrauma.SinglePlayerMission")==null) continue; ConstructorInfo constructor = t.GetConstructor(new[] { typeof(XElement) }); + object instance = constructor.Invoke(new object[] { element }); Mission mission = (Mission)instance; for (int n = 0; n<2; n++) { + mission.Locations[n] = locations[n].Name; mission.description = mission.description.Replace("[location"+(n+1)+"]", locations[n].Name); mission.successMessage = mission.successMessage.Replace("[location" + (n + 1) + "]", locations[n].Name); mission.failureMessage = mission.failureMessage.Replace("[location" + (n + 1) + "]", locations[n].Name); + + for (int m=0;m clients) { return false; } + public void ShowMessage(int index) { if (index >= headers.Count && index >= messages.Count) return; @@ -242,4 +253,9 @@ namespace Barotrauma mode.Money += reward; } } + + interface SinglePlayerMission + { + //all valid single player missions should inherit this + } } diff --git a/Subsurface/Source/Events/Missions/MonsterMission.cs b/Subsurface/Source/Events/Missions/MonsterMission.cs index 6c4d2fa7e..a5f4f89f1 100644 --- a/Subsurface/Source/Events/Missions/MonsterMission.cs +++ b/Subsurface/Source/Events/Missions/MonsterMission.cs @@ -5,7 +5,7 @@ using System.Xml.Linq; namespace Barotrauma { - class MonsterMission : Mission + class MonsterMission : Mission, SinglePlayerMission { private string monsterFile; diff --git a/Subsurface/Source/Events/Missions/SalvageMission.cs b/Subsurface/Source/Events/Missions/SalvageMission.cs index 74f7b65c7..60a1f66a5 100644 --- a/Subsurface/Source/Events/Missions/SalvageMission.cs +++ b/Subsurface/Source/Events/Missions/SalvageMission.cs @@ -8,7 +8,7 @@ using System.Xml.Linq; namespace Barotrauma { - class SalvageMission : Mission + class SalvageMission : Mission, SinglePlayerMission { private ItemPrefab itemPrefab; diff --git a/Subsurface/Source/GameSession/GameSession.cs b/Subsurface/Source/GameSession/GameSession.cs index 563ebc875..329394163 100644 --- a/Subsurface/Source/GameSession/GameSession.cs +++ b/Subsurface/Source/GameSession/GameSession.cs @@ -107,14 +107,14 @@ namespace Barotrauma } } - public void StartShift(string levelSeed) + public void StartShift(string levelSeed, bool loadSecondSub = true) { Level level = Level.CreateRandom(levelSeed); - StartShift(level); + StartShift(level,true,loadSecondSub); } - public void StartShift(Level level, bool reloadSub = true) + public void StartShift(Level level, bool reloadSub = true, bool loadSecondSub = false) { GameMain.LightManager.LosEnabled = (GameMain.Server==null || GameMain.Server.CharacterInfo!=null); @@ -128,6 +128,18 @@ namespace Barotrauma if (reloadSub || Submarine.MainSub != submarine) submarine.Load(true); Submarine.MainSub = submarine; + if (loadSecondSub) + { + if (Submarine.MainSubs[1] == null) + { + Submarine.MainSubs[1] = new Submarine(Submarine.MainSub.FilePath,Submarine.MainSub.MD5Hash.Hash,true); + Submarine.MainSubs[1].Load(false); + } + else if (reloadSub) + { + Submarine.MainSubs[1].Load(false); + } + } //var secondSub = new Submarine(submarine.FilePath, submarine.MD5Hash.Hash); //secondSub.Load(false); @@ -153,7 +165,7 @@ namespace Barotrauma if (gameMode!=null) gameMode.Start(); if (gameMode.Mission != null) Mission.Start(Level.Loaded); - + TaskManager.StartShift(level); GameMain.GameScreen.ColorFade(Color.Black, Color.TransparentBlack, 5.0f); diff --git a/Subsurface/Source/Items/Components/Machines/Radar.cs b/Subsurface/Source/Items/Components/Machines/Radar.cs index 4db8c1f8e..a3af19b58 100644 --- a/Subsurface/Source/Items/Components/Machines/Radar.cs +++ b/Subsurface/Source/Items/Components/Machines/Radar.cs @@ -21,6 +21,9 @@ namespace Barotrauma.Items.Components private List radarBlips; private float prevPingRadius; + public static string StartMarker = "Start"; + public static string EndMarker = "End"; + [HasDefaultValue(10000.0f, false)] public float Range { @@ -260,11 +263,11 @@ namespace Barotrauma.Items.Components DrawMarker(spriteBatch, - (GameMain.GameSession.Map == null) ? "Start" : GameMain.GameSession.Map.CurrentLocation.Name, + (GameMain.GameSession.Map == null) ? StartMarker : GameMain.GameSession.Map.CurrentLocation.Name, (Level.Loaded.StartPosition - item.WorldPosition), displayScale, center, (rect.Width * 0.5f)); DrawMarker(spriteBatch, - (GameMain.GameSession.Map == null) ? "End" : GameMain.GameSession.Map.SelectedLocation.Name, + (GameMain.GameSession.Map == null) ? EndMarker : GameMain.GameSession.Map.SelectedLocation.Name, (Level.Loaded.EndPosition - item.WorldPosition), displayScale, center, (rect.Width * 0.5f)); if (GameMain.GameSession.Mission != null) diff --git a/Subsurface/Source/Map/Map/Map.cs b/Subsurface/Source/Map/Map/Map.cs index bef123f48..ff70f8c7f 100644 --- a/Subsurface/Source/Map/Map/Map.cs +++ b/Subsurface/Source/Map/Map/Map.cs @@ -295,10 +295,6 @@ namespace Barotrauma Location location = locations[i]; Vector2 pos = rectCenter + (location.MapPosition + offset) * scale; - - - - Rectangle drawRect = location.Type.Sprite.SourceRect; Rectangle sourceRect = drawRect; drawRect.X = (int)pos.X - drawRect.Width/2; @@ -387,7 +383,7 @@ namespace Barotrauma if (rand.NextDouble() < 0.3f) return null; - mission = Mission.LoadRandom(locations, rand); + mission = Mission.LoadRandom(locations, rand, "", true); } return mission; diff --git a/Subsurface/Source/Map/Submarine.cs b/Subsurface/Source/Map/Submarine.cs index 28a396846..ce1c7c717 100644 --- a/Subsurface/Source/Map/Submarine.cs +++ b/Subsurface/Source/Map/Submarine.cs @@ -52,7 +52,12 @@ namespace Barotrauma public static readonly Vector2 GridSize = new Vector2(16.0f, 16.0f); - public static Submarine MainSub; + public static Submarine[] MainSubs = new Submarine[2]; + public static Submarine MainSub + { + get { return MainSubs[0]; } + set { MainSubs[0] = value; } + } private static List loaded = new List(); private SubmarineBody subBody; @@ -1010,6 +1015,7 @@ namespace Barotrauma subBody = null; if (MainSub == this) MainSub = null; + if (MainSubs[1] == this) MainSubs[1] = null; DockedTo.Clear(); } diff --git a/Subsurface/Source/Networking/Client.cs b/Subsurface/Source/Networking/Client.cs index 0c5cdebae..9cafb37d6 100644 --- a/Subsurface/Source/Networking/Client.cs +++ b/Subsurface/Source/Networking/Client.cs @@ -24,6 +24,8 @@ namespace Barotrauma.Networking public string name; public byte ID; + public byte TeamID = 0; + public Character Character; public CharacterInfo characterInfo; public NetConnection Connection { get; set; } diff --git a/Subsurface/Source/Networking/GameClient.cs b/Subsurface/Source/Networking/GameClient.cs index 3cf55fa90..1f53804fc 100644 --- a/Subsurface/Source/Networking/GameClient.cs +++ b/Subsurface/Source/Networking/GameClient.cs @@ -737,6 +737,7 @@ namespace Barotrauma.Networking string modeName = inc.ReadString(); bool respawnAllowed = inc.ReadBoolean(); + bool loadSecondSub = inc.ReadBoolean(); GameModePreset gameMode = GameModePreset.list.Find(gm => gm.Name == modeName); @@ -760,7 +761,7 @@ namespace Barotrauma.Networking //int gameModeIndex = inc.ReadInt32(); GameMain.GameSession = new GameSession(GameMain.NetLobbyScreen.SelectedSub, "", gameMode, Mission.MissionTypes[missionTypeIndex]); - GameMain.GameSession.StartShift(levelSeed); + GameMain.GameSession.StartShift(levelSeed,loadSecondSub); if (respawnAllowed) respawnManager = new RespawnManager(this, GameMain.NetLobbyScreen.SelectedShuttle); @@ -1137,6 +1138,7 @@ namespace Barotrauma.Networking { bool noInfo = inc.ReadBoolean(); ushort id = inc.ReadUInt16(); + byte teamid = inc.ReadByte(); string configPath = inc.ReadString(); Vector2 position = new Vector2(inc.ReadFloat(), inc.ReadFloat()); @@ -1155,6 +1157,7 @@ namespace Barotrauma.Networking character = Character.Create(configPath, position, null, true); character.ID = id; + character.TeamID = teamid; } else { @@ -1179,6 +1182,7 @@ namespace Barotrauma.Networking character = Character.Create(configPath, position, ch, ownerId != myID, hasAi); character.ID = id; + character.TeamID = teamid; if (configPath == Character.HumanConfigFile) { diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index c60cb27f5..64259e76e 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -312,7 +312,7 @@ namespace Barotrauma.Networking //restart if all characters are dead or submarine is at the end of the level if ((autoRestart && isCrewDead) || - (EndRoundAtLevelEnd && Submarine.MainSub != null && Submarine.MainSub.AtEndPosition)) + (EndRoundAtLevelEnd && Submarine.MainSub != null && Submarine.MainSub.AtEndPosition && Submarine.MainSubs[1]==null)) { if (AutoRestart && isCrewDead) { @@ -883,13 +883,17 @@ namespace Barotrauma.Networking GUIMessageBox.CloseAll(); - AssignJobs(connectedClients); - roundStartSeed = DateTime.Now.Millisecond; Rand.SetSyncedSeed(roundStartSeed); - + GameMain.GameSession = new GameSession(selectedSub, "", selectedMode, Mission.MissionTypes[GameMain.NetLobbyScreen.MissionTypeIndex]); - GameMain.GameSession.StartShift(GameMain.NetLobbyScreen.LevelSeed); + + yield return CoroutineStatus.Running; + + bool hasTwoTeams = false; + if (GameMain.GameSession.gameMode.Mission != null) hasTwoTeams = GameMain.GameSession.gameMode.Mission.AssignClientIDs(connectedClients); + + GameMain.GameSession.StartShift(GameMain.NetLobbyScreen.LevelSeed,hasTwoTeams); GameServer.Log("Starting a new round...", Color.Cyan); GameServer.Log("Submarine: " + selectedSub.Name, Color.Cyan); @@ -897,49 +901,105 @@ namespace Barotrauma.Networking GameServer.Log("Level seed: " + GameMain.NetLobbyScreen.LevelSeed, Color.Cyan); if (AllowRespawn) respawnManager = new RespawnManager(this, selectedShuttle); - - yield return CoroutineStatus.Running; - - List characterInfos = new List(); - - foreach (Client client in connectedClients) - { - client.inGame = true; - - if (client.characterInfo == null) - { - client.characterInfo = new CharacterInfo(Character.HumanConfigFile, client.name); - } - characterInfos.Add(client.characterInfo); - - client.characterInfo.Job = new Job(client.assignedJob); - } - - if (characterInfo != null) - { - characterInfo.Job = new Job(GameMain.NetLobbyScreen.JobPreferences[0]); - characterInfos.Add(characterInfo); - } - - WayPoint[] assignedWayPoints = WayPoint.SelectCrewSpawnPoints(characterInfos, Submarine.MainSub); - for (int i = 0; i < connectedClients.Count; i++) + if (!hasTwoTeams) { - 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); + AssignJobs(connectedClients); + + List characterInfos = new List(); + + foreach (Client client in connectedClients) + { + client.inGame = true; + + if (client.characterInfo == null) + { + client.characterInfo = new CharacterInfo(Character.HumanConfigFile, client.name); + } + characterInfos.Add(client.characterInfo); + + client.characterInfo.Job = new Job(client.assignedJob); + } + + if (characterInfo != null) + { + characterInfo.Job = new Job(GameMain.NetLobbyScreen.JobPreferences[0]); + characterInfos.Add(characterInfo); + } + + WayPoint[] assignedWayPoints = WayPoint.SelectCrewSpawnPoints(characterInfos, Submarine.MainSub); + + for (int i = 0; i < connectedClients.Count; i++) + { + 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); + } + + if (characterInfo != null) + { + myCharacter = Character.Create(characterInfo, assignedWayPoints[assignedWayPoints.Length - 1].WorldPosition, false, false); + Character.Controlled = myCharacter; + + myCharacter.GiveJobItems(assignedWayPoints[assignedWayPoints.Length - 1]); + + GameMain.GameSession.CrewManager.characters.Add(myCharacter); + } } - - if (characterInfo != null) + else { - myCharacter = Character.Create(characterInfo, assignedWayPoints[assignedWayPoints.Length - 1].WorldPosition, false, false); - Character.Controlled = myCharacter; + for (int j=0;j<2;j++) + { + List teamClients = connectedClients.FindAll(c => c.TeamID == j).ToList(); + if (teamClients == null) continue; + AssignJobs(teamClients); - myCharacter.GiveJobItems(assignedWayPoints[assignedWayPoints.Length - 1]); + List characterInfos = new List(); - GameMain.GameSession.CrewManager.characters.Add(myCharacter); + foreach (Client client in teamClients) + { + client.inGame = true; + + if (client.characterInfo == null) + { + client.characterInfo = new CharacterInfo(Character.HumanConfigFile, client.name); + } + characterInfos.Add(client.characterInfo); + + client.characterInfo.Job = new Job(client.assignedJob); + } + + if (characterInfo != null) + { + characterInfo.Job = new Job(GameMain.NetLobbyScreen.JobPreferences[0]); + characterInfos.Add(characterInfo); + } + + WayPoint[] assignedWayPoints = WayPoint.SelectCrewSpawnPoints(characterInfos, Submarine.MainSubs[j]); + + for (int i = 0; i < teamClients.Count; i++) + { + teamClients[i].Character = Character.Create( + teamClients[i].characterInfo, assignedWayPoints[i].WorldPosition, true, false); + teamClients[i].Character.GiveJobItems(assignedWayPoints[i]); + + GameMain.GameSession.CrewManager.characters.Add(teamClients[i].Character); + + teamClients[i].Character.TeamID = teamClients[i].TeamID; + } + + if (characterInfo != null) + { + myCharacter = Character.Create(characterInfo, assignedWayPoints[assignedWayPoints.Length - 1].WorldPosition, false, false); + Character.Controlled = myCharacter; + + myCharacter.GiveJobItems(assignedWayPoints[assignedWayPoints.Length - 1]); + + GameMain.GameSession.CrewManager.characters.Add(myCharacter); + } + } } var startMessage = CreateStartMessage(roundStartSeed, Submarine.MainSub, GameMain.GameSession.gameMode.Preset); @@ -1006,6 +1066,7 @@ namespace Barotrauma.Networking msg.Write(selectedMode.Name); msg.Write(AllowRespawn); + msg.Write(Submarine.MainSubs[1] != null); //loadSecondSub //msg.Write(GameMain.NetLobbyScreen.GameDuration.TotalMinutes); @@ -1670,6 +1731,7 @@ namespace Barotrauma.Networking { msg.Write(c.Info == null); msg.Write(c.ID); + msg.Write(c.TeamID); msg.Write(c.ConfigPath); msg.Write(c.WorldPosition.X);