From 2ef92c4af018560cba331d654032e8b1980c649d Mon Sep 17 00:00:00 2001 From: Regalis Date: Tue, 16 Jun 2015 22:53:02 +0300 Subject: [PATCH] Moved CrewManager & HireManager to a separate single player GameMode, bleeding decreases health --- Subsurface/Characters/Character.cs | 125 +++++++----- Subsurface/Characters/CharacterInfo.cs | 7 +- .../Content/Characters/Crawler/crawler.xml | 2 +- .../TigerThresher/tigerthresher.xml | 2 +- Subsurface/Content/Items/Weapons/weapons.xml | 2 +- Subsurface/DebugConsole.cs | 9 +- Subsurface/GUI/GUIComponent.cs | 2 +- Subsurface/GUI/GUIListBox.cs | 16 +- Subsurface/GUI/GUIMessageBox.cs | 13 +- Subsurface/GameSession/CrewManager.cs | 14 +- Subsurface/GameSession/GameMode.cs | 70 +++++-- Subsurface/GameSession/GameSession.cs | 159 +++++---------- Subsurface/GameSession/SinglePlayerMode.cs | 155 +++++++++++++++ Subsurface/GameSession/TraitorMode.cs | 7 +- Subsurface/Items/Components/Signal/Wire.cs | 8 +- Subsurface/Map/Map.cs | 16 +- Subsurface/SaveUtil.cs | 30 +-- Subsurface/Screens/LobbyScreen.cs | 185 +++++++++++++++--- Subsurface/Screens/MainMenu.cs | 92 +++++++-- Subsurface/Screens/NetLobbyScreen.cs | 24 +-- Subsurface/Subsurface.csproj | 1 + Subsurface_Solution.v12.suo | Bin 279552 -> 291328 bytes 22 files changed, 656 insertions(+), 283 deletions(-) create mode 100644 Subsurface/GameSession/SinglePlayerMode.cs diff --git a/Subsurface/Characters/Character.cs b/Subsurface/Characters/Character.cs index cb5f938dc..b84404b8a 100644 --- a/Subsurface/Characters/Character.cs +++ b/Subsurface/Characters/Character.cs @@ -79,7 +79,7 @@ namespace Subsurface protected float soundInterval; private float bleeding; - private float blood; + //private float blood; private Sound[] sounds; //which AIstate each sound is for @@ -152,15 +152,15 @@ namespace Subsurface } } - public float Blood - { - get { return blood; } - set - { - blood = MathHelper.Clamp(value, 0.0f, 100.0f); - if (blood == 0.0f) Kill(); - } - } + //public float Blood + //{ + // get { return blood; } + // set + // { + // blood = MathHelper.Clamp(value, 0.0f, 100.0f); + // if (blood == 0.0f) Kill(); + // } + //} public Item[] SelectedItems { @@ -286,20 +286,20 @@ namespace Subsurface IsNetworkPlayer = isNetworkPlayer; oxygen = 100.0f; - blood = 100.0f; + //blood = 100.0f; aiTarget = new AITarget(this); properties = ObjectProperty.GetProperties(this); + info = characterInfo==null ? new CharacterInfo(file) : characterInfo; + XDocument doc = ToolBox.TryLoadXml(file); if (doc == null) return; - + speciesName = ToolBox.GetAttributeString(doc.Root, "name", "Unknown"); isHumanoid = ToolBox.GetAttributeBool(doc.Root, "humanoid", false); - - info = characterInfo ?? new CharacterInfo(file); - + if (isHumanoid) { animController = new HumanoidAnimController(this, doc.Root.Element("ragdoll")); @@ -427,7 +427,11 @@ namespace Subsurface /// public void ControlLocalPlayer(Camera cam, bool moveCam = true) { - if (isDead) return; + //if (isDead) + //{ + + // return; + //} Limb head = animController.GetLimb(LimbType.Head); @@ -522,7 +526,16 @@ namespace Subsurface public void Update(Camera cam, float deltaTime) { - if (isDead) return; + if (isDead) + { + if (controlled == this) + { + cam.Zoom = MathHelper.Lerp(cam.Zoom, 1.5f, 0.1f); + cam.TargetPos = ConvertUnits.ToDisplayUnits(animController.limbs[0].SimPosition); + cam.OffsetAmount = 0.0f; + } + return; + } if (PressureProtection==0.0f && (animController.CurrentHull == null || animController.CurrentHull.LethalPressure >= 100.0f)) @@ -568,7 +581,7 @@ namespace Subsurface //foreach (Limb limb in animController.limbs) //{ - Blood = blood - bleeding * deltaTime; + Health = health - bleeding * deltaTime; //} if (aiController != null) aiController.Update(deltaTime); @@ -591,18 +604,24 @@ namespace Subsurface aiTarget.SightRange += torso.LinearVelocity.Length() * 500.0f; } } - + public void Draw(SpriteBatch spriteBatch) { animController.Draw(spriteBatch); if (IsNetworkPlayer) { - Vector2 pos = new Vector2(Position.X, -Position.Y - 50.0f) - GUI.font.MeasureString(info.name) * 0.5f; - spriteBatch.DrawString(GUI.font, info.name, pos - new Vector2(1.0f, 1.0f), Color.Black); - spriteBatch.DrawString(GUI.font, info.name, pos, Color.White); + Vector2 namePos = new Vector2(Position.X, -Position.Y - 80.0f) - GUI.font.MeasureString(info.name) * 0.5f; + spriteBatch.DrawString(GUI.font, info.name, namePos - new Vector2(1.0f, 1.0f), Color.Black); + spriteBatch.DrawString(GUI.font, info.name, namePos, Color.White); } + if (this == Character.controlled) return; + + Vector2 healthBarPos = new Vector2(Position.X - 50, -Position.Y - 50.0f); + GUI.DrawRectangle(spriteBatch, new Rectangle((int)healthBarPos.X-2, (int)healthBarPos.Y-2, 100+4, 15+4), Color.Black, false); + GUI.DrawRectangle(spriteBatch, new Rectangle((int)healthBarPos.X, (int)healthBarPos.Y, (int)(100.0f*(health/maxHealth)), 15), Color.Red, true); + //spriteBatch.DrawString(GUI.font, ID.ToString(), ConvertUnits.ToDisplayUnits(animController.limbs[0].Position), Color.White); //GUI.DrawLine(spriteBatch, ConvertUnits.ToDisplayUnits(animController.limbs[0].SimPosition.X, animController.limbs[0].SimPosition.Y), // ConvertUnits.ToDisplayUnits(animController.limbs[0].SimPosition.X, animController.limbs[0].SimPosition.Y) + @@ -610,7 +629,7 @@ namespace Subsurface } - private static GUIProgressBar drowningBar, bloodBar; + private static GUIProgressBar drowningBar, healthBar; public void DrawHud(SpriteBatch spriteBatch, Camera cam) { if (drowningBar==null) @@ -618,19 +637,16 @@ namespace Subsurface int width = 100, height = 20; drowningBar = new GUIProgressBar(new Rectangle(20, Game1.GraphicsHeight/2, width, height), Color.Blue, 1.0f); - bloodBar = new GUIProgressBar(new Rectangle(20, Game1.GraphicsHeight / 2 + 30, width, height), Color.Red, 1.0f); + healthBar = new GUIProgressBar(new Rectangle(20, Game1.GraphicsHeight / 2 + 30, width, height), Color.Red, 1.0f); } drowningBar.BarSize = Controlled.Oxygen / 100.0f; - if (drowningBar.BarSize < 1.0f) - drowningBar.Draw(spriteBatch); + if (drowningBar.BarSize < 0.95f) drowningBar.Draw(spriteBatch); - bloodBar.BarSize = Blood / 100.0f; - if (bloodBar.BarSize < 1.0f) - bloodBar.Draw(spriteBatch); + healthBar.BarSize = health / maxHealth; + if (healthBar.BarSize < 1.0f) healthBar.Draw(spriteBatch); - if (Controlled.Inventory != null) - Controlled.Inventory.Draw(spriteBatch); + if (Controlled.Inventory != null) Controlled.Inventory.Draw(spriteBatch); if (closestItem!=null) { @@ -773,29 +789,6 @@ namespace Subsurface { if (isDead) return; - //if the game is run by a client, characters are only killed when the server says so - if (Game1.Client != null) - { - if (networkMessage) - { - new NetworkEvent(NetworkEventType.KillCharacter, ID, true); - } - else - { - return; - } - } - - if (Game1.Server != null) - { - new NetworkEvent(NetworkEventType.KillCharacter, ID, false); - } - - if (Game1.GameSession!=null && Game1.GameSession.crewManager != null) - { - Game1.GameSession.crewManager.KillCharacter(this); - } - isDead = true; animController.movement = Vector2.Zero; animController.TargetMovement = Vector2.Zero; @@ -820,6 +813,30 @@ namespace Subsurface joint.MotorEnabled = false; joint.MaxMotorTorque = 0.0f; } + + + //if the game is run by a client, characters are only killed when the server says so + if (Game1.Client != null) + { + if (networkMessage) + { + new NetworkEvent(NetworkEventType.KillCharacter, ID, true); + } + else + { + return; + } + } + + if (Game1.Server != null) + { + new NetworkEvent(NetworkEventType.KillCharacter, ID, false); + } + + if (Game1.GameSession != null) + { + Game1.GameSession.KillCharacter(this); + } } public override void FillNetworkData(NetworkEventType type, NetOutgoingMessage message, object data) diff --git a/Subsurface/Characters/CharacterInfo.cs b/Subsurface/Characters/CharacterInfo.cs index 17f4177e2..c6c583cce 100644 --- a/Subsurface/Characters/CharacterInfo.cs +++ b/Subsurface/Characters/CharacterInfo.cs @@ -89,12 +89,16 @@ namespace Subsurface public CharacterInfo(XElement element) { - name = element.Name.ToString(); + name = ToolBox.GetAttributeString(element, "name", "unnamed"); string genderStr = ToolBox.GetAttributeString(element, "gender", "male").ToLower(); gender = (genderStr == "male") ? Gender.Male : Gender.Female; + file = ToolBox.GetAttributeString(element, "file", ""); + salary = ToolBox.GetAttributeInt(element, "salary", 1000); + + headSpriteId = ToolBox.GetAttributeInt(element, "headspriteid", 1); } public virtual XElement Save(XElement parentElement) @@ -103,6 +107,7 @@ namespace Subsurface componentElement.Add( new XAttribute("name", name), + new XAttribute("file", file), new XAttribute("gender", gender == Gender.Male ? "male" : "female"), new XAttribute("salary", salary), new XAttribute("headspriteid", headSpriteId)); diff --git a/Subsurface/Content/Characters/Crawler/crawler.xml b/Subsurface/Content/Characters/Crawler/crawler.xml index c2151cd42..dd5fce2a4 100644 --- a/Subsurface/Content/Characters/Crawler/crawler.xml +++ b/Subsurface/Content/Characters/Crawler/crawler.xml @@ -26,7 +26,7 @@ - + diff --git a/Subsurface/Content/Characters/TigerThresher/tigerthresher.xml b/Subsurface/Content/Characters/TigerThresher/tigerthresher.xml index 321b558a0..6a0aae69a 100644 --- a/Subsurface/Content/Characters/TigerThresher/tigerthresher.xml +++ b/Subsurface/Content/Characters/TigerThresher/tigerthresher.xml @@ -15,7 +15,7 @@ - + diff --git a/Subsurface/Content/Items/Weapons/weapons.xml b/Subsurface/Content/Items/Weapons/weapons.xml index fe3d8c847..d73e3941c 100644 --- a/Subsurface/Content/Items/Weapons/weapons.xml +++ b/Subsurface/Content/Items/Weapons/weapons.xml @@ -11,7 +11,7 @@ - + diff --git a/Subsurface/DebugConsole.cs b/Subsurface/DebugConsole.cs index 772eb006c..6bac7d957 100644 --- a/Subsurface/DebugConsole.cs +++ b/Subsurface/DebugConsole.cs @@ -148,8 +148,13 @@ namespace Subsurface { WayPoint spawnPoint = WayPoint.GetRandom(WayPoint.SpawnType.Human); Character.Controlled = new Character("Content/Characters/Human/human.xml", (spawnPoint == null) ? Vector2.Zero : spawnPoint.SimPosition); - if (Game1.GameSession != null) Game1.GameSession.crewManager.AddCharacter(Character.Controlled); - if (Game1.GameSession != null) Game1.GameSession.crewManager.SelectCharacter(Character.Controlled); + if (Game1.GameSession != null) + { + SinglePlayerMode mode = Game1.GameSession.gameMode as SinglePlayerMode; + if (mode == null) break; + mode.crewManager.AddCharacter(Character.Controlled); + mode.crewManager.SelectCharacter(Character.Controlled); + } } else { diff --git a/Subsurface/GUI/GUIComponent.cs b/Subsurface/GUI/GUIComponent.cs index bea9cc29b..c21a4dd73 100644 --- a/Subsurface/GUI/GUIComponent.cs +++ b/Subsurface/GUI/GUIComponent.cs @@ -242,7 +242,7 @@ namespace Subsurface if (children.Contains(child)) children.Remove(child); } - public void ClearChildren() + public virtual void ClearChildren() { children.Clear(); } diff --git a/Subsurface/GUI/GUIListBox.cs b/Subsurface/GUI/GUIListBox.cs index 8f5db6ddc..92e1ff62a 100644 --- a/Subsurface/GUI/GUIListBox.cs +++ b/Subsurface/GUI/GUIListBox.cs @@ -28,7 +28,10 @@ namespace Subsurface public object SelectedData { - get { return (selected == null) ? null : selected.UserData; } + get + { + return (selected == null) ? null : selected.UserData; + } } public int SelectedIndex @@ -166,12 +169,19 @@ namespace Subsurface } + public override void ClearChildren() + { + base.ClearChildren(); + selected = null; + } + public override void RemoveChild(GUIComponent child) { base.RemoveChild(child); - UpdateScrollBarSize(); - + if (selected == child) selected = null; + + UpdateScrollBarSize(); } private void ShowScrollBar() diff --git a/Subsurface/GUI/GUIMessageBox.cs b/Subsurface/GUI/GUIMessageBox.cs index 1676c778f..de5f4dfd8 100644 --- a/Subsurface/GUI/GUIMessageBox.cs +++ b/Subsurface/GUI/GUIMessageBox.cs @@ -13,12 +13,12 @@ namespace Subsurface //public OnClickedHandler OnClicked; //GUIFrame frame; - GUIButton[] buttons; + public GUIButton[] Buttons; public GUIMessageBox(string header, string text) : this(header, text, new string[] {"OK"}) { - this.buttons[0].OnClicked = OkClicked; + this.Buttons[0].OnClicked = Close; } public GUIMessageBox(string header, string text, string[] buttons, Alignment textAlignment = (Alignment.Left | Alignment.Top)) @@ -37,22 +37,21 @@ namespace Subsurface new GUITextBlock(new Rectangle(0, 30, 0, DefaultHeight - 70), text, Color.Transparent, Color.White, textAlignment, this, true); int x = 0; - this.buttons = new GUIButton[buttons.Length]; + this.Buttons = new GUIButton[buttons.Length]; for (int i = 0; i < buttons.Length; i++) { - this.buttons[i] = new GUIButton(new Rectangle(x, 0, 150, 30), buttons[i], GUI.style, Alignment.Left | Alignment.Bottom, this); + this.Buttons[i] = new GUIButton(new Rectangle(x, 0, 150, 30), buttons[i], GUI.style, Alignment.Left | Alignment.Bottom, this); - x += this.buttons[i].Rect.Width + 20; + x += this.Buttons[i].Rect.Width + 20; } messageBoxes.Enqueue(this); } - private bool OkClicked(GUIButton button, object obj) + public bool Close(GUIButton button, object obj) { messageBoxes.Dequeue(); return true; - } } } diff --git a/Subsurface/GameSession/CrewManager.cs b/Subsurface/GameSession/CrewManager.cs index ea3d019e0..631306fe2 100644 --- a/Subsurface/GameSession/CrewManager.cs +++ b/Subsurface/GameSession/CrewManager.cs @@ -77,16 +77,16 @@ namespace Subsurface characterInfos.Add(character.info); } - GUIFrame frame = new GUIFrame(new Rectangle(0,0,0,40), Color.Transparent, listBox); + GUIFrame frame = new GUIFrame(new Rectangle(0, 0, 0, 40), Color.Transparent, listBox); frame.UserData = character; frame.Padding = new Vector4(5.0f, 5.0f, 5.0f, 5.0f); frame.HoverColor = Color.LightGray * 0.5f; frame.SelectedColor = Color.Gold * 0.5f; - string name = character.info.name.Replace(' ','\n'); + string name = character.info.name.Replace(' ', '\n'); GUITextBlock textBlock = new GUITextBlock( - new Rectangle(40,0,0,25), + new Rectangle(40, 0, 0, 25), name, Color.Transparent, Color.White, Alignment.Left, @@ -94,7 +94,7 @@ namespace Subsurface frame); textBlock.Padding = new Vector4(5.0f, 0.0f, 5.0f, 0.0f); - new GUIImage(new Rectangle(-10,-10,0,0), character.animController.limbs[0].sprite, Alignment.Left, frame); + new GUIImage(new Rectangle(-10, -10, 0, 0), character.animController.limbs[0].sprite, Alignment.Left, frame); } public void Update(float deltaTime) @@ -106,7 +106,11 @@ namespace Subsurface { GUIComponent characterBlock = listBox.GetChild(killedCharacter) as GUIComponent; if (characterBlock != null) characterBlock.Color = Color.DarkRed * 0.5f; - + + if (characters.Find(c => !c.IsDead)==null) + { + Game1.GameSession.EndShift(null, null); + } } public void StartShift() diff --git a/Subsurface/GameSession/GameMode.cs b/Subsurface/GameSession/GameMode.cs index ab7e72f67..45ab79427 100644 --- a/Subsurface/GameSession/GameMode.cs +++ b/Subsurface/GameSession/GameMode.cs @@ -1,19 +1,53 @@ -using System; +using Microsoft.Xna.Framework.Graphics; +using System; using System.Collections.Generic; +using System.Reflection; namespace Subsurface { + class GameModePreset + { + public static List list = new List(); + + public ConstructorInfo Constructor; + public string Name; + public bool IsSinglePlayer; + + public GameModePreset(string name, Type type, bool isSinglePlayer = false) + { + this.Name = name; + //Constructor = constructor; + + + Constructor = type.GetConstructor(new Type[] { typeof(GameModePreset) }); + + IsSinglePlayer = isSinglePlayer; + + list.Add(this); + } + + public GameMode Instantiate() + { + object[] lobject = new object[] { this }; + return(GameMode)Constructor.Invoke(lobject); + } + } + class GameMode { - public static List list = new List(); + public static List presetList = new List(); //TimeSpan duration; protected DateTime startTime; protected DateTime endTime; + //public readonly bool IsSinglePlayer; + protected bool isRunning; - protected string name; + //protected string name; + + protected GameModePreset preset; private string endMessage; @@ -32,9 +66,14 @@ namespace Subsurface get { return isRunning; } } + public bool IsSinglePlayer + { + get { return preset.IsSinglePlayer; } + } + public string Name { - get { return name; } + get { return preset.Name; } } public string EndMessage @@ -42,13 +81,16 @@ namespace Subsurface get { return endMessage; } } - public GameMode(string name) + public GameMode(GameModePreset preset) { - this.name = name; + this.preset = preset; - list.Add(this); + //list.Add(this); } + public virtual void Draw(SpriteBatch spriteBatch) + { } + public virtual void Start(TimeSpan duration) { startTime = DateTime.Now; @@ -59,7 +101,7 @@ namespace Subsurface isRunning = true; } - public virtual void Update() + public virtual void Update(float deltaTime) { if (!isRunning) return; @@ -69,7 +111,7 @@ namespace Subsurface } } - public void End(string endMessage = "") + public virtual void End(string endMessage = "") { isRunning = false; @@ -80,8 +122,14 @@ namespace Subsurface public static void Init() { - new GameMode("Sandbox"); - new TraitorMode("Traitor"); + new GameModePreset("Single Player", typeof(SinglePlayerMode), true); + new GameModePreset("SandBox", typeof(GameMode), false); + new GameModePreset("Traitor", typeof(TraitorMode), false); + + + //new SinglePlayerMode("Single Player", true); + //new GameMode("Sandbox"); + //new TraitorMode("Traitor"); } } } diff --git a/Subsurface/GameSession/GameSession.cs b/Subsurface/GameSession/GameSession.cs index 6dda2ee69..a1b98ea42 100644 --- a/Subsurface/GameSession/GameSession.cs +++ b/Subsurface/GameSession/GameSession.cs @@ -13,8 +13,7 @@ namespace Subsurface class GameSession { public readonly TaskManager taskManager; - public readonly CrewManager crewManager; - public readonly HireManager hireManager; + protected DateTime startTime; protected DateTime endTime; @@ -34,23 +33,20 @@ namespace Subsurface private Map selectedMap; - private int day; + - public int Day + public GameSession(Map selectedMap, TimeSpan gameDuration, GameModePreset gameModePreset) + :this(selectedMap, gameDuration, gameModePreset.Instantiate()) { - get { return day; } + } public GameSession(Map selectedMap, TimeSpan gameDuration, GameMode gameMode = null) { taskManager = new TaskManager(this); - crewManager = new CrewManager(); - hireManager = new HireManager(); savePath = SaveUtil.CreateSavePath(SaveUtil.SaveFolder); - hireManager.GenerateCharacters("Content/Characters/Human/human.xml", 10); - guiRoot = new GUIFrame(new Rectangle(0,0,Game1.GraphicsWidth,Game1.GraphicsWidth), Color.Transparent); int width = 350, height = 100; @@ -75,10 +71,12 @@ namespace Subsurface } timerBar = new GUIProgressBar(new Rectangle(Game1.GraphicsWidth - 120, 20, 100, 25), Color.Gold, 0.0f, guiRoot); - - + this.gameMode = gameMode; - if (this.gameMode != null) this.gameMode.Start(Game1.NetLobbyScreen.GameDuration); + //if (gameMode != null && !gameMode.IsSinglePlayer) + //{ + // gameMode.Start(Game1.NetLobbyScreen.GameDuration); + //} startTime = DateTime.Now; endTime = startTime + gameDuration; @@ -88,53 +86,38 @@ namespace Subsurface //if (!save) return; //CreateSaveFile(selectedMapFile); - - day = 1; + } - public GameSession(string filePath) - : this(null, new TimeSpan(0,0,0,0)) + public GameSession(Map selectedMap, string savePath, string filePath) + : this(selectedMap, new TimeSpan(0,0,0,0)) { XDocument doc = ToolBox.TryLoadXml(filePath); if (doc == null) return; - day = ToolBox.GetAttributeInt(doc.Root,"day",1); + //gameMode = GameModePreset.list.Find(gm => gm.Name == "Single Player").Instantiate(); + + //day = ToolBox.GetAttributeInt(doc.Root,"day",1); foreach (XElement subElement in doc.Root.Elements()) { - if (subElement.Name.ToString().ToLower()=="crew") - { - crewManager = new CrewManager(subElement); - } + if (subElement.Name.ToString().ToLower() != "gamemode") continue; + + gameMode = new SinglePlayerMode(subElement); } - savePath = filePath; - } - - public bool TryHireCharacter(CharacterInfo characterInfo) - { - if (crewManager.Money < characterInfo.salary) return false; - - hireManager.availableCharacters.Remove(characterInfo); - crewManager.characterInfos.Add(characterInfo); - - crewManager.Money -= characterInfo.salary; - - return true; - } - - public string GetMoney() - { - return ("Money: " + crewManager.Money); + this.savePath = savePath; } public void StartShift(int scriptedEventCount = 1) { - if (crewManager.characterInfos.Count == 0) return; + //if (crewManager.characterInfos.Count == 0) return; if (Map.Loaded!=selectedMap) selectedMap.Load(); - crewManager.StartShift(); + gameMode.Start(TimeSpan.Zero); + + //crewManager.StartShift(); taskManager.StartShift(scriptedEventCount); } @@ -148,76 +131,30 @@ namespace Subsurface } else if (Game1.Client==null) - { - StringBuilder sb = new StringBuilder(); - List casualties = crewManager.characters.FindAll(c => c.IsDead); - - if (casualties.Any()) - { - sb.Append("Casualties: \n"); - foreach (Character c in casualties) - { - sb.Append(" - " + c.info.name + "\n"); - } - } - else - { - sb.Append("No casualties!"); - } - - new GUIMessageBox("Day #" + day + " is over!\n", sb.ToString()); - - - //if (saveFile == null) return false; - - //Map.Loaded.SaveAs(saveFile); - - crewManager.EndShift(); - + { Game1.LobbyScreen.Select(); - day++; - } - - for (int i = Character.characterList.Count - 1; i >= 0; i--) - { - Character.characterList.RemoveAt(i); + SaveUtil.SaveGame(savePath); } taskManager.EndShift(); + gameMode.End(); return true; } - private void CreateSaveFile(string mapName) + + public void KillCharacter(Character character) { - //string path = "Content/Data/Saves/"; + SinglePlayerMode singlePlayerMode = gameMode as SinglePlayerMode; + if (singlePlayerMode == null) return; + singlePlayerMode.crewManager.KillCharacter(character); + } - //if (!Directory.Exists(path)) - //{ - // Directory.CreateDirectory(path); - //} - - //string name = Path.GetFileNameWithoutExtension(mapName); - //string extension = Path.GetExtension(mapName); - - //int i = 0; - //while (File.Exists(path + name + i + extension)) - //{ - // i++; - //} - - //saveFile = path + name + i+extension; - - - //try - //{ - // File.Copy(mapName, saveFile); - //} - //catch (Exception e) - //{ - // DebugConsole.ThrowError("Copying map file ''" + mapName + "'' to ''" + saveFile + "'' failed", e); - //} + public bool LoadPrevious(GUIButton button, object obj) + { + SaveUtil.LoadGame(savePath); + return true; } public bool EnterChatMessage(GUITextBox textBox, string message) @@ -237,7 +174,7 @@ namespace Subsurface return true; } - + public void NewChatMessage(string text, Color color) { GUITextBlock msg = new GUITextBlock(new Rectangle(0, 0, 0, 20), text, @@ -252,20 +189,20 @@ namespace Subsurface chatBox.RemoveChild(chatBox.children.First()); } } - + public void Update(float deltaTime) { taskManager.Update(deltaTime); - if (endShiftButton!=null) endShiftButton.Enabled = !taskManager.CriticalTasks; - + //if (endShiftButton!=null) endShiftButton.Enabled = !taskManager.CriticalTasks; + endShiftButton.Enabled = true; guiRoot.Update(deltaTime); - crewManager.Update(deltaTime); + //endShiftButton.Update(deltaTime); //textBox.Update(deltaTime); - if (gameMode != null) gameMode.Update(); + if (gameMode != null) gameMode.Update(deltaTime); double duration = (endTime - startTime).TotalSeconds; double elapsedTime = (DateTime.Now-startTime).TotalSeconds; @@ -288,9 +225,11 @@ namespace Subsurface public void Draw(SpriteBatch spriteBatch) { guiRoot.Draw(spriteBatch); - crewManager.Draw(spriteBatch); + //crewManager.Draw(spriteBatch); taskManager.Draw(spriteBatch); + gameMode.Draw(spriteBatch); + //chatBox.Draw(spriteBatch); //textBox.Draw(spriteBatch); @@ -304,7 +243,11 @@ namespace Subsurface XDocument doc = new XDocument( new XElement((XName)"Gamesession")); - doc.Root.Add(new XAttribute("day", day)); + ((SinglePlayerMode)gameMode).Save(doc.Root); + + //doc.Root.Add(new XAttribute("day", day)); + + //crewManager.Save(doc.Root); try { diff --git a/Subsurface/GameSession/SinglePlayerMode.cs b/Subsurface/GameSession/SinglePlayerMode.cs new file mode 100644 index 000000000..678dae8c2 --- /dev/null +++ b/Subsurface/GameSession/SinglePlayerMode.cs @@ -0,0 +1,155 @@ +using Microsoft.Xna.Framework.Graphics; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Linq; + +namespace Subsurface +{ + class SinglePlayerMode : GameMode + { + public readonly CrewManager crewManager; + public readonly HireManager hireManager; + + private int day; + + public int Day + { + get { return day; } + } + + bool crewDead; + private float endTimer; + + public SinglePlayerMode(GameModePreset preset) + : base(preset) + { + crewManager = new CrewManager(); + hireManager = new HireManager(); + + hireManager.GenerateCharacters("Content/Characters/Human/human.xml", 10); + + day = 1; + } + + public SinglePlayerMode(XElement element) + : this(GameModePreset.list.Find(gm => gm.Name == "Single Player")) + { + day = ToolBox.GetAttributeInt(element,"day",1); + + foreach (XElement subElement in element.Elements()) + { + if (subElement.Name.ToString().ToLower() != "crew") continue; + + crewManager = new CrewManager(subElement); + } + } + + public override void Start(TimeSpan duration) + { + endTimer = 5.0f; + + crewManager.StartShift(); + } + + public bool TryHireCharacter(CharacterInfo characterInfo) + { + if (crewManager.Money < characterInfo.salary) return false; + + hireManager.availableCharacters.Remove(characterInfo); + crewManager.characterInfos.Add(characterInfo); + + crewManager.Money -= characterInfo.salary; + + return true; + } + + public string GetMoney() + { + return ("Money: " + crewManager.Money); + } + + + public override void Draw(SpriteBatch spriteBatch) + { + crewManager.Draw(spriteBatch); + + //chatBox.Draw(spriteBatch); + //textBox.Draw(spriteBatch); + + //timerBar.Draw(spriteBatch); + + //if (Game1.Client == null) endShiftButton.Draw(spriteBatch); + } + + public override void Update(float deltaTime) + { + crewManager.Update(deltaTime); + + if (!crewDead) + { + if (crewManager.characters.Find(c => !c.IsDead) == null) + { + crewDead = true; + } + } + else + { + endTimer -= deltaTime; + + if (endTimer <= 0.0f) End(""); + } + } + + public override void End(string endMessage = "") + { + StringBuilder sb = new StringBuilder(); + List casualties = crewManager.characters.FindAll(c => c.IsDead); + + if (casualties.Count == crewManager.characters.Count) + { + sb.Append("Your entire crew has died!"); + + var msgBox = new GUIMessageBox("GG", sb.ToString(), new string[] { "Load game", "Quit" }); + msgBox.Buttons[0].OnClicked += Game1.GameSession.LoadPrevious; + msgBox.Buttons[0].OnClicked += msgBox.Close; + msgBox.Buttons[1].OnClicked = Game1.LobbyScreen.QuitToMainMenu; + msgBox.Buttons[1].OnClicked += msgBox.Close; + } + else + { + if (casualties.Any()) + { + sb.Append("Casualties: \n"); + foreach (Character c in casualties) + { + sb.Append(" - " + c.info.name + "\n"); + } + } + else + { + sb.Append("No casualties!"); + } + + new GUIMessageBox("Day #" + day + " is over!\n", sb.ToString()); + + day++; + } + + crewManager.EndShift(); + for (int i = Character.characterList.Count-1; i>=0; i--) + { + Character.characterList.RemoveAt(i); + } + } + + public void Save(XElement element) + { + element.Add(new XAttribute("day", day)); + + crewManager.Save(element); + + } + } +} diff --git a/Subsurface/GameSession/TraitorMode.cs b/Subsurface/GameSession/TraitorMode.cs index e114f9fbb..c8f1b066b 100644 --- a/Subsurface/GameSession/TraitorMode.cs +++ b/Subsurface/GameSession/TraitorMode.cs @@ -9,8 +9,8 @@ namespace Subsurface Client traitor; Client target; - public TraitorMode(string name) - : base(name) + public TraitorMode(GameModePreset preset) + : base(preset) { } @@ -23,9 +23,8 @@ namespace Subsurface target = null; } - public override void Update() + public override void Update(float deltaTime) { - if (!isRunning) return; if (DateTime.Now >= endTime) diff --git a/Subsurface/Items/Components/Signal/Wire.cs b/Subsurface/Items/Components/Signal/Wire.cs index 56553d701..b2ce301cf 100644 --- a/Subsurface/Items/Components/Signal/Wire.cs +++ b/Subsurface/Items/Components/Signal/Wire.cs @@ -224,10 +224,10 @@ namespace Subsurface.Items.Components { if (nodes.Count == 0) return; - for (int i = 0; i < nodes.Count; i++) - { - GUI.DrawRectangle(spriteBatch, new Rectangle((int)nodes[i].X, (int)-nodes[i].Y, 5, 5), Color.DarkGray, true, wireSprite.Depth - 0.01f); - } + //for (int i = 0; i < nodes.Count; i++) + //{ + // GUI.DrawRectangle(spriteBatch, new Rectangle((int)nodes[i].X, (int)-nodes[i].Y, 5, 5), Color.DarkGray, true, wireSprite.Depth - 0.01f); + //} for (int i = 1; i < nodes.Count; i++) { diff --git a/Subsurface/Map/Map.cs b/Subsurface/Map/Map.cs index 64dd2ca72..05d67c70c 100644 --- a/Subsurface/Map/Map.cs +++ b/Subsurface/Map/Map.cs @@ -275,8 +275,7 @@ namespace Subsurface // DebugConsole.ThrowError("No save file selected"); // return; //} - XDocument doc = new XDocument( - new XElement((XName)name)); + XDocument doc = new XDocument(new XElement((XName)name)); foreach (MapEntity e in MapEntity.mapEntityList) { @@ -290,9 +289,9 @@ namespace Subsurface { SaveUtil.CompressStringToFile(filePath, doc.ToString()); } - catch + catch (Exception e) { - DebugConsole.ThrowError("Saving map ''" + filePath + "'' failed!"); + DebugConsole.ThrowError("Saving map ''" + filePath + "'' failed!", e); } @@ -505,12 +504,14 @@ namespace Subsurface loaded = this; } - public static void Load(string file) + public static Map Load(string file) { Unload(); Map map = new Map(file); map.Load(); + + return map; } @@ -528,9 +529,8 @@ namespace Subsurface if (Game1.GameScreen.Cam != null) Game1.GameScreen.Cam.TargetPos = Vector2.Zero; Entity.RemoveAll(); - - if (Game1.GameSession!=null) - Game1.GameSession.crewManager.EndShift(); + + if (Game1.GameSession != null) Game1.GameSession.EndShift(null, null); PhysicsBody.list.Clear(); diff --git a/Subsurface/SaveUtil.cs b/Subsurface/SaveUtil.cs index 397d46e52..beef366c4 100644 --- a/Subsurface/SaveUtil.cs +++ b/Subsurface/SaveUtil.cs @@ -9,36 +9,40 @@ namespace Subsurface { class SaveUtil { - public const string SaveFolder = "Content/SavedMaps/"; + public const string SaveFolder = "Content/Data/Saves/"; public delegate void ProgressDelegate(string sMessage); - public static void SaveGame(string directory) + public static void SaveGame(string savePath) { - if (Directory.Exists(directory)) + string tempPath = Path.GetDirectoryName(savePath) + "\\temp"; + if (!Directory.Exists(tempPath)) { - Directory.Delete(directory, true); + Directory.CreateDirectory(tempPath); } - Directory.CreateDirectory(directory); + //Directory.CreateDirectory(Path.GetDirectoryName(filePath) + "\\temp"); - Map.Loaded.SaveAs(directory+"\\map.gz"); + Map.Loaded.SaveAs(tempPath + "\\map.gz"); - Game1.GameSession.Save(directory+"\\gamesession.xml"); + Game1.GameSession.Save(tempPath + "\\gamesession.xml"); //Game1.GameSession.crewManager.Save(directory+"\\crew.xml"); - CompressDirectory(directory, directory+".save", null); + CompressDirectory(tempPath, savePath, null); - Directory.Delete(directory, true); + Directory.Delete(tempPath, true); } public static void LoadGame(string filePath) { - DecompressToDirectory(filePath+".save", Path.GetDirectoryName(filePath)+"\\temp", null); + string tempPath = Path.GetDirectoryName(filePath) + "\\temp"; - Map.Load(Path.GetDirectoryName(filePath) + "\\temp\\map.gz"); - Game1.GameSession = new GameSession(Path.GetDirectoryName(filePath) + "\\temp\\gamesession.gz"); + DecompressToDirectory(filePath, tempPath, null); + Map selectedMap = Map.Load(tempPath +"\\map.gz"); + Game1.GameSession = new GameSession(selectedMap, filePath, tempPath + "\\gamesession.xml"); + + Directory.Delete(tempPath, true); } public static string CreateSavePath(string saveFolder, string fileName="save") @@ -57,7 +61,7 @@ namespace Subsurface i++; } - return saveFolder + fileName + i + extension; + return saveFolder + fileName + i; } public static void CompressStringToFile(string fileName, string value) diff --git a/Subsurface/Screens/LobbyScreen.cs b/Subsurface/Screens/LobbyScreen.cs index f2233466a..7b573ff33 100644 --- a/Subsurface/Screens/LobbyScreen.cs +++ b/Subsurface/Screens/LobbyScreen.cs @@ -1,4 +1,7 @@ -using Microsoft.Xna.Framework; +using FarseerPhysics; +using FarseerPhysics.Dynamics; +using FarseerPhysics.Factories; +using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace Subsurface @@ -15,6 +18,13 @@ namespace Subsurface GUIListBox characterList; GUIListBox hireList; + SinglePlayerMode gameMode; + + Body previewPlatform; + Hull previewHull; + + Character previewCharacter; + public LobbyScreen() { Rectangle panelRect = new Rectangle( @@ -81,14 +91,14 @@ namespace Subsurface new GUITextBlock(new Rectangle(0, 0, 200, 25), "Crew:", Color.Transparent, Color.White, Alignment.Left, rightPanel[0]); characterList = new GUIListBox(new Rectangle(0, 30, 300, 0), Color.White, rightPanel[0]); + characterList.OnSelected = SelectCharacter; //--------------------------------------- rightPanel[1] = new GUIFrame(panelRect, GUI.style.backGroundColor); rightPanel[1].Padding = GUI.style.smallPadding; - hireList = new GUIListBox(new Rectangle(0, 30, 300, 0), Color.White, Alignment.Left, rightPanel[1]); - + hireList = new GUIListBox(new Rectangle(0, 30, 300, 0), Color.White, Alignment.Left, rightPanel[1]); hireList.OnSelected = HireCharacter; } @@ -96,16 +106,74 @@ namespace Subsurface { base.Select(); + gameMode = Game1.GameSession.gameMode as SinglePlayerMode; + //Map.Unload(); - UpdateCharacterLists(); - + UpdateCharacterLists(); + } + + public override void Deselect() + { + base.Deselect(); + + if (previewPlatform != null) + { + Game1.world.RemoveBody(previewPlatform); + previewPlatform = null; + } + + if (previewHull != null) + { + previewHull.Remove(); + previewHull = null; + } + + if (previewCharacter != null) + { + previewCharacter.Remove(); + previewCharacter = null; + } + } + + private void CreatePreviewCharacter() + { + if (previewCharacter != null) previewCharacter.Remove(); + + Vector2 pos = new Vector2(1000.0f, 1000.0f); + + previewCharacter = new Character(characterList.SelectedData as CharacterInfo, pos); + + previewCharacter.animController.isStanding = true; + + if (previewPlatform == null) + { + Body platform = BodyFactory.CreateRectangle(Game1.world, 3.0f, 1.0f, 5.0f); + platform.SetTransform(new Vector2(pos.X, pos.Y - 3.5f), 0.0f); + platform.IsStatic = true; + } + + if (previewHull == null) + { + pos = ConvertUnits.ToDisplayUnits(pos); + previewHull = new Hull(new Rectangle((int)pos.X - 100, (int)pos.Y + 100, 200, 500)); + } + + Physics.Alpha = 1.0f; + + for (int i = 0; i < 500; i++) + { + previewCharacter.animController.Update((float)Physics.step); + previewCharacter.animController.UpdateAnim((float)Physics.step); + Game1.world.Step((float)Physics.step); + } + } private void UpdateCharacterLists() { characterList.ClearChildren(); - foreach (CharacterInfo c in Game1.GameSession.crewManager.characterInfos) + foreach (CharacterInfo c in gameMode.crewManager.characterInfos) { GUITextBlock textBlock = new GUITextBlock( new Rectangle(0, 0, 0, 25), @@ -114,10 +182,11 @@ namespace Subsurface Alignment.Left, characterList); textBlock.Padding = new Vector4(10.0f, 0.0f, 0.0f, 0.0f); + textBlock.UserData = c; } hireList.ClearChildren(); - foreach (CharacterInfo c in Game1.GameSession.hireManager.availableCharacters) + foreach (CharacterInfo c in gameMode.hireManager.availableCharacters) { GUIFrame frame = new GUIFrame( new Rectangle(0, 0, 0, 25), @@ -156,8 +225,8 @@ namespace Subsurface public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch) { - if (characterList.CountChildren != Game1.GameSession.crewManager.characterInfos.Count - || hireList.CountChildren != Game1.GameSession.hireManager.availableCharacters.Count) + if (characterList.CountChildren != gameMode.crewManager.characterInfos.Count + || hireList.CountChildren != gameMode.hireManager.availableCharacters.Count) { UpdateCharacterLists(); } @@ -168,10 +237,6 @@ namespace Subsurface spriteBatch.Begin(); - - //ConstructionPrefab.list[5].sprite.Draw(spriteBatch, Vector2.Zero, new Vector2(10.0f, 1.0f), Color.White); - - leftPanel.Draw(spriteBatch); shiftPanel.Draw(spriteBatch); @@ -180,6 +245,26 @@ namespace Subsurface GUI.Draw((float)deltaTime, spriteBatch, null); spriteBatch.End(); + + if (characterList.SelectedData != null) + { + if (previewCharacter != null) + { + Vector2 position = new Vector2(characterList.Rect.Right + 100, characterList.Rect.Y + 25.0f); + + Vector2 pos = previewCharacter.Position; + pos.Y = -pos.Y; + Matrix transform = Matrix.CreateTranslation(new Vector3(-pos + position, 0.0f)); + + spriteBatch.Begin(SpriteSortMode.BackToFront, null, null, null, null, null, transform); + previewCharacter.Draw(spriteBatch); + spriteBatch.End(); + } + else + { + CreatePreviewCharacter(); + } + } } public bool SelectRightPanel(GUIButton button, object selection) @@ -189,49 +274,93 @@ namespace Subsurface return true; } + //private void CreatePreviewCharacter() + //{ + // if (Game1.Client.Character != null) Game1.Client.Character.Remove(); + + // Vector2 pos = new Vector2(1000.0f, 1000.0f); + + // Character character = new Character(Game1.Client.CharacterInfo, pos); + + // Game1.Client.Character = character; + + // character.animController.isStanding = true; + + // if (previewPlatform == null) + // { + // Body platform = BodyFactory.CreateRectangle(Game1.world, 3.0f, 1.0f, 5.0f); + // platform.SetTransform(new Vector2(pos.X, pos.Y - 2.5f), 0.0f); + // platform.IsStatic = true; + // } + + // if (previewPlatform == null) + // { + // pos = ConvertUnits.ToDisplayUnits(pos); + // new Hull(new Rectangle((int)pos.X - 100, (int)pos.Y + 100, 200, 200)); + // } + + // Physics.Alpha = 1.0f; + + // for (int i = 0; i < 500; i++) + // { + // character.animController.Update((float)Physics.step); + // character.animController.UpdateAnim((float)Physics.step); + // Game1.world.Step((float)Physics.step); + // } + //} + private string GetMoney() { - return "Money: " + ((Game1.GameSession == null) ? "" : Game1.GameSession.crewManager.Money.ToString()); + return "Money: " + ((Game1.GameSession == null) ? "" : gameMode.crewManager.Money.ToString()); } private string GetDay() { - return "Day #" + ((Game1.GameSession == null) ? "" : Game1.GameSession.Day.ToString()); + return "Day #" + ((Game1.GameSession == null) ? "" : gameMode.Day.ToString()); } private float GetWeekProgress() { if (Game1.GameSession == null) return 0.0f; - return (float)((Game1.GameSession.Day - 1) % 7) / 7.0f; + return (float)((gameMode.Day - 1) % 7) / 7.0f; + } + + private bool SelectCharacter(object selection) + { + CharacterInfo characterInfo = selection as CharacterInfo; + if (characterInfo == null) return false; + + if (Character.Controlled != null && characterInfo == Character.Controlled.info) return false; + + CreatePreviewCharacter(); + + return false; } private bool HireCharacter(object selection) { - CharacterInfo characterInfo; - try { characterInfo = (CharacterInfo)selection; } - catch { return false; } - + CharacterInfo characterInfo = selection as CharacterInfo; if (characterInfo == null) return false; - Game1.GameSession.TryHireCharacter(characterInfo); + gameMode.TryHireCharacter(characterInfo); return false; } private bool StartShift(GUIButton button, object selection) - { - - //Map.Load(Game1.GameSession.SaveFile); - + { Game1.GameSession.StartShift(); - - //EventManager.StartShift(); - Game1.GameScreen.Select(); return true; } + + public bool QuitToMainMenu(GUIButton button, object selection) + { + Game1.MainMenuScreen.Select(); + return true; + } } } diff --git a/Subsurface/Screens/MainMenu.cs b/Subsurface/Screens/MainMenu.cs index 7e9ef24f1..da30e75e1 100644 --- a/Subsurface/Screens/MainMenu.cs +++ b/Subsurface/Screens/MainMenu.cs @@ -2,16 +2,19 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Subsurface.Networking; +using System.IO; namespace Subsurface { class MainMenuScreen : Screen { - enum Tabs { Main = 0, NewGame = 1, JoinServer = 2} + enum Tabs { Main = 0, NewGame = 1, LoadGame = 2, JoinServer = 3 } GUIFrame[] menuTabs; GUIListBox mapList; + GUIListBox saveList; + GUITextBox nameBox; GUITextBox ipBox; @@ -21,7 +24,7 @@ namespace Subsurface public MainMenuScreen(Game1 game) { - menuTabs = new GUIFrame[3]; + menuTabs = new GUIFrame[Enum.GetValues(typeof(Tabs)).Length]; Rectangle panelRect = new Rectangle( Game1.GraphicsWidth / 2 - 250, @@ -32,17 +35,23 @@ namespace Subsurface menuTabs[(int)Tabs.Main].Padding = GUI.style.smallPadding; GUIButton button = new GUIButton(new Rectangle(0, 0, 0, 30), "New Game", GUI.style, Alignment.CenterX, menuTabs[(int)Tabs.Main]); - button.OnClicked = NewGameClicked; + button.UserData = (int)Tabs.NewGame; + button.OnClicked = SelectTab; //button.Enabled = false; - button = new GUIButton(new Rectangle(0, 60, 0, 30), "Join Server", GUI.style, Alignment.CenterX, menuTabs[(int)Tabs.Main]); - button.OnClicked = JoinServerClicked; + button = new GUIButton(new Rectangle(0, 60, 0, 30), "Load Game", GUI.style, Alignment.CenterX, menuTabs[(int)Tabs.Main]); + button.UserData = (int)Tabs.LoadGame; + button.OnClicked = SelectTab; - button = new GUIButton(new Rectangle(0, 120, 0, 30), "Host Server", GUI.style, Alignment.CenterX, menuTabs[(int)Tabs.Main]); + button = new GUIButton(new Rectangle(0, 120, 0, 30), "Join Server", GUI.style, Alignment.CenterX, menuTabs[(int)Tabs.Main]); + button.UserData = (int)Tabs.JoinServer; + button.OnClicked = SelectTab; + + button = new GUIButton(new Rectangle(0, 180, 0, 30), "Host Server", GUI.style, Alignment.CenterX, menuTabs[(int)Tabs.Main]); button.OnClicked = HostServerClicked; //button.Enabled = false; - button = new GUIButton(new Rectangle(0, 180, 0, 30), "Quit", GUI.style, Alignment.CenterX, menuTabs[(int)Tabs.Main]); + button = new GUIButton(new Rectangle(0, 240, 0, 30), "Quit", GUI.style, Alignment.CenterX, menuTabs[(int)Tabs.Main]); button.OnClicked = QuitClicked; //---------------------------------------------------------------------- @@ -53,7 +62,7 @@ namespace Subsurface new GUITextBlock(new Rectangle(0, 0, 0, 30), "New Game", Color.Transparent, Color.Black, Alignment.CenterX, menuTabs[(int)Tabs.NewGame]); new GUITextBlock(new Rectangle(0, 30, 0, 30), "Selected map:", Color.Transparent, Color.Black, Alignment.Left, menuTabs[(int)Tabs.NewGame]); - mapList = new GUIListBox(new Rectangle(0, 60, 200, 400), Color.White, menuTabs[1]); + mapList = new GUIListBox(new Rectangle(0, 60, 200, 400), Color.White, menuTabs[(int)Tabs.NewGame]); foreach (Map map in Map.SavedMaps) { @@ -73,6 +82,34 @@ namespace Subsurface button = new GUIButton(new Rectangle(0, 0, 100, 30), "Start", GUI.style, Alignment.Right | Alignment.Bottom, menuTabs[(int)Tabs.NewGame]); button.OnClicked = StartGame; + //---------------------------------------------------------------------- + + menuTabs[(int)Tabs.LoadGame] = new GUIFrame(panelRect, GUI.style.backGroundColor); + menuTabs[(int)Tabs.LoadGame].Padding = GUI.style.smallPadding; + + new GUITextBlock(new Rectangle(0, 0, 0, 30), "Load Game", Color.Transparent, Color.Black, Alignment.CenterX, menuTabs[(int)Tabs.LoadGame]); + + + string[] saveFiles = Directory.GetFiles(SaveUtil.SaveFolder, "*.save"); + + //new GUITextBlock(new Rectangle(0, 30, 0, 30), "Selected map:", Color.Transparent, Color.Black, Alignment.Left, menuTabs[(int)Tabs.NewGame]); + saveList = new GUIListBox(new Rectangle(0, 60, 200, 400), Color.White, menuTabs[(int)Tabs.LoadGame]); + + foreach (string saveFile in saveFiles) + { + GUITextBlock textBlock = new GUITextBlock( + new Rectangle(0, 0, 0, 25), + saveFile, + GUI.style, + Alignment.Left, + Alignment.Left, + saveList); + textBlock.Padding = new Vector4(10.0f, 0.0f, 0.0f, 0.0f); + textBlock.UserData = saveFile; + } + + button = new GUIButton(new Rectangle(0, 0, 100, 30), "Start", GUI.style, Alignment.Right | Alignment.Bottom, menuTabs[(int)Tabs.LoadGame]); + button.OnClicked = LoadGame; //---------------------------------------------------------------------- @@ -95,18 +132,12 @@ namespace Subsurface } - private bool NewGameClicked(GUIButton button, object obj) + private bool SelectTab(GUIButton button, object obj) { - selectedTab = (int)Tabs.NewGame; + selectedTab = (int)obj; return true; } - - private bool JoinServerClicked(GUIButton button, object obj) - { - selectedTab = (int)Tabs.JoinServer; - return true; - } - + private bool HostServerClicked(GUIButton button, object obj) { Game1.NetLobbyScreen.isServer = true; @@ -142,18 +173,39 @@ namespace Subsurface private bool StartGame(GUIButton button, object obj) { - Map selectedMap = mapList.SelectedData as Map; if (selectedMap == null) return false; - - Game1.GameSession = new GameSession(selectedMap, TimeSpan.Zero); + Game1.GameSession = new GameSession(selectedMap, TimeSpan.Zero, GameModePreset.list.Find(gm => gm.Name == "Single Player")); Game1.LobbyScreen.Select(); return true; } + private bool LoadGame(GUIButton button, object obj) + { + + string saveFile = saveList.SelectedData as string; + if (string.IsNullOrWhiteSpace(saveFile)) return false; + + try + { + SaveUtil.LoadGame(saveFile); + } + catch (Exception e) + { + DebugConsole.ThrowError("Loading map ''"+saveFile+"'' failed", e); + return false; + } + + + Game1.LobbyScreen.Select(); + + return true; + } + + private bool JoinServer(GUIButton button, object obj) { if (string.IsNullOrEmpty(nameBox.Text)) return false; diff --git a/Subsurface/Screens/NetLobbyScreen.cs b/Subsurface/Screens/NetLobbyScreen.cs index 4992832d9..d2e304ce3 100644 --- a/Subsurface/Screens/NetLobbyScreen.cs +++ b/Subsurface/Screens/NetLobbyScreen.cs @@ -36,9 +36,9 @@ namespace Subsurface } - public GameMode SelectedMode + public GameModePreset SelectedMode { - get { return modeList.SelectedData as GameMode; } + get { return modeList.SelectedData as GameModePreset; } } public TimeSpan GameDuration @@ -161,8 +161,10 @@ namespace Subsurface modeList.Enabled = (Game1.Server != null); //modeList.OnSelected = new GUIListBox.OnSelectedHandler(SelectEvent); - foreach (GameMode mode in GameMode.list) + foreach (GameModePreset mode in GameModePreset.list) { + if (mode.IsSinglePlayer) continue; + GUITextBlock textBlock = new GUITextBlock( new Rectangle(0, 0, 0, 25), mode.Name, @@ -192,8 +194,8 @@ namespace Subsurface modeList.OnSelected = Game1.Server.UpdateNetLobby; durationBar.OnMoved = Game1.Server.UpdateNetLobby; - if (mapList.CountChildren > 0) mapList.Select(Map.SavedMaps[0]); - if (GameMode.list.Count > 0) modeList.Select(GameMode.list[0]); + if (mapList.CountChildren > 0) mapList.Select(0); + if (GameModePreset.list.Count > 0) modeList.Select(0); } else { @@ -319,9 +321,9 @@ namespace Subsurface Vector2 pos = Game1.Client.Character.Position; pos.Y = -pos.Y; - Matrix transform = Matrix.CreateTranslation(new Vector3(-pos+position, 0.0f)); - - spriteBatch.Begin(SpriteSortMode.BackToFront, null,null,null,null,null,transform); + Matrix transform = Matrix.CreateTranslation(new Vector3(-pos + position, 0.0f)); + + spriteBatch.Begin(SpriteSortMode.BackToFront, null, null, null, null, null, transform); Game1.Client.Character.Draw(spriteBatch); spriteBatch.End(); } @@ -386,10 +388,10 @@ namespace Subsurface platform.IsStatic = true; } - if (previewPlatform==null) + if (previewHull==null) { pos = ConvertUnits.ToDisplayUnits(pos); - new Hull(new Rectangle((int)pos.X - 100, (int)pos.Y + 100, 200, 200)); + previewHull = new Hull(new Rectangle((int)pos.X - 100, (int)pos.Y + 100, 200, 200)); } Physics.Alpha = 1.0f; @@ -473,7 +475,7 @@ namespace Subsurface } else { - msg.Write(Path.GetFileName(selectedMap.FilePath)); + msg.Write(Path.GetFileName(selectedMap.Name)); msg.Write(selectedMap.MapHash.MD5Hash); } diff --git a/Subsurface/Subsurface.csproj b/Subsurface/Subsurface.csproj index 35706e91c..97d8e02d9 100644 --- a/Subsurface/Subsurface.csproj +++ b/Subsurface/Subsurface.csproj @@ -71,6 +71,7 @@ + diff --git a/Subsurface_Solution.v12.suo b/Subsurface_Solution.v12.suo index b61cebe5ae2863ef0d94656f25ebff8beda5e86a..62606192a817994e4cab53434bcefa4f7ce97fb9 100644 GIT binary patch delta 17247 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