diff --git a/Barotrauma/BarotraumaClient/ClientSource/Characters/AI/HumanAIController.cs b/Barotrauma/BarotraumaClient/ClientSource/Characters/AI/HumanAIController.cs index fdf426063..48d6c619c 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Characters/AI/HumanAIController.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Characters/AI/HumanAIController.cs @@ -44,19 +44,20 @@ namespace Barotrauma var currentObjective = ObjectiveManager.CurrentObjective; if (currentObjective != null) { - if (currentOrder == null) + int offset = currentOrder != null ? 20 : 0; + if (currentOrder == null || currentOrder.Priority <= 0) { - GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 20), $"MAIN OBJECTIVE: {currentObjective.DebugTag} ({currentObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black); + GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 20 + offset), $"MAIN OBJECTIVE: {currentObjective.DebugTag} ({currentObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black); } var subObjective = currentObjective.CurrentSubObjective; if (subObjective != null) { - GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 40), $"SUBOBJECTIVE: {subObjective.DebugTag} ({subObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black); + GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 40 + offset), $"SUBOBJECTIVE: {subObjective.DebugTag} ({subObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black); } var activeObjective = ObjectiveManager.GetActiveObjective(); if (activeObjective != null) { - GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 60), $"ACTIVE OBJECTIVE: {activeObjective.DebugTag} ({activeObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black); + GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 60 + offset), $"ACTIVE OBJECTIVE: {activeObjective.DebugTag} ({activeObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black); } } } @@ -86,7 +87,7 @@ namespace Barotrauma new Vector2(path.CurrentNode.DrawPosition.X, -path.CurrentNode.DrawPosition.Y), Color.BlueViolet, 0, 3); - GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 80), "Path cost: " + path.Cost.FormatZeroDecimal(), Color.White, Color.Black * 0.5f); + GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 100), "Path cost: " + path.Cost.FormatZeroDecimal(), Color.White, Color.Black * 0.5f); } } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Characters/Animation/Ragdoll.cs b/Barotrauma/BarotraumaClient/ClientSource/Characters/Animation/Ragdoll.cs index 3a2159e52..132570e65 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Characters/Animation/Ragdoll.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Characters/Animation/Ragdoll.cs @@ -510,7 +510,7 @@ namespace Barotrauma Collider.DebugDraw(spriteBatch, frozen ? GUI.Style.Red : (inWater ? Color.SkyBlue : Color.Gray)); GUI.Font.DrawString(spriteBatch, Collider.LinearVelocity.X.FormatSingleDecimal(), new Vector2(Collider.DrawPosition.X, -Collider.DrawPosition.Y), Color.Orange); - foreach (RevoluteJoint joint in LimbJoints) + foreach (var joint in LimbJoints) { Vector2 pos = ConvertUnits.ToDisplayUnits(joint.WorldAnchorA); GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 5, 5), Color.White, true); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Characters/Character.cs b/Barotrauma/BarotraumaClient/ClientSource/Characters/Character.cs index c6a2f74d3..96502da37 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Characters/Character.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Characters/Character.cs @@ -358,7 +358,16 @@ namespace Barotrauma partial void OnAttackedProjSpecific(Character attacker, AttackResult attackResult, float stun) { - if (attackResult.Damage <= 1.0f || IsDead) { return; } + if (IsDead) { return; } + if (attacker != null) + { + if (attackResult.Damage <= 0.01f) { return; } + } + else + { + if (attackResult.Damage <= 1.0f) { return; } + } + if (soundTimer < soundInterval * 0.5f) { PlaySound(CharacterSound.SoundType.Damage); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterHUD.cs b/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterHUD.cs index f9ce57161..989b52794 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterHUD.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterHUD.cs @@ -163,7 +163,7 @@ namespace Barotrauma foreach (Item item in Item.ItemList) { if (item.Submarine == null || item.Submarine.TeamID != character.TeamID || item.Submarine.Info.IsWreck) { continue; } - if (!item.Repairables.Any(r => item.ConditionPercentage <= r.AIRepairThreshold)) { continue; } + if (!item.Repairables.Any(r => item.ConditionPercentage <= r.RepairThreshold)) { continue; } if (Submarine.VisibleEntities != null && !Submarine.VisibleEntities.Contains(item)) { continue; } Vector2 diff = item.WorldPosition - character.WorldPosition; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterInfo.cs b/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterInfo.cs index 8058762f4..6419470f9 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterInfo.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterInfo.cs @@ -146,7 +146,8 @@ namespace Barotrauma "+" + ((int)((newLevel - prevLevel) * 100.0f)).ToString() + " XP", GUI.Style.Green, textPopupPos, - Vector2.UnitY * 10.0f); + Vector2.UnitY * 10.0f, + playSound: Character.Controlled?.Info == this); } else if (prevLevel % 0.1f > 0.05f && newLevel % 0.1f < 0.05f) { @@ -154,7 +155,8 @@ namespace Barotrauma "+10 XP", GUI.Style.Green, textPopupPos, - Vector2.UnitY * 10.0f); + Vector2.UnitY * 10.0f, + playSound: Character.Controlled?.Info == this); } if ((int)newLevel > (int)prevLevel) diff --git a/Barotrauma/BarotraumaClient/ClientSource/Characters/Limb.cs b/Barotrauma/BarotraumaClient/ClientSource/Characters/Limb.cs index 9511fb746..30f07e97d 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Characters/Limb.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Characters/Limb.cs @@ -3,7 +3,6 @@ using Barotrauma.Particles; using Barotrauma.SpriteDeformations; using Barotrauma.Extensions; using FarseerPhysics; -using FarseerPhysics.Dynamics.Joints; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System; @@ -15,13 +14,13 @@ using SpriteParams = Barotrauma.RagdollParams.SpriteParams; namespace Barotrauma { - partial class LimbJoint : RevoluteJoint + partial class LimbJoint { public void UpdateDeformations(float deltaTime) { float diff = Math.Abs(UpperLimit - LowerLimit); float strength = MathHelper.Lerp(0, 1, MathUtils.InverseLerp(0, MathHelper.Pi, diff)); - float jointAngle = this.JointAngle * strength; + float jointAngle = JointAngle * strength; JointBendDeformation limbADeformation = LimbA.Deformations.Find(d => d is JointBendDeformation) as JointBendDeformation; JointBendDeformation limbBDeformation = LimbB.Deformations.Find(d => d is JointBendDeformation) as JointBendDeformation; diff --git a/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs b/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs index 393c1228d..fd785b74b 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs @@ -658,6 +658,44 @@ namespace Barotrauma } }, isCheat: true)); + commands.Add(new Command("listcloudfiles", "Lists all of your files on the Steam Cloud.", args => + { + int i = 0; + foreach (var file in Steamworks.SteamRemoteStorage.Files) + { + NewMessage($"* {i}: {file.Filename}, {file.Size} bytes", Color.Orange); + i++; + } + NewMessage($"Bytes remaining: {Steamworks.SteamRemoteStorage.QuotaRemainingBytes}/{Steamworks.SteamRemoteStorage.QuotaBytes}", Color.Yellow); + })); + + commands.Add(new Command("removefromcloud", "Removes a file from Steam Cloud.", args => + { + if (args.Length < 1) { return; } + var files = Steamworks.SteamRemoteStorage.Files; + Steamworks.SteamRemoteStorage.RemoteFile file; + if (int.TryParse(args[0], out int index) && index>=0 && index f.Filename.Equals(args[0], StringComparison.InvariantCultureIgnoreCase)); + } + + if (!string.IsNullOrEmpty(file.Filename)) + { + if (file.Delete()) + { + NewMessage($"Deleting {file.Filename}", Color.Orange); + } + else + { + ThrowError($"Failed to delete {file.Filename}"); + } + } + })); + commands.Add(new Command("resetall", "Reset all items and structures to prefabs. Only applicable in the subeditor.", args => { if (Screen.Selected == GameMain.SubEditorScreen) @@ -1260,6 +1298,13 @@ namespace Barotrauma TextManager.Language = "English"; })); + commands.Add(new Command("eventstats", "", (string[] args) => + { + var debugLines = ScriptedEventSet.GetDebugStatistics(); + string filePath = "eventstats.txt"; + File.WriteAllLines(filePath, debugLines); + ToolBox.OpenFileWithShell(Path.GetFullPath(filePath)); + })); #if DEBUG commands.Add(new Command("printreceivertransfers", "", (string[] args) => { @@ -1834,7 +1879,7 @@ namespace Barotrauma "giveperm", (string[] args) => { - if (args.Length < 1) return; + if (args.Length < 1) { return; } NewMessage("Valid permissions are:", Color.White); foreach (ClientPermissions permission in Enum.GetValues(typeof(ClientPermissions))) @@ -1891,7 +1936,7 @@ namespace Barotrauma { if (args.Length < 1) return; - ShowQuestionPrompt("Console command permissions to grant to client " + args[0] + "? You may enter multiple commands separated with a space.", (commandNames) => + ShowQuestionPrompt("Console command permissions to grant to client " + args[0] + "? You may enter multiple commands separated with a space or use \"all\" to give the permission to use all console commands.", (commandNames) => { GameMain.Client?.SendConsoleCommand("givecommandperm " + args[0] + " " + commandNames); }, args, 1); @@ -1904,7 +1949,7 @@ namespace Barotrauma { if (args.Length < 1) return; - ShowQuestionPrompt("Console command permissions to revoke from client " + args[0] + "? You may enter multiple commands separated with a space.", (commandNames) => + ShowQuestionPrompt("Console command permissions to revoke from client " + args[0] + "? You may enter multiple commands separated with a space or use \"all\" to revoke the permission to use any console commands.", (commandNames) => { GameMain.Client?.SendConsoleCommand("revokecommandperm " + args[0] + " " + commandNames); }, args, 1); diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSession/CrewManager.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSession/CrewManager.cs index 160f54f88..044ec7c39 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameSession/CrewManager.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameSession/CrewManager.cs @@ -1376,32 +1376,6 @@ namespace Barotrauma } } - if (assignmentNodeIcons.Any()) - { - if (PlayerInput.KeyDown(Keys.LeftShift) || PlayerInput.KeyDown(Keys.RightShift)) - { - if (assignmentNodeIcons.First().OrderIcon.Visible) - { - foreach (AssignmentNodeIconSet set in assignmentNodeIcons) - { - set.OrderIcon.Visible = false; - set.JobIcon.Visible = true; - } - } - } - else - { - if (assignmentNodeIcons.First().JobIcon.Visible) - { - foreach (AssignmentNodeIconSet set in assignmentNodeIcons) - { - set.JobIcon.Visible = false; - set.OrderIcon.Visible = true; - } - } - } - } - var hotkeyHit = false; foreach (Tuple node in optionNodes) { @@ -1578,17 +1552,6 @@ namespace Barotrauma private List availableCategories; private Stack historyNodes = new Stack(); private readonly List extraOptionCharacters = new List(); - private readonly List assignmentNodeIcons = new List(); - private struct AssignmentNodeIconSet - { - public GUIImage OrderIcon { get; private set; } - public GUIImage JobIcon { get; private set; } - public AssignmentNodeIconSet(GUIImage orderIcon, GUIImage jobIcon) - { - OrderIcon = orderIcon; - JobIcon = jobIcon; - } - } /// /// node.Color = node.HighlightColor * nodeColorMultiplier @@ -1865,7 +1828,6 @@ namespace Barotrauma background = null; commandFrame = null; extraOptionCharacters.Clear(); - assignmentNodeIcons.Clear(); isOpeningClick = isSelectionHighlighted = false; characterContext = null; itemContext = null; @@ -2028,7 +1990,6 @@ namespace Barotrauma expandNode = null; expandNodeHotkey = Keys.None; RemoveExtraOptionNodes(); - assignmentNodeIcons.Clear(); } private void RemoveExtraOptionNodes() @@ -2734,24 +2695,14 @@ namespace Barotrauma GUIImage orderIcon; if (character.CurrentOrder != null && !character.CurrentOrder.Identifier.Equals("dismissed")) { - orderIcon = new GUIImage(new RectTransform(new Vector2(1.2f), node.RectTransform, anchor: Anchor.Center), - character.CurrentOrder.SymbolSprite, scaleToFit: true); + orderIcon = new GUIImage(new RectTransform(new Vector2(1.2f), node.RectTransform, anchor: Anchor.Center), character.CurrentOrder.SymbolSprite, scaleToFit: true); var tooltip = character.CurrentOrder.Name; if (!string.IsNullOrWhiteSpace(character.CurrentOrderOption)) { tooltip += " (" + character.CurrentOrder.GetOptionName(character.CurrentOrderOption) + ")"; }; orderIcon.ToolTip = tooltip; } else { - // TODO: Replace with an icon that symbols the characters dismissed state and their availability to new orders OR localize the text - orderIcon = new GUIImage(new RectTransform(new Vector2(1.2f), node.RectTransform, anchor: Anchor.Center), - "CommandNodeContainer", scaleToFit: true); - var label = new GUITextBlock(new RectTransform(new Vector2(0.9f / 1.2f), orderIcon.RectTransform, anchor: Anchor.Center), - "FREE", textColor: jobColor * nodeColorMultiplier, font: GUI.SubHeadingFont, textAlignment: Alignment.Center, style: null) - { - CanBeFocused = false, - ForceUpperCase = true, - HoverTextColor = jobColor - }; + orderIcon = new GUIImage(new RectTransform(new Vector2(1.2f), node.RectTransform, anchor: Anchor.Center), "CommandIdleNode", scaleToFit: true); } orderIcon.Color = jobColor * nodeColorMultiplier; orderIcon.HoverColor = jobColor; @@ -2765,7 +2716,7 @@ namespace Barotrauma var nameLabel = new GUITextBlock( new RectTransform(new Point(width, 0), parent: node.RectTransform, anchor: Anchor.TopCenter, pivot: Pivot.BottomCenter) { - RelativeOffset = new Vector2(0f, -0.1f) + RelativeOffset = new Vector2(0f, -0.25f) }, ToolBox.LimitString(character.Info?.DisplayName, font, width), textColor: jobColor * nodeColorMultiplier, font: font, textAlignment: Alignment.Center, style: null) { @@ -2774,33 +2725,22 @@ namespace Barotrauma HoverTextColor = jobColor }; - // Job icon - GUIImage jobIcon = null; - if (character?.Info?.Job?.Prefab?.Icon is Sprite sprite) + if (character.Info?.Job?.Prefab?.IconSmall is Sprite smallJobIcon) { - jobIcon = new GUIImage(new RectTransform(new Vector2(1.2f), node.RectTransform, anchor: Anchor.Center), - "CommandNodeContainer", scaleToFit: true) + // Job icon + new GUIImage( + new RectTransform(new Vector2(0.4f), node.RectTransform, anchor: Anchor.TopCenter, pivot: Pivot.Center) + { + RelativeOffset = new Vector2(0.0f, -((orderIcon.RectTransform.RelativeSize.Y - 1) / 2)) + }, + smallJobIcon, scaleToFit: true) { CanBeFocused = false, - Color = jobColor * nodeColorMultiplier, - HoverColor = jobColor, - PressedColor = jobColor, - SelectedColor = jobColor, - Visible = false - }; ; - new GUIImage(new RectTransform(new Vector2(0.9f / 1.2f), jobIcon.RectTransform, anchor: Anchor.Center), - sprite, scaleToFit: true) - { - CanBeFocused = false, - Color = jobColor * nodeColorMultiplier, - HoverColor = jobColor, - PressedColor = jobColor, - SelectedColor = jobColor + Color = jobColor, + HoverColor = jobColor }; } - assignmentNodeIcons.Add(new AssignmentNodeIconSet(orderIcon, jobIcon)); - #if DEBUG bool canHear = true; #else diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/CharacterInventory.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/CharacterInventory.cs index 85e6546d9..50a8cb166 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/CharacterInventory.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/CharacterInventory.cs @@ -929,24 +929,24 @@ namespace Barotrauma //attempt to put in a free slot first for (int i = capacity - 1; i >= 0; i--) { - if (Items[i] != null) continue; - if (SlotTypes[i] == InvSlotType.Any || !item.AllowedSlots.Any(a => a.HasFlag(SlotTypes[i]))) continue; + if (Items[i] != null) { continue; } + if (SlotTypes[i] == InvSlotType.Any || !item.AllowedSlots.Any(a => a.HasFlag(SlotTypes[i]))) { continue; } success = TryPutItem(item, i, true, false, Character.Controlled, true); - if (success) break; + if (success) { break; } } if (!success) { for (int i = capacity - 1; i >= 0; i--) { - if (SlotTypes[i] == InvSlotType.Any || !item.AllowedSlots.Any(a => a.HasFlag(SlotTypes[i]))) continue; + if (SlotTypes[i] == InvSlotType.Any || !item.AllowedSlots.Any(a => a.HasFlag(SlotTypes[i]))) { continue; } // something else already equipped in a hand slot, attempt to unequip it so items aren't unnecessarily swapped to it - if (Items[i] != null && Items[i].AllowedSlots.Contains(InvSlotType.Any) && SlotTypes[i] == InvSlotType.LeftHand || SlotTypes[i] == InvSlotType.RightHand) + if (Items[i] != null && Items[i].AllowedSlots.Contains(InvSlotType.Any) && (SlotTypes[i] == InvSlotType.LeftHand || SlotTypes[i] == InvSlotType.RightHand)) { TryPutItem(Items[i], Character.Controlled, new List() { InvSlotType.Any }, true); } success = TryPutItem(item, i, true, false, Character.Controlled, true); - if (success) break; + if (success) { break; } } } break; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Holdable/Holdable.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Holdable/Holdable.cs index f6abd8b28..14767d23e 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Holdable/Holdable.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Holdable/Holdable.cs @@ -17,7 +17,7 @@ namespace Barotrauma.Items.Components public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1) { - if (!IsActive || picker == null || !CanBeAttached() || !picker.IsKeyDown(InputType.Aim) || picker != Character.Controlled) { return; } + if (!IsActive || picker == null || !CanBeAttached(picker) || !picker.IsKeyDown(InputType.Aim) || picker != Character.Controlled) { return; } Vector2 gridPos = picker.Position; Vector2 roundedGridPos = new Vector2( diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Sonar.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Sonar.cs index b7df977f6..5b98c3fea 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Sonar.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Sonar.cs @@ -74,6 +74,7 @@ namespace Barotrauma.Items.Components public readonly Vector2 TransducerWorldPos; public readonly Vector2 WorldPos; public readonly float Distance; + public double RecalculationTime; public CachedDistance(Vector2 transducerWorldPos, Vector2 worldPos, float dist) { @@ -1333,20 +1334,22 @@ namespace Barotrauma.Items.Components private void DrawMarker(SpriteBatch spriteBatch, string label, string iconIdentifier, object targetIdentifier, Vector2 worldPosition, Vector2 transducerPosition, float scale, Vector2 center, float radius) { - float dist = Vector2.Distance(worldPosition, transducerPosition); - if (Vector2.DistanceSquared(worldPosition, transducerPosition) > Range * Range) + float linearDist = Vector2.Distance(worldPosition, transducerPosition); + float dist = linearDist; + if (linearDist > Range) { if (markerDistances.TryGetValue(targetIdentifier, out CachedDistance cachedDistance)) { - if (Vector2.DistanceSquared(cachedDistance.TransducerWorldPos, transducerPosition) > 500 * 500 || - Vector2.DistanceSquared(cachedDistance.WorldPos, worldPosition) > 500 * 500) + if (Timing.TotalTime > cachedDistance.RecalculationTime && + (Vector2.DistanceSquared(cachedDistance.TransducerWorldPos, transducerPosition) > 500 * 500 || + Vector2.DistanceSquared(cachedDistance.WorldPos, worldPosition) > 500 * 500)) { markerDistances.Remove(targetIdentifier); CalculateDistance(); } else { - dist = cachedDistance.Distance; + dist = Math.Max(cachedDistance.Distance, linearDist); } } else @@ -1361,7 +1364,11 @@ namespace Barotrauma.Items.Components var path = pathFinder.FindPath(ConvertUnits.ToSimUnits(transducerPosition), ConvertUnits.ToSimUnits(worldPosition)); if (!path.Unreachable) { - markerDistances.Add(targetIdentifier, new CachedDistance(transducerPosition, worldPosition, path.TotalLength)); + var cachedDistance = new CachedDistance(transducerPosition, worldPosition, path.TotalLength) + { + RecalculationTime = Timing.TotalTime + Rand.Range(1.0f, 5.0f) + }; + markerDistances.Add(targetIdentifier, cachedDistance); dist = path.TotalLength; } } @@ -1375,16 +1382,16 @@ namespace Barotrauma.Items.Components float textAlpha = MathHelper.Clamp(1.5f - dist / 50000.0f, 0.5f, 1.0f); Vector2 dir = Vector2.Normalize(position); - Vector2 markerPos = (dist * zoom * scale > radius) ? dir * radius : position; + Vector2 markerPos = (linearDist * zoom * scale > radius) ? dir * radius : position; markerPos += center; markerPos.X = (int)markerPos.X; markerPos.Y = (int)markerPos.Y; float alpha = 1.0f; - if (dist * scale < radius) + if (linearDist * scale < radius) { - float normalizedDist = dist * scale / radius; + float normalizedDist = linearDist * scale / radius; alpha = Math.Max(normalizedDist - 0.4f, 0.0f); float mouseDist = Vector2.Distance(PlayerInput.MousePosition, markerPos); @@ -1395,14 +1402,6 @@ namespace Barotrauma.Items.Components } } - if (!GuiFrame.Children.First().Rect.Contains(markerPos)) - { - if (MathUtils.GetLineRectangleIntersection(center, markerPos, GuiFrame.Children.First().Rect, out Vector2 intersection)) - { - markerPos = intersection; - } - } - if (string.IsNullOrEmpty(iconIdentifier) || !targetIcons.ContainsKey(iconIdentifier)) { GUI.DrawRectangle(spriteBatch, new Rectangle((int)markerPos.X - 3, (int)markerPos.Y - 3, 6, 6), markerColor, thickness: 2); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Repairable.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Repairable.cs index 42a9f28b5..e7eb0f15d 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Repairable.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Repairable.cs @@ -45,7 +45,7 @@ namespace Barotrauma.Items.Components public override bool ShouldDrawHUD(Character character) { if (!HasRequiredItems(character, false) || character.SelectedConstruction != item) return false; - return !item.IsFullCondition || character.IsTraitor && item.ConditionPercentage > MinSabotageCondition || (CurrentFixer == character && (!item.IsFullCondition || (character.IsTraitor && item.ConditionPercentage > MinSabotageCondition))); + return item.ConditionPercentage < RepairThreshold || character.IsTraitor && item.ConditionPercentage > MinSabotageCondition || (CurrentFixer == character && (!item.IsFullCondition || (character.IsTraitor && item.ConditionPercentage > MinSabotageCondition))); } partial void InitProjSpecific(XElement element) diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Item.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Item.cs index 91244fc72..4702c1767 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Item.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Item.cs @@ -32,7 +32,19 @@ namespace Barotrauma private readonly Dictionary spriteAnimState = new Dictionary(); - public bool FakeBroken; + private bool fakeBroken; + public bool FakeBroken + { + get { return fakeBroken; } + set + { + if (value != fakeBroken) + { + fakeBroken = value; + SetActiveSprite(); + } + } + } private Sprite activeSprite; public override Sprite Sprite @@ -145,11 +157,12 @@ namespace Barotrauma } } + float displayCondition = FakeBroken ? 0.0f : condition; for (int i = 0; i < Prefab.BrokenSprites.Count;i++) { if (Prefab.BrokenSprites[i].FadeIn) { continue; } float minCondition = i > 0 ? Prefab.BrokenSprites[i - i].MaxCondition : 0.0f; - if (condition <= minCondition || condition <= Prefab.BrokenSprites[i].MaxCondition) + if (displayCondition <= minCondition || displayCondition <= Prefab.BrokenSprites[i].MaxCondition) { activeSprite = Prefab.BrokenSprites[i].Sprite; break; @@ -315,19 +328,25 @@ namespace Barotrauma if (holdable.Picker.SelectedItems[0] == this) { Limb holdLimb = holdable.Picker.AnimController.GetLimb(LimbType.RightHand); - depth = holdLimb.ActiveSprite.Depth + holdable.Picker.AnimController.GetDepthOffset() + depthStep * 2; - foreach (WearableSprite wearableSprite in holdLimb.WearingItems) + if (holdLimb != null) { - if (!wearableSprite.InheritLimbDepth && wearableSprite.Sprite != null) { depth = Math.Max(wearableSprite.Sprite.Depth + depthStep, depth); } + depth = holdLimb.ActiveSprite.Depth + holdable.Picker.AnimController.GetDepthOffset() + depthStep * 2; + foreach (WearableSprite wearableSprite in holdLimb.WearingItems) + { + if (!wearableSprite.InheritLimbDepth && wearableSprite.Sprite != null) { depth = Math.Max(wearableSprite.Sprite.Depth + depthStep, depth); } + } } } else if (holdable.Picker.SelectedItems[1] == this) { Limb holdLimb = holdable.Picker.AnimController.GetLimb(LimbType.LeftHand); - depth = holdLimb.ActiveSprite.Depth + holdable.Picker.AnimController.GetDepthOffset() - depthStep * 2; - foreach (WearableSprite wearableSprite in holdLimb.WearingItems) + if (holdLimb != null) { - if (!wearableSprite.InheritLimbDepth && wearableSprite.Sprite != null) { depth = Math.Min(wearableSprite.Sprite.Depth - depthStep, depth); } + depth = holdLimb.ActiveSprite.Depth + holdable.Picker.AnimController.GetDepthOffset() - depthStep * 2; + foreach (WearableSprite wearableSprite in holdLimb.WearingItems) + { + if (!wearableSprite.InheritLimbDepth && wearableSprite.Sprite != null) { depth = Math.Min(wearableSprite.Sprite.Depth - depthStep, depth); } + } } } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Map/Lights/ConvexHull.cs b/Barotrauma/BarotraumaClient/ClientSource/Map/Lights/ConvexHull.cs index c099e6c39..11d7d1736 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Map/Lights/ConvexHull.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Map/Lights/ConvexHull.cs @@ -370,6 +370,30 @@ namespace Barotrauma.Lights } } + public static void RecalculateAll(Submarine sub) + { + var chList = HullLists.Find(h => h.Submarine == sub); + if (chList != null) + { + foreach (ConvexHull ch in chList.List) + { + ch.overlappingHulls.Clear(); + for (int i = 0; i < 4; i++) + { + ch.ignoreEdge[i] = false; + } + } + for (int i = 0; i < chList.List.Count; i++) + { + for (int j = i + 1; j < chList.List.Count; j++) + { + chList.List[i].MergeOverlappingSegments(chList.List[j]); + chList.List[j].MergeOverlappingSegments(chList.List[i]); + } + } + } + } + public void SetVertices(Vector2[] points, Matrix? rotationMatrix = null) { Debug.Assert(points.Length == 4, "Only rectangular convex hulls are supported"); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Map/Submarine.cs b/Barotrauma/BarotraumaClient/ClientSource/Map/Submarine.cs index 6e7c82795..cd717dad2 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Map/Submarine.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Map/Submarine.cs @@ -31,7 +31,7 @@ namespace Barotrauma Stream = sound.Stream; Range = element.GetAttributeFloat("range", 1000.0f); Volume = element.GetAttributeFloat("volume", 1.0f); - sound.DisableMuffle = element.GetAttributeBool("disablemuffle", false); + sound.IgnoreMuffling = element.GetAttributeBool("dontmuffle", false); } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Networking/SteamManager.cs b/Barotrauma/BarotraumaClient/ClientSource/Networking/SteamManager.cs index 581fd85f1..d603cf0a1 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Networking/SteamManager.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Networking/SteamManager.cs @@ -578,7 +578,7 @@ namespace Barotrauma.Steam if (!isInitialized) return; var query = new Steamworks.Ugc.Query(Steamworks.UgcType.All) - .RankedByTotalUniqueSubscriptions() + .RankedByTrend() .WithLongDescription(); if (requireTags != null) query.WithTags(requireTags); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/SteamWorkshopScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/SteamWorkshopScreen.cs index 1833713a8..78efe5617 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/SteamWorkshopScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/SteamWorkshopScreen.cs @@ -1765,7 +1765,7 @@ namespace Barotrauma var workshopPublishStatus = SteamManager.StartPublishItem(itemContentPackage, itemEditor); if (workshopPublishStatus != null) { - if (!itemEditor.Value.Tags.Contains("unstable")) { itemEditor.Value.Tags.Add("unstable"); } + if (!(itemEditor?.HasTag("unstable") ?? false)) { itemEditor = itemEditor?.WithTag("unstable"); } CoroutineManager.StartCoroutine(WaitForPublish(workshopPublishStatus), "WaitForPublish"); } msgBox.Close(); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs index 544e51f87..544e271a0 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs @@ -1234,8 +1234,6 @@ namespace Barotrauma return false; } - Submarine.MainSub.Info.Name = name; - string savePath = name + ".sub"; string prevSavePath = null; if (!string.IsNullOrEmpty(Submarine.MainSub?.Info.FilePath) && @@ -1740,10 +1738,24 @@ namespace Barotrauma } } - var hideInMenusTickBox = nameBox.Parent.GetChildByUserData("hideinmenus") as GUITickBox; - bool hideInMenus = hideInMenusTickBox == null ? false : hideInMenusTickBox.Selected; - - string saveFolder = Path.Combine("Content", "Items", "Assemblies"); + bool hideInMenus = !(nameBox.Parent.GetChildByUserData("hideinmenus") is GUITickBox hideInMenusTickBox) ? false : hideInMenusTickBox.Selected; +#if DEBUG + string saveFolder = ItemAssemblyPrefab.VanillaSaveFolder; +#else + string saveFolder = ItemAssemblyPrefab.SaveFolder; + if (!Directory.Exists(saveFolder)) + { + try + { + Directory.CreateDirectory(saveFolder); + } + catch (Exception e) + { + DebugConsole.ThrowError("Failed to create a directory for the item assmebly.", e); + return false; + } + } +#endif string filePath = Path.Combine(saveFolder, nameBox.Text + ".xml"); if (File.Exists(filePath)) @@ -1771,7 +1783,6 @@ namespace Barotrauma #else doc.SaveSafe(filePath); #endif - new ItemAssemblyPrefab(filePath); UpdateEntityList(); } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Sounds/Sound.cs b/Barotrauma/BarotraumaClient/ClientSource/Sounds/Sound.cs index 155d38266..26080fec0 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Sounds/Sound.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Sounds/Sound.cs @@ -76,7 +76,7 @@ namespace Barotrauma.Sounds protected set; } - public bool DisableMuffle { get; set; } + public bool IgnoreMuffling { get; set; } /// /// How many instances of the same sound clip can be playing at the same time diff --git a/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPlayer.cs b/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPlayer.cs index f3ada5ea7..ba6395a32 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPlayer.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPlayer.cs @@ -648,7 +648,7 @@ namespace Barotrauma { return null; } - bool muffle = !sound.DisableMuffle && ShouldMuffleSound(Character.Controlled, position, far, hullGuess); + bool muffle = !sound.IgnoreMuffling && ShouldMuffleSound(Character.Controlled, position, far, hullGuess); return sound.Play(volume ?? sound.BaseGain, far, position, muffle: muffle); } diff --git a/Barotrauma/BarotraumaClient/LinuxClient.csproj b/Barotrauma/BarotraumaClient/LinuxClient.csproj index b227fdac4..4860f7350 100644 --- a/Barotrauma/BarotraumaClient/LinuxClient.csproj +++ b/Barotrauma/BarotraumaClient/LinuxClient.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma - 0.9.1001.0 + 0.9.10.0 Copyright © FakeFish 2018-2020 AnyCPU;x64 Barotrauma diff --git a/Barotrauma/BarotraumaClient/MacClient.csproj b/Barotrauma/BarotraumaClient/MacClient.csproj index 5382cb343..ebad2035d 100644 --- a/Barotrauma/BarotraumaClient/MacClient.csproj +++ b/Barotrauma/BarotraumaClient/MacClient.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma - 0.9.1001.0 + 0.9.10.0 Copyright © FakeFish 2018-2020 AnyCPU;x64 Barotrauma diff --git a/Barotrauma/BarotraumaClient/WindowsClient.csproj b/Barotrauma/BarotraumaClient/WindowsClient.csproj index b5e73a429..2f9b13cd3 100644 --- a/Barotrauma/BarotraumaClient/WindowsClient.csproj +++ b/Barotrauma/BarotraumaClient/WindowsClient.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma - 0.9.1001.0 + 0.9.10.0 Copyright © FakeFish 2018-2020 AnyCPU;x64 Barotrauma @@ -25,6 +25,9 @@ TRACE;DEBUG;CLIENT;WINDOWS;X64;USE_STEAM x64 ..\bin\$(Configuration)Windows\ + full + true + Auto @@ -43,12 +46,16 @@ TRACE;CLIENT;WINDOWS;X64;USE_STEAM x64 ..\bin\$(Configuration)Windows\ + full + true TRACE;CLIENT;WINDOWS;X64;USE_STEAM x64 ..\bin\$(Configuration)Windows\ + full + true diff --git a/Barotrauma/BarotraumaServer/LinuxServer.csproj b/Barotrauma/BarotraumaServer/LinuxServer.csproj index d63fc5fb7..aa65697c0 100644 --- a/Barotrauma/BarotraumaServer/LinuxServer.csproj +++ b/Barotrauma/BarotraumaServer/LinuxServer.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma Dedicated Server - 0.9.1001.0 + 0.9.10.0 Copyright © FakeFish 2018-2020 AnyCPU;x64 DedicatedServer diff --git a/Barotrauma/BarotraumaServer/MacServer.csproj b/Barotrauma/BarotraumaServer/MacServer.csproj index eff7c1785..c30bd01aa 100644 --- a/Barotrauma/BarotraumaServer/MacServer.csproj +++ b/Barotrauma/BarotraumaServer/MacServer.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma Dedicated Server - 0.9.1001.0 + 0.9.10.0 Copyright © FakeFish 2018-2020 AnyCPU;x64 DedicatedServer diff --git a/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs b/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs index 8bfe7b4b4..688831efc 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs @@ -487,7 +487,7 @@ namespace Barotrauma if (GameMain.Server == null) return; if (args.Length < 1) { - NewMessage("giveperm [id]: Grants administrative permissions to the player with the specified client ID.", Color.Cyan); + NewMessage("giveperm [id/steamid/endpoint/name]: Grants administrative permissions to the player with the specified client.", Color.Cyan); return; } @@ -522,7 +522,7 @@ namespace Barotrauma if (GameMain.Server == null) return; if (args.Length < 1) { - NewMessage("revokeperm [id]: Revokes administrative permissions to the player with the specified client ID.", Color.Cyan); + NewMessage("revokeperm [id/steamid/endpoint/name]: Revokes administrative permissions to the player with the specified client.", Color.Cyan); return; } @@ -604,7 +604,7 @@ namespace Barotrauma if (GameMain.Server == null) return; if (args.Length < 1) { - NewMessage("givecommandperm [id]: Gives the player with the specified client ID the permission to use the specified console commands.", Color.Cyan); + NewMessage("givecommandperm [id/steamid/endpoint/name]: Gives the specified client the permission to use the specified console commands.", Color.Cyan); return; } @@ -649,7 +649,7 @@ namespace Barotrauma { NewMessage("Gave the client \"" + client.Name + "\" the permission to use all console commands.", Color.White); } - else + else if (grantedCommands.Count > 0) { NewMessage("Gave the client \"" + client.Name + "\" the permission to use console commands " + string.Join(", ", grantedCommands.Select(c => c.names[0])) + ".", Color.White); } @@ -662,7 +662,7 @@ namespace Barotrauma if (GameMain.Server == null) return; if (args.Length < 1) { - NewMessage("revokecommandperm [id]: Revokes permission to use the specified console commands from the player with the specified client ID.", Color.Cyan); + NewMessage("revokecommandperm [id/steamid/endpoint/name]: Revokes permission to use the specified console commands from the specified client.", Color.Cyan); return; } @@ -682,23 +682,39 @@ namespace Barotrauma { string[] splitCommands = commandsStr.Split(' '); List revokedCommands = new List(); - for (int i = 0; i < splitCommands.Length; i++) + bool revokeAll = splitCommands.Length > 0 && splitCommands[0].Equals("all", StringComparison.OrdinalIgnoreCase); + if (revokeAll) { - splitCommands[i] = splitCommands[i].Trim().ToLowerInvariant(); - Command matchingCommand = commands.Find(c => c.names.Contains(splitCommands[i])); - if (matchingCommand == null) - { - ThrowError("Could not find the command \"" + splitCommands[i] + "\"!"); - } - else - { - revokedCommands.Add(matchingCommand); - } + revokedCommands.AddRange(commands); } + else + { + for (int i = 0; i < splitCommands.Length; i++) + { + splitCommands[i] = splitCommands[i].Trim().ToLowerInvariant(); + Command matchingCommand = commands.Find(c => c.names.Contains(splitCommands[i])); + if (matchingCommand == null) + { + ThrowError("Could not find the command \"" + splitCommands[i] + "\"!"); + } + else + { + revokedCommands.Add(matchingCommand); + } + } + } client.SetPermissions(client.Permissions, client.PermittedConsoleCommands.Except(revokedCommands).ToList()); GameMain.Server.UpdateClientPermissions(client); - NewMessage("Revoked \"" + client.Name + "\"'s permission to use the console commands " + string.Join(", ", revokedCommands.Select(c => c.names[0])) + ".", Color.White); + if (revokeAll) + { + NewMessage("Revoked \"" + client.Name + "\"'s permission to use console commands.", Color.White); + } + else if (revokedCommands.Any()) + { + NewMessage("Revoked \"" + client.Name + "\"'s permission to use the console commands " + string.Join(", ", revokedCommands.Select(c => c.names[0])) + ".", Color.White); + } + }, args, 1); }); @@ -707,7 +723,7 @@ namespace Barotrauma if (GameMain.Server == null) return; if (args.Length < 1) { - NewMessage("showperm [id]: Shows the current administrative permissions of the client with the specified client ID.", Color.Cyan); + NewMessage("showperm [id/steamid/endpoint/name]: Shows the current administrative permissions of the specified client.", Color.Cyan); return; } @@ -1808,7 +1824,7 @@ namespace Barotrauma var client = FindClient(args[0]); if (client == null) { - ThrowError("Client \"" + args[0] + "\" not found."); + GameMain.Server.SendConsoleMessage("Client \"" + args[0] + "\" not found.", senderClient); return; } if (client.Connection == GameMain.Server.OwnerConnection) @@ -1817,27 +1833,42 @@ namespace Barotrauma return; } - string[] splitCommands = args.Skip(1).ToArray(); List grantedCommands = new List(); - for (int i = 0; i < splitCommands.Length; i++) + string[] splitCommands = args.Skip(1).ToArray(); + bool giveAll = splitCommands.Length > 0 && splitCommands[0].Equals("all", StringComparison.OrdinalIgnoreCase); + if (giveAll) { - splitCommands[i] = splitCommands[i].Trim().ToLowerInvariant(); - Command matchingCommand = commands.Find(c => c.names.Contains(splitCommands[i])); - if (matchingCommand == null) + grantedCommands.AddRange(commands); + } + else + { + for (int i = 0; i < splitCommands.Length; i++) { - GameMain.Server.SendConsoleMessage("Could not find the command \"" + splitCommands[i] + "\"!", senderClient); - } - else - { - grantedCommands.Add(matchingCommand); + splitCommands[i] = splitCommands[i].Trim().ToLowerInvariant(); + Command matchingCommand = commands.Find(c => c.names.Contains(splitCommands[i])); + if (matchingCommand == null) + { + GameMain.Server.SendConsoleMessage("Could not find the command \"" + splitCommands[i] + "\"!", senderClient); + } + else + { + grantedCommands.Add(matchingCommand); + } } } client.GivePermission(ClientPermissions.ConsoleCommands); client.SetPermissions(client.Permissions, client.PermittedConsoleCommands.Union(grantedCommands).Distinct().ToList()); + GameMain.Server.UpdateClientPermissions(client); - GameMain.Server.SendConsoleMessage("Gave the client \"" + client.Name + "\" the permission to use the console commands " + string.Join(", ", grantedCommands.Select(c => c.names[0])) + ".", senderClient); - NewMessage("Gave the client \"" + client.Name + "\" the permission to use the console commands " + string.Join(", ", grantedCommands.Select(c => c.names[0])) + ".", Color.White); + if (giveAll) + { + GameMain.Server.SendConsoleMessage("Gave the client \"" + client.Name + "\" the permission to use all console commands.", senderClient); + } + else if (grantedCommands.Count > 0) + { + GameMain.Server.SendConsoleMessage("Gave the client \"" + client.Name + "\" the permission to use console commands " + string.Join(", ", grantedCommands.Select(c => c.names[0])) + ".", senderClient); + } } ); @@ -1845,7 +1876,7 @@ namespace Barotrauma "revokecommandperm", (Client senderClient, Vector2 cursorWorldPos, string[] args) => { - if (args.Length < 2) return; + if (args.Length < 2) { return; } var client = FindClient(args[0]); if (client == null) @@ -1858,28 +1889,43 @@ namespace Barotrauma GameMain.Server.SendConsoleMessage("Cannot revoke command permissions from the server owner!", senderClient); return; } - - string[] splitCommands = args.Skip(1).ToArray(); List revokedCommands = new List(); - for (int i = 0; i < splitCommands.Length; i++) + string[] splitCommands = args.Skip(1).ToArray(); + bool revokeAll = splitCommands.Length > 0 && splitCommands[0].Equals("all", StringComparison.OrdinalIgnoreCase); + if (revokeAll) { - splitCommands[i] = splitCommands[i].Trim().ToLowerInvariant(); - Command matchingCommand = commands.Find(c => c.names.Contains(splitCommands[i])); - if (matchingCommand == null) + revokedCommands.AddRange(commands); + client.RemovePermission(ClientPermissions.ConsoleCommands); + } + else + { + for (int i = 0; i < splitCommands.Length; i++) { - GameMain.Server.SendConsoleMessage("Could not find the command \"" + splitCommands[i] + "\"!", senderClient); - } - else - { - revokedCommands.Add(matchingCommand); + splitCommands[i] = splitCommands[i].Trim().ToLowerInvariant(); + Command matchingCommand = commands.Find(c => c.names.Contains(splitCommands[i])); + if (matchingCommand == null) + { + GameMain.Server.SendConsoleMessage("Could not find the command \"" + splitCommands[i] + "\"!", senderClient); + } + else + { + revokedCommands.Add(matchingCommand); + } } + client.GivePermission(ClientPermissions.ConsoleCommands); } - client.GivePermission(ClientPermissions.ConsoleCommands); client.SetPermissions(client.Permissions, client.PermittedConsoleCommands.Except(revokedCommands).ToList()); GameMain.Server.UpdateClientPermissions(client); GameMain.Server.SendConsoleMessage("Revoked \"" + client.Name + "\"'s permission to use the console commands " + string.Join(", ", revokedCommands.Select(c => c.names[0])) + ".", senderClient); - NewMessage(senderClient.Name + " revoked \"" + client.Name + "\"'s permission to use the console commands " + string.Join(", ", revokedCommands.Select(c => c.names[0])) + ".", Color.White); + if (revokeAll) + { + GameMain.Server.SendConsoleMessage("Revoked \"" + client.Name + "\"'s permission to use console commands.", senderClient); + } + else if (revokedCommands.Count > 0) + { + GameMain.Server.SendConsoleMessage("Revoked \"" + client.Name + "\"'s permission to use the console commands " + string.Join(", ", revokedCommands.Select(c => c.names[0])) + ".", senderClient); + } } ); diff --git a/Barotrauma/BarotraumaServer/ServerSource/Events/Missions/CargoMission.cs b/Barotrauma/BarotraumaServer/ServerSource/Events/Missions/CargoMission.cs index 9ee80f0f8..7909a789e 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Events/Missions/CargoMission.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Events/Missions/CargoMission.cs @@ -9,7 +9,9 @@ namespace Barotrauma msg.Write((ushort)items.Count); foreach (Item item in items) { - item.WriteSpawnData(msg, item.ID, item.ParentInventory?.Owner?.ID ?? 0); + item.WriteSpawnData(msg, + itemIDs[item], + parentInventoryIDs.ContainsKey(item) ? parentInventoryIDs[item] : Entity.NullEntityID); } } } diff --git a/Barotrauma/BarotraumaServer/ServerSource/Events/Missions/SalvageMission.cs b/Barotrauma/BarotraumaServer/ServerSource/Events/Missions/SalvageMission.cs index 1ceed8427..d9c626d8d 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Events/Missions/SalvageMission.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Events/Missions/SalvageMission.cs @@ -1,4 +1,5 @@ using Barotrauma.Networking; +using System; using System.Collections.Generic; namespace Barotrauma @@ -7,6 +8,9 @@ namespace Barotrauma { private bool usedExistingItem; + private UInt16 originalItemID; + private UInt16 originalInventoryID; + private readonly List> executedEffectIndices = new List>(); public override void ServerWriteInitial(IWriteMessage msg, Client c) @@ -14,11 +18,11 @@ namespace Barotrauma msg.Write(usedExistingItem); if (usedExistingItem) { - msg.Write(item.ID); + msg.Write(originalItemID); } else { - item.WriteSpawnData(msg, item.ID, item.ParentInventory?.Owner?.ID ?? 0); + item.WriteSpawnData(msg, originalItemID, originalInventoryID); } msg.Write((byte)executedEffectIndices.Count); diff --git a/Barotrauma/BarotraumaServer/ServerSource/Items/Components/Holdable/Holdable.cs b/Barotrauma/BarotraumaServer/ServerSource/Items/Components/Holdable/Holdable.cs index 8d1de81ae..90d4be060 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Items/Components/Holdable/Holdable.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Items/Components/Holdable/Holdable.cs @@ -27,7 +27,7 @@ namespace Barotrauma.Items.Components simPosition = c.Character.SimPosition + offset; Drop(false, null); - item.SetTransform(simPosition, 0.0f); + item.SetTransform(simPosition, 0.0f, findNewHull: false); AttachToWall(); item.CreateServerEvent(this); diff --git a/Barotrauma/BarotraumaServer/WindowsServer.csproj b/Barotrauma/BarotraumaServer/WindowsServer.csproj index ac68954bc..6ac37ca59 100644 --- a/Barotrauma/BarotraumaServer/WindowsServer.csproj +++ b/Barotrauma/BarotraumaServer/WindowsServer.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma Dedicated Server - 0.9.1001.0 + 0.9.10.0 Copyright © FakeFish 2018-2020 AnyCPU;x64 DedicatedServer @@ -24,6 +24,8 @@ TRACE;DEBUG;SERVER;WINDOWS;X64;USE_STEAM x64 ..\bin\$(Configuration)Windows\ + full + true @@ -42,12 +44,16 @@ TRACE;SERVER;WINDOWS;X64;USE_STEAM x64 ..\bin\$(Configuration)Windows\ + full + true TRACE;SERVER;WINDOWS;X64;USE_STEAM x64 ..\bin\$(Configuration)Windows\ + full + true diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs index 08a666469..b033c6eb5 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs @@ -559,7 +559,7 @@ namespace Barotrauma if (item.CurrentHull != hull) { continue; } if (AIObjectiveRepairItems.IsValidTarget(item, Character)) { - if (item.Repairables.All(r => item.ConditionPercentage > r.AIRepairThreshold)) { continue; } + if (item.Repairables.All(r => item.ConditionPercentage > r.RepairThreshold)) { continue; } if (AddTargets(Character, item) && newOrder == null && !ObjectiveManager.HasActiveObjective()) { var orderPrefab = Order.GetPrefab("reportbrokendevices"); @@ -609,9 +609,13 @@ namespace Barotrauma if (ObjectiveManager.CurrentObjective is AIObjectiveFightIntruders) { return; } if (attacker == null || attacker.IsDead || attacker.Removed) { + // Don't react on the damage if there's no attacker. + // We might consider launching the retreat combat objective in some cases, so that the bot does not just stand somewhere getting damaged and dying. + // But fires and enemies should already be handled by the FindSafetyObjective. + return; // Ignore damage from falling etc that we shouldn't react to. - if (Character.LastDamageSource == null) { return; } - AddCombatObjective(AIObjectiveCombat.CombatMode.Retreat, Rand.Range(0.5f, 1f, Rand.RandSync.Unsynced)); + //if (Character.LastDamageSource == null) { return; } + //AddCombatObjective(AIObjectiveCombat.CombatMode.Retreat, Rand.Range(0.5f, 1f, Rand.RandSync.Unsynced)); } else if (IsFriendly(attacker)) { @@ -827,7 +831,7 @@ namespace Barotrauma if (item.CurrentHull != hull) { continue; } if (AIObjectiveRepairItems.IsValidTarget(item, character)) { - if (item.Repairables.All(r => item.ConditionPercentage >= r.AIRepairThreshold)) { continue; } + if (item.Repairables.All(r => item.ConditionPercentage >= r.RepairThreshold)) { continue; } AddTargets(character, item); } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/IndoorsSteeringManager.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/IndoorsSteeringManager.cs index ad515afa2..2f2b44d6b 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/IndoorsSteeringManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/IndoorsSteeringManager.cs @@ -412,7 +412,11 @@ namespace Barotrauma { //the node we're heading towards is the last one in the path, and at a door //the door needs to be open for the character to reach the node - shouldBeOpen = true; + if (currentWaypoint.ConnectedDoor.LinkedGap != null && currentWaypoint.ConnectedDoor.LinkedGap.IsRoomToRoom) + { + shouldBeOpen = true; + door = currentWaypoint.ConnectedDoor; + } } else { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveManager.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveManager.cs index 698b777d7..89e2d3dda 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveManager.cs @@ -194,24 +194,18 @@ namespace Barotrauma { if (CurrentOrder != null) { +#if DEBUG + // Note: don't automatically remove orders here. Removing orders needs to be done via dismissing. if (CurrentOrder.IsCompleted) { -#if DEBUG - DebugConsole.NewMessage($"{character.Name}: Removing order {CurrentOrder.DebugTag}, because it is completed.", Color.LightGreen); -#endif - CurrentOrder = null; + DebugConsole.NewMessage($"{character.Name}: ORDER {CurrentOrder.DebugTag} IS COMPLETED. CURRENTLY ALL ORDERS SHOULD BE LOOPING.", Color.Red); } else if (!CurrentOrder.CanBeCompleted) { -#if DEBUG - DebugConsole.NewMessage($"{character.Name}: Removing order {CurrentOrder.DebugTag}, because it cannot be completed.", Color.Red); + DebugConsole.NewMessage($"{character.Name}: ORDER {CurrentOrder.DebugTag}, CANNOT BE COMPLETED.", Color.Red); + } #endif - CurrentOrder = null; - } - else - { - CurrentOrder.Update(deltaTime); - } + CurrentOrder.Update(deltaTime); } if (WaitTimer > 0) { @@ -379,7 +373,7 @@ namespace Barotrauma newObjective = new AIObjectiveOperateItem(order.TargetItemComponent, character, this, option, requireEquip: false, useController: order.UseController, controller: order.ConnectedController, priorityModifier: priorityModifier) { - IsLoop = option != "shutdown", + IsLoop = true, // Don't override unless it's an order by a player Override = orderGiver != null && orderGiver.IsPlayer }; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveOperateItem.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveOperateItem.cs index 7dba6a78c..471d776e3 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveOperateItem.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveOperateItem.cs @@ -28,6 +28,7 @@ namespace Barotrauma public ItemComponent GetTarget() => useController ? controller : component; public Func completionCondition; + private bool isDoneOperating; public override float GetPriority() { @@ -51,30 +52,34 @@ namespace Barotrauma if (targetItem == null) { #if DEBUG - DebugConsole.ThrowError("Item or component of AI Objective Operate item wass null. This shouldn't happen."); + DebugConsole.ThrowError("Item or component of AI Objective Operate item was null. This shouldn't happen."); #endif Abandon = true; Priority = 0; return Priority; } - switch (Option) + var reactor = component?.Item.GetComponent(); + if (reactor != null) { - case "shutdown": - var powered = component?.Item.GetComponent(); - if (powered != null && !powered.IsActive) - { - Priority = 0; - return Priority; - } - break; - case "powerup": - // Check that we don't already have another order that is targeting the same item. - if (objectiveManager.CurrentOrder is AIObjectiveOperateItem operateOrder && operateOrder != this && operateOrder.GetTarget() == target) - { - Priority = 0; - return Priority; - } - break; + switch (Option) + { + case "shutdown": + if (!reactor.PowerOn) + { + Priority = 0; + return Priority; + } + break; + case "powerup": + // Check that we don't already have another order that is targeting the same item. + // Without this the autonomous objective will tell the bot to turn the reactor on again. + if (objectiveManager.CurrentOrder is AIObjectiveOperateItem operateOrder && operateOrder != this && operateOrder.GetTarget() == target) + { + Priority = 0; + return Priority; + } + break; + } } if (targetItem.CurrentHull == null || targetItem.CurrentHull.FireSources.Any() || HumanAIController.IsItemOperatedByAnother(target, out _)) { @@ -87,7 +92,7 @@ namespace Barotrauma else { float value = CumulatedDevotion + (AIObjectiveManager.OrderPriority * PriorityModifier); - float max = objectiveManager.CurrentOrder == this ? MathHelper.Min(AIObjectiveManager.OrderPriority - 1, 90) : AIObjectiveManager.RunPriority - 1; + float max = objectiveManager.CurrentOrder == this ? MathHelper.Min(AIObjectiveManager.OrderPriority, 90) : AIObjectiveManager.RunPriority - 1; Priority = MathHelper.Clamp(value, 0, max); } } @@ -148,7 +153,7 @@ namespace Barotrauma } if (component.AIOperate(deltaTime, character, this)) { - IsCompleted = completionCondition == null || completionCondition(); + isDoneOperating = completionCondition == null || completionCondition(); } } else @@ -215,12 +220,12 @@ namespace Barotrauma } if (component.AIOperate(deltaTime, character, this)) { - IsCompleted = completionCondition == null || completionCondition(); + isDoneOperating = completionCondition == null || completionCondition(); } } } } - protected override bool Check() => IsCompleted && !IsLoop; + protected override bool Check() => isDoneOperating && !IsLoop; } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveRepairItems.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveRepairItems.cs index e9287f01f..8271b4af8 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveRepairItems.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveRepairItems.cs @@ -76,7 +76,7 @@ namespace Barotrauma if (item != character.SelectedConstruction) { float condition = item.ConditionPercentage; - if (item.Repairables.All(r => condition >= r.AIRepairThreshold)) { return false; } + if (item.Repairables.All(r => condition >= r.RepairThreshold)) { return false; } } } if (!string.IsNullOrWhiteSpace(RelevantSkill)) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveRescue.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveRescue.cs index 87f72d5ce..d85f2b39a 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveRescue.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveRescue.cs @@ -279,7 +279,7 @@ namespace Barotrauma ic.PlaySound(ActionType.OnUse, character); #endif ic.WasUsed = true; - ic.ApplyStatusEffects(ActionType.OnUse, 1.0f, targetCharacter, targetLimb); + ic.ApplyStatusEffects(ActionType.OnUse, 1.0f, targetCharacter, targetLimb, user: character); if (ic.DeleteOnUse) { remove = true; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/HumanoidAnimController.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/HumanoidAnimController.cs index f20290277..c4bdeda23 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/HumanoidAnimController.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/HumanoidAnimController.cs @@ -1713,7 +1713,7 @@ namespace Barotrauma if (holdable.ControlPose) { - head.body.SmoothRotate(itemAngle); + head?.body.SmoothRotate(itemAngle); if (TargetMovement == Vector2.Zero && inWater) { @@ -1735,13 +1735,13 @@ namespace Barotrauma { if (character.SelectedItems[0] == item) { - if (rightHand.IsSevered) return; + if (rightHand == null || rightHand.IsSevered) { return; } transformedHoldPos = rightHand.PullJointWorldAnchorA - transformedHandlePos[0]; itemAngle = (rightHand.Rotation + (holdAngle - MathHelper.PiOver2) * Dir); } else if (character.SelectedItems[1] == item) { - if (leftHand.IsSevered) return; + if (leftHand == null || leftHand.IsSevered) { return; } transformedHoldPos = leftHand.PullJointWorldAnchorA - transformedHandlePos[1]; itemAngle = (leftHand.Rotation + (holdAngle - MathHelper.PiOver2) * Dir); } @@ -1750,12 +1750,12 @@ namespace Barotrauma { if (character.SelectedItems[0] == item) { - if (rightHand.IsSevered) return; + if (rightHand == null || rightHand.IsSevered) { return; } rightHand.Disabled = true; } if (character.SelectedItems[1] == item) { - if (leftHand.IsSevered) return; + if (leftHand == null || leftHand.IsSevered) { return; } leftHand.Disabled = true; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/Ragdoll.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/Ragdoll.cs index 939b49ea6..e7e1ae99f 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/Ragdoll.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/Ragdoll.cs @@ -441,7 +441,7 @@ namespace Barotrauma { foreach (LimbJoint joint in LimbJoints) { - if (GameMain.World.JointList.Contains(joint)) { GameMain.World.Remove(joint); } + if (GameMain.World.JointList.Contains(joint.Joint)) { GameMain.World.Remove(joint.Joint); } } } DebugConsole.Log($"Creating joints from {RagdollParams.Name}."); @@ -526,7 +526,7 @@ namespace Barotrauma public void AddJoint(JointParams jointParams) { LimbJoint joint = new LimbJoint(Limbs[jointParams.Limb1], Limbs[jointParams.Limb2], jointParams, this); - GameMain.World.Add(joint); + GameMain.World.Add(joint.Joint); for (int i = 0; i < LimbJoints.Length; i++) { if (LimbJoints[i] != null) continue; @@ -609,7 +609,7 @@ namespace Barotrauma limb.Remove(); foreach (LimbJoint limbJoint in attachedJoints) { - GameMain.World.Remove(limbJoint); + GameMain.World.Remove(limbJoint.Joint); } } @@ -726,7 +726,7 @@ namespace Barotrauma private readonly List connectedLimbs = new List(); private readonly List checkedJoints = new List(); - public bool SeverLimbJoint(LimbJoint limbJoint, bool playSound = true) + public bool SeverLimbJoint(LimbJoint limbJoint) { if (!limbJoint.CanBeSevered || limbJoint.IsSevered) { @@ -750,6 +750,14 @@ namespace Barotrauma { if (connectedLimbs.Contains(limb)) { continue; } limb.IsSevered = true; + if (limb.type == LimbType.RightHand) + { + character.SelectedItems[0]?.Drop(character); + } + else if (limb.type == LimbType.LeftHand) + { + character.SelectedItems[1]?.Drop(character); + } } SeverLimbJointProjSpecific(limbJoint, playSound: true); @@ -1776,11 +1784,12 @@ namespace Barotrauma if (LimbJoints != null) { - foreach (RevoluteJoint joint in LimbJoints) + foreach (var joint in LimbJoints) { - if (GameMain.World.JointList.Contains(joint)) + var j = joint.Joint; + if (GameMain.World.JointList.Contains(j)) { - GameMain.World.Remove(joint); + GameMain.World.Remove(j); } } LimbJoints = null; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs index 297bb4c62..08cbae9b9 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs @@ -1150,8 +1150,11 @@ namespace Barotrauma float reduction = 0; reduction = CalculateMovementPenalty(AnimController.GetLimb(LimbType.RightFoot, excludeSevered: false), reduction); reduction = CalculateMovementPenalty(AnimController.GetLimb(LimbType.LeftFoot, excludeSevered: false), reduction); - reduction = CalculateMovementPenalty(AnimController.GetLimb(LimbType.RightHand, excludeSevered: false), reduction); - reduction = CalculateMovementPenalty(AnimController.GetLimb(LimbType.LeftHand, excludeSevered: false), reduction); + if (!(AnimController is HumanoidAnimController)) + { + reduction = CalculateMovementPenalty(AnimController.GetLimb(LimbType.RightHand, excludeSevered: false), reduction); + reduction = CalculateMovementPenalty(AnimController.GetLimb(LimbType.LeftHand, excludeSevered: false), reduction); + } int totalTailLimbs = 0; int destroyedTailLimbs = 0; foreach (var limb in AnimController.Limbs) @@ -1176,7 +1179,7 @@ namespace Barotrauma { if (limb != null) { - sum += MathHelper.Lerp(0, max, CharacterHealth.GetLimbDamage(limb)); + sum += MathHelper.Lerp(0, max, CharacterHealth.GetLimbDamage(limb, afflictionType: "damage")); } return Math.Clamp(sum, 0, 1f); } @@ -2895,6 +2898,7 @@ namespace Barotrauma { SelectedConstruction = null; } + HealthUpdateInterval = 0.0f; } private readonly List targets = new List(); @@ -2956,6 +2960,7 @@ namespace Barotrauma } CharacterHealth.ApplyAffliction(null, new Affliction(AfflictionPrefab.Pressure, AfflictionPrefab.Pressure.MaxStrength)); + if (isNetworkMessage && GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient && Vitality <= CharacterHealth.MinVitality) { Kill(CauseOfDeathType.Pressure, null, isNetworkMessage: true); } if (IsDead) { BreakJoints(); @@ -2988,7 +2993,10 @@ namespace Barotrauma foreach (var joint in AnimController.LimbJoints) { - joint.LimitEnabled = false; + if (joint.revoluteJoint != null) + { + joint.revoluteJoint.LimitEnabled = false; + } } } @@ -3055,9 +3063,12 @@ namespace Barotrauma AnimController.ResetPullJoints(); - foreach (RevoluteJoint joint in AnimController.LimbJoints) + foreach (var joint in AnimController.LimbJoints) { - joint.MotorEnabled = false; + if (joint.revoluteJoint != null) + { + joint.revoluteJoint.MotorEnabled = false; + } } if (GameMain.GameSession != null) @@ -3088,7 +3099,11 @@ namespace Barotrauma foreach (LimbJoint joint in AnimController.LimbJoints) { - joint.MotorEnabled = true; + var revoluteJoint = joint.revoluteJoint; + if (revoluteJoint != null) + { + revoluteJoint.MotorEnabled = true; + } joint.Enabled = true; joint.IsSevered = false; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/CharacterHealth.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/CharacterHealth.cs index b5e866b60..cbff12b47 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/CharacterHealth.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/CharacterHealth.cs @@ -252,8 +252,8 @@ namespace Barotrauma : afflictions.Where(limbHealthFilter).Union(limbHealths.SelectMany(lh => lh.Afflictions.Where(limbHealthFilter))); } - private LimbHealth GetMatchingLimbHealth(Limb limb) => limbHealths[limb.HealthIndex]; - private LimbHealth GetMatchingLimbHealth(Affliction affliction) => GetMatchingLimbHealth(Character.AnimController.GetLimb(affliction.Prefab.IndicatorLimb)); + private LimbHealth GetMatchingLimbHealth(Limb limb) => limb == null ? null : limbHealths[limb.HealthIndex]; + private LimbHealth GetMatchingLimbHealth(Affliction affliction) => GetMatchingLimbHealth(Character.AnimController.GetLimb(affliction.Prefab.IndicatorLimb, excludeSevered: false)); /// /// Returns the limb afflictions and non-limbspecific afflictions that are set to be displayed on this limb. @@ -515,7 +515,7 @@ namespace Barotrauma if (Vitality <= MinVitality) { Kill(); } } - public float GetLimbDamage(Limb limb) + public float GetLimbDamage(Limb limb, string afflictionType = null) { float damageStrength; if (limb.IsSevered) @@ -528,10 +528,17 @@ namespace Barotrauma // Therefore with e.g. 80 health, the max damage per limb would be 20. // Having at least 20 damage on both legs would cause maximum limping. float max = MaxVitality / 4; - float damage = GetAfflictionStrength("damage", limb, true); - float bleeding = GetAfflictionStrength("bleeding", limb, true); - float burn = GetAfflictionStrength("burn", limb, true); - damageStrength = Math.Min(damage + bleeding + burn, max); + if (string.IsNullOrEmpty(afflictionType)) + { + float damage = GetAfflictionStrength("damage", limb, true); + float bleeding = GetAfflictionStrength("bleeding", limb, true); + float burn = GetAfflictionStrength("burn", limb, true); + damageStrength = Math.Min(damage + bleeding + burn, max); + } + else + { + damageStrength = Math.Min(GetAfflictionStrength("damage", limb, true), max); + } return damageStrength / max; } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Jobs/JobPrefab.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Jobs/JobPrefab.cs index fbd323fc8..836d86c16 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Jobs/JobPrefab.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Jobs/JobPrefab.cs @@ -163,6 +163,7 @@ namespace Barotrauma } public Sprite Icon; + public Sprite IconSmall; public string FilePath { get; private set; } public XElement Element { get; private set; } @@ -207,6 +208,9 @@ namespace Barotrauma case "jobicon": Icon = new Sprite(subElement.FirstElement()); break; + case "jobiconsmall": + IconSmall = new Sprite(subElement.FirstElement()); + break; } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Limb.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Limb.cs index 910a38ecb..e3ebd0ce4 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Limb.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Limb.cs @@ -19,8 +19,8 @@ namespace Barotrauma None, LeftHand, RightHand, LeftArm, RightArm, LeftForearm, RightForearm, LeftLeg, RightLeg, LeftFoot, RightFoot, Head, Torso, Tail, Legs, RightThigh, LeftThigh, Waist, Jaw }; - - partial class LimbJoint : RevoluteJoint + + partial class LimbJoint { public bool IsSevered; public bool CanBeSevered => Params.CanBeSevered; @@ -30,27 +30,135 @@ namespace Barotrauma public float Scale => Params.Scale * ragdoll.RagdollParams.JointScale; - public LimbJoint(Limb limbA, Limb limbB, JointParams jointParams, Ragdoll ragdoll) : this(limbA, limbB, Vector2.One, Vector2.One) + public readonly RevoluteJoint revoluteJoint; + public readonly WeldJoint weldJoint; + public Joint Joint => revoluteJoint ?? weldJoint as Joint; + + public bool Enabled + { + get => Joint.Enabled; + set => Joint.Enabled = value; + } + + public Body BodyA => Joint.BodyA; + + public Body BodyB => Joint.BodyB; + + public Vector2 WorldAnchorA + { + get => Joint.WorldAnchorA; + set => Joint.WorldAnchorA = value; + } + + public Vector2 WorldAnchorB + { + get => Joint.WorldAnchorB; + set => Joint.WorldAnchorB = value; + } + + public Vector2 LocalAnchorA + { + get => revoluteJoint != null ? revoluteJoint.LocalAnchorA : weldJoint.LocalAnchorA; + set + { + if (weldJoint != null) + { + weldJoint.LocalAnchorA = value; + } + else + { + revoluteJoint.LocalAnchorA = value; + } + } + } + + public Vector2 LocalAnchorB + { + get => revoluteJoint != null ? revoluteJoint.LocalAnchorB : weldJoint.LocalAnchorB; + set + { + if (weldJoint != null) + { + weldJoint.LocalAnchorB = value; + } + else + { + revoluteJoint.LocalAnchorB = value; + } + } + } + + public bool LimitEnabled + { + get => revoluteJoint != null ? revoluteJoint.LimitEnabled : false; + set + { + if (revoluteJoint != null) + { + revoluteJoint.LimitEnabled = value; + } + } + } + + public float LowerLimit + { + get => revoluteJoint != null ? revoluteJoint.LowerLimit : 0; + set + { + if (revoluteJoint != null) + { + revoluteJoint.LowerLimit = value; + } + } + } + + public float UpperLimit + { + get => revoluteJoint != null ? revoluteJoint.UpperLimit : 0; + set + { + if (revoluteJoint != null) + { + revoluteJoint.UpperLimit = value; + } + } + } + + public float JointAngle => revoluteJoint != null ? revoluteJoint.JointAngle : weldJoint.ReferenceAngle; + + public LimbJoint(Limb limbA, Limb limbB, JointParams jointParams, Ragdoll ragdoll) : this(limbA, limbB, Vector2.One, Vector2.One, jointParams.WeldJoint) { Params = jointParams; this.ragdoll = ragdoll; LoadParams(); } - public LimbJoint(Limb limbA, Limb limbB, Vector2 anchor1, Vector2 anchor2) - : base(limbA.body.FarseerBody, limbB.body.FarseerBody, anchor1, anchor2) + public LimbJoint(Limb limbA, Limb limbB, Vector2 anchor1, Vector2 anchor2, bool weld = false) { - CollideConnected = false; - MotorEnabled = true; - MaxMotorTorque = 0.25f; + if (weld) + { + weldJoint = new WeldJoint(limbA.body.FarseerBody, limbB.body.FarseerBody, anchor1, anchor2); + } + else + { + revoluteJoint = new RevoluteJoint(limbA.body.FarseerBody, limbB.body.FarseerBody, anchor1, anchor2) + { + MotorEnabled = true, + MaxMotorTorque = 0.25f + }; + } + Joint.CollideConnected = false; LimbA = limbA; LimbB = limbB; } public void LoadParams() { - MaxMotorTorque = Params.Stiffness; - LimitEnabled = Params.LimitEnabled; + if (revoluteJoint != null) + { + revoluteJoint.MaxMotorTorque = Params.Stiffness; + revoluteJoint.LimitEnabled = Params.LimitEnabled; + } if (float.IsNaN(Params.LowerLimit)) { Params.LowerLimit = 0; @@ -61,17 +169,33 @@ namespace Barotrauma } if (ragdoll.IsFlipped) { - LocalAnchorA = ConvertUnits.ToSimUnits(new Vector2(-Params.Limb1Anchor.X, Params.Limb1Anchor.Y) * Scale); - LocalAnchorB = ConvertUnits.ToSimUnits(new Vector2(-Params.Limb2Anchor.X, Params.Limb2Anchor.Y) * Scale); - UpperLimit = MathHelper.ToRadians(-Params.LowerLimit); - LowerLimit = MathHelper.ToRadians(-Params.UpperLimit); + if (weldJoint != null) + { + weldJoint.LocalAnchorA = ConvertUnits.ToSimUnits(new Vector2(-Params.Limb1Anchor.X, Params.Limb1Anchor.Y) * Scale); + weldJoint.LocalAnchorB = ConvertUnits.ToSimUnits(new Vector2(-Params.Limb2Anchor.X, Params.Limb2Anchor.Y) * Scale); + } + else + { + revoluteJoint.LocalAnchorA = ConvertUnits.ToSimUnits(new Vector2(-Params.Limb1Anchor.X, Params.Limb1Anchor.Y) * Scale); + revoluteJoint.LocalAnchorB = ConvertUnits.ToSimUnits(new Vector2(-Params.Limb2Anchor.X, Params.Limb2Anchor.Y) * Scale); + revoluteJoint.UpperLimit = MathHelper.ToRadians(-Params.LowerLimit); + revoluteJoint.LowerLimit = MathHelper.ToRadians(-Params.UpperLimit); + } } else { - LocalAnchorA = ConvertUnits.ToSimUnits(Params.Limb1Anchor * Scale); - LocalAnchorB = ConvertUnits.ToSimUnits(Params.Limb2Anchor * Scale); - UpperLimit = MathHelper.ToRadians(Params.UpperLimit); - LowerLimit = MathHelper.ToRadians(Params.LowerLimit); + if (weldJoint != null) + { + weldJoint.LocalAnchorA = ConvertUnits.ToSimUnits(Params.Limb1Anchor * Scale); + weldJoint.LocalAnchorB = ConvertUnits.ToSimUnits(Params.Limb2Anchor * Scale); + } + else + { + revoluteJoint.LocalAnchorA = ConvertUnits.ToSimUnits(Params.Limb1Anchor * Scale); + revoluteJoint.LocalAnchorB = ConvertUnits.ToSimUnits(Params.Limb2Anchor * Scale); + revoluteJoint.UpperLimit = MathHelper.ToRadians(Params.UpperLimit); + revoluteJoint.LowerLimit = MathHelper.ToRadians(Params.LowerLimit); + } } } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Params/Ragdoll/RagdollParams.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Params/Ragdoll/RagdollParams.cs index c45cb5faa..ea95217df 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Params/Ragdoll/RagdollParams.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Params/Ragdoll/RagdollParams.cs @@ -470,7 +470,7 @@ namespace Barotrauma [Serialize(true, true), Editable] public bool CanBeSevered { get; set; } - [Serialize(1f, true, description:"Modifies the severance probability (defined per item/attack) when the character is alive. Currently only affects limbs of type None, Shield, or Tail on non-humanoid ragdolls. Also note that if CanBeSevered is false, this property doesn't have any effect."), Editable(MinValueFloat = 0, MaxValueFloat = 10, ValueStep = 0.1f, DecimalCount = 2)] + [Serialize(0f, true, description:"Default 0 (Can't be severed when the creature is alive). Modifies the severance probability (defined per item/attack) when the character is alive. Currently only affects non-humanoid ragdolls. Also note that if CanBeSevered is false, this property doesn't have any effect."), Editable(MinValueFloat = 0, MaxValueFloat = 10, ValueStep = 0.1f, DecimalCount = 2)] public float SeveranceProbabilityModifier { get; set; } [Serialize("gore", true), Editable] @@ -497,6 +497,9 @@ namespace Barotrauma [Serialize(1f, true, description: "CAUTION: Not fully implemented. Only use for limb joints that connect non-animated limbs!"), Editable] public float Scale { get; set; } + [Serialize(false, false), Editable(ReadOnly = true)] + public bool WeldJoint { get; set; } + public JointParams(XElement element, RagdollParams ragdoll) : base(element, ragdoll) { } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/ContentPackage.cs b/Barotrauma/BarotraumaShared/SharedSource/ContentPackage.cs index 4be3f7eed..dfb7c477b 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/ContentPackage.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/ContentPackage.cs @@ -650,22 +650,30 @@ namespace Barotrauma public static void SortContentPackages() { - List = List - .OrderByDescending(p => p.CorePackage) - .ThenBy(p => List.IndexOf(p)) - .ToList(); - if (GameMain.Config != null) { + List = List + .OrderByDescending(p => p.CorePackage) + .ThenBy(p => GameMain.Config.SelectedContentPackages.IndexOf(p)) + .ThenBy(p => List.IndexOf(p)) + .ToList(); + var sortedSelected = GameMain.Config.SelectedContentPackages .OrderByDescending(p => p.CorePackage) - .ThenBy(p => List.IndexOf(p)) + .ThenBy(p => GameMain.Config.SelectedContentPackages.IndexOf(p)) .ToList(); GameMain.Config.SelectedContentPackages.Clear(); GameMain.Config.SelectedContentPackages.AddRange(sortedSelected); - var reportList = List.Where(p => GameMain.Config.SelectedContentPackages.Contains(p)); + var reportList = GameMain.Config.SelectedContentPackages; DebugConsole.NewMessage($"Content package load order: { string.Join(" | ", reportList.Select(cp => cp.Name)) }"); } + else + { + List = List + .OrderByDescending(p => p.CorePackage) + .ThenBy(p => List.IndexOf(p)) + .ToList(); + } } public void Delete() diff --git a/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs b/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs index ac85be2a8..99852f5bd 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs @@ -292,7 +292,7 @@ namespace Barotrauma commands.Add(new Command("startwhenclientsready", "startwhenclientsready [true/false]: Enable or disable automatically starting the round when clients are ready to start.", null)); - commands.Add(new Command("giveperm", "giveperm [id]: Grants administrative permissions to the player with the specified client ID.", null, + commands.Add(new Command("giveperm", "giveperm [id/steamid/endpoint/name]: Grants administrative permissions to the specified client.", null, () => { if (GameMain.NetworkMember == null) return null; @@ -304,7 +304,7 @@ namespace Barotrauma }; })); - commands.Add(new Command("revokeperm", "revokeperm [id]: Revokes administrative permissions to the player with the specified client ID.", null, + commands.Add(new Command("revokeperm", "revokeperm [id/steamid/endpoint/name]: Revokes administrative permissions from the specified client.", null, () => { if (GameMain.NetworkMember == null) return null; @@ -316,7 +316,7 @@ namespace Barotrauma }; })); - commands.Add(new Command("giverank", "giverank [id]: Assigns a specific rank (= a set of administrative permissions) to the player with the specified client ID.", null, + commands.Add(new Command("giverank", "giverank [id/steamid/endpoint/name]: Assigns a specific rank (= a set of administrative permissions) to the specified client.", null, () => { if (GameMain.NetworkMember == null) return null; @@ -328,12 +328,41 @@ namespace Barotrauma }; })); - commands.Add(new Command("givecommandperm", "givecommandperm [id]: Gives the player with the specified client ID the permission to use the specified console commands.", null)); + commands.Add(new Command("givecommandperm", "givecommandperm [id/steamid/endpoint/name]: Gives the specified client the permission to use the specified console commands.", null, + () => + { + if (GameMain.NetworkMember == null) return null; + + return new string[][] + { + GameMain.NetworkMember.ConnectedClients.Select(c => c.Name).ToArray(), + commands.Select(c => c.names[0]).ToArray() + }; + })); + + commands.Add(new Command("revokecommandperm", "revokecommandperm [id/steamid/endpoint/name]: Revokes permission to use the specified console commands from the specified client.", null, + () => + { + if (GameMain.NetworkMember == null) return null; + + return new string[][] + { + GameMain.NetworkMember.ConnectedClients.Select(c => c.Name).ToArray(), + new string[0] + }; + })); + + commands.Add(new Command("showperm", "showperm [id/steamid/endpoint/name]: Shows the current administrative permissions of the specified client.", null, + () => + { + if (GameMain.NetworkMember == null) return null; + + return new string[][] + { + GameMain.NetworkMember.ConnectedClients.Select(c => c.Name).ToArray() + }; + })); - commands.Add(new Command("revokecommandperm", "revokecommandperm [id]: Revokes permission to use the specified console commands from the player with the specified client ID.", null)); - - commands.Add(new Command("showperm", "showperm [id]: Shows the current administrative permissions of the client with the specified client ID.", null)); - commands.Add(new Command("respawnnow", "respawnnow: Trigger a respawn immediately if there are any clients waiting to respawn.", null)); commands.Add(new Command("showkarma", "showkarma: Show the current karma values of the players.", null)); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/CargoMission.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/CargoMission.cs index c5960b7e4..2dbd0ffcd 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/CargoMission.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/CargoMission.cs @@ -1,4 +1,5 @@ using Microsoft.Xna.Framework; +using System; using System.Collections.Generic; using System.Linq; using System.Xml.Linq; @@ -10,6 +11,8 @@ namespace Barotrauma private readonly XElement itemConfig; private readonly List items = new List(); + private readonly Dictionary itemIDs = new Dictionary(); + private readonly Dictionary parentInventoryIDs = new Dictionary(); private int requiredDeliveryAmount; @@ -23,6 +26,8 @@ namespace Barotrauma private void InitItems() { items.Clear(); + itemIDs.Clear(); + parentInventoryIDs.Clear(); if (itemConfig == null) { @@ -91,8 +96,13 @@ namespace Barotrauma var item = new Item(itemPrefab, position, cargoRoom.Submarine); item.FindHull(); items.Add(item); - - if (parent != null) parent.Combine(item, user: null); + itemIDs.Add(item, item.ID); + + if (parent != null) + { + parentInventoryIDs.Add(item, parent.ID); + parent.Combine(item, user: null); + } foreach (XElement subElement in element.Elements()) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/SalvageMission.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/SalvageMission.cs index 2c3333212..7ded8cb67 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/SalvageMission.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/SalvageMission.cs @@ -103,6 +103,10 @@ namespace Barotrauma public override void Start(Level level) { +#if SERVER + originalItemID = Entity.NullEntityID; + originalInventoryID = Entity.NullEntityID; +#endif if (!IsClient) { //ruin/wreck items are allowed to spawn close to the sub @@ -147,6 +151,9 @@ namespace Barotrauma item.body.FarseerBody.BodyType = BodyType.Kinematic; item.FindHull(); } +#if SERVER + originalItemID = item.ID; +#endif for (int i = 0; i < statusEffects.Count; i++) { @@ -181,7 +188,13 @@ namespace Barotrauma } var itemContainer = it.GetComponent(); if (itemContainer == null) { continue; } - if (itemContainer.Combine(item, user: null)) { break; } // Placement successful + if (itemContainer.Combine(item, user: null)) + { +#if SERVER + originalInventoryID = it.ID; +#endif + break; + } // Placement successful } } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/ScriptedEventSet.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/ScriptedEventSet.cs index 33a357cc0..2fb9be6e9 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/ScriptedEventSet.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/ScriptedEventSet.cs @@ -1,13 +1,28 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; +using System.Runtime.InteropServices.ComTypes; using System.Xml.Linq; +using Barotrauma.Extensions; using Microsoft.Xna.Framework; namespace Barotrauma -{ +{ + class ScriptedEventSet { + internal class EventDebugStats + { + public readonly ScriptedEventSet RootSet; + public readonly Dictionary MonsterCounts = new Dictionary(); + + public EventDebugStats(ScriptedEventSet rootSet) + { + RootSet = rootSet; + } + } + public static List List { get; @@ -131,5 +146,115 @@ namespace Barotrauma } } } + + public static List GetDebugStatistics(int simulatedRoundCount = 100) + { + List debugLines = new List(); + + foreach (var eventSet in List) + { + List stats = new List(); + for (int i = 0; i < simulatedRoundCount; i++) + { + var newStats = new EventDebugStats(eventSet); + CheckEventSet(newStats, eventSet); + stats.Add(newStats); + } + debugLines.Add($"Event stats ({eventSet.DebugIdentifier}): "); + LogEventStats(stats, debugLines); + } + + for (int difficulty = 0; difficulty <= 100; difficulty += 10) + { + debugLines.Add($"Event stats on difficulty level {difficulty}: "); + List stats = new List(); + for (int i = 0; i < simulatedRoundCount; i++) + { + ScriptedEventSet selectedSet = List.Where(s => difficulty >= s.MinLevelDifficulty && difficulty <= s.MaxLevelDifficulty).GetRandom(); + if (selectedSet == null) { continue; } + var newStats = new EventDebugStats(selectedSet); + CheckEventSet(newStats, selectedSet); + stats.Add(newStats); + } + LogEventStats(stats, debugLines); + } + + return debugLines; + + static void CheckEventSet(EventDebugStats stats, ScriptedEventSet thisSet) + { + if (thisSet.ChooseRandom) + { + var eventPrefab = ToolBox.SelectWeightedRandom(thisSet.EventPrefabs, thisSet.EventPrefabs.Select(e => e.Commonness).ToList(), Rand.RandSync.Unsynced); + if (eventPrefab != null) + { + AddEvent(stats, eventPrefab); + } + } + else + { + foreach (var eventPrefab in thisSet.EventPrefabs) + { + AddEvent(stats, eventPrefab); + } + } + foreach (var childSet in thisSet.ChildSets) + { + CheckEventSet(stats, childSet); + } + } + + static void AddEvent(EventDebugStats stats, ScriptedEventPrefab eventPrefab) + { + if (eventPrefab.EventType == typeof(MonsterEvent)) + { + float spawnProbability = eventPrefab.ConfigElement.GetAttributeFloat("spawnprobability", 1.0f); + if (Rand.Value(Rand.RandSync.Server) > spawnProbability) + { + return; + } + + string character = eventPrefab.ConfigElement.GetAttributeString("characterfile", ""); + System.Diagnostics.Debug.Assert(!string.IsNullOrEmpty(character)); + int amount = eventPrefab.ConfigElement.GetAttributeInt("amount", 0); + int minAmount = eventPrefab.ConfigElement.GetAttributeInt("minamount", amount); + int maxAmount = eventPrefab.ConfigElement.GetAttributeInt("maxamount", amount); + + int count = Rand.Range(minAmount, maxAmount + 1); + if (count <= 0) { return; } + + if (!stats.MonsterCounts.ContainsKey(character)) { stats.MonsterCounts[character] = 0; } + stats.MonsterCounts[character] += count; + } + } + + static void LogEventStats(List stats, List debugLines) + { + if (stats.Count == 0 || stats.All(s => s.MonsterCounts.Values.Sum() == 0)) + { + debugLines.Add(" No monster spawns"); + debugLines.Add($" "); + } + else + { + stats.Sort((s1, s2) => { return s1.MonsterCounts.Values.Sum().CompareTo(s2.MonsterCounts.Values.Sum()); }); + + EventDebugStats minStats = stats.First(); + EventDebugStats maxStats = stats.First(); + debugLines.Add($" Minimum monster spawns: {stats.First().MonsterCounts.Values.Sum()}"); + debugLines.Add($" {LogMonsterCounts(stats.First())}"); + debugLines.Add($" Median monster spawns: {stats[stats.Count / 2].MonsterCounts.Values.Sum()}"); + debugLines.Add($" {LogMonsterCounts(stats[stats.Count / 2])}"); + debugLines.Add($" Maximum monster spawns: {stats.Last().MonsterCounts.Values.Sum()}"); + debugLines.Add($" {LogMonsterCounts(stats.Last())}"); + debugLines.Add($" "); + } + } + + static string LogMonsterCounts(EventDebugStats stats) + { + return string.Join(", ", stats.MonsterCounts.Select(mc => mc.Key + " x " + mc.Value)); + } + } } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/CharacterInventory.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/CharacterInventory.cs index 9d70d1f58..ed58a87c6 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/CharacterInventory.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/CharacterInventory.cs @@ -159,6 +159,15 @@ namespace Barotrauma return false; #endif } + if (item.Removed) + { +#if DEBUG + throw new Exception("Tried to put a removed item (" + item.Name + ") in an inventory"); +#else + DebugConsole.ThrowError("Tried to put a removed item (" + item.Name + ") in an inventory.\n" + Environment.StackTrace); + return false; +#endif + } bool inSuitableSlot = false; bool inWrongSlot = false; @@ -192,6 +201,9 @@ namespace Barotrauma int placedInSlot = -1; foreach (InvSlotType allowedSlot in allowedSlots) { + if (allowedSlot.HasFlag(InvSlotType.RightHand) && character.AnimController.GetLimb(LimbType.RightHand) == null) { continue; } + if (allowedSlot.HasFlag(InvSlotType.LeftHand) && character.AnimController.GetLimb(LimbType.LeftHand) == null) { continue; } + //check if all the required slots are free bool free = true; for (int i = 0; i < capacity; i++) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/DockingPort.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/DockingPort.cs index 5764c311c..7150c763e 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/DockingPort.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/DockingPort.cs @@ -205,18 +205,6 @@ namespace Barotrauma.Items.Components DockingDir = GetDir(DockingTarget); DockingTarget.DockingDir = -DockingDir; - if (door != null && DockingTarget.door != null) - { - WayPoint myWayPoint = WayPoint.WayPointList.Find(wp => door.LinkedGap == wp.ConnectedGap); - WayPoint targetWayPoint = WayPoint.WayPointList.Find(wp => DockingTarget.door.LinkedGap == wp.ConnectedGap); - - if (myWayPoint != null && targetWayPoint != null) - { - myWayPoint.linkedTo.Add(targetWayPoint); - targetWayPoint.linkedTo.Add(myWayPoint); - } - } - CreateJoint(false); #if SERVER @@ -283,6 +271,20 @@ namespace Barotrauma.Items.Components { CreateHulls(); } + + if (door != null && DockingTarget.door != null) + { + WayPoint myWayPoint = WayPoint.WayPointList.Find(wp => door.LinkedGap == wp.ConnectedGap); + WayPoint targetWayPoint = WayPoint.WayPointList.Find(wp => DockingTarget.door.LinkedGap == wp.ConnectedGap); + + if (myWayPoint != null && targetWayPoint != null) + { + myWayPoint.FindHull(); + myWayPoint.linkedTo.Add(targetWayPoint); + targetWayPoint.FindHull(); + targetWayPoint.linkedTo.Add(myWayPoint); + } + } } @@ -778,7 +780,9 @@ namespace Barotrauma.Items.Components if (myWayPoint != null && targetWayPoint != null) { + myWayPoint.FindHull(); myWayPoint.linkedTo.Remove(targetWayPoint); + targetWayPoint.FindHull(); targetWayPoint.linkedTo.Remove(myWayPoint); } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/Holdable.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/Holdable.cs index 3ea347165..c037406fb 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/Holdable.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/Holdable.cs @@ -331,8 +331,8 @@ namespace Barotrauma.Items.Components if (!item.body.Enabled) { - Limb rightHand = picker.AnimController.GetLimb(LimbType.RightHand); - item.SetTransform(rightHand.SimPosition, 0.0f); + Limb hand = picker.AnimController.GetLimb(LimbType.RightHand) ?? picker.AnimController.GetLimb(LimbType.LeftHand); + item.SetTransform(hand != null ? hand.SimPosition : character.SimPosition, 0.0f); } bool alreadyEquipped = character.HasEquippedItem(item); @@ -369,17 +369,19 @@ namespace Barotrauma.Items.Components IsActive = false; } - public bool CanBeAttached() + public bool CanBeAttached(Character user) { if (!attachable || !Reattachable) { return false; } //can be attached anywhere in sub editor if (Screen.Selected == GameMain.SubEditorScreen) { return true; } - //can be attached anywhere inside hulls - if (item.CurrentHull != null) { return true; } + Vector2 attachPos = user == null ? item.WorldPosition : GetAttachPosition(user, useWorldCoordinates: true); - return Structure.GetAttachTarget(item.WorldPosition) != null; + //can be attached anywhere inside hulls + if (item.CurrentHull != null && Submarine.RectContains(item.CurrentHull.WorldRect, attachPos)) { return true; } + + return Structure.GetAttachTarget(attachPos) != null; } public bool CanBeDeattached() @@ -396,8 +398,14 @@ namespace Barotrauma.Items.Components return false; } - //don't allow deattaching if part of a sub and outside hulls - return item.Submarine == null || item.CurrentHull != null; + if (item.CurrentHull == null) + { + return Structure.GetAttachTarget(item.WorldPosition) != null; + } + else + { + return true; + } } public override bool Pick(Character picker) @@ -505,7 +513,7 @@ namespace Barotrauma.Items.Components if (character != null) { if (!character.IsKeyDown(InputType.Aim)) { return false; } - if (!CanBeAttached()) { return false; } + if (!CanBeAttached(character)) { return false; } if (GameMain.NetworkMember != null) { @@ -534,7 +542,7 @@ namespace Barotrauma.Items.Components else { item.Drop(character); - item.SetTransform(ConvertUnits.ToSimUnits(GetAttachPosition(character)), 0.0f); + item.SetTransform(ConvertUnits.ToSimUnits(GetAttachPosition(character)), 0.0f, findNewHull: false); } } @@ -543,16 +551,18 @@ namespace Barotrauma.Items.Components return true; } - private Vector2 GetAttachPosition(Character user) + private Vector2 GetAttachPosition(Character user, bool useWorldCoordinates = false) { - if (user == null) { return item.Position; } + if (user == null) { return useWorldCoordinates ? item.WorldPosition : item.Position; } Vector2 mouseDiff = user.CursorWorldPosition - user.WorldPosition; mouseDiff = mouseDiff.ClampLength(MaxAttachDistance); + Vector2 userPos = useWorldCoordinates ? user.WorldPosition : user.Position; + return new Vector2( - MathUtils.RoundTowardsClosest(user.Position.X + mouseDiff.X, Submarine.GridSize.X), - MathUtils.RoundTowardsClosest(user.Position.Y + mouseDiff.Y, Submarine.GridSize.Y)); + MathUtils.RoundTowardsClosest(userPos.X + mouseDiff.X, Submarine.GridSize.X), + MathUtils.RoundTowardsClosest(userPos.Y + mouseDiff.Y, Submarine.GridSize.Y)); } public override void UpdateBroken(float deltaTime, Camera cam) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/Pickable.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/Pickable.cs index 72012a444..0684b67a6 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/Pickable.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/Pickable.cs @@ -114,10 +114,6 @@ namespace Barotrauma.Items.Components { activePicker = picker; picker.PickingItem = item; - - var leftHand = picker.AnimController.GetLimb(LimbType.LeftHand); - var rightHand = picker.AnimController.GetLimb(LimbType.RightHand); - pickTimer = 0.0f; while (pickTimer < requiredTime && Screen.Selected != GameMain.SubEditorScreen) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/RepairTool.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/RepairTool.cs index 3bf38223e..d1e714904 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/RepairTool.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/RepairTool.cs @@ -326,6 +326,18 @@ namespace Barotrauma.Items.Components { if (RepairThroughHoles && f.IsSensor && f.Body?.UserData is Structure) { return false; } if (f.Body?.UserData as string == "ruinroom") { return false; } + if (f.Body?.UserData is Item targetItem) + { + if (!HitItems) { return false; } + if (HitBrokenDoors) + { + if (targetItem.GetComponent() == null && targetItem.Condition <= 0) { return false; } + } + else + { + if (targetItem.Condition <= 0) { return false; } + } + } return f.Body?.UserData != null; }, allowInsideFixture: true)); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Engine.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Engine.cs index 540c6ec98..171d8ba6e 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Engine.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Engine.cs @@ -93,8 +93,8 @@ namespace Barotrauma.Items.Components controlLockTimer -= deltaTime; currPowerConsumption = Math.Abs(targetForce) / 100.0f * powerConsumption; - //pumps consume more power when in a bad condition - currPowerConsumption *= MathHelper.Lerp(1.5f, 1.0f, item.Condition / item.MaxCondition); + //engines consume more power when in a bad condition + item.GetComponent()?.AdjustPowerConsumption(ref currPowerConsumption); if (powerConsumption == 0.0f) { Voltage = 1.0f; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Fabricator.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Fabricator.cs index 746bca34a..6a74bc728 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Fabricator.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Fabricator.cs @@ -171,7 +171,7 @@ namespace Barotrauma.Items.Components outputContainer.Inventory.Locked = true; currPowerConsumption = powerConsumption; - currPowerConsumption *= MathHelper.Lerp(1.5f, 1.0f, item.Condition / item.MaxCondition); + item.GetComponent()?.AdjustPowerConsumption(ref currPowerConsumption); if (GameMain.NetworkMember?.IsServer ?? true) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/OxygenGenerator.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/OxygenGenerator.cs index b9f61f395..78bee5143 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/OxygenGenerator.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/OxygenGenerator.cs @@ -42,7 +42,7 @@ namespace Barotrauma.Items.Components CurrFlow = 0.0f; currPowerConsumption = powerConsumption; //consume more power when in a bad condition - currPowerConsumption *= MathHelper.Lerp(1.5f, 1.0f, item.Condition / item.MaxCondition); + item.GetComponent()?.AdjustPowerConsumption(ref currPowerConsumption); if (powerConsumption <= 0.0f) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Pump.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Pump.cs index de6db2cef..c183afb02 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Pump.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Pump.cs @@ -69,7 +69,7 @@ namespace Barotrauma.Items.Components currPowerConsumption = powerConsumption * Math.Abs(flowPercentage / 100.0f); //pumps consume more power when in a bad condition - currPowerConsumption *= MathHelper.Lerp(1.5f, 1.0f, item.Condition / item.MaxCondition); + item.GetComponent()?.AdjustPowerConsumption(ref currPowerConsumption); if (!HasPower) { return; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Steering.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Steering.cs index 0f382768f..ecf9b8a36 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Steering.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Steering.cs @@ -298,10 +298,7 @@ namespace Barotrauma.Items.Components { if (user != null && user.Info != null && user.SelectedConstruction == item) { - user.Info.IncreaseSkillLevel( - "helm", - SkillSettings.Current.SkillIncreasePerSecondWhenSteering / Math.Max(userSkill, 1.0f) * deltaTime, - user.WorldPosition + Vector2.UnitY * 150.0f); + IncreaseSkillLevel(user, deltaTime); } Vector2 velocityDiff = steeringInput - targetVelocity; @@ -330,6 +327,18 @@ namespace Barotrauma.Items.Components item.SendSignal(0, targetLevel.ToString(CultureInfo.InvariantCulture), "velocity_y_out", null); } + private void IncreaseSkillLevel(Character user, float deltaTime) + { + if (user?.Info == null) { return; } + + float userSkill = user.GetSkillLevel("helm") / 100.0f; + user.Info.IncreaseSkillLevel( + "helm", + SkillSettings.Current.SkillIncreasePerSecondWhenSteering / Math.Max(userSkill, 1.0f) * deltaTime, + user.WorldPosition + Vector2.UnitY * 150.0f); + + } + private void UpdateAutoPilot(float deltaTime) { if (controlledSub == null) { return; } @@ -565,6 +574,7 @@ namespace Barotrauma.Items.Components unsentChanges = true; AutoPilot = true; } + IncreaseSkillLevel(user, deltaTime); switch (objective.Option.ToLowerInvariant()) { case "maintainposition": diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Repairable.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Repairable.cs index ece3c0323..5cc9f2d32 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Repairable.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Repairable.cs @@ -55,8 +55,8 @@ namespace Barotrauma.Items.Components set; } - [Serialize(80.0f, true, description: "The condition of the item has to be below this for AI characters to repair it. Percentages of max condition."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f)] - public float AIRepairThreshold + [Serialize(80.0f, true, description: "The condition of the item has to be below this for it to become repairable. Percentages of max condition."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f)] + public float RepairThreshold { get; set; @@ -112,13 +112,14 @@ namespace Barotrauma.Items.Components element.GetAttributeString("name", ""); //backwards compatibility - var showRepairUIAttribute = element.Attributes().FirstOrDefault(a => a.Name.ToString().Equals("showrepairuithreshold", StringComparison.OrdinalIgnoreCase)); - if (showRepairUIAttribute != null) + var repairThresholdAttribute = + element.Attributes().FirstOrDefault(a => a.Name.ToString().Equals("showrepairuithreshold", StringComparison.OrdinalIgnoreCase)) ?? + element.Attributes().FirstOrDefault(a => a.Name.ToString().Equals("airepairth44reshold", StringComparison.OrdinalIgnoreCase)); + if (repairThresholdAttribute != null) { - float repairThreshold; - if (Single.TryParse(showRepairUIAttribute.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out repairThreshold)) + if (float.TryParse(repairThresholdAttribute.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out float repairThreshold)) { - AIRepairThreshold = repairThreshold; + RepairThreshold = repairThreshold; } } @@ -273,7 +274,7 @@ namespace Barotrauma.Items.Components float successFactor = requiredSkills.Count == 0 ? 1.0f : DegreeOfSuccess(CurrentFixer, requiredSkills); //item must have been below the repair threshold for the player to get an achievement or XP for repairing it - if (item.ConditionPercentage < AIRepairThreshold) + if (item.ConditionPercentage < RepairThreshold) { wasBroken = true; } @@ -357,6 +358,14 @@ namespace Barotrauma.Items.Components partial void UpdateProjSpecific(float deltaTime); + public void AdjustPowerConsumption(ref float powerConsumption) + { + if (item.ConditionPercentage < RepairThreshold) + { + powerConsumption *= MathHelper.Lerp(1.5f, 1.0f, item.Condition / item.MaxCondition); + } + } + private bool ShouldDeteriorate() { if (LastActiveTime > Timing.TotalTime) { return true; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/Wire.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/Wire.cs index fc0e86bb6..601c02216 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/Wire.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/Wire.cs @@ -182,8 +182,6 @@ namespace Barotrauma.Items.Components newConnection.Item.Position : newConnection.Item.Position - refSub.HiddenSubPosition; - nodePos = RoundNode(nodePos); - if (nodes.Count > 0 && nodes[0] == nodePos) { break; } if (nodes.Count > 1 && nodes[nodes.Count - 1] == nodePos) { break; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs index 2e24432e5..ec8ad9a57 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs @@ -2111,13 +2111,19 @@ namespace Barotrauma public void Equip(Character character) { - foreach (ItemComponent ic in components) ic.Equip(character); + if (Removed) + { + DebugConsole.ThrowError($"Tried to equip a removed item ({Name}).\n{Environment.StackTrace}"); + return; + } + + foreach (ItemComponent ic in components) { ic.Equip(character); } } public void Unequip(Character character) { character.DeselectItem(this); - foreach (ItemComponent ic in components) ic.Unequip(character); + foreach (ItemComponent ic in components) { ic.Unequip(character); } } public List> GetProperties() diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Explosion.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Explosion.cs index fedd4c35b..eb2e2c7ec 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Explosion.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Explosion.cs @@ -136,7 +136,7 @@ namespace Barotrauma if (powered == null || !powered.VulnerableToEMP) continue; if (item.Repairables.Any()) { - item.Condition -= 100 * EmpStrength * distFactor; + item.Condition -= item.MaxCondition * EmpStrength * distFactor; } //discharge batteries diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/ItemAssemblyPrefab.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/ItemAssemblyPrefab.cs index bdefa47e8..27fc88be1 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/ItemAssemblyPrefab.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/ItemAssemblyPrefab.cs @@ -10,11 +10,14 @@ namespace Barotrauma { partial class ItemAssemblyPrefab : MapEntityPrefab { - private string name; + private readonly string name; public override string Name { get { return name; } } public static readonly PrefabCollection Prefabs = new PrefabCollection(); + public static readonly string VanillaSaveFolder = Path.Combine("Content", "Items", "Assemblies"); + public static readonly string SaveFolder = "ItemAssemblies"; + private bool disposed = false; public override void Dispose() { @@ -144,11 +147,14 @@ namespace Barotrauma List itemAssemblyFiles = new List(); - //find assembly files in the item assembly folder - string directoryPath = Path.Combine("Content", "Items", "Assemblies"); - if (Directory.Exists(directoryPath)) + //find assembly files in the item assembly folders + if (Directory.Exists(VanillaSaveFolder)) { - itemAssemblyFiles.AddRange(Directory.GetFiles(directoryPath)); + itemAssemblyFiles.AddRange(Directory.GetFiles(VanillaSaveFolder)); + } + if (Directory.Exists(SaveFolder)) + { + itemAssemblyFiles.AddRange(Directory.GetFiles(SaveFolder)); } //find assembly files in selected content packages diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Structure.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Structure.cs index 05dd06837..271567653 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Structure.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Structure.cs @@ -174,7 +174,11 @@ namespace Barotrauma int newWidth = ResizeHorizontal ? rect.Width : (int)(defaultRect.Width * relativeScale); int newHeight = ResizeVertical ? rect.Height : (int)(defaultRect.Height * relativeScale); Rect = new Rectangle(rect.X, rect.Y, newWidth, newHeight); - if (Sections != null) + if (StairDirection != Direction.None) + { + CreateStairBodies(); + } + else if (Sections != null) { UpdateSections(); } @@ -431,6 +435,7 @@ namespace Barotrauma private void CreateStairBodies() { Bodies = new List(); + bodyDebugDimensions.Clear(); float stairAngle = MathHelper.ToRadians(Math.Min(Prefab.StairAngle, 75.0f)); @@ -448,7 +453,7 @@ namespace Barotrauma newBody.Friction = 0.8f; newBody.UserData = this; - newBody.Position = ConvertUnits.ToSimUnits(stairPos) + BodyOffset; + newBody.Position = ConvertUnits.ToSimUnits(stairPos) + BodyOffset * Scale; bodyDebugDimensions.Add(new Vector2(bodyWidth, bodyHeight)); @@ -575,12 +580,12 @@ namespace Barotrauma { foreach (MapEntity mapEntity in mapEntityList) { - if (!(mapEntity is Structure structure)) continue; - if (!structure.Prefab.AllowAttachItems) continue; - if (structure.Bodies != null && structure.Bodies.Count > 0) continue; + if (!(mapEntity is Structure structure)) { continue; } + if (!structure.Prefab.AllowAttachItems) { continue; } + if (structure.Bodies != null && structure.Bodies.Count > 0) { continue; } Rectangle worldRect = mapEntity.WorldRect; - if (worldPosition.X < worldRect.X || worldPosition.X > worldRect.Right) continue; - if (worldPosition.Y > worldRect.Y || worldPosition.Y < worldRect.Y - worldRect.Height) continue; + if (worldPosition.X < worldRect.X || worldPosition.X > worldRect.Right) { continue; } + if (worldPosition.Y > worldRect.Y || worldPosition.Y < worldRect.Y - worldRect.Height) { continue; } return structure; } return null; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Submarine.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Submarine.cs index 7f89167bc..82dd9643b 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Submarine.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Submarine.cs @@ -817,6 +817,9 @@ namespace Barotrauma Item.UpdateHulls(); Gap.UpdateHulls(); +#if CLIENT + Lights.ConvexHull.RecalculateAll(this); +#endif } public void Update(float deltaTime) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/SubmarineInfo.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/SubmarineInfo.cs index 951c830a5..e0a2279d8 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/SubmarineInfo.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/SubmarineInfo.cs @@ -372,10 +372,13 @@ namespace Barotrauma public bool SaveAs(string filePath, System.IO.MemoryStream previewImage=null) { var newElement = new XElement(SubmarineElement.Name, - SubmarineElement.Attributes().Where(a => !string.Equals(a.Name.LocalName, "previewimage", StringComparison.InvariantCultureIgnoreCase)), + SubmarineElement.Attributes().Where(a => !string.Equals(a.Name.LocalName, "previewimage", StringComparison.InvariantCultureIgnoreCase) && + !string.Equals(a.Name.LocalName, "name", StringComparison.InvariantCultureIgnoreCase)), SubmarineElement.Elements()); XDocument doc = new XDocument(newElement); + doc.Root.Add(new XAttribute("name", Name)); + if (previewImage != null) { doc.Root.Add(new XAttribute("previewimage", Convert.ToBase64String(previewImage.ToArray()))); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/WayPoint.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/WayPoint.cs index 7cf5bc934..a60cd2c67 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/WayPoint.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/WayPoint.cs @@ -594,6 +594,11 @@ namespace Barotrauma return assignedWayPoints; } + public void FindHull() + { + currentHull = Hull.FindHull(WorldPosition, CurrentHull); + } + public override void OnMapLoaded() { currentHull = Hull.FindHull(WorldPosition, currentHull); diff --git a/Barotrauma/BarotraumaShared/Submarines/Berilia.sub b/Barotrauma/BarotraumaShared/Submarines/Berilia.sub index f849543c7..cadb3a239 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Berilia.sub and b/Barotrauma/BarotraumaShared/Submarines/Berilia.sub differ diff --git a/Barotrauma/BarotraumaShared/changelog.txt b/Barotrauma/BarotraumaShared/changelog.txt index 23423019b..f2d0a58ff 100644 --- a/Barotrauma/BarotraumaShared/changelog.txt +++ b/Barotrauma/BarotraumaShared/changelog.txt @@ -1,31 +1,18 @@ --------------------------------------------------------------------------------------------------------- -v0.9.1001.0 (Unstable) ---------------------------------------------------------------------------------------------------------- - -- Overhauled level layouts and events (longer and more difficult levels). -- Added two new afflictions: medical items and poisons cause organ damage instead of internal damage and explosions cause deep tissue injuries. Both are functionally identical to internal damage, and treated with the same items. -- Always draw steering indicators at the center of the display instead of the center of the sub. Fixes indicators getting offset (sometimes even outside the display) during docking. -- Added option to give all command perms with the "givecommandperm" command by using "all" as the parameter. -- Non-raycast projectiles ignore destroyed items (= destroyed thalamus organs don't block harpoons). -- Fixed monsters staying invisible if they die far away from the camera view. -- Fixed very small limbs (mudraptor's mouth tentacles, husk appendages) launching off at a very high velocity, leading to glitchy physics behavior, when hit by a non-raycast projectile or an explosion. -- Allow subs to be saved to subdirectories of the "Submarines" folder (e.g. "Submarines/Downloaded"). -- Fixed item deterioration delay not resetting when the item is repaired if the condition of the item wasn't below the repair threshold when starting to repair it. -- Fixed bots failing to use welding tools inside their toolbox when fixing leaks. -- Items that appear broken to psychotic characters revert back to normal immediately when the psychosis is healed or when switching to another character. -- Fixed wires connected to a wifi component getting moved from signal_out to set_channel in old subs. -- Fixed plasma cutter beam not going through broken doors. -- Fixed extinguishers getting blocked by broken doors. -- The explosive cargo mission that places a block of Volatile Compound N in one of the crates no longer requires delivering the volatile block to the destination. -- Disabled crush depth in the submarine test mode. - ---------------------------------------------------------------------------------------------------------- -v0.9.1000.0 (Unstable) +v0.9.10.0 (Unstable) --------------------------------------------------------------------------------------------------------- Additions and changes: -- Added 2 new moloch variants: Black Moloch and Baby Moloch. +- Added 2 new moloch variants: Black Moloch and Moloch Pupa. +- Reworked Moloch. +- Overhauled level layouts and events (longer and more difficult levels). +- Added two new afflictions: medical items and poisons cause organ damage instead of internal damage and explosions cause deep tissue injuries. Both are functionally identical to internal damage, and treated with the same items. - Added DXT5 texture compression to reduce memory consumption. Slightly increases loading times; if you're not short on memory, you may want to disable the compression from the game settings. +- Added partial dismemberment for live creatures. Currently enabled only for non-humanoids. (Dismembering dead bodies was already in the game). +- Destructible shells/armor -> Moloch's shell can now be destroyed. +- Added a new monster AI state: Protect. +- Increased the threshold for limping and changed the calculations. +- Added limping for non-human characters. - Modded servers show up as purple in the server list. - Added 4 new background music tracks. - Added parameter autocompletion to the "spawnsub" command. @@ -58,12 +45,35 @@ Additions and changes: - Distance calculations on the navigation terminal take the shape of the path into account instead of just using the direct distance to the target. - Made improvements to the manual order assignment by adding always visible name labels, displaying indicators for characters' current orders, and repositioning the nodes. - Reduced the damage range of fires, characters don't take damage from fires if there's a closed door or a wall in between. +- Always draw steering indicators at the center of the display instead of the center of the sub. Fixes indicators getting offset (sometimes even outside the display) during docking. +- Added option to give all command perms with the "givecommandperm" command by using "all" as the parameter. +- Wrecks with no predefined Thalamus items can no more be infested by Thalamus. Allows to create wrecks that always spawn without Thalamus. +- The "shut down" reactor order now allows the bot to continue doing other things after powering off the reactor, instead of just standing next to the reactor. +- Removed the "initiative" skill -> all bots should now react better when there's something to do. Note: this doesn't mean that they always react on everything. It's just the end of individualism. At least for now. +- Reduced the physical forces applied on characters when they are hit by melee weapons, harpoons, or frag grenades. Adjusted stun for crowbar and harpoon. +- Disabled retreat/escape behavior for the bots when they take damage from items or explosions. They still escape/fight back when attacked by other characters. +- Refactor the medic priority calculations/logic: Bots should never treat others autonomously, unless they are medics or ordered to rescue. Bots should always give a high priority for treating themselves, unless there's a medic on board. +- Adjusted the flipping logic of non-humanoids to make them flip less frequently. +- Monsters won't anymore target nasonov artifact unless it is inside the player submarine or in the player inventory. Modding: - Made it possible to use repair tools with StatusEffect's UseItem. - Made pressure deaths more moddable. Dying because of high pressure isn't hard-coded anymore, the characters are just given the barotrauma affliction which (by default) kills them. - Added "HideConditionBar" property to items. - Fixed wearables staying on the character when the item is removed by a status effect. +- Character light sprites can now deform. The cells of Thalamus (Leucocyte and Terminal cell) now use the deformable light sprites. +- Creature flipping parameters are now exposed. Adjusted the flipping for all creatures. +- Allow to define character joints as weld joints in addition of normal revolute joints. Weld joints don't rotate. +- Allow sound definitions to ignore the muffling effect. +- Exposed the "scatter" value and added new "offset" attribute for monster events. +- Added support for status effects in limb definitions (ragdoll file). +- Status effects defined in the character definition can now also target limbs. +- Added ActionType.OnSevered status effect for limbs. +- Creatures can now be set to disrupt sonar. +- Player attacks can now also use the conditionals (i.e. when player is controlling a character). Previously only the AI used the conditionals. +- Conditional sprites don't anymore require a texture definition. +- Conditional Sprites can now be non-exclusive -> Draw more than just one sprite at a time. +- Fixed conditional sprites not being able to target limbs. Bugfixes: - Fixed crashing when opening the tab menu when there are clients present with no job preferences set. @@ -84,7 +94,7 @@ Bugfixes: - Fixed rare "item with the same key has already been added" errors when starting a round (particularly when playing with a submarine with very large numbers of items/structures). - Thalamus entities can't be selected in the sub editor when they're hidden. - Fixed "spawnsub" console command not working. -- Fixed welding tools and plasma cutters hitting destroyed Thalamus organs. +- Fixed projectiles, welding tools and plasma cutters hitting destroyed Thalamus organs. - Fixed reactor not shutting down if the turbine/fission rate are controlled via signals even the power switch is toggled off. - Fixed reactor sliders not moving when they're controlled by signals. - Fixed level triggers sometimes affecting entities that have left the trigger. The most noticeable effect was characters getting burn damage indefinitely after they've been close to a hydrothermal vent. @@ -102,7 +112,7 @@ Bugfixes: - Fixed depth charges going through level walls. - Fixed husks attacking human husks wearing a diving suit. - Fixed first shot from a firearm that uses a magazine/clip not doing anything. -- Fixes to waypoints in Kastrull and Remora. +- Fixes to waypoints in Kastrull, Berilia and Remora. - Fixed chat-linked wifi components not working in single player. - Fixed chat-linked wifi components not working in multiplayer outside of combat missions. - Fixed Azimuth using tutorial junction boxes instead of normal ones (the tutorial variants are indestructible and don't have signal connections). @@ -119,6 +129,23 @@ Bugfixes: - Fixed "attempting to remove an already removed item" errors when mass-deleting items. Happened because removing items a wire is connected to removes the orphaned wires automatically. - Fixed multi-part subs (example case: The Aeche III) getting teleported to oblivion when flipped. - Fixes to bots getting stuck or killed for no apparent reason when the player is very far from them, due to the bots switching to a "simple physics mode" which prevents them from doing certain kinds of interactions. +- The explosive cargo mission that places a block of Volatile Compound N in one of the crates no longer requires delivering the volatile block to the destination. +- Disabled crush depth in the submarine test mode. +- Fixed monsters staying invisible if they die far away from the camera view. +- Fixed very small limbs (mudraptor's mouth tentacles, husk appendages) launching off at a very high velocity, leading to glitchy physics behavior, when hit by a non-raycast projectile or an explosion. +- Allow subs to be saved to subdirectories of the "Submarines" folder (e.g. "Submarines/Downloaded"). +- Fixed a couple of waypoints in Berilia that prevented bots from using the ladders. +- Fixed Husked Crawler bleeding red blood. +- Fixed the priority of the operate order being 69 when it should be 70, which sometimes caused bots to get stuck between two objectives (like repairing or fixing leaks). +- Fixed enemies always using the priority defined for "room" when they should use the priority for "sonar". +- Fixed monsters not being able to drop down from platforms/hatches etc. In practice they still have difficulties in getting down from the hatches when they don't swim because they are so big. +- Fixed bots ignoring themselves as a target when they are rescuing others. +- Fixed numerous issues in the monster behavior when simple physics is enabled. e.g. Ignoring targets or not being able to attack or eat them. +- Fixed hitting damage modifiers emitting a ridiculous amount of particles. +- Fixed some cases where bots fail to open the door that they should be able to open (because they skipped a waypoint without checking the doors). +- Fixed monsters not keeping inside the level. +- Fixed a crash when the command interface button was pressed while the player is controlling a custom monster that didn't have character info but was able to speak. +- Fixed monsters sometimes ignoring their target after attacking. --------------------------------------------------------------------------------------------------------- v0.9.9.1 diff --git a/Libraries/Facepunch.Steamworks/SteamRemoteStorage.cs b/Libraries/Facepunch.Steamworks/SteamRemoteStorage.cs index d559ffa4d..a197fe919 100644 --- a/Libraries/Facepunch.Steamworks/SteamRemoteStorage.cs +++ b/Libraries/Facepunch.Steamworks/SteamRemoteStorage.cs @@ -160,21 +160,34 @@ namespace Steamworks /// public static int FileCount => Internal.GetFileCount(); - /// - /// Get a list of filenames synchronized by Steam Cloud - /// - public static IEnumerable Files + public struct RemoteFile { - get + public string Filename; + public int Size; + + public bool Delete() { - int _ = 0; - for( int i=0; i + /// Get a list of filenames synchronized by Steam Cloud + /// + public static List Files + { + get + { + var ret = new List(); + int count = FileCount; + for( int i=0; i SubmitAsync( IProgress progress = null ) { var result = default( PublishResult ); diff --git a/Libraries/MonoGame.Framework/Src/MonoGame.Framework/Graphics/SamplerStateCollection.DirectX.cs b/Libraries/MonoGame.Framework/Src/MonoGame.Framework/Graphics/SamplerStateCollection.DirectX.cs index 868b9cf77..bf869dfb2 100644 --- a/Libraries/MonoGame.Framework/Src/MonoGame.Framework/Graphics/SamplerStateCollection.DirectX.cs +++ b/Libraries/MonoGame.Framework/Src/MonoGame.Framework/Graphics/SamplerStateCollection.DirectX.cs @@ -8,8 +8,18 @@ namespace Microsoft.Xna.Framework.Graphics { public sealed partial class SamplerStateCollection { + private int _d3dMaxDirty; private int _d3dDirty; + partial void CalculateMaxDirty() + { + _d3dMaxDirty = 0; + for (var i = 0; i < _actualSamplers.Length; i++) + { + _d3dMaxDirty |= 1 << i; + } + } + private void PlatformSetSamplerState(int index) { _d3dDirty |= 1 << index; @@ -17,12 +27,12 @@ namespace Microsoft.Xna.Framework.Graphics private void PlatformClear() { - _d3dDirty = int.MaxValue; + _d3dDirty = _d3dMaxDirty; } private void PlatformDirty() { - _d3dDirty = int.MaxValue; + _d3dDirty = _d3dMaxDirty; } internal void PlatformSetSamplers(GraphicsDevice device) @@ -60,7 +70,8 @@ namespace Microsoft.Xna.Framework.Graphics break; } - _d3dDirty = 0; + if (_d3dDirty != 0) { throw new System.Exception($"SamplerStateCollection still dirty ({_d3dDirty})"); } + //_d3dDirty = 0; } } } diff --git a/Libraries/MonoGame.Framework/Src/MonoGame.Framework/Graphics/SamplerStateCollection.cs b/Libraries/MonoGame.Framework/Src/MonoGame.Framework/Graphics/SamplerStateCollection.cs index 20826622a..d90a5ab91 100644 --- a/Libraries/MonoGame.Framework/Src/MonoGame.Framework/Graphics/SamplerStateCollection.cs +++ b/Libraries/MonoGame.Framework/Src/MonoGame.Framework/Graphics/SamplerStateCollection.cs @@ -23,6 +23,8 @@ namespace Microsoft.Xna.Framework.Graphics private readonly SamplerState[] _actualSamplers; private readonly bool _applyToVertexStage; + partial void CalculateMaxDirty(); + internal SamplerStateCollection(GraphicsDevice device, int maxSamplers, bool applyToVertexStage) { _graphicsDevice = device; @@ -38,7 +40,8 @@ namespace Microsoft.Xna.Framework.Graphics _actualSamplers = new SamplerState[maxSamplers]; _applyToVertexStage = applyToVertexStage; - Clear(); + CalculateMaxDirty(); + Clear(); } public SamplerState this [int index] diff --git a/Libraries/MonoGame.Framework/Src/MonoGame.Framework/Graphics/TextureCollection.DirectX.cs b/Libraries/MonoGame.Framework/Src/MonoGame.Framework/Graphics/TextureCollection.DirectX.cs index 6cd68880c..ae0350aec 100644 --- a/Libraries/MonoGame.Framework/Src/MonoGame.Framework/Graphics/TextureCollection.DirectX.cs +++ b/Libraries/MonoGame.Framework/Src/MonoGame.Framework/Graphics/TextureCollection.DirectX.cs @@ -16,13 +16,15 @@ namespace Microsoft.Xna.Framework.Graphics return; if (_applyToVertexStage) - ClearTargets(targets, device._d3dContext.VertexShader); + ClearTargets(targets, device, device._d3dContext.VertexShader); else - ClearTargets(targets, device._d3dContext.PixelShader); + ClearTargets(targets, device, device._d3dContext.PixelShader); } - private void ClearTargets(RenderTargetBinding[] targets, SharpDX.Direct3D11.CommonShaderStage shaderStage) + private void ClearTargets(RenderTargetBinding[] targets, GraphicsDevice device, SharpDX.Direct3D11.CommonShaderStage shaderStage) { + PlatformSetTextures(device); + // NOTE: We make the assumption here that the caller has // locked the d3dContext for us to use. @@ -92,7 +94,8 @@ namespace Microsoft.Xna.Framework.Graphics break; } - _dirty = 0; + if (_dirty != 0) { throw new System.Exception($"TextureCollection still dirty ({_dirty})"); } + //_dirty = 0; } } } diff --git a/Libraries/MonoGame.Framework/Src/MonoGame.Framework/Graphics/TextureCollection.cs b/Libraries/MonoGame.Framework/Src/MonoGame.Framework/Graphics/TextureCollection.cs index 25aeab3dc..096c432de 100644 --- a/Libraries/MonoGame.Framework/Src/MonoGame.Framework/Graphics/TextureCollection.cs +++ b/Libraries/MonoGame.Framework/Src/MonoGame.Framework/Graphics/TextureCollection.cs @@ -12,13 +12,18 @@ namespace Microsoft.Xna.Framework.Graphics private readonly Texture[] _textures; private readonly bool _applyToVertexStage; private int _dirty; + private int _dirtyMax; internal TextureCollection(GraphicsDevice graphicsDevice, int maxTextures, bool applyToVertexStage) { _graphicsDevice = graphicsDevice; _textures = new Texture[maxTextures]; _applyToVertexStage = applyToVertexStage; - _dirty = int.MaxValue; + for (int i=0;i @@ -55,7 +60,7 @@ namespace Microsoft.Xna.Framework.Graphics /// internal void Dirty() { - _dirty = int.MaxValue; + _dirty = _dirtyMax; } internal void SetTextures(GraphicsDevice device)