diff --git a/Barotrauma/BarotraumaClient/ClientCode.projitems b/Barotrauma/BarotraumaClient/ClientCode.projitems index 1b79fd2eb..ab5b8970a 100644 --- a/Barotrauma/BarotraumaClient/ClientCode.projitems +++ b/Barotrauma/BarotraumaClient/ClientCode.projitems @@ -79,8 +79,8 @@ + - @@ -213,7 +213,6 @@ - Never diff --git a/Barotrauma/BarotraumaClient/Properties/AssemblyInfo.cs b/Barotrauma/BarotraumaClient/Properties/AssemblyInfo.cs index 49e4b1029..def51c503 100644 --- a/Barotrauma/BarotraumaClient/Properties/AssemblyInfo.cs +++ b/Barotrauma/BarotraumaClient/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.8.9.8")] -[assembly: AssemblyFileVersion("0.8.9.8")] +[assembly: AssemblyVersion("0.8.9.6")] +[assembly: AssemblyFileVersion("0.8.9.6")] diff --git a/Barotrauma/BarotraumaClient/Source/Characters/AI/AITarget.cs b/Barotrauma/BarotraumaClient/Source/Characters/AI/AITarget.cs index 318d1d048..6a7d9ec27 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/AI/AITarget.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/AI/AITarget.cs @@ -13,39 +13,13 @@ namespace Barotrauma var pos = new Vector2(WorldPosition.X, -WorldPosition.Y); if (soundRange > 0.0f) { - Color color; - if (Entity is Character) - { - color = Color.Yellow; - } - else if (Entity is Item) - { - color = Color.Orange; - } - else - { - color = Color.OrangeRed; - } + Color color = Entity is Character ? Color.Yellow : Color.Orange; ShapeExtensions.DrawCircle(spriteBatch, pos, SoundRange, 100, color, thickness: 1 / Screen.Selected.Cam.Zoom); - ShapeExtensions.DrawCircle(spriteBatch, pos, 3, 8, color, thickness: 2 / Screen.Selected.Cam.Zoom); } if (sightRange > 0.0f) { - Color color; - if (Entity is Character) - { - color = Color.CornflowerBlue; - } - else if (Entity is Item) - { - color = Color.CadetBlue; - } - else - { - color = Color.WhiteSmoke; - } + Color color = Entity is Character ? Color.CornflowerBlue : Color.CadetBlue; ShapeExtensions.DrawCircle(spriteBatch, pos, SightRange, 100, color, thickness: 1 / Screen.Selected.Cam.Zoom); - ShapeExtensions.DrawCircle(spriteBatch, pos, 6, 8, color, thickness: 2 / Screen.Selected.Cam.Zoom); } } } diff --git a/Barotrauma/BarotraumaClient/Source/Characters/AI/EnemyAIController.cs b/Barotrauma/BarotraumaClient/Source/Characters/AI/EnemyAIController.cs index b20e0e7c8..f80a46f5b 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/AI/EnemyAIController.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/AI/EnemyAIController.cs @@ -16,17 +16,18 @@ namespace Barotrauma if (SelectedAiTarget?.Entity != null) { - GUI.DrawLine(spriteBatch, pos, new Vector2(SelectedAiTarget.WorldPosition.X, -SelectedAiTarget.WorldPosition.Y), Color.Red * 0.5f, 0, 4); + GUI.DrawLine(spriteBatch, pos, new Vector2(SelectedAiTarget.WorldPosition.X, -SelectedAiTarget.WorldPosition.Y), Color.Red * 0.3f, 0, 5); if (wallTarget != null) { Vector2 wallTargetPos = wallTarget.Position; - if (wallTarget.Structure.Submarine != null) { wallTargetPos += wallTarget.Structure.Submarine.Position; } + if (wallTarget.Structure.Submarine != null) wallTargetPos += wallTarget.Structure.Submarine.Position; wallTargetPos.Y = -wallTargetPos.Y; - GUI.DrawRectangle(spriteBatch, wallTargetPos - new Vector2(10.0f, 10.0f), new Vector2(20.0f, 20.0f), Color.Orange, false); + GUI.DrawRectangle(spriteBatch, wallTargetPos - new Vector2(10.0f, 10.0f), new Vector2(20.0f, 20.0f), Color.Red, false); GUI.DrawLine(spriteBatch, pos, wallTargetPos, Color.Orange * 0.5f, 0, 5); } - GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 60.0f, $"{SelectedAiTarget.Entity.ToString()} ({targetValue.FormatZeroDecimal()})", Color.Red, Color.Black); + + GUI.Font.DrawString(spriteBatch, $"{SelectedAiTarget.Entity.ToString()} ({targetValue.ToString()})", pos - Vector2.UnitY * 20.0f, Color.Red); } /*GUI.Font.DrawString(spriteBatch, targetValue.ToString(), pos - Vector2.UnitY * 80.0f, Color.Red); @@ -51,15 +52,24 @@ namespace Barotrauma } GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 80.0f, State.ToString(), stateColor, Color.Black); - if (LatchOntoAI != null) + if (latchOntoAI != null) { - foreach (Joint attachJoint in LatchOntoAI.AttachJoints) + foreach (Joint attachJoint in latchOntoAI.AttachJoints) { GUI.DrawLine(spriteBatch, ConvertUnits.ToDisplayUnits(new Vector2(attachJoint.WorldAnchorA.X, -attachJoint.WorldAnchorA.Y)), - ConvertUnits.ToDisplayUnits(new Vector2(attachJoint.WorldAnchorB.X, -attachJoint.WorldAnchorB.Y)), Color.Green, 0, 4); + ConvertUnits.ToDisplayUnits(new Vector2(attachJoint.WorldAnchorB.X, -attachJoint.WorldAnchorB.Y)), Color.Orange * 0.6f, 0, 5); } + if (latchOntoAI.WallAttachPos.HasValue) + { + GUI.DrawLine(spriteBatch, pos, + ConvertUnits.ToDisplayUnits(new Vector2(latchOntoAI.WallAttachPos.Value.X, -latchOntoAI.WallAttachPos.Value.Y)), Color.Orange * 0.6f, 0, 3); + } + } + + GUI.DrawLine(spriteBatch, pos, pos + ConvertUnits.ToDisplayUnits(new Vector2(Steering.X, -Steering.Y)), Color.Blue, width: 3); + if (LatchOntoAI.WallAttachPos.HasValue) { GUI.DrawLine(spriteBatch, pos, @@ -67,34 +77,22 @@ namespace Barotrauma } } - if (steeringManager is IndoorsSteeringManager pathSteering) + GUI.DrawLine(spriteBatch, + new Vector2(Character.DrawPosition.X, -Character.DrawPosition.Y), + new Vector2(pathSteering.CurrentPath.CurrentNode.DrawPosition.X, -pathSteering.CurrentPath.CurrentNode.DrawPosition.Y), + Color.Orange * 0.6f, 0, 3); + + for (int i = 1; i < pathSteering.CurrentPath.Nodes.Count; i++) { - var path = pathSteering.CurrentPath; - if (path != null) - { - if (path.CurrentNode != null) - { - GUI.DrawLine(spriteBatch, pos, - new Vector2(path.CurrentNode.DrawPosition.X, -path.CurrentNode.DrawPosition.Y), - Color.DarkViolet, 0, 3); + GUI.DrawLine(spriteBatch, + new Vector2(pathSteering.CurrentPath.Nodes[i].DrawPosition.X, -pathSteering.CurrentPath.Nodes[i].DrawPosition.Y), + new Vector2(pathSteering.CurrentPath.Nodes[i - 1].DrawPosition.X, -pathSteering.CurrentPath.Nodes[i - 1].DrawPosition.Y), + Color.Orange * 0.6f, 0, 3); - GUI.DrawString(spriteBatch, pos - new Vector2(0, 100), "Path cost: " + path.Cost.FormatZeroDecimal(), Color.White, Color.Black * 0.5f); - } - for (int i = 1; i < path.Nodes.Count; i++) - { - var previousNode = path.Nodes[i - 1]; - var currentNode = path.Nodes[i]; - GUI.DrawLine(spriteBatch, - new Vector2(currentNode.DrawPosition.X, -currentNode.DrawPosition.Y), - new Vector2(previousNode.DrawPosition.X, -previousNode.DrawPosition.Y), - Color.Red * 0.5f, 0, 3); - - GUI.SmallFont.DrawString(spriteBatch, - currentNode.ID.ToString(), - new Vector2(currentNode.DrawPosition.X + 20, -currentNode.DrawPosition.Y - 20), - Color.Red); - } - } + GUI.SmallFont.DrawString(spriteBatch, + pathSteering.CurrentPath.Nodes[i].ID.ToString(), + new Vector2(pathSteering.CurrentPath.Nodes[i].DrawPosition.X, -pathSteering.CurrentPath.Nodes[i].DrawPosition.Y - 10), + Color.LightGreen); } GUI.DrawLine(spriteBatch, pos, pos + ConvertUnits.ToDisplayUnits(new Vector2(Character.AnimController.TargetMovement.X, -Character.AnimController.TargetMovement.Y)), Color.SteelBlue, width: 2); GUI.DrawLine(spriteBatch, pos, pos + ConvertUnits.ToDisplayUnits(new Vector2(Steering.X, -Steering.Y)), Color.Blue, width: 3); diff --git a/Barotrauma/BarotraumaClient/Source/Characters/AI/HumanAIController.cs b/Barotrauma/BarotraumaClient/Source/Characters/AI/HumanAIController.cs index 28686501e..b588b93a4 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/AI/HumanAIController.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/AI/HumanAIController.cs @@ -75,12 +75,11 @@ namespace Barotrauma GUI.SmallFont.DrawString(spriteBatch, currentNode.ID.ToString(), - new Vector2(currentNode.DrawPosition.X + 20, -currentNode.DrawPosition.Y - 20), - Color.SkyBlue); + new Vector2(currentNode.DrawPosition.X, -currentNode.DrawPosition.Y - 10), + Color.LightGreen); } } } - GUI.DrawLine(spriteBatch, pos, pos + ConvertUnits.ToDisplayUnits(new Vector2(Character.AnimController.TargetMovement.X, -Character.AnimController.TargetMovement.Y)), Color.SteelBlue, width: 2); GUI.DrawLine(spriteBatch, pos, pos + ConvertUnits.ToDisplayUnits(new Vector2(Steering.X, -Steering.Y)), Color.Blue, width: 3); //if (Character.IsKeyDown(InputType.Aim)) diff --git a/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs b/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs index c6f95eda7..c2761aeaf 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs @@ -88,13 +88,13 @@ namespace Barotrauma Collider.AngularVelocity = newAngularVelocity; float distSqrd = Vector2.DistanceSquared(newPosition, Collider.SimPosition); - float errorTolerance = character.AllowInput ? 0.01f : 0.1f; + float errorTolerance = character.AllowInput ? 0.01f : 0.2f; if (distSqrd > errorTolerance) { if (distSqrd > 10.0f || !character.AllowInput) { Collider.TargetRotation = newRotation; - SetPosition(newPosition, lerp: distSqrd < 1.0f); + SetPosition(newPosition, lerp: distSqrd < 5.0f); } else { @@ -108,8 +108,15 @@ namespace Barotrauma // -> we need to correct it manually if (!character.AllowInput) { - MainLimb.PullJointWorldAnchorB = Collider.SimPosition; - MainLimb.PullJointEnabled = true; + float mainLimbDistSqrd = Vector2.DistanceSquared(MainLimb.PullJointWorldAnchorA, Collider.SimPosition); + float mainLimbErrorTolerance = 0.1f; + //if the main limb is roughly at the correct position and the collider isn't moving (much at least), + //don't attempt to correct the position. + if (mainLimbDistSqrd > mainLimbErrorTolerance || Collider.LinearVelocity.LengthSquared() > 0.05f) + { + MainLimb.PullJointWorldAnchorB = Collider.SimPosition; + MainLimb.PullJointEnabled = true; + } } } character.MemLocalState.Clear(); diff --git a/Barotrauma/BarotraumaClient/Source/Characters/Character.cs b/Barotrauma/BarotraumaClient/Source/Characters/Character.cs index 79abaed11..615423b53 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/Character.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/Character.cs @@ -224,20 +224,6 @@ namespace Barotrauma } } - if (SelectedConstruction != null && SelectedConstruction.ActiveHUDs.Any(ic => ic.GuiFrame != null && HUD.CloseHUD(ic.GuiFrame.Rect))) - { - if (GameMain.Client != null) - { - //emulate a Select input to get the character to deselect the item server-side - keys[(int)InputType.Select].Hit = true; - } - //reset focus to prevent us from accidentally interacting with another entity - focusedItem = null; - focusedCharacter = null; - findFocusedTimer = 0.2f; - SelectedConstruction = null; - } - DoInteractionUpdate(deltaTime, mouseSimPos); } @@ -341,8 +327,6 @@ namespace Barotrauma debugInteractablesAtCursor.Clear(); debugInteractablesNearCursor.Clear(); - bool draggingItemToWorld = CharacterInventory.DraggingItemToWorld; - //reduce the amount of aim assist if an item has been selected //= can't switch selection to another item without deselecting the current one first UNLESS the cursor is directly on the item //otherwise it would be too easy to accidentally switch the selected item when rewiring items @@ -364,15 +348,6 @@ namespace Barotrauma if (item.body != null && !item.body.Enabled) continue; if (item.ParentInventory != null) continue; if (ignoredItems != null && ignoredItems.Contains(item)) continue; - - if (draggingItemToWorld) - { - if (item.OwnInventory == null || - !item.OwnInventory.CanBePut(CharacterInventory.draggingItem)) - { - continue; - } - } float distanceToItem = float.PositiveInfinity; if (item.IsInsideTrigger(displayPosition, out Rectangle transformedTrigger)) diff --git a/Barotrauma/BarotraumaClient/Source/Characters/CharacterHUD.cs b/Barotrauma/BarotraumaClient/Source/Characters/CharacterHUD.cs index a2137e660..fd0af8cf0 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/CharacterHUD.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/CharacterHUD.cs @@ -248,7 +248,7 @@ namespace Barotrauma scale: scale); } - if (!GUI.DisableItemHighlights && !Inventory.DraggingItemToWorld) + if (!GUI.DisableItemHighlights) { var hudTexts = focusedItem.GetHUDTexts(character); diff --git a/Barotrauma/BarotraumaClient/Source/Characters/CharacterInfo.cs b/Barotrauma/BarotraumaClient/Source/Characters/CharacterInfo.cs index e01a3dd2b..a4eb436a9 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/CharacterInfo.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/CharacterInfo.cs @@ -241,12 +241,10 @@ namespace Barotrauma if (!string.IsNullOrEmpty(jobIdentifier)) { jobPrefab = JobPrefab.List.Find(jp => jp.Identifier == jobIdentifier); - byte skillCount = inc.ReadByte(); - for (int i = 0; i < skillCount; i++) + for (int i = 0; i < jobPrefab.Skills.Count; i++) { - string skillIdentifier = inc.ReadString(); float skillLevel = inc.ReadSingle(); - skillLevels.Add(skillIdentifier, skillLevel); + skillLevels.Add(jobPrefab.Skills[i].Identifier, skillLevel); } } @@ -256,6 +254,7 @@ namespace Barotrauma ID = infoID, }; ch.RecreateHead(headSpriteID,(Race)race, (Gender)gender, hairIndex, beardIndex, moustacheIndex, faceAttachmentIndex); + System.Diagnostics.Debug.Assert(skillLevels.Count == ch.Job.Skills.Count); if (ch.Job != null) { foreach (KeyValuePair skill in skillLevels) @@ -263,12 +262,11 @@ namespace Barotrauma Skill matchingSkill = ch.Job.Skills.Find(s => s.Identifier == skill.Key); if (matchingSkill == null) { - ch.Job.Skills.Add(new Skill(skill.Key, skill.Value)); + DebugConsole.ThrowError("Skill \"" + skill.Key + "\" not found in character \"" + newName + "\""); continue; } matchingSkill.Level = skill.Value; } - ch.Job.Skills.RemoveAll(s => !skillLevels.ContainsKey(s.Identifier)); } return ch; } diff --git a/Barotrauma/BarotraumaClient/Source/Characters/CharacterNetworking.cs b/Barotrauma/BarotraumaClient/Source/Characters/CharacterNetworking.cs index 5753c1c97..c53b261fa 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/CharacterNetworking.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/CharacterNetworking.cs @@ -3,6 +3,7 @@ using Lidgren.Network; using Microsoft.Xna.Framework; using System; using System.Linq; +using System.Collections.Generic; namespace Barotrauma { diff --git a/Barotrauma/BarotraumaClient/Source/Characters/Health/CharacterHealth.cs b/Barotrauma/BarotraumaClient/Source/Characters/Health/CharacterHealth.cs index 777bc7775..04cb8c620 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/Health/CharacterHealth.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/Health/CharacterHealth.cs @@ -68,14 +68,6 @@ namespace Barotrauma private float dropItemAnimDuration = 0.5f; private float dropItemAnimTimer; - - public Item DroppedItem - { - get - { - return droppedItem; - } - } private Item droppedItem; private GUIComponent draggingMed; @@ -108,15 +100,13 @@ namespace Barotrauma if (openHealthWindow == value) return; if (value != null && !value.UseHealthWindow) return; - var prevOpenHealthWindow = openHealthWindow; - openHealthWindow = value; toggledThisFrame = true; if (Character.Controlled == null) { return; } if (value == null && Character.Controlled?.SelectedCharacter?.CharacterHealth != null && - Character.Controlled.SelectedCharacter.CharacterHealth == prevOpenHealthWindow && + Character.Controlled.SelectedCharacter.CharacterHealth == openHealthWindow && !Character.Controlled.SelectedCharacter.CanInventoryBeAccessed) { Character.Controlled.DeselectCharacter(); @@ -547,13 +537,11 @@ namespace Barotrauma } if (PlayerInput.KeyHit(InputType.Health) && GUI.KeyboardDispatcher.Subscriber == null && - Character.Controlled.AllowInput && !toggledThisFrame) + Character.AllowInput && Character.FocusedCharacter == null && !toggledThisFrame) { if (openHealthWindow != null) - { OpenHealthWindow = null; - } - else if (Character.Controlled == Character && Character.Controlled.FocusedCharacter == null) + else { OpenHealthWindow = this; forceAfflictionContainerUpdate = true; @@ -566,10 +554,7 @@ namespace Barotrauma HUD.CloseHUD(HUDLayoutSettings.HealthWindowAreaLeft)) { //emulate a Health input to get the character to deselect the item server-side - if (GameMain.Client != null) - { - Character.Controlled.Keys[(int)InputType.Health].Hit = true; - } + Character.Keys[(int)InputType.Health].Hit = true; OpenHealthWindow = null; } } diff --git a/Barotrauma/BarotraumaClient/Source/DebugConsole.cs b/Barotrauma/BarotraumaClient/Source/DebugConsole.cs index b8c7f8fee..4d681d60b 100644 --- a/Barotrauma/BarotraumaClient/Source/DebugConsole.cs +++ b/Barotrauma/BarotraumaClient/Source/DebugConsole.cs @@ -403,11 +403,6 @@ namespace Barotrauma AssignRelayToServer("help", false); AssignRelayToServer("verboselogging", false); AssignRelayToServer("freecam", false); -#if DEBUG - AssignRelayToServer("simulatedlatency", false); - AssignRelayToServer("simulatedloss", false); - AssignRelayToServer("simulatedduplicateschance", false); -#endif commands.Add(new Command("clientlist", "", (string[] args) => { })); AssignRelayToServer("clientlist", true); diff --git a/Barotrauma/BarotraumaClient/Source/GUI/ChatBox.cs b/Barotrauma/BarotraumaClient/Source/GUI/ChatBox.cs index 64fecb33b..308aa869f 100644 --- a/Barotrauma/BarotraumaClient/Source/GUI/ChatBox.cs +++ b/Barotrauma/BarotraumaClient/Source/GUI/ChatBox.cs @@ -178,15 +178,10 @@ namespace Barotrauma string displayedText = message.TranslatedText; string senderName = ""; - Color senderColor = Color.White; if (!string.IsNullOrWhiteSpace(message.SenderName)) { senderName = (message.Type == ChatMessageType.Private ? "[PM] " : "") + message.SenderName; } - if (message.Sender?.Info?.Job != null) - { - senderColor = Color.Lerp(message.Sender.Info.Job.Prefab.UIColor, Color.White, 0.25f); - } var msgHolder = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.0f), chatBox.Content.RectTransform, Anchor.TopCenter), style: null, color: ((chatBox.Content.CountChildren % 2) == 0) ? Color.Transparent : Color.Black * 0.1f); @@ -196,7 +191,7 @@ namespace Barotrauma { senderNameBlock = new GUITextBlock(new RectTransform(new Vector2(0.98f, 0.0f), msgHolder.RectTransform) { AbsoluteOffset = new Point((int)(5 * GUI.Scale), 0) }, - senderName, textColor: senderColor, font: GUI.SmallFont, textAlignment: Alignment.TopLeft, style: null) + senderName, textColor: Color.White, font: GUI.SmallFont, textAlignment: Alignment.TopLeft, style: null) { CanBeFocused = true }; @@ -235,7 +230,7 @@ namespace Barotrauma Visible = false }; var senderText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), popupMsg.RectTransform, Anchor.TopRight), - senderName, textColor: senderColor, font: GUI.SmallFont, textAlignment: Alignment.TopRight) + senderName, textColor: Color.White, font: GUI.SmallFont, textAlignment: Alignment.TopRight) { CanBeFocused = false }; @@ -333,7 +328,6 @@ namespace Barotrauma var popupMsg = popupMessages.Count > 0 ? popupMessages.Peek() : null; if (popupMsg != null) { - int offset = -popupMsg.Rect.Width - toggleButton.Rect.Width * 2 - (int)(50 * GUI.Scale) - (guiFrame.Rect.X - GameMain.GraphicsWidth); popupMsg.Visible = true; //popup messages appear and disappear faster when there's more pending messages popupMessageTimer += deltaTime * popupMessages.Count * popupMessages.Count; @@ -341,7 +335,7 @@ namespace Barotrauma { //move the message out of the screen and delete it popupMsg.RectTransform.ScreenSpaceOffset = - new Point((int)MathHelper.SmoothStep(offset, 10, (popupMessageTimer - PopupMessageDuration) * 5.0f), 0); + new Point((int)MathHelper.SmoothStep(-popupMsg.Rect.Width - toggleButton.Rect.Width * 2, 10, (popupMessageTimer - PopupMessageDuration) * 5.0f), 0); if (popupMessageTimer > PopupMessageDuration + 1.0f) { popupMessageTimer = 0.0f; @@ -353,7 +347,7 @@ namespace Barotrauma { //move the message on the screen popupMsg.RectTransform.ScreenSpaceOffset = new Point( - (int)MathHelper.SmoothStep(0, offset, popupMessageTimer * 5.0f), 0); + (int)MathHelper.SmoothStep(0, -popupMsg.Rect.Width - toggleButton.Rect.Width * 2 - (int)(35 * GUI.Scale), popupMessageTimer * 5.0f), 0); } } } diff --git a/Barotrauma/BarotraumaClient/Source/GUI/GUI.cs b/Barotrauma/BarotraumaClient/Source/GUI/GUI.cs index 87ccc8154..cd8a792c0 100644 --- a/Barotrauma/BarotraumaClient/Source/GUI/GUI.cs +++ b/Barotrauma/BarotraumaClient/Source/GUI/GUI.cs @@ -56,7 +56,7 @@ namespace Barotrauma private static List messages = new List(); private static Sound[] sounds; private static bool pauseMenuOpen, settingsMenuOpen; - public static GUIFrame PauseMenu { get; private set; } + private static GUIFrame pauseMenu; private static Sprite arrow, lockIcon, checkmarkIcon, timerIcon; public static KeyboardDispatcher KeyboardDispatcher { get; set; } @@ -69,9 +69,6 @@ namespace Barotrauma public static ScalableFont Font => Style?.Font; public static ScalableFont SmallFont => Style?.SmallFont; public static ScalableFont LargeFont => Style?.LargeFont; - public static ScalableFont VideoTitleFont => Style?.VideoTitleFont; - public static ScalableFont ObjectiveTitleFont => Style?.ObjectiveTitleFont; - public static ScalableFont ObjectiveNameFont => Style?.ObjectiveNameFont; public static UISprite UIGlow => Style.UIGlow; @@ -533,19 +530,14 @@ namespace Barotrauma if (list.Count == 0) { return; } foreach (var item in list) { - int index = 0; - if (updateList.Count > 0) + int i = updateList.Count - 1; + while (updateList[i].UpdateOrder > item.UpdateOrder) { - index = updateList.Count - 1; - while (updateList[index].UpdateOrder > item.UpdateOrder) - { - index--; - if (index == 0) { break; } - } + i--; } if (!updateListSet.Contains(item)) { - updateList.Insert(index, item); + updateList.Insert(Math.Max(i, 0), item); updateListSet.Add(item); } } @@ -561,7 +553,7 @@ namespace Barotrauma if (pauseMenuOpen) { - PauseMenu.AddToGUIUpdateList(); + pauseMenu.AddToGUIUpdateList(); } if (settingsMenuOpen) { @@ -1420,9 +1412,9 @@ namespace Barotrauma if (pauseMenuOpen) { - PauseMenu = new GUIFrame(new RectTransform(Vector2.One, Canvas), style: null, color: Color.Black * 0.5f); + pauseMenu = new GUIFrame(new RectTransform(Vector2.One, Canvas), style: null, color: Color.Black * 0.5f); - var pauseMenuInner = new GUIFrame(new RectTransform(new Vector2(0.13f, 0.3f), PauseMenu.RectTransform, Anchor.Center) { MinSize = new Point(200, 300) }); + var pauseMenuInner = new GUIFrame(new RectTransform(new Vector2(0.13f, 0.3f), pauseMenu.RectTransform, Anchor.Center) { MinSize = new Point(200, 300) }); var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.85f, 0.85f), pauseMenuInner.RectTransform, Anchor.Center)) { diff --git a/Barotrauma/BarotraumaClient/Source/GUI/GUIStyle.cs b/Barotrauma/BarotraumaClient/Source/GUI/GUIStyle.cs index 2b3317682..c7f947b4e 100644 --- a/Barotrauma/BarotraumaClient/Source/GUI/GUIStyle.cs +++ b/Barotrauma/BarotraumaClient/Source/GUI/GUIStyle.cs @@ -12,9 +12,6 @@ namespace Barotrauma public ScalableFont Font { get; private set; } public ScalableFont SmallFont { get; private set; } public ScalableFont LargeFont { get; private set; } - public ScalableFont VideoTitleFont { get; private set; } - public ScalableFont ObjectiveTitleFont { get; private set; } - public ScalableFont ObjectiveNameFont { get; private set; } public Sprite CursorSprite { get; private set; } @@ -51,15 +48,6 @@ namespace Barotrauma case "largefont": LargeFont = new ScalableFont(subElement, graphicsDevice); break; - case "objectivetitle": - ObjectiveTitleFont = new ScalableFont(subElement, graphicsDevice); - break; - case "objectivename": - ObjectiveNameFont = new ScalableFont(subElement, graphicsDevice); - break; - case "videotitle": - VideoTitleFont = new ScalableFont(subElement, graphicsDevice); - break; case "cursor": CursorSprite = new Sprite(subElement); break; diff --git a/Barotrauma/BarotraumaClient/Source/GUI/HUDLayoutSettings.cs b/Barotrauma/BarotraumaClient/Source/GUI/HUDLayoutSettings.cs index 1a0b79cd9..199cdb9d6 100644 --- a/Barotrauma/BarotraumaClient/Source/GUI/HUDLayoutSettings.cs +++ b/Barotrauma/BarotraumaClient/Source/GUI/HUDLayoutSettings.cs @@ -50,11 +50,6 @@ namespace Barotrauma get; private set; } - public static Rectangle ObjectiveAnchor - { - get; private set; - } - public static Rectangle InventoryAreaLower { get; private set; @@ -161,10 +156,6 @@ namespace Barotrauma new Rectangle(Padding, CrewArea.Y, chatBoxWidth, chatBoxHeight) : new Rectangle(GameMain.GraphicsWidth - Padding - chatBoxWidth, CrewArea.Y, chatBoxWidth, chatBoxHeight); - int objectiveAnchorWidth = (int)(250 * GUI.Scale); - int objectiveAnchorOffsetY = (int)(100 * GUI.Scale); - ObjectiveAnchor = new Rectangle(GameMain.GraphicsWidth - Padding - objectiveAnchorWidth, CrewArea.Y + crewAreaHeight + objectiveAnchorOffsetY, objectiveAnchorWidth, 0); - int lowerAreaHeight = (int)Math.Min(GameMain.GraphicsHeight * 0.25f, 280); InventoryAreaLower = new Rectangle(Padding, GameMain.GraphicsHeight - lowerAreaHeight, GameMain.GraphicsWidth - Padding * 2, lowerAreaHeight); @@ -200,9 +191,6 @@ namespace Barotrauma { public static bool CloseHUD(Rectangle rect) { - // Always close when hitting escape - if (PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape)) { return true; } - //don't close when the cursor is on a UI element if (GUI.MouseOn != null) return false; diff --git a/Barotrauma/BarotraumaClient/Source/GameMain.cs b/Barotrauma/BarotraumaClient/Source/GameMain.cs index 09c332ced..ded8f0896 100644 --- a/Barotrauma/BarotraumaClient/Source/GameMain.cs +++ b/Barotrauma/BarotraumaClient/Source/GameMain.cs @@ -601,10 +601,8 @@ namespace Barotrauma { ((GUIMessageBox)GUIMessageBox.VisibleBox).Close(); } - else if ((Character.Controlled?.SelectedConstruction == null || !Character.Controlled.SelectedConstruction.ActiveHUDs.Any(ic => ic.GuiFrame != null)) - && Inventory.SelectedSlot == null && CharacterHealth.OpenHealthWindow == null) + else // Otherwise toggle pausing. { - // Otherwise toggle pausing, unless another window/interface is open. GUI.TogglePauseMenu(); } } diff --git a/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs b/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs index 0762fbd00..1ef20b629 100644 --- a/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs +++ b/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs @@ -130,12 +130,7 @@ namespace Barotrauma { OnEnterMessage = (textbox, text) => { - if (Character.Controlled?.Info == null) - { - textbox.Deselect(); - textbox.Text = ""; - return true; - } + if (Character.Controlled == null) { return true; } textbox.TextColor = ChatMessage.MessageColor[(int)ChatMessageType.Default]; @@ -981,7 +976,7 @@ namespace Barotrauma foreach (GUIComponent c in prevCharacterListBox.Content.Children) { Character character = c.UserData as Character; - if (character == null || character.IsDead || character.Removed) continue; + if (character == null) continue; AddCharacter(character); DisplayCharacterOrder(character, character.CurrentOrder); } diff --git a/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/SinglePlayerCampaign.cs b/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/SinglePlayerCampaign.cs index b4a7414e3..01caedf9e 100644 --- a/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/SinglePlayerCampaign.cs +++ b/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/SinglePlayerCampaign.cs @@ -44,7 +44,7 @@ namespace Barotrauma ContextualTutorial = Tutorial.Tutorials.Find(t => t is ContextualTutorial) as ContextualTutorial; - if (ContextualTutorial.Selected) // Selected when starting a new game -> initialize + if (ContextualTutorial.Selected && !ContextualTutorial.Initialized) // Selected when starting a new game -> initialize { ContextualTutorial.Initialize(); } diff --git a/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/Tutorials/ContextualTutorial.cs b/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/Tutorials/ContextualTutorial.cs index 292f85b37..843b7566e 100644 --- a/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/Tutorials/ContextualTutorial.cs +++ b/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/Tutorials/ContextualTutorial.cs @@ -1,10 +1,9 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Xml.Linq; using System; using Microsoft.Xna.Framework; using Barotrauma.Items.Components; using System.Linq; -using Microsoft.Xna.Framework.Input; namespace Barotrauma.Tutorials { @@ -14,59 +13,38 @@ namespace Barotrauma.Tutorials public static bool ContentRunning = false; public static bool Initialized = false; - private enum ContentTypes { None = 0, Video = 1, TextOnly = 2 }; + private enum ContentTypes { None = 0, Video = 1, Text = 2 }; private TutorialSegment activeSegment; private List segments; - private VideoPlayer videoPlayer; - + private SpriteSheetPlayer spriteSheetPlayer; private Steering navConsole; private Reactor reactor; private Sonar sonar; private Vector2 subStartingPosition; private List crew; - private Character mechanic; - private Character engineer; - private Character injuredMember = null; - private List> characterTimeOnSonar; private float requiredTimeOnSonar = 5f; private bool started = false; private string playableContentPath; - + private float tutorialTimer; + private float degrading2ActivationCountdown; private bool disableTutorialOnDeficiencyFound = true; - private GUIFrame holderFrame, objectiveFrame; - private List activeObjectives = new List(); - private string objectiveTranslated; - - private float floodTutorialTimer = 0.0f; - private const float floodTutorialDelay = 2.0f; - private float medicalTutorialTimer = 0.0f; - private const float medicalTutorialDelay = 2.0f; - - private Point screenResolution; - private float prevUIScale; - private class TutorialSegment { - public string Id; - public string Objective; + public string Name; public ContentTypes ContentType; - public XElement TextContent; - public XElement VideoContent; + public XElement Content; public bool IsTriggered; - public GUIButton ReplayButton; - public GUITextBlock LinkedTitle, LinkedText; public TutorialSegment(XElement config) { - Id = config.GetAttributeString("id", "Missing ID"); - Objective = TextManager.Get(config.GetAttributeString("objective", string.Empty), true); + Name = config.GetAttributeString("name", "Missing Name"); Enum.TryParse(config.GetAttributeString("contenttype", "None"), true, out ContentType); IsTriggered = config.GetAttributeBool("istriggered", false); @@ -75,11 +53,10 @@ namespace Barotrauma.Tutorials case ContentTypes.None: break; case ContentTypes.Video: - VideoContent = config.Element("Video"); - TextContent = config.Element("Text"); + Content = config.Element("Video"); break; - case ContentTypes.TextOnly: - TextContent = config.Element("Text"); + case ContentTypes.Text: + Content = config.Element("Text"); break; } } @@ -100,17 +77,17 @@ namespace Barotrauma.Tutorials public override void Initialize() { - for (int i = 0; i < segments.Count; i++) - { - segments[i].IsTriggered = false; - } - if (Initialized) return; Initialized = true; base.Initialize(); - videoPlayer = new VideoPlayer(); + spriteSheetPlayer = new SpriteSheetPlayer(); characterTimeOnSonar = new List>(); + + for (int i = 0; i < segments.Count; i++) + { + segments[i].IsTriggered = false; + } } public void LoadPartiallyComplete(XElement element) @@ -134,6 +111,15 @@ namespace Barotrauma.Tutorials } } + private void PreloadVideoContent() + { + for (int i = 0; i < segments.Count; i++) + { + if (segments[i].ContentType != ContentTypes.Video || segments[i].IsTriggered) continue; + spriteSheetPlayer.PreloadContent(playableContentPath, "tutorial", segments[i].Name, segments[i].Content); + } + } + public void SavePartiallyComplete(XElement element) { XElement tutorialElement = new XElement("contextualtutorial"); @@ -165,13 +151,13 @@ namespace Barotrauma.Tutorials { if (!Initialized) return; + PreloadVideoContent(); + base.Start(); - injuredMember = null; - activeObjectives.Clear(); - objectiveTranslated = TextManager.Get("Objective"); - CreateObjectiveFrame(); + activeSegment = null; - tutorialTimer = floodTutorialTimer = medicalTutorialTimer = 0.0f; + tutorialTimer = 0.0f; + degrading2ActivationCountdown = -1; subStartingPosition = Vector2.Zero; characterTimeOnSonar.Clear(); @@ -207,9 +193,6 @@ namespace Barotrauma.Tutorials } crew = GameMain.GameSession.CrewManager.GetCharacters().ToList(); - mechanic = CrewMemberWithJob("mechanic"); - engineer = CrewMemberWithJob("engineer"); - Completed = true; // Trigger completed at start to prevent the contextual tutorial from automatically activating on starting new campaigns after this one started = true; } @@ -225,185 +208,71 @@ namespace Barotrauma.Tutorials public void Stop() { started = ContentRunning = Initialized = false; - videoPlayer.Remove(); - videoPlayer = null; + spriteSheetPlayer.Remove(); + spriteSheetPlayer = null; characterTimeOnSonar = null; } - private void CreateObjectiveFrame() - { - holderFrame = new GUIFrame(new RectTransform(new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight), GUI.Canvas, Anchor.Center)); - objectiveFrame = new GUIFrame(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.ObjectiveAnchor, holderFrame.RectTransform), style: null); - - for (int i = 0; i < activeObjectives.Count; i++) - { - CreateObjectiveGUI(activeObjectives[i], i); - } - - screenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight); - prevUIScale = GUI.Scale; - } - public override void AddToGUIUpdateList() { - if (videoPlayer != null) - { - videoPlayer.AddToGUIUpdateList(order: 100); - } - - if (GUI.DisableHUD) return; - if (GameMain.GraphicsWidth != screenResolution.X || GameMain.GraphicsHeight != screenResolution.Y || prevUIScale != GUI.Scale) - { - CreateObjectiveFrame(); - } - - if (objectiveFrame != null && activeObjectives.Count > 0) - { - objectiveFrame.AddToGUIUpdateList(order: -1); - } base.AddToGUIUpdateList(); + if (spriteSheetPlayer != null) + { + spriteSheetPlayer.AddToGUIUpdateList(); + } } public override void Update(float deltaTime) { - if (videoPlayer != null) - { - videoPlayer.Update(); - } - - if (infoBox != null) - { - if (PlayerInput.KeyHit(Keys.Enter) || PlayerInput.KeyHit(Keys.Escape)) - { - CloseInfoFrame(null, null); - } - } - if (!started || ContentRunning) return; deltaTime *= 0.5f; - + for (int i = 0; i < segments.Count; i++) { - if (segments[i].IsTriggered || activeObjectives.Contains(segments[i])) continue; + if (segments[i].IsTriggered) continue; if (CheckContextualTutorials(i, deltaTime)) // Found a relevant tutorial, halt finding new ones { break; } } - - for (int i = 0; i < activeObjectives.Count; i++) - { - CheckActiveObjectives(activeObjectives[i], deltaTime); - } - } - - private void ClosePreTextAndTriggerVideoCallback() - { - videoPlayer.LoadContent(playableContentPath, new VideoPlayer.VideoSettings(activeSegment.VideoContent), new VideoPlayer.TextSettings(activeSegment.VideoContent), activeSegment.Id, true, activeSegment.Objective, CurrentSegmentStopCallback); } private void CurrentSegmentStopCallback() { - if (!string.IsNullOrEmpty(activeSegment.Objective)) - { - AddNewObjective(activeSegment); - } - activeSegment = null; ContentRunning = false; } - private void AddNewObjective(TutorialSegment segment) - { - activeObjectives.Add(segment); - CreateObjectiveGUI(segment, activeObjectives.Count - 1); - } - - private void CreateObjectiveGUI(TutorialSegment segment, int index) - { - Point replayButtonSize = new Point((int)(GUI.ObjectiveNameFont.MeasureString(segment.Objective).X * GUI.Scale), (int)(GUI.ObjectiveNameFont.MeasureString(segment.Objective).Y * 1.45f * GUI.Scale)); - - segment.ReplayButton = new GUIButton(new RectTransform(replayButtonSize, objectiveFrame.RectTransform, Anchor.TopRight, Pivot.TopRight) { AbsoluteOffset = new Point(0, (replayButtonSize.Y + (int)(20f * GUI.Scale)) * index) }, style: null); - segment.ReplayButton.OnClicked += (GUIButton btn, object userdata) => - { - ReplaySegmentVideo(segment); - return true; - }; - - int yOffset = (int)((GUI.ObjectiveNameFont.MeasureString(objectiveTranslated).Y / 2f + 5) * GUI.Scale); - segment.LinkedTitle = new GUITextBlock(new RectTransform(new Point(replayButtonSize.X, yOffset), segment.ReplayButton.RectTransform, Anchor.Center, Pivot.BottomCenter) { AbsoluteOffset = new Point((int)(10 * GUI.Scale), 0) }, objectiveTranslated, textColor: Color.White, font: GUI.ObjectiveTitleFont, textAlignment: Alignment.CenterRight); - segment.LinkedText = new GUITextBlock(new RectTransform(new Point(replayButtonSize.X, yOffset), segment.ReplayButton.RectTransform, Anchor.Center, Pivot.TopCenter) { AbsoluteOffset = new Point((int)(10 * GUI.Scale), 0) }, segment.Objective, textColor: new Color(4, 180, 108), font: GUI.ObjectiveNameFont, textAlignment: Alignment.CenterRight); - - segment.LinkedTitle.TextScale = segment.LinkedText.TextScale = GUI.Scale; - - segment.LinkedTitle.Color = segment.LinkedTitle.HoverColor = segment.LinkedTitle.PressedColor = segment.LinkedTitle.SelectedColor = Color.Transparent; - segment.LinkedText.Color = segment.LinkedText.HoverColor = segment.LinkedText.PressedColor = segment.LinkedText.SelectedColor = Color.Transparent; - segment.ReplayButton.Color = segment.ReplayButton.HoverColor = segment.ReplayButton.PressedColor = segment.ReplayButton.SelectedColor = Color.Transparent; - } - - private void RemoveCompletedObjective(TutorialSegment objective) - { - objective.IsTriggered = true; - - int checkMarkHeight = (int)(objective.ReplayButton.Rect.Height * 1.2f); - int checkMarkWidth = (int)(checkMarkHeight * 0.93f); - - Color color = new Color(4, 180, 108); - RectTransform rectTA = new RectTransform(new Point(checkMarkWidth, checkMarkHeight), objective.ReplayButton.RectTransform, Anchor.BottomLeft, Pivot.BottomLeft); - rectTA.AbsoluteOffset = new Point(-rectTA.Rect.Width - 5, 0); - GUIImage checkmark = new GUIImage(rectTA, "CheckMark"); - checkmark.Color = color; - - RectTransform rectTB = new RectTransform(new Vector2(1.1f, .8f), objective.LinkedText.RectTransform, Anchor.Center, Pivot.Center); - GUIImage stroke = new GUIImage(rectTB, "Stroke"); - stroke.Color = color; - - CoroutineManager.StartCoroutine(WaitForObjectiveEnd(objective)); - } - - private IEnumerable WaitForObjectiveEnd(TutorialSegment objective) - { - yield return new WaitForSeconds(2.0f); - objectiveFrame.RemoveChild(objective.ReplayButton); - activeObjectives.Remove(objective); - - for (int i = 0; i < activeObjectives.Count; i++) - { - activeObjectives[i].ReplayButton.RectTransform.AbsoluteOffset = new Point(0, (activeObjectives[i].ReplayButton.Rect.Height + 20) * i); - } - } - private bool CheckContextualTutorials(int index, float deltaTime) { switch (index) { case 0: // Welcome: Game Start [Text] - if (tutorialTimer < 1.0f) + if (tutorialTimer < 0.5f) { tutorialTimer += deltaTime; return false; } break; - case 1: // Command Reactor: 2 seconds after 'Welcome' dismissed and only if no command given to start reactor [Video] - if (!segments[0].IsTriggered) return false; - if (tutorialTimer < 3.0f) + case 1: // Command Reactor: 10 seconds after 'Welcome' dismissed and only if no command given to start reactor [Video] + if (tutorialTimer < 10.5f) { tutorialTimer += deltaTime; if (HasOrder("operatereactor")) { segments[index].IsTriggered = true; - tutorialTimer = 2.5f; + tutorialTimer = 10.5f; } return false; } break; - case 2: // Nav Console: 2 seconds after 'Command Reactor' dismissed or if nav console is activated [Video] - if (!IsReactorPoweredUp()) return false; // Do not advance tutorial based on this segment if reactor has not been powered up + case 2: // Nav Console: 20 seconds after 'Command Reactor' dismissed or if nav console is activated [Video] if (Character.Controlled?.SelectedConstruction != navConsole.Item) - { - if (tutorialTimer < 4.5f) + { + if (!segments[1].IsTriggered) return false; // Do not advance tutorial timer based on this segment if reactor has not been powered up + if (tutorialTimer < 30.5f) { tutorialTimer += deltaTime; return false; @@ -411,13 +280,20 @@ namespace Barotrauma.Tutorials } else { - tutorialTimer = 4.5f; - } + if (!segments[1].IsTriggered || !HasOrder("operatereactor")) // If reactor has not been powered up or ordered to be, default to that one first + { + if (tutorialTimer < 10.5f) + { + tutorialTimer = 10.5f; + } + return false; + } - TriggerTutorialSegment(index, GameMain.GameSession.EndLocation.Name); - return true; + tutorialTimer = 30.5f; + } + break; case 3: // Objective: Travel ~150 meters and while sub is not flooding [Text] - if (Vector2.Distance(subStartingPosition, Submarine.MainSub.WorldPosition) < 8000f || IsFlooding()) + if (Vector2.Distance(subStartingPosition, Submarine.MainSub.WorldPosition) < 12000f || IsFlooding()) { return false; } @@ -431,11 +307,6 @@ namespace Barotrauma.Tutorials { return false; } - else if (floodTutorialTimer < floodTutorialDelay) - { - floodTutorialTimer += deltaTime; - return false; - } break; case 5: // Reactor: Player uses reactor for the first time [Video] if (Character.Controlled?.SelectedConstruction != reactor.Item) @@ -450,23 +321,19 @@ namespace Barotrauma.Tutorials } break; case 7: // Degrading1: Any equipment degrades to 50% health or less and player has not assigned any crew to perform maintenance [Text] - if ((mechanic == null || mechanic.IsDead) && (engineer == null || engineer.IsDead)) // Both engineer and mechanic are dead or do not exist -> do not display - { - return false; - } - bool degradedEquipmentFound = false; foreach (Item item in Item.ItemList) { - if (!item.Repairables.Any() || item.Condition > 50.0f) continue; + if (!item.Repairables.Any() || item.ConditionPercentage > 50) continue; degradedEquipmentFound = true; break; } if (degradedEquipmentFound) { - if (HasOrder("repairsystems", "jobspecific")) + degrading2ActivationCountdown = 5f; + if (HasOrder("repairsystems")) { segments[index].IsTriggered = true; return false; @@ -477,44 +344,43 @@ namespace Barotrauma.Tutorials return false; } break; - case 8: // Medical: Crewmember is injured but not killed [Video] - - if (injuredMember == null) + case 8: // Degrading2: 5 seconds after 'Degrading1' dismissed, and only if player has not assigned any crew to perform maintenance [Video] + if (degrading2ActivationCountdown == -1f) { - for (int i = 0; i < crew.Count; i++) + return false; + } + else if (degrading2ActivationCountdown > 0.0f) + { + degrading2ActivationCountdown -= deltaTime; + if (HasOrder("repairsystems")) { - Character member = crew[i]; - if (member.Vitality < member.MaxVitality && !member.IsDead) - { - injuredMember = member; - break; - } + segments[index].IsTriggered = true; } return false; } - else if (medicalTutorialTimer < medicalTutorialDelay) + break; + case 9: // Medical: Crewmember is injured but not killed [Video] + bool injuredFound = false; + for (int i = 0; i < crew.Count; i++) { - medicalTutorialTimer += deltaTime; - return false; + Character member = crew[i]; + if (member.Vitality < member.MaxVitality && !member.IsDead) + { + injuredFound = true; + break; + } } - else - { - TriggerTutorialSegment(index, new string[] { injuredMember.Info.DisplayName, - (injuredMember.Info.Gender == Gender.Male) ? TextManager.Get("PronounPossessiveMale").ToLower() : TextManager.Get("PronounPossessiveFemale").ToLower() }); - return true; - } - case 9: // Approach1: Destination is within ~100m [Video] + + if (!injuredFound) return false; + break; + case 10: // Approach1: Destination is within ~100m [Video] if (Vector2.Distance(Submarine.MainSub.WorldPosition, Level.Loaded.EndPosition) > 8000f) { return false; } - else - { - TriggerTutorialSegment(index, GameMain.GameSession.EndLocation.Name); - return true; - } - case 10: // Approach2: Sub is docked [Text] + break; + case 11: // Approach2: Sub is docked [Text] if (!Submarine.MainSub.AtEndPosition || Submarine.MainSub.DockedTo.Count == 0) { return false; @@ -526,128 +392,11 @@ namespace Barotrauma.Tutorials return true; } - private bool HasObjective(string objectiveName) - { - for (int i = 0; i < activeObjectives.Count; i++) - { - if (activeObjectives[i].Id == objectiveName) return true; - } - - return false; - } - - private void CheckActiveObjectives(TutorialSegment objective, float deltaTime) - { - switch(objective.Id) - { - case "ReactorCommand": // Reactor commanded - if (!IsReactorPoweredUp()) - { - if (!HasOrder("operatereactor")) return; - } - break; - case "NavConsole": // traveled 50 meters - if (Vector2.Distance(subStartingPosition, Submarine.MainSub.WorldPosition) < 4000f) - { - return; - } - break; - case "Flood": // Hull breaches repaired - if (IsFlooding()) return; - break; - case "Medical": - if (injuredMember != null && !injuredMember.IsDead) - { - if (injuredMember.CharacterHealth.DroppedItem == null) return; - } - break; - case "EnemyOnSonar": // Enemy dispatched - if (HasEnemyOnSonarForDuration(deltaTime)) - { - return; - } - break; - case "Degrading": // Fixed - if (mechanic != null && !mechanic.IsDead) - { - HumanAIController humanAI = mechanic.AIController as HumanAIController; - if (mechanic.CurrentOrder?.AITag != "repairsystems" || humanAI.CurrentOrderOption != "jobspecific") - { - return; - } - } - - if (engineer != null && !engineer.IsDead) - { - HumanAIController humanAI = engineer.AIController as HumanAIController; - if (engineer.CurrentOrder?.AITag != "repairsystems" || humanAI.CurrentOrderOption != "jobspecific") - { - return; - } - } - - break; - case "Approach1": // Wait until docked - if (!Submarine.MainSub.AtEndPosition || Submarine.MainSub.DockedTo.Count == 0) - { - return; - } - break; - } - - RemoveCompletedObjective(objective); - } - - private bool IsReactorPoweredUp() - { - float load = 0.0f; - List connections = reactor.Item.Connections; - if (connections != null && connections.Count > 0) - { - foreach (Connection connection in connections) - { - if (!connection.IsPower) continue; - foreach (Connection recipient in connection.Recipients) - { - if (!(recipient.Item is Item it)) continue; - - PowerTransfer pt = it.GetComponent(); - if (pt == null) continue; - - load = Math.Max(load, pt.PowerLoad); - } - } - } - - return Math.Abs(load + reactor.CurrPowerConsumption) < 10; - } - - private Character CrewMemberWithJob(string job) + private bool HasOrder(string aiTag) { for (int i = 0; i < crew.Count; i++) { - if (crew[i].Info.Job.Name == job) return crew[i]; - } - - return null; - } - - private bool HasOrder(string aiTag, string option = null) - { - for (int i = 0; i < crew.Count; i++) - { - if (crew[i].CurrentOrder?.AITag == aiTag) - { - if (option == null) - { - return true; - } - else - { - HumanAIController humanAI = crew[i].AIController as HumanAIController; - return humanAI.CurrentOrderOption == option; - } - } + if (crew[i].CurrentOrder?.AITag == aiTag) return true; } return false; @@ -657,12 +406,10 @@ namespace Barotrauma.Tutorials { foreach (Gap gap in Gap.GapList) { - if (gap.ConnectedWall == null || gap.IsRoomToRoom) continue; + if (gap.ConnectedWall == null) continue; if (gap.ConnectedDoor != null || gap.Open <= 0.0f) continue; if (gap.Submarine == null) continue; - if (gap.Submarine.IsOutpost) continue; if (gap.Submarine != Submarine.MainSub) continue; - if (gap.FlowTargetHull == null || gap.FlowTargetHull.WaterPercentage <= 0.0f) continue; return true; } @@ -700,51 +447,27 @@ namespace Barotrauma.Tutorials } } - return characterTimeOnSonar.Find(ct => ct.Second >= requiredTimeOnSonar && !ct.First.IsDead) != null; + return characterTimeOnSonar.Find(ct => ct.Second >= requiredTimeOnSonar) != null; } private void TriggerTutorialSegment(int index, params object[] args) { - Inventory.draggingItem = null; ContentRunning = true; activeSegment = segments[index]; - - string tutorialText = TextManager.GetFormatted(activeSegment.TextContent.GetAttributeString("tag", ""), true, args); - string objectiveText = string.Empty; - - if (!string.IsNullOrEmpty(activeSegment.Objective)) - { - if (args.Length == 0) - { - objectiveText = activeSegment.Objective; - } - else - { - objectiveText = string.Format(activeSegment.Objective, args); - } - - activeSegment.Objective = objectiveText; - } - else - { - activeSegment.IsTriggered = true; // Complete at this stage only if no related objective - } + activeSegment.IsTriggered = true; switch (activeSegment.ContentType) { case ContentTypes.None: break; case ContentTypes.Video: - infoBox = CreateInfoFrame(TextManager.Get(activeSegment.Id), tutorialText, - activeSegment.TextContent.GetAttributeInt("width", 300), - activeSegment.TextContent.GetAttributeInt("height", 80), - activeSegment.TextContent.GetAttributeString("anchor", "Center"), true, ClosePreTextAndTriggerVideoCallback); + spriteSheetPlayer.LoadContent(playableContentPath, activeSegment.Content, activeSegment.Name, true, true, CurrentSegmentStopCallback); break; - case ContentTypes.TextOnly: - infoBox = CreateInfoFrame(TextManager.Get(activeSegment.Id), tutorialText, - activeSegment.TextContent.GetAttributeInt("width", 300), - activeSegment.TextContent.GetAttributeInt("height", 80), - activeSegment.TextContent.GetAttributeString("anchor", "Center"), true, CurrentSegmentStopCallback); + case ContentTypes.Text: + infoBox = CreateInfoFrame(TextManager.Get(activeSegment.Name), TextManager.GetFormatted(activeSegment.Content.GetAttributeString("tag", ""), false, args), + activeSegment.Content.GetAttributeInt("width", 300), + activeSegment.Content.GetAttributeInt("height", 80), + activeSegment.Content.GetAttributeString("anchor", "Center"), true, CurrentSegmentStopCallback); break; } @@ -756,13 +479,6 @@ namespace Barotrauma.Tutorials CoroutineManager.StartCoroutine(WaitToStop()); // Completed } - private void ReplaySegmentVideo(TutorialSegment segment) - { - if (ContentRunning) return; - ContentRunning = true; - videoPlayer.LoadContent(playableContentPath, new VideoPlayer.VideoSettings(segment.VideoContent), new VideoPlayer.TextSettings(segment.VideoContent), segment.Id, true, callback: () => ContentRunning = false); - } - private IEnumerable WaitToStop() { while (ContentRunning) yield return null; diff --git a/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/Tutorials/Tutorial.cs b/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/Tutorials/Tutorial.cs index 69a5e98c9..e2c38e71b 100644 --- a/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/Tutorials/Tutorial.cs +++ b/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/Tutorials/Tutorial.cs @@ -121,7 +121,7 @@ namespace Barotrauma.Tutorials public virtual void AddToGUIUpdateList() { - if (infoBox != null) infoBox.AddToGUIUpdateList(order: 100); + if (infoBox != null) infoBox.AddToGUIUpdateList(); } public virtual void Update(float deltaTime) @@ -188,19 +188,17 @@ namespace Barotrauma.Tutorials Anchor anchor = Anchor.TopRight; Enum.TryParse(anchorStr, out anchor); - var infoBlock = new GUIFrame(new RectTransform(new Point((int)(width * GUI.Scale), (int)(height * GUI.Scale)), GUI.Canvas, anchor) { AbsoluteOffset = new Point(20) }); + var infoBlock = new GUIFrame(new RectTransform(new Point(width, height), GUI.Canvas, anchor) { AbsoluteOffset = new Point(20) }); infoBlock.Flash(Color.Green); if (title.Length > 0) { var titleBlock = new GUITextBlock(new RectTransform(new Vector2(1f, .35f), infoBlock.RectTransform, Anchor.TopCenter, - Pivot.TopCenter), title, font: GUI.VideoTitleFont, textAlignment: Alignment.Center, textColor: new Color(253, 174, 0)); - titleBlock.TextScale = GUI.Scale; + Pivot.TopCenter), title, font: GUI.LargeFont, textAlignment: Alignment.Center); } var textBlock = new GUITextBlock(new RectTransform(new Vector2(0.9f, 1f), infoBlock.RectTransform, Anchor.BottomCenter), text, wrap: true); - textBlock.TextScale = GUI.Scale; infoBoxClosedCallback = callback; diff --git a/Barotrauma/BarotraumaClient/Source/GameSettings.cs b/Barotrauma/BarotraumaClient/Source/GameSettings.cs index e8d6e8cfe..22e8604d9 100644 --- a/Barotrauma/BarotraumaClient/Source/GameSettings.cs +++ b/Barotrauma/BarotraumaClient/Source/GameSettings.cs @@ -14,13 +14,12 @@ namespace Barotrauma { private enum Tab { + General, Graphics, Audio, Controls, } - private readonly Point MinSupportedResolution = new Point(1024, 540); - private GUIFrame settingsFrame; private GUIButton applyButton; @@ -72,79 +71,16 @@ namespace Barotrauma private void CreateSettingsFrame() { - settingsFrame = new GUIFrame(new RectTransform(new Vector2(0.8f, 0.8f), GUI.Canvas, Anchor.Center)); + settingsFrame = new GUIFrame(new RectTransform(new Point(500, 500), GUI.Canvas, Anchor.Center)); - var settingsFramePadding = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.9f), settingsFrame.RectTransform, Anchor.TopCenter) { RelativeOffset = new Vector2(0.0f, 0.05f) }, style: null); + new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), settingsFrame.RectTransform), + TextManager.Get("Settings"), textAlignment: Alignment.Center, font: GUI.LargeFont); - /// General tab -------------------------------------------------------------- - - var leftPanel = new GUILayoutGroup(new RectTransform(new Vector2(0.25f, 1.0f), settingsFramePadding.RectTransform, Anchor.TopLeft)); - - new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), leftPanel.RectTransform), - TextManager.Get("Settings"), textAlignment: Alignment.TopLeft, font: GUI.LargeFont); - - var generalLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), leftPanel.RectTransform, Anchor.TopLeft)); - - new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), generalLayoutGroup.RectTransform), TextManager.Get("ContentPackages")); - var contentPackageList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.75f), generalLayoutGroup.RectTransform)) - { - CanBeFocused = false - }; - - foreach (ContentPackage contentPackage in ContentPackage.List) - { - var tickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.067f), contentPackageList.Content.RectTransform, minSize: new Point(0, 15)), contentPackage.Name) - { - UserData = contentPackage, - OnSelected = SelectContentPackage, - Selected = SelectedContentPackages.Contains(contentPackage) - }; - if (!contentPackage.IsCompatible()) - { - tickBox.TextColor = Color.Red; - tickBox.Enabled = false; - tickBox.ToolTip = TextManager.Get(contentPackage.GameVersion <= new Version(0, 0, 0, 0) ? "IncompatibleContentPackageUnknownVersion" : "IncompatibleContentPackage") - .Replace("[packagename]", contentPackage.Name) - .Replace("[packageversion]", contentPackage.GameVersion.ToString()) - .Replace("[gameversion]", GameMain.Version.ToString()); - } - else if (contentPackage.CorePackage && !contentPackage.ContainsRequiredCorePackageFiles(out List missingContentTypes)) - { - tickBox.TextColor = Color.Red; - tickBox.Enabled = false; - tickBox.ToolTip = TextManager.Get("ContentPackageMissingCoreFiles") - .Replace("[packagename]", contentPackage.Name) - .Replace("[missingfiletypes]", string.Join(", ", missingContentTypes)); - } - } - - new GUITextBlock(new RectTransform(new Vector2(0.92f, 0.05f), generalLayoutGroup.RectTransform), TextManager.Get("Language")); - var languageDD = new GUIDropDown(new RectTransform(new Vector2(0.92f, 0.05f), generalLayoutGroup.RectTransform)); - foreach (string language in TextManager.AvailableLanguages) - { - languageDD.AddItem(TextManager.Get("Language." + language), language); - } - languageDD.SelectItem(TextManager.Language); - languageDD.OnSelected = (guiComponent, obj) => - { - string newLanguage = obj as string; - if (newLanguage == Language) return true; - - UnsavedSettings = true; - Language = newLanguage; - - new GUIMessageBox(TextManager.Get("RestartRequiredLabel"), TextManager.Get("RestartRequiredLanguage")); - - return true; - }; - - var rightPanel = new GUILayoutGroup(new RectTransform(new Vector2(1.0f - leftPanel.RectTransform.RelativeSize.X, 0.95f), - settingsFramePadding.RectTransform, Anchor.TopRight)); - - var tabButtonHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), rightPanel.RectTransform, Anchor.TopCenter), isHorizontal: true); - - var paddedFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 1.0f), rightPanel.RectTransform, Anchor.Center), style: null); + var paddedFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.8f), settingsFrame.RectTransform, Anchor.Center) + { RelativeOffset = new Vector2(0.0f, 0.06f) }, style: null); + var tabButtonHolder = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.05f), settingsFrame.RectTransform, Anchor.TopCenter) + { RelativeOffset = new Vector2(0.0f, 0.11f) }, isHorizontal: true); tabs = new GUIFrame[Enum.GetValues(typeof(Tab)).Length]; tabButtons = new GUIButton[tabs.Length]; @@ -168,10 +104,10 @@ namespace Barotrauma var leftColumn = new GUILayoutGroup(new RectTransform(new Vector2(0.46f, 0.95f), tabs[(int)Tab.Graphics].RectTransform, Anchor.CenterLeft) { RelativeOffset = new Vector2(0.02f, 0.0f) }) - { RelativeSpacing = 0.01f }; + { RelativeSpacing = 0.01f, Stretch = true }; var rightColumn = new GUILayoutGroup(new RectTransform(new Vector2(0.46f, 0.95f), tabs[(int)Tab.Graphics].RectTransform, Anchor.CenterRight) { RelativeOffset = new Vector2(0.02f, 0.0f) }) - { RelativeSpacing = 0.01f }; + { RelativeSpacing = 0.01f, Stretch = true }; var supportedDisplayModes = new List(); foreach (DisplayMode mode in GraphicsAdapter.DefaultAdapter.SupportedDisplayModes) @@ -199,7 +135,6 @@ namespace Barotrauma foreach (DisplayMode mode in supportedDisplayModes) { - if (mode.Width < MinSupportedResolution.X || mode.Height < MinSupportedResolution.Y) { continue; } resolutionDD.AddItem(mode.Width + "x" + mode.Height, mode); if (GraphicsWidth == mode.Width && GraphicsHeight == mode.Height) resolutionDD.SelectItem(mode); } @@ -238,6 +173,9 @@ namespace Barotrauma return true; }; + //spacing + new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), leftColumn.RectTransform), style: null); + GUITickBox vsyncTickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), leftColumn.RectTransform), TextManager.Get("EnableVSync")) { ToolTip = TextManager.Get("EnableVSyncToolTip"), @@ -253,6 +191,8 @@ namespace Barotrauma Selected = VSyncEnabled }; + //spacing + new GUIFrame(new RectTransform(new Vector2(1.0f, 0.5f), leftColumn.RectTransform), style: null); GUITextBlock particleLimitText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("ParticleLimit")); GUIScrollBar particleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), barSize: 0.1f) @@ -269,6 +209,8 @@ namespace Barotrauma }; particleScrollBar.OnMoved(particleScrollBar, particleScrollBar.BarScroll); + //spacing + new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), rightColumn.RectTransform), style: null); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("LosEffect")); var losModeDD = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform)); losModeDD.AddItem(TextManager.Get("LosModeNone"), LosMode.None); @@ -287,6 +229,8 @@ namespace Barotrauma return true; }; + //spacing + new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), style: null); GUITextBlock LightText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("LightMapScale")) { ToolTip = TextManager.Get("LightMapScaleToolTip") @@ -307,6 +251,8 @@ namespace Barotrauma }; lightScrollBar.OnMoved(lightScrollBar, lightScrollBar.BarScroll); + //spacing + new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), style: null); new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("SpecularLighting")) { ToolTip = TextManager.Get("SpecularLightingToolTip"), @@ -319,6 +265,8 @@ namespace Barotrauma } }; + //spacing + new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), rightColumn.RectTransform), style: null); new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("ChromaticAberration")) { ToolTip = TextManager.Get("ChromaticAberrationToolTip"), @@ -331,6 +279,9 @@ namespace Barotrauma } }; + //spacing + new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), style: null); + GUITextBlock HUDScaleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("HUDScale")); GUIScrollBar HUDScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), barSize: 0.1f) @@ -349,6 +300,9 @@ namespace Barotrauma }; HUDScaleScrollBar.OnMoved(HUDScaleScrollBar, HUDScaleScrollBar.BarScroll); + //spacing + new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), rightColumn.RectTransform), style: null); + GUITextBlock inventoryScaleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("InventoryScale")); GUIScrollBar inventoryScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), barSize: 0.1f) { @@ -365,6 +319,9 @@ namespace Barotrauma }; inventoryScaleScrollBar.OnMoved(inventoryScaleScrollBar, inventoryScaleScrollBar.BarScroll); + //spacing + new GUIFrame(new RectTransform(new Vector2(1.0f, 0.2f), rightColumn.RectTransform), style: null); + /// Audio tab ---------------------------------------------------------------- var audioSliders = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.4f), tabs[(int)Tab.Audio].RectTransform, Anchor.TopCenter) @@ -610,10 +567,9 @@ namespace Barotrauma var inputNames = Enum.GetValues(typeof(InputType)); for (int i = 0; i < inputNames.Length; i++) { - var inputContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.06f), inputFrame.RectTransform)) { Stretch = true, IsHorizontal = true, RelativeSpacing = 0.05f }; - new GUITextBlock(new RectTransform(new Vector2(0.3f, 1.0f), inputContainer.RectTransform, Anchor.TopLeft) { MinSize = new Point(150, 0) }, - TextManager.Get("InputType." + ((InputType)i)) + ": ", font: GUI.SmallFont) { ForceUpperCase = true }; - var keyBox = new GUITextBox(new RectTransform(new Vector2(0.7f, 1.0f), inputContainer.RectTransform), + var inputContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.06f), inputFrame.RectTransform), style: null); + new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), inputContainer.RectTransform), TextManager.Get("InputType." + ((InputType)i)) + ": ", font: GUI.SmallFont); + var keyBox = new GUITextBox(new RectTransform(new Vector2(0.4f, 1.0f), inputContainer.RectTransform, Anchor.TopRight), text: keyMapping[i].ToString(), font: GUI.SmallFont) { UserData = i @@ -637,11 +593,88 @@ namespace Barotrauma }; aimAssistSlider.OnMoved(aimAssistSlider, aimAssistSlider.BarScroll); + /// General tab -------------------------------------------------------------- + + var generalLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), tabs[(int)Tab.General].RectTransform, Anchor.Center) + { RelativeOffset = new Vector2(0.0f, 0.0f) }) { RelativeSpacing = 0.01f, Stretch = true }; + + new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), generalLayoutGroup.RectTransform), TextManager.Get("ContentPackages")); + var contentPackageList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.4f), generalLayoutGroup.RectTransform)) + { + CanBeFocused = false + }; + + + foreach (ContentPackage contentPackage in ContentPackage.List) + { + var tickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.1f), contentPackageList.Content.RectTransform, minSize: new Point(0, 15)), contentPackage.Name) + { + UserData = contentPackage, + OnSelected = SelectContentPackage, + Selected = SelectedContentPackages.Contains(contentPackage) + }; + if (!contentPackage.IsCompatible()) + { + tickBox.TextColor = Color.Red; + tickBox.Enabled = false; + tickBox.ToolTip = TextManager.Get(contentPackage.GameVersion <= new Version(0, 0, 0, 0) ? "IncompatibleContentPackageUnknownVersion" : "IncompatibleContentPackage") + .Replace("[packagename]", contentPackage.Name) + .Replace("[packageversion]", contentPackage.GameVersion.ToString()) + .Replace("[gameversion]", GameMain.Version.ToString()); + } + else if (contentPackage.CorePackage && !contentPackage.ContainsRequiredCorePackageFiles(out List missingContentTypes)) + { + tickBox.TextColor = Color.Red; + tickBox.Enabled = false; + tickBox.ToolTip = TextManager.Get("ContentPackageMissingCoreFiles") + .Replace("[packagename]", contentPackage.Name) + .Replace("[missingfiletypes]", string.Join(", ", missingContentTypes)); + } + } + GUITextBlock aimAssistText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), controlsLayoutGroup.RectTransform), TextManager.Get("AimAssist")); + GUIScrollBar aimAssistSlider = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), controlsLayoutGroup.RectTransform), + barSize: 0.1f) + { + UserData = aimAssistText, + BarScroll = MathUtils.InverseLerp(0.0f, 5.0f, AimAssistAmount), + OnMoved = (scrollBar, scroll) => + { + ChangeSliderText(scrollBar, scroll); + AimAssistAmount = MathHelper.Lerp(0.0f, 5.0f, scroll); + return true; + }, + Step = 0.1f + }; + aimAssistSlider.OnMoved(aimAssistSlider, aimAssistSlider.BarScroll); + + //spacing + new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), generalLayoutGroup.RectTransform), style: null); + + new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), generalLayoutGroup.RectTransform), TextManager.Get("Language")); + var languageDD = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.05f), generalLayoutGroup.RectTransform)); + foreach (string language in TextManager.AvailableLanguages) + { + languageDD.AddItem(TextManager.Get("Language." + language), language); + } + languageDD.SelectItem(TextManager.Language); + languageDD.OnSelected = (guiComponent, obj) => + { + string newLanguage = obj as string; + if (newLanguage == Language) return true; + + UnsavedSettings = true; + Language = newLanguage; + + new GUIMessageBox(TextManager.Get("RestartRequiredLabel"), TextManager.Get("RestartRequiredLanguage")); + + return true; + }; + //spacing new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), generalLayoutGroup.RectTransform), style: null); new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), buttonArea.RectTransform, Anchor.BottomLeft), - TextManager.Get("Cancel"), style: "GUIButtonLarge") + TextManager.Get("Cancel")) { IgnoreLayoutGroups = true, OnClicked = (x, y) => @@ -657,7 +690,7 @@ namespace Barotrauma }; applyButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), buttonArea.RectTransform, Anchor.BottomRight), - TextManager.Get("ApplySettingsButton"), style: "GUIButtonLarge") + TextManager.Get("ApplySettingsButton")) { IgnoreLayoutGroups = true, Enabled = false @@ -665,7 +698,7 @@ namespace Barotrauma applyButton.OnClicked = ApplyClicked; UnsavedSettings = false; // Reset unsaved settings to false once the UI has been created - SelectTab(Tab.Graphics); + SelectTab(Tab.General); } private void SelectTab(Tab tab) diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Door.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Door.cs index 8a1858095..d536e9fe5 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Door.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Door.cs @@ -93,7 +93,7 @@ namespace Barotrauma.Items.Components public void Draw(SpriteBatch spriteBatch, bool editing) { - Color color = item.SpriteColor; + Color color = Color.White; if (brokenSprite == null) { //broken doors turn black if no broken sprite has been configured @@ -108,7 +108,7 @@ namespace Barotrauma.Items.Components weldSpritePos.Y = -weldSpritePos.Y; weldedSprite.Draw(spriteBatch, - weldSpritePos, item.SpriteColor * (stuck / 100.0f), scale: item.Scale); + weldSpritePos, Color.White * (stuck / 100.0f), scale: item.Scale); } if (openState == 1.0f) diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/ItemComponent.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/ItemComponent.cs index cc9829018..c136956f9 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/ItemComponent.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/ItemComponent.cs @@ -175,6 +175,63 @@ namespace Barotrauma.Items.Components } } } + + public void ApplyTo(RectTransform target) + { + if (RelativeOffset.HasValue) + { + target.RelativeOffset = RelativeOffset.Value; + } + else if (AbsoluteOffset.HasValue) + { + target.AbsoluteOffset = AbsoluteOffset.Value; + } + if (RelativeSize.HasValue) + { + target.RelativeSize = RelativeSize.Value; + } + else if (AbsoluteSize.HasValue) + { + target.NonScaledSize = AbsoluteSize.Value; + } + if (Anchor.HasValue) + { + target.Anchor = Anchor.Value; + } + if (Pivot.HasValue) + { + target.Pivot = Pivot.Value; + } + else + { + target.Pivot = RectTransform.MatchPivotToAnchor(target.Anchor); + } + target.RecalculateChildren(true, true); + } + } + + public GUIFrame GuiFrame { get; protected set; } + + [Serialize(false, false)] + public bool AllowUIOverlap + { + get; + set; + } + + private ItemComponent linkToUIComponent; + [Serialize("", false)] + public string LinkUIToComponent + { + get; + set; + } + + [Serialize(0, false)] + public int HudPriority + { + get; + private set; } private bool shouldMuffleLooping; @@ -496,11 +553,7 @@ namespace Barotrauma.Items.Components { msg = msg.Replace("[" + inputType.ToString().ToLowerInvariant() + "]", GameMain.Config.KeyBind(inputType).ToString()); } - DisplayMsg = msg; - } - else - { - DisplayMsg = Msg; + Msg = msg; } } } diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/LevelResource.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/LevelResource.cs index b7951ce7d..8a73e7e2e 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/LevelResource.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/LevelResource.cs @@ -7,7 +7,7 @@ namespace Barotrauma.Items.Components { public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime) { - deattachTimer = msg.ReadSingle(); + DeattachTimer = msg.ReadSingle(); if (deattachTimer >= DeattachDuration) { holdable.DeattachFromWall(); diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/MiniMap.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/MiniMap.cs index cd5c74d3e..b0ad89489 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/MiniMap.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/MiniMap.cs @@ -25,12 +25,6 @@ namespace Barotrauma.Items.Components DrawHUDBack, null); submarineContainer = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.85f), GuiFrame.RectTransform, Anchor.Center), style: null); - new GUICustomComponent(new RectTransform(new Vector2(0.9f, 0.85f), GuiFrame.RectTransform, Anchor.Center), - DrawHUDFront, null) - { - CanBeFocused = false - }; - hullInfoFrame = new GUIFrame(new RectTransform(new Vector2(0.13f, 0.13f), GUI.Canvas, minSize: new Point(250, 150)), style: "InnerFrame") { @@ -53,7 +47,7 @@ namespace Barotrauma.Items.Components public override void AddToGUIUpdateList() { base.AddToGUIUpdateList(); - hullInfoFrame.AddToGUIUpdateList(); + if (hasPower) hullInfoFrame.AddToGUIUpdateList(); } public override void OnMapLoaded() @@ -102,27 +96,6 @@ namespace Barotrauma.Items.Components } } - private void DrawHUDFront(SpriteBatch spriteBatch, GUICustomComponent container) - { - foreach (GUIComponent child in submarineContainer.Children.First().Children) - { - if (child.UserData is Hull hull) - { - if (hull.Submarine == null || !hull.Submarine.IsOutpost) { continue; } - string text = TextManager.Get("MiniMapOutpostDockingInfo").Replace("[outpost]", hull.Submarine.Name); - Vector2 textSize = GUI.Font.MeasureString(text); - Vector2 textPos = child.Center; - if (textPos.X + textSize.X / 2 > submarineContainer.Rect.Right) - textPos.X -= ((textPos.X + textSize.X / 2) - submarineContainer.Rect.Right) + 10; - if (textPos.X - textSize.X / 2 < submarineContainer.Rect.X) - textPos.X += (submarineContainer.Rect.X - (textPos.X - textSize.X / 2)) + 10; - GUI.DrawString(spriteBatch, textPos - textSize / 2, text, - Color.Orange * (float)Math.Abs(Math.Sin(Timing.TotalTime)), Color.Black * 0.8f); - break; - } - } - } - private void DrawHUDBack(SpriteBatch spriteBatch, GUICustomComponent container) { Hull mouseOnHull = null; @@ -248,7 +221,7 @@ namespace Barotrauma.Items.Components foreach (Submarine sub in subs) { if (sub.HullVertices == null) { continue; } - + Rectangle worldBorders = sub.GetDockedBorders(); worldBorders.Location += sub.WorldPosition.ToPoint(); @@ -269,8 +242,6 @@ namespace Barotrauma.Items.Components GUI.DrawLine(spriteBatch, center + start, center + end, Color.DarkCyan * Rand.Range(0.3f, 0.35f), width: 10); } } - - } private void GetLinkedHulls(Hull hull, List linkedHulls) diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Reactor.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Reactor.cs index d46b2029a..d0e98ead9 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Reactor.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Reactor.cs @@ -107,10 +107,7 @@ namespace Barotrauma.Items.Components { var warningBtn = new GUIButton(new RectTransform(new Point(buttonWidth, buttonHeight), columnLeft.RectTransform) { AbsoluteOffset = new Point((i % buttonsPerRow) * (buttonWidth + spacing), (int)Math.Floor(i / (float)buttonsPerRow) * (buttonHeight + spacing)) }, - TextManager.Get(warningTexts[i]), style: "IndicatorButton") - { - CanBeFocused = false - }; + TextManager.Get(warningTexts[i]), style: "IndicatorButton"); var btnText = warningBtn.GetChild(); btnText.Font = GUI.SmallFont; diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Sonar.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Sonar.cs index a4935fd62..da6fe39d0 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Sonar.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Sonar.cs @@ -785,9 +785,8 @@ namespace Barotrauma.Items.Components foreach (Limb limb in c.AnimController.Limbs) { - if (!limb.body.Enabled) { continue; } - float pointDist = ((limb.WorldPosition - pingSource) * displayScale).LengthSquared(); + if (limb.SimPosition == Vector2.Zero || pointDist > DisplayRadius * DisplayRadius) continue; if (pointDist > prevPingRadiusSqr && pointDist < pingRadiusSqr) diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Steering.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Steering.cs index dc51312ea..7fc554b01 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Steering.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Steering.cs @@ -484,14 +484,26 @@ namespace Barotrauma.Items.Components user = Character.Controlled; } } - if (!AutoPilot && Character.DisableControls && GUI.KeyboardDispatcher.Subscriber == null) + if (!AutoPilot && Character.DisableControls) { steeringAdjustSpeed = character == null ? 0.2f : MathHelper.Lerp(0.2f, 1.0f, character.GetSkillLevel("helm") / 100.0f); Vector2 input = Vector2.Zero; - if (PlayerInput.KeyDown(InputType.Left)) { input -= Vector2.UnitX; } - if (PlayerInput.KeyDown(InputType.Right)) { input += Vector2.UnitX; } - if (PlayerInput.KeyDown(InputType.Up)) { input += Vector2.UnitY; } - if (PlayerInput.KeyDown(InputType.Down)) { input -= Vector2.UnitY; } + if (PlayerInput.KeyDown(InputType.Left)) + { + input -= Vector2.UnitX; + } + if (PlayerInput.KeyDown(InputType.Right)) + { + input += Vector2.UnitX; + } + if (PlayerInput.KeyDown(InputType.Up)) + { + input += Vector2.UnitY; + } + if (PlayerInput.KeyDown(InputType.Down)) + { + input -= Vector2.UnitY; + } if (PlayerInput.KeyDown(Keys.LeftShift)) { SteeringInput += input * deltaTime * 200; diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/RepairTool.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/RepairTool.cs index f8f82df50..9b14effac 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/RepairTool.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/RepairTool.cs @@ -1,7 +1,6 @@ using Barotrauma.Particles; using FarseerPhysics; using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; using System; using System.Collections.Generic; using System.Linq; @@ -12,21 +11,12 @@ using System.Xml.Linq; namespace Barotrauma.Items.Components { partial class RepairTool -#if DEBUG - : IDrawableComponent -#endif { public ParticleEmitter ParticleEmitter { get; private set; } -#if DEBUG - public Vector2 DrawSize - { - get { return GameMain.DebugDraw ? Vector2.One * Range : Vector2.Zero; } - } -#endif private List ParticleEmitterHitStructure = new List(); private List ParticleEmitterHitCharacter = new List(); @@ -132,17 +122,5 @@ namespace Barotrauma.Items.Components emitter.Second.Emit(deltaTime, particlePos, item.CurrentHull, particleAngle + MathHelper.Pi, -particleAngle + MathHelper.Pi); } } -#if DEBUG - public void Draw(SpriteBatch spriteBatch, bool editing) - { - if (GameMain.DebugDraw && IsActive) - { - GUI.DrawLine(spriteBatch, - new Vector2(debugRayStartPos.X, -debugRayStartPos.Y), - new Vector2(debugRayEndPos.X, -debugRayEndPos.Y), - Color.Yellow); - } - } -#endif } } diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Repairable.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Repairable.cs index 7e8fdc706..b1871cfe0 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Repairable.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Repairable.cs @@ -1,6 +1,4 @@ -using Barotrauma.Networking; -using Barotrauma.Particles; -using Lidgren.Network; +using Barotrauma.Particles; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System.Collections.Generic; @@ -114,7 +112,8 @@ namespace Barotrauma.Items.Components System.Diagnostics.Debug.Assert(GuiFrame.GetChild(0) is GUILayoutGroup, "Repair UI hierarchy has changed, could not find skill texts"); foreach (GUIComponent c in GuiFrame.GetChild(0).Children) { - if (!(c.UserData is Skill skill)) continue; + Skill skill = c.UserData as Skill; + if (skill == null) continue; GUITextBlock textBlock = (GUITextBlock)c; if (character.GetSkillLevel(skill.Identifier) < skill.Level) @@ -127,15 +126,5 @@ namespace Barotrauma.Items.Components } } } - - public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime) - { - deteriorationTimer = msg.ReadSingle(); - } - - public void ClientWrite(NetBuffer msg, object[] extraData = null) - { - //no need to write anything, just letting the server know we started repairing - } } } diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Signal/Connection.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Signal/Connection.cs index 53c247abe..e1d57fa4b 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Signal/Connection.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Signal/Connection.cs @@ -169,6 +169,20 @@ namespace Barotrauma.Items.Components if (draggingConnected.Connect(this, !alreadyConnected, true)) { var otherConnection = draggingConnected.OtherConnection(this); +#if SERVER + //TODO: ffs + if (otherConnection == null) + { + GameServer.Log(Character.Controlled.LogName + " connected a wire to " + + Item.Name + " (" + Name + ")", ServerLog.MessageType.ItemInteraction); + } + else + { + GameServer.Log(Character.Controlled.LogName + " connected a wire from " + + Item.Name + " (" + Name + ") to " + otherConnection.item.Name + " (" + otherConnection.Name + ")", ServerLog.MessageType.ItemInteraction); + } +#endif + SetWire(index, draggingConnected); } } diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/StatusHUD.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/StatusHUD.cs index b56dd8629..8b6b5ea03 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/StatusHUD.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/StatusHUD.cs @@ -181,7 +181,7 @@ namespace Barotrauma.Items.Components Dictionary combinedAfflictionStrengths = new Dictionary(); foreach (Affliction affliction in allAfflictions) { - if (affliction.Strength < affliction.Prefab.ShowInHealthScannerThreshold || affliction.Strength <= 0.0f) continue; + if (affliction.Strength < affliction.Prefab.ActivationThreshold || affliction.Strength <= 0.0f) continue; if (combinedAfflictionStrengths.ContainsKey(affliction.Prefab)) { combinedAfflictionStrengths[affliction.Prefab] += affliction.Strength; diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Turret.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Turret.cs index 6e42a2949..4ed31820f 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Turret.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Turret.cs @@ -237,13 +237,13 @@ namespace Barotrauma.Items.Components railSprite?.Draw(spriteBatch, drawPos, - item.SpriteColor, + Color.White, rotation + MathHelper.PiOver2, item.Scale, SpriteEffects.None, item.SpriteDepth + (railSprite.Depth - item.Sprite.Depth)); barrelSprite?.Draw(spriteBatch, - drawPos - new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation)) * recoilOffset * item.Scale, - item.SpriteColor, + drawPos - new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation)) * recoilOffset * item.Scale, + Color.White, rotation + MathHelper.PiOver2, item.Scale, SpriteEffects.None, item.SpriteDepth + (barrelSprite.Depth - item.Sprite.Depth)); diff --git a/Barotrauma/BarotraumaClient/Source/Items/Inventory.cs b/Barotrauma/BarotraumaClient/Source/Items/Inventory.cs index 7944ddf9d..f9e043913 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Inventory.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Inventory.cs @@ -301,7 +301,7 @@ namespace Barotrauma protected virtual void ControlInput(Camera cam) { // Note that these targets are static. Therefore the outcome is the same if this method is called multiple times or only once. - if (selectedSlot != null && !DraggingItemToWorld) + if (selectedSlot != null || draggingItem != null) { cam.Freeze = true; } @@ -609,17 +609,8 @@ namespace Barotrauma if (selectedSlot == null) { - if (DraggingItemToWorld && - Character.Controlled.FocusedItem?.OwnInventory != null && - Character.Controlled.FocusedItem.OwnInventory.TryPutItem(draggingItem, Character.Controlled)) - { - GUI.PlayUISound(GUISoundType.PickItem); - } - else - { - GUI.PlayUISound(GUISoundType.DropItem); - draggingItem.Drop(Character.Controlled); - } + draggingItem.Drop(Character.Controlled); + GUI.PlayUISound(GUISoundType.DropItem); } else if (selectedSlot.ParentInventory.Items[selectedSlot.SlotIndex] != draggingItem) { @@ -711,27 +702,10 @@ namespace Barotrauma int iconSize = (int)(64 * GUI.Scale); float scale = Math.Min(Math.Min(iconSize / sprite.size.X, iconSize / sprite.size.Y), 1.5f); Vector2 itemPos = PlayerInput.MousePosition; - - if (GUI.MouseOn == null && selectedSlot == null) - { - var shadowSprite = GUI.Style.GetComponentStyle("OuterGlow").Sprites[GUIComponent.ComponentState.None][0]; - string toolTip = Character.Controlled.FocusedItem != null ? - TextManager.Get("PutItemIn").Replace("[itemname]", Character.Controlled.FocusedItem.Name) : - TextManager.Get("DropItem"); - int textWidth = (int)Math.Max(GUI.Font.MeasureString(draggingItem.Name).X, GUI.SmallFont.MeasureString(toolTip).X); - int textSpacing = (int)(15 * GUI.Scale); - Point shadowBorders = (new Point(40, 10)).Multiply(GUI.Scale); - shadowSprite.Draw(spriteBatch, - new Rectangle(itemPos.ToPoint() - new Point(iconSize / 2) - shadowBorders, new Point(iconSize + textWidth + textSpacing, iconSize) + shadowBorders.Multiply(2)), Color.Black * 0.8f); - GUI.DrawString(spriteBatch, new Vector2(itemPos.X + iconSize / 2 + textSpacing, itemPos.Y - iconSize / 2), draggingItem.Name, Color.White); - GUI.DrawString(spriteBatch, new Vector2(itemPos.X + iconSize / 2 + textSpacing, itemPos.Y), toolTip, - color: Character.Controlled.FocusedItem == null ? Color.Red : Color.LightGreen, - font: GUI.SmallFont); - } sprite.Draw(spriteBatch, itemPos + Vector2.One * 2, Color.Black, scale: scale); - sprite.Draw(spriteBatch, - itemPos, - sprite == draggingItem.Sprite ? draggingItem.GetSpriteColor() : draggingItem.GetInventoryIconColor(), + sprite.Draw(spriteBatch, + itemPos, + sprite == draggingItem.Sprite ? draggingItem.GetSpriteColor() : draggingItem.GetInventoryIconColor(), scale: scale); } } diff --git a/Barotrauma/BarotraumaClient/Source/Items/ItemInventory.cs b/Barotrauma/BarotraumaClient/Source/Items/ItemInventory.cs index 8f2aa6359..8c126470f 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/ItemInventory.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/ItemInventory.cs @@ -10,6 +10,12 @@ namespace Barotrauma protected override void ControlInput(Camera cam) { + if (draggingItem == null && HUD.CloseHUD(BackgroundFrame)) + { + // TODO: fix so that works with the server side + Character.Controlled.SelectedConstruction = null; + return; + } base.ControlInput(cam); if (BackgroundFrame.Contains(PlayerInput.MousePosition)) { diff --git a/Barotrauma/BarotraumaClient/Source/Items/ItemPrefab.cs b/Barotrauma/BarotraumaClient/Source/Items/ItemPrefab.cs index 307e86801..d96f135ca 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/ItemPrefab.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/ItemPrefab.cs @@ -232,11 +232,11 @@ namespace Barotrauma { if (!ResizeHorizontal && !ResizeVertical) { - sprite.Draw(spriteBatch, new Vector2(placeRect.Center.X, -(placeRect.Y - placeRect.Height / 2)), SpriteColor * 0.8f, scale: Scale * scale); + sprite.Draw(spriteBatch, new Vector2(placeRect.Center.X, -(placeRect.Y - placeRect.Height / 2)), SpriteColor, scale: Scale * scale); } else { - if (sprite != null) sprite.DrawTiled(spriteBatch, new Vector2(placeRect.X, -placeRect.Y), placeRect.Size.ToVector2(), null, SpriteColor * 0.8f); + if (sprite != null) sprite.DrawTiled(spriteBatch, new Vector2(placeRect.X, -placeRect.Y), placeRect.Size.ToVector2(), null, SpriteColor); } } } diff --git a/Barotrauma/BarotraumaClient/Source/Map/Hull.cs b/Barotrauma/BarotraumaClient/Source/Map/Hull.cs index 8dc7700f0..f295160c6 100644 --- a/Barotrauma/BarotraumaClient/Source/Map/Hull.cs +++ b/Barotrauma/BarotraumaClient/Source/Map/Hull.cs @@ -1,13 +1,14 @@ using Barotrauma.Networking; using Barotrauma.Particles; using Barotrauma.Sounds; -using Lidgren.Network; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using System; using System.Collections.Generic; +using Microsoft.Xna.Framework.Input; using System.Linq; +using Lidgren.Network; namespace Barotrauma { @@ -18,8 +19,6 @@ namespace Barotrauma private List decals = new List(); private float serverUpdateDelay; - private float remoteWaterVolume, remoteOxygenPercentage; - private List remoteFireSources; private bool networkUpdatePending; private float networkUpdateTimer; @@ -140,10 +139,6 @@ namespace Barotrauma partial void UpdateProjSpecific(float deltaTime, Camera cam) { serverUpdateDelay -= deltaTime; - if (serverUpdateDelay <= 0.0f) - { - ApplyRemoteState(); - } if (networkUpdatePending) { @@ -552,18 +547,18 @@ namespace Barotrauma public void ClientRead(ServerNetObject type, NetBuffer message, float sendingTime) { - remoteWaterVolume = message.ReadRangedSingle(0.0f, 1.5f, 8) * Volume; - remoteOxygenPercentage = message.ReadRangedSingle(0.0f, 100.0f, 8); + float newWaterVolume = message.ReadRangedSingle(0.0f, 1.5f, 8) * Volume; + float newOxygenPercentage = message.ReadRangedSingle(0.0f, 100.0f, 8); bool hasFireSources = message.ReadBoolean(); int fireSourceCount = 0; - remoteFireSources = new List(); + List newFireSources = new List(); if (hasFireSources) { fireSourceCount = message.ReadRangedInteger(0, 16); for (int i = 0; i < fireSourceCount; i++) { - remoteFireSources.Add(new Vector3( + newFireSources.Add(new Vector3( MathHelper.Clamp(message.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f), MathHelper.Clamp(message.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f), message.ReadRangedSingle(0.0f, 1.0f, 8))); @@ -572,6 +567,41 @@ namespace Barotrauma if (serverUpdateDelay > 0.0f) { return; } + WaterVolume = newWaterVolume; + OxygenPercentage = newOxygenPercentage; + + for (int i = 0; i < fireSourceCount; i++) + { + Vector2 pos = new Vector2( + rect.X + rect.Width * newFireSources[i].X, + rect.Y - rect.Height + (rect.Height * newFireSources[i].Y)); + float size = newFireSources[i].Z * rect.Width; + + var newFire = i < FireSources.Count ? + FireSources[i] : + new FireSource(Submarine == null ? pos : pos + Submarine.Position, null, true); + newFire.Position = pos; + newFire.Size = new Vector2(size, newFire.Size.Y); + + //ignore if the fire wasn't added to this room (invalid position)? + if (!FireSources.Contains(newFire)) + { + newFire.Remove(); + continue; + } + } + + for (int i = FireSources.Count - 1; i >= fireSourceCount; i--) + { + FireSources[i].Remove(); + if (i < FireSources.Count) + { + FireSources.RemoveAt(i); + } + } + + if (serverUpdateDelay > 0.0f) { return; } + ApplyRemoteState(); } diff --git a/Barotrauma/BarotraumaClient/Source/Map/Lights/ConvexHull.cs b/Barotrauma/BarotraumaClient/Source/Map/Lights/ConvexHull.cs index 90e8517a7..e7467fec3 100644 --- a/Barotrauma/BarotraumaClient/Source/Map/Lights/ConvexHull.cs +++ b/Barotrauma/BarotraumaClient/Source/Map/Lights/ConvexHull.cs @@ -247,11 +247,15 @@ namespace Barotrauma.Lights public void Rotate(Vector2 origin, float amount) { - Matrix rotationMatrix = - Matrix.CreateTranslation(-origin.X, -origin.Y, 0.0f) * - Matrix.CreateRotationZ(amount) * - Matrix.CreateTranslation(origin.X, origin.Y, 0.0f); - SetVertices(vertices.Select(v => v.Pos).ToArray(), rotationMatrix); + Matrix rotationMatrix = Matrix.CreateRotationZ(amount); + + Vector2[] newVerts = new Vector2[vertices.Length]; + for (int i = 0; i < vertices.Length; i++) + { + newVerts[i] = Vector2.Transform(vertices[i].Pos - origin, rotationMatrix) + origin; + } + + SetVertices(newVerts); } private void CalculateDimensions() diff --git a/Barotrauma/BarotraumaClient/Source/Map/Lights/LightManager.cs b/Barotrauma/BarotraumaClient/Source/Map/Lights/LightManager.cs index f8d12f957..c61e3821d 100644 --- a/Barotrauma/BarotraumaClient/Source/Map/Lights/LightManager.cs +++ b/Barotrauma/BarotraumaClient/Source/Map/Lights/LightManager.cs @@ -453,10 +453,7 @@ namespace Barotrauma.Lights //raster pattern on top of everything spriteBatch.Begin(blendState: BlendState.AlphaBlend, samplerState: SamplerState.LinearWrap); - spriteBatch.Draw(highlightRaster, - new Rectangle(0, 0, HighlightMap.Width, HighlightMap.Height), - new Rectangle(0, 0, (int)(HighlightMap.Width / currLightMapScale * 0.5f), (int)(HighlightMap.Height / currLightMapScale * 0.5f)), - Color.White * 0.5f); + spriteBatch.Draw(highlightRaster, new Rectangle(0, 0, HighlightMap.Width, HighlightMap.Height), new Rectangle(0, 0, HighlightMap.Width, HighlightMap.Height), Color.White * 0.5f); spriteBatch.End(); DeformableSprite.Effect.CurrentTechnique = DeformableSprite.Effect.Techniques["DeformShader"]; diff --git a/Barotrauma/BarotraumaClient/Source/Map/Map/Map.cs b/Barotrauma/BarotraumaClient/Source/Map/Map/Map.cs index 4abd51d76..83b5d1622 100644 --- a/Barotrauma/BarotraumaClient/Source/Map/Map/Map.cs +++ b/Barotrauma/BarotraumaClient/Source/Map/Map/Map.cs @@ -364,23 +364,15 @@ namespace Barotrauma } } - if (GUI.KeyboardDispatcher.Subscriber == null) - { - float moveSpeed = 1000.0f; - Vector2 moveAmount = Vector2.Zero; - if (PlayerInput.KeyDown(InputType.Left)) { moveAmount += Vector2.UnitX; } - if (PlayerInput.KeyDown(InputType.Right)) { moveAmount -= Vector2.UnitX; } - if (PlayerInput.KeyDown(InputType.Up)) { moveAmount += Vector2.UnitY; } - if (PlayerInput.KeyDown(InputType.Down)) { moveAmount -= Vector2.UnitY; } - drawOffset += moveAmount * moveSpeed / zoom * deltaTime; - } - if (GUI.MouseOn == mapContainer) { zoom += PlayerInput.ScrollWheelSpeed / 1000.0f; zoom = MathHelper.Clamp(zoom, 1.0f, 4.0f); - if (PlayerInput.MidButtonHeld()) { drawOffset += PlayerInput.MouseSpeed / zoom; } + if (PlayerInput.MidButtonHeld()) + { + drawOffset += PlayerInput.MouseSpeed / zoom; + } #if DEBUG if (PlayerInput.DoubleClicked() && highlightedLocation != null) { @@ -559,6 +551,11 @@ namespace Barotrauma null, connectionColor * MathHelper.Clamp(a, 0.1f, 0.5f), MathUtils.VectorToAngle(end - start), new Vector2(0, 16), SpriteEffects.None, 0.01f); } + } + + rect.Inflate(8, 8); + GUI.DrawRectangle(spriteBatch, rect, Color.Black, false, 0.0f, 8); + GUI.DrawRectangle(spriteBatch, rect, Color.LightGray); if (GameMain.DebugDraw && zoom > 1.0f && generationParams.ShowLevelTypeNames) { diff --git a/Barotrauma/BarotraumaClient/Source/Map/MapEntityPrefab.cs b/Barotrauma/BarotraumaClient/Source/Map/MapEntityPrefab.cs index 2d2a03e2b..f8ab3faa8 100644 --- a/Barotrauma/BarotraumaClient/Source/Map/MapEntityPrefab.cs +++ b/Barotrauma/BarotraumaClient/Source/Map/MapEntityPrefab.cs @@ -44,7 +44,7 @@ namespace Barotrauma drawRect.Location -= Submarine.MainSub.Position.ToPoint(); } drawRect.Y = -drawRect.Y; - GUI.DrawRectangle(spriteBatch, drawRect, Color.White); + GUI.DrawRectangle(spriteBatch, drawRect, Color.DarkBlue); } public void DrawListLine(SpriteBatch spriteBatch, Vector2 pos, Color color) { diff --git a/Barotrauma/BarotraumaClient/Source/Map/Structure.cs b/Barotrauma/BarotraumaClient/Source/Map/Structure.cs index e97d93cac..02545e1e0 100644 --- a/Barotrauma/BarotraumaClient/Source/Map/Structure.cs +++ b/Barotrauma/BarotraumaClient/Source/Map/Structure.cs @@ -75,10 +75,7 @@ namespace Barotrauma }; var h = new ConvexHull(verts, Color.Black, this); - if (Math.Abs(rotation) > 0.001f) - { - h.Rotate(position, rotation); - } + h.Rotate(position, rotation); convexHulls.Add(h); } @@ -295,7 +292,8 @@ namespace Barotrauma { if (damageEffect != null) { - float newCutoff = MathHelper.Lerp(0.0f, 0.65f, Sections[i].damage / Prefab.Health); + float newCutoff = Sections[i].damage > 0 ? + MathHelper.Lerp(0.2f, 0.65f, Sections[i].damage / Prefab.Health) : 0.0f; if (Math.Abs(newCutoff - Submarine.DamageEffectCutoff) > 0.01f || color != Submarine.DamageEffectColor) { @@ -362,7 +360,6 @@ namespace Barotrauma -Bodies[i].Rotation, Color.White); } } - AiTarget?.Draw(spriteBatch); } } diff --git a/Barotrauma/BarotraumaClient/Source/Map/StructurePrefab.cs b/Barotrauma/BarotraumaClient/Source/Map/StructurePrefab.cs index 4863ea754..5e818059f 100644 --- a/Barotrauma/BarotraumaClient/Source/Map/StructurePrefab.cs +++ b/Barotrauma/BarotraumaClient/Source/Map/StructurePrefab.cs @@ -35,7 +35,7 @@ namespace Barotrauma public override void DrawPlacing(SpriteBatch spriteBatch, Rectangle placeRect, float scale = 1.0f) { // TODO: the scale property is not used - sprite.DrawTiled(spriteBatch, new Vector2(placeRect.X, -placeRect.Y), new Vector2(placeRect.Width, placeRect.Height), color: Color.White * 0.8f, textureScale: TextureScale * Scale); + sprite.DrawTiled(spriteBatch, new Vector2(placeRect.X, -placeRect.Y), new Vector2(placeRect.Width, placeRect.Height), textureScale: TextureScale * Scale); } } } diff --git a/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs b/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs index fb6837e05..70aa20dfe 100644 --- a/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs +++ b/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs @@ -141,23 +141,8 @@ namespace Barotrauma.Networking { OnClicked = (btn, userdata) => { - if (!permissions.HasFlag(ClientPermissions.ManageRound)) { return false; } - if (!Submarine.MainSub.AtStartPosition && !Submarine.MainSub.AtEndPosition) - { - var msgBox = new GUIMessageBox("", TextManager.Get("EndRoundSubNotAtLevelEnd"), - new string[] { TextManager.Get("Yes"), TextManager.Get("No") }); - msgBox.Buttons[0].OnClicked = (_, __) => - { - GameMain.Client.RequestRoundEnd(); - return true; - }; - msgBox.Buttons[0].OnClicked += msgBox.Close; - msgBox.Buttons[1].OnClicked += msgBox.Close; - } - else - { - RequestRoundEnd(); - } + if (!permissions.HasFlag(ClientPermissions.ManageRound)) return false; + RequestRoundEnd(); return true; }, Visible = false @@ -637,7 +622,7 @@ namespace Barotrauma.Networking if (gameStarted) SetRadioButtonColor(); - if (ShowNetStats && client?.ServerConnection != null) + if (ShowNetStats) { netStats.AddValue(NetStats.NetStatType.ReceivedBytes, client.ServerConnection.Statistics.ReceivedBytes); netStats.AddValue(NetStats.NetStatType.SentBytes, client.ServerConnection.Statistics.SentBytes); @@ -1124,7 +1109,7 @@ namespace Barotrauma.Networking { string errorMsg = "Level equality check failed. The level generated at your end doesn't match the level generated by the server (seed " + Level.Loaded.Seed + ")."; DebugConsole.ThrowError(errorMsg, createMessageBox: true); - GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:LevelsDontMatch" + levelSeed, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); + GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:LevelsDontMatch"+levelSeed, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); CoroutineManager.StartCoroutine(EndGame("")); yield return CoroutineStatus.Failure; } @@ -1396,6 +1381,9 @@ namespace Barotrauma.Networking case ServerNetObject.CLIENT_LIST: ReadClientList(inc); break; + case ServerNetObject.CLIENT_LIST: + ReadClientList(inc); + break; case ServerNetObject.CHAT_MESSAGE: ChatMessage.ClientRead(inc); break; @@ -1451,11 +1439,7 @@ namespace Barotrauma.Networking break; case ServerNetObject.ENTITY_EVENT: case ServerNetObject.ENTITY_EVENT_INITIAL: - if (!entityEventManager.Read(objHeader, inc, sendingTime, entities)) - { - eventReadFailed = true; - break; - } + if (!entityEventManager.Read(objHeader, inc, sendingTime, entities)) { break; } break; case ServerNetObject.CHAT_MESSAGE: ChatMessage.ClientRead(inc); @@ -1719,8 +1703,6 @@ namespace Barotrauma.Networking SaveUtil.LoadGame(GameMain.GameSession.SavePath, GameMain.GameSession); campaign.LastSaveID = campaign.PendingSaveID; - - DebugConsole.Log("Campaign save received, save ID " + campaign.LastSaveID); //decrement campaign update ID so the server will send us the latest data //(as there may have been campaign updates after the save file was created) campaign.LastUpdateID--; @@ -1738,7 +1720,7 @@ namespace Barotrauma.Networking public override void CreateEntityEvent(INetSerializable entity, object[] extraData) { - if (!(entity is IClientSerializable)) throw new InvalidCastException("Entity is not IClientSerializable"); + if (!(entity is IClientSerializable)) throw new InvalidCastException("entity is not IClientSerializable"); entityEventManager.CreateEvent(entity as IClientSerializable, extraData); } @@ -2007,8 +1989,7 @@ namespace Barotrauma.Networking msg.Write(true); msg.WritePadBits(); msg.Write(savePath); msg.Write(mapSeed); - msg.Write(sub.Name); - msg.Write(sub.MD5Hash.Hash); + msg.Write(sub.FilePath); client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered); diff --git a/Barotrauma/BarotraumaClient/Source/Screens/CampaignSetupUI.cs b/Barotrauma/BarotraumaClient/Source/Screens/CampaignSetupUI.cs index b173ae194..51ed62071 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/CampaignSetupUI.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/CampaignSetupUI.cs @@ -62,7 +62,7 @@ namespace Barotrauma UpdateSubList(submarines); - // New game right sideon + // New game right side new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), rightColumn.RectTransform), TextManager.Get("SaveName") + ":", textAlignment: Alignment.BottomLeft); saveNameBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.1f), rightColumn.RectTransform), string.Empty); @@ -262,6 +262,11 @@ namespace Barotrauma saveFiles = SaveUtil.GetSaveFiles(isMultiplayer ? SaveUtil.SaveType.Multiplayer : SaveUtil.SaveType.Singleplayer); } + saveList = new GUIListBox(new RectTransform(new Vector2(0.5f, 1.0f), loadGameContainer.RectTransform, Anchor.CenterLeft)) + { + OnSelected = SelectSaveFile + }; + saveList = new GUIListBox(new RectTransform(new Vector2(0.5f, 1.0f), loadGameContainer.RectTransform, Anchor.CenterLeft)) { OnSelected = SelectSaveFile @@ -269,9 +274,7 @@ namespace Barotrauma foreach (string saveFile in saveFiles) { - string fileName = saveFile; - string subName = ""; - string saveTime = ""; + XDocument doc = SaveUtil.LoadGameSessionDoc(saveFile); var saveFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.1f), saveList.Content.RectTransform), style: "ListBoxElement") { UserData = saveFile @@ -279,38 +282,25 @@ namespace Barotrauma var nameText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), saveFrame.RectTransform), text: Path.GetFileNameWithoutExtension(saveFile)); + if (doc?.Root == null) + { + DebugConsole.ThrowError("Error loading save file \"" + saveFile + "\". The file may be corrupted."); + nameText.Color = Color.Red; + continue; + } - if (!isMultiplayer) - { - XDocument doc = SaveUtil.LoadGameSessionDoc(saveFile); - if (doc?.Root == null) - { - DebugConsole.ThrowError("Error loading save file \"" + saveFile + "\". The file may be corrupted."); - nameText.Color = Color.Red; - continue; - } - subName = doc.Root.GetAttributeString("submarine", ""); - saveTime = doc.Root.GetAttributeString("savetime", ""); - } - else - { - string[] splitSaveFile = saveFile.Split(';'); - saveFrame.UserData = splitSaveFile[0]; - fileName = nameText.Text = Path.GetFileNameWithoutExtension(splitSaveFile[0]); - if (splitSaveFile.Length > 1) { subName = splitSaveFile[1]; } - if (splitSaveFile.Length > 2) { saveTime = splitSaveFile[2]; } - } - + string submarineName = doc.Root.GetAttributeString("submarine", ""); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), saveFrame.RectTransform, Anchor.BottomLeft), - text: subName, font: GUI.SmallFont) + text: submarineName, font: GUI.SmallFont) { - UserData = fileName + UserData = saveFile }; + string saveTime = doc.Root.GetAttributeString("savetime", ""); new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), saveFrame.RectTransform), text: saveTime, textAlignment: Alignment.Right, font: GUI.SmallFont) { - UserData = fileName + UserData = saveFile }; } @@ -355,6 +345,13 @@ namespace Barotrauma }; } + public void UpdateTutorialSelection() + { + if (isMultiplayer) return; + Tutorial contextualTutorial = Tutorial.Tutorials.Find(t => t is ContextualTutorial); + contextualTutorialBox.Selected = (contextualTutorial != null) ? !GameMain.Config.CompletedTutorialNames.Contains(contextualTutorial.Name) : true; + } + public void UpdateTutorialSelection() { if (isMultiplayer) return; diff --git a/Barotrauma/BarotraumaClient/Source/Screens/CampaignUI.cs b/Barotrauma/BarotraumaClient/Source/Screens/CampaignUI.cs index 3c322d12d..827b8af57 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/CampaignUI.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/CampaignUI.cs @@ -457,14 +457,11 @@ namespace Barotrauma { IgnoreLayoutGroups = true, OnClicked = (GUIButton btn, object obj) => { StartRound?.Invoke(); return true; }, - Enabled = true + Enabled = true, + Visible = GameMain.Client == null || + GameMain.Client.HasPermission(Networking.ClientPermissions.ManageRound) || + GameMain.Client.HasPermission(Networking.ClientPermissions.ManageCampaign) }; - if (GameMain.Client != null) - { - startButton.Visible = !GameMain.Client.GameStarted && - (GameMain.Client.HasPermission(Networking.ClientPermissions.ManageRound) || - GameMain.Client.HasPermission(Networking.ClientPermissions.ManageCampaign)); - } } OnLocationSelected?.Invoke(location, connection); diff --git a/Barotrauma/BarotraumaClient/Source/Screens/CharacterEditorScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/CharacterEditorScreen.cs index 8dfbbdd0b..48be1e5a0 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/CharacterEditorScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/CharacterEditorScreen.cs @@ -118,19 +118,6 @@ namespace Barotrauma instance = this; } - private void Reset() - { - AnimParams.ForEach(a => a.Reset(true)); - RagdollParams.Reset(true); - RagdollParams.ClearHistory(); - CurrentAnimation.ClearHistory(); - if (!character.Removed) - { - character.Remove(); - } - character = null; - } - public override void Deselect() { base.Deselect(); @@ -141,7 +128,15 @@ namespace Barotrauma isEndlessRunner = false; if (character != null) { - Reset(); + AnimParams.ForEach(a => a.Reset(true)); + RagdollParams.Reset(true); + RagdollParams.ClearHistory(); + CurrentAnimation.ClearHistory(); + if (!character.Removed) + { + character.Remove(); + } + character = null; } GameMain.World.ProcessChanges(); } @@ -398,12 +393,6 @@ namespace Barotrauma } if (!isFreezed) { - if (character.AnimController.Invalid) - { - Reset(); - SpawnCharacter(currentCharacterConfig); - } - Submarine.MainSub.SetPrevTransform(Submarine.MainSub.Position); Submarine.MainSub.Update((float)deltaTime); @@ -1226,7 +1215,7 @@ namespace Barotrauma Cam.Position = character.WorldPosition; } - private bool CreateCharacter(string name, string mainFolder, bool isHumanoid, params object[] ragdollConfig) + private bool CreateCharacter(string name, bool isHumanoid, params object[] ragdollConfig) { var contentPackage = GameMain.Config.SelectedContentPackages.LastOrDefault(); if (contentPackage == null) @@ -1239,22 +1228,23 @@ namespace Barotrauma #if !DEBUG if (vanilla != null && contentPackage == vanilla) { - GUI.AddMessage(GetCharacterEditorTranslation("CannotEditVanillaCharacters"), Color.Red, font: GUI.LargeFont); + GUI.AddMessage($"Cannot edit the Vanilla content!", Color.Red, font: GUI.LargeFont); return false; } #endif string speciesName = name; + string mainFolder = $"Content/Characters/{speciesName}"; // Config file - string configFilePath = Path.Combine(mainFolder, $"{speciesName}.xml").Replace(@"\", @"/"); + string configFilePath = $"{mainFolder}/{speciesName}.xml"; if (ContentPackage.GetFilesOfType(GameMain.SelectedPackages, ContentType.Character).None(path => path.Contains(speciesName))) { // Create the config file XElement mainElement = new XElement("Character", new XAttribute("name", speciesName), new XAttribute("humanoid", isHumanoid), - new XElement("ragdolls", new XAttribute("folder", Path.Combine(mainFolder, $"Ragdolls/").Replace(@"\", @"/"))), - new XElement("animations", new XAttribute("folder", Path.Combine(mainFolder, $"Animations/").Replace(@"\", @"/"))), + new XElement("ragdolls"), + new XElement("animations"), new XElement("health"), new XElement("ai")); XDocument doc = new XDocument(mainElement); @@ -1269,13 +1259,13 @@ namespace Barotrauma DebugConsole.NewMessage(GetCharacterEditorTranslation("ContentPackageSaved").Replace("[path]", contentPackage.Path)); } // Ragdoll - string ragdollFolder = RagdollParams.GetFolder(speciesName); + string ragdollFolder = RagdollParams.GetDefaultFolder(speciesName); string ragdollPath = RagdollParams.GetDefaultFile(speciesName); RagdollParams ragdollParams = isHumanoid ? RagdollParams.CreateDefault(ragdollPath, speciesName, ragdollConfig) : RagdollParams.CreateDefault(ragdollPath, speciesName, ragdollConfig) as RagdollParams; // Animations - string animFolder = AnimationParams.GetFolder(speciesName); + string animFolder = AnimationParams.GetDefaultFolder(speciesName); foreach (AnimationType animType in Enum.GetValues(typeof(AnimationType))) { if (animType != AnimationType.NotDefined) @@ -1879,7 +1869,7 @@ namespace Barotrauma #if !DEBUG if (VanillaCharacters != null && VanillaCharacters.Contains(currentCharacterConfig)) { - GUI.AddMessage(GetCharacterEditorTranslation("CannotEditVanillaCharacters"), Color.Red, font: GUI.LargeFont); + GUI.AddMessage(GetCharacterEditorTranslation("CantEditVanillaContent"), Color.Red, font: GUI.LargeFont); return false; } #endif @@ -1894,7 +1884,7 @@ namespace Barotrauma #if !DEBUG if (VanillaCharacters != null && VanillaCharacters.Contains(currentCharacterConfig)) { - GUI.AddMessage(GetCharacterEditorTranslation("CannotEditVanillaCharacters"), Color.Red, font: GUI.LargeFont); + GUI.AddMessage(GetCharacterEditorTranslation("CantEditVanillaContent"), Color.Red, font: GUI.LargeFont); return false; } #endif @@ -1966,7 +1956,7 @@ namespace Barotrauma #if !DEBUG if (VanillaCharacters != null && VanillaCharacters.Contains(currentCharacterConfig)) { - GUI.AddMessage(GetCharacterEditorTranslation("CannotEditVanillaCharacters"), Color.Red, font: GUI.LargeFont); + GUI.AddMessage(GetCharacterEditorTranslation("CantEditVanillaContent"), Color.Red, font: GUI.LargeFont); box.Close(); return false; } @@ -2099,7 +2089,7 @@ namespace Barotrauma #if !DEBUG if (VanillaCharacters != null && VanillaCharacters.Contains(currentCharacterConfig)) { - GUI.AddMessage(GetCharacterEditorTranslation("CannotEditVanillaCharacters"), Color.Red, font: GUI.LargeFont); + GUI.AddMessage(GetCharacterEditorTranslation("CantEditVanillaContent"), Color.Red, font: GUI.LargeFont); box.Close(); return false; } @@ -3928,7 +3918,7 @@ namespace Barotrauma private void DrawJointLimitWidgets(SpriteBatch spriteBatch, Limb limb, LimbJoint joint, Vector2 drawPos, bool autoFreeze, bool allowPairEditing, float rotationOffset = 0) { - rotationOffset += MathHelper.ToRadians(RagdollParams.SpritesheetOrientation); + rotationOffset -= MathHelper.ToRadians(RagdollParams.SpritesheetOrientation); Color angleColor = joint.UpperLimit - joint.LowerLimit > 0 ? Color.LightGreen * 0.5f : Color.Red; DrawRadialWidget(spriteBatch, drawPos, MathHelper.ToDegrees(joint.UpperLimit), $"joint.jointParams.Name {GetCharacterEditorTranslation("UpperLimit")}", Color.Cyan, angle => { @@ -4662,7 +4652,7 @@ namespace Barotrauma LimbXElements.Values, JointXElements }; - if (CharacterEditorScreen.instance.CreateCharacter(Name, Path.GetDirectoryName(XMLPath), IsHumanoid, ragdollParams)) + if (CharacterEditorScreen.instance.CreateCharacter(Name, IsHumanoid, ragdollParams)) { GUI.AddMessage(GetCharacterEditorTranslation("CharacterCreated").Replace("[name]", Name), Color.Green, font: GUI.Font); } diff --git a/Barotrauma/BarotraumaClient/Source/Screens/MainMenuScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/MainMenuScreen.cs index 01fba6b49..c46668705 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/MainMenuScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/MainMenuScreen.cs @@ -51,7 +51,7 @@ namespace Barotrauma new GUIImage(new RectTransform(new Vector2(0.35f, 0.2f), Frame.RectTransform, Anchor.BottomRight) { RelativeOffset = new Vector2(0.05f, 0.05f) }, style: "TitleText"); - buttonsParent = new GUILayoutGroup(new RectTransform(new Vector2(0.3f, 0.85f), parent: Frame.RectTransform, anchor: Anchor.BottomLeft, pivot: Pivot.BottomLeft) + buttonsParent = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 0.85f), parent: Frame.RectTransform, anchor: Anchor.BottomLeft, pivot: Pivot.BottomLeft) { AbsoluteOffset = new Point(50, 0) }) @@ -61,7 +61,7 @@ namespace Barotrauma }; // === CAMPAIGN - var campaignHolder = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 1.0f), parent: buttonsParent.RectTransform) { RelativeOffset = new Vector2(0.1f, 0.0f) }, isHorizontal: true); + var campaignHolder = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), parent: buttonsParent.RectTransform) { RelativeOffset = new Vector2(0.1f, 0.0f) }, isHorizontal: true); new GUIImage(new RectTransform(new Vector2(0.2f, 0.7f), campaignHolder.RectTransform), "MainMenuCampaignIcon") { @@ -107,7 +107,7 @@ namespace Barotrauma }; // === MULTIPLAYER - var multiplayerHolder = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 1.0f), parent: buttonsParent.RectTransform) { RelativeOffset = new Vector2(0.05f, 0.0f) }, isHorizontal: true); + var multiplayerHolder = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), parent: buttonsParent.RectTransform) { RelativeOffset = new Vector2(0.05f, 0.0f) }, isHorizontal: true); new GUIImage(new RectTransform(new Vector2(0.2f, 0.7f), multiplayerHolder.RectTransform), "MainMenuMultiplayerIcon") { @@ -152,7 +152,7 @@ namespace Barotrauma }; // === CUSTOMIZE - var customizeHolder = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 1.0f), parent: buttonsParent.RectTransform) { RelativeOffset = new Vector2(0.15f, 0.0f) }, isHorizontal: true); + var customizeHolder = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), parent: buttonsParent.RectTransform) { RelativeOffset = new Vector2(0.15f, 0.0f) }, isHorizontal: true); new GUIImage(new RectTransform(new Vector2(0.2f, 0.7f), customizeHolder.RectTransform), "MainMenuCustomizeIcon") { @@ -209,7 +209,7 @@ namespace Barotrauma } // === OPTION - var optionHolder = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.5f), parent: buttonsParent.RectTransform), isHorizontal: true); + var optionHolder = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 0.5f), parent: buttonsParent.RectTransform), isHorizontal: true); new GUIImage(new RectTransform(new Vector2(0.15f, 0.6f), optionHolder.RectTransform), "MainMenuOptionIcon") { @@ -219,7 +219,7 @@ namespace Barotrauma //spacing new GUIFrame(new RectTransform(new Vector2(0.01f, 0.0f), optionHolder.RectTransform), style: null); - var optionButtons = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 1.0f), parent: optionHolder.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.15f) }); + var optionButtons = new GUILayoutGroup(new RectTransform(new Vector2(0.55f, 1.0f), parent: optionHolder.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.15f) }); var optionList = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 0.15f), parent: optionButtons.RectTransform)) { @@ -241,7 +241,7 @@ namespace Barotrauma //debug button for quickly starting a new round #if DEBUG - new GUIButton(new RectTransform(new Vector2(0.8f, 0.1f), buttonsParent.RectTransform, Anchor.TopLeft, Pivot.BottomLeft) { AbsoluteOffset = new Point(0, -40) }, + new GUIButton(new RectTransform(new Vector2(0.5f, 0.1f), buttonsParent.RectTransform, Anchor.TopLeft, Pivot.BottomLeft) { AbsoluteOffset = new Point(0, -40) }, "Quickstart (dev)", style: "GUIButtonLarge", color: Color.Red) { IgnoreLayoutGroups = true, @@ -255,7 +255,7 @@ namespace Barotrauma #endif var minButtonSize = new Point(120, 20); - var maxButtonSize = new Point(480, 80); + var maxButtonSize = new Point(240, 40); /*new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), buttonsParent.RectTransform), TextManager.Get("TutorialButton"), style: "GUIButtonLarge") { @@ -270,21 +270,16 @@ namespace Barotrauma SetupButtons(buttons); buttons.ForEach(b => b.TextBlock.SetTextPos());*/ - var relativeSize = new Vector2(0.6f, 0.5f); + var relativeSize = new Vector2(0.5f, 0.5f); var minSize = new Point(600, 400); - var maxSize = new Point(2000, 1500); - var anchor = Anchor.CenterRight; - var pivot = Pivot.CenterRight; - Vector2 relativeSpacing = new Vector2(0.05f, 0.0f); - + var maxSize = new Point(900, 600); + var anchor = Anchor.Center; + var pivot = Pivot.Center; menuTabs = new GUIFrame[Enum.GetValues(typeof(Tab)).Length + 1]; - - menuTabs[(int)Tab.Settings] = new GUIFrame(new RectTransform(new Vector2(relativeSize.X, 0.8f), GUI.Canvas, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeSpacing }, - style: null); - - menuTabs[(int)Tab.NewGame] = new GUIFrame(new RectTransform(relativeSize, GUI.Canvas, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeSpacing }); + + menuTabs[(int)Tab.NewGame] = new GUIFrame(new RectTransform(relativeSize, GUI.Canvas, anchor, pivot, minSize, maxSize)); var paddedNewGame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), menuTabs[(int)Tab.NewGame].RectTransform, Anchor.Center), style: null); - menuTabs[(int)Tab.LoadGame] = new GUIFrame(new RectTransform(relativeSize, GUI.Canvas, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeSpacing }); + menuTabs[(int)Tab.LoadGame] = new GUIFrame(new RectTransform(relativeSize, GUI.Canvas, anchor, pivot, minSize, maxSize)); var paddedLoadGame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), menuTabs[(int)Tab.LoadGame].RectTransform, Anchor.Center), style: null); campaignSetupUI = new CampaignSetupUI(false, paddedNewGame, paddedLoadGame, Submarine.SavedSubmarines) @@ -295,13 +290,13 @@ namespace Barotrauma var hostServerScale = new Vector2(0.7f, 1.0f); menuTabs[(int)Tab.HostServer] = new GUIFrame(new RectTransform( - Vector2.Multiply(relativeSize, hostServerScale), GUI.Canvas, anchor, pivot, minSize.Multiply(hostServerScale), maxSize.Multiply(hostServerScale)) { RelativeOffset = relativeSpacing }); + Vector2.Multiply(relativeSize, hostServerScale), GUI.Canvas, anchor, pivot, minSize.Multiply(hostServerScale), maxSize.Multiply(hostServerScale))); CreateHostServerFields(); //---------------------------------------------------------------------- - menuTabs[(int)Tab.Tutorials] = new GUIFrame(new RectTransform(relativeSize, GUI.Canvas, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeSpacing }); + menuTabs[(int)Tab.Tutorials] = new GUIFrame(new RectTransform(relativeSize, GUI.Canvas, anchor, pivot, minSize, maxSize)); //PLACEHOLDER var tutorialList = new GUIListBox( @@ -382,7 +377,6 @@ namespace Barotrauma return false; } - GameMain.Config.ResetSettingsFrame(); selectedTab = (Tab)obj; switch (selectedTab) @@ -395,9 +389,8 @@ namespace Barotrauma campaignSetupUI.UpdateLoadMenu(); break; case Tab.Settings: - menuTabs[(int)Tab.Settings].RectTransform.ClearChildren(); - GameMain.Config.SettingsFrame.RectTransform.Parent = menuTabs[(int)Tab.Settings].RectTransform; - GameMain.Config.SettingsFrame.RectTransform.RelativeSize = Vector2.One; + GameMain.Config.ResetSettingsFrame(); + menuTabs[(int)Tab.Settings] = GameMain.Config.SettingsFrame; break; case Tab.JoinServer: GameMain.ServerListScreen.Select(); diff --git a/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs index a79601b8b..e249c088f 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs @@ -233,8 +233,7 @@ namespace Barotrauma levelSeed = value; - int intSeed = ToolBox.StringToInt(levelSeed); - backgroundSprite = LocationType.Random(new MTRandom(intSeed))?.GetPortrait(intSeed); + backgroundSprite = LocationType.Random(levelSeed)?.GetPortrait(ToolBox.StringToInt(levelSeed)); seedBox.Text = levelSeed; //lastUpdateID++; @@ -727,12 +726,6 @@ namespace Barotrauma spectateButton.Visible = GameMain.Client.GameStarted; ReadyToStartBox.Visible = !GameMain.Client.GameStarted; ReadyToStartBox.Selected = false; - if (campaignUI?.StartButton != null) - { - campaignUI.StartButton.Visible = !GameMain.Client.GameStarted && - (GameMain.Client.HasPermission(ClientPermissions.ManageRound) || - GameMain.Client.HasPermission(ClientPermissions.ManageCampaign)); - } GameMain.Client.SetReadyToStart(ReadyToStartBox); } else @@ -854,9 +847,9 @@ namespace Barotrauma if (campaignUI?.StartButton != null) { - campaignUI.StartButton.Visible = !GameMain.Client.GameStarted && - (GameMain.Client.HasPermission(ClientPermissions.ManageRound) || - GameMain.Client.HasPermission(ClientPermissions.ManageCampaign)); + campaignUI.StartButton.Visible = + GameMain.Client.HasPermission(ClientPermissions.ManageRound) || + GameMain.Client.HasPermission(ClientPermissions.ManageCampaign); } } @@ -867,21 +860,18 @@ namespace Barotrauma spectateButton.Enabled = true; } - public void SetCampaignCharacterInfo(CharacterInfo newCampaignCharacterInfo) - { - if (newCampaignCharacterInfo != null) + public void SetCampaignCharacterInfo(CharacterInfo characterInfo) + { + if (CampaignCharacterDiscarded) return; + + campaignCharacterInfo = characterInfo; + if (campaignCharacterInfo != null) { - if (CampaignCharacterDiscarded) { return; } - if (campaignCharacterInfo != newCampaignCharacterInfo) - { - campaignCharacterInfo = newCampaignCharacterInfo; - UpdatePlayerFrame(campaignCharacterInfo, false); - } + UpdatePlayerFrame(campaignCharacterInfo, false); } - else if (campaignCharacterInfo != null) + else { - campaignCharacterInfo = null; - UpdatePlayerFrame(campaignCharacterInfo, false); + UpdatePlayerFrame(null, true); } } diff --git a/Barotrauma/BarotraumaClient/Source/Screens/SubEditorScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/SubEditorScreen.cs index ae3a090d2..7ac15f8d0 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/SubEditorScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/SubEditorScreen.cs @@ -337,24 +337,7 @@ namespace Barotrauma var tickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.03f), paddedLeftPanel.RectTransform), TextManager.Get("ShowLighting")) { Selected = lightingEnabled, - OnSelected = (GUITickBox obj) => - { - lightingEnabled = obj.Selected; - if (lightingEnabled) - { - //turn off lights that are inside containers - foreach (Item item in Item.ItemList) - { - foreach (LightComponent lightComponent in item.GetComponents()) - { - lightComponent.Light.Color = item.Container != null || (item.body != null && !item.body.Enabled) ? - Color.Transparent : - lightComponent.LightColor; - } - } - } - return true; - } + OnSelected = (GUITickBox obj) => { lightingEnabled = obj.Selected; return true; } }; tickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.03f), paddedLeftPanel.RectTransform), TextManager.Get("ShowWalls")) { @@ -581,9 +564,6 @@ namespace Barotrauma MapEntityPrefab.Selected = null; - saveFrame = null; - loadFrame = null; - MapEntity.DeselectAll(); MapEntity.SelectionGroups.Clear(); @@ -786,20 +766,6 @@ namespace Barotrauma savePath = Path.Combine(Submarine.SavePath, savePath); } -#if !DEBUG - var vanilla = GameMain.VanillaContent; - if (vanilla != null) - { - var vanillaSubs = vanilla.GetFilesOfType(ContentType.Submarine); - string pathToCompare = savePath.Replace(@"\", @"/").ToLowerInvariant(); - if (vanillaSubs.Any(sub => sub.Replace(@"\", @"/").ToLowerInvariant() == pathToCompare)) - { - GUI.AddMessage(TextManager.Get("CannotEditVanillaSubs"), Color.Red, font: GUI.LargeFont); - return false; - } - } -#endif - /*foreach (var contentPackage in GameMain.Config.SelectedContentPackages) { Submarine.MainSub.RequiredContentPackages.Add(contentPackage.Name); @@ -2074,10 +2040,6 @@ namespace Barotrauma dummyCharacter.SelectedConstruction = null; } } - else if (MapEntity.SelectedList.Count == 1) - { - (MapEntity.SelectedList[0] as Item)?.UpdateHUD(cam, dummyCharacter, (float)deltaTime); - } CharacterHUD.Update((float)deltaTime, dummyCharacter, cam); } diff --git a/Barotrauma/BarotraumaServer/Properties/AssemblyInfo.cs b/Barotrauma/BarotraumaServer/Properties/AssemblyInfo.cs index 8eae69a5b..eb0a60828 100644 --- a/Barotrauma/BarotraumaServer/Properties/AssemblyInfo.cs +++ b/Barotrauma/BarotraumaServer/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.8.9.8")] -[assembly: AssemblyFileVersion("0.8.9.8")] +[assembly: AssemblyVersion("0.8.9.6")] +[assembly: AssemblyFileVersion("0.8.9.6")] diff --git a/Barotrauma/BarotraumaServer/Source/Characters/CharacterInfo.cs b/Barotrauma/BarotraumaServer/Source/Characters/CharacterInfo.cs index 8a75d42e0..29f5bade5 100644 --- a/Barotrauma/BarotraumaServer/Source/Characters/CharacterInfo.cs +++ b/Barotrauma/BarotraumaServer/Source/Characters/CharacterInfo.cs @@ -20,10 +20,8 @@ namespace Barotrauma if (Job != null) { msg.Write(Job.Prefab.Identifier); - msg.Write((byte)Job.Skills.Count); foreach (Skill skill in Job.Skills) { - msg.Write(skill.Identifier); msg.Write(skill.Level); } } diff --git a/Barotrauma/BarotraumaServer/Source/Characters/CharacterNetworking.cs b/Barotrauma/BarotraumaServer/Source/Characters/CharacterNetworking.cs index d9831f872..95f9d1e53 100644 --- a/Barotrauma/BarotraumaServer/Source/Characters/CharacterNetworking.cs +++ b/Barotrauma/BarotraumaServer/Source/Characters/CharacterNetworking.cs @@ -15,8 +15,6 @@ namespace Barotrauma private bool networkUpdateSent; - private double LastInputTime; - public float GetPositionUpdateInterval(Client recipient) { if (!Enabled) { return 1000.0f; } @@ -63,15 +61,6 @@ namespace Barotrauma else if (memInput.Count == 0) { AnimController.Frozen = true; - if (Timing.TotalTime > LastInputTime + 0.5) - { - //no inputs have been received in 0.5 seconds, reset input - //(if there's a temporary network hiccup that prevents us from receiving inputs, we assume the inputs haven't changed, - //but if it takes too long, for example due to a client crashing/disconnecting, we don't want to keep the character - //firing a welding tool or whatever else they were doing until the kill disconnect timer kicks in) - prevDequeuedInput = dequeuedInput = - dequeuedInput.HasFlag(InputNetFlags.FacingLeft) ? InputNetFlags.FacingLeft : InputNetFlags.None; - } } else { @@ -201,7 +190,6 @@ namespace Barotrauma networkUpdateID = (ushort)(networkUpdateID - i) }; memInput.Insert(i, newMem); - LastInputTime = Timing.TotalTime; } } diff --git a/Barotrauma/BarotraumaServer/Source/GameSession/GameModes/MultiPlayerCampaign.cs b/Barotrauma/BarotraumaServer/Source/GameSession/GameModes/MultiPlayerCampaign.cs index 0e1667db4..b94525388 100644 --- a/Barotrauma/BarotraumaServer/Source/GameSession/GameModes/MultiPlayerCampaign.cs +++ b/Barotrauma/BarotraumaServer/Source/GameSession/GameModes/MultiPlayerCampaign.cs @@ -11,11 +11,11 @@ namespace Barotrauma { private List characterData = new List(); - public static void StartNewCampaign(string savePath, string subPath, string seed) + public static void StartNewCampaign(string savePath, string subName, string seed) { if (string.IsNullOrWhiteSpace(savePath)) return; - GameMain.GameSession = new GameSession(new Submarine(subPath, ""), savePath, + GameMain.GameSession = new GameSession(new Submarine(subName, ""), savePath, GameModePreset.List.Find(g => g.Identifier == "multiplayercampaign")); var campaign = ((MultiPlayerCampaign)GameMain.GameSession.GameMode); campaign.GenerateMap(seed); @@ -251,7 +251,6 @@ namespace Barotrauma } lastSaveID++; - DebugConsole.Log("Campaign saved, save ID " + lastSaveID); } } } diff --git a/Barotrauma/BarotraumaServer/Source/Items/Components/Repairable.cs b/Barotrauma/BarotraumaServer/Source/Items/Components/Repairable.cs index 7bf36f0f8..04ca33c0d 100644 --- a/Barotrauma/BarotraumaServer/Source/Items/Components/Repairable.cs +++ b/Barotrauma/BarotraumaServer/Source/Items/Components/Repairable.cs @@ -1,5 +1,9 @@ using Barotrauma.Networking; -using Lidgren.Network; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; namespace Barotrauma.Items.Components { @@ -10,16 +14,5 @@ namespace Barotrauma.Items.Components //let the clients know the initial deterioration delay item.CreateServerEvent(this); } - - public void ServerRead(ClientNetObject type, NetBuffer msg, Client c) - { - if (c.Character == null) return; - StartRepairing(c.Character); - } - - public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null) - { - msg.Write(deteriorationTimer); - } } } diff --git a/Barotrauma/BarotraumaServer/Source/Map/Hull.cs b/Barotrauma/BarotraumaServer/Source/Map/Hull.cs index 3b10a7a24..907ba6f45 100644 --- a/Barotrauma/BarotraumaServer/Source/Map/Hull.cs +++ b/Barotrauma/BarotraumaServer/Source/Map/Hull.cs @@ -20,16 +20,6 @@ namespace Barotrauma partial void UpdateProjSpecific(float deltaTime, Camera cam) { if (IdFreed) { return; } - - //don't create updates if all clients are very far from the hull - float hullUpdateDistanceSqr = NetConfig.HullUpdateDistance * NetConfig.HullUpdateDistance; - if (!GameMain.Server.ConnectedClients.Any(c => - c.Character != null && - Vector2.DistanceSquared(c.Character.WorldPosition, WorldPosition) < hullUpdateDistanceSqr)) - { - return; - } - //update client hulls if the amount of water has changed by >10% //or if oxygen percentage has changed by 5% if (Math.Abs(lastSentVolume - waterVolume) > Volume * 0.1f || @@ -41,8 +31,8 @@ namespace Barotrauma GameMain.NetworkMember.CreateEntityEvent(this); lastSentVolume = waterVolume; lastSentOxygen = OxygenPercentage; - sendUpdateTimer = NetConfig.HullUpdateInterval; - } + sendUpdateTimer = NetworkUpdateInterval; + } } } diff --git a/Barotrauma/BarotraumaServer/Source/Networking/Client.cs b/Barotrauma/BarotraumaServer/Source/Networking/Client.cs index 28e291e39..78ff44d7a 100644 --- a/Barotrauma/BarotraumaServer/Source/Networking/Client.cs +++ b/Barotrauma/BarotraumaServer/Source/Networking/Client.cs @@ -46,7 +46,7 @@ namespace Barotrauma.Networking //when was a specific entity event last sent to the client // key = event id, value = NetTime.Now when sending - public readonly Dictionary EntityEventLastSent = new Dictionary(); + public readonly Dictionary EntityEventLastSent = new Dictionary(); //when was a position update for a given entity last sent to the client // key = entity id, value = NetTime.Now when sending diff --git a/Barotrauma/BarotraumaServer/Source/Networking/FileTransfer/FileSender.cs b/Barotrauma/BarotraumaServer/Source/Networking/FileTransfer/FileSender.cs index 88ad64ba2..2303577e9 100644 --- a/Barotrauma/BarotraumaServer/Source/Networking/FileTransfer/FileSender.cs +++ b/Barotrauma/BarotraumaServer/Source/Networking/FileTransfer/FileSender.cs @@ -187,7 +187,7 @@ namespace Barotrauma.Networking transfer.WaitTimer -= deltaTime; if (transfer.WaitTimer > 0.0f) continue; - if (!transfer.Connection.CanSendImmediately(NetDeliveryMethod.ReliableOrdered, transfer.SequenceChannel)) continue; + if (!transfer.Connection.CanSendImmediately(NetDeliveryMethod.ReliableOrdered, 1)) continue; transfer.WaitTimer = 0.05f;// transfer.Connection.AverageRoundtripTime; @@ -202,6 +202,15 @@ namespace Barotrauma.Networking { message = peer.CreateMessage(); message.Write((byte)ServerPacketHeader.FILE_TRANSFER); + message.Write((byte)FileTransferMessageType.Initiate); + message.Write((byte)transfer.FileType); + message.Write((ushort)chunkLen); + message.Write((ulong)transfer.Data.Length); + message.Write(transfer.FileName); + GameMain.Server.CompressOutgoingMessage(message); + transfer.Connection.SendMessage(message, NetDeliveryMethod.ReliableOrdered, transfer.SequenceChannel); + + transfer.Status = FileTransferStatus.Sending; //if the recipient is the owner of the server (= a client running the server from the main exe) //we don't need to send anything, the client can just read the file directly diff --git a/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs b/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs index d4c20614b..4ea71904f 100644 --- a/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs +++ b/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs @@ -10,7 +10,6 @@ using System.Text; using System.IO.Compression; using System.IO; using Barotrauma.Steam; -using System.Xml.Linq; namespace Barotrauma.Networking { @@ -716,25 +715,23 @@ namespace Barotrauma.Networking { string savePath = inc.ReadString(); string seed = inc.ReadString(); - string subName = inc.ReadString(); - string subHash = inc.ReadString(); + string subPath = inc.ReadString(); - var matchingSub = Submarine.SavedSubmarines.FirstOrDefault(s => s.Name == subName && s.MD5Hash.Hash == subHash); - - if (matchingSub == null) + if (!File.Exists(subPath)) { SendDirectChatMessage( - TextManager.Get("CampaignStartFailedSubNotFound").Replace("[subname]", subName), + TextManager.Get("CampaignStartFailedSubNotFound").Replace("[subpath]", subPath), connectedClient, ChatMessageType.MessageBox); } else { - if (connectedClient.HasPermission(ClientPermissions.SelectMode)) MultiPlayerCampaign.StartNewCampaign(savePath, matchingSub.FilePath, seed); + if (connectedClient.HasPermission(ClientPermissions.SelectMode)) MultiPlayerCampaign.StartNewCampaign(savePath, subPath, seed); } } else { string saveName = inc.ReadString(); + if (connectedClient.HasPermission(ClientPermissions.SelectMode)) MultiPlayerCampaign.LoadCampaign(saveName); } break; @@ -814,16 +811,7 @@ namespace Barotrauma.Networking Log(c.Name + " has reported an error: " + errorStr, ServerLog.MessageType.Error); GameAnalyticsManager.AddErrorEventOnce("GameServer.HandleClientError:LevelsDontMatch" + error, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorStr); - - if (c.Connection == OwnerConnection) - { - SendDirectChatMessage(errorStr, c, ChatMessageType.MessageBox); - EndGame(); - } - else - { - KickClient(c, errorStr); - } + KickClient(c, errorStr); } public override void CreateEntityEvent(INetSerializable entity, object[] extraData = null) @@ -1040,7 +1028,7 @@ namespace Barotrauma.Networking if (command == ClientPermissions.ManageRound && inc.PeekBoolean() && GameMain.GameSession?.GameMode is MultiPlayerCampaign mpCampaign) { - if (!mpCampaign.AllowedToEndRound(sender.Character) && !sender.HasPermission(command)) + if (!mpCampaign.AllowedToEndRound(sender.Character)) { return; } @@ -1126,22 +1114,9 @@ namespace Barotrauma.Networking UInt16 modeIndex = inc.ReadUInt16(); if (GameMain.NetLobbyScreen.GameModes[modeIndex].Identifier.ToLowerInvariant() == "multiplayercampaign") { - string[] saveFiles = SaveUtil.GetSaveFiles(SaveUtil.SaveType.Multiplayer); - for (int i = 0; i < saveFiles.Length; i++) - { - XDocument doc = SaveUtil.LoadGameSessionDoc(saveFiles[i]); - if (doc?.Root != null) - { - saveFiles[i] = - string.Join(";", - saveFiles[i].Replace(';', ' '), - doc.Root.GetAttributeString("submarine", ""), - doc.Root.GetAttributeString("savetime", "")); - } - } - NetOutgoingMessage msg = server.CreateMessage(); msg.Write((byte)ServerPacketHeader.CAMPAIGN_SETUP_INFO); + string[] saveFiles = SaveUtil.GetSaveFiles(SaveUtil.SaveType.Multiplayer); msg.Write((UInt16)saveFiles.Count()); foreach (string saveFile in saveFiles) { @@ -1226,7 +1201,6 @@ namespace Barotrauma.Networking ClientWriteLobby(c); if (GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign && - GameMain.NetLobbyScreen.SelectedMode == campaign.Preset && NetIdUtils.IdMoreRecent(campaign.LastSaveID, c.LastRecvCampaignSave)) { //already sent an up-to-date campaign save @@ -1361,6 +1335,10 @@ namespace Barotrauma.Networking WriteClientList(c, outmsg); clientListBytes = outmsg.LengthBytes - clientListBytes; + int eventManagerBytes = outmsg.LengthBytes; + entityEventManager.Write(c, outmsg, out List sentEvents); + eventManagerBytes = outmsg.LengthBytes - eventManagerBytes; + int chatMessageBytes = outmsg.LengthBytes; WriteChatMessages(outmsg, c); chatMessageBytes = outmsg.LengthBytes - chatMessageBytes; @@ -1370,8 +1348,7 @@ namespace Barotrauma.Networking while (!c.NeedsMidRoundSync && c.PendingPositionUpdates.Count > 0) { var entity = c.PendingPositionUpdates.Peek(); - if (entity == null || entity.Removed || - (entity is Item item && item.PositionUpdateInterval == float.PositiveInfinity)) + if (entity == null || entity.Removed) { c.PendingPositionUpdates.Dequeue(); continue; @@ -1410,55 +1387,25 @@ namespace Barotrauma.Networking errorMsg += " Client list size: " + clientListBytes + " bytes\n" + " Chat message size: " + chatMessageBytes + " bytes\n" + + " Event size: " + eventManagerBytes + " bytes\n" + " Position update size: " + positionUpdateBytes + " bytes\n\n"; + + if (sentEvents != null && sentEvents.Count > 0) + { + errorMsg += "Sent events: \n"; + foreach (var entityEvent in sentEvents) + { + errorMsg += " - " + (entityEvent.Entity?.ToString() ?? "null") + "\n"; + } + } + DebugConsole.ThrowError(errorMsg); - GameAnalyticsManager.AddErrorEventOnce("GameServer.ClientWriteIngame1:PacketSizeExceeded" + outmsg.LengthBytes, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); + GameAnalyticsManager.AddErrorEventOnce("GameServer.ClientWriteIngame:PacketSizeExceeded" + outmsg.LengthBytes, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); } CompressOutgoingMessage(outmsg); + server.SendMessage(outmsg, c.Connection, NetDeliveryMethod.Unreliable); - - //--------------------------------------------------------------------------- - - for (int i = 0; i < NetConfig.MaxEventPacketsPerUpdate; i++) - { - outmsg = server.CreateMessage(); - outmsg.Write((byte)ServerPacketHeader.UPDATE_INGAME); - outmsg.Write((float)NetTime.Now); - - int eventManagerBytes = outmsg.LengthBytes; - entityEventManager.Write(c, outmsg, out List sentEvents); - eventManagerBytes = outmsg.LengthBytes - eventManagerBytes; - - if (sentEvents.Count == 0) - { - break; - } - - outmsg.Write((byte)ServerNetObject.END_OF_MESSAGE); - - if (outmsg.LengthBytes > NetPeerConfiguration.MaximumTransmissionUnit) - { - string errorMsg = "Maximum packet size exceeded (" + outmsg.LengthBytes + " > " + NetPeerConfiguration.MaximumTransmissionUnit + ")\n"; - errorMsg += - " Event size: " + eventManagerBytes + " bytes\n"; - - if (sentEvents != null && sentEvents.Count > 0) - { - errorMsg += "Sent events: \n"; - foreach (var entityEvent in sentEvents) - { - errorMsg += " - " + (entityEvent.Entity?.ToString() ?? "null") + "\n"; - } - } - - DebugConsole.ThrowError(errorMsg); - GameAnalyticsManager.AddErrorEventOnce("GameServer.ClientWriteIngame2:PacketSizeExceeded" + outmsg.LengthBytes, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); - } - - CompressOutgoingMessage(outmsg); - server.SendMessage(outmsg, c.Connection, NetDeliveryMethod.Unreliable); - } } private void WriteClientList(Client c, NetOutgoingMessage outmsg) @@ -1545,8 +1492,7 @@ namespace Barotrauma.Networking } var campaign = GameMain.GameSession?.GameMode as MultiPlayerCampaign; - if (campaign != null && campaign.Preset == GameMain.NetLobbyScreen.SelectedMode && - NetIdUtils.IdMoreRecent(campaign.LastUpdateID, c.LastRecvCampaignUpdate)) + if (campaign != null && NetIdUtils.IdMoreRecent(campaign.LastUpdateID, c.LastRecvCampaignUpdate)) { outmsg.Write(true); outmsg.WritePadBits(); diff --git a/Barotrauma/BarotraumaServer/Source/Networking/GameServerLogin.cs b/Barotrauma/BarotraumaServer/Source/Networking/GameServerLogin.cs index 2d720f969..630c09a08 100644 --- a/Barotrauma/BarotraumaServer/Source/Networking/GameServerLogin.cs +++ b/Barotrauma/BarotraumaServer/Source/Networking/GameServerLogin.cs @@ -164,7 +164,7 @@ namespace Barotrauma.Networking if (connectedClient != null) { Log("Disconnecting client " + connectedClient.Name + " (Steam ID: " + steamID + "). Steam authentication no longer valid (" + status + ").", ServerLog.MessageType.ServerMessage); - KickClient(connectedClient, $"DisconnectMessage.SteamAuthNoLongerValid~[status]={status.ToString()}"); + KickClient(connectedClient, $"DisconnectMessage.SteamAuthNoLongerValid_[status]={status.ToString()}"); } }*/ } @@ -342,7 +342,7 @@ namespace Barotrauma.Networking if (clVersion != GameMain.Version.ToString()) { DisconnectUnauthClient(inc, unauthClient, DisconnectReason.InvalidVersion, - $"DisconnectMessage.InvalidVersion~[version]={GameMain.Version.ToString()}~[clientversion]={clVersion}"); + $"DisconnectMessage.InvalidVersion_[version]={GameMain.Version.ToString()}_[clientversion]={clVersion}"); Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (wrong game version)", ServerLog.MessageType.Error); DebugConsole.NewMessage(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (wrong game version)", Color.Red); @@ -368,7 +368,7 @@ namespace Barotrauma.Networking if (missingPackages.Count == 1) { - DisconnectUnauthClient(inc, unauthClient, DisconnectReason.MissingContentPackage, $"DisconnectMessage.MissingContentPackage~[missingcontentpackage]={GetPackageStr(missingPackages[0])}"); + DisconnectUnauthClient(inc, unauthClient, DisconnectReason.MissingContentPackage, $"DisconnectMessage.MissingContentPackage_[missingcontentpackage]={GetPackageStr(missingPackages[0])}"); Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (missing content package " + GetPackageStr(missingPackages[0]) + ")", ServerLog.MessageType.Error); return; } @@ -376,7 +376,7 @@ namespace Barotrauma.Networking { List packageStrs = new List(); missingPackages.ForEach(cp => packageStrs.Add(GetPackageStr(cp))); - DisconnectUnauthClient(inc, unauthClient, DisconnectReason.MissingContentPackage, $"DisconnectMessage.MissingContentPackages~[missingcontentpackages]={string.Join(", ", packageStrs)}"); + DisconnectUnauthClient(inc, unauthClient, DisconnectReason.MissingContentPackage, $"DisconnectMessage.MissingContentPackages_[missingcontentpackages]={string.Join(", ", packageStrs)}"); Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (missing content packages " + string.Join(", ", packageStrs) + ")", ServerLog.MessageType.Error); return; } @@ -399,7 +399,7 @@ namespace Barotrauma.Networking if (incompatiblePackages.Count == 1) { DisconnectUnauthClient(inc, unauthClient, DisconnectReason.IncompatibleContentPackage, - $"DisconnectMessage.IncompatibleContentPackage~[incompatiblecontentpackage]={GetPackageStr2(incompatiblePackages[0])}"); + $"DisconnectMessage.IncompatibleContentPackage_[incompatiblecontentpackage]={GetPackageStr2(incompatiblePackages[0])}"); Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (incompatible content package " + GetPackageStr2(incompatiblePackages[0]) + ")", ServerLog.MessageType.Error); return; } @@ -408,7 +408,7 @@ namespace Barotrauma.Networking List packageStrs = new List(); incompatiblePackages.ForEach(cp => packageStrs.Add(GetPackageStr2(cp))); DisconnectUnauthClient(inc, unauthClient, DisconnectReason.IncompatibleContentPackage, - $"DisconnectMessage.IncompatibleContentPackages~[incompatiblecontentpackages]={string.Join(", ", packageStrs)}"); + $"DisconnectMessage.IncompatibleContentPackages_[incompatiblecontentpackages]={string.Join(", ", packageStrs)}"); Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (incompatible content packages " + string.Join(", ", packageStrs) + ")", ServerLog.MessageType.Error); return; } @@ -494,21 +494,13 @@ namespace Barotrauma.Networking } else { - var defaultPerms = PermissionPreset.List.Find(p => p.Name == "None"); - if (defaultPerms != null) - { - newClient.SetPermissions(defaultPerms.Permissions, defaultPerms.PermittedCommands); - } - else - { - newClient.SetPermissions(ClientPermissions.None, new List()); - } + newClient.SetPermissions(ClientPermissions.None, new List()); } } private void DisconnectUnauthClient(NetIncomingMessage inc, UnauthenticatedClient unauthClient, DisconnectReason reason, string message) { - inc.SenderConnection.Disconnect(reason.ToString() + "/ " + TextManager.GetServerMessage(message)); + inc.SenderConnection.Disconnect(reason.ToString() + "/ " + message); if (unauthClient.SteamID > 0) { Steam.SteamManager.StopAuthSession(unauthClient.SteamID); } if (unauthClient != null) { diff --git a/Barotrauma/BarotraumaServer/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs b/Barotrauma/BarotraumaServer/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs index 326df08c3..83e59c6cc 100644 --- a/Barotrauma/BarotraumaServer/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs +++ b/Barotrauma/BarotraumaServer/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs @@ -228,7 +228,7 @@ namespace Barotrauma.Networking GameServer.Log("Disconnecting client " + c.Name + " due to excessive desync (expected old event " + (c.LastRecvEntityEventID + 1).ToString() + " (created " + (Timing.TotalTime - firstEventToResend.CreateTime).ToString("0.##") + " s ago)" + - " Events queued: " + events.Count + ", last sent to all: " + lastSentToAll, ServerLog.MessageType.Error); + " Events queued: " + events.Count + ", last sent to all: " + lastSentToAll, ServerLog.MessageType.ServerMessage); server.DisconnectClient(c, "", "ServerMessage.ExcessiveDesyncOldEvent"); } ); @@ -242,7 +242,7 @@ namespace Barotrauma.Networking toKick.ForEach(c => { DebugConsole.NewMessage(c.Name + " was kicked due to excessive desync (expected removed event " + (c.LastRecvEntityEventID + 1).ToString() + ", last available is " + events[0].ID.ToString() + ")", Color.Red); - GameServer.Log("Disconnecting client " + c.Name + " due to excessive desync (expected removed event " + (c.LastRecvEntityEventID + 1).ToString() + ", last available is " + events[0].ID.ToString() + ")", ServerLog.MessageType.Error); + GameServer.Log("Disconnecting client " + c.Name + " due to excessive desync (expected removed event " + (c.LastRecvEntityEventID + 1).ToString() + ", last available is " + events[0].ID.ToString() + ")", ServerLog.MessageType.ServerMessage); server.DisconnectClient(c, "", "ServerMessage.ExcessiveDesyncRemovedEvent"); }); } @@ -251,7 +251,7 @@ namespace Barotrauma.Networking var timedOutClients = clients.FindAll(c => c.InGame && c.NeedsMidRoundSync && Timing.TotalTime > c.MidRoundSyncTimeOut); foreach (Client timedOutClient in timedOutClients) { - GameServer.Log("Disconnecting client " + timedOutClient.Name + ". Syncing the client with the server took too long.", ServerLog.MessageType.Error); + GameServer.Log("Disconnecting client " + timedOutClient.Name + ". Syncing the client with the server took too long.", ServerLog.MessageType.ServerMessage); GameMain.Server.DisconnectClient(timedOutClient, "", "ServerMessage.SyncTimeout"); } @@ -305,7 +305,19 @@ namespace Barotrauma.Networking } //too many events for one packet - if (eventsToSync.Count > 200) + if (eventsToSync.Count > MaxEventsPerWrite) + { + if (eventsToSync.Count > MaxEventsPerWrite * 3 && !client.NeedsMidRoundSync) + { + Color color = eventsToSync.Count > MaxEventsPerWrite * 20 ? Color.Red : Color.Orange; + if (eventsToSync.Count < MaxEventsPerWrite * 5) { color = Color.Yellow; } + DebugConsole.NewMessage("WARNING: event count very high: " + eventsToSync.Count + "/" + MaxEventsPerWrite, color); + } + + eventsToSync.RemoveRange(MaxEventsPerWrite, eventsToSync.Count - MaxEventsPerWrite); + } + + foreach (NetEntityEvent entityEvent in eventsToSync) { if (eventsToSync.Count > 200 && !client.NeedsMidRoundSync) { @@ -338,7 +350,7 @@ namespace Barotrauma.Networking msg.Write(client.UnreceivedEntityEventCount); msg.Write(client.FirstNewEventID); - Write(msg, eventsToSync, out sentEvents, client); + Write(msg, eventsToSync, client); } else { @@ -351,6 +363,7 @@ namespace Barotrauma.Networking (entityEvent as ServerEntityEvent).Sent = true; client.EntityEventLastSent[entityEvent.ID] = NetTime.Now; } + sentEvents = eventsToSync; } /// @@ -374,10 +387,10 @@ namespace Barotrauma.Networking for (int i = startIndex; i < eventList.Count; i++) { - //find the first event that hasn't been sent in roundtriptime or at all - client.EntityEventLastSent.TryGetValue(eventList[i].ID, out double lastSent); + //find the first event that hasn't been sent in 1.5 * roundtriptime or at all + client.EntityEventLastSent.TryGetValue(eventList[i].ID, out float lastSent); - float minInterval = Math.Max(client.Connection.AverageRoundtripTime, (float)server.UpdateInterval.TotalSeconds * 2); + float minInterval = Math.Max(client.Connection.AverageRoundtripTime * 1.5f, (float)server.UpdateInterval.TotalSeconds * 2); if (lastSent > NetTime.Now - Math.Min(minInterval, 0.5f)) { @@ -403,7 +416,7 @@ namespace Barotrauma.Networking } else { - double midRoundSyncTimeOut = uniqueEvents.Count / 100 * server.UpdateInterval.TotalSeconds; + double midRoundSyncTimeOut = uniqueEvents.Count / MaxEventsPerWrite * server.UpdateInterval.TotalSeconds; midRoundSyncTimeOut = Math.Max(10.0f, midRoundSyncTimeOut * 10.0f); client.UnreceivedEntityEventCount = (UInt16)uniqueEvents.Count; diff --git a/Barotrauma/BarotraumaServer/Source/Networking/ServerSettings.cs b/Barotrauma/BarotraumaServer/Source/Networking/ServerSettings.cs index ec80a70d9..a9a354d94 100644 --- a/Barotrauma/BarotraumaServer/Source/Networking/ServerSettings.cs +++ b/Barotrauma/BarotraumaServer/Source/Networking/ServerSettings.cs @@ -337,56 +337,37 @@ namespace Barotrauma.Networking continue; } + string permissionsStr = clientElement.GetAttributeString("permissions", ""); ClientPermissions permissions = Networking.ClientPermissions.None; - List permittedCommands = new List(); - - if (clientElement.Attribute("preset") == null) + if (permissionsStr.ToLowerInvariant() == "all") { - string permissionsStr = clientElement.GetAttributeString("permissions", ""); - if (permissionsStr.ToLowerInvariant() == "all") + foreach (ClientPermissions permission in Enum.GetValues(typeof(ClientPermissions))) { - foreach (ClientPermissions permission in Enum.GetValues(typeof(ClientPermissions))) - { - permissions |= permission; - } - } - else if (!Enum.TryParse(permissionsStr, out permissions)) - { - DebugConsole.ThrowError("Error in " + ClientPermissionsFile + " - \"" + permissionsStr + "\" is not a valid client permission."); - continue; - } - - if (permissions.HasFlag(Networking.ClientPermissions.ConsoleCommands)) - { - foreach (XElement commandElement in clientElement.Elements()) - { - if (commandElement.Name.ToString().ToLowerInvariant() != "command") continue; - - string commandName = commandElement.GetAttributeString("name", ""); - DebugConsole.Command command = DebugConsole.FindCommand(commandName); - if (command == null) - { - DebugConsole.ThrowError("Error in " + ClientPermissionsFile + " - \"" + commandName + "\" is not a valid console command."); - continue; - } - - permittedCommands.Add(command); - } + permissions |= permission; } } - else + else if (!Enum.TryParse(permissionsStr, out permissions)) { - string presetName = clientElement.GetAttributeString("preset", ""); - PermissionPreset preset = PermissionPreset.List.Find(p => p.Name == presetName); - if (preset == null) + DebugConsole.ThrowError("Error in " + ClientPermissionsFile + " - \"" + permissionsStr + "\" is not a valid client permission."); + continue; + } + + List permittedCommands = new List(); + if (permissions.HasFlag(Networking.ClientPermissions.ConsoleCommands)) + { + foreach (XElement commandElement in clientElement.Elements()) { - DebugConsole.ThrowError("Failed to restore saved permissions to the client \"" + clientName + "\". Permission preset \"" + presetName + "\" not found."); - return; - } - else - { - permissions = preset.Permissions; - permittedCommands = preset.PermittedCommands.ToList(); + if (commandElement.Name.ToString().ToLowerInvariant() != "command") continue; + + string commandName = commandElement.GetAttributeString("name", ""); + DebugConsole.Command command = DebugConsole.FindCommand(commandName); + if (command == null) + { + DebugConsole.ThrowError("Error in " + ClientPermissionsFile + " - \"" + commandName + "\" is not a valid console command."); + continue; + } + + permittedCommands.Add(command); } } @@ -459,14 +440,9 @@ namespace Barotrauma.Networking foreach (SavedClientPermission clientPermission in ClientPermissions) { - var matchingPreset = PermissionPreset.List.Find(p => p.MatchesPermissions(clientPermission.Permissions, clientPermission.PermittedCommands)); - if (matchingPreset != null && matchingPreset.Name == "None") - { - continue; - } - XElement clientElement = new XElement("Client", - new XAttribute("name", clientPermission.Name)); + new XAttribute("name", clientPermission.Name), + new XAttribute("permissions", clientPermission.Permissions.ToString())); if (clientPermission.SteamID > 0) { @@ -477,21 +453,14 @@ namespace Barotrauma.Networking clientElement.Add(new XAttribute("ip", clientPermission.IP)); } - if (matchingPreset == null) + if (clientPermission.Permissions.HasFlag(Barotrauma.Networking.ClientPermissions.ConsoleCommands)) { - clientElement.Add(new XAttribute("permissions", clientPermission.Permissions.ToString())); - if (clientPermission.Permissions.HasFlag(Networking.ClientPermissions.ConsoleCommands)) + foreach (DebugConsole.Command command in clientPermission.PermittedCommands) { - foreach (DebugConsole.Command command in clientPermission.PermittedCommands) - { - clientElement.Add(new XElement("command", new XAttribute("name", command.names[0]))); - } + clientElement.Add(new XElement("command", new XAttribute("name", command.names[0]))); } } - else - { - clientElement.Add(new XAttribute("preset", matchingPreset.Name)); - } + doc.Root.Add(clientElement); } diff --git a/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml b/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml index 98144c0a5..4b478cc34 100644 --- a/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml +++ b/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml @@ -56,7 +56,6 @@ - @@ -71,11 +70,10 @@ - + - diff --git a/Barotrauma/BarotraumaShared/Data/permissionpresets.xml b/Barotrauma/BarotraumaShared/Data/permissionpresets.xml index 3187fe94a..49e953433 100644 --- a/Barotrauma/BarotraumaShared/Data/permissionpresets.xml +++ b/Barotrauma/BarotraumaShared/Data/permissionpresets.xml @@ -13,13 +13,6 @@ - - - - - - - @@ -35,8 +28,6 @@ - - diff --git a/Barotrauma/BarotraumaShared/SharedContent.projitems b/Barotrauma/BarotraumaShared/SharedContent.projitems index 85b8d8b4d..619c6b810 100644 --- a/Barotrauma/BarotraumaShared/SharedContent.projitems +++ b/Barotrauma/BarotraumaShared/SharedContent.projitems @@ -10,7 +10,7 @@ - + @@ -45,27 +45,12 @@ PreserveNewest - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - PreserveNewest PreserveNewest - - PreserveNewest - - - PreserveNewest - PreserveNewest @@ -357,36 +342,6 @@ PreserveNewest - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - PreserveNewest @@ -942,13 +897,13 @@ PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest @@ -2000,9 +1955,6 @@ PreserveNewest - - PreserveNewest - @@ -2404,7 +2356,7 @@ PreserveNewest - + PreserveNewest @@ -3162,7 +3114,7 @@ PreserveNewest - Never + PreserveNewest PreserveNewest @@ -3182,9 +3134,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/AITarget.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/AITarget.cs index 42f9d6515..84f39ee7a 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/AITarget.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/AITarget.cs @@ -115,7 +115,7 @@ namespace Barotrauma SonarLabel = element.GetAttributeString("sonarlabel", ""); } - public AITarget(Entity e, float sightRange = -1, float soundRange = 0) + public AITarget(Entity e) { Entity = e; if (sightRange < 0) diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs index cc21a1ad1..6702051d9 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs @@ -26,6 +26,7 @@ namespace Barotrauma } } + public class TargetingPriority { public string TargetTag; @@ -70,10 +71,12 @@ namespace Barotrauma private float raycastTimer; - private bool IsCoolDownRunning => AttackingLimb != null && AttackingLimb.attack.CoolDownTimer > 0; + private bool IsCoolDownRunning => attackingLimb != null && attackingLimb.attack.CoolDownTimer > 0; private bool aggressiveBoarding; + private LatchOntoAI latchOntoAI; + //a point in a wall which the Character is currently targeting private WallTarget wallTarget; @@ -110,11 +113,6 @@ namespace Barotrauma private readonly float aggressiongreed; private readonly float aggressionhurt; - // TODO: expose? - private readonly float priorityFearIncreasement = 2; - private readonly float memoryFadeTime = 0.5f; - - public LatchOntoAI LatchOntoAI { get; private set; } public bool AttackHumans { @@ -134,6 +132,11 @@ namespace Barotrauma } } + public Limb AttackingLimb + { + get { return attackingLimb; } + } + public float CombatStrength { get { return combatStrength; } @@ -144,7 +147,7 @@ namespace Barotrauma get { //can't enter a submarine when attached to something - return LatchOntoAI == null || !LatchOntoAI.IsAttached; + return latchOntoAI == null || !latchOntoAI.IsAttached; } } @@ -152,13 +155,11 @@ namespace Barotrauma { get { - //can't flip when attached to something or when reversing - return !Reverse && (LatchOntoAI == null || !LatchOntoAI.IsAttached); + //can't flip when attached to something + return latchOntoAI == null || !latchOntoAI.IsAttached; } } - public bool Reverse { get; private set; } - public EnemyAIController(Character c, string file, string seed) : base(c) { targetMemories = new Dictionary(); @@ -206,7 +207,7 @@ namespace Barotrauma switch (subElement.Name.ToString().ToLowerInvariant()) { case "latchonto": - LatchOntoAI = new LatchOntoAI(subElement, this); + latchOntoAI = new LatchOntoAI(subElement, this); break; case "targetpriority": targetingPriorities.Add(subElement.GetAttributeString("tag", "").ToLowerInvariant(), new TargetingPriority(subElement)); @@ -261,7 +262,8 @@ namespace Barotrauma public override void SelectTarget(AITarget target) { SelectedAiTarget = target; - selectedTargetMemory = GetTargetMemory(target); + selectedTargetMemory = FindTargetMemory(target); + targetValue = 100.0f; } @@ -298,7 +300,8 @@ namespace Barotrauma } else { - UpdateTargets(Character, out TargetingPriority targetingPriority); + TargetingPriority targetingPriority = null; + UpdateTargets(Character, out targetingPriority); updateTargetsTimer = UpdateTargetsInterval; if (SelectedAiTarget == null) @@ -315,7 +318,7 @@ namespace Barotrauma } } - LatchOntoAI?.Update(this, deltaTime); + latchOntoAI?.Update(this, deltaTime); if (SelectedAiTarget != null && (SelectedAiTarget.Entity == null || SelectedAiTarget.Entity.Removed)) { @@ -405,44 +408,13 @@ namespace Barotrauma State = AIState.Idle; return; } - else if (selectedTargetMemory != null) + + Vector2 escapeDir = Vector2.Normalize(SimPosition - SelectedAiTarget.SimPosition); + if (!MathUtils.IsValid(escapeDir)) escapeDir = Vector2.UnitY; + SteeringManager.SteeringManual(deltaTime, escapeDir); + SteeringManager.SteeringWander(); + if (Character.CurrentHull == null) { - selectedTargetMemory.Priority += deltaTime * priorityFearIncreasement; - } - if (Character.CurrentHull != null) - { - // Seek exit, if inside - if (SteeringManager is IndoorsSteeringManager indoorSteering && escapePoint == Vector2.Zero) - { - foreach (Gap gap in Gap.GapList) - { - if (gap.Submarine != Character.Submarine) { continue; } - if (gap.Open < 1 || gap.IsRoomToRoom) { continue; } - var path = indoorSteering.PathFinder.FindPath(Character.SimPosition, gap.SimPosition); - if (!path.Unreachable) - { - if (escapePoint != Vector2.Zero) - { - // Ignore the gap if it's further away than the previously assigned escape point - if (Vector2.DistanceSquared(Character.SimPosition, gap.SimPosition) > Vector2.DistanceSquared(Character.SimPosition, escapePoint)) { continue; } - } - escapePoint = gap.SimPosition; - } - } - } - } - if (escapePoint != Vector2.Zero && Vector2.DistanceSquared(Character.SimPosition, escapePoint) > 1) - { - SteeringManager.SteeringSeek(escapePoint); - } - else - { - // If outside or near enough the escapePoint, steer away - escapePoint = Vector2.Zero; - Vector2 escapeDir = Vector2.Normalize(WorldPosition - SelectedAiTarget.WorldPosition); - if (!MathUtils.IsValid(escapeDir)) escapeDir = Vector2.UnitY; - SteeringManager.SteeringManual(deltaTime, escapeDir); - SteeringManager.SteeringWander(); SteeringManager.SteeringAvoid(deltaTime, colliderSize * 3.0f); } } @@ -459,8 +431,9 @@ namespace Barotrauma return; } - Vector2 attackWorldPos = SelectedAiTarget.WorldPosition; - Vector2 attackSimPos = SelectedAiTarget.SimPosition; + selectedTargetMemory.Priority -= deltaTime * 0.1f; + + Vector2 attackPos = SelectedAiTarget.WorldPosition; if (SelectedAiTarget.Entity is Item item) { @@ -482,14 +455,19 @@ namespace Barotrauma } else { - if (!IsProperlyLatched) - { - UpdateWallTarget(); - } + UpdateWallTarget(); raycastTimer = RaycastInterval; } - if (SelectedAiTarget.Entity is Character c) + if (wallTarget != null) + { + attackPos = wallTarget.Position; + if (Character.Submarine == null && wallTarget.Structure.Submarine != null) + { + attackPos += wallTarget.Structure.Submarine.Position; + } + } + else if (SelectedAiTarget.Entity is Character c) { //target the closest limb if the target is a character float closestDist = Vector2.DistanceSquared(SelectedAiTarget.WorldPosition, WorldPosition) * 10.0f; @@ -500,37 +478,14 @@ namespace Barotrauma if (dist < closestDist) { closestDist = dist; - attackWorldPos = limb.WorldPosition; - attackSimPos = limb.SimPosition; + attackPos = limb.WorldPosition; } } } if (wallTarget != null) { - attackWorldPos = wallTarget.Position; - if (wallTarget.Structure.Submarine != null) - { - attackWorldPos += wallTarget.Structure.Submarine.Position; - } - attackSimPos = ConvertUnits.ToSimUnits(attackWorldPos); - } - else - { - // Take the sub position into account in the sim pos - if (Character.Submarine == null && SelectedAiTarget.Entity.Submarine != null) - { - attackSimPos += SelectedAiTarget.Entity.Submarine.SimPosition; - } - else if (Character.Submarine != null && SelectedAiTarget.Entity.Submarine == null) - { - attackSimPos -= Character.Submarine.SimPosition; - } - } - - if (Math.Abs(Character.AnimController.movement.X) > 0.1f && !Character.AnimController.InWater) - { - Character.AnimController.TargetDir = Character.WorldPosition.X < attackWorldPos.X ? Direction.Right : Direction.Left; + Character.AnimController.TargetDir = Character.WorldPosition.X < attackPos.X ? Direction.Right : Direction.Left; } if (aggressiveBoarding) @@ -539,24 +494,30 @@ namespace Barotrauma if (wallTarget != null && wallTarget.SectionIndex > -1 && CanPassThroughHole(wallTarget.Structure, wallTarget.SectionIndex)) { WallSection section = wallTarget.Structure.GetSection(wallTarget.SectionIndex); - Vector2 targetPos = wallTarget.Structure.SectionPosition(wallTarget.SectionIndex, true); - if (section?.gap != null && section.gap.IsRoomToRoom && SteerThroughGap(wallTarget.Structure, section, targetPos, deltaTime)) + Hull targetHull = section.gap?.FlowTargetHull; + if (targetHull != null && !section.gap.IsRoomToRoom) { - return; - } - } - else if (SelectedAiTarget.Entity is Structure wall) - { - for (int i = 0; i < wall.Sections.Length; i++) - { - WallSection section = wall.Sections[i]; - if (CanPassThroughHole(wall, i) && section?.gap != null) + Vector2 targetPos = wallTarget.Structure.SectionPosition(wallTarget.SectionIndex, true); + if (wallTarget.Structure.IsHorizontal) { - if (SteerThroughGap(wall, section, section.gap.WorldPosition, deltaTime)) - { - return; - } + targetPos.Y = targetHull.WorldRect.Y - targetHull.Rect.Height / 2; } + else + { + targetPos.X = targetHull.WorldRect.Center.X; + } + + latchOntoAI?.DeattachFromBody(); + Character.AnimController.ReleaseStuckLimbs(); + if (steeringManager is IndoorsSteeringManager) + { + steeringManager.SteeringManual(deltaTime, Vector2.Normalize(targetPos - Character.WorldPosition)); + } + else + { + steeringManager.SteeringSeek(ConvertUnits.ToSimUnits(targetPos)); + } + return; } } else if (SelectedAiTarget.Entity is Item i) @@ -571,8 +532,6 @@ namespace Barotrauma if (Character.WorldPosition.Y < door.Item.WorldRect.Y && Character.WorldPosition.Y > door.Item.WorldRect.Y - door.Item.Rect.Height) { velocity.Y = 0; - LatchOntoAI?.DeattachFromBody(); - Character.AnimController.ReleaseStuckLimbs(); steeringManager.SteeringManual(deltaTime, velocity); return; } @@ -582,8 +541,6 @@ namespace Barotrauma if (Character.WorldPosition.X < door.Item.WorldRect.X && Character.WorldPosition.X > door.Item.WorldRect.Right) { velocity.X = 0; - LatchOntoAI?.DeattachFromBody(); - Character.AnimController.ReleaseStuckLimbs(); steeringManager.SteeringManual(deltaTime, velocity); return; } @@ -595,58 +552,58 @@ namespace Barotrauma bool canAttack = true; if (IsCoolDownRunning) { - switch (AttackingLimb.attack.AfterAttack) + switch (attackingLimb.attack.AfterAttack) { case AIBehaviorAfterAttack.Pursue: case AIBehaviorAfterAttack.PursueIfCanAttack: - if (AttackingLimb.attack.SecondaryCoolDown <= 0) + if (attackingLimb.attack.SecondaryCoolDown <= 0) { // No (valid) secondary cooldown defined. - if (AttackingLimb.attack.AfterAttack == AIBehaviorAfterAttack.Pursue) + if (attackingLimb.attack.AfterAttack == AIBehaviorAfterAttack.Pursue) { canAttack = false; } else { - UpdateFallBack(attackWorldPos, deltaTime); + UpdateFallBack(attackPos, deltaTime); return; } } else { - if (AttackingLimb.attack.SecondaryCoolDownTimer <= 0) + if (attackingLimb.attack.SecondaryCoolDownTimer <= 0) { // Don't allow attacking when the attack target has just changed. if (_previousAiTarget != null && SelectedAiTarget != _previousAiTarget) { canAttack = false; - if (AttackingLimb.attack.AfterAttack == AIBehaviorAfterAttack.PursueIfCanAttack) + if (attackingLimb.attack.AfterAttack == AIBehaviorAfterAttack.PursueIfCanAttack) { // Fall back if cannot attack. - UpdateFallBack(attackWorldPos, deltaTime); + UpdateFallBack(attackPos, deltaTime); return; } - AttackingLimb = null; + attackingLimb = null; } else { // If the secondary cooldown is defined and expired, check if we can switch the attack - var newLimb = GetAttackLimb(attackWorldPos, AttackingLimb); + var previousLimb = attackingLimb; + var newLimb = GetAttackLimb(attackPos, previousLimb); if (newLimb != null) { - // Attack with the new limb - AttackingLimb = newLimb; + attackingLimb = newLimb; } else { // No new limb was found. - if (AttackingLimb.attack.AfterAttack == AIBehaviorAfterAttack.Pursue) + if (attackingLimb.attack.AfterAttack == AIBehaviorAfterAttack.Pursue) { canAttack = false; } else { - UpdateFallBack(attackWorldPos, deltaTime); + UpdateFallBack(attackPos, deltaTime); return; } } @@ -659,106 +616,44 @@ namespace Barotrauma } } break; - case AIBehaviorAfterAttack.FallBackUntilCanAttack: - if (AttackingLimb.attack.SecondaryCoolDown <= 0) - { - // No (valid) secondary cooldown defined. - UpdateFallBack(attackWorldPos, deltaTime); - return; - } - else - { - if (AttackingLimb.attack.SecondaryCoolDownTimer <= 0) - { - // Don't allow attacking when the attack target has just changed. - if (_previousAiTarget != null && SelectedAiTarget != _previousAiTarget) - { - UpdateFallBack(attackWorldPos, deltaTime); - return; - } - else - { - // If the secondary cooldown is defined and expired, check if we can switch the attack - var newLimb = GetAttackLimb(attackWorldPos, AttackingLimb); - if (newLimb != null) - { - // Attack with the new limb - AttackingLimb = newLimb; - } - else - { - // No new limb was found. - UpdateFallBack(attackWorldPos, deltaTime); - return; - } - } - } - else - { - // Cooldown not yet expired -> steer away from the target - UpdateFallBack(attackWorldPos, deltaTime); - return; - } - } - break; case AIBehaviorAfterAttack.FallBack: default: - UpdateFallBack(attackWorldPos, deltaTime); + UpdateFallBack(attackPos, deltaTime); return; } } - if (AttackingLimb == null || _previousAiTarget != SelectedAiTarget) + if (attackingLimb == null) { - AttackingLimb = GetAttackLimb(attackWorldPos); + attackingLimb = GetAttackLimb(attackPos); } if (canAttack) { - canAttack = AttackingLimb != null && AttackingLimb.attack.CoolDownTimer <= 0; + canAttack = attackingLimb != null && attackingLimb.attack.CoolDownTimer <= 0; } float distance = 0; if (canAttack) { // Check that we can reach the target - distance = Vector2.Distance(AttackingLimb.WorldPosition, attackWorldPos); - canAttack = distance < AttackingLimb.attack.Range; - if (!canAttack && !IsCoolDownRunning) - { - // If not, reset the attacking limb, if the cooldown is not running - AttackingLimb = null; - } + distance = Vector2.Distance(attackingLimb.WorldPosition, attackPos); + canAttack = distance < attackingLimb.attack.Range; } - // If the attacking limb is a hand or claw, for example, using it as the steering limb can end in the result where the character circles around the target. For example the Hammerhead steering with the claws when it should use the torso. - // If we always use the main limb, this causes the character to seek the target with it's torso/head, when it should not. For example Mudraptor steering with it's belly, when it should use it's head. - // So let's use the one that's closer to the attacking limb. - Limb steeringLimb; - var torso = Character.AnimController.GetLimb(LimbType.Torso); - var head = Character.AnimController.GetLimb(LimbType.Head); - if (AttackingLimb == null) - { - steeringLimb = head ?? torso; - } - else - { - if (head != null && torso != null) - { - steeringLimb = Vector2.DistanceSquared(AttackingLimb.SimPosition, head.SimPosition) < Vector2.DistanceSquared(AttackingLimb.SimPosition, torso.SimPosition) ? head : torso; - } - else - { - steeringLimb = head ?? torso; - } - } + Limb steeringLimb = Character.AnimController.MainLimb; if (steeringLimb != null) { - Vector2 offset = Character.SimPosition - steeringLimb.SimPosition; - // Offset so that we don't overshoot the movement - Vector2 steerPos = attackSimPos + offset; - SteeringManager.SteeringSeek(steerPos, 10); + Vector2 toTarget = Vector2.Normalize(attackPos - steeringLimb.WorldPosition); + Vector2 targetingVector = toTarget * attackingLimb.attack.Range; + // Offset the position a bit so that we don't overshoot the movement. + Vector2 steerPos = attackPos + targetingVector; + steeringManager.SteeringSeek(ConvertUnits.ToSimUnits(attackPos), 10); + if (Character.CurrentHull == null) + { + SteeringManager.SteeringAvoid(deltaTime, colliderSize * 1.5f); + } - if (SteeringManager is IndoorsSteeringManager indoorsSteering) + if (steeringManager is IndoorsSteeringManager indoorsSteering) { if (indoorsSteering.CurrentPath != null && !indoorsSteering.IsPathDirty) { @@ -773,7 +668,7 @@ namespace Barotrauma } else if (indoorsSteering.CurrentPath.Finished) { - SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(attackSimPos - steeringLimb.SimPosition)); + steeringManager.SteeringManual(deltaTime, toTarget); } else if (indoorsSteering.CurrentPath.CurrentNode?.ConnectedDoor != null) { @@ -787,97 +682,50 @@ namespace Barotrauma } } } - else if (Character.CurrentHull == null) - { - SteeringManager.SteeringAvoid(deltaTime, colliderSize * 1.5f); - } } if (canAttack) { - UpdateLimbAttack(deltaTime, AttackingLimb, attackSimPos, distance); + UpdateLimbAttack(deltaTime, attackingLimb, ConvertUnits.ToSimUnits(attackPos), distance); } } - private bool SteerThroughGap(Structure wall, WallSection section, Vector2 targetWorldPos, float deltaTime) - { - Hull targetHull = section.gap?.FlowTargetHull; - if (targetHull != null) - { - if (wall.IsHorizontal) - { - targetWorldPos.Y = targetHull.WorldRect.Y - targetHull.Rect.Height / 2; - } - else - { - targetWorldPos.X = targetHull.WorldRect.Center.X; - } - LatchOntoAI?.DeattachFromBody(); - Character.AnimController.ReleaseStuckLimbs(); - if (steeringManager is IndoorsSteeringManager) - { - steeringManager.SteeringManual(deltaTime, Vector2.Normalize(targetWorldPos - Character.WorldPosition)); - } - else - { - steeringManager.SteeringSeek(ConvertUnits.ToSimUnits(targetWorldPos)); - } - return true; - } - return false; - } - private Limb GetAttackLimb(Vector2 attackWorldPos, Limb ignoredLimb = null) { AttackContext currentContext = Character.GetAttackContext(); var target = wallTarget != null ? wallTarget.Structure : SelectedAiTarget.Entity; - Limb selectedLimb = null; - float currentPriority = 0; - foreach (Limb limb in Character.AnimController.Limbs) - { - if (limb == ignoredLimb) { continue; } - if (limb.IsSevered || limb.IsStuck) { continue; } - var attack = limb.attack; - if (attack == null) { continue; } - if (attack.CoolDownTimer > 0) { continue; } - if (!attack.IsValidContext(currentContext)) { continue; } - if (!attack.IsValidTarget(target)) { continue; } - if (target is ISerializableEntity se && target is Character) - { - // TODO: allow conditionals of which matching any is enough instead of having to fulfill all - if (attack.Conditionals.Any(c => !c.Matches(se))) { continue; } - } - float priority = CalculatePriority(limb, attackWorldPos); - if (priority > currentPriority) - { - currentPriority = priority; - selectedLimb = limb; - } - } - return selectedLimb; - - float CalculatePriority(Limb limb, Vector2 attackPos) - { - float dist = Vector2.Distance(limb.WorldPosition, attackPos); - // The limb is ignored if the target is not close. Prevents character going in reverse if very far away from it. - // We also need a max value that is more than the actual range. - float distanceFactor = MathHelper.Lerp(1, 0, MathUtils.InverseLerp(0, limb.attack.Range * 3, dist)); - return (1 + limb.attack.Priority) * distanceFactor; - } + var limbs = Character.AnimController.Limbs + .Where(l => + l != ignoredLimb && + l.attack != null && + !l.IsSevered && + !l.IsStuck && + l.attack.IsValidContext(currentContext) && + l.attack.IsValidTarget(target) && + l.attack.Conditionals.All(c => (target is ISerializableEntity se && c.Matches(se)) || !(target is ISerializableEntity) || !(target is Character))) + .OrderByDescending(l => l.attack.Priority) + .ThenBy(l => Vector2.Distance(l.WorldPosition, attackWorldPos)); + // TODO: priority should probably not override the distance -> use values instead of booleans + return limbs.FirstOrDefault(); } private void UpdateWallTarget() { wallTarget = null; + if (Character.AnimController.CurrentHull != null) + { + return; + } + //check if there's a wall between the target and the Character - Vector2 rayStart = SimPosition; + Vector2 rayStart = Character.SimPosition; Vector2 rayEnd = SelectedAiTarget.SimPosition; bool offset = SelectedAiTarget.Entity.Submarine != null && Character.Submarine == null; if (offset) { - rayStart -= SelectedAiTarget.Entity.Submarine.SimPosition; + rayStart -= ConvertUnits.ToSimUnits(SelectedAiTarget.Entity.Submarine.Position); } Body closestBody = Submarine.CheckVisibility(rayStart, rayEnd, ignoreSubs: true); @@ -890,21 +738,20 @@ namespace Barotrauma if (closestBody.UserData is Structure wall && wall.Submarine != null) { int sectionIndex = wall.FindSectionIndex(ConvertUnits.ToDisplayUnits(Submarine.LastPickedPosition)); + int passableHoleCount = GetMinimumPassableHoleCount(); float sectionDamage = wall.SectionDamage(sectionIndex); for (int i = sectionIndex - 2; i <= sectionIndex + 2; i++) { if (wall.SectionBodyDisabled(i)) { - if (aggressiveBoarding && CanPassThroughHole(wall, i)) + if (aggressiveBoarding && CanPassThroughHole(wall, i)) //aggressive boarders always target holes they can pass through { - //aggressive boarders always target holes they can pass through sectionIndex = i; break; } - else + else //otherwise ignore and keep breaking other sections { - //otherwise ignore and keep breaking other sections continue; } } @@ -915,16 +762,17 @@ namespace Barotrauma Vector2 attachTargetNormal; if (wall.IsHorizontal) { - attachTargetNormal = new Vector2(0.0f, Math.Sign(WorldPosition.Y - wall.WorldPosition.Y)); + attachTargetNormal = new Vector2(0.0f, Math.Sign(Character.WorldPosition.Y - wall.WorldPosition.Y)); sectionPos.Y += (wall.BodyHeight <= 0.0f ? wall.Rect.Height : wall.BodyHeight) / 2 * attachTargetNormal.Y; } else { - attachTargetNormal = new Vector2(Math.Sign(WorldPosition.X - wall.WorldPosition.X), 0.0f); + attachTargetNormal = new Vector2(Math.Sign(Character.WorldPosition.X - wall.WorldPosition.X), 0.0f); sectionPos.X += (wall.BodyWidth <= 0.0f ? wall.Rect.Width : wall.BodyWidth) / 2 * attachTargetNormal.X; } - LatchOntoAI?.SetAttachTarget(wall.Submarine.PhysicsBody.FarseerBody, wall.Submarine, ConvertUnits.ToSimUnits(sectionPos), attachTargetNormal); + wallTarget = new WallTarget(sectionPos, wall, sectionIndex); + latchOntoAI?.SetAttachTarget(wall.Submarine.PhysicsBody.FarseerBody, wall.Submarine, ConvertUnits.ToSimUnits(sectionPos), attachTargetNormal); } } @@ -941,11 +789,11 @@ namespace Barotrauma } } - LatchOntoAI?.DeattachFromBody(); + latchOntoAI?.DeattachFromBody(); Character.AnimController.ReleaseStuckLimbs(); if (attacker == null || attacker.AiTarget == null) return; - AITargetMemory targetMemory = GetTargetMemory(attacker.AiTarget); + AITargetMemory targetMemory = FindTargetMemory(attacker.AiTarget); targetMemory.Priority += GetRelativeDamage(attackResult.Damage, Character.Vitality) * aggressionhurt; // Reduce the cooldown so that the character can react @@ -967,37 +815,23 @@ namespace Barotrauma private void UpdateLimbAttack(float deltaTime, Limb limb, Vector2 attackSimPos, float distance = -1) { - if (SelectedAiTarget == null) { return; } - if (wallTarget != null) + var damageTarget = wallTarget != null ? wallTarget.Structure : SelectedAiTarget.Entity as IDamageable; + if (damageTarget == null) return; + + float prevHealth = damageTarget.Health; + if (limb.UpdateAttack(deltaTime, attackSimPos, damageTarget, out AttackResult attackResult, distance)) { - // If the selected target is not the wall target, make the wall target the selected target. - var aiTarget = wallTarget.Structure.AiTarget; - if (aiTarget != null && SelectedAiTarget != aiTarget) + if (damageTarget.Health > 0) { - SelectTarget(aiTarget); - } - } - if (SelectedAiTarget.Entity is IDamageable damageTarget) - { - float prevHealth = damageTarget.Health; - if (limb.UpdateAttack(deltaTime, attackSimPos, damageTarget, out AttackResult attackResult, distance)) - { - if (damageTarget.Health > 0) - { - // Managed to hit a living/non-destroyed target. Increase the priority more if the target is low in health -> dies easily/soon - selectedTargetMemory.Priority += GetRelativeDamage(attackResult.Damage, damageTarget.Health) * aggressiongreed; - } - else - { - selectedTargetMemory.Priority = 0; - } + // Managed to hit a living/non-destroyed target. Increase the priority more if the target is low in health -> dies easily/soon + selectedTargetMemory.Priority += GetRelativeDamage(attackResult.Damage, damageTarget.Health) * aggressiongreed; } } } - private void UpdateFallBack(Vector2 attackWorldPos, float deltaTime) + private void UpdateFallBack(Vector2 attackPosition, float deltaTime) { - Vector2 attackVector = attackWorldPos - WorldPosition; + Vector2 attackVector = attackPosition - Character.WorldPosition; float dist = attackVector.Length(); float desiredDist = colliderSize * 2.0f; if (dist < desiredDist) @@ -1056,20 +890,15 @@ namespace Barotrauma //goes through all the AItargets, evaluates how preferable it is to attack the target, //whether the Character can see/hear the target and chooses the most preferable target within //sight/hearing range - public AITarget UpdateTargets(Character character, out TargetingPriority priority) + public void UpdateTargets(Character character, out TargetingPriority targetingPriority) { - if (IsProperlyLatched) - { - // If attached to a valid target, just keep the target. - // Priority not used in this case. - priority = null; - return SelectedAiTarget; - } AITarget newTarget = null; - priority = null; + targetingPriority = null; selectedTargetMemory = null; targetValue = 0.0f; + UpdateTargetMemories(); + foreach (AITarget target in AITarget.List) { if (!target.Enabled) continue; @@ -1078,63 +907,26 @@ namespace Barotrauma continue; } + float valueModifier = 1.0f; + float dist = 0.0f; + Character targetCharacter = target.Entity as Character; //ignore the aitarget if it is the Character itself if (targetCharacter == character) continue; - float valueModifier = 1; string targetingTag = null; if (targetCharacter != null) { - if (targetCharacter.IsDead) - { - targetingTag = "dead"; - if (targetCharacter.Submarine != Character.Submarine) - { - // In a different sub or the target is outside when we are inside or vice versa -> Ignore the target - continue; - } - else if (targetCharacter.CurrentHull != Character.CurrentHull) - { - // In the same sub, halve the priority, if not in the same hull. - valueModifier = 0.5f; - } - } - else if (targetCharacter.AIController is EnemyAIController enemy) - { - if (enemy.combatStrength > combatStrength) - { - targetingTag = "stronger"; - } - else if (enemy.combatStrength < combatStrength) - { - targetingTag = "weaker"; - } - if (State == AIState.Escape && targetingTag == "stronger") - { - // Frightened - valueModifier = 2; - } - else - { - if (targetCharacter.Submarine != Character.Submarine) - { - // In a different sub or the target is outside when we are inside or vice versa -> Ignore the target - continue; - } - else if (targetCharacter.CurrentHull != Character.CurrentHull) - { - // In the same sub, halve the priority, if not in the same hull. - valueModifier = 0.5f; - } - } - } - else if (targetCharacter.Submarine != null && Character.Submarine == null) + if (targetCharacter.Submarine != null && Character.Submarine == null) { //target inside, AI outside -> we'll be attacking a wall between the characters so use the priority for attacking rooms targetingTag = "room"; } + else if (targetCharacter.IsDead) + { + targetingTag = "dead"; + } else if (targetingPriorities.ContainsKey(targetCharacter.SpeciesName.ToLowerInvariant())) { targetingTag = targetCharacter.SpeciesName.ToLowerInvariant(); @@ -1145,6 +937,42 @@ namespace Barotrauma //skip the target if it's a room and the character is already inside a sub if (character.CurrentHull != null && target.Entity is Hull) continue; + Door door = null; + if (target.Entity is Item item) + { + if (targetCharacter.AIController is EnemyAIController enemy) + { + targetingTag = "room"; + } + + door = item.GetComponent(); + foreach (TargetingPriority prio in targetingPriorities.Values) + { + if (item.HasTag(prio.TargetTag)) + { + targetingTag = "stronger"; + } + } + } + else if (target.Entity is Structure s) + { + targetingTag = "wall"; + if (aggressiveBoarding) + { + // Ignore walls when inside. + valueModifier = character.CurrentHull == null ? 2 : 0; + if (valueModifier > 0) + { + targetingTag = "weaker"; + } + } + } + } + else if (target.Entity != null) + { + //skip the target if it's a room and the character is already inside a sub + if (character.CurrentHull != null && target.Entity is Hull) continue; + Door door = null; if (target.Entity is Item item) { @@ -1164,63 +992,19 @@ namespace Barotrauma } } } - else if (target.Entity is Structure s) - { - targetingTag = "wall"; - if (aggressiveBoarding) - { - // Ignore walls when inside. - valueModifier = character.CurrentHull == null ? 2 : 0; - if (valueModifier > 0) - { - // Ignore structures that doesn't have a body (not walls) - valueModifier *= s.HasBody ? 1 : 0; - } - for (int i = 0; i < s.Sections.Length; i++) - { - var section = s.Sections[i]; - if (CanPassThroughHole(s, i)) - { - // Ignore walls that can be passed through - valueModifier = 0; - break; - } - else if (section.gap != null) - { - // up to 100% priority increase for every gap in the wall - valueModifier *= 1 + section.gap.Open; - } - } - } - } else { targetingTag = "room"; } + if (door != null) { - // If there's not a more specific tag for the door - if (string.IsNullOrEmpty(targetingTag) || targetingTag == "room") - { - targetingTag = "door"; - } - bool isOutdoor = door.LinkedGap?.FlowTargetHull != null && !door.LinkedGap.IsRoomToRoom; - bool isOpen = door.IsOpen || door.Item.Condition <= 0.0f; //increase priority if the character is outside and an aggressive boarder, and the door is from outside to inside - if (aggressiveBoarding) + if (character.CurrentHull == null && aggressiveBoarding && !door.LinkedGap.IsRoomToRoom) { - if (character.CurrentHull == null) - { - valueModifier = isOutdoor ? 1 : 0; - valueModifier *= isOpen ? 5 : 1; - } - else - { - valueModifier = isOutdoor ? 0 : 1; - valueModifier *= isOpen ? 0 : 1; - } + valueModifier = door.IsOpen ? 10 : 5; } - else if (isOpen) //ignore broken and open doors + else if (door.IsOpen || door.Item.Condition <= 0.0f) //ignore broken and open doors { continue; } @@ -1239,7 +1023,7 @@ namespace Barotrauma if (valueModifier == 0.0f) continue; Vector2 toTarget = target.WorldPosition - character.WorldPosition; - float dist = toTarget.Length(); + dist = toTarget.Length(); //if the target has been within range earlier, the character will notice it more easily //(i.e. remember where the target was) @@ -1253,29 +1037,30 @@ namespace Barotrauma // -> just ignore the distance and attack whatever has the highest priority dist = Math.Max(dist, 100.0f); - AITargetMemory targetMemory = GetTargetMemory(target); + AITargetMemory targetMemory = FindTargetMemory(target); if (Character.CurrentHull != null && Math.Abs(toTarget.Y) > Character.CurrentHull.Size.Y) { // Inside the sub, treat objects that are up or down, as they were farther away. dist *= 3; } - valueModifier *= targetMemory.Priority / (float)Math.Sqrt(dist); + valueModifier = valueModifier * targetMemory.Priority / (float)Math.Sqrt(dist); if (valueModifier > targetValue) { newTarget = target; selectedTargetMemory = targetMemory; - priority = targetingPriorities[targetingTag]; + targetingPriority = targetingPriorities[targetingTag]; targetValue = valueModifier; } } SelectedAiTarget = newTarget; + if (SelectedAiTarget != _previousAiTarget) { wallTarget = null; } - return SelectedAiTarget; + _previousAiTarget = SelectedAiTarget; } private AITargetMemory GetTargetMemory(AITarget target) @@ -1285,21 +1070,24 @@ namespace Barotrauma memory = new AITargetMemory(10); targetMemories.Add(target, memory); } + + memory = new AITargetMemory(10.0f); + targetMemories.Add(target, memory); + return memory; } private List removals = new List(); private void UpdateTargetMemories(float deltaTime) { - removals.Clear(); - foreach (var memory in targetMemories) + List toBeRemoved = null; + foreach (KeyValuePair memory in targetMemories) { - // Slowly decrease all memories - memory.Value.Priority -= memoryFadeTime * deltaTime; - // Remove targets that have no priority or have been removed - if (memory.Value.Priority <= 1 || !AITarget.List.Contains(memory.Key)) + memory.Value.Priority += 0.1f; + if (Math.Abs(memory.Value.Priority) < 1.0f || !AITarget.List.Contains(memory.Key)) { - removals.Add(memory.Key); + if (toBeRemoved == null) toBeRemoved = new List(); + toBeRemoved.Add(memory.Key); } } removals.ForEach(r => targetMemories.Remove(r)); @@ -1316,9 +1104,25 @@ namespace Barotrauma AttackingLimb = null; } + if (toBeRemoved != null) + { + foreach (AITarget target in toBeRemoved) + { + targetMemories.Remove(target); + } + } + + #endregion + + protected override void OnStateChanged(AIState from, AIState to) + { + latchOntoAI?.DeattachFromBody(); + Character.AnimController.ReleaseStuckLimbs(); + } + private int GetMinimumPassableHoleCount() { - return (int)Math.Ceiling(ConvertUnits.ToDisplayUnits(colliderSize) / Structure.WallSectionSize); + return (int)Math.Ceiling(ConvertUnits.ToDisplayUnits(colliderSize) / Structure.WallSectionSize); } private bool CanPassThroughHole(Structure wall, int sectionIndex) diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/LatchOntoAI.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/LatchOntoAI.cs index fe5304a4c..200e20a5a 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/LatchOntoAI.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/LatchOntoAI.cs @@ -193,7 +193,7 @@ namespace Barotrauma // is not attached or is attached to something else if (!IsAttached || IsAttached && attachJoints[0].BodyB == attachTargetBody) { - if (Vector2.DistanceSquared(ConvertUnits.ToDisplayUnits(transformedAttachPos), enemyAI.AttackingLimb.WorldPosition) < enemyAI.AttackingLimb.attack.DamageRange * enemyAI.AttackingLimb.attack.DamageRange) + if (Vector2.DistanceSquared(ConvertUnits.ToDisplayUnits(transformedAttachPos), enemyAI.AttackingLimb.WorldPosition) < enemyAI.AttackingLimb.attack.Range * enemyAI.AttackingLimb.attack.Range) { AttachToBody(character.AnimController.Collider, attachLimb, attachTargetBody, transformedAttachPos); } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs index 12c490f57..f3afac468 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs @@ -115,8 +115,7 @@ namespace Barotrauma unreachable.Add(goToObjective.Target as Hull); } goToObjective = null; - HumanAIController.ObjectiveManager.GetObjective().Wander(deltaTime); - //SteeringManager.SteeringWander(); + SteeringManager.SteeringWander(); } } else if (currentHull != null) diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveGetItem.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveGetItem.cs index d0c519da9..7f20924c1 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveGetItem.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveGetItem.cs @@ -99,8 +99,7 @@ namespace Barotrauma FindTargetItem(); if (targetItem == null || moveToTarget == null) { - HumanAIController.ObjectiveManager.GetObjective().Wander(deltaTime); - //SteeringManager.SteeringWander(); + SteeringManager.SteeringWander(); return; } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveIdle.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveIdle.cs index 378a71008..bba2a53e5 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveIdle.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveIdle.cs @@ -98,64 +98,60 @@ namespace Barotrauma PathSteering.Reset(); return; } + if (standStillTimer < -walkDuration) { standStillTimer = Rand.Range(standStillMin, standStillMax); } + + //steer away from edges of the hull + if (character.AnimController.CurrentHull != null && !character.IsClimbing) + { + standStillTimer = Rand.Range(standStillMin, standStillMax); + } Wander(deltaTime); return; } - if (currentTarget != null) - { - character.AIController.SteeringManager.SteeringSeek(currentTarget.SimPosition); - } - } - - public void Wander(float deltaTime) - { - //steer away from edges of the hull - if (character.AnimController.CurrentHull != null && !character.IsClimbing) - { - float leftDist = character.Position.X - character.AnimController.CurrentHull.Rect.X; - float rightDist = character.AnimController.CurrentHull.Rect.Right - character.Position.X; - if (leftDist < WallAvoidDistance && rightDist < WallAvoidDistance) - { - if (Math.Abs(rightDist - leftDist) > WallAvoidDistance / 2) + if (leftDist < WallAvoidDistance && rightDist < WallAvoidDistance) { - PathSteering.SteeringManual(deltaTime, Vector2.UnitX * Math.Sign(rightDist - leftDist)); + if (Math.Abs(rightDist - leftDist) > WallAvoidDistance / 2) + { + PathSteering.SteeringManual(deltaTime, Vector2.UnitX * Math.Sign(rightDist - leftDist)); + } + else + { + PathSteering.Reset(); + return; + } + } + else if (leftDist < WallAvoidDistance) + { + PathSteering.SteeringManual(deltaTime, Vector2.UnitX * (WallAvoidDistance-leftDist) / WallAvoidDistance); + PathSteering.WanderAngle = 0.0f; + return; } else { - PathSteering.Reset(); + PathSteering.SteeringManual(deltaTime, -Vector2.UnitX * (WallAvoidDistance-rightDist) / WallAvoidDistance); + PathSteering.WanderAngle = MathHelper.Pi; + return; } } - else if (leftDist < WallAvoidDistance) + + character.AIController.SteeringManager.SteeringWander(); + if (!character.IsClimbing && !character.AnimController.InWater) { - //PathSteering.SteeringManual(deltaTime, Vector2.UnitX * (WallAvoidDistance - leftDist) / WallAvoidDistance); - PathSteering.SteeringManual(deltaTime, Vector2.UnitX); - PathSteering.WanderAngle = 0.0f; - } - else if (rightDist < WallAvoidDistance) - { - //PathSteering.SteeringManual(deltaTime, -Vector2.UnitX * (WallAvoidDistance - rightDist) / WallAvoidDistance); - PathSteering.SteeringManual(deltaTime, -Vector2.UnitX); - PathSteering.WanderAngle = MathHelper.Pi; - } - else - { - SteeringManager.SteeringWander(); - } + //reset vertical steering to prevent dropping down from platforms etc + character.AIController.SteeringManager.ResetY(); + } + return; } - else + + if (currentTarget != null) { - SteeringManager.SteeringWander(); - } - if (!character.IsClimbing && !character.AnimController.InWater) - { - //reset vertical steering to prevent dropping down from platforms etc - character.AIController.SteeringManager.ResetY(); + character.AIController.SteeringManager.SteeringSeek(currentTarget.SimPosition); } } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Animation/FishAnimController.cs b/Barotrauma/BarotraumaShared/Source/Characters/Animation/FishAnimController.cs index 7faa82a97..05d777513 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Animation/FishAnimController.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Animation/FishAnimController.cs @@ -369,18 +369,9 @@ namespace Barotrauma return; } - Vector2 transformedMovement = reverse ? -movement : movement; - float movementAngle = MathUtils.VectorToAngle(transformedMovement) - MathHelper.PiOver2; - float mainLimbAngle = 0; - if (MainLimb.type == LimbType.Torso && TorsoAngle.HasValue) - { - mainLimbAngle = TorsoAngle.Value; - } - else if (MainLimb.type == LimbType.Head && HeadAngle.HasValue) - { - mainLimbAngle = HeadAngle.Value; - } - mainLimbAngle *= Dir; + float movementAngle = MathUtils.VectorToAngle(movement) - MathHelper.PiOver2; + + float mainLimbAngle = (MainLimb.type == LimbType.Torso ? TorsoAngle.Value : HeadAngle.Value) * Dir; while (MainLimb.Rotation - (movementAngle + mainLimbAngle) > MathHelper.Pi) { movementAngle += MathHelper.TwoPi; @@ -388,7 +379,7 @@ namespace Barotrauma while (MainLimb.Rotation - (movementAngle + mainLimbAngle) < -MathHelper.Pi) { movementAngle -= MathHelper.TwoPi; - } + } if (CurrentSwimParams.RotateTowardsMovement) { @@ -412,6 +403,7 @@ namespace Barotrauma if (TailAngle.HasValue) { Limb tail = GetLimb(LimbType.Tail); + //tail?.body.SmoothRotate(movementAngle + TailAngle.Value * Dir, TailTorque); if (tail != null) { SmoothRotateWithoutWrapping(tail, movementAngle + TailAngle.Value * Dir, MainLimb, TailTorque); @@ -421,10 +413,6 @@ namespace Barotrauma else { movementAngle = Dir > 0 ? -MathHelper.PiOver2 : MathHelper.PiOver2; - if (reverse) - { - movementAngle = MathUtils.WrapAngleTwoPi(movementAngle - MathHelper.Pi); - } if (MainLimb.type == LimbType.Head && HeadAngle.HasValue) { Collider.SmoothRotate(HeadAngle.Value * Dir, CurrentSwimParams.SteerTorque); @@ -454,7 +442,7 @@ namespace Barotrauma var waveAmplitude = Math.Abs(CurrentSwimParams.WaveAmplitude); if (waveLength > 0 && waveAmplitude > 0) { - WalkPos -= transformedMovement.Length() / Math.Abs(waveLength); + WalkPos -= movement.Length() / Math.Abs(waveLength); WalkPos = MathUtils.WrapAngleTwoPi(WalkPos); } @@ -697,6 +685,12 @@ namespace Barotrauma limb.body.ApplyForce(diff * (float)(Math.Sin(WalkPos) * Math.Sqrt(limb.Mass)) * 30.0f * animStrength); } + while (referenceLimb.Rotation - angle < -MathHelper.TwoPi) + { + angle -= MathHelper.TwoPi; + } + + limb?.body.SmoothRotate(angle, torque, wrapAngle: false); } private void SmoothRotateWithoutWrapping(Limb limb, float angle, Limb referenceLimb, float torque) diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimController.cs b/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimController.cs index 241a2fb3b..445aade77 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimController.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimController.cs @@ -1463,6 +1463,11 @@ namespace Barotrauma targetRightHand.PullJointMaxForce = 5000.0f; } + if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) + { + Collider.ResetDynamics(); + } + target.AnimController.IgnorePlatforms = true; } else diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Animation/Params/Animation/AnimationParams.cs b/Barotrauma/BarotraumaShared/Source/Characters/Animation/Params/Animation/AnimationParams.cs index 0d3b77f8a..4c8d56679 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Animation/Params/Animation/AnimationParams.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Animation/Params/Animation/AnimationParams.cs @@ -99,7 +99,7 @@ namespace Barotrauma public static string GetDefaultFolder(string speciesName) => $"Content/Characters/{speciesName.CapitaliseFirstInvariant()}/Animations/"; public static string GetDefaultFile(string speciesName, AnimationType animType) => $"{GetFolder(speciesName)}{GetDefaultFileName(speciesName, animType)}.xml"; - public static string GetFolder(string speciesName) + protected static string GetFolder(string speciesName) { var folder = XMLExtensions.TryLoadXml(Character.GetConfigFile(speciesName))?.Root?.Element("animations")?.GetAttributeString("folder", string.Empty); if (string.IsNullOrEmpty(folder) || folder.ToLowerInvariant() == "default") diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Animation/Params/Ragdoll/RagdollParams.cs b/Barotrauma/BarotraumaShared/Source/Characters/Animation/Params/Ragdoll/RagdollParams.cs index 3f32ca43d..bb2a4d0b4 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Animation/Params/Ragdoll/RagdollParams.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Animation/Params/Ragdoll/RagdollParams.cs @@ -79,7 +79,7 @@ namespace Barotrauma new XAttribute("sourcerect", $"0, 0, 1, 1"))) }; - public static string GetFolder(string speciesName) + protected static string GetFolder(string speciesName) { var folder = XMLExtensions.TryLoadXml(Character.GetConfigFile(speciesName))?.Root?.Element("ragdolls")?.GetAttributeString("folder", string.Empty); if (string.IsNullOrEmpty(folder) || folder.ToLowerInvariant() == "default") diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs b/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs index 82467a761..c060cbb92 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs @@ -1035,6 +1035,8 @@ namespace Barotrauma CheckValidity(); + CheckValidity(); + UpdateNetPlayerPosition(deltaTime); CheckDistFromCollider(); UpdateCollisionCategories(); @@ -1295,42 +1297,17 @@ namespace Barotrauma UpdateProjSpecific(deltaTime); } - public bool Invalid { get; private set; } - private int validityResets; - private bool CheckValidity() + private void CheckValidity() { - bool isColliderValid = CheckValidity(Collider); - bool limbsValid = true; + CheckValidity(Collider); foreach (Limb limb in limbs) { if (limb.body == null || !limb.body.Enabled) { continue; } - if (!CheckValidity(limb.body)) - { - limbsValid = false; - break; - } + CheckValidity(limb.body); } - bool isValid = isColliderValid && limbsValid; - if (!isValid) - { - validityResets++; - if (validityResets > 1) - { - Invalid = true; - DebugConsole.ThrowError("Invalid ragdoll physics. Ragdoll freezed to prevent crashes."); - Collider.SetTransform(Vector2.Zero, 0.0f); - foreach (Limb limb in Limbs) - { - limb.body.SetTransform(Collider.SimPosition, 0.0f); - limb.body.ResetDynamics(); - } - Frozen = true; - } - } - return isValid; } - private bool CheckValidity(PhysicsBody body) + private void CheckValidity(PhysicsBody body) { string errorMsg = null; string bodyName = body.UserData is Limb ? "Limb" : "Collider"; @@ -1382,7 +1359,7 @@ namespace Barotrauma limb.body.ResetDynamics(); } SetInitialLimbPositions(); - return false; + return; } return true; } @@ -1524,7 +1501,8 @@ namespace Barotrauma float allowedDist = Math.Max(Math.Max(Collider.radius, Collider.width), Collider.height) * 2.0f; float resetDist = allowedDist * 5.0f; - float distSqrd = Vector2.DistanceSquared(Collider.SimPosition, MainLimb.SimPosition); + Vector2 diff = Collider.SimPosition - MainLimb.SimPosition; + float distSqrd = diff.LengthSquared(); if (distSqrd > resetDist * resetDist) { @@ -1535,10 +1513,13 @@ namespace Barotrauma { //ragdoll too far from the collider, disable collisions until it's close enough //(in case the ragdoll has gotten stuck somewhere) + + Vector2 forceDir = diff / (float)Math.Sqrt(distSqrd); foreach (Limb limb in Limbs) { if (limb.IsSevered) continue; limb.body.CollidesWith = Physics.CollisionNone; + limb.body.ApplyForce(forceDir * limb.Mass * 10.0f, maxVelocity: 10.0f); } collisionsDisabled = true; diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Attack.cs b/Barotrauma/BarotraumaShared/Source/Characters/Attack.cs index 0e84aa580..d815d3771 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Attack.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Attack.cs @@ -28,7 +28,6 @@ namespace Barotrauma public enum AIBehaviorAfterAttack { FallBack, - FallBackUntilCanAttack, PursueIfCanAttack, Pursue } @@ -82,9 +81,6 @@ namespace Barotrauma [Serialize(AIBehaviorAfterAttack.FallBack, true), Editable(ToolTip = "The preferred AI behavior after the attack.")] public AIBehaviorAfterAttack AfterAttack { get; private set; } - [Serialize(false, true), Editable(ToolTip = "Should the ai try to reverse when aiming with this attack?")] - public bool Reverse { get; private set; } - [Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 2000.0f, ToolTip = "Min distance from the attack limb to the target before the AI tries to attack.")] public float Range { get; private set; } @@ -100,9 +96,6 @@ namespace Barotrauma [Serialize(0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, DecimalCount = 2, ToolTip = "Used as the attack cooldown between different kind of attacks. Does not have effect, if set to 0.")] public float SecondaryCoolDown { get; private set; } = 0; - [Serialize(0f, true), Editable(MinValueFloat = 0, MaxValueFloat = 1, DecimalCount = 2, ToolTip = "Random factor applied to all cooldowns. Example: 0.1 -> adds a random value between -10% and 10% of the cooldown. Min 0 (default), Max 1 (could disable or double the cooldown in extreme cases).")] - public float CoolDownRandomFactor { get; private set; } = 0; - [Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10000.0f)] public float StructureDamage { get; private set; } @@ -189,7 +182,7 @@ namespace Barotrauma public readonly List Afflictions = new List(); /// - /// Only affects ai decision making. All the conditionals has to be met in order to select the attack. TODO: allow to define conditionals using any (implemented in StatusEffect -> move from there to PropertyConditional?) + /// Only affects ai decision making. /// public List Conditionals { get; private set; } = new List(); @@ -451,10 +444,8 @@ namespace Barotrauma public void SetCoolDown() { - float randomFraction = CoolDown * CoolDownRandomFactor; - CoolDownTimer = CoolDown + MathHelper.Lerp(-randomFraction, randomFraction, Rand.Value(Rand.RandSync.Server)); - randomFraction = SecondaryCoolDown * CoolDownRandomFactor; - SecondaryCoolDownTimer = SecondaryCoolDown + MathHelper.Lerp(-randomFraction, randomFraction, Rand.Value(Rand.RandSync.Server)); + CoolDownTimer = CoolDown; + SecondaryCoolDownTimer = SecondaryCoolDown; } public void ResetCoolDown() diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs index 19c905b3f..39e60ac07 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs @@ -810,7 +810,6 @@ namespace Barotrauma public void LoadHeadAttachments() { - if (Info == null) { return; } if (AnimController == null) { return; } var head = AnimController.GetLimb(LimbType.Head); if (head == null) { return; } @@ -1113,15 +1112,13 @@ namespace Barotrauma ViewTarget = null; if (!AllowInput) return; - if (Controlled == this || (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)) + Vector2 smoothedCursorDiff = cursorPosition - SmoothedCursorPosition; + if (Controlled == this) { SmoothedCursorPosition = cursorPosition; } else { - //apply some smoothing to the cursor positions of remote players when playing as a client - //to make aiming look a little less choppy - Vector2 smoothedCursorDiff = cursorPosition - SmoothedCursorPosition; smoothedCursorDiff = NetConfig.InterpolateCursorPositionError(smoothedCursorDiff); SmoothedCursorPosition = cursorPosition - smoothedCursorDiff; } @@ -1188,10 +1185,6 @@ namespace Barotrauma if (PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.F)) { AnimController.ReleaseStuckLimbs(); - if (AIController != null && AIController is EnemyAIController enemyAI) - { - enemyAI.LatchOntoAI?.DeattachFromBody(); - } } #endif @@ -1271,7 +1264,15 @@ namespace Barotrauma if (IsKeyDown(InputType.Aim) && selectedItems[i] != null) selectedItems[i].SecondaryUse(deltaTime, this); } } - + +#if CLIENT + if (SelectedConstruction != null && SelectedConstruction.ActiveHUDs.Any(ic => ic.GuiFrame != null && HUD.CloseHUD(ic.GuiFrame.Rect))) + { + //emulate a Select input to get the character to deselect the item server-side + keys[(int)InputType.Select].Hit = true; + SelectedConstruction = null; + } +#endif if (SelectedConstruction != null) { if (IsKeyDown(InputType.Use)) SelectedConstruction.Use(deltaTime, this); @@ -1649,8 +1650,7 @@ namespace Barotrauma #if CLIENT if (isLocalPlayer) { - if (GUI.MouseOn == null && - (!CharacterInventory.IsMouseOnInventory() || CharacterInventory.DraggingItemToWorld)) + if (GUI.MouseOn == null && !CharacterInventory.IsMouseOnInventory()) { if (findFocusedTimer <= 0.0f || Screen.Selected == GameMain.SubEditorScreen) { @@ -1665,12 +1665,10 @@ namespace Barotrauma focusedItem = null; } findFocusedTimer -= deltaTime; - } + } #endif //climb ladders automatically when pressing up/down inside their trigger area - Ladder currentLadder = SelectedConstruction?.GetComponent(); - if ((SelectedConstruction == null || currentLadder != null) && - !AnimController.InWater && Screen.Selected != GameMain.SubEditorScreen) + if (SelectedConstruction == null && !AnimController.InWater && Screen.Selected != GameMain.SubEditorScreen) { bool climbInput = IsKeyDown(InputType.Up) || IsKeyDown(InputType.Down); bool isControlled = Controlled == this; @@ -1681,19 +1679,6 @@ namespace Barotrauma float minDist = float.PositiveInfinity; foreach (Ladder ladder in Ladder.List) { - if (ladder == currentLadder) - { - continue; - } - else if (currentLadder != null) - { - //only switch from ladder to another if the ladders are above the current ladders and pressing up, or vice versa - if (ladder.Item.WorldPosition.Y > currentLadder.Item.WorldPosition.Y != IsKeyDown(InputType.Up)) - { - continue; - } - } - if (CanInteractWith(ladder.Item, out float dist, checkLinked: false) && dist < minDist) { minDist = dist; @@ -1710,7 +1695,7 @@ namespace Barotrauma } } - if (SelectedCharacter != null && (IsKeyHit(InputType.Grab) || IsKeyHit(InputType.Health))) //Let people use ladders and buttons and stuff when dragging chars + if (SelectedCharacter != null && IsKeyHit(InputType.Grab)) //Let people use ladders and buttons and stuff when dragging chars { DeselectCharacter(); } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/CharacterInfo.cs b/Barotrauma/BarotraumaShared/Source/Characters/CharacterInfo.cs index 8dcf98fce..c5aee6f70 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/CharacterInfo.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/CharacterInfo.cs @@ -804,12 +804,6 @@ namespace Barotrauma var newItem = Item.Load(itemElement, inventory.Owner.Submarine, createNetworkEvent: true); if (newItem == null) { continue; } - if (!MathUtils.NearlyEqual(newItem.Condition, newItem.MaxCondition) && - GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer) - { - GameMain.NetworkMember.CreateEntityEvent(newItem, new object[] { NetEntityEvent.Type.Status }); - } - int[] slotIndices = itemElement.GetAttributeIntArray("i", new int[] { 0 }); if (!slotIndices.Any()) { diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Health/Afflictions/AfflictionPrefab.cs b/Barotrauma/BarotraumaShared/Source/Characters/Health/Afflictions/AfflictionPrefab.cs index 708e62bee..aea153f53 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Health/Afflictions/AfflictionPrefab.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Health/Afflictions/AfflictionPrefab.cs @@ -153,10 +153,7 @@ namespace Barotrauma //how high the strength has to be for the affliction icon to be shown in the UI public readonly float ShowIconThreshold = 0.05f; public readonly float MaxStrength = 100.0f; - - //how high the strength has to be for the affliction icon to be shown with a health scanner - public readonly float ShowInHealthScannerThreshold = 0.05f; - + public float BurnOverlayAlpha; public float DamageOverlayAlpha; @@ -260,8 +257,6 @@ namespace Barotrauma ShowIconThreshold = element.GetAttributeFloat("showiconthreshold", Math.Max(ActivationThreshold, 0.05f)); MaxStrength = element.GetAttributeFloat("maxstrength", 100.0f); - ShowInHealthScannerThreshold = element.GetAttributeFloat("showinhealthscannerthreshold", Math.Max(ActivationThreshold, 0.05f)); - DamageOverlayAlpha = element.GetAttributeFloat("damageoverlayalpha", 0.0f); BurnOverlayAlpha = element.GetAttributeFloat("burnoverlayalpha", 0.0f); diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Health/CharacterHealth.cs b/Barotrauma/BarotraumaShared/Source/Characters/Health/CharacterHealth.cs index 5d6f70a83..dff106cfb 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Health/CharacterHealth.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Health/CharacterHealth.cs @@ -460,7 +460,6 @@ namespace Barotrauma { affliction.Strength = 0.0f; } - CalculateVitality(); } private void AddLimbAffliction(Limb limb, Affliction newAffliction) diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Limb.cs b/Barotrauma/BarotraumaShared/Source/Characters/Limb.cs index b19684c74..0c1d42432 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Limb.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Limb.cs @@ -520,15 +520,14 @@ namespace Barotrauma // Ignore blocking on items, because it causes cases where a Mudraptor cannot hit the hatch, for example. wasHit = true; } - else if (damageTarget is Structure wall && structureBody != null && - (structureBody.UserData is Structure || (structureBody.UserData is Submarine sub && sub == wall.Submarine))) + else if (damageTarget is Structure && structureBody?.UserData is Structure) { - // If the attack is aimed to a structure (wall) and hits a structure or the sub, it's successful + // If the attack is aimed to a structure and hits a structure, it's successful wasHit = true; } else { - // If there is nothing between, the hit is successful + // If the attack is aimed to a character but hits a structure, the hit is blocked. wasHit = structureBody == null; } } diff --git a/Barotrauma/BarotraumaShared/Source/Events/Missions/Mission.cs b/Barotrauma/BarotraumaShared/Source/Events/Missions/Mission.cs index e8f5e87fd..adc0b0d8d 100644 --- a/Barotrauma/BarotraumaShared/Source/Events/Missions/Mission.cs +++ b/Barotrauma/BarotraumaShared/Source/Events/Missions/Mission.cs @@ -103,6 +103,12 @@ namespace Barotrauma if (missionType == MissionType.Random) { allowedMissions.AddRange(MissionPrefab.List); +#if SERVER + if (GameMain.Server != null) + { + allowedMissions.RemoveAll(mission => !GameMain.Server.ServerSettings.AllowedRandomMissionTypes.Contains(mission.type)); + } +#endif } else if (missionType == MissionType.None) { @@ -118,11 +124,6 @@ namespace Barotrauma { allowedMissions.RemoveAll(m => !m.IsAllowed(locations[0], locations[1])); } - - if (allowedMissions.Count == 0) - { - return null; - } int probabilitySum = allowedMissions.Sum(m => m.Commonness); int randomNumber = rand.NextInt32() % probabilitySum; diff --git a/Barotrauma/BarotraumaShared/Source/GameSession/GameSession.cs b/Barotrauma/BarotraumaShared/Source/GameSession/GameSession.cs index 9e00659fe..d5778c9cc 100644 --- a/Barotrauma/BarotraumaShared/Source/GameSession/GameSession.cs +++ b/Barotrauma/BarotraumaShared/Source/GameSession/GameSession.cs @@ -139,7 +139,7 @@ namespace Barotrauma MTRandom rand = new MTRandom(ToolBox.StringToInt(seed)); for (int i = 0; i < 2; i++) { - dummyLocations[i] = Location.CreateRandom(new Vector2((float)rand.NextDouble() * 10000.0f, (float)rand.NextDouble() * 10000.0f), null, rand); + dummyLocations[i] = Location.CreateRandom(new Vector2((float)rand.NextDouble() * 10000.0f, (float)rand.NextDouble() * 10000.0f), null); } } @@ -208,7 +208,7 @@ namespace Barotrauma DockingPort myPort = null, outPostPort = null; foreach (DockingPort port in DockingPort.List) { - if (port.IsHorizontal || port.Docked) { continue; } + if (port.IsHorizontal) { continue; } if (port.Item.Submarine == level.StartOutpost) { outPostPort = port; diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/DockingPort.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/DockingPort.cs index 189a94651..89ee1b073 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/DockingPort.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/DockingPort.cs @@ -880,7 +880,8 @@ namespace Barotrauma.Items.Components List linked = new List(item.linkedTo); foreach (MapEntity entity in linked) { - if (!(entity is Item linkedItem)) { continue; } + Item linkedItem = entity as Item; + if (linkedItem == null) { continue; } var dockingPort = linkedItem.GetComponent(); if (dockingPort != null) diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Holdable.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Holdable.cs index e5171e5c8..dc355a5c6 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Holdable.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Holdable.cs @@ -426,11 +426,11 @@ namespace Barotrauma.Items.Components public override bool Use(float deltaTime, Character character = null) { - if (!attachable || item.body == null) { return character == null || character.IsKeyDown(InputType.Aim); } + if (!attachable || item.body == null) return (character == null || character.IsKeyDown(InputType.Aim)); if (character != null) { - if (!character.IsKeyDown(InputType.Aim)) { return false; } - if (!CanBeAttached()) { return false; } + if (!character.IsKeyDown(InputType.Aim)) return false; + if (!CanBeAttached()) return false; #if SERVER if (GameMain.Server != null) { @@ -438,12 +438,7 @@ namespace Barotrauma.Items.Components GameServer.Log(character.LogName + " attached " + item.Name + " to a wall", ServerLog.MessageType.ItemInteraction); } #endif - } - - if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer) - { - if (character != null) { item.Drop(character); } - AttachToWall(); + item.Drop(character); } return true; @@ -555,7 +550,7 @@ namespace Barotrauma.Items.Components public override void ServerWrite(NetBuffer msg, Client c, object[] extraData = null) { base.ServerWrite(msg, c, extraData); - if (!attachable || body == null) { return; } + if (!attachable || body == null) return; msg.Write(Attached); msg.Write(body.SimPosition.X); diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/LevelResource.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/LevelResource.cs index 90a7a0665..458bf0c57 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/LevelResource.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/LevelResource.cs @@ -2,15 +2,12 @@ using Lidgren.Network; using Microsoft.Xna.Framework; using System; -using System.Linq; using System.Xml.Linq; namespace Barotrauma.Items.Components { partial class LevelResource : ItemComponent, IServerSerializable { - private float lastSentDeattachTimer; - [Serialize(1.0f, false)] public float DeattachDuration { @@ -24,29 +21,12 @@ namespace Barotrauma.Items.Components get { return deattachTimer; } set { - //clients don't deattach the item until the server says so (handled in ClientRead) - if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) - { - return; - } deattachTimer = Math.Max(0.0f, value); -#if SERVER - if (deattachTimer >= DeattachDuration) - { - if (holdable.Attached){ item.CreateServerEvent(this); } - holdable.DeattachFromWall(); - } - else if (Math.Abs(lastSentDeattachTimer - deattachTimer) > 0.1f) - { - item.CreateServerEvent(this); - lastSentDeattachTimer = deattachTimer; - } -#else - if (deattachTimer >= DeattachDuration) + //clients don't deattach the item until the server says so (handled in ClientRead) + if ((GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient) && deattachTimer >= DeattachDuration) { holdable.DeattachFromWall(); } -#endif } } @@ -87,10 +67,7 @@ namespace Barotrauma.Items.Components return; } holdable.Reattachable = false; - if (requiredItems.Any()) - { - holdable.PickingTime = float.MaxValue; - } + holdable.PickingTime = float.MaxValue; var body = item.body ?? holdable.Body; diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/RepairTool.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/RepairTool.cs index 153e694ef..160efdf9b 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/RepairTool.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/RepairTool.cs @@ -17,8 +17,6 @@ namespace Barotrauma.Items.Components private readonly List fixableEntities; private Vector2 pickedPosition; private float activeTimer; - - private Vector2 debugRayStartPos, debugRayEndPos; [Serialize(0.0f, false)] public float Range { get; set; } @@ -158,7 +156,7 @@ namespace Barotrauma.Items.Components var collisionCategories = Physics.CollisionWall | Physics.CollisionCharacter | Physics.CollisionItem | Physics.CollisionLevel | Physics.CollisionRepair; if (RepairThroughWalls) { - var bodies = Submarine.PickBodies(rayStart, rayEnd, ignoredBodies, collisionCategories, ignoreSensors: false, allowInsideFixture: true); + var bodies = Submarine.PickBodies(rayStart, rayEnd, ignoredBodies, collisionCategories, ignoreSensors: false); foreach (Body body in bodies) { FixBody(user, deltaTime, degreeOfSuccess, body); @@ -166,7 +164,7 @@ namespace Barotrauma.Items.Components } else { - FixBody(user, deltaTime, degreeOfSuccess, Submarine.PickBody(rayStart, rayEnd, ignoredBodies, collisionCategories, ignoreSensors: false, allowInsideFixture: true)); + FixBody(user, deltaTime, degreeOfSuccess, Submarine.PickBody(rayStart, rayEnd, ignoredBodies, collisionCategories, ignoreSensors: false)); } if (ExtinguishAmount > 0.0f && item.CurrentHull != null) @@ -249,7 +247,6 @@ namespace Barotrauma.Items.Components var levelResource = targetItem.GetComponent(); if (levelResource != null && levelResource.IsActive && - levelResource.requiredItems.Any() && levelResource.HasRequiredItems(user, addMessage: false)) { levelResource.DeattachTimer += deltaTime; diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Throwable.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Throwable.cs index b1634a6b7..46c994f66 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Throwable.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Throwable.cs @@ -104,8 +104,8 @@ namespace Barotrauma.Items.Components #if SERVER GameServer.Log(picker.LogName + " threw " + item.Name, ServerLog.MessageType.ItemInteraction); #endif - Character thrower = picker; - item.Drop(thrower, createNetworkEvent: GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer); + + item.Drop(picker, createNetworkEvent: GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer); item.body.ApplyLinearImpulse(throwVector * throwForce * item.body.Mass * 3.0f); ac.GetLimb(LimbType.Head).body.ApplyLinearImpulse(throwVector*10.0f); diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/ItemComponent.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/ItemComponent.cs index 21fd14e74..5807a6e7f 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/ItemComponent.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/ItemComponent.cs @@ -189,7 +189,7 @@ namespace Barotrauma.Items.Components { get { return name; } } - + [Editable, Serialize("", true)] public string Msg { @@ -203,6 +203,12 @@ namespace Barotrauma.Items.Components set; } + public AITarget AITarget + { + get; + private set; + } + public AITarget AITarget { get; @@ -247,7 +253,7 @@ namespace Barotrauma.Items.Components { DebugConsole.ThrowError("Invalid pick key in " + element + "!", e); } - + SerializableProperties = SerializableProperty.DeserializeProperties(this, element); ParseMsg(); diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/ItemContainer.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/ItemContainer.cs index d9abe0511..11a3ea4e0 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/ItemContainer.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/ItemContainer.cs @@ -155,14 +155,13 @@ namespace Barotrauma.Items.Components { if (item.Container != null) { return false; } - if (AutoInteractWithContained && character.SelectedConstruction == null) + if (AutoInteractWithContained) { foreach (Item contained in Inventory.Items) { if (contained == null) continue; if (contained.TryInteract(character)) { - character.FocusedItem = contained; return false; } } @@ -179,7 +178,6 @@ namespace Barotrauma.Items.Components if (contained == null) continue; if (contained.TryInteract(picker)) { - picker.FocusedItem = contained; return true; } } diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Engine.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Engine.cs index 5df72ff59..010f24173 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Engine.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Engine.cs @@ -90,7 +90,7 @@ namespace Barotrauma.Items.Components Force = MathHelper.Lerp(force, (voltage < minVoltage) ? 0.0f : targetForce, 0.1f); if (Math.Abs(Force) > 1.0f) { - Vector2 currForce = new Vector2((force / 10.0f) * maxForce * Math.Min(voltage / minVoltage, 1.0f), 0.0f); + Vector2 currForce = new Vector2((force / 100.0f) * maxForce * Math.Min(voltage / minVoltage, 1.0f), 0.0f); //less effective when in a bad condition currForce *= MathHelper.Lerp(0.5f, 2.0f, item.Condition / item.MaxCondition); diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Reactor.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Reactor.cs index a26222b4b..101439383 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Reactor.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Reactor.cs @@ -49,7 +49,7 @@ namespace Barotrauma.Items.Components private bool shutDown; - const float AIUpdateInterval = 0.2f; + const float AIUpdateInterval = 1.0f; private float aiUpdateTimer; private Character lastUser; @@ -209,10 +209,10 @@ namespace Barotrauma.Items.Components allowedFissionRate = Vector2.Lerp(new Vector2(20, AvailableFuel), new Vector2(10, AvailableFuel), degreeOfSuccess); allowedFissionRate.X = Math.Min(allowedFissionRate.X, allowedFissionRate.Y - 10); - float heatAmount = GetGeneratedHeat(fissionRate); + float heatAmount = fissionRate * (AvailableFuel / 100.0f) * 2.0f; float temperatureDiff = (heatAmount - turbineOutput) - Temperature; Temperature += MathHelper.Clamp(Math.Sign(temperatureDiff) * 10.0f * deltaTime, -Math.Abs(temperatureDiff), Math.Abs(temperatureDiff)); - //if (item.InWater && AvailableFuel < 100.0f) Temperature -= 12.0f * deltaTime; + if (item.InWater && AvailableFuel < 100.0f) Temperature -= 12.0f * deltaTime; FissionRate = MathHelper.Lerp(fissionRate, Math.Min(targetFissionRate, AvailableFuel), deltaTime); TurbineOutput = MathHelper.Lerp(turbineOutput, targetTurbineOutput, deltaTime); @@ -313,48 +313,6 @@ namespace Barotrauma.Items.Components } } - private float GetGeneratedHeat(float fissionRate) - { - return fissionRate * (prevAvailableFuel / 100.0f) * 2.0f; - } - - /// - /// Do we need more fuel to generate enough power to match the current load. - /// - /// How low we allow the output/load ratio to go before loading more fuel. - /// 1.0 = always load more fuel when maximum output is too low, 0.5 = load more if max output is 50% of the load - private bool NeedMoreFuel(float minimumOutputRatio) - { - if (prevAvailableFuel <= 0.0f && load > 0.0f) - { - return true; - } - - //fission rate is clamped to the amount of available fuel - float maxFissionRate = Math.Min(prevAvailableFuel, 100.0f); - float maxTurbineOutput = 100.0f; - - //calculate the maximum output if the fission rate is cranked as high as it goes and turbine output is at max - float theoreticalMaxHeat = GetGeneratedHeat(fissionRate: maxFissionRate); - float temperatureFactor = Math.Min(theoreticalMaxHeat / 50.0f, 1.0f); - float theoreticalMaxOutput = Math.Min(maxTurbineOutput / 100.0f, temperatureFactor) * MaxPowerOutput; - - //maximum output not enough, we need more fuel - return theoreticalMaxOutput < load * minimumOutputRatio; - } - - private bool TooMuchFuel() - { - var containedItems = item.ContainedItems; - if (containedItems != null && containedItems.Count() <= 1) { return false; } - - //get the amount of heat we'd generate if the fission rate was at the low end of the optimal range - float minimumHeat = GetGeneratedHeat(optimalFissionRate.X); - - //if we need a very high turbine output to keep the engine from overheating, there's too much fuel - return minimumHeat > Math.Min(correctTurbineOutput * 1.5f, 90); - } - private void UpdateFailures(float deltaTime) { if (temperature > allowedTemperature.Y) @@ -409,11 +367,6 @@ namespace Barotrauma.Items.Components targetFissionRate = Math.Min(targetFissionRate + speed * 2 * deltaTime, 100.0f); } targetFissionRate = MathHelper.Clamp(targetFissionRate, 0.0f, 100.0f); - - //don't push the target too far from the current fission rate - //otherwise we may "overshoot", cranking the target fission rate all the way up because it takes a while - //for the actual fission rate and temperature to follow - targetFissionRate = MathHelper.Clamp(targetFissionRate, FissionRate - 5, FissionRate + 5); } public override void UpdateBroken(float deltaTime, Camera cam) @@ -488,18 +441,12 @@ namespace Barotrauma.Items.Components } } - if (aiUpdateTimer > 0.0f) - { - aiUpdateTimer -= deltaTime; - return false; - } - - //load more fuel if the current maximum output is only 50% of the current load - if (NeedMoreFuel(minimumOutputRatio: 0.5f)) + //we need more fuel + if (-currPowerConsumption < load * 0.5f && prevAvailableFuel <= 0.0f) { var containFuelObjective = new AIObjectiveContainItem(character, new string[] { "fuelrod", "reactorfuel" }, item.GetComponent()) { - MinContainedAmount = item.ContainedItems.Count(i => i != null && i.Prefab.Identifier == "fuelrod" || i.HasTag("reactorfuel")) + 1, + MinContainedAmount = containedItems.Count(i => i != null && i.Prefab.Identifier == "fuelrod" || i.HasTag("reactorfuel")) + 1, GetItemPriority = (Item fuelItem) => { if (fuelItem.ParentInventory?.Owner is Item) @@ -514,7 +461,6 @@ namespace Barotrauma.Items.Components character?.Speak(TextManager.Get("DialogReactorFuel"), null, 0.0f, "reactorfuel", 30.0f); - aiUpdateTimer = AIUpdateInterval; return false; } else if (TooMuchFuel()) @@ -533,6 +479,12 @@ namespace Barotrauma.Items.Components } } + if (aiUpdateTimer > 0.0f) + { + aiUpdateTimer -= deltaTime; + return false; + } + if (lastUser != character && lastUser != null && lastUser.SelectedConstruction == item) { character.Speak(TextManager.Get("DialogReactorTaken"), null, 0.0f, "reactortaken", 10.0f); @@ -554,7 +506,7 @@ namespace Barotrauma.Items.Components { AutoTemp = false; unsentChanges = true; - UpdateAutoTemp(MathHelper.Lerp(0.5f, 2.0f, degreeOfSuccess), 1.0f); + UpdateAutoTemp(2.0f + degreeOfSuccess * 5.0f, 1.0f); } #if CLIENT onOffSwitch.BarScroll = 0.0f; diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Power/PowerContainer.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Power/PowerContainer.cs index b5d448e79..542f66087 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Power/PowerContainer.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Power/PowerContainer.cs @@ -153,7 +153,7 @@ namespace Barotrauma.Items.Components } continue; } - if (!pt.IsActive || !pt.CanTransfer) { continue; } + if (!pt.IsActive) { continue; } gridLoad += pt.PowerLoad; gridPower -= pt.CurrPowerConsumption; @@ -209,9 +209,9 @@ namespace Barotrauma.Items.Components Charge -= CurrPowerOutput / 3600.0f; } - item.SendSignal(0, ((int)Charge).ToString(), "charge", null); - item.SendSignal(0, ((int)((Charge / capacity) * 100)).ToString(), "charge_%", null); - item.SendSignal(0, ((int)((RechargeSpeed / maxRechargeSpeed) * 100)).ToString(), "charge_rate", null); + item.SendSignal(0, Charge.ToString(), "charge", null); + item.SendSignal(0, ((Charge / capacity) * 100).ToString(), "charge_%", null); + item.SendSignal(0, ((RechargeSpeed / maxRechargeSpeed) * 100).ToString(), "charge_rate", null); foreach (Pair connected in directlyConnected) { diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Power/PowerTransfer.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Power/PowerTransfer.cs index 3bd5416bb..f15e8dc8c 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Power/PowerTransfer.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Power/PowerTransfer.cs @@ -180,8 +180,7 @@ namespace Barotrauma.Items.Components #endif //items in a bad condition are more sensitive to overvoltage - float maxOverVoltage = MathHelper.Lerp(OverloadVoltage * 0.75f, OverloadVoltage, item.Condition / item.MaxCondition); - maxOverVoltage = Math.Max(OverloadVoltage, 1.0f); + float maxOverVoltage = MathHelper.Lerp(Math.Min(OverloadVoltage, 1.0f), OverloadVoltage, item.Condition / item.MaxCondition); //if the item can't be fixed, don't allow it to break if (!item.Repairables.Any() || !CanBeOverloaded) continue; @@ -320,13 +319,7 @@ namespace Barotrauma.Items.Components ApplyStatusEffects(ActionType.OnActive, deltaTime, null); - //float maxPower = this is RelayComponent relayComponent ? relayComponent.MaxPower : float.PositiveInfinity; - RelayComponent thisRelayComponent = this as RelayComponent; - if (thisRelayComponent != null) - { - clampPower = Math.Min(Math.Min(clampPower, thisRelayComponent.MaxPower), powerLoad); - clampLoad = Math.Min(clampLoad, thisRelayComponent.MaxPower); - } + float maxPower = this is RelayComponent relayComponent ? relayComponent.MaxPower : float.PositiveInfinity; foreach (Connection c in PowerConnections) { @@ -364,8 +357,6 @@ namespace Barotrauma.Items.Components continue; } - float addLoad = 0.0f; - float addPower = 0.0f; if (powered is PowerContainer powerContainer) { if (recipient.Name == "power_in") @@ -374,7 +365,7 @@ namespace Barotrauma.Items.Components } else { - addPower = powerContainer.CurrPowerOutput; + fullPower += Math.Min(powerContainer.CurrPowerOutput, maxPower); } } else @@ -389,16 +380,10 @@ namespace Barotrauma.Items.Components //negative power consumption = the construction is a //generator/battery or another junction box { - addPower -= powered.CurrPowerConsumption; + fullPower -= Math.Max(powered.CurrPowerConsumption, -maxPower); } } - - if (addPower + fullPower > clampPower) { addPower -= (addPower + fullPower) - clampPower; }; - if (addPower > 0) { fullPower += addPower; } - - if (addLoad + fullLoad > clampLoad) { addLoad -= (addLoad + fullLoad) - clampLoad; }; - if (addLoad > 0) { fullLoad += addLoad; } - } + } } } } diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Repairable.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Repairable.cs index 8d3baec86..3f33c4df2 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Repairable.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Repairable.cs @@ -12,6 +12,8 @@ namespace Barotrauma.Items.Components public static float SkillIncreaseMultiplier = 0.4f; private string header; + + private float fixDurationLowSkill, fixDurationHighSkill; private float deteriorationTimer; @@ -50,20 +52,17 @@ namespace Barotrauma.Items.Components set; } - [Serialize(100.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, ToolTip = "The amount of time it takes to fix the item with insufficient skill levels.")] - public float FixDurationLowSkill + /*private float repairProgress; + public float RepairProgress { - get; - set; - } - - [Serialize(10.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, ToolTip = "The amount of time it takes to fix the item with sufficient skill levels.")] - public float FixDurationHighSkill - { - get; - set; - } - + get { return repairProgress; } + set + { + repairProgress = MathHelper.Clamp(value, 0.0f, 1.0f); + if (repairProgress >= 1.0f && currentFixer != null) currentFixer.AnimController.Anim = AnimController.Animation.None; + } + }*/ + private Character currentFixer; public Character CurrentFixer { @@ -84,6 +83,8 @@ namespace Barotrauma.Items.Components this.item = item; header = element.GetAttributeString("name", ""); + fixDurationLowSkill = element.GetAttributeFloat("fixdurationlowskill", 100.0f); + fixDurationHighSkill = element.GetAttributeFloat("fixdurationhighskill", 5.0f); InitProjSpecific(element); } @@ -159,7 +160,7 @@ namespace Barotrauma.Items.Components } bool wasBroken = !item.IsFullCondition; - float fixDuration = MathHelper.Lerp(FixDurationLowSkill, FixDurationHighSkill, successFactor); + float fixDuration = MathHelper.Lerp(fixDurationLowSkill, fixDurationHighSkill, successFactor); if (fixDuration <= 0.0f) { item.Condition = item.MaxCondition; @@ -185,5 +186,26 @@ namespace Barotrauma.Items.Components { character.AnimController.UpdateUseItem(false, item.WorldPosition + new Vector2(0.0f, 100.0f) * ((item.Condition / item.MaxCondition) % 0.1f)); } + + public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null) + { + msg.Write(deteriorationTimer); + } + + public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime) + { + deteriorationTimer = msg.ReadSingle(); + } + + public void ClientWrite(NetBuffer msg, object[] extraData = null) + { + //no need to write anything, just letting the server know we started repairing + } + + public void ServerRead(ClientNetObject type, NetBuffer msg, Client c) + { + if (c.Character == null) return; + StartRepairing(c.Character); + } } } diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/Connection.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/Connection.cs index 27335ef0c..49a620e56 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/Connection.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/Connection.cs @@ -163,14 +163,6 @@ namespace Barotrauma.Items.Components { wires[index] = wire; recipientsDirty = true; - if (wire != null) - { - var otherConnection = wire.OtherConnection(this); - if (otherConnection != null) - { - otherConnection.recipientsDirty = true; - } - } } public void SendSignal(int stepsTaken, string signal, Item source, Character sender, float power, float signalStrength = 1.0f) diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Turret.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Turret.cs index 91255ae23..3dfbf16d9 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Turret.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Turret.cs @@ -356,7 +356,7 @@ namespace Barotrauma.Items.Components projectile.body.ResetDynamics(); projectile.body.Enabled = true; projectile.SetTransform(ConvertUnits.ToSimUnits(new Vector2(item.WorldRect.X + transformedBarrelPos.X, item.WorldRect.Y - transformedBarrelPos.Y)), -rotation); - projectile.UpdateTransform(); + projectile.FindHull(); projectile.Submarine = projectile.body.Submarine; Projectile projectileComponent = projectile.GetComponent(); diff --git a/Barotrauma/BarotraumaShared/Source/Items/Item.cs b/Barotrauma/BarotraumaShared/Source/Items/Item.cs index 12141e208..191e78a43 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Item.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Item.cs @@ -59,7 +59,7 @@ namespace Barotrauma public PhysicsBody body; public readonly XElement StaticBodyConfig; - + private float lastSentCondition; private float sendConditionUpdateTimer; private bool conditionUpdatePending; @@ -211,14 +211,14 @@ namespace Barotrauma set { spriteColor = value; } } - [Serialize("1.0,1.0,1.0,1.0", true), Editable] + [Serialize("1.0,1.0,1.0,1.0", false), Editable] public Color InventoryIconColor { get; protected set; } - [Serialize("1.0,1.0,1.0,1.0", true), Editable(ToolTip = "Changes the color of the item this item is contained inside. Only has an effect if either of the UseContainedSpriteColor or UseContainedInventoryIconColor property of the container is set to true.")] + [Serialize("1.0,1.0,1.0,1.0", false), Editable(ToolTip = "Changes the color of the item this item is contained inside. Only has an effect if either of the UseContainedSpriteColor or UseContainedInventoryIconColor property of the container is set to true.")] public Color ContainerColor { get; @@ -274,11 +274,12 @@ namespace Barotrauma SetActiveSprite(); - if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer && !MathUtils.NearlyEqual(lastSentCondition, condition)) + if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer && lastSentCondition != condition) { if (Math.Abs(lastSentCondition - condition) > 1.0f || condition == 0.0f || condition == Prefab.Health) { - conditionUpdatePending = true; + GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status }); + lastSentCondition = condition; } } } @@ -995,21 +996,6 @@ namespace Barotrauma aiTarget.SightRange -= deltaTime * 1000.0f; aiTarget.SoundRange -= deltaTime * 1000.0f; } - - if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer) - { - sendConditionUpdateTimer -= deltaTime; - if (conditionUpdatePending) - { - if (sendConditionUpdateTimer <= 0.0f) - { - GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status }); - lastSentCondition = condition; - sendConditionUpdateTimer = NetConfig.ItemConditionUpdateInterval; - conditionUpdatePending = false; - } - } - } ApplyStatusEffects(ActionType.Always, deltaTime, null); diff --git a/Barotrauma/BarotraumaShared/Source/Items/ItemPrefab.cs b/Barotrauma/BarotraumaShared/Source/Items/ItemPrefab.cs index 81355ef74..105a87746 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/ItemPrefab.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/ItemPrefab.cs @@ -503,6 +503,23 @@ namespace Barotrauma break; } } + + if (!category.HasFlag(MapEntityCategory.Legacy) && string.IsNullOrEmpty(identifier)) + { + DebugConsole.ThrowError( + "Item prefab \"" + name + "\" has no identifier. All item prefabs have a unique identifier string that's used to differentiate between items during saving and loading."); + } + if (!string.IsNullOrEmpty(identifier)) + { + MapEntityPrefab existingPrefab = List.Find(e => e.Identifier == identifier); + if (existingPrefab != null) + { + DebugConsole.ThrowError( + "Map entity prefabs \"" + name + "\" and \"" + existingPrefab.Name + "\" have the same identifier!"); + } + } + + AllowedLinks = element.GetAttributeStringArray("allowedlinks", new string[0], convertToLowerInvariant: true).ToList(); if (sprite == null) { diff --git a/Barotrauma/BarotraumaShared/Source/Map/Explosion.cs b/Barotrauma/BarotraumaShared/Source/Map/Explosion.cs index 13c6965e9..c94dffa0f 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Explosion.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Explosion.cs @@ -28,10 +28,8 @@ namespace Barotrauma public Explosion(float range, float force, float damage, float structureDamage, float empStrength = 0.0f) { - attack = new Attack(damage, 0.0f, 0.0f, structureDamage, range) - { - SeverLimbsProbability = 1.0f - }; + attack = new Attack(damage, 0.0f, 0.0f, structureDamage, range); + attack.SeverLimbsProbability = 1.0f; this.force = force; this.empStrength = empStrength; sparks = true; @@ -185,6 +183,9 @@ namespace Barotrauma Hull hull = Hull.FindHull(ConvertUnits.ToDisplayUnits(explosionPos), null, false); bool underWater = hull == null || explosionPos.Y < hull.Surface; + Hull hull = Hull.FindHull(ConvertUnits.ToDisplayUnits(explosionPos), null, false); + bool underWater = hull == null || explosionPos.Y < hull.Surface; + explosionPos = ConvertUnits.ToSimUnits(explosionPos); Dictionary distFactors = new Dictionary(); diff --git a/Barotrauma/BarotraumaShared/Source/Map/FireSource.cs b/Barotrauma/BarotraumaShared/Source/Map/FireSource.cs index c252c399a..83ea002c0 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/FireSource.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/FireSource.cs @@ -175,11 +175,12 @@ namespace Barotrauma LimitSize(); UpdateProjSpecific(growModifier); - - if (size.X < 1.0f && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)) - { - Remove(); - } + +#if CLIENT + if (GameMain.Client != null) return; +#endif + + if (size.X < 1.0f) Remove(); } partial void UpdateProjSpecific(float growModifier); @@ -292,6 +293,10 @@ namespace Barotrauma //evaporate some of the water hull.WaterVolume -= extinguishAmount; +#if CLIENT + if (GameMain.Client != null) return; +#endif + if (size.X < 1.0f && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)) { Remove(); @@ -320,11 +325,12 @@ namespace Barotrauma size.X -= extinguishAmount; hull.WaterVolume -= extinguishAmount; - - if (size.X < 1.0f && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)) - { - Remove(); - } + +#if CLIENT + if (GameMain.Client != null) return; +#endif + + if (size.X < 1.0f) Remove(); } public void Extinguish(float deltaTime, float amount, Vector2 worldPosition) diff --git a/Barotrauma/BarotraumaShared/Source/Map/Hull.cs b/Barotrauma/BarotraumaShared/Source/Map/Hull.cs index 883c85f65..485a7b2bc 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Hull.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Hull.cs @@ -88,7 +88,6 @@ namespace Barotrauma Gap.UpdateHulls(); } - OxygenPercentage = prevOxygenPercentage; surface = drawSurface = rect.Y - rect.Height + WaterVolume / rect.Width; Pressure = surface; } diff --git a/Barotrauma/BarotraumaShared/Source/Map/Levels/Level.cs b/Barotrauma/BarotraumaShared/Source/Map/Levels/Level.cs index 5bace8352..8ffc5630e 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Levels/Level.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Levels/Level.cs @@ -231,7 +231,7 @@ namespace Barotrauma public static Level CreateRandom(LocationConnection locationConnection) { - string seed = locationConnection.Locations[0].BaseName + locationConnection.Locations[1].BaseName; + string seed = locationConnection.Locations[0].Name + locationConnection.Locations[1].Name; float sizeFactor = MathUtils.InverseLerp( MapGenerationParams.Instance.SmallLevelConnectionLength, @@ -1522,49 +1522,21 @@ namespace Barotrauma outpost.MakeOutpost(); Point? minSize = null; - DockingPort subPort = null; if (Submarine.MainSub != null) { Point subSize = Submarine.MainSub.GetDockedBorders().Size; Point outpostSize = outpost.GetDockedBorders().Size; minSize = new Point(Math.Max(subSize.X, outpostSize.X), subSize.Y + outpostSize.Y); - - float closestDistance = float.MaxValue; - foreach (DockingPort port in DockingPort.List) - { - if (port.IsHorizontal || port.Docked) { continue; } - if (port.Item.Submarine != Submarine.MainSub) { continue; } - //the submarine port has to be at the top of the sub - if (port.Item.WorldPosition.Y < Submarine.MainSub.WorldPosition.Y) { continue; } - float dist = Math.Abs(port.Item.WorldPosition.X - Submarine.MainSub.WorldPosition.X); - if (dist < closestDistance) - { - subPort = port; - closestDistance = dist; - } - } } - float subDockingPortOffset = subPort == null ? 0.0f : subPort.Item.WorldPosition.X - Submarine.MainSub.WorldPosition.X; - //don't try to compensate if the port is very far from the sub's center of mass - if (Math.Abs(subDockingPortOffset) > 2000.0f) - { - subDockingPortOffset = MathHelper.Clamp(subDockingPortOffset, -2000.0f, 2000.0f); - string warningMsg = "Docking port very far from the sub's center of mass (submarine: " + Submarine.MainSub.Name + ", dist: " + subDockingPortOffset + "). The level generator may not be able to place the outpost so that docking is possible."; - DebugConsole.NewMessage(warningMsg, Color.Orange); - GameAnalyticsManager.AddErrorEventOnce("Lever.CreateOutposts:DockingPortVeryFar" + Submarine.MainSub.Name, GameAnalyticsSDK.Net.EGAErrorSeverity.Warning, warningMsg); - } - - outpost.SetPosition(outpost.FindSpawnPos(i == 0 ? StartPosition : EndPosition, minSize, subDockingPortOffset)); + outpost.SetPosition(outpost.FindSpawnPos(i == 0 ? StartPosition : EndPosition, minSize)); if ((i == 0) == !Mirrored) { StartOutpost = outpost; - if (GameMain.GameSession?.StartLocation != null) { outpost.Name = GameMain.GameSession.StartLocation.Name; } } else { EndOutpost = outpost; - if (GameMain.GameSession?.EndLocation != null) { outpost.Name = GameMain.GameSession.EndLocation.Name; } } } } diff --git a/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelObject.cs b/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelObject.cs index 3874415b6..d96da098c 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelObject.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelObject.cs @@ -15,6 +15,7 @@ namespace Barotrauma public Vector3 Position; public float NetworkUpdateTimer; + public const float NetworkUpdateInterval = 0.2f; public float Scale; diff --git a/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelObjectManager.cs b/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelObjectManager.cs index b75965a40..40469cea9 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelObjectManager.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelObjectManager.cs @@ -343,7 +343,7 @@ namespace Barotrauma { GameMain.NetworkMember.CreateEntityEvent(this, new object[] { obj }); obj.NeedsNetworkSyncing = false; - obj.NetworkUpdateTimer = NetConfig.LevelObjectUpdateInterval; + obj.NetworkUpdateTimer = LevelObject.NetworkUpdateInterval; } } diff --git a/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelTrigger.cs b/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelTrigger.cs index 1c3ec2339..83033e604 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelTrigger.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelTrigger.cs @@ -432,16 +432,12 @@ namespace Barotrauma { if (ForceFluctuationStrength > 0.0f) { - //no need for force fluctuation (or network updates) if the trigger limits velocity and there are no triggerers - if (forceMode != TriggerForceMode.LimitVelocity || triggerers.Any()) + forceFluctuationTimer += deltaTime; + if (forceFluctuationTimer > ForceFluctuationInterval) { - forceFluctuationTimer += deltaTime; - if (forceFluctuationTimer > ForceFluctuationInterval) - { - NeedsNetworkSyncing = true; - currentForceFluctuation = Rand.Range(1.0f - ForceFluctuationStrength, 1.0f); - forceFluctuationTimer = 0.0f; - } + NeedsNetworkSyncing = true; + currentForceFluctuation = Rand.Range(1.0f - ForceFluctuationStrength, 1.0f); + forceFluctuationTimer = 0.0f; } } diff --git a/Barotrauma/BarotraumaShared/Source/Map/Map/Location.cs b/Barotrauma/BarotraumaShared/Source/Map/Map/Location.cs index 04a108a94..ff9172eb8 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Map/Location.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Map/Location.cs @@ -16,8 +16,6 @@ namespace Barotrauma public int TypeChangeTimer; - public string BaseName { get => baseName; } - public string Name { get; private set; } public Vector2 MapPosition { get; private set; } @@ -34,10 +32,10 @@ namespace Barotrauma get { CheckMissionCompleted(); - + for (int i = availableMissions.Count; i < Connections.Count * 2; i++) { - int seed = (ToolBox.StringToInt(BaseName) + MissionsCompleted * 10 + i) % int.MaxValue; + int seed = (ToolBox.StringToInt(Name) + MissionsCompleted * 10 + i) % int.MaxValue; MTRandom rand = new MTRandom(seed); LocationConnection connection = Connections[(MissionsCompleted + i) % Connections.Count]; @@ -48,7 +46,7 @@ namespace Barotrauma if (availableMissions.Any(m => m.Prefab == mission.Prefab)) { continue; } if (GameSettings.VerboseLogging && mission != null) { - DebugConsole.NewMessage("Generated a new mission for a location (location: " + Name + ", seed: " + seed.ToString("X") + ", missions completed: " + MissionsCompleted + ", type: " + mission.Name + ")", Color.White); + DebugConsole.NewMessage("Generated a new mission for a location connection (seed: " + seed.ToString("X") + ", type: " + mission.Name + ")", Color.White); } availableMissions.Add(mission); } @@ -77,10 +75,10 @@ namespace Barotrauma } } - public Location(Vector2 mapPosition, int? zone, Random rand) + public Location(Vector2 mapPosition, int? zone) { - this.Type = LocationType.Random(rand, zone); - this.Name = RandomName(Type, rand); + this.Type = LocationType.Random("", zone); + this.Name = RandomName(Type); this.MapPosition = mapPosition; PortraitId = ToolBox.StringToInt(Name); @@ -88,9 +86,9 @@ namespace Barotrauma Connections = new List(); } - public static Location CreateRandom(Vector2 position, int? zone , Random rand) + public static Location CreateRandom(Vector2 position, int? zone) { - return new Location(position, zone, rand); + return new Location(position, zone); } public IEnumerable GetMissionsInConnection(LocationConnection connection) @@ -101,16 +99,7 @@ namespace Barotrauma public void ChangeType(LocationType newType) { - if (newType == Type) { return; } - - //clear missions from this and adjacent locations (they may be invalid now) - availableMissions.Clear(); - foreach (LocationConnection connection in Connections) - { - connection.OtherLocation(this)?.availableMissions.Clear(); - } - - DebugConsole.Log("Location " + baseName + " changed it's type from " + Type + " to " + newType); + if (newType == Type) return; Type = newType; Name = Type.NameFormats[nameFormatIndex % Type.NameFormats.Count].Replace("[name]", baseName); @@ -122,7 +111,6 @@ namespace Barotrauma { if (mission.Completed) { - DebugConsole.Log("Mission \"" + mission.Name + "\" completed in \"" + Name + "\"."); MissionsCompleted++; } } @@ -130,10 +118,10 @@ namespace Barotrauma availableMissions.RemoveAll(m => m.Completed); } - private string RandomName(LocationType type, Random rand) + private string RandomName(LocationType type) { - baseName = type.GetRandomName(rand); - nameFormatIndex = rand.Next() % type.NameFormats.Count; + baseName = type.GetRandomName(); + nameFormatIndex = Rand.Int(type.NameFormats.Count, Rand.RandSync.Server); return type.NameFormats[nameFormatIndex].Replace("[name]", baseName); } diff --git a/Barotrauma/BarotraumaShared/Source/Map/Map/LocationType.cs b/Barotrauma/BarotraumaShared/Source/Map/Map/LocationType.cs index aecfd8149..09483cc25 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Map/LocationType.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Map/LocationType.cs @@ -155,15 +155,20 @@ namespace Barotrauma return portraits[Math.Abs(portraitId) % portraits.Count]; } + public string GetRandomName() + { + return names[Rand.Int(names.Count, Rand.RandSync.Server)]; + } + + public static LocationType Random(string seed = "", int? zone = null) + { + Debug.Assert(List.Count > 0, "LocationType.list.Count == 0, you probably need to initialize LocationTypes"); + public string GetRandomName(Random rand) { return names[rand.Next() % names.Count]; } - public static LocationType Random(Random rand, int? zone = null) - { - Debug.Assert(List.Count > 0, "LocationType.list.Count == 0, you probably need to initialize LocationTypes"); - List allowedLocationTypes = zone.HasValue ? List.FindAll(lt => lt.CommonnessPerZone.ContainsKey(zone.Value)) : List; if (allowedLocationTypes.Count == 0) @@ -175,12 +180,12 @@ namespace Barotrauma { return ToolBox.SelectWeightedRandom( allowedLocationTypes, - allowedLocationTypes.Select(a => a.CommonnessPerZone[zone.Value]).ToList(), - rand); + allowedLocationTypes.Select(a => a.CommonnessPerZone[zone.Value]).ToList(), + Rand.RandSync.Server); } else { - return allowedLocationTypes[rand.Next() % allowedLocationTypes.Count]; + return allowedLocationTypes[Rand.Int(allowedLocationTypes.Count, Rand.RandSync.Server)]; } } diff --git a/Barotrauma/BarotraumaShared/Source/Map/Map/Map.cs b/Barotrauma/BarotraumaShared/Source/Map/Map/Map.cs index eedceada2..ba0ba04d5 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Map/Map.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Map/Map.cs @@ -196,7 +196,7 @@ namespace Barotrauma Vector2 position = points[positionIndex]; if (newLocations[1 - i] != null && newLocations[1 - i].MapPosition == position) position = points[1 - positionIndex]; int zone = MathHelper.Clamp(generationParams.DifficultyZones - (int)Math.Floor(Vector2.Distance(position, mapCenter) / zoneRadius), 1, generationParams.DifficultyZones); - newLocations[i] = Location.CreateRandom(position, zone, Rand.GetRNG(Rand.RandSync.Server)); + newLocations[i] = Location.CreateRandom(position, zone); Locations.Add(newLocations[i]); } @@ -578,12 +578,10 @@ namespace Barotrauma location.MissionsCompleted = missionsCompleted; if (showNotifications && prevLocationType != location.Type) { - var change = prevLocationType.CanChangeTo.Find(c => - c.ChangeToType.ToLowerInvariant() == location.Type.Identifier.ToLowerInvariant()); - if (change != null) - { - ChangeLocationType(location, prevLocationName, change); - } + ChangeLocationType( + location, + prevLocationName, + prevLocationType.CanChangeTo.Find(c => c.ChangeToType.ToLowerInvariant() == location.Type.Identifier.ToLowerInvariant())); } break; case "connection": diff --git a/Barotrauma/BarotraumaShared/Source/Map/Structure.cs b/Barotrauma/BarotraumaShared/Source/Map/Structure.cs index 82d0dad3e..6e4fe089b 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Structure.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Structure.cs @@ -297,13 +297,7 @@ namespace Barotrauma CreateStairBodies(); } } - - // Only add ai targets automatically to walls - if (aiTarget == null && HasBody && Tags.Contains("wall")) - { - aiTarget = new AITarget(this); - } - + InsertToList(); } diff --git a/Barotrauma/BarotraumaShared/Source/Map/StructurePrefab.cs b/Barotrauma/BarotraumaShared/Source/Map/StructurePrefab.cs index 037364597..a4077554a 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/StructurePrefab.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/StructurePrefab.cs @@ -217,10 +217,6 @@ namespace Barotrauma } SerializableProperty.DeserializeProperties(sp, element); - if (sp.Body) - { - sp.Tags.Add("wall"); - } string translatedDescription = TextManager.Get("EntityDescription." + sp.identifier, true); if (!string.IsNullOrEmpty(translatedDescription)) sp.Description = translatedDescription; diff --git a/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs b/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs index 738c9fa16..e78224c15 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs @@ -10,7 +10,6 @@ using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Linq; -using System.Threading; using System.Xml.Linq; using Voronoi2; @@ -494,7 +493,7 @@ namespace Barotrauma } } - public Vector2 FindSpawnPos(Vector2 spawnPos, Point? submarineSize = null, float subDockingPortOffset = 0.0f) + public Vector2 FindSpawnPos(Vector2 spawnPos, Point? submarineSize = null) { Rectangle dockedBorders = GetDockedBorders(); Vector2 diffFromDockedBorders = @@ -542,17 +541,17 @@ namespace Barotrauma else if (minX < 0) { //no wall found at the left side, spawn to the left from the right-side wall - spawnPos.X = maxX - minWidth - 100.0f + subDockingPortOffset; + spawnPos.X = maxX - minWidth - 100.0f; } else if (maxX > Level.Loaded.Size.X) { //no wall found at right side, spawn to the right from the left-side wall - spawnPos.X = minX + minWidth + 100.0f + subDockingPortOffset; + spawnPos.X = minX + minWidth + 100.0f; } else { //walls found at both sides, use their midpoint - spawnPos.X = (minX + maxX) / 2 + subDockingPortOffset; + spawnPos.X = (minX + maxX) / 2; } spawnPos.Y = Math.Min(spawnPos.Y, Level.Loaded.Size.Y - dockedBorders.Height / 2 - 10); @@ -666,7 +665,7 @@ namespace Barotrauma } } - public static Body PickBody(Vector2 rayStart, Vector2 rayEnd, IEnumerable ignoredBodies = null, Category? collisionCategory = null, bool ignoreSensors = true, Predicate customPredicate = null, bool allowInsideFixture = false) + public static Body PickBody(Vector2 rayStart, Vector2 rayEnd, IEnumerable ignoredBodies = null, Category? collisionCategory = null, bool ignoreSensors = true, Predicate customPredicate = null) { if (Vector2.DistanceSquared(rayStart, rayEnd) < 0.00001f) { @@ -678,20 +677,21 @@ namespace Barotrauma Body closestBody = null; if (allowInsideFixture) { - var aabb = new FarseerPhysics.Collision.AABB(rayStart - Vector2.One * 0.001f, rayStart + Vector2.One * 0.001f); - GameMain.World.QueryAABB((fixture) => - { - if (!CheckFixtureCollision(fixture, ignoredBodies, collisionCategory, ignoreSensors, customPredicate)) { return true; } + if (fixture == null || + (ignoreSensors && fixture.IsSensor) || + fixture.CollisionCategories == Category.None || + fixture.CollisionCategories == Physics.CollisionItem) return -1; + + if (customPredicate != null && !customPredicate(fixture)) return -1; + + if (collisionCategory != null && + !fixture.CollisionCategories.HasFlag((Category)collisionCategory) && + !((Category)collisionCategory).HasFlag(fixture.CollisionCategories)) return -1; fixture.Body.GetTransform(out FarseerPhysics.Common.Transform transform); if (!fixture.Shape.TestPoint(ref transform, ref rayStart)) { return true; } - closestFraction = 0.0f; - closestNormal = Vector2.Normalize(rayEnd - rayStart); - if (fixture.Body != null) closestBody = fixture.Body; - return false; - }, ref aabb); - if (closestFraction <= 0.0f) + if (fixture.Body.UserData is Structure structure) { lastPickedPosition = rayStart; lastPickedFraction = closestFraction; @@ -721,7 +721,7 @@ namespace Barotrauma return closestBody; } - public static List PickBodies(Vector2 rayStart, Vector2 rayEnd, IEnumerable ignoredBodies = null, Category? collisionCategory = null, bool ignoreSensors = true, Predicate customPredicate = null, bool allowInsideFixture = false) + public static List PickBodies(Vector2 rayStart, Vector2 rayEnd, List ignoredBodies = null, Category? collisionCategory = null, bool ignoreSensors = true, Predicate customPredicate = null) { if (Vector2.DistanceSquared(rayStart, rayEnd) < 0.00001f) { @@ -732,9 +732,25 @@ namespace Barotrauma List bodies = new List(); GameMain.World.RayCast((fixture, point, normal, fraction) => { - if (!CheckFixtureCollision(fixture, ignoredBodies, collisionCategory, ignoreSensors, customPredicate)) { return -1; } + if (fixture == null || + (ignoreSensors && fixture.IsSensor) || + fixture.CollisionCategories == Category.None || + fixture.CollisionCategories == Physics.CollisionItem) return -1; - if (fixture.Body != null) { bodies.Add(fixture.Body); } + if (customPredicate != null && !customPredicate(fixture)) return -1; + + if (collisionCategory != null && + !fixture.CollisionCategories.HasFlag((Category)collisionCategory) && + !((Category)collisionCategory).HasFlag(fixture.CollisionCategories)) return -1; + + if (ignoredBodies != null && ignoredBodies.Contains(fixture.Body)) return -1; + + if (fixture.Body.UserData is Structure structure) + { + if (structure.IsPlatform && collisionCategory != null && !((Category)collisionCategory).HasFlag(Physics.CollisionPlatform)) return -1; + } + + bodies.Add(fixture.Body); if (fraction < closestFraction) { lastPickedPosition = rayStart + (rayEnd - rayStart) * fraction; @@ -744,68 +760,10 @@ namespace Barotrauma return fraction; }, rayStart, rayEnd); - - if (allowInsideFixture) - { - var aabb = new FarseerPhysics.Collision.AABB(rayStart - Vector2.One * 0.001f, rayStart + Vector2.One * 0.001f); - GameMain.World.QueryAABB((fixture) => - { - if (bodies.Contains(fixture.Body) || fixture.Body == null) { return true; } - if (!CheckFixtureCollision(fixture, ignoredBodies, collisionCategory, ignoreSensors, customPredicate)) { return true; } - - fixture.Body.GetTransform(out FarseerPhysics.Common.Transform transform); - if (!fixture.Shape.TestPoint(ref transform, ref rayStart)) { return true; } - - closestFraction = 0.0f; - lastPickedPosition = rayStart; - lastPickedFraction = 0.0f; - lastPickedNormal = Vector2.Normalize(rayEnd - rayStart); - bodies.Add(fixture.Body); - return false; - }, ref aabb); - } - + return bodies; } - private static bool CheckFixtureCollision(Fixture fixture, IEnumerable ignoredBodies = null, Category? collisionCategory = null, bool ignoreSensors = true, Predicate customPredicate = null) - { - if (fixture == null || - (ignoreSensors && fixture.IsSensor) || - fixture.CollisionCategories == Category.None || - fixture.CollisionCategories == Physics.CollisionItem) - { - return false; - } - - if (customPredicate != null && !customPredicate(fixture)) - { - return false; - } - - if (collisionCategory != null && - !fixture.CollisionCategories.HasFlag((Category)collisionCategory) && - !((Category)collisionCategory).HasFlag(fixture.CollisionCategories)) - { - return false; - } - - if (ignoredBodies != null && ignoredBodies.Contains(fixture.Body)) - { - return false; - } - - if (fixture.Body.UserData is Structure structure) - { - if (structure.IsPlatform && collisionCategory != null && !((Category)collisionCategory).HasFlag(Physics.CollisionPlatform)) - { - return false; - } - } - - return true; - } - /// /// check visibility between two points (in sim units) /// diff --git a/Barotrauma/BarotraumaShared/Source/Map/WayPoint.cs b/Barotrauma/BarotraumaShared/Source/Map/WayPoint.cs index f932497b4..bb1a5556f 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/WayPoint.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/WayPoint.cs @@ -615,7 +615,7 @@ namespace Barotrauma ID = (ushort)int.Parse(element.Attribute("ID").Value) }; - w.spawnType = spawnType; + Enum.TryParse(element.GetAttributeString("spawn", "Path"), out w.spawnType); string idCardDescString = element.GetAttributeString("idcarddesc", ""); if (!string.IsNullOrWhiteSpace(idCardDescString)) diff --git a/Barotrauma/BarotraumaShared/Source/Networking/FileTransfer/FileTransfer.cs b/Barotrauma/BarotraumaShared/Source/Networking/FileTransfer/FileTransfer.cs index a5d48f5eb..51a489698 100644 --- a/Barotrauma/BarotraumaShared/Source/Networking/FileTransfer/FileTransfer.cs +++ b/Barotrauma/BarotraumaShared/Source/Networking/FileTransfer/FileTransfer.cs @@ -7,7 +7,7 @@ enum FileTransferMessageType { - Unknown, Initiate, Data, TransferOnSameMachine, Cancel + Unknown, Initiate, Data, Cancel } enum FileTransferType diff --git a/Barotrauma/BarotraumaShared/Source/Networking/NetConfig.cs b/Barotrauma/BarotraumaShared/Source/Networking/NetConfig.cs index 74b6afcb2..d7a36e28c 100644 --- a/Barotrauma/BarotraumaShared/Source/Networking/NetConfig.cs +++ b/Barotrauma/BarotraumaShared/Source/Networking/NetConfig.cs @@ -32,14 +32,11 @@ namespace Barotrauma.Networking public const float HighPrioCharacterPositionUpdateInterval = 0.0f; public const float LowPrioCharacterPositionUpdateInterval = 1.0f; - public const float DeleteDisconnectedTime = 20.0f; - - public const float ItemConditionUpdateInterval = 0.15f; - public const float LevelObjectUpdateInterval = 0.5f; - public const float HullUpdateInterval = 0.5f; - public const float HullUpdateDistance = 20000.0f; - - public const int MaxEventPacketsPerUpdate = 4; + //how much the physics body of an item has to move until the server + //send a position update to clients (in sim units) + public const float ItemPosUpdateDistance = 2.0f; + + public const float DeleteDisconnectedTime = 10.0f; /// /// Interpolates the positional error of a physics body towards zero. diff --git a/Barotrauma/BarotraumaShared/Source/Networking/NetEntityEvent/NetEntityEventManager.cs b/Barotrauma/BarotraumaShared/Source/Networking/NetEntityEvent/NetEntityEventManager.cs index 20cb9b39a..03e28f3d4 100644 --- a/Barotrauma/BarotraumaShared/Source/Networking/NetEntityEvent/NetEntityEventManager.cs +++ b/Barotrauma/BarotraumaShared/Source/Networking/NetEntityEvent/NetEntityEventManager.cs @@ -58,8 +58,15 @@ namespace Barotrauma.Networking eventCount++; continue; } - - if (msg.LengthBytes + tempBuffer.LengthBytes + tempEventBuffer.LengthBytes > MaxEventBufferLength) + //the ID has been taken by another entity (the original entity has been removed) -> write an empty event + /*else if (Entity.FindEntityByID(e.Entity.ID) != e.Entity || e.Entity.IdFreed) + { + //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 + tempBuffer.Write(Entity.NullEntityID); + tempBuffer.WritePadBits(); + }*/ + else { //no more room in this packet break; diff --git a/Barotrauma/BarotraumaShared/Source/StatusEffects/StatusEffect.cs b/Barotrauma/BarotraumaShared/Source/StatusEffects/StatusEffect.cs index dfe562c88..e03708d6e 100644 --- a/Barotrauma/BarotraumaShared/Source/StatusEffects/StatusEffect.cs +++ b/Barotrauma/BarotraumaShared/Source/StatusEffects/StatusEffect.cs @@ -4,6 +4,11 @@ using System; using System.Collections.Generic; using System.Linq; using System.Xml.Linq; +using Barotrauma.Items.Components; +#if CLIENT +using Barotrauma.Particles; +using Barotrauma.Sounds; +#endif namespace Barotrauma { @@ -86,10 +91,52 @@ namespace Barotrauma } } - private TargetType targetTypes; protected HashSet targetIdentifiers; + public readonly ItemPrefab ItemPrefab; + public readonly SpawnPositionType SpawnPosition; + public readonly float Speed; + public readonly float Rotation; + + public ItemSpawnInfo(XElement element, string parentDebugName) + { + if (element.Attribute("name") != null) + { + //backwards compatibility + DebugConsole.ThrowError("Error in StatusEffect config (" + element.ToString() + ") - use item identifier instead of the name."); + string itemPrefabName = element.GetAttributeString("name", ""); + ItemPrefab = MapEntityPrefab.List.Find(m => m is ItemPrefab && (m.NameMatches(itemPrefabName) || m.Tags.Contains(itemPrefabName))) as ItemPrefab; + if (ItemPrefab == null) + { + DebugConsole.ThrowError("Error in StatusEffect \""+ parentDebugName + "\" - item prefab \"" + itemPrefabName + "\" not found."); + } + } + else + { + string itemPrefabIdentifier = element.GetAttributeString("identifier", ""); + if (string.IsNullOrEmpty(itemPrefabIdentifier)) itemPrefabIdentifier = element.GetAttributeString("identifiers", ""); + if (string.IsNullOrEmpty(itemPrefabIdentifier)) + { + DebugConsole.ThrowError("Invalid item spawn in StatusEffect \"" + parentDebugName + "\" - identifier not found in the element \"" + element.ToString() + "\""); + } + ItemPrefab = MapEntityPrefab.List.Find(m => m is ItemPrefab && m.Identifier == itemPrefabIdentifier) as ItemPrefab; + if (ItemPrefab == null) + { + DebugConsole.ThrowError("Error in StatusEffect config - item prefab with the identifier \"" + itemPrefabIdentifier + "\" not found."); + return; + } + } + + Speed = element.GetAttributeFloat("speed", 0.0f); + Rotation = MathHelper.ToRadians(element.GetAttributeFloat("rotation", 0.0f)); + + private List sounds = new List(); + private SoundSelectionMode soundSelectionMode; + private SoundChannel soundChannel; + private bool loopSound; +#endif + private List requiredItems; public string[] propertyNames; @@ -181,6 +228,10 @@ namespace Barotrauma Range = element.GetAttributeFloat("range", 0.0f); +#if CLIENT + particleEmitters = new List(); +#endif + IEnumerable attributes = element.Attributes(); List propertyAttributes = new List(); propertyConditionals = new List(); @@ -357,6 +408,25 @@ namespace Barotrauma var newSpawnItem = new ItemSpawnInfo(subElement, parentDebugName); if (newSpawnItem.ItemPrefab != null) spawnItems.Add(newSpawnItem); break; +#if CLIENT + case "particleemitter": + particleEmitters.Add(new ParticleEmitter(subElement)); + break; + case "sound": + var sound = Submarine.LoadRoundSound(subElement); + if (sound != null) + { + loopSound = subElement.GetAttributeBool("loop", false); + if (subElement.Attribute("selectionmode") != null) + { + if (Enum.TryParse(subElement.GetAttributeString("selectionmode", "Random"), out SoundSelectionMode selectionMode)) + { + soundSelectionMode = selectionMode; + } + } + sounds.Add(sound); + } + break; } } InitProjSpecific(element, parentDebugName); @@ -369,6 +439,11 @@ namespace Barotrauma return (targetTypes & targetType) != 0; } + public bool HasTargetType(TargetType targetType) + { + return (targetTypes & targetType) != 0; + } + public virtual bool HasRequiredItems(Entity entity) { if (requiredItems == null) return true; @@ -568,10 +643,46 @@ namespace Barotrauma { hull = ((Item)entity).CurrentHull; } +#if CLIENT + if (entity != null && sounds.Count > 0) + { + if (soundChannel == null || !soundChannel.IsPlaying) + { + if (soundSelectionMode == SoundSelectionMode.All) + { + foreach (RoundSound sound in sounds) + { + soundChannel = SoundPlayer.PlaySound(sound.Sound, sound.Volume, sound.Range, entity.WorldPosition, hull); + if (soundChannel != null) soundChannel.Looping = loopSound; + } + } + else + { + int selectedSoundIndex = 0; + if (soundSelectionMode == SoundSelectionMode.ItemSpecific && entity is Item item) + { + selectedSoundIndex = item.ID % sounds.Count; + } + else if (soundSelectionMode == SoundSelectionMode.CharacterSpecific && entity is Character user) + { + selectedSoundIndex = user.ID % sounds.Count; + } + else + { + selectedSoundIndex = Rand.Int(sounds.Count); + } + var selectedSound = sounds[selectedSoundIndex]; + soundChannel = SoundPlayer.PlaySound(selectedSound.Sound, selectedSound.Volume, selectedSound.Range, entity.WorldPosition, hull); + if (soundChannel != null) soundChannel.Looping = loopSound; + } + } + } +#endif foreach (ISerializableEntity serializableEntity in targets) { - if (!(serializableEntity is Item item)) continue; + Item item = serializableEntity as Item; + if (item == null) continue; Character targetCharacter = targets.FirstOrDefault(t => t is Character character && !character.Removed) as Character; if (targetCharacter == null) @@ -671,7 +782,11 @@ namespace Barotrauma fire.Size = new Vector2(FireSize, fire.Size.Y); } - bool isNotClient = GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient; + bool isNotClient = true; +#if CLIENT + isNotClient = GameMain.Client == null; +#endif + if (isNotClient && entity != null && Entity.Spawner != null) //clients are not allowed to spawn items { foreach (ItemSpawnInfo itemSpawnInfo in spawnItems) @@ -728,11 +843,27 @@ namespace Barotrauma } } +#if CLIENT + if (entity != null) + { + foreach (ParticleEmitter emitter in particleEmitters) + { + float angle = 0.0f; + if (emitter.Prefab.CopyEntityAngle) + { + if (entity is Item it) + { + angle = it.body == null ? 0.0f : it.body.Rotation; + } + } + + emitter.Emit(deltaTime, entity.WorldPosition, hull, angle); + } + } + ApplyProjSpecific(deltaTime, entity, targets, hull); } - partial void ApplyProjSpecific(float deltaTime, Entity entity, List targets, Hull currentHull); - private void ApplyToProperty(ISerializableEntity target, SerializableProperty property, object value, float deltaTime) { if (disableDeltaTime || setValue) deltaTime = 1.0f; diff --git a/Barotrauma/BarotraumaShared/Source/Utils/SaveUtil.cs b/Barotrauma/BarotraumaShared/Source/Utils/SaveUtil.cs index 3fbf744cc..5ad818e21 100644 --- a/Barotrauma/BarotraumaShared/Source/Utils/SaveUtil.cs +++ b/Barotrauma/BarotraumaShared/Source/Utils/SaveUtil.cs @@ -403,18 +403,18 @@ namespace Barotrauma } } - public static void ClearFolder(string FolderName, string[] ignoredFileNames = null) + public static void ClearFolder(string FolderName, string[] ignoredFiles = null) { DirectoryInfo dir = new DirectoryInfo(FolderName); foreach (FileInfo fi in dir.GetFiles()) { - if (ignoredFileNames != null) + if (ignoredFiles != null) { bool ignore = false; - foreach (string ignoredFile in ignoredFileNames) + foreach (string ignoredFile in ignoredFiles) { - if (Path.GetFileName(fi.FullName).Equals(Path.GetFileName(ignoredFile))) + if (Path.GetFullPath(fi.FullName).Equals(Path.GetFullPath(ignoredFile))) { ignore = true; break; diff --git a/Barotrauma/BarotraumaShared/Submarines/Bunyip.sub b/Barotrauma/BarotraumaShared/Submarines/Bunyip.sub index 513c14cde..62696becf 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Bunyip.sub and b/Barotrauma/BarotraumaShared/Submarines/Bunyip.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Dugong.sub b/Barotrauma/BarotraumaShared/Submarines/Dugong.sub index 6403c9fd9..d2cd1ff19 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Dugong.sub and b/Barotrauma/BarotraumaShared/Submarines/Dugong.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Humpback.sub b/Barotrauma/BarotraumaShared/Submarines/Humpback.sub index 7b714e62c..aacc41a31 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Humpback.sub and b/Barotrauma/BarotraumaShared/Submarines/Humpback.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Orca.sub b/Barotrauma/BarotraumaShared/Submarines/Orca.sub index 7ac100e81..3d9685698 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Orca.sub and b/Barotrauma/BarotraumaShared/Submarines/Orca.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/PAX.sub b/Barotrauma/BarotraumaShared/Submarines/PAX.sub index 13fefe7ba..58e7cc9a0 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/PAX.sub and b/Barotrauma/BarotraumaShared/Submarines/PAX.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Remora.sub b/Barotrauma/BarotraumaShared/Submarines/Remora.sub index ede107e94..74c05a493 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Remora.sub and b/Barotrauma/BarotraumaShared/Submarines/Remora.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/RemoraDrone.sub b/Barotrauma/BarotraumaShared/Submarines/RemoraDrone.sub index f8378ae05..57f8fc56f 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/RemoraDrone.sub and b/Barotrauma/BarotraumaShared/Submarines/RemoraDrone.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Typhon.sub b/Barotrauma/BarotraumaShared/Submarines/Typhon.sub index 0f4a06fc3..d3c065fe6 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Typhon.sub and b/Barotrauma/BarotraumaShared/Submarines/Typhon.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Venture.sub b/Barotrauma/BarotraumaShared/Submarines/Venture.sub index 2ed054c24..8cb2b6a3d 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Venture.sub and b/Barotrauma/BarotraumaShared/Submarines/Venture.sub differ diff --git a/Barotrauma/BarotraumaShared/changelog.txt b/Barotrauma/BarotraumaShared/changelog.txt index 54b1c3eac..15722f9ce 100644 --- a/Barotrauma/BarotraumaShared/changelog.txt +++ b/Barotrauma/BarotraumaShared/changelog.txt @@ -1,109 +1,3 @@ ---------------------------------------------------------------------------------------------------------- -v0.8.9.8 ---------------------------------------------------------------------------------------------------------- - -Additions and changes: -- Improved tutorial - better videos, instructional texts, objective list that suggest what you should do -next, option to rewatch the videos and re-read the instructions. -- Overhauled charybdis (still a work in progress though). -- Automatically grab adjacent ladders when the top/bottom of the current ladder is reached. Makes moving -through docking ports a little less confusing. -- Option to configure when afflictions become visible with the health scanner by adding -a "ShowInHealthScannerThreshold" attribute to the affliction. -- Added labels next to periscopes in Humpback and Dugong. -- Modified Humpback's bilge to make it easier for AI characters to fix. -- Enemies can now navigate out from the sub when escaping. -- Bunch of tweaks to monster AIs (targeting priorities, attack cooldowns, etc). -- Made InventoryIconColor and ContainerColor properties saveable. -- Removed descriptive name from alien artifacts (so be extra careful when bringing them on board). -- Allow panning the campaign map with WASD. -- Allow editing selected wires in wiring mode (so it's possible to, for example, modify the colors of -the wires without having to disconnect them). -- Decreased structure damage done by frag grenades and made them disappear after they've exploded. -- Batteries output charge values as integers. -- Made damaged junction boxes less sensitive to overvoltage. Nearly broken junction boxes were barely able -to handle any overvoltage, leading to chain reaction where one junction box breaking causes the grid to be -overloaded, and the rest of the boxes start taking damage at an increasing speed. -- Reactors don't cool down when underwater anymore. -- Removed minimum conditions from battery deconstruction output (= deconstructing an empty battery still -gives the materials used to craft the battery). -- Made a bunch of ItemContainer UI panels larger. -- Items can be dragged and dropped directly from the inventory into containers without having to select -the container first. -- Plants can be picked up from the environment without any tools. -- Added more help texts to highlighted items ("[E] Interact", "[E] Climb"...) - -Multiplayer fixes: -- Changes to event syncing logic to prevent cases where clients fall behind the server and get kicked out -due to the server not being able to send out network events to the clients fast enough. -- Networking optimizations that prevent level objects and continuously deteriorating items from creating -excessive amounts of network events. -- Fixed windows clients being unable to start a campaign in servers running on Linux. -- Fixed clients being unable to start a campaign using a submarine that's not in the default Submarine -folder at the server's side. -- Fixed loading submarine files and campaign saves occasionally failing when running multiple instances -of the game from the same install location (for example, a dedicated server executable and a client -executable). -- Don't transfer files through the network when sending them to the owner of the server (i.e. a client -hosting directly from the main executable). -- Fixed fires and water occasionally getting out of sync between a client using the fire/water console -commands and the server. -- Fixed clients disconnecting with an "unknown object header" error if they fail to read a network event -(when they should instead report the error to the server and wait for a message that contains a more -descriptive error). -- Campaign fix: clear missions from locations that change their type, and all adjacent locations. Not -clearing them caused missions to still be available when they logically shouldn't be (e.g. a transport -mission from an uninhabited location to another) and syncing issues in multiplayer. -- Disable campaign start button if a round is already running when joining. -- Fixed clients being unable to end campaign rounds at all if the sub isn't at the start/end outpost -(regardless if they have the permission to end the round or not). -- Fixed campaign characters still being displayed in the server lobby after the game mode has been -changed to something else. -- Fixed items in the characters inventory always starting at 100% condition client-side even if they had -deteriorated during the previous round. -- Fixed LevelResource (mineral, plant, etc) deattach timers not being synced with clients. -- AI characters can take out excess fuel rods from the reactor when needed. -- Item interfaces and the health interface can be closed with Esc. - -Bugfixes: -- Fixed almost all items using default repair duration values (10 seconds with high skills, 100 seconds -with low skills) instead of the ones configured in the item XMLs. -- Nuclear shells and nuclear depth charges disappear after they've exploded. -- Fixed "trying to add a dead character to crewmanager" errors when attempting to revive a character -killed by some other affliction than internal damage, bleeding or burns. -- Take the position of a sub's docking port into account when determining where to place outposts. -Previously the outposts were simply placed midway between the adjacent walls, which occasionally caused -problems with submarines whose docking port is close to the bow or tail. -- Fixed a bug in relay components that caused a bunch of issues in power grids that utilize relays: -Relays would receive the full amount of power from the grid regardless of the load of the devices -connected to the power_out connection, causing unnecessary overloads and fires. -- Fixed batteries being able to draw power through relay components that are connected directly to -a power source, even if the relay isn't on. -- Don't allow steering the sub with WASD when a textbox is selected. -- Use the SpriteColor of the item when drawing the moving parts of turrets and doors. -- Fixed steering issues that caused monsters to swim in an incorrect direction when they're targeting -something inside the sub. -- Fixed a crash due when scaling the Mudraptor. -- Fixed crashing if a character has neither a head angle or a torso angle configured. -- Fixed characters always being created in the default folder in the character editor. -- Monsters don't target doors/hatches at the exterior of the sub when inside or inner doors when outside. -- Don't display disabled limbs on sonar (i.e. severed limbs that have "faded out"). -- Close the save/load dialogs when leaving the sub editor. Otherwise they'll still be visible when -re-entering the editor, and saving at that point will overwrite the previously loaded sub with an empty one. -- Removing an item after it's been combined doesn't trigger the OnBroken StatusEffects (e.g. combining two -half-full flash powder jars doesn't cause them to explode). -- Fixed welding tools and plasma cutters not hitting targets if the barrel is inside the target (e.g. if -trying to weld a completely broken wall with the cutter partially inside the wall). -- Fixed very small mineral colliders that made them extremely hard to hit with the plasma cutter. -- Fixed items with no sprite crashing the game (now they just cause a console error). -- Don't allow autointeracting with contained items (e.g. picking up an ammunition box from a loader) -if another item is currently selected. Makes it less likely for players to accidentally pick up items -from containers when they deselect another item. -- Fixed characters not letting go of the character they're grabbing when the health interface is closed -by clicking outside the window. -- Fixed CrewManager throwing errors if changing resolutions when a crew member is dead. -- Fixed items emitting light from inside containers in the sub editor. - --------------------------------------------------------------------------------------------------------- v0.8.9.7 --------------------------------------------------------------------------------------------------------- @@ -115,8 +9,6 @@ Additions and changes: - Clients communicate syncing errors to the server, and the server logs a more descriptive error about what went wrong. Should make it easier to diagnose disconnection issues from now on. - Ending a multiplayer campaign round by talking to watchman doesn't require any special permissions. -- Server automatically ends rounds if there have been no players alive in 60 seconds and respawning -is not allowed during the round. - Added a button for resetting an entity's properties to the default values to the sub editor. - Updated handheld sonar UI graphics.