diff --git a/BarotraumaClient/Source/GameMain.cs b/BarotraumaClient/Source/GameMain.cs index c8c6b5921..de1e0d6ef 100644 --- a/BarotraumaClient/Source/GameMain.cs +++ b/BarotraumaClient/Source/GameMain.cs @@ -218,6 +218,11 @@ namespace Barotrauma yield return CoroutineStatus.Running; JobPrefab.LoadAll(SelectedPackage.GetFilesOfType(ContentType.Jobs)); + // Add any missing jobs from the prefab into Config.JobNamePreferences. + foreach (JobPrefab job in JobPrefab.List) + { + if (!Config.JobNamePreferences.Contains(job.Name)) { Config.JobNamePreferences.Add(job.Name); } + } StructurePrefab.LoadAll(SelectedPackage.GetFilesOfType(ContentType.Structure)); TitleScreen.LoadState = 20.0f; yield return CoroutineStatus.Running; diff --git a/BarotraumaClient/Source/GameSettings.cs b/BarotraumaClient/Source/GameSettings.cs index 3d6ff6597..653301a2c 100644 --- a/BarotraumaClient/Source/GameSettings.cs +++ b/BarotraumaClient/Source/GameSettings.cs @@ -18,7 +18,17 @@ namespace Barotrauma { private GUIFrame settingsFrame; private GUIButton applyButton; + + private float soundVolume, musicVolume; + private WindowMode windowMode; + + public List jobNamePreferences; + + private KeyOrMouse[] keyMapping; + + private bool unsavedSettings; + public GUIFrame SettingsFrame { get @@ -28,12 +38,6 @@ namespace Barotrauma } } - private float soundVolume, musicVolume; - - private WindowMode windowMode; - - private KeyOrMouse[] keyMapping; - public KeyOrMouse KeyBind(InputType inputType) { return keyMapping[(int)inputType]; @@ -53,9 +57,20 @@ namespace Barotrauma get { return windowMode; } set { windowMode = value; } } + + public List JobNamePreferences + { + get { return jobNamePreferences; } + set + { + // Begin saving coroutine. Remove any existing save coroutines if one is running. + if (CoroutineManager.IsCoroutineRunning("saveCoroutine")) { CoroutineManager.StopCoroutines("saveCoroutine"); } + CoroutineManager.StartCoroutine(ApplyUnsavedChanges(), "saveCoroutine"); - private bool unsavedSettings; - + jobNamePreferences = value; + } + } + public bool UnsavedSettings { get @@ -65,6 +80,7 @@ namespace Barotrauma private set { unsavedSettings = value; + if (applyButton != null) { //applyButton.Selected = unsavedSettings; @@ -100,7 +116,16 @@ namespace Barotrauma { GraphicsWidth = 1024; GraphicsHeight = 678; + + MasterServerUrl = ""; + + SelectedContentPackage = ContentPackage.list.Any() ? ContentPackage.list[0] : new ContentPackage(""); + JobNamePreferences = new List(); + foreach (JobPrefab job in JobPrefab.List) + { + JobNamePreferences.Add(job.Name); + } return; } @@ -170,6 +195,13 @@ namespace Barotrauma } } break; + case "gameplay": + JobNamePreferences = new List(); + foreach (XElement ele in subElement.Element("jobpreferences").Elements("job")) + { + JobNamePreferences.Add(ToolBox.GetAttributeString(ele, "name", "")); + } + break; } } @@ -253,6 +285,17 @@ namespace Barotrauma } + var gameplay = new XElement("gameplay"); + + var jobPreferences = new XElement("jobpreferences"); + foreach (string jobName in JobNamePreferences) + { + jobPreferences.Add(new XElement("job", new XAttribute("name", jobName))); + } + + gameplay.Add(jobPreferences); + doc.Root.Add(gameplay); + doc.Save(filePath); } @@ -462,6 +505,13 @@ namespace Barotrauma yield return CoroutineStatus.Success; } + private IEnumerable ApplyUnsavedChanges() + { + yield return new WaitForSeconds(10.0f); + + Save("config.xml"); + } + private bool ApplyClicked(GUIButton button, object userData) { Save("config.xml"); diff --git a/BarotraumaClient/Source/Screens/NetLobbyScreen.cs b/BarotraumaClient/Source/Screens/NetLobbyScreen.cs index a10cabde1..4734b8d59 100644 --- a/BarotraumaClient/Source/Screens/NetLobbyScreen.cs +++ b/BarotraumaClient/Source/Screens/NetLobbyScreen.cs @@ -527,11 +527,17 @@ namespace Barotrauma int i = 1; - foreach (JobPrefab job in JobPrefab.List) + foreach (string jobName in GameMain.Config.JobNamePreferences) { - GUITextBlock jobText = new GUITextBlock(new Rectangle(0, 0, 0, 20), i + ". " + job.Name + " ", + JobPrefab job = JobPrefab.List.Find(x => x.Name == jobName); + if (job == null) + { + continue; + } + + GUITextBlock jobText = new GUITextBlock(new Rectangle(0, 0, 0, 20), i + ". " + job.Name + " ", "", Alignment.Left, Alignment.Right, jobList, false, - GameMain.GraphicsWidth < 1000 ? GUI.SmallFont : GUI.Font); + GameMain.GraphicsWidth<1000 ? GUI.SmallFont : GUI.Font); jobText.UserData = job; GUIButton infoButton = new GUIButton(new Rectangle(0, 2, 15, 15), "?", "", jobText); @@ -1165,6 +1171,8 @@ namespace Barotrauma private void UpdateJobPreferences(GUIListBox listBox) { listBox.Deselect(); + List jobNamePreferences = new List(); + for (int i = 0; i < listBox.children.Count; i++) { float a = (float)(i - 1) / 3.0f; @@ -1174,10 +1182,13 @@ namespace Barotrauma listBox.children[i].Color = color; listBox.children[i].HoverColor = color; listBox.children[i].SelectedColor = color; + + (listBox.children[i] as GUITextBlock).Text = (i+1) + ". " + (listBox.children[i].UserData as JobPrefab).Name; - (listBox.children[i] as GUITextBlock).Text = (i + 1) + ". " + (listBox.children[i].UserData as JobPrefab).Name; + jobNamePreferences.Add((listBox.children[i].UserData as JobPrefab).Name); } + GameMain.Config.JobNamePreferences = jobNamePreferences; } public bool TrySelectSub(string subName, string md5Hash, GUIListBox subList) diff --git a/BarotraumaClient/freetype6.dll b/BarotraumaClient/freetype6.dll index 3a94b26fe..ac931f039 100644 Binary files a/BarotraumaClient/freetype6.dll and b/BarotraumaClient/freetype6.dll differ diff --git a/BarotraumaShared/Content/Items/Button/button.xml b/BarotraumaShared/Content/Items/Button/button.xml index 35954a92c..40c4df950 100644 --- a/BarotraumaShared/Content/Items/Button/button.xml +++ b/BarotraumaShared/Content/Items/Button/button.xml @@ -15,7 +15,7 @@ - + diff --git a/BarotraumaShared/Content/Jobs.xml b/BarotraumaShared/Content/Jobs.xml index 15abeb486..0b98e6c24 100644 --- a/BarotraumaShared/Content/Jobs.xml +++ b/BarotraumaShared/Content/Jobs.xml @@ -1,6 +1,6 @@  - + @@ -18,7 +18,7 @@ - + @@ -32,11 +32,11 @@ - + - + @@ -50,11 +50,11 @@ - + - + @@ -74,7 +74,7 @@ - + diff --git a/BarotraumaShared/Source/Characters/Attack.cs b/BarotraumaShared/Source/Characters/Attack.cs index d85e8c74f..7a1af2956 100644 --- a/BarotraumaShared/Source/Characters/Attack.cs +++ b/BarotraumaShared/Source/Characters/Attack.cs @@ -75,6 +75,14 @@ namespace Barotrauma return (Duration == 0.0f) ? structureDamage : structureDamage * deltaTime; } + public Attack(float damage, float structureDamage, float bleedingDamage, float range = 0.0f) + { + Range = range; + this.damage = damage; + this.structureDamage = structureDamage; + this.bleedingDamage = bleedingDamage; + } + public Attack(XElement element) { try @@ -111,9 +119,7 @@ namespace Barotrauma Duration = ToolBox.GetAttributeFloat(element, "duration", 0.0f); priority = ToolBox.GetAttributeFloat(element, "priority", 1.0f); - - statusEffects = new List(); - + foreach (XElement subElement in element.Elements()) { switch (subElement.Name.ToString().ToLowerInvariant()) @@ -124,6 +130,10 @@ namespace Barotrauma break; #endif case "statuseffect": + if (statusEffects == null) + { + statusEffects = new List(); + } statusEffects.Add(StatusEffect.Load(subElement)); break; } @@ -150,6 +160,11 @@ namespace Barotrauma var effectType = attackResult.Damage > 0.0f ? ActionType.OnUse : ActionType.OnFailure; + if (statusEffects == null) + { + return attackResult; + } + foreach (StatusEffect effect in statusEffects) { if (effect.Targets.HasFlag(StatusEffect.TargetType.This) && attacker is Character) diff --git a/BarotraumaShared/Source/DebugConsole.cs b/BarotraumaShared/Source/DebugConsole.cs index 14162193d..1101f292f 100644 --- a/BarotraumaShared/Source/DebugConsole.cs +++ b/BarotraumaShared/Source/DebugConsole.cs @@ -402,7 +402,7 @@ namespace Barotrauma healedCharacter.AddDamage(CauseOfDeath.Damage, -healedCharacter.MaxHealth, null); healedCharacter.Oxygen = 100.0f; healedCharacter.Bleeding = 0.0f; - healedCharacter.Stun = 0.0f; + healedCharacter.SetStun(0.0f, true); } break; @@ -444,6 +444,14 @@ namespace Barotrauma case "water": if (GameMain.Client == null) Hull.EditWater = !Hull.EditWater; + break; + case "explosion": + Vector2 explosionPos = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition); + float range = 500, force = 10, damage=50; + if (commands.Length > 1) float.TryParse(commands[1], out range); + if (commands.Length > 2) float.TryParse(commands[2], out force); + if (commands.Length > 3) float.TryParse(commands[3], out damage); + new Explosion(range, force, damage, damage).Explode(explosionPos); break; case "fire": if (GameMain.Client == null) Hull.EditFire = !Hull.EditFire; diff --git a/BarotraumaShared/Source/Items/Components/Holdable/Holdable.cs b/BarotraumaShared/Source/Items/Components/Holdable/Holdable.cs index cc57a1cb4..e32101497 100644 --- a/BarotraumaShared/Source/Items/Components/Holdable/Holdable.cs +++ b/BarotraumaShared/Source/Items/Components/Holdable/Holdable.cs @@ -2,17 +2,18 @@ using FarseerPhysics; using Microsoft.Xna.Framework; using System.Collections.Generic; +using Barotrauma.Networking; +using Lidgren.Network; namespace Barotrauma.Items.Components { - class Holdable : Pickable + class Holdable : Pickable, IServerSerializable { //the position(s) in the item that the Character grabs protected Vector2[] handlePos; - - private List prevRequiredItems; - - string prevMsg; + + private InputType prevPickKey; + private string prevMsg; //the distance from the holding characters elbow to center of the physics body of the item protected Vector2 holdPos; @@ -94,25 +95,28 @@ namespace Barotrauma.Items.Components if (attachable) { - prevRequiredItems = new List(requiredItems); prevMsg = Msg; + prevPickKey = PickKey; - requiredItems.Clear(); - Msg = ""; + DeattachFromWall(); } - - if (attachedByDefault || (Screen.Selected == GameMain.EditMapScreen)) Use(1.0f); - - - //holdAngle = ToolBox.GetAttributeFloat(element, "holdangle", 0.0f); - //holdAngle = MathHelper.ToRadians(holdAngle); + + if ((Screen.Selected == GameMain.EditMapScreen)) Use(1.0f); } public override void Drop(Character dropper) { DropConnectedWires(dropper); - if (body != null) item.body = body; + if (attachable) + { + DeattachFromWall(); + + if (body != null) + { + item.body = body; + } + } if (item.body != null) item.body.Enabled = true; IsActive = false; @@ -121,7 +125,6 @@ namespace Barotrauma.Items.Components { if (dropper == null) return; picker = dropper; - } if (picker.Inventory == null) return; @@ -190,29 +193,36 @@ namespace Barotrauma.Items.Components return base.Pick(picker); } - if (!base.Pick(picker)) + if (Attached) { - return false; + return base.Pick(picker); } else { - requiredItems.Clear(); - Msg = ""; + //not attached -> pick the item instantly, ignoring picking time + return OnPicked(picker); } - - attached = false; - if (body != null) item.body = body; - //item.body.Enabled = true; - - return true; } - public override bool Use(float deltaTime, Character character = null) + protected override bool OnPicked(Character picker) { - if (!attachable || item.body == null) return true; - if (character != null && !character.IsKeyDown(InputType.Aim)) return false; + if (base.OnPicked(picker)) + { + DeattachFromWall(); - item.Drop(); + if (GameMain.Server != null && attachable) + { + item.CreateServerEvent(this); + } + return true; + } + + return false; + } + + private void AttachToWall() + { + if (!attachable) return; var containedItems = item.ContainedItems; if (containedItems != null) @@ -226,12 +236,40 @@ namespace Barotrauma.Items.Components item.body.Enabled = false; item.body = null; - - requiredItems = new List(prevRequiredItems); + Msg = prevMsg; + PickKey = prevPickKey; attached = true; + } + + private void DeattachFromWall() + { + if (!attachable) return; + + attached = false; + + //make the item pickable with the default pick key and with no specific tools/items when it's deattached + requiredItems.Clear(); + Msg = ""; + PickKey = InputType.Select; + } + + public override bool Use(float deltaTime, Character character = null) + { + if (!attachable || item.body == null) return true; + if (character != null) + { + if (!character.IsKeyDown(InputType.Aim)) return false; + if (character != null && GameMain.Server != null) + { + item.CreateServerEvent(this); + } + item.Drop(); + } + AttachToWall(); + return true; } @@ -284,13 +322,39 @@ namespace Barotrauma.Items.Components item.body = body; body.Enabled = false; } - attached = false; } + attached = false; - requiredItems.Clear(); - Msg = ""; + DeattachFromWall(); + } + } + + public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null) + { + msg.Write(Attached); + } + + public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime) + { + bool isAttached = msg.ReadBoolean(); + if (isAttached) + { + item.Drop(); + AttachToWall(); + } + else + { + DropConnectedWires(null); + + if (body != null) + { + item.body = body; + item.body.Enabled = true; + } + IsActive = false; + + DeattachFromWall(); } } - } } diff --git a/BarotraumaShared/Source/Items/Components/Holdable/Pickable.cs b/BarotraumaShared/Source/Items/Components/Holdable/Pickable.cs index 5c7d6cdfe..a98b76cbe 100644 --- a/BarotraumaShared/Source/Items/Components/Holdable/Pickable.cs +++ b/BarotraumaShared/Source/Items/Components/Holdable/Pickable.cs @@ -70,11 +70,9 @@ namespace Barotrauma.Items.Components { return OnPicked(picker); } - - } - private bool OnPicked(Character picker) + protected virtual bool OnPicked(Character picker) { if (picker.Inventory.TryPutItem(item, allowedSlots)) { diff --git a/BarotraumaShared/Source/Items/Components/ItemComponent.cs b/BarotraumaShared/Source/Items/Components/ItemComponent.cs index 722d23fd6..da0e36187 100644 --- a/BarotraumaShared/Source/Items/Components/ItemComponent.cs +++ b/BarotraumaShared/Source/Items/Components/ItemComponent.cs @@ -130,13 +130,13 @@ namespace Barotrauma.Items.Components public InputType PickKey { get; - private set; + protected set; } public InputType SelectKey { get; - private set; + protected set; } [HasDefaultValue(false, false)] diff --git a/BarotraumaShared/Source/Items/Components/Signal/ConnectionPanel.cs b/BarotraumaShared/Source/Items/Components/Signal/ConnectionPanel.cs index e12ebfeb4..f405feda8 100644 --- a/BarotraumaShared/Source/Items/Components/Signal/ConnectionPanel.cs +++ b/BarotraumaShared/Source/Items/Components/Signal/ConnectionPanel.cs @@ -53,6 +53,13 @@ namespace Barotrauma.Items.Components public override bool Select(Character picker) { + //attaching wires to items with a body is not allowed + //(signal items remove their bodies when attached to a wall) + if (item.body != null) + { + return false; + } + user = picker; IsActive = true; return true; diff --git a/BarotraumaShared/Source/Map/Explosion.cs b/BarotraumaShared/Source/Map/Explosion.cs index 49f22dff7..439dce2e6 100644 --- a/BarotraumaShared/Source/Map/Explosion.cs +++ b/BarotraumaShared/Source/Map/Explosion.cs @@ -17,6 +17,15 @@ namespace Barotrauma private bool sparks, shockwave, flames, smoke; + public Explosion(float range, float force, float damage, float structureDamage) + { + attack = new Attack(damage, structureDamage, 0.0f, range); + this.force = force; + sparks = true; + shockwave = true; + flames = true; + } + public Explosion(XElement element) { attack = new Attack(element); diff --git a/BarotraumaShared/Source/Map/Levels/CaveGenerator.cs b/BarotraumaShared/Source/Map/Levels/CaveGenerator.cs index db35a027b..dc1b8e536 100644 --- a/BarotraumaShared/Source/Map/Levels/CaveGenerator.cs +++ b/BarotraumaShared/Source/Map/Levels/CaveGenerator.cs @@ -257,8 +257,20 @@ namespace Barotrauma var targetCells = new List(); for (int i = 0; i < pathNodes.Count; i++) { - int cellIndex = FindCellIndex(pathNodes[i], cells, cellGrid, gridCellSize, 2, gridOffset); - targetCells.Add(cells[cellIndex]); + //a search depth of 2 is large enough to find a cell in almost all maps, but in case it fails, we increase the depth + int searchDepth = 2; + while (searchDepth < 5) + { + int cellIndex = FindCellIndex(pathNodes[i], cells, cellGrid, gridCellSize, searchDepth, gridOffset); + if (cellIndex > -1) + { + targetCells.Add(cells[cellIndex]); + break; + } + + searchDepth++; + } + } return GeneratePath(targetCells, cells, cellGrid, gridCellSize, limits, wanderAmount, mirror); diff --git a/BarotraumaShared/Source/Networking/NetEntityEvent/NetEntityEventManager.cs b/BarotraumaShared/Source/Networking/NetEntityEvent/NetEntityEventManager.cs index 495f4e705..186bbc7d4 100644 --- a/BarotraumaShared/Source/Networking/NetEntityEvent/NetEntityEventManager.cs +++ b/BarotraumaShared/Source/Networking/NetEntityEvent/NetEntityEventManager.cs @@ -1,10 +1,6 @@ using Lidgren.Network; using System; using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Barotrauma.Networking { @@ -12,55 +8,66 @@ namespace Barotrauma.Networking { public const int MaxEventBufferLength = 1024; public const int MaxEventsPerWrite = 64; - - //public UInt16 LastReceivedEntityEventID - //{ - // get { return lastReceivedEntityEventID; } - //} - + /// /// Write the events to the outgoing message. The recipient parameter is only needed for ServerEntityEventManager /// protected void Write(NetOutgoingMessage msg, List eventsToSync, Client recipient = null) { - msg.Write(eventsToSync[0].ID); - msg.Write((byte)eventsToSync.Count); + //write into a temporary buffer so we can write the number of events before the actual data + NetBuffer tempBuffer = new NetBuffer(); + int eventCount = 0; foreach (NetEntityEvent e in eventsToSync) { //write into a temporary buffer so we can write the length before the actual data - NetBuffer tempBuffer = new NetBuffer(); + NetBuffer tempEventBuffer = new NetBuffer(); try { - WriteEvent(tempBuffer, e, recipient); + WriteEvent(tempEventBuffer, e, recipient); } catch (Exception exception) { - DebugConsole.ThrowError("Failed to write an event for the entity \""+e.Entity+"\"", exception); + DebugConsole.ThrowError("Failed to write an event for the entity \"" + e.Entity + "\"", exception); + + //write an empty event to avoid messing up IDs + //(otherwise the clients might read the next event in the message and think its ID + //is consecutive to the previous one, even though we skipped over this broken event) + tempBuffer.Write((UInt16)0); + tempBuffer.WritePadBits(); + eventCount++; continue; } - Debug.Assert( - tempBuffer.LengthBytes < 128, - "Maximum EntityEvent size exceeded when serializing \""+e.Entity+"\"!"); - + if (msg.LengthBytes + tempBuffer.LengthBytes + tempEventBuffer.LengthBytes > NetPeerConfiguration.kDefaultMTU - 20) + { + //no more room in this packet + break; + } + //the ID has been taken by another entity (the original entity has been removed) -> write an empty event if (Entity.FindEntityByID(e.Entity.ID) != e.Entity) { //technically the clients don't have any use for these, but removing events and shifting the IDs of all //consecutive ones is so error-prone that I think this is a safer option - msg.Write((UInt16)0); - msg.WritePadBits(); + tempBuffer.Write((UInt16)0); + tempBuffer.WritePadBits(); } else { - msg.Write((UInt16)e.Entity.ID); - msg.Write((byte)tempBuffer.LengthBytes); - msg.Write(tempBuffer); - msg.WritePadBits(); + tempBuffer.Write((UInt16)e.Entity.ID); + tempBuffer.Write((byte)tempEventBuffer.LengthBytes); + tempBuffer.Write(tempEventBuffer); + tempBuffer.WritePadBits(); } + + eventCount++; } + + msg.Write(eventsToSync[0].ID); + msg.Write((byte)eventCount); + msg.Write(tempBuffer); } protected virtual void WriteEvent(NetBuffer buffer, NetEntityEvent entityEvent, Client recipient = null) diff --git a/BarotraumaShared/config.xml b/BarotraumaShared/config.xml index b59175f88..131fbcafc 100644 --- a/BarotraumaShared/config.xml +++ b/BarotraumaShared/config.xml @@ -3,4 +3,14 @@ + + + + + + + + + + \ No newline at end of file