diff --git a/Barotrauma/BarotraumaClient/LinuxClient.csproj.user b/Barotrauma/BarotraumaClient/LinuxClient.csproj.user deleted file mode 100644 index 944ec00e2..000000000 --- a/Barotrauma/BarotraumaClient/LinuxClient.csproj.user +++ /dev/null @@ -1,6 +0,0 @@ - - - - ShowAllFiles - - \ No newline at end of file diff --git a/Barotrauma/BarotraumaClient/MacClient.csproj b/Barotrauma/BarotraumaClient/MacClient.csproj index 181d1f112..d21001bd7 100644 --- a/Barotrauma/BarotraumaClient/MacClient.csproj +++ b/Barotrauma/BarotraumaClient/MacClient.csproj @@ -1,4 +1,4 @@ - + ReleaseMac @@ -265,24 +265,61 @@ PreserveNewest - - - - - - - - - - - - - - - - + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + Icon.bmp + + diff --git a/Barotrauma/BarotraumaClient/Properties/AssemblyInfo.cs b/Barotrauma/BarotraumaClient/Properties/AssemblyInfo.cs index a62ca56a0..82dea8e96 100644 --- a/Barotrauma/BarotraumaClient/Properties/AssemblyInfo.cs +++ b/Barotrauma/BarotraumaClient/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.9.0.7")] -[assembly: AssemblyFileVersion("0.9.0.7")] +[assembly: AssemblyVersion("0.9.1.0")] +[assembly: AssemblyFileVersion("0.9.1.0")] diff --git a/Barotrauma/BarotraumaClient/Source/Characters/Animation/HumanoidAnimParams.cs b/Barotrauma/BarotraumaClient/Source/Characters/Animation/HumanoidAnimParams.cs deleted file mode 100644 index 74de4a791..000000000 --- a/Barotrauma/BarotraumaClient/Source/Characters/Animation/HumanoidAnimParams.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using Microsoft.Xna.Framework.Input; -using System; -using System.Collections.Generic; -using System.Text; -using System.Xml; -using System.Xml.Linq; - -namespace Barotrauma -{ - partial class HumanoidAnimParams : ISerializableEntity - { - private static GUIListBox editor; - public static GUIListBox Editor - { - get - { - if (editor == null) - { - editor = new GUIListBox(new RectTransform(new Vector2(0.3f, 1), GUI.Canvas)); - //editor.AddChild(new SerializableEntityEditor(editor.RectTransform, WalkInstance, false, true, elementHeight: 20)); - //editor.AddChild(new SerializableEntityEditor(editor.RectTransform, RunInstance, false, true, elementHeight: 20)); - } - return editor; - } - } - -#if FALSE - //TODO: fix - public void Save() - { - XDocument doc = XMLExtensions.TryLoadXml(filePath); - if (doc == null || doc.Root == null) return; - - SerializableProperty.SerializeProperties(this, doc.Root, true); - - XmlWriterSettings settings = new XmlWriterSettings(); - settings.Indent = true; - settings.OmitXmlDeclaration = true; - settings.NewLineOnAttributes = true; - - using (var writer = XmlWriter.Create(filePath, settings)) - { - doc.WriteTo(writer); - writer.Flush(); - } - } -#endif - } -} diff --git a/Barotrauma/BarotraumaClient/Source/Characters/Character.cs b/Barotrauma/BarotraumaClient/Source/Characters/Character.cs index b281ff3bb..5c36aae9e 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/Character.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/Character.cs @@ -365,7 +365,7 @@ namespace Barotrauma { string chatMessage = CauseOfDeath.Type == CauseOfDeathType.Affliction ? CauseOfDeath.Affliction.SelfCauseOfDeathDescription : - TextManager.Get("Self_CauseOfDeathDescription." + CauseOfDeath.Type.ToString()); + TextManager.Get("Self_CauseOfDeathDescription." + CauseOfDeath.Type.ToString(), fallBackTag: "Self_CauseOfDeathDescription.Damage"); if (GameMain.Client != null) chatMessage += " " + TextManager.Get("DeathChatNotification"); diff --git a/Barotrauma/BarotraumaClient/Source/Characters/CharacterHUD.cs b/Barotrauma/BarotraumaClient/Source/Characters/CharacterHUD.cs index a38ec2a93..004c074a0 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/CharacterHUD.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/CharacterHUD.cs @@ -216,7 +216,7 @@ namespace Barotrauma Color.LightGreen, Color.Black, 2, GUI.SmallFont); textPos.Y += offset.Y; } - if (character.FocusedCharacter.CharacterHealth.UseHealthWindow) + if (character.FocusedCharacter.CharacterHealth.UseHealthWindow && character.CanInteractWith(character.FocusedCharacter, 160f, false)) { GUI.DrawString(spriteBatch, textPos, GetCachedHudText("HealHint", GameMain.Config.KeyBind(InputType.Health).ToString()), Color.LightGreen, Color.Black, 2, GUI.SmallFont); diff --git a/Barotrauma/BarotraumaClient/Source/Characters/CharacterHealth.cs b/Barotrauma/BarotraumaClient/Source/Characters/CharacterHealth.cs deleted file mode 100644 index becb82d8c..000000000 --- a/Barotrauma/BarotraumaClient/Source/Characters/CharacterHealth.cs +++ /dev/null @@ -1,1387 +0,0 @@ -using Barotrauma.Items.Components; -using Barotrauma.Networking; -using Lidgren.Network; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Xml.Linq; - -namespace Barotrauma -{ - partial class CharacterHealth - { - private static bool toggledThisFrame; - - private static Sprite damageOverlay; - - private static string[] strengthTexts; - - private GUIButton cprButton; - - private Point screenResolution; - - private float uiScale, inventoryScale; - - private Alignment alignment = Alignment.Left; - public Alignment Alignment - { - get { return alignment; } - set - { - if (alignment == value) return; - alignment = value; - UpdateAlignment(); - } - } - - private GUIButton suicideButton; - - // healthbars - private GUIProgressBar healthBar; - private GUIProgressBar healthBarShadow; - private GUIProgressBar healthWindowHealthBar; - private GUIProgressBar healthWindowHealthBarShadow; - private float healthShadowSize; - private float healthShadowDelay; - private float healthBarPulsateTimer; - private float healthBarPulsatePhase; - - private GUITextBlock characterName; - private GUIFrame afflictionInfoFrame; - private GUIListBox afflictionInfoContainer; - private GUIListBox recommendedTreatmentContainer; - - private float bloodParticleTimer; - - private GUIFrame healthWindow; - - private GUIComponent deadIndicator; - - private GUIComponent lowSkillIndicator; - - private SpriteSheet limbIndicatorOverlay; - private float limbIndicatorOverlayAnimState; - - private GUIFrame dropItemArea; - - private float dropItemAnimDuration = 0.5f; - private float dropItemAnimTimer; - - public Item DroppedItem - { - get - { - return droppedItem; - } - } - private Item droppedItem; - - private GUIComponent draggingMed; - - private int highlightedLimbIndex = -1; - private int selectedLimbIndex = -1; - - private float distortTimer; - - // 0-1 - private float damageIntensity; - private float damageIntensityDropdownRate = 0.1f; - - public float DamageOverlayTimer { get; private set; } - - private static CharacterHealth openHealthWindow; - public static CharacterHealth OpenHealthWindow - { - get - { - return openHealthWindow; - } - set - { - if (openHealthWindow == value) return; - if (value != null && !value.UseHealthWindow) return; - - openHealthWindow = value; - toggledThisFrame = true; - if (Character.Controlled == null) { return; } - - if (value == null && - Character.Controlled?.SelectedCharacter?.CharacterHealth != null && - Character.Controlled.SelectedCharacter.CharacterHealth == openHealthWindow && - !Character.Controlled.SelectedCharacter.CanInventoryBeAccessed) - { - Character.Controlled.DeselectCharacter(); - } - - Character.Controlled.ResetInteract = true; - if (openHealthWindow != null) - { - openHealthWindow.characterName.Text = value.Character.Name; - Character.Controlled.SelectedConstruction = null; - } - } - } - - static CharacterHealth() - { - damageOverlay = new Sprite("Content/UI/damageOverlay.png", Vector2.Zero); - } - - partial void InitProjSpecific(XElement element, Character character) - { - if (strengthTexts == null) - { - strengthTexts = new string[] - { - TextManager.Get("AfflictionStrengthLow"), - TextManager.Get("AfflictionStrengthMedium"), - TextManager.Get("AfflictionStrengthHigh") - }; - } - - character.OnAttacked += OnAttacked; - - bool horizontal = HUDLayoutSettings.HealthBarAreaLeft.Width > HUDLayoutSettings.HealthBarAreaLeft.Height; - healthBar = new GUIProgressBar(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.HealthBarAreaLeft, GUI.Canvas), - barSize: 1.0f, color: Color.Green, style: horizontal ? "GUIProgressBar" : "GUIProgressBarVertical") - { - IsHorizontal = horizontal - }; - healthBarShadow = new GUIProgressBar(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.HealthBarAreaLeft, GUI.Canvas), - barSize: 1.0f, color: Color.Green, style: horizontal ? "GUIProgressBar" : "GUIProgressBarVertical") - { - IsHorizontal = horizontal - }; - healthShadowSize = 1.0f; - - afflictionInfoFrame = new GUIFrame(new RectTransform(new Point(HUDLayoutSettings.HealthWindowAreaLeft.Width / 2, 200), GUI.Canvas)); - var paddedInfoFrame = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.9f), afflictionInfoFrame.RectTransform, Anchor.Center), style: null); - new GUITextBlock(new RectTransform(new Vector2(0.8f, 0.08f), paddedInfoFrame.RectTransform), "", font: GUI.LargeFont) - { - UserData = "selectedlimbname" - }; - - afflictionInfoContainer = new GUIListBox(new RectTransform(new Vector2(0.7f, 0.85f), paddedInfoFrame.RectTransform, Anchor.BottomLeft)); - - new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.08f), paddedInfoFrame.RectTransform), TextManager.Get("SuitableTreatments"), textAlignment: Alignment.TopRight); - lowSkillIndicator = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.07f), paddedInfoFrame.RectTransform, Anchor.TopRight) { RelativeOffset = new Vector2(0.0f, 0.08f) }, - TextManager.Get("LowMedicalSkillWarning"), Color.Orange, textAlignment: Alignment.Center, font: GUI.SmallFont, wrap: true) - { - Visible = false - }; - recommendedTreatmentContainer = new GUIListBox(new RectTransform(new Vector2(0.28f, 0.5f), paddedInfoFrame.RectTransform, Anchor.TopRight) { RelativeOffset = new Vector2(0.0f, 0.15f) }) - { - Spacing = 10 - }; - dropItemArea = new GUIFrame(new RectTransform(new Vector2(0.28f, 0.3f), paddedInfoFrame.RectTransform, Anchor.BottomRight) - { RelativeOffset = new Vector2(0.0f, 0.0f) }, style: null) - { - ToolTip = TextManager.Get("HealthItemUseTip") - }; - dropItemArea.RectTransform.NonScaledSize = new Point(dropItemArea.Rect.Width); - - string[] healthCircleStyles = new string[] { "HealthCircleInner", "HealthCircleMid", "HealthCircleOuter" }; - foreach (string healthCircleStyle in healthCircleStyles) - { - for (int i = 1; i < 4; i++) - { - var style = GUI.Style.GetComponentStyle(healthCircleStyle + i); - if (style != null) - { - new GUIImage(new RectTransform(Vector2.One, dropItemArea.RectTransform), healthCircleStyle + i) - { - CanBeFocused = false - }; - } - } - } - - new GUIImage(new RectTransform(Vector2.One * 0.2f, dropItemArea.RectTransform, Anchor.Center), "HealthCross") - { - CanBeFocused = false - }; - - healthWindow = new GUIFrame(new RectTransform(new Point(100, 200), GUI.Canvas)); - var paddedHealthWindow = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.9f), healthWindow.RectTransform, Anchor.Center)) - { - Stretch = true, - RelativeSpacing = 0.03f - }; - - var nameContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), paddedHealthWindow.RectTransform) { MinSize = new Point(0, 20) }, isHorizontal: true) - { - Stretch = true - }; - - characterName = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), nameContainer.RectTransform), "", textAlignment: Alignment.CenterLeft, font: GUI.LargeFont) - { - AutoScale = true - }; - new GUICustomComponent(new RectTransform(new Vector2(0.4f, 1.0f), nameContainer.RectTransform), - onDraw: (spriteBatch, component) => - { - character.Info.DrawPortrait(spriteBatch, new Vector2(component.Rect.X, component.Rect.Center.Y - component.Rect.Width / 2), component.Rect.Width); - }); - - new GUICustomComponent(new RectTransform(new Vector2(1.0f, 0.9f), paddedHealthWindow.RectTransform), - (spriteBatch, component) => - { - DrawHealthWindow(spriteBatch, component.RectTransform.Rect, true, false); - }, - (deltaTime, component) => - { - UpdateLimbIndicators(deltaTime, component.RectTransform.Rect); - } - ); - deadIndicator = new GUITextBlock(new RectTransform(new Vector2(0.9f, 0.1f), healthWindow.RectTransform, Anchor.Center), - text: TextManager.Get("Deceased"), font: GUI.LargeFont, textAlignment: Alignment.Center, wrap: true, style: "GUIToolTip") - { - Visible = false, - CanBeFocused = false - }; - - healthWindowHealthBar = new GUIProgressBar(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.HealthBarAreaLeft, GUI.Canvas), - barSize: 1.0f, color: Color.Green, style: "GUIProgressBarVertical") - { - IsHorizontal = false - }; - healthWindowHealthBarShadow = new GUIProgressBar(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.HealthBarAreaLeft, GUI.Canvas), - barSize: 1.0f, color: Color.Green, style: "GUIProgressBarVertical") - { - IsHorizontal = false - }; - cprButton = new GUIButton(new RectTransform(new Point(80, 80), GUI.Canvas), text: "", style: "CPRButton") - { - OnClicked = (button, userData) => - { - Character selectedCharacter = Character.Controlled?.SelectedCharacter; - if (selectedCharacter == null || (!selectedCharacter.IsUnconscious && selectedCharacter.Stun <= 0.0f)) return false; - - Character.Controlled.AnimController.Anim = (Character.Controlled.AnimController.Anim == AnimController.Animation.CPR) ? - AnimController.Animation.None : AnimController.Animation.CPR; - - selectedCharacter.AnimController.ResetPullJoints(); - - if (GameMain.Client != null) - { - GameMain.Client.CreateEntityEvent(Character.Controlled, new object[] { NetEntityEvent.Type.Treatment }); - } - - return true; - }, - Visible = false - }; - - UpdateAlignment(); - - suicideButton = new GUIButton(new RectTransform(new Vector2(0.06f, 0.02f), GUI.Canvas, Anchor.TopCenter) - { MinSize = new Point(120, 20), RelativeOffset = new Vector2(0.0f, 0.01f) }, - TextManager.Get("GiveInButton")) - { - ToolTip = TextManager.Get(GameMain.NetworkMember == null ? "GiveInHelpSingleplayer" : "GiveInHelpMultiplayer"), - OnClicked = (button, userData) => - { - GUI.ForceMouseOn(null); - if (Character.Controlled != null) - { - if (GameMain.Client != null) - { - GameMain.Client.CreateEntityEvent(Character.Controlled, new object[] { NetEntityEvent.Type.Status }); - } - else - { - var causeOfDeath = GetCauseOfDeath(); - Character.Controlled.Kill(causeOfDeath.First, causeOfDeath.Second); - Character.Controlled = null; - } - } - return true; - } - }; - - if (element != null) - { - foreach (XElement subElement in element.Elements()) - { - switch (subElement.Name.ToString().ToLowerInvariant()) - { - case "sprite": - limbIndicatorOverlay = new SpriteSheet(subElement); - break; - } - } - } - } - - private void OnAttacked(Character attacker, AttackResult attackResult) - { - if (Math.Abs(attackResult.Damage) < 0.01f && attackResult.Afflictions.Count == 0) return; - DamageOverlayTimer = MathHelper.Clamp(attackResult.Damage / MaxVitality, DamageOverlayTimer, 1.0f); - if (healthShadowDelay <= 0.0f) healthShadowDelay = 1.0f; - - if (healthBarPulsateTimer <= 0.0f) healthBarPulsatePhase = 0.0f; - healthBarPulsateTimer = 1.0f; - - float additionalIntensity = MathHelper.Lerp(0, 1, MathUtils.InverseLerp(0, 0.1f, attackResult.Damage / MaxVitality)); - damageIntensity = MathHelper.Clamp(damageIntensity + additionalIntensity, 0, 1); - } - - private void UpdateAlignment() - { - healthBar.RectTransform.RelativeOffset = healthBarShadow.RectTransform.RelativeOffset = Vector2.Zero; - healthWindowHealthBar.RectTransform.RelativeOffset = healthWindowHealthBarShadow.RectTransform.RelativeOffset = Vector2.Zero; - - int healthWindowHealthBarWidth = (int)(40 * GUI.Scale); - - if (alignment == Alignment.Left) - { - healthBar.RectTransform.SetPosition(Anchor.BottomLeft); - healthBarShadow.RectTransform.SetPosition(Anchor.BottomLeft); - healthBar.RectTransform.AbsoluteOffset = healthBarShadow.RectTransform.AbsoluteOffset = - new Point(HUDLayoutSettings.HealthBarAreaLeft.X, GameMain.GraphicsHeight - HUDLayoutSettings.HealthBarAreaLeft.Bottom); - healthBar.RectTransform.NonScaledSize = healthBarShadow.RectTransform.NonScaledSize = HUDLayoutSettings.HealthBarAreaLeft.Size; - - healthWindow.RectTransform.AbsoluteOffset = HUDLayoutSettings.HealthWindowAreaLeft.Location + new Point(healthWindowHealthBarWidth, 0); - healthWindow.RectTransform.NonScaledSize = new Point( - HUDLayoutSettings.HealthWindowAreaLeft.Width / 3 - healthWindowHealthBarWidth, - HUDLayoutSettings.HealthWindowAreaLeft.Height); - - afflictionInfoFrame.RectTransform.AbsoluteOffset = new Point( - healthWindow.Rect.Right, - HUDLayoutSettings.HealthWindowAreaLeft.Y); - afflictionInfoFrame.RectTransform.NonScaledSize = new Point( - (int)(HUDLayoutSettings.HealthWindowAreaLeft.Width * 0.66f), - (int)(HUDLayoutSettings.HealthWindowAreaLeft.Height)); - - healthWindowHealthBar.RectTransform.NonScaledSize = healthWindowHealthBarShadow.RectTransform.NonScaledSize = - new Point(healthWindowHealthBarWidth, healthWindow.Rect.Height); - healthWindowHealthBar.RectTransform.AbsoluteOffset = healthWindowHealthBarShadow.RectTransform.AbsoluteOffset = - HUDLayoutSettings.HealthWindowAreaLeft.Location; - - int cprButtonSize = (int)(100 * GUI.Scale); - cprButton.RectTransform.AbsoluteOffset = new Point(HUDLayoutSettings.HealthWindowAreaLeft.Right, dropItemArea.Rect.Center.Y - cprButtonSize / 2); - cprButton.RectTransform.NonScaledSize = new Point(cprButtonSize); - } - else - { - healthBar.RectTransform.SetPosition(Anchor.TopLeft); - healthBarShadow.RectTransform.SetPosition(Anchor.TopLeft); - healthBar.RectTransform.AbsoluteOffset = healthBarShadow.RectTransform.AbsoluteOffset = - HUDLayoutSettings.HealthBarAreaRight.Location; - healthBar.RectTransform.NonScaledSize = healthBarShadow.RectTransform.NonScaledSize = HUDLayoutSettings.HealthBarAreaRight.Size; - - healthWindow.RectTransform.AbsoluteOffset = new Point( - HUDLayoutSettings.HealthWindowAreaRight.X + HUDLayoutSettings.HealthWindowAreaRight.Width / 3 * 2, - HUDLayoutSettings.HealthWindowAreaRight.Y); - healthWindow.RectTransform.NonScaledSize = new Point( - HUDLayoutSettings.HealthWindowAreaRight.Width / 3 - healthWindowHealthBarWidth, - HUDLayoutSettings.HealthWindowAreaRight.Height); - - afflictionInfoFrame.RectTransform.AbsoluteOffset = new Point( - HUDLayoutSettings.HealthWindowAreaRight.X, - HUDLayoutSettings.HealthWindowAreaLeft.Y); - afflictionInfoFrame.RectTransform.NonScaledSize = new Point( - (int)(HUDLayoutSettings.HealthWindowAreaLeft.Width * 0.66f), - (int)(HUDLayoutSettings.HealthWindowAreaLeft.Height)); - - healthWindowHealthBar.RectTransform.NonScaledSize = healthWindowHealthBarShadow.RectTransform.NonScaledSize = - new Point(healthWindowHealthBarWidth, healthWindow.Rect.Height); - healthWindowHealthBar.RectTransform.AbsoluteOffset = healthWindowHealthBarShadow.RectTransform.AbsoluteOffset = - new Point(HUDLayoutSettings.HealthWindowAreaRight.Right - healthWindowHealthBarWidth, HUDLayoutSettings.HealthWindowAreaRight.Y); - - int cprButtonSize = (int)(100 * GUI.Scale); - cprButton.RectTransform.AbsoluteOffset = new Point(HUDLayoutSettings.HealthWindowAreaRight.X - cprButtonSize, dropItemArea.Rect.Center.Y - cprButtonSize / 2); - cprButton.RectTransform.NonScaledSize = new Point(cprButtonSize); - } - - dropItemArea.RectTransform.NonScaledSize = new Point(dropItemArea.Rect.Width); - - screenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight); - inventoryScale = Inventory.UIScale; - uiScale = GUI.Scale; - } - - partial void UpdateOxygenProjSpecific(float prevOxygen) - { - if (prevOxygen > 0.0f && OxygenAmount <= 0.0f && - Character.Controlled == Character) - { - SoundPlayer.PlaySound(Character.Info != null && Character.Info.Gender == Gender.Female ? "drownfemale" : "drownmale"); - } - } - - partial void UpdateBleedingProjSpecific(AfflictionBleeding affliction, Limb targetLimb, float deltaTime) - { - bloodParticleTimer -= deltaTime * (affliction.Strength / 10.0f); - if (bloodParticleTimer <= 0.0f) - { - float bloodParticleSize = MathHelper.Lerp(0.5f, 1.0f, affliction.Strength / 100.0f); - if (!Character.AnimController.InWater) bloodParticleSize *= 2.0f; - var blood = GameMain.ParticleManager.CreateParticle( - Character.AnimController.InWater ? "waterblood" : "blooddrop", - targetLimb.WorldPosition, Rand.Vector(affliction.Strength), 0.0f, Character.AnimController.CurrentHull); - - if (blood != null) - { - blood.Size *= bloodParticleSize; - } - bloodParticleTimer = 1.0f; - } - } - - public void UpdateHUD(float deltaTime) - { - if (GUI.DisableHUD) return; - if (openHealthWindow != null) - { - if (openHealthWindow != Character.Controlled?.CharacterHealth && openHealthWindow != Character.Controlled?.SelectedCharacter?.CharacterHealth) - { - openHealthWindow = null; - return; - } - } - - if (DamageOverlayTimer > 0.0f) - { - DamageOverlayTimer -= deltaTime; - } - if (damageIntensity > 0) - { - damageIntensity -= deltaTime * damageIntensityDropdownRate; - if (damageIntensity < 0) - { - damageIntensity = 0; - } - } - - if (healthShadowDelay > 0.0f) - { - healthShadowDelay -= deltaTime; - } - else - { - healthShadowSize = healthBar.BarSize > healthShadowSize ? - Math.Min(healthShadowSize + deltaTime, healthBar.BarSize) : - Math.Max(healthShadowSize - deltaTime, healthBar.BarSize); - } - - dropItemArea.Visible = !Character.IsDead; - - float blurStrength = 0.0f; - float distortStrength = 0.0f; - float distortSpeed = 0.0f; - float radialDistortStrength = 0.0f; - float chromaticAberrationStrength = 0.0f; - - if (Character.IsUnconscious) - { - blurStrength = 1.0f; - distortSpeed = 1.0f; - } - else if (OxygenAmount < 100.0f) - { - blurStrength = MathHelper.Lerp(0.5f, 1.0f, 1.0f - Vitality / MaxVitality); - distortStrength = blurStrength; - distortSpeed = (blurStrength + 1.0f); - distortSpeed *= distortSpeed * distortSpeed * distortSpeed; - } - - foreach (Affliction affliction in afflictions) - { - distortStrength = Math.Max(distortStrength, affliction.GetScreenDistortStrength()); - blurStrength = Math.Max(blurStrength, affliction.GetScreenBlurStrength()); - radialDistortStrength = Math.Max(radialDistortStrength, affliction.GetRadialDistortStrength()); - chromaticAberrationStrength = Math.Max(chromaticAberrationStrength, affliction.GetChromaticAberrationStrength()); - } - foreach (LimbHealth limbHealth in limbHealths) - { - foreach (Affliction affliction in limbHealth.Afflictions) - { - distortStrength = Math.Max(distortStrength, affliction.GetScreenDistortStrength()); - blurStrength = Math.Max(blurStrength, affliction.GetScreenBlurStrength()); - radialDistortStrength = Math.Max(radialDistortStrength, affliction.GetRadialDistortStrength()); - chromaticAberrationStrength = Math.Max(chromaticAberrationStrength, affliction.GetChromaticAberrationStrength()); - } - } - - Character.RadialDistortStrength = radialDistortStrength; - Character.ChromaticAberrationStrength = chromaticAberrationStrength; - if (blurStrength > 0.0f) - { - distortTimer = (distortTimer + deltaTime * distortSpeed) % MathHelper.TwoPi; - Character.BlurStrength = (float)(Math.Sin(distortTimer) + 1.5f) * 0.25f * blurStrength; - Character.DistortStrength = (float)(Math.Sin(distortTimer) + 1.0f) * 0.1f * distortStrength; - } - else - { - Character.BlurStrength = 0.0f; - Character.DistortStrength = 0.0f; - distortTimer = 0.0f; - } - - if (PlayerInput.KeyHit(InputType.Health) && GUI.KeyboardDispatcher.Subscriber == null && - Character.AllowInput && Character.FocusedCharacter == null && !toggledThisFrame) - { - if (openHealthWindow != null) - OpenHealthWindow = null; - else - OpenHealthWindow = this; - } - else if (openHealthWindow == this) - { - if (Alignment == Alignment.Right ? - HUD.CloseHUD(HUDLayoutSettings.HealthWindowAreaRight) : - HUD.CloseHUD(HUDLayoutSettings.HealthWindowAreaLeft)) - { - //emulate a Health input to get the character to deselect the item server-side - Character.Keys[(int)InputType.Health].Hit = true; - OpenHealthWindow = null; - } - } - toggledThisFrame = false; - - - if (Character.IsDead) - { - healthBar.Color = healthWindowHealthBar.Color = Color.Black; - healthBar.BarSize = healthWindowHealthBar.BarSize = 1.0f; - } - else - { - healthBar.Color = healthWindowHealthBar.Color = ToolBox.GradientLerp(Vitality / MaxVitality, Color.Red, Color.Orange, Color.Green); - healthBar.HoverColor = healthWindowHealthBar.HoverColor = healthBar.Color * 2.0f; - healthBar.BarSize = healthWindowHealthBar.BarSize = (Vitality > 0.0f) ? Vitality / MaxVitality : 1.0f - Vitality / MinVitality; - - if (healthBarPulsateTimer > 0.0f) - { - //0-1 - float pulsateAmount = (float)(Math.Sin(healthBarPulsatePhase) + 1.0f) / 2.0f; - - healthBar.RectTransform.LocalScale = healthBarShadow.RectTransform.LocalScale = new Vector2(1.0f, (1.0f + pulsateAmount * healthBarPulsateTimer * 0.5f)); - healthBarPulsatePhase += deltaTime * 5.0f; - healthBarPulsateTimer -= deltaTime; - } - else - { - healthBar.RectTransform.LocalScale = Vector2.One; - } - } - - if (OpenHealthWindow == this) - { - if (Character == Character.Controlled && !Character.AllowInput) - { - openHealthWindow = null; - } - - lowSkillIndicator.Visible = Timing.TotalTime % 1.0f < 0.8f && Character.Controlled != null && Character.Controlled.GetSkillLevel("medical") < 50.0f; - - float rotationSpeed = 0.25f; - int i = 0; - foreach (GUIComponent dropItemIndicator in dropItemArea.Children) - { - GUIImage img = dropItemIndicator as GUIImage; - if (img == null) continue; - - img.State = GUI.MouseOn == dropItemArea ? GUIComponent.ComponentState.Hover : GUIComponent.ComponentState.None; - - byte alpha = img.Color.A; - byte hoverAlpha = img.HoverColor.A; - img.Color = ToolBox.GradientLerp(Vitality / MaxVitality, Color.Red, Color.Orange, Color.Green); - img.Color = new Color(img.Color.R, img.Color.G, img.Color.B, alpha); - img.HoverColor = new Color(img.Color.R, img.Color.G, img.Color.B, hoverAlpha); - img.HoverColor = Color.Lerp(img.HoverColor, Color.White, 0.5f); - - if (img.State == GUIComponent.ComponentState.Hover && droppedItem == null) - { - dropItemAnimTimer = Math.Min(0.3f, dropItemAnimTimer + deltaTime * 0.5f); - } - - if (i < 4) - { - img.Scale = 1.0f - (float)Math.Sin(dropItemAnimTimer / dropItemAnimDuration * MathHelper.TwoPi) * 0.3f; - } - - if (dropItemIndicator == dropItemArea.Children.Last()) break; - img.Rotation = (img.Rotation + (rotationSpeed + dropItemAnimTimer * 10.0f) * deltaTime) % MathHelper.TwoPi; - rotationSpeed = (rotationSpeed + 0.3f) % 1.0f; - - i++; - } - - Rectangle limbArea = healthWindow.Children.First().Rect; - - var highlightedLimb = highlightedLimbIndex < 0 ? null : limbHealths[highlightedLimbIndex]; - if (highlightedLimb == null && selectedLimbIndex < 0) - { - // If no limb is selected or highlighted, select the one with the most critical afflictions. - var affliction = GetAllAfflictions(a => a.Prefab.IndicatorLimb != LimbType.None) - .OrderByDescending(a => a.DamagePerSecond) - .ThenByDescending(a => a.Strength).FirstOrDefault(); - var limbHealth = GetMathingLimbHealth(affliction); - if (limbHealth != null) - { - selectedLimbIndex = limbHealths.IndexOf(limbHealth); - } - } - UpdateAfflictionContainer(selectedLimbIndex < 0 ? highlightedLimb : limbHealths[selectedLimbIndex]); - - if (Inventory.draggingItem != null) - { - if (highlightedLimbIndex > -1) - { - selectedLimbIndex = highlightedLimbIndex; - } - } - - if (draggingMed != null) - { - if (!PlayerInput.LeftButtonHeld()) - { - OnItemDropped(draggingMed.UserData as Item, ignoreMousePos: false); - draggingMed = null; - } - } - - /*if (GUI.MouseOn?.UserData is Affliction affliction) - { - ShowAfflictionInfo(affliction, afflictionInfoContainer); - }*/ - - if (dropItemAnimTimer > 0.0f) - { - dropItemAnimTimer -= deltaTime; - if (dropItemAnimTimer <= 0.0f) droppedItem = null; - } - } - else - { - if (openHealthWindow != null && Character != Character.Controlled && Character != Character.Controlled?.SelectedCharacter) - { - openHealthWindow = null; - } - highlightedLimbIndex = -1; - } - - Rectangle hoverArea = alignment == Alignment.Left ? - Rectangle.Union(HUDLayoutSettings.AfflictionAreaLeft, HUDLayoutSettings.HealthBarAreaLeft) : - Rectangle.Union(HUDLayoutSettings.AfflictionAreaRight, HUDLayoutSettings.HealthBarAreaRight); - - if (Character.AllowInput && UseHealthWindow && hoverArea.Contains(PlayerInput.MousePosition) && Inventory.SelectedSlot == null) - { - healthBar.State = GUIComponent.ComponentState.Hover; - if (PlayerInput.LeftButtonClicked()) - { - OpenHealthWindow = openHealthWindow == this ? null : this; - } - } - else - { - healthBar.State = GUIComponent.ComponentState.None; - } - - suicideButton.Visible = Character == Character.Controlled && Character.IsUnconscious && !Character.IsDead; - - cprButton.Visible = - Character == Character.Controlled?.SelectedCharacter - && (Character.IsUnconscious || Character.Stun > 0.0f) - && !Character.IsDead - && openHealthWindow == this; - - deadIndicator.Visible = Character.IsDead; - } - - public void AddToGUIUpdateList() - { - if (GUI.DisableHUD) return; - if (OpenHealthWindow == this) - { - //afflictionContainer.AddToGUIUpdateList(); - afflictionInfoFrame.AddToGUIUpdateList(); - healthWindow.AddToGUIUpdateList(); - healthWindowHealthBarShadow.AddToGUIUpdateList(); - healthWindowHealthBar.AddToGUIUpdateList(); - } - else if (Character.Controlled == Character) - { - healthBarShadow.AddToGUIUpdateList(); - healthBar.AddToGUIUpdateList(); - } - if (suicideButton.Visible && Character == Character.Controlled) suicideButton.AddToGUIUpdateList(); - if (cprButton != null && cprButton.Visible) cprButton.AddToGUIUpdateList(); - } - - public void DrawHUD(SpriteBatch spriteBatch) - { - if (GUI.DisableHUD) return; - if (GameMain.GraphicsWidth != screenResolution.X || - GameMain.GraphicsHeight != screenResolution.Y || - Math.Abs(inventoryScale - Inventory.UIScale) > 0.01f || - Math.Abs(uiScale - GUI.Scale) > 0.01f) - { - UpdateAlignment(); - } - - float damageOverlayAlpha = DamageOverlayTimer; - if (Vitality < MaxVitality * 0.1f) - { - damageOverlayAlpha = Math.Max(1.0f - (Vitality / maxVitality * 10.0f), damageOverlayAlpha); - } - else - { - float pulsateAmount = (float)(Math.Sin(healthBarPulsatePhase) + 1.0f) / 2.0f; - damageOverlayAlpha = pulsateAmount * healthBarPulsateTimer * damageIntensity; - } - - if (damageOverlayAlpha > 0.0f) - { - damageOverlay.Draw(spriteBatch, Vector2.Zero, Color.White * damageOverlayAlpha, Vector2.Zero, 0.0f, - new Vector2(GameMain.GraphicsWidth / damageOverlay.size.X, GameMain.GraphicsHeight / damageOverlay.size.Y)); - } - - if (Character.Inventory != null) - { - if (Character.Inventory.CurrentLayout == CharacterInventory.Layout.Right) - { - //move the healthbar on top of the inventory slots - healthBar.RectTransform.ScreenSpaceOffset = new Point( - (GameMain.GraphicsWidth - HUDLayoutSettings.Padding) - HUDLayoutSettings.HealthBarAreaRight.Right, - HUDLayoutSettings.HealthBarAreaRight.Y - (int)(Character.Inventory.SlotPositions.Max(s => s.Y) + Inventory.EquipIndicator.size.Y * Inventory.UIScale * 2) - HUDLayoutSettings.HealthBarAreaRight.Height); - healthBarShadow.RectTransform.ScreenSpaceOffset = healthBar.RectTransform.ScreenSpaceOffset; - } - else - { - healthBar.RectTransform.ScreenSpaceOffset = healthBarShadow.RectTransform.ScreenSpaceOffset = Point.Zero; - } - } - - DrawStatusHUD(spriteBatch); - } - - public void DrawStatusHUD(SpriteBatch spriteBatch) - { - //Rectangle interactArea = healthBar.Rect; - if (openHealthWindow != this) - { - List> statusIcons = new List>(); - if (Character.CurrentHull == null || Character.CurrentHull.LethalPressure > 5.0f) - statusIcons.Add(new Pair(pressureAffliction, TextManager.Get("PressureHUDWarning"))); - if (Character.CurrentHull != null && Character.OxygenAvailable < LowOxygenThreshold && oxygenLowAffliction.Strength < oxygenLowAffliction.Prefab.ShowIconThreshold) - statusIcons.Add(new Pair(oxygenLowAffliction, TextManager.Get("OxygenHUDWarning"))); - - var allAfflictions = GetAllAfflictions(true); - foreach (Affliction affliction in allAfflictions) - { - if (affliction.Strength < affliction.Prefab.ShowIconThreshold || affliction.Prefab.Icon == null) continue; - statusIcons.Add(new Pair(affliction, affliction.Prefab.Name)); - } - - Pair highlightedIcon = null; - Vector2 highlightedIconPos = Vector2.Zero; - Rectangle afflictionArea = alignment == Alignment.Left ? HUDLayoutSettings.AfflictionAreaLeft : HUDLayoutSettings.AfflictionAreaRight; - Point pos = afflictionArea.Location + healthBar.RectTransform.ScreenSpaceOffset; - - bool horizontal = afflictionArea.Width > afflictionArea.Height; - int iconSize = horizontal ? afflictionArea.Height : afflictionArea.Width; - - foreach (Pair statusIcon in statusIcons) - { - Rectangle afflictionIconRect = new Rectangle(pos, new Point(iconSize)); - if (afflictionIconRect.Contains(PlayerInput.MousePosition)) - { - highlightedIcon = statusIcon; - highlightedIconPos = afflictionIconRect.Center.ToVector2(); - } - - if (statusIcon.First.DamagePerSecond > 1.0f) - { - Rectangle glowRect = afflictionIconRect; - glowRect.Inflate((int)(25 * GUI.Scale), (int)(25 * GUI.Scale)); - var glow = GUI.Style.GetComponentStyle("OuterGlow"); - glow.Sprites[GUIComponent.ComponentState.None][0].Draw( - spriteBatch, glowRect, - Color.Red * (float)((Math.Sin(statusIcon.First.DamagePerSecondTimer * MathHelper.TwoPi - MathHelper.PiOver2) + 1.0f) * 0.5f)); - } - - var slot = GUI.Style.GetComponentStyle("AfflictionIconSlot"); - slot.Sprites[highlightedIcon == statusIcon ? GUIComponent.ComponentState.Hover : GUIComponent.ComponentState.None][0].Draw( - spriteBatch, afflictionIconRect, - highlightedIcon == statusIcon ? slot.HoverColor : slot.Color); - - - statusIcon.First.Prefab.Icon?.Draw(spriteBatch, - pos.ToVector2(), - highlightedIcon == statusIcon ? statusIcon.First.Prefab.IconColor : statusIcon.First.Prefab.IconColor * 0.8f, - rotate: 0, - scale: iconSize / statusIcon.First.Prefab.Icon.size.X); - - if (horizontal) - pos.X += iconSize + (int)(5 * GUI.Scale); - else - pos.Y += iconSize + (int)(5 * GUI.Scale); - } - - if (highlightedIcon != null) - { - GUI.DrawString(spriteBatch, - alignment == Alignment.Left ? highlightedIconPos + new Vector2(60 * GUI.Scale, 5) : highlightedIconPos + new Vector2(-10.0f - GUI.Font.MeasureString(highlightedIcon.Second).X, 5), - highlightedIcon.Second, - Color.White * 0.8f, Color.Black * 0.5f); - } - - if (Vitality > 0.0f) - { - float currHealth = healthBar.BarSize; - Color prevColor = healthBar.Color; - healthBarShadow.BarSize = healthShadowSize; - healthBarShadow.Color = Color.Red; - healthBarShadow.Visible = true; - healthBar.BarSize = currHealth; - healthBar.Color = prevColor; - } - else - { - healthBarShadow.Visible = false; - } - } - else - { - if (Vitality > 0.0f) - { - float currHealth = healthWindowHealthBar.BarSize; - Color prevColor = healthWindowHealthBar.Color; - healthWindowHealthBarShadow.BarSize = healthShadowSize; - healthWindowHealthBarShadow.Color = Color.Red; - healthWindowHealthBarShadow.Visible = true; - healthWindowHealthBar.BarSize = currHealth; - healthWindowHealthBar.Color = prevColor; - } - else - { - healthWindowHealthBarShadow.Visible = false; - } - } - } - - private void UpdateAfflictionContainer(LimbHealth selectedLimb) - { - ((GUITextBlock)afflictionInfoContainer.Parent.GetChildByUserData("selectedlimbname")).Text = selectedLimb == null ? "" : selectedLimb.Name; - - if (selectedLimb == null) - { - afflictionInfoContainer.Content.ClearChildren(); - return; - } - var currentAfflictions = GetMatchingAfflictions(selectedLimb, a => a.Strength >= a.Prefab.ShowIconThreshold); - var displayedAfflictions = afflictionInfoContainer.Content.Children.Select(c => c.UserData as Affliction); - if (currentAfflictions.Any(a => !displayedAfflictions.Contains(a)) || - displayedAfflictions.Any(a => !currentAfflictions.Contains(a))) - { - CreateAfflictionInfos(currentAfflictions); - } - - UpdateAfflictionInfos(displayedAfflictions); - } - - private void CreateAfflictionInfos(IEnumerable afflictions) - { - afflictionInfoContainer.Content.ClearChildren(); - recommendedTreatmentContainer.Content.ClearChildren(); - - float characterSkillLevel = Character.Controlled == null ? 0.0f : Character.Controlled.GetSkillLevel("medical"); - - //random variance is 200% when the skill is 0 - //no random variance if the skill is 50 or more - float randomVariance = MathHelper.Lerp(2.0f, 0.0f, characterSkillLevel / 50.0f); - - //key = item identifier - //float = suitability - Dictionary treatmentSuitability = new Dictionary(); - float minSuitability = -10, maxSuitability = 10; - foreach (Affliction affliction in afflictions) - { - foreach (KeyValuePair treatment in affliction.Prefab.TreatmentSuitability) - { - if (!treatmentSuitability.ContainsKey(treatment.Key)) - { - treatmentSuitability[treatment.Key] = treatment.Value * affliction.Strength; - } - else - { - treatmentSuitability[treatment.Key] += treatment.Value * affliction.Strength; - } - minSuitability = Math.Min(treatmentSuitability[treatment.Key], minSuitability); - maxSuitability = Math.Max(treatmentSuitability[treatment.Key], maxSuitability); - } - } - //normalize the suitabilities to a range of 0 to 1 - foreach (string treatment in treatmentSuitability.Keys.ToList()) - { - treatmentSuitability[treatment] = (treatmentSuitability[treatment] - minSuitability) / (maxSuitability - minSuitability); - //lerp towards a random value if the medical skill is low - treatmentSuitability[treatment] = MathHelper.Lerp(treatmentSuitability[treatment], Rand.Range(0.0f, 1.0f), randomVariance); - } - - foreach (Affliction affliction in afflictions) - { - var child = new GUILayoutGroup(new RectTransform(Vector2.One * 0.95f, afflictionInfoContainer.Content.RectTransform, Anchor.TopCenter)) - { - Stretch = true, - RelativeSpacing = 0.02f, - UserData = affliction - }; - - var headerContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), child.RectTransform), isHorizontal: true) - { - Stretch = true, - UserData = "header" - }; - - new GUIImage(new RectTransform(new Vector2(0.15f, 1.0f), headerContainer.RectTransform), affliction.Prefab.Icon, scaleToFit: true) - { - Color = affliction.Prefab.IconColor - }; - - var labelContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 1.0f), headerContainer.RectTransform), isHorizontal: true) - { - Stretch = true, - AbsoluteSpacing = 10, - UserData = "label" - }; - var afflictionName = new GUITextBlock(new RectTransform(new Vector2(0.65f, 1.0f), labelContainer.RectTransform), affliction.Prefab.Name, textAlignment: Alignment.CenterLeft, font: GUI.LargeFont); - var afflictionStrength = new GUITextBlock(new RectTransform(new Vector2(0.35f, 0.6f), labelContainer.RectTransform), "", textAlignment: Alignment.TopRight, font: GUI.LargeFont) - { - Padding = Vector4.Zero, - UserData = "strength" - }; - var vitality = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.4f), labelContainer.RectTransform, Anchor.BottomRight), "", textAlignment: Alignment.BottomRight) - { - IgnoreLayoutGroups = true, - UserData = "vitality" - }; - - var description = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), child.RectTransform), - affliction.Prefab.Description, textAlignment: Alignment.TopLeft, wrap: true); - if (description.Font.MeasureString(description.WrappedText).Y > description.Rect.Height) - { - description.Font = GUI.SmallFont; - } - description.RectTransform.Resize(new Point(description.Rect.Width, (int)(description.TextSize.Y + 10))); - child.RectTransform.Resize(new Point(child.Rect.Width, child.Children.Sum(c => c.Rect.Height))); - child.Recalculate(); - afflictionStrength.AutoScale = true; - afflictionName.AutoScale = true; - vitality.AutoDraw = true; - } - - List> treatmentSuitabilities = treatmentSuitability.OrderByDescending(t => t.Value).ToList(); - - foreach (KeyValuePair treatment in treatmentSuitabilities) - { - ItemPrefab item = MapEntityPrefab.Find(name: null, identifier: treatment.Key, showErrorMessages: false) as ItemPrefab; - if (item == null) continue; - int slotSize = (int)(recommendedTreatmentContainer.Content.Rect.Width * 0.8f); - - var itemSlot = new GUIButton(new RectTransform(new Point(slotSize), recommendedTreatmentContainer.Content.RectTransform, Anchor.TopCenter), - text: "", style: "InventorySlotSmall") - { - UserData = item - }; - itemSlot.Color = ToolBox.GradientLerp(treatment.Value, Color.Red, Color.White, Color.LightGreen); - - Sprite itemSprite = item.InventoryIcon ?? item.sprite; - Color itemColor = itemSprite == item.sprite ? item.SpriteColor : item.InventoryIconColor; - var itemIcon = new GUIImage(new RectTransform(new Vector2(0.8f, 0.8f), itemSlot.RectTransform, Anchor.Center), - itemSprite, scaleToFit: true) - { - CanBeFocused = false, - Color = itemColor, - HoverColor = itemColor, - SelectedColor = itemColor - }; - itemSlot.ToolTip = item.Name + "\n" + item.Description; - } - - afflictionInfoContainer.Content.RectTransform.SortChildren((r1, r2) => - { - var first = r1.GUIComponent.UserData as Affliction; - var second = r2.GUIComponent.UserData as Affliction; - int dmgPerSecond = Math.Sign(second.DamagePerSecond - first.DamagePerSecond); - return dmgPerSecond != 0 ? dmgPerSecond : Math.Sign(second.Strength - first.Strength); - }); - - //afflictionInfoContainer.Content.RectTransform.SortChildren((r1, r2) => - //{ - // return Math.Sign(((Affliction)r2.GUIComponent.UserData).GetVitalityDecrease(this) - ((Affliction)r1.GUIComponent.UserData).GetVitalityDecrease(this)); - //}); - } - - private void UpdateAfflictionInfos(IEnumerable afflictions) - { - foreach (Affliction affliction in afflictions) - { - var child = afflictionInfoContainer.Content.FindChild(affliction); - var headerContainer = child.GetChildByUserData("header"); - var labelContainer = headerContainer.GetChildByUserData("label"); - var strengthText = labelContainer.GetChildByUserData("strength") as GUITextBlock; - - strengthText.Text = strengthTexts[ - MathHelper.Clamp((int)Math.Floor((affliction.Strength / affliction.Prefab.MaxStrength) * strengthTexts.Length), 0, strengthTexts.Length - 1)]; - - strengthText.TextColor = ToolBox.GradientLerp( - affliction.Strength / affliction.Prefab.MaxStrength, - Color.Yellow, Color.Orange, Color.Red); - - var vitalityText = labelContainer.GetChildByUserData("vitality") as GUITextBlock; - int vitalityDecrease = (int)affliction.GetVitalityDecrease(this); - if (vitalityDecrease == 0) - { - vitalityText.Visible = false; - } - else - { - vitalityText.Visible = true; - vitalityText.Text = TextManager.Get("Vitality") + " -" + vitalityDecrease; - vitalityText.TextColor = vitalityDecrease <= 0 ? Color.LightGreen : - Color.Lerp(Color.Orange, Color.Red, affliction.Strength / affliction.Prefab.MaxStrength); - } - } - } - - public bool OnItemDropped(Item item, bool ignoreMousePos) - { - //items can be dropped outside the health window - if (!ignoreMousePos && - !healthWindow.Rect.Contains(PlayerInput.MousePosition) && - !afflictionInfoFrame.Rect.Contains(PlayerInput.MousePosition)) - { - return false; - } - - //can't apply treatment to dead characters - if (Character.IsDead) return true; - if (item == null || !item.UseInHealthInterface) return true; - if (!ignoreMousePos) - { - if (highlightedLimbIndex > -1) - { - selectedLimbIndex = highlightedLimbIndex; - } - else if (!dropItemArea.Rect.Contains(PlayerInput.MousePosition)) - { - return true; - } - } - - Limb targetLimb = Character.AnimController.Limbs.FirstOrDefault(l => l.HealthIndex == selectedLimbIndex); - - item.ApplyTreatment(Character.Controlled, Character, targetLimb); - - dropItemAnimTimer = dropItemAnimDuration; - droppedItem = item; - return true; - } - - private List GetAvailableMedicalItems() - { - List allInventoryItems = new List(); - allInventoryItems.AddRange(Character.Inventory.Items); - if (Character.SelectedCharacter?.Inventory != null && Character.CanAccessInventory(Character.SelectedCharacter.Inventory)) - { - allInventoryItems.AddRange(Character.SelectedCharacter.Inventory.Items); - } - if (Character.SelectedBy?.Inventory != null) - { - allInventoryItems.AddRange(Character.SelectedBy.Inventory.Items); - } - - List medicalItems = new List(); - foreach (Item item in allInventoryItems) - { - if (item == null) continue; - - var containedItems = item.ContainedItems; - if (containedItems != null) - { - foreach (Item containedItem in containedItems) - { - if (containedItem == null) continue; - if (!containedItem.HasTag("medical") && !containedItem.HasTag("chem")) continue; - medicalItems.Add(containedItem); - } - } - - if (!item.HasTag("medical") && !item.HasTag("chem")) continue; - medicalItems.Add(item); - } - - return medicalItems.Distinct().ToList(); - } - - private void UpdateLimbIndicators(float deltaTime, Rectangle drawArea) - { - limbIndicatorOverlayAnimState += deltaTime * 8.0f; - - highlightedLimbIndex = -1; - int i = 0; - foreach (LimbHealth limbHealth in limbHealths) - { - if (limbHealth.IndicatorSprite == null) continue; - - float scale = Math.Min(drawArea.Width / (float)limbHealth.IndicatorSprite.SourceRect.Width, drawArea.Height / (float)limbHealth.IndicatorSprite.SourceRect.Height); - - Rectangle highlightArea = GetLimbHighlightArea(limbHealth, drawArea); - - if (highlightArea.Contains(PlayerInput.MousePosition)) - { - highlightedLimbIndex = i; - } - i++; - } - - if (PlayerInput.LeftButtonClicked() && highlightedLimbIndex > -1) - { - selectedLimbIndex = highlightedLimbIndex; - //afflictionContainer.ClearChildren(); - afflictionInfoContainer.ClearChildren(); - } - } - - private void DrawHealthWindow(SpriteBatch spriteBatch, Rectangle drawArea, bool allowHighlight, bool highlightAll) - { - if (Character.Removed) { return; } - - int i = 0; - foreach (LimbHealth limbHealth in limbHealths) - { - if (limbHealth.IndicatorSprite == null) continue; - - float damageLerp = limbHealth.TotalDamage > 0.0f ? MathHelper.Lerp(0.2f, 1.0f, limbHealth.TotalDamage / 100.0f) : 0.0f; - Color color = Character.IsDead ? - Color.Lerp(Color.Black, new Color(150, 100, 100), damageLerp) : - ToolBox.GradientLerp(damageLerp, Color.Green, Color.Orange, Color.Red); - float scale = Math.Min(drawArea.Width / (float)limbHealth.IndicatorSprite.SourceRect.Width, drawArea.Height / (float)limbHealth.IndicatorSprite.SourceRect.Height); - - if (((i == highlightedLimbIndex || i == selectedLimbIndex) && allowHighlight) || highlightAll) - { - color = Color.Lerp(color, Color.White, 0.5f); - } - - limbHealth.IndicatorSprite.Draw(spriteBatch, - drawArea.Center.ToVector2(), color, - limbHealth.IndicatorSprite.Origin, - 0, scale); - i++; - } - - spriteBatch.End(); - spriteBatch.Begin(SpriteSortMode.Deferred, Lights.CustomBlendStates.Multiplicative); - - float overlayScale = Math.Min( - drawArea.Width / (float)limbIndicatorOverlay.FrameSize.X, - drawArea.Height / (float)limbIndicatorOverlay.FrameSize.Y); - - int frame = 0; - int frameCount = 17; - if (limbIndicatorOverlayAnimState >= frameCount * 2) limbIndicatorOverlayAnimState = 0.0f; - if (limbIndicatorOverlayAnimState < frameCount) - { - frame = (int)limbIndicatorOverlayAnimState; - } - else - { - frame = frameCount - (int)(limbIndicatorOverlayAnimState - (frameCount - 1)); - } - - limbIndicatorOverlay.Draw(spriteBatch, frame, drawArea.Center.ToVector2(), Color.Gray, origin: limbIndicatorOverlay.FrameSize.ToVector2() / 2, rotate: 0.0f, - scale: Vector2.One * overlayScale); - - spriteBatch.End(); - spriteBatch.Begin(SpriteSortMode.Deferred, blendState: BlendState.AlphaBlend, rasterizerState: GameMain.ScissorTestEnable); - - i = 0; - foreach (LimbHealth limbHealth in limbHealths) - { - if (limbHealth.IndicatorSprite == null) continue; - float scale = Math.Min(drawArea.Width / (float)limbHealth.IndicatorSprite.SourceRect.Width, drawArea.Height / (float)limbHealth.IndicatorSprite.SourceRect.Height); - - Rectangle highlightArea = new Rectangle( - (int)(drawArea.Center.X - (limbHealth.IndicatorSprite.Texture.Width / 2 - limbHealth.HighlightArea.X) * scale), - (int)(drawArea.Center.Y - (limbHealth.IndicatorSprite.Texture.Height / 2 - limbHealth.HighlightArea.Y) * scale), - (int)(limbHealth.HighlightArea.Width * scale), - (int)(limbHealth.HighlightArea.Height * scale)); - - if (selectedLimbIndex == i) - { - if (alignment == Alignment.Left) - { - GUI.DrawLine(spriteBatch, - highlightArea.Center.ToVector2(), - afflictionInfoContainer.Parent.Rect.Location.ToVector2() + Vector2.UnitY * 20, - Color.LightBlue * 0.3f, 0, 4); - } - else - { - GUI.DrawLine(spriteBatch, - highlightArea.Center.ToVector2(), - new Vector2(afflictionInfoContainer.Parent.Rect.Right, afflictionInfoContainer.Parent.Rect.Y + 20), - Color.LightBlue * 0.3f, 0, 4); - } - } - - var slot = GUI.Style.GetComponentStyle("AfflictionIconSlot"); - - float iconScale = 0.3f * scale; - Vector2 iconPos = highlightArea.Center.ToVector2(); - foreach (Affliction affliction in limbHealth.Afflictions) - { - DrawLimbAfflictionIcon(spriteBatch, affliction, slot, iconScale, ref iconPos); - } - - foreach (Affliction affliction in afflictions) - { - Limb indicatorLimb = Character.AnimController.GetLimb(affliction.Prefab.IndicatorLimb); - if (indicatorLimb != null && indicatorLimb.HealthIndex == i) - { - DrawLimbAfflictionIcon(spriteBatch, affliction, slot, iconScale, ref iconPos); - } - } - i++; - } - - if (draggingMed != null) - { - GUIImage itemImage = draggingMed.GetChild(); - float scale = Math.Min(40.0f / itemImage.Sprite.size.X, 40.0f / itemImage.Sprite.size.Y); - itemImage.Sprite.Draw(spriteBatch, PlayerInput.MousePosition, itemImage.Color, 0, scale); - } - - if (dropItemAnimTimer > 0.0f && droppedItem?.Prefab.InventoryIcon != null) - { - var droppedItemSprite = droppedItem.Prefab.InventoryIcon ?? droppedItem.Sprite; - droppedItemSprite.Draw(spriteBatch, dropItemArea.Rect.Center.ToVector2(), - droppedItemSprite == droppedItem.Sprite ? droppedItem.GetSpriteColor() : droppedItem.GetInventoryIconColor(), - origin: droppedItemSprite.size / 2, - scale: MathHelper.SmoothStep(0.0f, 100.0f / droppedItemSprite.size.Length(), dropItemAnimTimer / dropItemAnimDuration)); - } - } - - private void DrawLimbAfflictionIcon(SpriteBatch spriteBatch, Affliction affliction, GUIComponentStyle slotStyle, float iconScale, ref Vector2 iconPos) - { - if (affliction.Strength < affliction.Prefab.ShowIconThreshold) return; - Vector2 iconSize = (affliction.Prefab.Icon.size * iconScale); - - //afflictions that have a strength of less than 10 are faded out slightly - float alpha = MathHelper.Lerp(0.3f, 1.0f, - (affliction.Strength - affliction.Prefab.ShowIconThreshold) / Math.Min(affliction.Prefab.MaxStrength - affliction.Prefab.ShowIconThreshold, 10.0f)); - - slotStyle.Sprites[GUIComponent.ComponentState.None][0].Draw( - spriteBatch, - new Rectangle((iconPos - iconSize / 2.0f).ToPoint(), iconSize.ToPoint()), - slotStyle.Color * alpha); - affliction.Prefab.Icon.Draw(spriteBatch, iconPos - iconSize / 2.0f, affliction.Prefab.IconColor * alpha, 0, iconScale); - iconPos += new Vector2(10.0f, 20.0f) * iconScale; - } - - private Rectangle GetLimbHighlightArea(LimbHealth limbHealth, Rectangle drawArea) - { - float scale = Math.Min(drawArea.Width / (float)limbHealth.IndicatorSprite.SourceRect.Width, drawArea.Height / (float)limbHealth.IndicatorSprite.SourceRect.Height); - return new Rectangle( - (int)(drawArea.Center.X - (limbHealth.IndicatorSprite.Texture.Width / 2 - limbHealth.HighlightArea.X) * scale), - (int)(drawArea.Center.Y - (limbHealth.IndicatorSprite.Texture.Height / 2 - limbHealth.HighlightArea.Y) * scale), - (int)(limbHealth.HighlightArea.Width * scale), - (int)(limbHealth.HighlightArea.Height * scale)); - } - - public void ClientRead(NetBuffer inc) - { - List> newAfflictions = new List>(); - - byte afflictionCount = inc.ReadByte(); - for (int i = 0; i < afflictionCount; i++) - { - AfflictionPrefab afflictionPrefab = AfflictionPrefab.List[inc.ReadRangedInteger(0, AfflictionPrefab.List.Count - 1)]; - float afflictionStrength = inc.ReadRangedSingle(0.0f, afflictionPrefab.MaxStrength, 8); - - newAfflictions.Add(new Pair(afflictionPrefab, afflictionStrength)); - } - - foreach (Affliction affliction in afflictions) - { - //deactivate afflictions that weren't included in the network message - if (!newAfflictions.Any(a => a.First == affliction.Prefab)) - { - affliction.Strength = 0.0f; - } - } - - foreach (Pair newAffliction in newAfflictions) - { - Affliction existingAffliction = afflictions.Find(a => a.Prefab == newAffliction.First); - if (existingAffliction == null) - { - afflictions.Add(newAffliction.First.Instantiate(newAffliction.Second)); - } - else - { - existingAffliction.Strength = newAffliction.Second; - if (existingAffliction == stunAffliction) Character.SetStun(existingAffliction.Strength, true, true); - } - } - - List> newLimbAfflictions = new List>(); - byte limbAfflictionCount = inc.ReadByte(); - for (int i = 0; i < limbAfflictionCount; i++) - { - int limbIndex = inc.ReadRangedInteger(0, limbHealths.Count - 1); - AfflictionPrefab afflictionPrefab = AfflictionPrefab.List[inc.ReadRangedInteger(0, AfflictionPrefab.List.Count - 1)]; - float afflictionStrength = inc.ReadRangedSingle(0.0f, afflictionPrefab.MaxStrength, 8); - - newLimbAfflictions.Add(new Triplet(limbHealths[limbIndex], afflictionPrefab, afflictionStrength)); - } - - foreach (LimbHealth limbHealth in limbHealths) - { - foreach (Affliction affliction in limbHealth.Afflictions) - { - //deactivate afflictions that weren't included in the network message - if (!newLimbAfflictions.Any(a => a.First == limbHealth && a.Second == affliction.Prefab)) - { - affliction.Strength = 0.0f; - } - } - - foreach (Triplet newAffliction in newLimbAfflictions) - { - if (newAffliction.First != limbHealth) continue; - Affliction existingAffliction = limbHealth.Afflictions.Find(a => a.Prefab == newAffliction.Second); - if (existingAffliction == null) - { - limbHealth.Afflictions.Add(newAffliction.Second.Instantiate(newAffliction.Third)); - } - else - { - existingAffliction.Strength = newAffliction.Third; - } - } - } - } - - partial void RemoveProjSpecific() - { - foreach (LimbHealth limbHealth in limbHealths) - { - if (limbHealth.IndicatorSprite != null) - { - limbHealth.IndicatorSprite.Remove(); - limbHealth.IndicatorSprite = null; - } - } - - limbIndicatorOverlay?.Remove(); - limbIndicatorOverlay = null; - } - } -} diff --git a/Barotrauma/BarotraumaClient/Source/Characters/Limb.cs b/Barotrauma/BarotraumaClient/Source/Characters/Limb.cs index a3d57f795..846e82d39 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/Limb.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/Limb.cs @@ -449,12 +449,11 @@ namespace Barotrauma { float depth = ActiveSprite.Depth - 0.0000015f; - // TODO: enable when the damage overlay textures have been remade. - //DamagedSprite.Draw(spriteBatch, - // new Vector2(body.DrawPosition.X, -body.DrawPosition.Y), - // color * Math.Min(damageOverlayStrength, 1.0f), ActiveSprite.Origin, - // -body.DrawRotation, - // 1.0f, spriteEffect, depth); + DamagedSprite.Draw(spriteBatch, + new Vector2(body.DrawPosition.X, -body.DrawPosition.Y), + color * Math.Min(damageOverlayStrength, 1.0f), ActiveSprite.Origin, + -body.DrawRotation, + 1.0f, spriteEffect, depth); } if (GameMain.DebugDraw) diff --git a/Barotrauma/BarotraumaClient/Source/DebugConsole.cs b/Barotrauma/BarotraumaClient/Source/DebugConsole.cs index 88f7a7f29..17fc3b0f6 100644 --- a/Barotrauma/BarotraumaClient/Source/DebugConsole.cs +++ b/Barotrauma/BarotraumaClient/Source/DebugConsole.cs @@ -206,6 +206,7 @@ namespace Barotrauma case "togglehud": case "toggleupperhud": case "togglecharacternames": + case "fpscounter": return true; default: return client.HasConsoleCommandPermission(command); @@ -1509,7 +1510,7 @@ namespace Barotrauma return; } RagdollParams ragdollParams = character.AnimController.RagdollParams; - ragdollParams.LimbScale = value; + ragdollParams.LimbScale = MathHelper.Clamp(value, RagdollParams.MIN_SCALE, RagdollParams.MAX_SCALE); var pos = character.WorldPosition; character.AnimController.Recreate(); character.TeleportTo(pos); @@ -1534,7 +1535,7 @@ namespace Barotrauma return; } RagdollParams ragdollParams = character.AnimController.RagdollParams; - ragdollParams.JointScale = value; + ragdollParams.JointScale = MathHelper.Clamp(value, RagdollParams.MIN_SCALE, RagdollParams.MAX_SCALE); var pos = character.WorldPosition; character.AnimController.Recreate(); character.TeleportTo(pos); @@ -1559,8 +1560,8 @@ namespace Barotrauma return; } RagdollParams ragdollParams = character.AnimController.RagdollParams; - ragdollParams.LimbScale = value; - ragdollParams.JointScale = value; + ragdollParams.LimbScale = MathHelper.Clamp(value, RagdollParams.MIN_SCALE, RagdollParams.MAX_SCALE); + ragdollParams.JointScale = MathHelper.Clamp(value, RagdollParams.MIN_SCALE, RagdollParams.MAX_SCALE); var pos = character.WorldPosition; character.AnimController.Recreate(); character.TeleportTo(pos); diff --git a/Barotrauma/BarotraumaClient/Source/GUI/GUI.cs b/Barotrauma/BarotraumaClient/Source/GUI/GUI.cs index f5d362374..3418d020e 100644 --- a/Barotrauma/BarotraumaClient/Source/GUI/GUI.cs +++ b/Barotrauma/BarotraumaClient/Source/GUI/GUI.cs @@ -1394,8 +1394,8 @@ namespace Barotrauma Vector2 moveAmount = centerDiff == Point.Zero ? Rand.Vector(1.0f) : Vector2.Normalize(centerDiff.ToVector2()); //make sure we don't move the interfaces out of the screen - Vector2 moveAmount1 = ClampMoveAmount(rect1, area, moveAmount * 10.0f * rect1Area / (rect1Area + rect2Area)); - Vector2 moveAmount2 = ClampMoveAmount(rect2, area, -moveAmount * 10.0f * rect1Area / (rect1Area + rect2Area)); + Vector2 moveAmount1 = ClampMoveAmount(rect1, area, moveAmount * 20.0f * rect1Area / (rect1Area + rect2Area)); + Vector2 moveAmount2 = ClampMoveAmount(rect2, area, -moveAmount * 20.0f * rect1Area / (rect1Area + rect2Area)); //move by 10 units in the desired direction and repeat until nothing overlaps //(or after 100 iterations, in which case we'll just give up and let them overlap) diff --git a/Barotrauma/BarotraumaClient/Source/GUI/GUIDropDown.cs b/Barotrauma/BarotraumaClient/Source/GUI/GUIDropDown.cs index 6cdba8652..c2f8351bc 100644 --- a/Barotrauma/BarotraumaClient/Source/GUI/GUIDropDown.cs +++ b/Barotrauma/BarotraumaClient/Source/GUI/GUIDropDown.cs @@ -148,7 +148,14 @@ namespace Barotrauma //find the parent GUIListBox highest in the hierarchy for (int i = parentHierarchy.Count - 1; i >= 0; i--) { - if (parentHierarchy[i].GUIComponent is GUIListBox) return parentHierarchy[i]; + if (parentHierarchy[i].GUIComponent is GUIListBox) + { + if (parentHierarchy[i].Parent != null && parentHierarchy[i].Parent.GUIComponent != null) + { + return parentHierarchy[i].Parent; + } + return parentHierarchy[i]; + } } //or just go with the direct parent if there are no listboxes in the hierarchy parentHierarchy.Clear(); diff --git a/Barotrauma/BarotraumaClient/Source/GUI/GUIImage.cs b/Barotrauma/BarotraumaClient/Source/GUI/GUIImage.cs index 27743748b..9817060fd 100644 --- a/Barotrauma/BarotraumaClient/Source/GUI/GUIImage.cs +++ b/Barotrauma/BarotraumaClient/Source/GUI/GUIImage.cs @@ -57,8 +57,8 @@ namespace Barotrauma } } - public GUIImage(RectTransform rectT, string style) - : this(rectT, null, null, false, style) + public GUIImage(RectTransform rectT, string style, bool scaleToFit = false) + : this(rectT, null, null, scaleToFit, style) { } diff --git a/Barotrauma/BarotraumaClient/Source/GUI/GUINumberInput.cs b/Barotrauma/BarotraumaClient/Source/GUI/GUINumberInput.cs index dbd796825..ca436e624 100644 --- a/Barotrauma/BarotraumaClient/Source/GUI/GUINumberInput.cs +++ b/Barotrauma/BarotraumaClient/Source/GUI/GUINumberInput.cs @@ -131,13 +131,13 @@ namespace Barotrauma private float pressedDelay = 0.5f; private bool IsPressedTimerRunning { get { return pressedTimer > 0; } } - public GUINumberInput(RectTransform rectT, NumberType inputType, string style = "", Alignment textAlignment = Alignment.Center) : base(style, rectT) + public GUINumberInput(RectTransform rectT, NumberType inputType, string style = "", Alignment textAlignment = Alignment.Center, float? relativeButtonAreaWidth = null) : base(style, rectT) { LayoutGroup = new GUILayoutGroup(new RectTransform(Vector2.One, rectT), isHorizontal: true) { Stretch = true }; - float relativeButtonAreaWidth = MathHelper.Clamp(Rect.Height / (float)Rect.Width, 0.1f, 0.5f); + float _relativeButtonAreaWidth = relativeButtonAreaWidth ?? MathHelper.Clamp(Rect.Height / (float)Rect.Width, 0.1f, 0.5f); - TextBox = new GUITextBox(new RectTransform(new Vector2(1.0f - relativeButtonAreaWidth, 1.0f), LayoutGroup.RectTransform), textAlignment: textAlignment, style: style) + TextBox = new GUITextBox(new RectTransform(new Vector2(1.0f - _relativeButtonAreaWidth, 1.0f), LayoutGroup.RectTransform), textAlignment: textAlignment, style: style) { ClampText = false, // For some reason the caret in the number inputs is dimmer than it should. @@ -146,7 +146,12 @@ namespace Barotrauma CaretColor = Color.White }; TextBox.OnTextChanged += TextChanged; - var buttonArea = new GUIFrame(new RectTransform(new Vector2(relativeButtonAreaWidth, 1.0f), LayoutGroup.RectTransform, Anchor.CenterRight) { MinSize = new Point(Rect.Height, 0) }, style: null); + var buttonArea = new GUIFrame(new RectTransform(new Vector2(_relativeButtonAreaWidth, 1.0f), LayoutGroup.RectTransform, Anchor.CenterRight), style: null); + if (!relativeButtonAreaWidth.HasValue) + { + // Not sure what's the point of this + buttonArea.RectTransform.MinSize = new Point(Rect.Height, 0); + } PlusButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.5f), buttonArea.RectTransform), "+"); PlusButton.OnButtonDown += () => { diff --git a/Barotrauma/BarotraumaClient/Source/GUI/GUIScrollBar.cs b/Barotrauma/BarotraumaClient/Source/GUI/GUIScrollBar.cs index 02856e82d..5f6f443c8 100644 --- a/Barotrauma/BarotraumaClient/Source/GUI/GUIScrollBar.cs +++ b/Barotrauma/BarotraumaClient/Source/GUI/GUIScrollBar.cs @@ -246,14 +246,12 @@ namespace Barotrauma private bool SelectBar() { - if (!enabled) return false; - // This doesn't work - if (barSize == 1.0f) return false; + if (!enabled || !PlayerInput.LeftButtonDown()) { return false; } + if (barSize >= 1.0f) { return false; } draggingBar = this; return true; - } public void MoveButton(Vector2 moveAmount) diff --git a/Barotrauma/BarotraumaClient/Source/GUI/GUIStyle.cs b/Barotrauma/BarotraumaClient/Source/GUI/GUIStyle.cs index 780264181..26fe1031b 100644 --- a/Barotrauma/BarotraumaClient/Source/GUI/GUIStyle.cs +++ b/Barotrauma/BarotraumaClient/Source/GUI/GUIStyle.cs @@ -40,6 +40,9 @@ namespace Barotrauma { ToolBox.IsProperFilenameCase(file); doc = XDocument.Load(file, LoadOptions.SetBaseUri); + if (doc == null) { throw new Exception("doc is null"); } + if (doc.Root == null) { throw new Exception("doc.Root is null"); } + if (doc.Root.Elements() == null) { throw new Exception("doc.Root.Elements() is null"); } } catch (Exception e) { @@ -113,26 +116,34 @@ namespace Barotrauma private void RescaleFonts() { + if (configElement == null) { return; } + if (configElement.Elements() == null) { return; } foreach (XElement subElement in configElement.Elements()) { switch (subElement.Name.ToString().ToLowerInvariant()) { case "font": + if (Font == null) { continue; } Font.Size = GetFontSize(subElement); break; case "smallfont": + if (SmallFont == null) { continue; } SmallFont.Size = GetFontSize(subElement); break; case "largefont": + if (LargeFont == null) { continue; } LargeFont.Size = GetFontSize(subElement); break; case "objectivetitle": + if (ObjectiveTitleFont == null) { continue; } ObjectiveTitleFont.Size = GetFontSize(subElement); break; case "objectivename": + if (ObjectiveNameFont == null) { continue; } ObjectiveNameFont.Size = GetFontSize(subElement); break; case "videotitle": + if (VideoTitleFont == null) { continue; } VideoTitleFont.Size = GetFontSize(subElement); break; } diff --git a/Barotrauma/BarotraumaClient/Source/GUI/GUITextBlock.cs b/Barotrauma/BarotraumaClient/Source/GUI/GUITextBlock.cs index 8692bf140..f6699c4f7 100644 --- a/Barotrauma/BarotraumaClient/Source/GUI/GUITextBlock.cs +++ b/Barotrauma/BarotraumaClient/Source/GUI/GUITextBlock.cs @@ -389,7 +389,7 @@ namespace Barotrauma public static void AutoScaleAndNormalize(IEnumerable textBlocks) { if (!textBlocks.Any()) { return; } - float minScale = textBlocks.First().TextScale; + float minScale = Math.Max(textBlocks.First().TextScale, 1.0f); foreach (GUITextBlock textBlock in textBlocks) { textBlock.AutoScale = true; diff --git a/Barotrauma/BarotraumaClient/Source/GUI/GUITickBox.cs b/Barotrauma/BarotraumaClient/Source/GUI/GUITickBox.cs index 06e197eeb..dd572c229 100644 --- a/Barotrauma/BarotraumaClient/Source/GUI/GUITickBox.cs +++ b/Barotrauma/BarotraumaClient/Source/GUI/GUITickBox.cs @@ -41,7 +41,28 @@ namespace Barotrauma OnSelected?.Invoke(this); } } - + + private Color? defaultTextColor; + + public override bool Enabled + { + get + { + return enabled; + } + + set + { + if (value == enabled) { return; } + enabled = value; + if (color.A == 0) + { + if (defaultTextColor == null) { defaultTextColor = TextBlock.TextColor; } + TextBlock.TextColor = enabled ? defaultTextColor.Value : defaultTextColor.Value * 0.5f; + } + } + } + public Color TextColor { get { return text.TextColor; } diff --git a/Barotrauma/BarotraumaClient/Source/GUI/ParamsEditor.cs b/Barotrauma/BarotraumaClient/Source/GUI/ParamsEditor.cs index 591c9ff1b..461a52fc7 100644 --- a/Barotrauma/BarotraumaClient/Source/GUI/ParamsEditor.cs +++ b/Barotrauma/BarotraumaClient/Source/GUI/ParamsEditor.cs @@ -19,6 +19,7 @@ namespace Barotrauma } } + public GUIComponent Parent { get; private set; } public GUIListBox EditorBox { get; private set; } /// /// Uses Linq queries. Don't use too frequently or reimplement. @@ -29,13 +30,12 @@ namespace Barotrauma public GUIListBox CreateEditorBox(RectTransform rectT = null) { - rectT = rectT ?? new RectTransform(new Vector2(0.25f, 0.95f), GUI.Canvas) { MinSize = new Point(340, GameMain.GraphicsHeight) }; + rectT = rectT ?? new RectTransform(new Vector2(0.25f, 1f), GUI.Canvas) { MinSize = new Point(340, GameMain.GraphicsHeight) }; rectT.SetPosition(Anchor.TopRight); - rectT.RelativeOffset = new Vector2(0.16f, 0); - EditorBox = new GUIListBox(rectT) + Parent = new GUIFrame(rectT, null, new Color(20, 20, 20, 255)); + EditorBox = new GUIListBox(new RectTransform(Vector2.One * 0.98f, rectT, Anchor.Center), color: Color.Black, style: null) { - Spacing = 10, - Color = Color.Black + Spacing = 10 }; return EditorBox; } diff --git a/Barotrauma/BarotraumaClient/Source/GUI/SpriteSheetPlayer.cs b/Barotrauma/BarotraumaClient/Source/GUI/SpriteSheetPlayer.cs deleted file mode 100644 index f533b7a1e..000000000 --- a/Barotrauma/BarotraumaClient/Source/GUI/SpriteSheetPlayer.cs +++ /dev/null @@ -1,274 +0,0 @@ -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using System; -using System.Xml.Linq; -using System.Collections.Generic; - -namespace Barotrauma -{ - class SpriteSheetPlayer - { - private SpriteSheet[] playingSheets; - private SpriteSheet currentSheet; - private List preloadedSheets; - - private GUIFrame background, videoFrame; - private GUITextBlock title; - private GUICustomComponent sheetView; - - private float totalElapsed = 0; - private float animationSpeed = 0.1f; - private float loopTimer = 0.0f; - private float loopDelay = 0.0f; - - private int currentSheetIndex = 0; - private int currentFrameIndex = 0; - - private Color backgroundColor = new Color(0f, 0f, 0f, 1f); - private Action callbackOnStop; - - private bool isPlaying; - public bool IsPlaying - { - get { return isPlaying; } - private set - { - if (isPlaying == value) return; - isPlaying = value; - } - } - - private readonly Vector2 defaultResolution = new Vector2(520, 300); - private readonly int borderSize = 20; - - private class PreloadedContent - { - public string ContentName; - public string ContentTag; - public SpriteSheet[] Sheets; - - public PreloadedContent(string name, string tag, SpriteSheet[] sheets) - { - ContentName = name; - ContentTag = tag; - Sheets = sheets; - } - } - - public SpriteSheetPlayer() - { - int width = (int)defaultResolution.X; - int height = (int)defaultResolution.Y; - - background = new GUIFrame(new RectTransform(new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight), GUI.Canvas, Anchor.Center), "InnerFrame", backgroundColor); - videoFrame = new GUIFrame(new RectTransform(new Point(width + borderSize, height + borderSize), background.RectTransform, Anchor.Center), "SonarFrame"); - sheetView = new GUICustomComponent(new RectTransform(new Point(width, height), videoFrame.RectTransform, Anchor.Center), - (spriteBatch, guiCustomComponent) => { DrawSheetView(spriteBatch, guiCustomComponent.Rect); }, UpdateSheetView); - title = new GUITextBlock(new RectTransform(new Vector2(1f, 0f), videoFrame.RectTransform, Anchor.TopCenter, Pivot.BottomCenter), string.Empty, font: GUI.LargeFont, textAlignment: Alignment.Center); - - preloadedSheets = new List(); - } - - public void PreloadContent(string contentPath, string contentTag, string contentId, XElement contentElement) - { - if (preloadedSheets.Find(s => s.ContentName == contentId) != null) return; // Already loaded - preloadedSheets.Add(new PreloadedContent(contentId, contentTag, CreateSpriteSheets(contentPath, contentElement))); - } - - public void RemoveAllPreloaded() - { - if (preloadedSheets == null || preloadedSheets.Count == 0) return; - - for (int i = 0; i < preloadedSheets.Count; i++) - { - for (int j = 0; j < preloadedSheets[i].Sheets.Length; j++) - { - preloadedSheets[i].Sheets[j].Remove(); - } - } - - preloadedSheets.Clear(); - } - - public void RemovePreloadedByTag(string tag) - { - if (preloadedSheets == null || preloadedSheets.Count == 0) return; - - for (int i = 0; i < preloadedSheets.Count; i++) - { - if (preloadedSheets[i].ContentTag != tag) continue; - for (int j = 0; j < preloadedSheets[i].Sheets.Length; j++) - { - preloadedSheets[i].Sheets[j].Remove(); - } - - preloadedSheets[i] = null; - preloadedSheets.RemoveAt(i); - i--; - } - } - - public void Play() - { - isPlaying = true; - } - - public void Stop() - { - isPlaying = false; - } - - private bool OKButtonClicked(GUIButton button, object userData) - { - Stop(); - callbackOnStop?.Invoke(); - return true; - } - - public void AddToGUIUpdateList() - { - if (!isPlaying) return; - background.AddToGUIUpdateList(); - } - - public void LoadContent(string contentPath, XElement videoElement, string contentId, bool startPlayback, bool hasButton, Action callback = null) - { - totalElapsed = loopTimer = 0.0f; - animationSpeed = videoElement.GetAttributeFloat("animationspeed", 0.1f); - loopDelay = videoElement.GetAttributeFloat("loopdelay", 0.0f); - - if (playingSheets != null) - { - foreach (SpriteSheet existingSheet in playingSheets) - { - existingSheet.Remove(); - } - playingSheets = null; - } - - playingSheets = preloadedSheets.Find(s => s.ContentName == contentId).Sheets; - - if (playingSheets == null) // No preloaded sheets found, create sheets - { - playingSheets = CreateSpriteSheets(contentPath, videoElement); - } - - currentSheet = playingSheets[0]; - - Point resolution = currentSheet.FrameSize; - - videoFrame.RectTransform.NonScaledSize = resolution + new Point(borderSize, borderSize); - sheetView.RectTransform.NonScaledSize = resolution; - - title.Text = TextManager.Get(contentId); - title.RectTransform.NonScaledSize = new Point(resolution.X, 30); - - callbackOnStop = callback; - - if (hasButton) - { - var okButton = new GUIButton(new RectTransform(new Point(160, 50), videoFrame.RectTransform, Anchor.BottomCenter, Pivot.TopCenter) { AbsoluteOffset = new Point(0, -10) }, - TextManager.Get("OK")) - { - OnClicked = OKButtonClicked - }; - } - - if (startPlayback) Play(); - } - - private SpriteSheet[] CreateSpriteSheets(string contentPath, XElement videoElement) - { - SpriteSheet[] sheets = null; - - try - { - List sheetElements = new List(); - - foreach (var sheetElement in videoElement.Elements("Sheet")) - { - sheetElements.Add(sheetElement); - } - - sheets = new SpriteSheet[sheetElements.Count]; - - for (int i = 0; i < sheetElements.Count; i++) - { - sheets[i] = new SpriteSheet(sheetElements[i], contentPath, sheetElements[i].GetAttributeString("path", ""), sheetElements[i].GetAttributeInt("empty", 0)); - } - } - catch (Exception e) - { - DebugConsole.ThrowError("Error loading sprite sheet content " + contentPath + "!", e); - } - - return sheets; - } - - private void UpdateSheetView(float deltaTime, GUICustomComponent viewContainer) - { - if (!isPlaying) return; - if (loopTimer > 0.0f) - { - loopTimer -= deltaTime; - - if (loopTimer <= 0.0f) - { - currentSheetIndex = 0; - currentFrameIndex = 0; - currentSheet = playingSheets[currentSheetIndex]; - } - else - { - return; - } - } - - totalElapsed += deltaTime; - if (totalElapsed > animationSpeed) - { - totalElapsed -= animationSpeed; - currentFrameIndex++; - - if (currentFrameIndex > currentSheet.FrameCount - 1) - { - currentSheetIndex++; - - if (currentSheetIndex > playingSheets.Length - 1) - { - if (loopDelay > 0.0f) - { - loopTimer = loopDelay; - return; - } - - currentSheetIndex = 0; - } - - currentFrameIndex = 0; - currentSheet = playingSheets[currentSheetIndex]; - } - } - } - - private void DrawSheetView(SpriteBatch spriteBatch, Rectangle rect) - { - if (!isPlaying) return; - currentSheet.Draw(spriteBatch, currentFrameIndex, rect.Center.ToVector2(), Color.White, currentSheet.Origin, 0f, Vector2.One); - } - - public void Remove() - { - if (playingSheets != null) - { - foreach (SpriteSheet existingSheet in playingSheets) - { - existingSheet.Remove(); - } - playingSheets = null; - } - - RemoveAllPreloaded(); - } - } -} diff --git a/Barotrauma/BarotraumaClient/Source/GameMain.cs b/Barotrauma/BarotraumaClient/Source/GameMain.cs index ea04b257e..92e524f18 100644 --- a/Barotrauma/BarotraumaClient/Source/GameMain.cs +++ b/Barotrauma/BarotraumaClient/Source/GameMain.cs @@ -30,6 +30,8 @@ namespace Barotrauma public static readonly Version Version = Assembly.GetEntryAssembly().GetName().Version; + public static string[] ConsoleArguments; + public static GameScreen GameScreen; public static MainMenuScreen MainMenuScreen; public static LobbyScreen LobbyScreen; @@ -116,7 +118,7 @@ namespace Barotrauma get; private set; } - + public static WindowMode WindowMode { get; @@ -159,7 +161,7 @@ namespace Barotrauma get { return loadingScreenOpen; } } - public GameMain() + public GameMain(string[] args) { Content.RootDirectory = "Content"; @@ -171,6 +173,8 @@ namespace Barotrauma Config = new GameSettings(); + ConsoleArguments = args; + GUI.KeyboardDispatcher = new EventInput.KeyboardDispatcher(Window); PerformanceCounter = new PerformanceCounter(); @@ -294,7 +298,7 @@ namespace Barotrauma loadingCoroutine = CoroutineManager.StartCoroutine(Load(canLoadInSeparateThread), "Load", canLoadInSeparateThread); } - + private void InitUserStats() { if (GameSettings.ShowUserStatisticsPrompt) @@ -356,6 +360,11 @@ namespace Barotrauma SoundManager.SetCategoryGainMultiplier("waterambience", Config.SoundVolume); SoundManager.SetCategoryGainMultiplier("music", Config.MusicVolume); SoundManager.SetCategoryGainMultiplier("voip", Config.VoiceChatVolume * 20.0f); + + if (ConsoleArguments.Contains("skipintro")) { + Config.EnableSplashScreen = false; + } + if (Config.EnableSplashScreen) { var pendingSplashScreens = TitleScreen.PendingSplashScreens; @@ -403,7 +412,7 @@ namespace Barotrauma InitUserStats(); yield return CoroutineStatus.Running; - + LightManager = new Lights.LightManager(base.GraphicsDevice, Content); TitleScreen.LoadState = 1.0f; @@ -664,7 +673,7 @@ namespace Barotrauma GUI.KeyboardDispatcher.Subscriber = null; } //if a verification prompt (are you sure you want to x) is open, close it - else if (GUIMessageBox.VisibleBox as GUIMessageBox != null && + else if (GUIMessageBox.VisibleBox as GUIMessageBox != null && GUIMessageBox.VisibleBox.UserData as string == "verificationprompt") { ((GUIMessageBox)GUIMessageBox.VisibleBox).Close(); @@ -826,8 +835,8 @@ namespace Barotrauma } }; } - - msgBox.InnerFrame.RectTransform.MinSize = new Point(0, + + msgBox.InnerFrame.RectTransform.MinSize = new Point(0, msgBox.InnerFrame.Rect.Height + linkHolder.Rect.Height + msgBox.Content.AbsoluteSpacing * 2 + 10); Config.EditorDisclaimerShown = true; Config.SaveNewPlayerConfig(); @@ -878,6 +887,7 @@ namespace Barotrauma if (NetworkMember != null) NetworkMember.Disconnect(); SteamManager.ShutDown(); if (GameSettings.SendUserStatistics) GameAnalytics.OnQuit(); + if (GameSettings.SaveDebugConsoleLogs) DebugConsole.SaveLogs(); base.OnExiting(sender, args); } } diff --git a/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs b/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs index a95140454..647871bfa 100644 --- a/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs +++ b/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs @@ -941,7 +941,7 @@ namespace Barotrauma else { orderTargetFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.2f + order.Options.Length * 0.1f, 0.18f), GUI.Canvas) - { AbsoluteOffset = new Point(orderButton.Rect.Center.X, orderButton.Rect.Bottom) }, + { AbsoluteOffset = new Point((int)(200 * GUI.Scale), orderButton.Rect.Bottom) }, isHorizontal: true, childAnchor: Anchor.BottomLeft) { UserData = character, @@ -970,6 +970,12 @@ namespace Barotrauma return true; } }; + new GUIFrame(new RectTransform(Vector2.One * 1.5f, optionButton.RectTransform, Anchor.Center), style: "OuterGlow") + { + Color = Color.Black, + HoverColor = Color.CadetBlue, + PressedColor = Color.Black + }.RectTransform.SetAsFirstChild(); OrderOptionButtons.Add(optionButton); @@ -1238,7 +1244,10 @@ namespace Barotrauma } hoverArea.Inflate(100, 100); - if (!hoverArea.Contains(PlayerInput.MousePosition)) orderTargetFrame = null; + if (!hoverArea.Contains(PlayerInput.MousePosition) || PlayerInput.RightButtonClicked()) + { + orderTargetFrame = null; + } } } diff --git a/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/SinglePlayerCampaign.cs b/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/SinglePlayerCampaign.cs index 0bb370465..d098cf3db 100644 --- a/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/SinglePlayerCampaign.cs +++ b/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/SinglePlayerCampaign.cs @@ -256,6 +256,18 @@ namespace Barotrauma { if (c.Info == null || c.Inventory == null) { continue; } var inventoryElement = new XElement("inventory"); + + // Recharge headset batteries + var headset = c.Inventory.FindItemByIdentifier("headset"); + if (headset != null) + { + var battery = headset.OwnInventory.FindItemByTag("loadable"); + if (battery != null) + { + battery.Condition = battery.MaxCondition; + } + } + c.SaveInventory(c.Inventory, inventoryElement); c.Info.InventoryData = inventoryElement; c.Inventory?.DeleteAllItems(); @@ -407,7 +419,8 @@ namespace Barotrauma public override void Save(XElement element) { XElement modeElement = new XElement("SinglePlayerCampaign", - new XAttribute("money", Money), + // Refunds the money when save & quitting from the map if there are items selected in the store + new XAttribute("money", Money + (CargoManager != null ? CargoManager.GetTotalItemCost() : 0)), new XAttribute("cheatsenabled", CheatsEnabled)); CrewManager.Save(modeElement); Map.Save(modeElement); diff --git a/Barotrauma/BarotraumaClient/Source/GameSession/RoundSummary.cs b/Barotrauma/BarotraumaClient/Source/GameSession/RoundSummary.cs index ffcc67311..823e0c6dc 100644 --- a/Barotrauma/BarotraumaClient/Source/GameSession/RoundSummary.cs +++ b/Barotrauma/BarotraumaClient/Source/GameSession/RoundSummary.cs @@ -42,8 +42,14 @@ namespace Barotrauma RelativeSpacing = 0.03f }; - GUIListBox infoTextBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.7f), paddedFrame.RectTransform)); - + GUIListBox infoTextBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.7f), paddedFrame.RectTransform)) + { + Spacing = (int)(5 * GUI.Scale) + }; + + //spacing + new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), infoTextBox.Content.RectTransform), style: null); + string summaryText = TextManager.GetWithVariables(gameOver ? "RoundSummaryGameOver" : (progress ? "RoundSummaryProgress" : "RoundSummaryReturn"), new string[2] { "[sub]", "[location]" }, new string[2] { Submarine.MainSub.Name, progress ? GameMain.GameSession.EndLocation.Name : GameMain.GameSession.StartLocation.Name }); diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Deconstructor.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Deconstructor.cs index cb8889292..dc704f775 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Deconstructor.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Deconstructor.cs @@ -19,6 +19,8 @@ namespace Barotrauma.Items.Components private GUIComponent inSufficientPowerWarning; + private bool pendingState; + partial void InitProjSpecific(XElement element) { var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.9f), GuiFrame.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter) @@ -73,32 +75,34 @@ namespace Barotrauma.Items.Components public override void UpdateHUD(Character character, float deltaTime, Camera cam) { - inSufficientPowerWarning.Visible = powerConsumption > 0 && voltage < minVoltage; - //activateButton.Enabled = !inSufficientPowerWarning.Visible; + inSufficientPowerWarning.Visible = CurrPowerConsumption > 0 && !hasPower; } private bool ToggleActive(GUIButton button, object obj) { - SetActive(!IsActive, Character.Controlled); - - currPowerConsumption = IsActive ? powerConsumption : 0.0f; - if (GameMain.Client != null) { + pendingState = !IsActive; item.CreateClientEvent(this); } + else + { + SetActive(!IsActive, Character.Controlled); + currPowerConsumption = IsActive ? powerConsumption : 0.0f; + } return true; } public void ClientWrite(NetBuffer msg, object[] extraData = null) { - msg.Write(IsActive); + msg.Write(pendingState); } public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime) { SetActive(msg.ReadBoolean()); + progressTimer = msg.ReadSingle(); } } } diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Fabricator.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Fabricator.cs index 9b6cf355e..984885460 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Fabricator.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Fabricator.cs @@ -34,6 +34,8 @@ namespace Barotrauma.Items.Components private GUIComponent inSufficientPowerWarning; + private FabricationRecipe pendingFabricatedItem; + private Pair tooltip; partial void InitProjSpecific() @@ -403,7 +405,7 @@ namespace Barotrauma.Items.Components } } } - + private bool StartButtonClicked(GUIButton button, object obj) { if (selectedItem == null) { return false; } @@ -412,19 +414,22 @@ namespace Barotrauma.Items.Components outputInventoryHolder.Flash(Color.Red); return false; } - - if (fabricatedItem == null) + + if (GameMain.Client != null) { - StartFabricating(selectedItem, Character.Controlled); + pendingFabricatedItem = fabricatedItem != null ? null : selectedItem; + item.CreateClientEvent(this); } else { - CancelFabricating(Character.Controlled); - } - - if (GameMain.Client != null) - { - item.CreateClientEvent(this); + if (fabricatedItem == null) + { + StartFabricating(selectedItem, Character.Controlled); + } + else + { + CancelFabricating(Character.Controlled); + } } return true; @@ -433,7 +438,7 @@ namespace Barotrauma.Items.Components public override void UpdateHUD(Character character, float deltaTime, Camera cam) { activateButton.Enabled = false; - inSufficientPowerWarning.Visible = powerConsumption > 0 && voltage < minVoltage; + inSufficientPowerWarning.Visible = currPowerConsumption > 0 && !hasPower; var availableIngredients = GetAvailableIngredients(); if (character != null) @@ -457,7 +462,7 @@ namespace Barotrauma.Items.Components public void ClientWrite(NetBuffer msg, object[] extraData = null) { - int itemIndex = fabricatedItem == null ? -1 : fabricationRecipes.IndexOf(fabricatedItem); + int itemIndex = pendingFabricatedItem == null ? -1 : fabricationRecipes.IndexOf(pendingFabricatedItem); msg.WriteRangedInteger(-1, fabricationRecipes.Count - 1, itemIndex); } @@ -474,8 +479,8 @@ namespace Barotrauma.Items.Components else { //if already fabricating the selected item, return - if (fabricatedItem != null && fabricationRecipes.IndexOf(fabricatedItem) == itemIndex) return; - if (itemIndex < 0 || itemIndex >= fabricationRecipes.Count) return; + if (fabricatedItem != null && fabricationRecipes.IndexOf(fabricatedItem) == itemIndex) { return; } + if (itemIndex < 0 || itemIndex >= fabricationRecipes.Count) { return; } SelectItem(user, fabricationRecipes[itemIndex]); StartFabricating(fabricationRecipes[itemIndex], user); diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/MiniMap.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/MiniMap.cs index 0e058bbd9..d1f6f9ad4 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/MiniMap.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/MiniMap.cs @@ -17,10 +17,14 @@ namespace Barotrauma.Items.Components private GUITextBlock hullNameText, hullBreachText, hullAirQualityText, hullWaterText; + private string noPowerTip = ""; + private List displayedSubs = new List(); partial void InitProjSpecific(XElement element) { + noPowerTip = TextManager.Get("SteeringNoPowerTip"); + GuiFrame.RectTransform.RelativeOffset = new Vector2(0.4f, 0.0f); new GUICustomComponent(new RectTransform(new Vector2(0.9f, 0.85f), GuiFrame.RectTransform, Anchor.Center), DrawHUDBack, null); @@ -105,6 +109,16 @@ namespace Barotrauma.Items.Components private void DrawHUDFront(SpriteBatch spriteBatch, GUICustomComponent container) { + if (voltage < minVoltage) + { + Vector2 textSize = GUI.Font.MeasureString(noPowerTip); + Vector2 textPos = GuiFrame.Rect.Center.ToVector2(); + + GUI.DrawString(spriteBatch, textPos - textSize / 2, noPowerTip, + Color.Orange * (float)Math.Abs(Math.Sin(Timing.TotalTime)), Color.Black * 0.8f); + return; + } + foreach (GUIComponent child in submarineContainer.Children.First().Children) { if (child.UserData is Hull hull) @@ -145,6 +159,11 @@ namespace Barotrauma.Items.Components } } + if (voltage < minVoltage) + { + return; + } + float scale = 1.0f; HashSet subs = new HashSet(); foreach (Hull hull in Hull.hullList) diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Sonar.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Sonar.cs index 840f47fb3..4b9fe65a0 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Sonar.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Sonar.cs @@ -34,7 +34,8 @@ namespace Barotrauma.Items.Components private GUITickBox directionalTickBox; private GUIScrollBar directionalSlider; - private GUIComponent activeControlsContainer; + private GUILayoutGroup activeControlsContainer; + private GUIFrame controlContainer; private GUICustomComponent sonarView; @@ -76,12 +77,11 @@ namespace Barotrauma.Items.Components { sonarBlips = new List(); - int viewSize = (int)Math.Min(GuiFrame.Rect.Width - 150, GuiFrame.Rect.Height * 0.9f); - sonarView = new GUICustomComponent(new RectTransform(new Point(viewSize), GuiFrame.RectTransform, Anchor.CenterLeft), + sonarView = new GUICustomComponent(new RectTransform(Point.Zero, GuiFrame.RectTransform, Anchor.CenterLeft), (spriteBatch, guiCustomComponent) => { DrawSonar(spriteBatch, guiCustomComponent.Rect); }, null); - var controlContainer = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.35f), GuiFrame.RectTransform, Anchor.TopLeft) - { MinSize = new Point(150, 0), AbsoluteOffset = new Point((int)(viewSize * 0.9f), 0) }, "SonarFrame"); + controlContainer = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.35f), GuiFrame.RectTransform, Anchor.TopLeft) + { MinSize = new Point(150, 0) }, "SonarFrame"); controlContainer.RectTransform.SetAsFirstChild(); @@ -200,6 +200,18 @@ namespace Barotrauma.Items.Components break; } } + + SetUILayout(); + + GameMain.Instance.OnResolutionChanged += SetUILayout; + GameMain.Config.OnHUDScaleChanged += SetUILayout; + } + + private void SetUILayout() + { + int viewSize = (int)Math.Min(GuiFrame.Rect.Width - 150, GuiFrame.Rect.Height * 0.9f); + sonarView.RectTransform.NonScaledSize = new Point(viewSize); + controlContainer.RectTransform.AbsoluteOffset = new Point((int)(viewSize * 0.9f), 0); } public override void OnItemLoaded() @@ -612,6 +624,16 @@ namespace Barotrauma.Items.Components { if (Level.Loaded != null && dockingPort.Item.Submarine.WorldPosition.Y > Level.Loaded.Size.Y) { continue; } + //don't show the docking ports of the opposing team on the sonar + if (item.Submarine != null && dockingPort.Item.Submarine != null) + { + if ((dockingPort.Item.Submarine.TeamID == Character.TeamType.Team1 && item.Submarine.TeamID == Character.TeamType.Team2) || + (dockingPort.Item.Submarine.TeamID == Character.TeamType.Team2 && item.Submarine.TeamID == Character.TeamType.Team1)) + { + continue; + } + } + Vector2 offset = (dockingPort.Item.WorldPosition - transducerCenter) * scale; offset.Y = -offset.Y; if (offset.LengthSquared() > DisplayRadius * DisplayRadius) { continue; } diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Steering.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Steering.cs index a6fc8d8e2..0f57fa4da 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Steering.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Steering.cs @@ -27,7 +27,7 @@ namespace Barotrauma.Items.Components }; private GUITickBox maintainPosTickBox, levelEndTickBox, levelStartTickBox; - private GUIComponent statusContainer, dockingContainer; + private GUIComponent statusContainer, dockingContainer, controlContainer; private bool dockingNetworkMessagePending; @@ -86,9 +86,8 @@ namespace Barotrauma.Items.Components partial void InitProjSpecific(XElement element) { - int viewSize = (int)Math.Min(GuiFrame.Rect.Width - 150, GuiFrame.Rect.Height * 0.9f); - var controlContainer = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.35f), GuiFrame.RectTransform, Anchor.CenterLeft) - { MinSize = new Point(150, 0), AbsoluteOffset = new Point((int)(viewSize * 0.99f), (int)(viewSize * 0.05f)) }, "SonarFrame"); + controlContainer = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.35f), GuiFrame.RectTransform, Anchor.CenterLeft) + { MinSize = new Point(150, 0) }, "SonarFrame"); var paddedControlContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.8f), controlContainer.RectTransform, Anchor.Center)) { RelativeSpacing = 0.03f, @@ -96,7 +95,7 @@ namespace Barotrauma.Items.Components }; statusContainer = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.25f), GuiFrame.RectTransform, Anchor.BottomLeft) - { MinSize = new Point(150, 0), AbsoluteOffset = new Point((int)(viewSize * 0.9f), 0) }, "SonarFrame"); + { MinSize = new Point(150, 0) }, "SonarFrame"); var paddedStatusContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.9f), statusContainer.RectTransform, Anchor.Center)) { RelativeSpacing = 0.03f, @@ -295,12 +294,12 @@ namespace Barotrauma.Items.Components autoPilotLevelEndTip = TextManager.GetWithVariable("SteeringAutoPilotLocationTip", "[locationname]", GameMain.GameSession?.EndLocation == null ? "End" : GameMain.GameSession.EndLocation.Name); - steerArea = new GUICustomComponent(new RectTransform(new Point(viewSize), GuiFrame.RectTransform, Anchor.CenterLeft), + steerArea = new GUICustomComponent(new RectTransform(Point.Zero, GuiFrame.RectTransform, Anchor.CenterLeft), (spriteBatch, guiCustomComponent) => { DrawHUD(spriteBatch, guiCustomComponent.Rect); }, null); //docking interface ---------------------------------------------------- dockingContainer = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.25f), GuiFrame.RectTransform, Anchor.BottomLeft) - { MinSize = new Point(150, 0), AbsoluteOffset = new Point((int)(viewSize * 0.9f), 0) }, style: null); + { MinSize = new Point(150, 0) }, style: null); var paddedDockingContainer = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), dockingContainer.RectTransform, Anchor.Center), style: null); //TODO: add new texts for these ("Dock" & "Undock") @@ -364,6 +363,21 @@ namespace Barotrauma.Items.Components break; } } + + SetUILayout(); + + GameMain.Instance.OnResolutionChanged += SetUILayout; + GameMain.Config.OnHUDScaleChanged += SetUILayout; + } + + private void SetUILayout() + { + int viewSize = (int)Math.Min(GuiFrame.Rect.Width - 150, GuiFrame.Rect.Height * 0.9f); + + controlContainer.RectTransform.AbsoluteOffset = new Point((int)(viewSize * 0.99f), (int)(viewSize * 0.05f)); + statusContainer.RectTransform.AbsoluteOffset = new Point((int)(viewSize * 0.9f), 0); + steerArea.RectTransform.NonScaledSize = new Point(viewSize); + dockingContainer.RectTransform.AbsoluteOffset = new Point((int)(viewSize * 0.9f), 0); } private void FindConnectedDockingPort() diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Signal/Connection.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Signal/Connection.cs index 294f6214b..ba5580dc7 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Signal/Connection.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Signal/Connection.cs @@ -41,8 +41,9 @@ namespace Barotrauma.Items.Components } Wire equippedWire = null; - - if (!panel.Locked || Screen.Selected == GameMain.SubEditorScreen) + + bool allowRewiring = GameMain.NetworkMember?.ServerSettings == null || GameMain.NetworkMember.ServerSettings.AllowRewiring; + if (allowRewiring && (!panel.Locked || Screen.Selected == GameMain.SubEditorScreen)) { //if the Character using the panel has a wire item equipped //and the wire hasn't been connected yet, draw it on the panel @@ -278,7 +279,8 @@ namespace Barotrauma.Items.Components { ConnectionPanel.HighlightedWire = wire; - if (!wire.Locked && (!panel.Locked || Screen.Selected == GameMain.SubEditorScreen)) + bool allowRewiring = GameMain.NetworkMember?.ServerSettings == null || GameMain.NetworkMember.ServerSettings.AllowRewiring; + if (allowRewiring && !wire.Locked && (!panel.Locked || Screen.Selected == GameMain.SubEditorScreen)) { //start dragging the wire if (PlayerInput.LeftButtonHeld()) draggingConnected = wire; diff --git a/Barotrauma/BarotraumaClient/Source/Items/Item.cs b/Barotrauma/BarotraumaClient/Source/Items/Item.cs index f6e4cb04a..ba6bc9a40 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Item.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Item.cs @@ -181,7 +181,7 @@ namespace Barotrauma public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true) { - if (!Visible || (!editing && hiddenInGame)) return; + if (!Visible || (!editing && HiddenInGame)) return; if (editing && !ShowItems) return; Color color = IsHighlighted && !GUI.DisableItemHighlights && Screen.Selected != GameMain.GameScreen ? Color.Orange : GetSpriteColor(); @@ -451,7 +451,7 @@ namespace Barotrauma public override void UpdateEditing(Camera cam) { - if (editingHUD == null || editingHUD.UserData == null) + if (editingHUD == null || editingHUD.UserData as Item != this) { editingHUD = CreateEditingHUD(Screen.Selected != GameMain.SubEditorScreen); } diff --git a/Barotrauma/BarotraumaClient/Source/Map/MapEntity.cs b/Barotrauma/BarotraumaClient/Source/Map/MapEntity.cs index 215e2db4d..f5da6ece8 100644 --- a/Barotrauma/BarotraumaClient/Source/Map/MapEntity.cs +++ b/Barotrauma/BarotraumaClient/Source/Map/MapEntity.cs @@ -132,96 +132,97 @@ namespace Barotrauma selectedList.Clear(); return; } - - if (PlayerInput.KeyDown(Keys.Delete)) + if (GUI.KeyboardDispatcher.Subscriber == null) { - selectedList.ForEach(e => e.Remove()); - selectedList.Clear(); - } - - if (PlayerInput.KeyDown(Keys.LeftControl) || PlayerInput.KeyDown(Keys.RightControl)) - { - if (PlayerInput.KeyHit(Keys.C)) + if (PlayerInput.KeyDown(Keys.Delete)) { - CopyEntities(selectedList); - } - else if (PlayerInput.KeyHit(Keys.X)) - { - CopyEntities(selectedList); - selectedList.ForEach(e => e.Remove()); selectedList.Clear(); } - else if (copiedList.Count > 0 && PlayerInput.KeyHit(Keys.V)) + + if (PlayerInput.KeyDown(Keys.LeftControl) || PlayerInput.KeyDown(Keys.RightControl)) { - List prevEntities = new List(mapEntityList); - Clone(copiedList); - - var clones = mapEntityList.Except(prevEntities).ToList(); - - Vector2 center = Vector2.Zero; - clones.ForEach(c => center += c.WorldPosition); - center = Submarine.VectorToWorldGrid(center / clones.Count); - - Vector2 moveAmount = Submarine.VectorToWorldGrid(cam.WorldViewCenter - center); - - selectedList = new List(clones); - foreach (MapEntity clone in selectedList) + if (PlayerInput.KeyHit(Keys.C)) { - clone.Move(moveAmount); - clone.Submarine = Submarine.MainSub; + CopyEntities(selectedList); } - } - else if (PlayerInput.KeyHit(Keys.G)) - { - if (selectedList.Any()) + else if (PlayerInput.KeyHit(Keys.X)) { - if (SelectionGroups.ContainsKey(selectedList.Last())) + CopyEntities(selectedList); + + selectedList.ForEach(e => e.Remove()); + selectedList.Clear(); + } + else if (copiedList.Count > 0 && PlayerInput.KeyHit(Keys.V)) + { + List prevEntities = new List(mapEntityList); + Clone(copiedList); + + var clones = mapEntityList.Except(prevEntities).ToList(); + + Vector2 center = Vector2.Zero; + clones.ForEach(c => center += c.WorldPosition); + center = Submarine.VectorToWorldGrid(center / clones.Count); + + Vector2 moveAmount = Submarine.VectorToWorldGrid(cam.WorldViewCenter - center); + + selectedList = new List(clones); + foreach (MapEntity clone in selectedList) { - // Ungroup all selected - selectedList.ForEach(e => SelectionGroups.Remove(e)); + clone.Move(moveAmount); + clone.Submarine = Submarine.MainSub; } - else + } + else if (PlayerInput.KeyHit(Keys.G)) + { + if (selectedList.Any()) { - foreach (var entity in selectedList) + if (SelectionGroups.ContainsKey(selectedList.Last())) { - // Remove the old group, if any - SelectionGroups.Remove(entity); - // Create a group that can be accessed with any member - SelectionGroups.Add(entity, selectedList); + // Ungroup all selected + selectedList.ForEach(e => SelectionGroups.Remove(e)); + } + else + { + foreach (var entity in selectedList) + { + // Remove the old group, if any + SelectionGroups.Remove(entity); + // Create a group that can be accessed with any member + SelectionGroups.Add(entity, selectedList); + } + } + } + } + else if (PlayerInput.KeyHit(Keys.Z)) + { + SetPreviousRects(e => e.rectMemento.Undo()); + } + else if (PlayerInput.KeyHit(Keys.R)) + { + SetPreviousRects(e => e.rectMemento.Redo()); + } + + void SetPreviousRects(Func memoryMethod) + { + foreach (var e in SelectedList) + { + if (e.rectMemento != null) + { + Point diff = memoryMethod(e).Location - e.Rect.Location; + // We have to call the move method, because there's a lot more than just storing the rect (in some cases) + // We also have to reassign the rect, because the move method does not set the width and height. They might have changed too. + // The Rect property is virtual and it's overridden for structs. Setting the rect via the property should automatically recreate the sections for resizable structures. + e.Move(diff.ToVector2()); + e.Rect = e.rectMemento.Current; } } } } - else if (PlayerInput.KeyHit(Keys.Z)) - { - SetPreviousRects(e => e.rectMemento.Undo()); - } - else if (PlayerInput.KeyHit(Keys.R)) - { - SetPreviousRects(e => e.rectMemento.Redo()); - } - void SetPreviousRects(Func memoryMethod) - { - foreach (var e in SelectedList) - { - if (e.rectMemento != null) - { - Point diff = memoryMethod(e).Location - e.Rect.Location; - // We have to call the move method, because there's a lot more than just storing the rect (in some cases) - // We also have to reassign the rect, because the move method does not set the width and height. They might have changed too. - // The Rect property is virtual and it's overridden for structs. Setting the rect via the property should automatically recreate the sections for resizable structures. - e.Move(diff.ToVector2()); - e.Rect = e.rectMemento.Current; - } - } - } - } + } Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition); - MapEntity highLightedEntity = null; - if (startMovingPos == Vector2.Zero) { List highlightedEntities = new List(); @@ -282,16 +283,19 @@ namespace Barotrauma if (highLightedEntity != null) highLightedEntity.isHighlighted = true; } - Vector2 nudgeAmount = Vector2.Zero; - if (PlayerInput.KeyHit(Keys.Up)) nudgeAmount.Y = 1f; - if (PlayerInput.KeyHit(Keys.Down)) nudgeAmount.Y = -1f; - if (PlayerInput.KeyHit(Keys.Left)) nudgeAmount.X = -1f; - if (PlayerInput.KeyHit(Keys.Right)) nudgeAmount.X = 1f; - if (nudgeAmount != Vector2.Zero) + if (GUI.KeyboardDispatcher.Subscriber == null) { - foreach (MapEntity entityToNudge in selectedList) + Vector2 nudgeAmount = Vector2.Zero; + if (PlayerInput.KeyHit(Keys.Up)) nudgeAmount.Y = 1f; + if (PlayerInput.KeyHit(Keys.Down)) nudgeAmount.Y = -1f; + if (PlayerInput.KeyHit(Keys.Left)) nudgeAmount.X = -1f; + if (PlayerInput.KeyHit(Keys.Right)) nudgeAmount.X = 1f; + if (nudgeAmount != Vector2.Zero) { - entityToNudge.Move(nudgeAmount); + foreach (MapEntity entityToNudge in selectedList) + { + entityToNudge.Move(nudgeAmount); + } } } @@ -619,7 +623,7 @@ namespace Barotrauma if (selectedList.Count == 0) return; foreach (var e in selectedList) { - if (e is Gap) { continue; } + if (e is Gap gap && gap.ConnectedDoor != null) { continue; } FilteredSelectedList.Add(e); } var first = FilteredSelectedList.FirstOrDefault(); diff --git a/Barotrauma/BarotraumaClient/Source/Map/WayPoint.cs b/Barotrauma/BarotraumaClient/Source/Map/WayPoint.cs index 27c059ccf..c14813c0b 100644 --- a/Barotrauma/BarotraumaClient/Source/Map/WayPoint.cs +++ b/Barotrauma/BarotraumaClient/Source/Map/WayPoint.cs @@ -18,11 +18,16 @@ namespace Barotrauma return Screen.Selected == GameMain.SubEditorScreen || GameMain.DebugDraw; } + public override bool SelectableInEditor + { + get { return !IsHidden(); } + } + public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true) { - if (!editing && !GameMain.DebugDraw) return; + if (!editing && !GameMain.DebugDraw) { return; } - if (IsHidden()) return; + if (IsHidden()) { return; } //Rectangle drawRect = // Submarine == null ? rect : new Rectangle((int)(Submarine.DrawPosition.X + rect.X), (int)(Submarine.DrawPosition.Y + rect.Y), rect.Width, rect.Height); diff --git a/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs b/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs index 482ee45a0..91366725a 100644 --- a/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs +++ b/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs @@ -1093,6 +1093,7 @@ namespace Barotrauma.Networking bool loadSecondSub = inc.ReadBoolean(); bool disguisesAllowed = inc.ReadBoolean(); + bool rewiringAllowed = inc.ReadBoolean(); bool isTraitor = inc.ReadBoolean(); string traitorTargetName = isTraitor ? inc.ReadString() : null; @@ -1114,6 +1115,7 @@ namespace Barotrauma.Networking GameMain.LightManager.LosMode = (LosMode)losMode; serverSettings.AllowDisguises = disguisesAllowed; + serverSettings.AllowRewiring = rewiringAllowed; serverSettings.AllowRagdollButton = allowRagdollButton; if (campaign == null) @@ -1706,6 +1708,8 @@ namespace Barotrauma.Networking case FileTransferType.Submarine: new GUIMessageBox(TextManager.Get("ServerDownloadFinished"), TextManager.GetWithVariable("FileDownloadedNotification", "[filename]", transfer.FileName)); var newSub = new Submarine(transfer.FilePath); + if (newSub.IsFileCorrupted) { return; } + var existingSubs = Submarine.SavedSubmarines.Where(s => s.Name == newSub.Name && s.MD5Hash.Hash == newSub.MD5Hash.Hash).ToList(); foreach (Submarine existingSub in existingSubs) { @@ -2494,7 +2498,7 @@ namespace Barotrauma.Networking } else { - BanPlayer(clientName, banReasonBox.Text, ban); + BanPlayer(clientName, banReasonBox.Text, range: rangeBan); } } else diff --git a/Barotrauma/BarotraumaClient/Source/Networking/ServerInfo.cs b/Barotrauma/BarotraumaClient/Source/Networking/ServerInfo.cs index 0223c9c69..ec1f695e5 100644 --- a/Barotrauma/BarotraumaClient/Source/Networking/ServerInfo.cs +++ b/Barotrauma/BarotraumaClient/Source/Networking/ServerInfo.cs @@ -60,54 +60,35 @@ namespace Barotrauma.Networking return contentPackageHashes.SetEquals(myContentPackageHashes); } - public void CreatePreviewWindow(GUIListBox listBox) + public void CreatePreviewWindow(GUIFrame frame) { - listBox.ClearChildren(); + frame.ClearChildren(); - if (listBox == null) return; + if (frame == null) return; - var previewContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), listBox.Content.RectTransform, Anchor.Center)) + var previewContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 1.0f), frame.RectTransform, Anchor.Center)) { Stretch = true }; - var titleHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.07f), previewContainer.RectTransform)) + var columnContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), previewContainer.RectTransform)) { - IsHorizontal = true, Stretch = true }; - var title = new GUITextBlock(new RectTransform(new Vector2(0.7f, 1.0f), titleHolder.RectTransform), ServerName, font: GUI.LargeFont, wrap: true); + float elementHeight = 0.075f; + + var title = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), columnContainer.RectTransform), ServerName, font: GUI.LargeFont); title.Text = ToolBox.LimitString(title.Text, title.Font, title.Rect.Width); - new GUITextBlock(new RectTransform(new Vector2(0.3f, 1.0f), titleHolder.RectTransform), - TextManager.AddPunctuation(':', TextManager.Get("ServerListVersion"), string.IsNullOrEmpty(GameVersion) ? TextManager.Get("Unknown") : GameVersion), - textAlignment: Alignment.Right); - - var columnContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), previewContainer.RectTransform), isHorizontal: true) - { - Stretch = true, - RelativeSpacing = 0.005f - }; - - var columnLeft = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), columnContainer.RectTransform)) - { - RelativeSpacing = 0.02f, - Stretch = true - }; - var columnRight = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), columnContainer.RectTransform)) - { - RelativeSpacing = 0.02f, - Stretch = true - }; - - float elementHeight = 0.1f; + new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), columnContainer.RectTransform), + TextManager.AddPunctuation(':', TextManager.Get("ServerListVersion"), string.IsNullOrEmpty(GameVersion) ? TextManager.Get("Unknown") : GameVersion)); // left column ----------------------------------------------------------------------------- //new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), columnLeft.RectTransform), IP + ":" + Port); - var serverMsg = new GUIListBox(new RectTransform(new Vector2(1.0f, 1.0f), columnLeft.RectTransform)) { ScrollBarVisible = true }; + var serverMsg = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.5f), columnContainer.RectTransform)) { ScrollBarVisible = true }; new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), serverMsg.Content.RectTransform), ServerMessage, wrap: true) { CanBeFocused = false }; // right column ----------------------------------------------------------------------------- @@ -122,20 +103,22 @@ namespace Barotrauma.Networking CanBeFocused = false };*/ - var gameMode = new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), columnRight.RectTransform), TextManager.Get("GameMode")); - new GUITextBlock(new RectTransform(Vector2.One, gameMode.RectTransform), TextManager.Get(string.IsNullOrEmpty(GameMode) ? "Unknown" : GameMode), textAlignment: Alignment.Right); + var gameMode = new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), columnContainer.RectTransform), TextManager.Get("GameMode")); + new GUITextBlock(new RectTransform(Vector2.One, gameMode.RectTransform), + TextManager.Get(string.IsNullOrEmpty(GameMode) ? "Unknown" : "GameMode." + GameMode, returnNull: true) ?? GameMode, + textAlignment: Alignment.Right); + + var traitors = new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), columnContainer.RectTransform), TextManager.Get("Traitors")); - var traitors = new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), columnRight.RectTransform), TextManager.Get("Traitors")); new GUITextBlock(new RectTransform(Vector2.One, traitors.RectTransform), TextManager.Get(!TraitorsEnabled.HasValue ? "Unknown" : TraitorsEnabled.Value.ToString()), textAlignment: Alignment.Right); - - var subSelection = new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), columnRight.RectTransform), TextManager.Get("ServerListSubSelection")); + var subSelection = new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), columnContainer.RectTransform), TextManager.Get("ServerListSubSelection")); new GUITextBlock(new RectTransform(Vector2.One, subSelection.RectTransform), TextManager.Get(!SubSelectionMode.HasValue ? "Unknown" : SubSelectionMode.Value.ToString()), textAlignment: Alignment.Right); - var modeSelection = new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), columnRight.RectTransform), TextManager.Get("ServerListModeSelection")); + var modeSelection = new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), columnContainer.RectTransform), TextManager.Get("ServerListModeSelection")); new GUITextBlock(new RectTransform(Vector2.One, modeSelection.RectTransform), TextManager.Get(!ModeSelectionMode.HasValue ? "Unknown" : ModeSelectionMode.Value.ToString()), textAlignment: Alignment.Right); - var allowSpectating = new GUITickBox(new RectTransform(new Vector2(1, elementHeight), columnRight.RectTransform), TextManager.Get("ServerListAllowSpectating")) + var allowSpectating = new GUITickBox(new RectTransform(new Vector2(1, elementHeight), columnContainer.RectTransform), TextManager.Get("ServerListAllowSpectating")) { CanBeFocused = false }; @@ -144,7 +127,7 @@ namespace Barotrauma.Networking else allowSpectating.Selected = AllowSpectating.Value; - var allowRespawn = new GUITickBox(new RectTransform(new Vector2(1, elementHeight), columnRight.RectTransform), TextManager.Get("ServerSettingsAllowRespawning")) + var allowRespawn = new GUITickBox(new RectTransform(new Vector2(1, elementHeight), columnContainer.RectTransform), TextManager.Get("ServerSettingsAllowRespawning")) { CanBeFocused = false }; @@ -153,7 +136,7 @@ namespace Barotrauma.Networking else allowRespawn.Selected = AllowRespawn.Value; - var voipEnabledTickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), columnRight.RectTransform), TextManager.Get("serversettingsvoicechatenabled")) + var voipEnabledTickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), columnContainer.RectTransform), TextManager.Get("serversettingsvoicechatenabled")) { CanBeFocused = false }; @@ -162,7 +145,7 @@ namespace Barotrauma.Networking else voipEnabledTickBox.Selected = VoipEnabled.Value; - var usingWhiteList = new GUITickBox(new RectTransform(new Vector2(1, elementHeight), columnRight.RectTransform), TextManager.Get("ServerListUsingWhitelist")) + var usingWhiteList = new GUITickBox(new RectTransform(new Vector2(1, elementHeight), columnContainer.RectTransform), TextManager.Get("ServerListUsingWhitelist")) { CanBeFocused = false }; @@ -171,10 +154,16 @@ namespace Barotrauma.Networking else usingWhiteList.Selected = UsingWhiteList.Value; - new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), columnRight.RectTransform), + + columnContainer.RectTransform.SizeChanged += () => + { + GUITextBlock.AutoScaleAndNormalize(allowSpectating.TextBlock, allowRespawn.TextBlock, voipEnabledTickBox.TextBlock, usingWhiteList.TextBlock); + }; + + new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), columnContainer.RectTransform), TextManager.Get("ServerListContentPackages")); - var contentPackageList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.3f), columnRight.RectTransform)); + var contentPackageList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.3f), columnContainer.RectTransform)) { ScrollBarVisible = true }; if (ContentPackageNames.Count == 0) { new GUITextBlock(new RectTransform(Vector2.One, contentPackageList.Content.RectTransform), TextManager.Get("Unknown"), textAlignment: Alignment.Center) @@ -223,7 +212,7 @@ namespace Barotrauma.Networking } if (availableWorkshopUrls.Count > 0) { - var workshopBtn = new GUIButton(new RectTransform(new Vector2(1.0f, 0.15f), columnLeft.RectTransform), TextManager.Get("ServerListSubscribeMissingPackages")) + var workshopBtn = new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), columnContainer.RectTransform), TextManager.Get("ServerListSubscribeMissingPackages")) { ToolTip = TextManager.Get(SteamManager.IsInitialized ? "ServerListSubscribeMissingPackagesTooltip" : "ServerListSubscribeMissingPackagesTooltipNoSteam"), Enabled = SteamManager.IsInitialized, @@ -240,11 +229,7 @@ namespace Barotrauma.Networking // ----------------------------------------------------------------------------- - foreach (GUIComponent c in columnLeft.Children) - { - if (c is GUITextBlock textBlock) textBlock.Padding = Vector4.Zero; - } - foreach (GUIComponent c in columnRight.Children) + foreach (GUIComponent c in columnContainer.Children) { if (c is GUITextBlock textBlock) textBlock.Padding = Vector4.Zero; } diff --git a/Barotrauma/BarotraumaClient/Source/Networking/SteamManager.cs b/Barotrauma/BarotraumaClient/Source/Networking/SteamManager.cs index ea5dfb19c..e505a5d24 100644 --- a/Barotrauma/BarotraumaClient/Source/Networking/SteamManager.cs +++ b/Barotrauma/BarotraumaClient/Source/Networking/SteamManager.cs @@ -242,6 +242,10 @@ namespace Barotrauma.Steam if (s.Rules.ContainsKey("gamestarted")) serverInfo.GameStarted = s.Rules["gamestarted"] == "True"; + if (s.Rules.ContainsKey("gamemode")) + { + serverInfo.GameMode = s.Rules["gamemode"]; + } if (serverInfo.ContentPackageNames.Count != serverInfo.ContentPackageHashes.Count || serverInfo.ContentPackageHashes.Count != serverInfo.ContentPackageWorkshopUrls.Count) { @@ -596,7 +600,7 @@ namespace Barotrauma.Steam } else { - DebugConsole.ThrowError("Publishing workshop item " + item.Title + " failed. " + item.Error); + DebugConsole.NewMessage("Publishing workshop item " + item.Title + " failed. " + item.Error, Microsoft.Xna.Framework.Color.Red); } SaveUtil.ClearFolder(WorkshopItemStagingFolder); diff --git a/Barotrauma/BarotraumaClient/Source/Networking/Voip/VoipCapture.cs b/Barotrauma/BarotraumaClient/Source/Networking/Voip/VoipCapture.cs index f47145846..f0bcb18f2 100644 --- a/Barotrauma/BarotraumaClient/Source/Networking/Voip/VoipCapture.cs +++ b/Barotrauma/BarotraumaClient/Source/Networking/Voip/VoipCapture.cs @@ -73,15 +73,31 @@ namespace Barotrauma.Networking if (captureDevice == IntPtr.Zero) { + DebugConsole.NewMessage("Alc.CaptureOpenDevice attempt 1 failed: error code " + Alc.GetError(IntPtr.Zero).ToString(),Color.Orange); + //attempt using a smaller buffer size + captureDevice = Alc.CaptureOpenDevice(deviceName, VoipConfig.FREQUENCY, Al.FormatMono16, VoipConfig.BUFFER_SIZE * 2); + } + + if (captureDevice == IntPtr.Zero) + { + DebugConsole.NewMessage("Alc.CaptureOpenDevice attempt 2 failed: error code " + Alc.GetError(IntPtr.Zero).ToString(), Color.Orange); + //attempt using the default device + captureDevice = Alc.CaptureOpenDevice("", VoipConfig.FREQUENCY, Al.FormatMono16, VoipConfig.BUFFER_SIZE * 2); + } + + if (captureDevice == IntPtr.Zero) + { + string errorCode = Alc.GetError(IntPtr.Zero).ToString(); if (!GUIMessageBox.MessageBoxes.Any(mb => mb.UserData as string == "capturedevicenotfound")) { GUI.SettingsMenuOpen = false; - new GUIMessageBox(TextManager.Get("Error"), - TextManager.Get("VoipCaptureDeviceNotFound", returnNull: true) ?? "Could not start voice capture, suitable capture device not found.") + new GUIMessageBox(TextManager.Get("Error"), + (TextManager.Get("VoipCaptureDeviceNotFound", returnNull: true) ?? "Could not start voice capture, suitable capture device not found.") + " (" + errorCode + ")") { UserData = "capturedevicenotfound" }; } + GameAnalyticsManager.AddErrorEventOnce("Alc.CaptureDeviceOpen(" + deviceName + ") failed", GameAnalyticsSDK.Net.EGAErrorSeverity.Error,"Error code: "+errorCode); GameMain.Config.VoiceSetting = GameSettings.VoiceMode.Disabled; Instance?.Dispose(); Instance = null; diff --git a/Barotrauma/BarotraumaClient/Source/Program.cs b/Barotrauma/BarotraumaClient/Source/Program.cs index 9c1a2d340..b64ef62d9 100644 --- a/Barotrauma/BarotraumaClient/Source/Program.cs +++ b/Barotrauma/BarotraumaClient/Source/Program.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Text; using GameAnalyticsSDK.Net; using Barotrauma.Steam; +using System.Diagnostics; #if WINDOWS using System.Windows.Forms; @@ -28,7 +29,7 @@ namespace Barotrauma /// The main entry point for the application. /// [STAThread] - static void Main() + static void Main(string[] args) { SteamManager.Initialize(); GameMain game = null; @@ -36,7 +37,7 @@ namespace Barotrauma try { #endif - game = new GameMain(); + game = new GameMain(args); #if !DEBUG } catch (Exception e) @@ -151,11 +152,14 @@ namespace Barotrauma return false; } - public static void CrashMessageBox(string message) + public static void CrashMessageBox(string message, string filePath) { #if WINDOWS MessageBox.Show(message, "Oops! Barotrauma just crashed.", MessageBoxButtons.OK, MessageBoxIcon.Error); #endif + + // Open the crash log. + Process.Start(filePath); } static void CrashDump(GameMain game, string filePath, Exception exception) @@ -278,14 +282,14 @@ namespace Barotrauma if (GameSettings.SendUserStatistics) { - CrashMessageBox("A crash report (\"" + filePath + "\") was saved in the root folder of the game and sent to the developers."); + CrashMessageBox("A crash report (\"" + filePath + "\") was saved in the root folder of the game and sent to the developers.", filePath); GameAnalytics.AddErrorEvent(EGAErrorSeverity.Critical, crashReport); GameAnalytics.OnQuit(); } else { CrashMessageBox("A crash report (\"" + filePath + "\") was saved in the root folder of the game. The error was not sent to the developers because user statistics have been disabled, but" + - " if you'd like to help fix this bug, you may post it on Barotrauma's GitHub issue tracker: https://github.com/Regalis11/Barotrauma/issues/"); + " if you'd like to help fix this bug, you may post it on Barotrauma's GitHub issue tracker: https://github.com/Regalis11/Barotrauma/issues/", filePath); } } } diff --git a/Barotrauma/BarotraumaClient/Source/Screens/CharacterEditorScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/CharacterEditorScreen.cs index 36bda70df..e396b450d 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/CharacterEditorScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/CharacterEditorScreen.cs @@ -32,13 +32,15 @@ namespace Barotrauma } } + private bool ShowExtraRagdollControls => selectedLimbs.Any() && editLimbs || selectedJoints.Any() && editJoints; + private Character character; private Vector2 spawnPosition; private bool editAnimations; private bool editLimbs; private bool editJoints; private bool editIK; - private bool showRagdoll; + private bool editRagdoll; private bool showParamsEditor; private bool showSpritesheet; private bool isFreezed; @@ -50,15 +52,17 @@ namespace Barotrauma private bool lockSpriteSize; private bool recalculateCollider; private bool copyJointSettings; - private bool displayColliders; + private bool showColliders; private bool displayWearables; private bool displayBackgroundColor; private bool ragdollResetRequiresForceLoading; private bool animationResetRequiresForceLoading; - private float cameraFollowSpeed = 20; + private bool jointCreationMode; + private bool useMouseOffset; private bool isExtrudingJoint; private bool isDrawingJoint; + private Limb closestSelectedLimb; private Limb targetLimb; private Vector2? anchor1Pos; @@ -69,6 +73,7 @@ namespace Barotrauma private int spriteSheetOffsetX; private bool hideBodySheet; private Color backgroundColor = new Color(0.2f, 0.2f, 0.2f, 1.0f); + private Vector2 cameraOffset; private List selectedJoints = new List(); private List selectedLimbs = new List(); @@ -139,7 +144,7 @@ namespace Barotrauma editLimbs = false; editJoints = false; editIK = false; - showRagdoll = false; + editRagdoll = false; showParamsEditor = false; showSpritesheet = false; isFreezed = false; @@ -151,14 +156,21 @@ namespace Barotrauma lockSpriteSize = false; recalculateCollider = false; copyJointSettings = false; - displayColliders = false; + showColliders = false; displayWearables = true; displayBackgroundColor = false; ragdollResetRequiresForceLoading = false; animationResetRequiresForceLoading = false; + jointCreationMode = false; isExtrudingJoint = false; isDrawingJoint = false; - Wizard.instance = null; + cameraOffset = Vector2.Zero; + targetLimb = null; + anchor1Pos = null; + useMouseOffset = false; + closestSelectedLimb = null; + allFiles = null; + Wizard.instance?.Reset(); } private void Reset() @@ -222,7 +234,13 @@ namespace Barotrauma public override void AddToGUIUpdateList() { //base.AddToGUIUpdateList(); - rightPanel.AddToGUIUpdateList(); + + fileEditPanel.AddToGUIUpdateList(); + modesPanel.AddToGUIUpdateList(); + toolsPanel.AddToGUIUpdateList(); + optionsPanel.AddToGUIUpdateList(); + characterSelectionPanel.AddToGUIUpdateList(); + Wizard.instance?.AddToGUIUpdateList(); if (displayBackgroundColor) { @@ -236,17 +254,25 @@ namespace Barotrauma { spriteSheetControls.AddToGUIUpdateList(); } - if (editJoints) + if (editRagdoll) { ragdollControls.AddToGUIUpdateList(); } + if (editJoints) + { + jointControls.AddToGUIUpdateList(); + } if (editLimbs) { limbControls.AddToGUIUpdateList(); } if (showParamsEditor) { - ParamsEditor.Instance.EditorBox.AddToGUIUpdateList(); + ParamsEditor.Instance.EditorBox.Parent.AddToGUIUpdateList(); + } + if (ShowExtraRagdollControls) + { + extraRagdollControls.AddToGUIUpdateList(); } } @@ -257,6 +283,18 @@ namespace Barotrauma // Handle shortcut keys if (GUI.KeyboardDispatcher.Subscriber == null && Wizard.instance == null) { + if (PlayerInput.KeyHit(Keys.D1)) + { + SetToggle(editLimbsToggle, true); + } + else if (PlayerInput.KeyHit(Keys.D2)) + { + SetToggle(jointsToggle, true); + } + else if (PlayerInput.KeyHit(Keys.D3)) + { + SetToggle(editAnimsToggle, true); + } if (PlayerInput.KeyDown(Keys.LeftControl)) { Character.DisableControls = true; @@ -313,9 +351,12 @@ namespace Barotrauma { copyJointsToggle.Selected = !copyJointsToggle.Selected; } - if (PlayerInput.KeyHit(Keys.T) || PlayerInput.KeyHit(Keys.X)) + if (character.IsHumanoid) { - animTestPoseToggle.Selected = !animTestPoseToggle.Selected; + if (PlayerInput.KeyHit(Keys.T) || PlayerInput.KeyHit(Keys.X)) + { + animTestPoseToggle.Selected = !animTestPoseToggle.Selected; + } } if (PlayerInput.KeyHit(InputType.Run)) { @@ -352,7 +393,7 @@ namespace Barotrauma CurrentAnimation.CreateSnapshot(); } } - if (PlayerInput.KeyHit(Keys.E)) + if (!PlayerInput.KeyDown(Keys.LeftControl) && PlayerInput.KeyHit(Keys.E)) { bool isSwimming = character.AnimController.ForceSelectAnimationType == AnimationType.SwimFast || character.AnimController.ForceSelectAnimationType == AnimationType.SwimSlow; if (isSwimming) @@ -366,9 +407,9 @@ namespace Barotrauma } if (PlayerInput.KeyHit(Keys.F)) { - freezeToggle.Selected = !freezeToggle.Selected; + SetToggle(freezeToggle, !freezeToggle.Selected); } - if (PlayerInput.RightButtonClicked()) + if (PlayerInput.RightButtonClicked() || PlayerInput.KeyHit(Keys.Escape)) { bool reset = false; if (selectedLimbs.Any()) @@ -390,6 +431,8 @@ namespace Barotrauma { ResetParamsEditor(); } + jointCreationMode = false; + closestSelectedLimb = null; } if (PlayerInput.KeyHit(Keys.Delete)) { @@ -397,15 +440,33 @@ namespace Barotrauma } if (editLimbs && PlayerInput.KeyDown(Keys.LeftControl)) { - if (PlayerInput.KeyHit(Keys.C)) + var selectedLimb = selectedLimbs.FirstOrDefault(); + if (selectedLimb != null) { - var selectedLimb = selectedLimbs.FirstOrDefault(); - if (selectedLimb != null) + if (PlayerInput.KeyHit(Keys.C)) { CopyLimb(selectedLimb); } } } + if (ShowExtraRagdollControls && PlayerInput.KeyDown(Keys.LeftControl)) + { + if (PlayerInput.KeyHit(Keys.E)) + { + jointCreationMode = !jointCreationMode; + useMouseOffset = true; + } + } + if (jointCreationMode) + { + createJointButton.HoverColor = Color.LightGreen; + createJointButton.Color = Color.LightGreen; + } + else + { + createJointButton.HoverColor = Color.White; + createJointButton.Color = Color.White; + } UpdateJointCreation(); if (PlayerInput.KeyHit(Keys.Left)) { @@ -489,20 +550,18 @@ namespace Barotrauma } // Camera Cam.MoveCamera((float)deltaTime, allowMove: false); - bool movementInput = PlayerInput.KeyDown(InputType.Up) || PlayerInput.KeyDown(InputType.Down) || PlayerInput.KeyDown(InputType.Left) || PlayerInput.KeyDown(InputType.Right); - // 0.2f, because we want to allow the user to pan when the character is floating in the water without actively moving - if (!isFreezed && (character.AnimController.Collider.LinearVelocity.LengthSquared() > 0.2f || movementInput)) - { - // Follow - Cam.Position = Vector2.SmoothStep(Cam.Position, character.WorldPosition, (float)deltaTime * cameraFollowSpeed); - } - else if (PlayerInput.MidButtonHeld()) + Vector2 targetPos = character.WorldPosition; + if (PlayerInput.MidButtonHeld()) { // Pan Vector2 moveSpeed = PlayerInput.MouseSpeed * (float)deltaTime * 100.0f / Cam.Zoom; moveSpeed.X = -moveSpeed.X; - cam.Position += moveSpeed; + cameraOffset += moveSpeed; + Vector2 max = new Vector2(GameMain.GraphicsWidth * 0.3f, GameMain.GraphicsHeight * 0.38f) / Cam.Zoom; + Vector2 min = -max; + cameraOffset = Vector2.Clamp(cameraOffset, min, max); } + Cam.Position = targetPos + cameraOffset; MapEntity.mapEntityList.ForEach(e => e.IsHighlighted = false); // Update widgets jointSelectionWidgets.Values.ForEach(w => w.Update((float)deltaTime)); @@ -526,6 +585,11 @@ namespace Barotrauma } } } + optionsToggle?.UpdateOpenState((float)deltaTime, new Vector2(optionsPanel.Rect.Width + rightArea.RectTransform.AbsoluteOffset.X, 0), optionsPanel.RectTransform); + fileEditToggle?.UpdateOpenState((float)deltaTime, new Vector2(-fileEditPanel.Rect.Width - rightArea.RectTransform.AbsoluteOffset.X, 0), fileEditPanel.RectTransform); + characterPanelToggle?.UpdateOpenState((float)deltaTime, new Vector2(-characterSelectionPanel.Rect.Width - rightArea.RectTransform.AbsoluteOffset.X, 0), characterSelectionPanel.RectTransform); + modesToggle?.UpdateOpenState((float)deltaTime, new Vector2(-modesPanel.Rect.Width - leftArea.RectTransform.AbsoluteOffset.X, 0), modesPanel.RectTransform); + toolsToggle?.UpdateOpenState((float)deltaTime, new Vector2(-toolsPanel.Rect.Width - leftArea.RectTransform.AbsoluteOffset.X, 0), toolsPanel.RectTransform); } /// @@ -541,10 +605,6 @@ namespace Barotrauma scaledMouseSpeed = PlayerInput.MouseSpeedPerSecond * (float)deltaTime; Cam.UpdateTransform(true); Submarine.CullEntities(Cam); - - // TODO: use the same drawing method as in game? - //GameMain.GameScreen.DrawMap(graphics, spriteBatch, deltaTime, Cam); - Submarine.MainSub.UpdateTransform(); // Lightmaps @@ -570,7 +630,7 @@ namespace Barotrauma { character.AnimController.DebugDraw(spriteBatch); } - else if (displayColliders) + else if (showColliders) { character.AnimController.Collider.DebugDraw(spriteBatch, Color.White, forceColor: true); character.AnimController.Limbs.ForEach(l => l.body.DebugDraw(spriteBatch, Color.LightGreen, forceColor: true)); @@ -595,7 +655,7 @@ namespace Barotrauma { DrawLimbEditor(spriteBatch); } - if (showRagdoll) + if (editRagdoll || editJoints || editLimbs) { DrawRagdoll(spriteBatch, (float)deltaTime); } @@ -603,48 +663,51 @@ namespace Barotrauma { DrawSpritesheetEditor(spriteBatch, (float)deltaTime); } - if (isExtrudingJoint) + if (jointCreationMode) { - var selectedJoint = selectedJoints.FirstOrDefault(); - if (selectedJoint != null) + var textPos = new Vector2(GameMain.GraphicsWidth / 2 - 120, GameMain.GraphicsHeight / 4); + if (isExtrudingJoint) { - GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2 - 120, GameMain.GraphicsHeight / 5), GetCharacterEditorTranslation("CreatingNewJoint"), Color.White, font: GUI.LargeFont); - if (spriteSheetRect.Contains(PlayerInput.MousePosition)) + var selectedJoint = selectedJoints.LastOrDefault(); + if (selectedJoint != null) { - var startPos = GetLimbSpritesheetRect(selectedJoint.LimbB).Center.ToVector2(); - var offset = ConvertUnits.ToDisplayUnits(selectedJoint.LocalAnchorB) * spriteSheetZoom; - offset.Y = -offset.Y; - DrawJointCreationOnSpritesheet(spriteBatch, startPos + offset); - } - else - { - DrawJointCreationOnRagdoll(spriteBatch, SimToScreen(selectedJoint.WorldAnchorB)); + GUI.DrawString(spriteBatch, textPos, GetCharacterEditorTranslation("CreatingNewJoint"), Color.White, font: GUI.LargeFont); + if (spriteSheetRect.Contains(PlayerInput.MousePosition)) + { + var startPos = GetLimbSpritesheetRect(selectedJoint.LimbB).Center.ToVector2(); + var offset = ConvertUnits.ToDisplayUnits(selectedJoint.LocalAnchorB) * spriteSheetZoom; + offset.Y = -offset.Y; + DrawJointCreationOnSpritesheet(spriteBatch, startPos + offset); + } + else + { + DrawJointCreationOnRagdoll(spriteBatch, SimToScreen(selectedJoint.WorldAnchorB)); + } } } - } - else if (isDrawingJoint) - { - var selectedLimb = selectedLimbs.FirstOrDefault(); - if (selectedLimb != null) + else if (isDrawingJoint) { - GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2 - 120, GameMain.GraphicsHeight / 5), GetCharacterEditorTranslation("CreatingNewJoint"), Color.White, font: GUI.LargeFont); - if (spriteSheetRect.Contains(PlayerInput.MousePosition)) + if (closestSelectedLimb != null) { - var startPos = GetLimbSpritesheetRect(selectedLimb).Center.ToVector2(); - if (anchor1Pos.HasValue) + GUI.DrawString(spriteBatch, textPos, GetCharacterEditorTranslation("CreatingNewJoint"), Color.White, font: GUI.LargeFont); + if (spriteSheetRect.Contains(PlayerInput.MousePosition)) { - var offset = anchor1Pos.Value; - offset = -offset; - startPos += offset; + var startPos = GetLimbSpritesheetRect(closestSelectedLimb).Center.ToVector2(); + if (anchor1Pos.HasValue) + { + var offset = anchor1Pos.Value; + offset = -offset; + startPos += offset; + } + DrawJointCreationOnSpritesheet(spriteBatch, startPos); + } + else + { + var startPos = anchor1Pos.HasValue + ? SimToScreen(closestSelectedLimb.SimPosition + Vector2.Transform(ConvertUnits.ToSimUnits(anchor1Pos.Value), Matrix.CreateRotationZ(closestSelectedLimb.Rotation))) + : SimToScreen(closestSelectedLimb.SimPosition); + DrawJointCreationOnRagdoll(spriteBatch, startPos); } - DrawJointCreationOnSpritesheet(spriteBatch, startPos); - } - else - { - var startPos = anchor1Pos.HasValue - ? SimToScreen(selectedLimb.SimPosition + Vector2.Transform(ConvertUnits.ToSimUnits(anchor1Pos.Value), Matrix.CreateRotationZ(selectedLimb.Rotation))) - : SimToScreen(selectedLimb.SimPosition); - DrawJointCreationOnRagdoll(spriteBatch, startPos); } } } @@ -657,11 +720,11 @@ namespace Barotrauma GUI.Draw(Cam, spriteBatch); if (isFreezed) { - GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2 - 35, 100), GetCharacterEditorTranslation("Frozen"), Color.Blue, Color.White * 0.5f, 10, GUI.Font); + GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2 - 35, 200), GetCharacterEditorTranslation("Frozen"), Color.Blue, Color.White * 0.5f, 10, GUI.Font); } if (animTestPoseToggle.Selected) { - GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2 - 100, 150), GetCharacterEditorTranslation("AnimationTestPoseEnabled"), Color.Blue, Color.White * 0.5f, 10, GUI.Font); + GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2 - 100, 300), GetCharacterEditorTranslation("AnimationTestPoseEnabled"), Color.Blue, Color.White * 0.5f, 10, GUI.Font); } if (showSpritesheet) { @@ -686,20 +749,6 @@ namespace Barotrauma GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2, 20), $"Cursor Pos: {character.CursorPosition}", Color.White, font: GUI.SmallFont); GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2, 40), $"Cursor Screen Pos: {PlayerInput.MousePosition}", Color.White, font: GUI.SmallFont); - //GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2, 80), $"Character World Pos: {character.WorldPosition}", Color.White, font: GUI.SmallFont); - //GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2, 100), $"Character Pos: {character.Position}", Color.White, font: GUI.SmallFont); - //GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2, 120), $"Character Sim Pos: {character.SimPosition}", Color.White, font: GUI.SmallFont); - //GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2, 140), $"Character Draw Pos: {character.DrawPosition}", Color.White, font: GUI.SmallFont); - - //GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2, 180), $"Submarine World Pos: {Submarine.MainSub.WorldPosition}", Color.White, font: GUI.SmallFont); - //GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2, 200), $"Submarine Pos: {Submarine.MainSub.Position}", Color.White, font: GUI.SmallFont); - //GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2, 220), $"Submarine Sim Pos: {Submarine.MainSub.Position}", Color.White, font: GUI.SmallFont); - //GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2, 240), $"Submarine Draw Pos: {Submarine.MainSub.DrawPosition}", Color.White, font: GUI.SmallFont); - - //GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2, 280), $"Movement Limits: MIN: {min} MAX: {max}", Color.White, font: GUI.SmallFont); - //GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2, 300), $"Clones: {clones.Length}", Color.White, font: GUI.SmallFont); - //GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2, 320), $"Total amount of walls: {Structure.WallList.Count}", Color.White, font: GUI.SmallFont); - // Collider var collider = character.AnimController.Collider; var colliderDrawPos = SimToScreen(collider.SimPosition); @@ -707,8 +756,6 @@ namespace Barotrauma var endPos = SimToScreen(collider.SimPosition + forward * collider.radius); GUI.DrawLine(spriteBatch, colliderDrawPos, endPos, Color.LightGreen); GUI.DrawLine(spriteBatch, colliderDrawPos, SimToScreen(collider.SimPosition + forward * 0.25f), Color.Blue); - //Vector2 left = Vector2.Transform(-Vector2.UnitX, Matrix.CreateRotationZ(collider.Rotation)); - //Vector2 left = -Vector2.UnitX.TransformVector(forward); Vector2 left = forward.Left(); GUI.DrawLine(spriteBatch, colliderDrawPos, SimToScreen(collider.SimPosition + left * 0.25f), Color.Red); ShapeExtensions.DrawCircle(spriteBatch, colliderDrawPos, (endPos - colliderDrawPos).Length(), 40, Color.LightGreen); @@ -721,12 +768,11 @@ namespace Barotrauma #region Ragdoll Manipulation private void UpdateJointCreation() { - bool ctrlAlt = PlayerInput.KeyDown(Keys.LeftControl) && PlayerInput.KeyDown(Keys.LeftAlt); - isExtrudingJoint = !editLimbs && editJoints && ctrlAlt; - isDrawingJoint = !editJoints && editLimbs && ctrlAlt; + isExtrudingJoint = !editLimbs && editJoints && jointCreationMode; + isDrawingJoint = !editJoints && editLimbs && jointCreationMode; if (isExtrudingJoint) { - var selectedJoint = selectedJoints.FirstOrDefault(); + var selectedJoint = selectedJoints.LastOrDefault(); if (selectedJoint != null) { if (spriteSheetRect.Contains(PlayerInput.MousePosition)) @@ -758,37 +804,48 @@ namespace Barotrauma } else if (isDrawingJoint) { - var selectedLimb = selectedLimbs.FirstOrDefault(); - if (selectedLimb != null) + if (selectedLimbs.Any()) { if (spriteSheetRect.Contains(PlayerInput.MousePosition)) { - if (anchor1Pos == null) + if (closestSelectedLimb == null) { - anchor1Pos = GetLimbSpritesheetRect(selectedLimb).Center.ToVector2() - PlayerInput.MousePosition; + closestSelectedLimb = GetClosestLimbOnSpritesheet(PlayerInput.MousePosition, l => selectedLimbs.Contains(l)); } - targetLimb = GetClosestLimbOnSpritesheet(PlayerInput.MousePosition, l => l != null && l != selectedLimb && l.ActiveSprite != null); + if (anchor1Pos == null && useMouseOffset) + { + anchor1Pos = GetLimbSpritesheetRect(closestSelectedLimb).Center.ToVector2() - PlayerInput.MousePosition; + } + targetLimb = GetClosestLimbOnSpritesheet(PlayerInput.MousePosition, l => l != null && l != closestSelectedLimb && l.ActiveSprite != null); if (targetLimb != null && PlayerInput.LeftButtonClicked()) { Vector2 anchor1 = anchor1Pos.HasValue ? anchor1Pos.Value / spriteSheetZoom : Vector2.Zero; anchor1.X = -anchor1.X; Vector2 anchor2 = (GetLimbSpritesheetRect(targetLimb).Center.ToVector2() - PlayerInput.MousePosition) / spriteSheetZoom; anchor2.X = -anchor2.X; - CreateJoint(selectedLimb.limbParams.ID, targetLimb.limbParams.ID, anchor1, anchor2); + CreateJoint(closestSelectedLimb.limbParams.ID, targetLimb.limbParams.ID, anchor1, anchor2); + jointCreationMode = false; + closestSelectedLimb = null; } } else { - if (anchor1Pos == null) + if (closestSelectedLimb == null) { - anchor1Pos = ConvertUnits.ToDisplayUnits(selectedLimb.body.FarseerBody.GetLocalPoint(ScreenToSim(PlayerInput.MousePosition))); + closestSelectedLimb = GetClosestLimbOnRagdoll(PlayerInput.MousePosition, l => selectedLimbs.Contains(l)); } - targetLimb = GetClosestLimbOnRagdoll(PlayerInput.MousePosition, l => l != null && l != selectedLimb && l.ActiveSprite != null); + if (anchor1Pos == null && useMouseOffset) + { + anchor1Pos = ConvertUnits.ToDisplayUnits(closestSelectedLimb.body.FarseerBody.GetLocalPoint(ScreenToSim(PlayerInput.MousePosition))); + } + targetLimb = GetClosestLimbOnRagdoll(PlayerInput.MousePosition, l => l != null && l != closestSelectedLimb && l.ActiveSprite != null); if (targetLimb != null && PlayerInput.LeftButtonClicked()) { Vector2 anchor1 = anchor1Pos ?? Vector2.Zero; Vector2 anchor2 = ConvertUnits.ToDisplayUnits(targetLimb.body.FarseerBody.GetLocalPoint(ScreenToSim(PlayerInput.MousePosition))); - CreateJoint(selectedLimb.limbParams.ID, targetLimb.limbParams.ID, anchor1, anchor2); + CreateJoint(closestSelectedLimb.limbParams.ID, targetLimb.limbParams.ID, anchor1, anchor2); + jointCreationMode = false; + closestSelectedLimb = null; } } } @@ -807,6 +864,7 @@ namespace Barotrauma private void CopyLimb(Limb limb) { + if (limb == null) { return; } //RagdollParams.StoreState(); // TODO: copy all params and sub params -> use a generic method? var rect = limb.ActiveSprite.SourceRect; @@ -844,6 +902,7 @@ namespace Barotrauma /// private void ExtrudeJoint(LimbJoint joint, int targetLimb, Vector2? anchor1 = null, Vector2? anchor2 = null) { + if (joint == null) { return; } CreateJoint(joint.jointParams.Limb2, targetLimb, anchor1, anchor2); } @@ -895,6 +954,7 @@ namespace Barotrauma for (int i = 0; i < selectedJoints.Count; i++) { var joint = selectedJoints[i]; + joint.jointParams.Element.Remove(); RagdollParams.Joints.Remove(joint.jointParams); } var removedIDs = new List(); @@ -906,12 +966,11 @@ namespace Barotrauma break; } var limb = selectedLimbs[i]; - //if (limb == character.AnimController.MainLimb) - //{ - // // TODO: this should be possible now -> test - // DebugConsole.ThrowError("Can't remove the main limb, because it will crash the game."); - // continue; - //} + if (limb == character.AnimController.MainLimb) + { + DebugConsole.ThrowError("Can't remove the main limb, because it will cause unreveratable issues."); + continue; + } removedIDs.Add(limb.limbParams.ID); limb.limbParams.Element.Remove(); RagdollParams.Limbs.Remove(limb.limbParams); @@ -1201,6 +1260,7 @@ namespace Barotrauma private void OnPreSpawn() { + cameraOffset = Vector2.Zero; WayPoint wayPoint = null; if (!isEndlessRunner) { @@ -1289,16 +1349,48 @@ namespace Barotrauma Cam.Position = character.WorldPosition; } - private bool CreateCharacter(string name, string mainFolder, bool isHumanoid, params object[] ragdollConfig) + private bool CreateCharacter(string name, string mainFolder, bool isHumanoid, ContentPackage contentPackage = null, params object[] ragdollConfig) { - var contentPackage = GameMain.Config.SelectedContentPackages.LastOrDefault(); + var vanilla = GameMain.VanillaContent; + if (contentPackage == null) { - // This should not normally happen. +#if DEBUG + contentPackage = GameMain.Config.SelectedContentPackages.LastOrDefault(); +#else + contentPackage = GameMain.Config.SelectedContentPackages.LastOrDefault(cp => cp != vanilla); +#endif + } + if (contentPackage == null) + { + string modName = "NewCharacterMod"; + if (ContentPackage.List.Any(cp => cp.Name == modName)) + { + string tempName = modName; + for (int i = 0; i < 100; i++) + { + tempName = modName + i.ToString(); + if (ContentPackage.List.None(cp => cp.Name == tempName)) + { + modName = tempName; + break; + } + } + } + contentPackage = ContentPackage.CreatePackage(modName, Path.Combine(ContentPackage.Folder, $"{modName}.xml"), false); + ContentPackage.List.Add(contentPackage); + } + if (contentPackage == null) + { + // This should not be possible. DebugConsole.ThrowError(GetCharacterEditorTranslation("NoContentPackageSelected")); return false; } - var vanilla = GameMain.VanillaContent; + if (!GameMain.Config.SelectedContentPackages.Contains(contentPackage)) + { + GameMain.Config.SelectedContentPackages.Add(contentPackage); + GameMain.Config.SaveNewPlayerConfig(); + } #if !DEBUG if (vanilla != null && contentPackage == vanilla) { @@ -1306,7 +1398,6 @@ namespace Barotrauma return false; } #endif - string speciesName = name; // Config file string configFilePath = Path.Combine(mainFolder, $"{speciesName}.xml").Replace(@"\", @"/"); @@ -1367,6 +1458,10 @@ namespace Barotrauma AllFiles.Add(configFilePath); } SpawnCharacter(configFilePath, ragdollParams); + + editLimbsToggle.Selected = true; + recalculateColliderToggle.Selected = true; + selectedLimbs.Add(character.AnimController.Limbs.First()); return true; } @@ -1388,61 +1483,355 @@ namespace Barotrauma #endregion #region GUI - private GUIFrame rightPanel; - private GUIFrame leftPanel; - private GUIFrame centerPanel; + private static Point outerMargin = new Point(0, 0); + private static Point innerMargin = new Point(40, 40); + private static Color panelColor = new Color(20, 20, 20, 255); + private static Color toggleButtonColor = new Color(0.4f, 0.4f, 0.4f, 1); + + private GUIFrame rightArea; + private GUIFrame leftArea; + private GUIFrame centerArea; + + private GUIFrame characterSelectionPanel; + private GUIFrame fileEditPanel; + private GUIFrame modesPanel; + private GUIFrame toolsPanel; + private GUIFrame optionsPanel; + private GUIFrame ragdollControls; + private GUIFrame jointControls; private GUIFrame animationControls; private GUIFrame limbControls; private GUIFrame spriteSheetControls; private GUIFrame backgroundColorPanel; + private GUIDropDown animSelection; private GUITickBox freezeToggle; private GUITickBox animTestPoseToggle; - private GUITickBox displayCollidersToggle; + private GUITickBox showCollidersToggle; private GUIScrollBar jointScaleBar; private GUIScrollBar limbScaleBar; private GUIScrollBar spriteSheetZoomBar; private GUITickBox copyJointsToggle; + private GUITickBox recalculateColliderToggle; + private GUITickBox jointsToggle; private GUITickBox editAnimsToggle; private GUITickBox editLimbsToggle; + private GUITickBox paramsToggle; + private GUITickBox spritesheetToggle; + private GUITickBox ragdollToggle; + private GUITickBox ikToggle; + private GUIFrame extraRagdollControls; + private GUIButton duplicateLimbButton; + private GUIButton deleteSelectedButton; + private GUIButton createJointButton; + + private ToggleButton modesToggle; + private ToggleButton toolsToggle; + private ToggleButton optionsToggle; + private ToggleButton characterPanelToggle; + private ToggleButton fileEditToggle; private void CreateGUI() { - CreateRightPanel(); - CreateCenterPanel(); - } - - private void CreateCenterPanel() - { - // Release the old panels - if (centerPanel != null) + // Release the old areas + if (rightArea != null) { - centerPanel.RectTransform.Parent = null; + rightArea.RectTransform.Parent = null; } - if (leftPanel != null) + if (centerArea != null) { - leftPanel.RectTransform.Parent = null; + centerArea.RectTransform.Parent = null; } - centerPanel = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.95f), parent: Frame.RectTransform, anchor: Anchor.TopRight) + if (leftArea != null) { - AbsoluteOffset = new Point((int)(rightPanel.RectTransform.ScaledSize.X + rightPanel.RectTransform.RelativeOffset.X * rightPanel.RectTransform.Parent.ScaledSize.X + 20), 20) + leftArea.RectTransform.Parent = null; + } + // Create the areas + rightArea = new GUIFrame(new RectTransform(new Vector2(0.15f, 0.95f), parent: Frame.RectTransform, anchor: Anchor.CenterRight) + { + AbsoluteOffset = new Point(outerMargin.X, 0) }, style: null) { CanBeFocused = false }; - leftPanel = new GUIFrame(new RectTransform(new Vector2(0.2f, 0.95f), parent: Frame.RectTransform, anchor: Anchor.CenterLeft) + centerArea = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.95f), parent: Frame.RectTransform, anchor: Anchor.TopRight) { - AbsoluteOffset = new Point(20, 0) + AbsoluteOffset = new Point((int)(rightArea.RectTransform.ScaledSize.X + rightArea.RectTransform.RelativeOffset.X * rightArea.RectTransform.Parent.ScaledSize.X + 20), outerMargin.Y + 20) + + }, style: null) + { CanBeFocused = false }; + leftArea = new GUIFrame(new RectTransform(new Vector2(0.2f, 0.95f), parent: Frame.RectTransform, anchor: Anchor.CenterLeft) + { + AbsoluteOffset = new Point(outerMargin.X, 0) }, style: null) { CanBeFocused = false }; + + Vector2 buttonSize = new Vector2(1, 0.04f); + Vector2 toggleSize = new Vector2(0.03f, 0.03f); + + CreateCharacterSelectionPanel(); + CreateModesPanel(toggleSize); + CreateToolsPanel(); + CreateFileEditPanel(); + CreateOptionsPanel(toggleSize); + CreateContextualControls(); + } + + private void CreateModesPanel(Vector2 toggleSize) + { + modesPanel = new GUIFrame(new RectTransform(new Vector2(0.6f, 0.3f), leftArea.RectTransform, Anchor.BottomLeft), style: null, color: panelColor); + var layoutGroup = new GUILayoutGroup(new RectTransform(new Point(modesPanel.Rect.Width - innerMargin.X, modesPanel.Rect.Height - innerMargin.Y), + modesPanel.RectTransform, Anchor.Center)) + { + AbsoluteSpacing = 2, + Stretch = true + }; + + new GUITextBlock(new RectTransform(new Vector2(0.03f, 0.06f), layoutGroup.RectTransform), GetCharacterEditorTranslation("ModesPanel"), font: GUI.LargeFont); + // Main modes + editLimbsToggle = new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("EditLimbs")) { Selected = editLimbs }; + jointsToggle = new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("EditJoints")) { Selected = editJoints }; + editAnimsToggle = new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("EditAnimations")) { Selected = editAnimations }; + // Spacing + new GUIFrame(new RectTransform(toggleSize, layoutGroup.RectTransform), style: null) { CanBeFocused = false }; + // Minor modes + ragdollToggle = new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("ShowRagdoll")) { Selected = editRagdoll }; + paramsToggle = new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("ShowParameters")) { Selected = showParamsEditor }; + spritesheetToggle = new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("ShowSpriteSheet")) { Selected = showSpritesheet }; + showCollidersToggle = new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("ShowColliders")) + { + Selected = showColliders, + OnSelected = box => + { + showColliders = box.Selected; + return true; + } + }; + ikToggle = new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("EditIKTargets")) { Selected = editIK }; + editAnimsToggle.OnSelected = box => + { + editAnimations = box.Selected; + if (editAnimations) + { + SetToggle(editLimbsToggle, false); + SetToggle(jointsToggle, false); + spritesheetToggle.Selected = false; + ClearSelection(); + } + ResetParamsEditor(); + return true; + }; + paramsToggle.OnSelected = box => + { + showParamsEditor = box.Selected; + return true; + }; + editLimbsToggle.OnSelected = box => + { + editLimbs = box.Selected; + if (editLimbs) + { + SetToggle(editAnimsToggle, false); + SetToggle(jointsToggle, false); + spritesheetToggle.Selected = true; + ClearSelection(); + } + ResetParamsEditor(); + return true; + }; + ragdollToggle.OnSelected = box => + { + editRagdoll = box.Selected; + if (editRagdoll) + { + if (!editIK) + { + paramsToggle.Selected = true; + } + ClearSelection(); + } + ResetParamsEditor(); + return true; + }; + jointsToggle.OnSelected = box => + { + editJoints = box.Selected; + if (editJoints) + { + SetToggle(editLimbsToggle, false); + SetToggle(editAnimsToggle, false); + ikToggle.Selected = false; + spritesheetToggle.Selected = true; + ClearSelection(); + } + ResetParamsEditor(); + return true; + }; + ikToggle.OnSelected = box => + { + editIK = box.Selected; + if (editIK) + { + ragdollToggle.Selected = true; + } + return true; + }; + spritesheetToggle.OnSelected = box => + { + showSpritesheet = box.Selected; + return true; + }; + modesToggle = new ToggleButton(new RectTransform(new Vector2(0.125f, 1), modesPanel.RectTransform, Anchor.CenterRight, Pivot.CenterLeft), Direction.Left); + } + + private void SetToggle(GUITickBox toggle, bool value) + { + if (toggle.Selected != value) + { + if (value) + { + toggle.Box.Flash(Color.LightGreen, useRectangleFlash: true); + } + else + { + toggle.Box.Flash(Color.Red, useRectangleFlash: true); + } + } + toggle.Selected = value; + } + + private void CreateToolsPanel() + { + Vector2 buttonSize = new Vector2(1, 0.06f); + toolsPanel = new GUIFrame(new RectTransform(new Vector2(0.6f, 0.15f), leftArea.RectTransform, Anchor.CenterLeft) + { + RelativeOffset = new Vector2(0, 0.1f) + }, style: null, color: panelColor); + var layoutGroup = new GUILayoutGroup(new RectTransform(new Point(toolsPanel.Rect.Width - innerMargin.X, toolsPanel.Rect.Height - innerMargin.Y), + toolsPanel.RectTransform, Anchor.Center)) + { + AbsoluteSpacing = 2, + Stretch = true + }; + new GUITextBlock(new RectTransform(new Vector2(0.03f, 0.06f), layoutGroup.RectTransform), GetCharacterEditorTranslation("ToolsPanel"), font: GUI.LargeFont); + var reloadTexturesButton = new GUIButton(new RectTransform(buttonSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("ReloadTextures")); + reloadTexturesButton.OnClicked += (button, userData) => + { + foreach (var limb in character.AnimController.Limbs) + { + limb.ActiveSprite.ReloadTexture(); + limb.WearingItems.ForEach(i => i.Sprite.ReloadTexture()); + limb.OtherWearables.ForEach(w => w.Sprite.ReloadTexture()); + } + CreateTextures(); + return true; + }; + new GUIButton(new RectTransform(buttonSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("RecreateRagdoll")) + { + ToolTip = GetCharacterEditorTranslation("RecreateRagdollTooltip"), + OnClicked = (button, data) => + { + RecreateRagdoll(); + character.AnimController.ResetLimbs(); + return true; + } + }; + + toolsToggle = new ToggleButton(new RectTransform(new Vector2(0.125f, 1), toolsPanel.RectTransform, Anchor.CenterRight, Pivot.CenterLeft), Direction.Left); + } + + private void CreateOptionsPanel(Vector2 toggleSize) + { + optionsPanel = new GUIFrame(new RectTransform(new Vector2(1, 0.3f), rightArea.RectTransform, Anchor.Center) + { + RelativeOffset = new Vector2(0, -0.075f) + }, style: null, color: panelColor); + var layoutGroup = new GUILayoutGroup(new RectTransform(new Point(optionsPanel.Rect.Width - innerMargin.X, optionsPanel.Rect.Height - innerMargin.Y), + optionsPanel.RectTransform, Anchor.Center)) + { + AbsoluteSpacing = 2, + Stretch = true + }; + + new GUITextBlock(new RectTransform(new Vector2(0.03f, 0.06f), layoutGroup.RectTransform), GetCharacterEditorTranslation("OptionsPanel"), font: GUI.LargeFont); + freezeToggle = new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("Freeze")) { Selected = isFreezed }; + var autoFreezeToggle = new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("AutoFreeze")) { Selected = autoFreeze }; + var limbPairEditToggle = new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("LimbPairEditing")) + { + Selected = limbPairEditing, + Enabled = character.IsHumanoid // TODO: remove when limb pair editing works for non-humanoids + }; + animTestPoseToggle = new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("AnimationTestPose")) + { + Selected = character.AnimController.AnimationTestPose, + Enabled = character.IsHumanoid + }; + new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("AutoMove")) + { + Selected = character.OverrideMovement != null, + OnSelected = box => + { + character.OverrideMovement = box.Selected ? new Vector2(1, 0) as Vector2? : null; + return true; + } + }; + new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("FollowCursor")) + { + Selected = !character.dontFollowCursor, + OnSelected = box => + { + character.dontFollowCursor = !box.Selected; + return true; + } + }; + new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("EditBackgroundColor")) + { + Selected = displayBackgroundColor, + OnSelected = box => + { + displayBackgroundColor = box.Selected; + return true; + } + }; + freezeToggle.OnSelected = box => + { + isFreezed = box.Selected; + return true; + }; + autoFreezeToggle.OnSelected = box => + { + autoFreeze = box.Selected; + return true; + }; + limbPairEditToggle.OnSelected = box => + { + limbPairEditing = box.Selected; + return true; + }; + animTestPoseToggle.OnSelected = box => + { + character.AnimController.AnimationTestPose = box.Selected; + return true; + }; + optionsToggle = new ToggleButton(new RectTransform(new Vector2(0.1f, 1), optionsPanel.RectTransform, Anchor.CenterLeft, Pivot.CenterRight), Direction.Right); + } + + private void CreateContextualControls() + { Point elementSize = new Point(120, 20); int textAreaHeight = 20; // General controls - backgroundColorPanel = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.1f), centerPanel.RectTransform, Anchor.TopRight), style: null) { CanBeFocused = false }; + backgroundColorPanel = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.1f), centerArea.RectTransform, Anchor.TopRight) + { + AbsoluteOffset = new Point(10, 0) + }, style: null) + { + CanBeFocused = false + }; // Background color - var frame = new GUIFrame(new RectTransform(new Point(400, 80), backgroundColorPanel.RectTransform, Anchor.TopRight), style: null, color: Color.Black * 0.4f); + var frame = new GUIFrame(new RectTransform(new Point(500, 80), backgroundColorPanel.RectTransform, Anchor.TopRight), style: null, color: Color.Black * 0.4f); new GUITextBlock(new RectTransform(new Vector2(0.2f, 1), frame.RectTransform) { MinSize = new Point(80, 26) }, GetCharacterEditorTranslation("BackgroundColor") + ":", textColor: Color.WhiteSmoke); var inputArea = new GUILayoutGroup(new RectTransform(new Vector2(0.7f, 1), frame.RectTransform, Anchor.TopRight) { @@ -1456,7 +1845,7 @@ namespace Barotrauma string[] colorComponentLabels = { "R", "G", "B" }; for (int i = 2; i >= 0; i--) { - var element = new GUIFrame(new RectTransform(new Vector2(0.2f, 1), inputArea.RectTransform) + var element = new GUIFrame(new RectTransform(new Vector2(0.3f, 1), inputArea.RectTransform) { MinSize = new Point(40, 0), MaxSize = new Point(100, 50) @@ -1464,7 +1853,7 @@ namespace Barotrauma var colorLabel = new GUITextBlock(new RectTransform(new Vector2(0.3f, 1), element.RectTransform, Anchor.CenterLeft), colorComponentLabels[i], font: GUI.SmallFont, textAlignment: Alignment.CenterLeft); GUINumberInput numberInput = new GUINumberInput(new RectTransform(new Vector2(0.7f, 1), element.RectTransform, Anchor.CenterRight), - GUINumberInput.NumberType.Int) + GUINumberInput.NumberType.Int, relativeButtonAreaWidth: 0.25f) { Font = GUI.SmallFont }; @@ -1476,22 +1865,25 @@ namespace Barotrauma case 0: colorLabel.TextColor = Color.Red; numberInput.IntValue = backgroundColor.R; - numberInput.OnValueChanged += (numInput) => backgroundColor.R = (byte)(numInput.IntValue); + numberInput.OnValueChanged += (numInput) => backgroundColor.R = (byte)numInput.IntValue; break; case 1: colorLabel.TextColor = Color.LightGreen; numberInput.IntValue = backgroundColor.G; - numberInput.OnValueChanged += (numInput) => backgroundColor.G = (byte)(numInput.IntValue); + numberInput.OnValueChanged += (numInput) => backgroundColor.G = (byte)numInput.IntValue; break; case 2: colorLabel.TextColor = Color.DeepSkyBlue; numberInput.IntValue = backgroundColor.B; - numberInput.OnValueChanged += (numInput) => backgroundColor.B = (byte)(numInput.IntValue); + numberInput.OnValueChanged += (numInput) => backgroundColor.B = (byte)numInput.IntValue; break; } } // Spritesheet controls - spriteSheetControls = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.1f), centerPanel.RectTransform, Anchor.BottomLeft), style: null) + spriteSheetControls = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.1f), centerArea.RectTransform, Anchor.BottomLeft) + { + RelativeOffset = new Vector2(0, 0.05f) + }, style: null) { CanBeFocused = false }; @@ -1509,7 +1901,7 @@ namespace Barotrauma return true; } }; - new GUIButton(new RectTransform(new Vector2(0.2f, 1), spriteSheetControlElement.RectTransform, Anchor.TopRight), GetCharacterEditorTranslation("Reset")) + new GUIButton(new RectTransform(new Vector2(0.3f, 1), spriteSheetControlElement.RectTransform, Anchor.TopRight), GetCharacterEditorTranslation("Reset")) { OnClicked = (box, data) => { @@ -1558,7 +1950,7 @@ namespace Barotrauma // } //}; // Limb controls - limbControls = new GUIFrame(new RectTransform(Vector2.One, centerPanel.RectTransform), style: null) { CanBeFocused = false }; + limbControls = new GUIFrame(new RectTransform(Vector2.One, centerArea.RectTransform), style: null) { CanBeFocused = false }; var layoutGroupLimbControls = new GUILayoutGroup(new RectTransform(Vector2.One, limbControls.RectTransform), childAnchor: Anchor.TopLeft) { CanBeFocused = false }; var lockSpriteOriginToggle = new GUITickBox(new RectTransform(new Point(elementSize.X, textAreaHeight), layoutGroupLimbControls.RectTransform), GetCharacterEditorTranslation("LockSpriteOrigin")) { @@ -1590,32 +1982,22 @@ namespace Barotrauma } }; lockSpriteSizeToggle.TextColor = Color.White; - var recalculateColliderToggle = new GUITickBox(new RectTransform(new Point(elementSize.X, textAreaHeight), layoutGroupLimbControls.RectTransform), GetCharacterEditorTranslation("AdjustCollider")) + recalculateColliderToggle = new GUITickBox(new RectTransform(new Point(elementSize.X, textAreaHeight), layoutGroupLimbControls.RectTransform), GetCharacterEditorTranslation("AdjustCollider")) { Selected = recalculateCollider, OnSelected = (GUITickBox box) => { recalculateCollider = box.Selected; - displayCollidersToggle.Selected = recalculateCollider; + showCollidersToggle.Selected = recalculateCollider; return true; } }; recalculateColliderToggle.TextColor = Color.White; - // Ragdoll + // Joint controls Point sliderSize = new Point(300, 20); - ragdollControls = new GUIFrame(new RectTransform(Vector2.One, centerPanel.RectTransform), style: null) { CanBeFocused = false }; - var layoutGroupRagdoll = new GUILayoutGroup(new RectTransform(Vector2.One, ragdollControls.RectTransform), childAnchor: Anchor.TopLeft) { CanBeFocused = false }; - var uniformScalingToggle = new GUITickBox(new RectTransform(new Point(elementSize.X, textAreaHeight), layoutGroupRagdoll.RectTransform), GetCharacterEditorTranslation("UniformScale")) - { - Selected = uniformScaling, - OnSelected = (GUITickBox box) => - { - uniformScaling = box.Selected; - return true; - } - }; - uniformScalingToggle.TextColor = Color.White; - copyJointsToggle = new GUITickBox(new RectTransform(new Point(elementSize.X, textAreaHeight), layoutGroupRagdoll.RectTransform), GetCharacterEditorTranslation("CopyJointSettings")) + jointControls = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.075f), centerArea.RectTransform), style: null) { CanBeFocused = false }; + var layoutGroupJoints = new GUILayoutGroup(new RectTransform(Vector2.One, jointControls.RectTransform), childAnchor: Anchor.TopLeft) { CanBeFocused = false }; + copyJointsToggle = new GUITickBox(new RectTransform(new Point(elementSize.X, textAreaHeight), layoutGroupJoints.RectTransform), GetCharacterEditorTranslation("CopyJointSettings")) { ToolTip = GetCharacterEditorTranslation("CopyJointSettingsTooltip"), Selected = copyJointSettings, @@ -1627,8 +2009,19 @@ namespace Barotrauma return true; } }; - // Spacing - new GUIFrame(new RectTransform(new Point(elementSize.X, textAreaHeight), layoutGroupRagdoll.RectTransform), style: null) { CanBeFocused = false }; + // Ragdoll controls + ragdollControls = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.25f), centerArea.RectTransform) { AbsoluteOffset = new Point(0, jointControls.Rect.Bottom) }, style: null) { CanBeFocused = false }; + var layoutGroupRagdoll = new GUILayoutGroup(new RectTransform(Vector2.One, ragdollControls.RectTransform), childAnchor: Anchor.TopLeft) { CanBeFocused = false }; + var uniformScalingToggle = new GUITickBox(new RectTransform(new Point(elementSize.X, textAreaHeight), layoutGroupRagdoll.RectTransform), GetCharacterEditorTranslation("UniformScale")) + { + Selected = uniformScaling, + OnSelected = (GUITickBox box) => + { + uniformScaling = box.Selected; + return true; + } + }; + uniformScalingToggle.TextColor = Color.White; var jointScaleElement = new GUIFrame(new RectTransform(sliderSize + new Point(0, textAreaHeight), layoutGroupRagdoll.RectTransform), style: null); var jointScaleText = new GUITextBlock(new RectTransform(new Point(elementSize.X, textAreaHeight), jointScaleElement.RectTransform), $"{GetCharacterEditorTranslation("JointScale")}: {RagdollParams.JointScale.FormatDoubleDecimal()}", Color.WhiteSmoke, textAlignment: Alignment.Center); var limbScaleElement = new GUIFrame(new RectTransform(sliderSize + new Point(0, textAreaHeight), layoutGroupRagdoll.RectTransform), style: null); @@ -1695,8 +2088,44 @@ namespace Barotrauma ragdollResetRequiresForceLoading = true; return true; }; + + // Ragdoll manipulation + extraRagdollControls = new GUIFrame(new RectTransform(new Point(140, 30), centerArea.RectTransform, Anchor.BottomRight) + { + RelativeOffset = new Vector2(0.2f, 0.15f) + }, style: null) + { + CanBeFocused = false + }; + var extraRagdollLayout = new GUILayoutGroup(new RectTransform(Vector2.One, extraRagdollControls.RectTransform)); + duplicateLimbButton = new GUIButton(new RectTransform(new Point(140, 30), extraRagdollLayout.RectTransform), "Duplicate Limb") + { + OnClicked = (button, data) => + { + CopyLimb(selectedLimbs.FirstOrDefault()); + return true; + } + }; + deleteSelectedButton = new GUIButton(new RectTransform(new Point(140, 30), extraRagdollLayout.RectTransform), "Delete Selected") + { + OnClicked = (button, data) => + { + DeleteSelected(); + return true; + } + }; + createJointButton = new GUIButton(new RectTransform(new Point(140, 30), extraRagdollLayout.RectTransform), "Create Joint") + { + OnClicked = (button, data) => + { + jointCreationMode = !jointCreationMode; + useMouseOffset = false; + return true; + } + }; + // Animation - animationControls = new GUIFrame(new RectTransform(Vector2.One, centerPanel.RectTransform), style: null) { CanBeFocused = false }; + animationControls = new GUIFrame(new RectTransform(Vector2.One, centerArea.RectTransform), style: null) { CanBeFocused = false }; var layoutGroupAnimation = new GUILayoutGroup(new RectTransform(Vector2.One, animationControls.RectTransform), childAnchor: Anchor.TopLeft) { CanBeFocused = false }; var animationSelectionElement = new GUIFrame(new RectTransform(new Point(elementSize.X * 2 - 5, elementSize.Y), layoutGroupAnimation.RectTransform), style: null); var animationSelectionText = new GUITextBlock(new RectTransform(new Point(elementSize.X, elementSize.Y), animationSelectionElement.RectTransform), GetCharacterEditorTranslation("SelectedAnimation") + ": ", Color.WhiteSmoke, textAlignment: Alignment.Center); @@ -1769,31 +2198,30 @@ namespace Barotrauma }; } - private void CreateRightPanel() + private void CreateCharacterSelectionPanel() { - // Release the old panel - if (rightPanel != null) + characterSelectionPanel = new GUIFrame(new RectTransform(new Vector2(1, 0.25f), rightArea.RectTransform, Anchor.TopRight), style: null, color: panelColor); + var padding = new GUIFrame(new RectTransform(new Point(characterSelectionPanel.Rect.Width - innerMargin.X, characterSelectionPanel.Rect.Height - innerMargin.Y), + characterSelectionPanel.RectTransform, Anchor.Center), style: null) { - rightPanel.RectTransform.Parent = null; - } - Vector2 buttonSize = new Vector2(1, 0.04f); - Vector2 toggleSize = new Vector2(0.03f, 0.03f); - Point margin = new Point(40, 60); - rightPanel = new GUIFrame(new RectTransform(new Vector2(0.15f, 1.0f), parent: Frame.RectTransform, anchor: Anchor.CenterRight), style: "GUIFrameRight"); - var layoutGroup = new GUILayoutGroup(new RectTransform(new Point(rightPanel.Rect.Width - margin.X, rightPanel.Rect.Height - margin.Y), rightPanel.RectTransform, Anchor.Center)) - { - Stretch = true + CanBeFocused = false }; - var disclaimerBtnHolder = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.04f), layoutGroup.RectTransform), style: null); - + // Disclaimer + var disclaimerBtnHolder = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.2f), padding.RectTransform), style: null); var disclaimerBtn = new GUIButton(new RectTransform(new Vector2(1.0f, 0.8f), disclaimerBtnHolder.RectTransform, Anchor.TopRight), style: "GUINotificationButton") { OnClicked = (btn, userdata) => { GameMain.Instance.ShowEditorDisclaimer(); return true; } }; disclaimerBtn.RectTransform.MaxSize = new Point(disclaimerBtn.Rect.Height); - var characterDropDown = new GUIDropDown(new RectTransform(new Vector2(1, 0.04f), layoutGroup.RectTransform), elementCount: 10, style: null); + // Character selection + new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.2f), padding.RectTransform), GetCharacterEditorTranslation("CharacterPanel"), font: GUI.LargeFont); + + var characterDropDown = new GUIDropDown(new RectTransform(new Vector2(1, 0.2f), padding.RectTransform) + { + RelativeOffset = new Vector2(0, 0.2f) + }, elementCount: 8, style: null); characterDropDown.ListBox.Color = new Color(characterDropDown.ListBox.Color.R, characterDropDown.ListBox.Color.G, characterDropDown.ListBox.Color.B, byte.MaxValue); foreach (var file in AllFiles) { @@ -1807,7 +2235,10 @@ namespace Barotrauma }; if (currentCharacterConfig == Character.HumanConfigFile) { - var jobDropDown = new GUIDropDown(new RectTransform(new Vector2(1, 0.04f), layoutGroup.RectTransform), elementCount: 7, style: null); + var jobDropDown = new GUIDropDown(new RectTransform(new Vector2(1, 0.15f), padding.RectTransform) + { + RelativeOffset = new Vector2(0, 0.45f) + }, elementCount: 8, style: null); jobDropDown.ListBox.Color = new Color(jobDropDown.ListBox.Color.R, jobDropDown.ListBox.Color.G, jobDropDown.ListBox.Color.B, byte.MaxValue); jobDropDown.AddItem("None"); JobPrefab.List.ForEach(j => jobDropDown.AddItem(j.Name, j.Identifier)); @@ -1823,7 +2254,7 @@ namespace Barotrauma return true; }; } - var charButtons = new GUIFrame(new RectTransform(new Vector2(buttonSize.X, buttonSize.Y * 1.5f), parent: layoutGroup.RectTransform), style: null); + var charButtons = new GUIFrame(new RectTransform(new Vector2(1, 0.25f), parent: padding.RectTransform, anchor: Anchor.BottomLeft), style: null); var prevCharacterButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), charButtons.RectTransform, Anchor.TopLeft), GetCharacterEditorTranslation("PreviousCharacter")); prevCharacterButton.TextBlock.AutoScale = true; prevCharacterButton.OnClicked += (b, obj) => @@ -1838,133 +2269,27 @@ namespace Barotrauma SpawnCharacter(GetNextConfigFile()); return true; }; - var paramsToggle = new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("ShowParameters")) { Selected = showParamsEditor }; - var spritesheetToggle = new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("ShowSpriteSheet")) { Selected = showSpritesheet }; - var ragdollToggle = new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("ShowRagdoll")) { Selected = showRagdoll }; - editAnimsToggle = new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("EditAnimations")) { Selected = editAnimations }; - editLimbsToggle = new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("EditLimbs")) { Selected = editLimbs }; - jointsToggle = new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("EditJoints")) { Selected = editJoints }; - var ikToggle = new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("EditIKTargets")) { Selected = editIK }; - freezeToggle = new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("Freeze")) { Selected = isFreezed }; - var autoFreezeToggle = new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("AutoFreeze")) { Selected = autoFreeze }; - var limbPairEditToggle = new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("LimbPairEditing")) { Selected = limbPairEditing }; - animTestPoseToggle = new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("AnimationTestPose")) { Selected = character.AnimController.AnimationTestPose }; - editAnimsToggle.OnSelected = box => + characterPanelToggle = new ToggleButton(new RectTransform(new Vector2(0.1f, 1), characterSelectionPanel.RectTransform, Anchor.CenterLeft, Pivot.CenterRight), Direction.Right); + } + + private void CreateFileEditPanel() + { + Vector2 buttonSize = new Vector2(1, 0.04f); + + fileEditPanel = new GUIFrame(new RectTransform(new Vector2(1, 0.4f), rightArea.RectTransform, Anchor.BottomRight), style: null, color: panelColor); + var layoutGroup = new GUILayoutGroup(new RectTransform(new Point(fileEditPanel.Rect.Width - innerMargin.X, fileEditPanel.Rect.Height - innerMargin.Y), + fileEditPanel.RectTransform, Anchor.Center)) { - editAnimations = box.Selected; - if (editAnimations) - { - spritesheetToggle.Selected = false; - editLimbsToggle.Selected = false; - jointsToggle.Selected = false; - ResetParamsEditor(); - } - return true; - }; - paramsToggle.OnSelected = box => - { - showParamsEditor = box.Selected; - return true; - }; - editLimbsToggle.OnSelected = box => - { - editLimbs = box.Selected; - if (editLimbs) - { - editAnimsToggle.Selected = false; - jointsToggle.Selected = false; - spritesheetToggle.Selected = true; - ResetParamsEditor(); - } - return true; - }; - ragdollToggle.OnSelected = box => showRagdoll = box.Selected; - jointsToggle.OnSelected = box => - { - editJoints = box.Selected; - if (editJoints) - { - editLimbsToggle.Selected = false; - editAnimsToggle.Selected = false; - ikToggle.Selected = false; - spritesheetToggle.Selected = true; - ragdollToggle.Selected = true; - ResetParamsEditor(); - } - return true; - }; - ikToggle.OnSelected = box => - { - editIK = box.Selected; - if (editIK) - { - ragdollToggle.Selected = true; - } - return true; - }; - spritesheetToggle.OnSelected = box => - { - showSpritesheet = box.Selected; - return true; - }; - freezeToggle.OnSelected = box => - { - isFreezed = box.Selected; - return true; - }; - autoFreezeToggle.OnSelected = box => - { - autoFreeze = box.Selected; - return true; - }; - limbPairEditToggle.OnSelected = box => - { - limbPairEditing = box.Selected; - return true; - }; - animTestPoseToggle.OnSelected = box => - { - character.AnimController.AnimationTestPose = box.Selected; - return true; - }; - new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("AutoMove")) - { - Selected = character.OverrideMovement != null, - OnSelected = box => - { - character.OverrideMovement = box.Selected ? new Vector2(1, 0) as Vector2? : null; - return true; - } - }; - new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("FollowCursor")) - { - Selected = !character.dontFollowCursor, - OnSelected = box => - { - character.dontFollowCursor = !box.Selected; - return true; - } - }; - displayCollidersToggle = new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("DisplayColliders")) - { - Selected = displayColliders, - OnSelected = box => - { - displayColliders = box.Selected; - return true; - } - }; - new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("EditBackgroundColor")) - { - Selected = displayBackgroundColor, - OnSelected = box => - { - displayBackgroundColor = box.Selected; - return true; - } + AbsoluteSpacing = 1, + Stretch = true }; + new GUITextBlock(new RectTransform(new Vector2(0.03f, 0.06f), layoutGroup.RectTransform), GetCharacterEditorTranslation("FileEditPanel"), font: GUI.LargeFont); + + // Spacing + new GUIFrame(new RectTransform(buttonSize / 2, layoutGroup.RectTransform), style: null) { CanBeFocused = false }; var quickSaveAnimButton = new GUIButton(new RectTransform(buttonSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("QuickSaveAnimations")); + quickSaveAnimButton.Color = Color.LightGreen; quickSaveAnimButton.OnClicked += (button, userData) => { #if !DEBUG @@ -1980,6 +2305,7 @@ namespace Barotrauma return true; }; var quickSaveRagdollButton = new GUIButton(new RectTransform(buttonSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("QuickSaveRagdoll")); + quickSaveRagdollButton.Color = Color.LightGreen; quickSaveRagdollButton.OnClicked += (button, userData) => { #if !DEBUG @@ -1994,52 +2320,8 @@ namespace Barotrauma GUI.AddMessage(GetCharacterEditorTranslation("RagdollSavedTo").Replace("[path]", RagdollParams.FullPath), Color.Green, font: GUI.Font); return true; }; - var resetAnimButton = new GUIButton(new RectTransform(buttonSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("ResetAnimations")); - resetAnimButton.OnClicked += (button, userData) => - { - AnimParams.ForEach(p => p.Reset(true)); - ResetParamsEditor(); - GUI.AddMessage(GetCharacterEditorTranslation("AllAnimationsReset"), Color.WhiteSmoke, font: GUI.Font); - animationResetRequiresForceLoading = false; - return true; - }; - var resetRagdollButton = new GUIButton(new RectTransform(buttonSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("ResetRagdoll")); - resetRagdollButton.OnClicked += (button, userData) => - { - if (ragdollResetRequiresForceLoading) - { - character.AnimController.ResetRagdoll(forceReload: true); - RecreateRagdoll(); - ragdollResetRequiresForceLoading = false; - } - else - { - character.AnimController.ResetRagdoll(forceReload: false); - // For some reason Enumerable.Contains() method does not find the match, threfore the conversion to a list. - var selectedJointParams = selectedJoints.Select(j => j.jointParams).ToList(); - var selectedLimbParams = selectedLimbs.Select(l => l.limbParams).ToList(); - ClearWidgets(); - ClearSelection(); - foreach (var joint in character.AnimController.LimbJoints) - { - if (selectedJointParams.Contains(joint.jointParams)) - { - selectedJoints.Add(joint); - } - } - foreach (var limb in character.AnimController.Limbs) - { - if (selectedLimbParams.Contains(limb.limbParams)) - { - selectedLimbs.Add(limb); - } - } - ResetParamsEditor(); - } - CreateGUI(); - GUI.AddMessage(GetCharacterEditorTranslation("RagdollReset"), Color.WhiteSmoke, font: GUI.Font); - return true; - }; + // Spacing + new GUIFrame(new RectTransform(buttonSize / 2, layoutGroup.RectTransform), style: null) { CanBeFocused = false }; Vector2 messageBoxRelSize = new Vector2(0.5f, 0.5f); int messageBoxWidth = GameMain.GraphicsWidth / 2; int messageBoxHeight = GameMain.GraphicsHeight / 2; @@ -2152,7 +2434,7 @@ namespace Barotrauma ragdoll.Reset(true); GUI.AddMessage(GetCharacterEditorTranslation("RagdollLoadedFrom").Replace("[file]", selectedFile), Color.WhiteSmoke, font: GUI.Font); RecreateRagdoll(ragdoll); - CreateCenterPanel(); + CreateContextualControls(); loadBox.Close(); return true; }; @@ -2348,28 +2630,62 @@ namespace Barotrauma }; return true; }; - var reloadTexturesButton = new GUIButton(new RectTransform(buttonSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("ReloadTextures")); - reloadTexturesButton.OnClicked += (button, userData) => + + // Spacing + new GUIFrame(new RectTransform(buttonSize / 2, layoutGroup.RectTransform), style: null) { CanBeFocused = false }; + var resetAnimButton = new GUIButton(new RectTransform(buttonSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("ResetAnimations")); + resetAnimButton.Color = Color.Red; + resetAnimButton.OnClicked += (button, userData) => { - foreach (var limb in character.AnimController.Limbs) - { - limb.ActiveSprite.ReloadTexture(); - limb.WearingItems.ForEach(i => i.Sprite.ReloadTexture()); - limb.OtherWearables.ForEach(w => w.Sprite.ReloadTexture()); - } - CreateTextures(); + AnimParams.ForEach(p => p.Reset(true)); + ResetParamsEditor(); + GUI.AddMessage(GetCharacterEditorTranslation("AllAnimationsReset"), Color.WhiteSmoke, font: GUI.Font); + animationResetRequiresForceLoading = false; return true; }; - new GUIButton(new RectTransform(buttonSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("RecreateRagdoll")) + var resetRagdollButton = new GUIButton(new RectTransform(buttonSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("ResetRagdoll")); + resetRagdollButton.Color = Color.Red; + resetRagdollButton.OnClicked += (button, userData) => { - ToolTip = GetCharacterEditorTranslation("RecreateRagdollTooltip"), - OnClicked = (button, data) => + if (ragdollResetRequiresForceLoading) { + character.AnimController.ResetRagdoll(forceReload: true); RecreateRagdoll(); - character.AnimController.ResetLimbs(); - return true; + ragdollResetRequiresForceLoading = false; } + else + { + character.AnimController.ResetRagdoll(forceReload: false); + // For some reason Enumerable.Contains() method does not find the match, threfore the conversion to a list. + var selectedJointParams = selectedJoints.Select(j => j.jointParams).ToList(); + var selectedLimbParams = selectedLimbs.Select(l => l.limbParams).ToList(); + ClearWidgets(); + ClearSelection(); + foreach (var joint in character.AnimController.LimbJoints) + { + if (selectedJointParams.Contains(joint.jointParams)) + { + selectedJoints.Add(joint); + } + } + foreach (var limb in character.AnimController.Limbs) + { + if (selectedLimbParams.Contains(limb.limbParams)) + { + selectedLimbs.Add(limb); + } + } + ResetParamsEditor(); + } + jointCreationMode = false; + closestSelectedLimb = null; + CreateGUI(); + GUI.AddMessage(GetCharacterEditorTranslation("RagdollReset"), Color.WhiteSmoke, font: GUI.Font); + return true; }; + + // Spacing + new GUIFrame(new RectTransform(buttonSize / 2, layoutGroup.RectTransform), style: null) { CanBeFocused = false }; new GUIButton(new RectTransform(buttonSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("CreateNewCharacter")) { OnClicked = (button, data) => @@ -2379,13 +2695,82 @@ namespace Barotrauma spritesheetToggle.Selected = false; jointsToggle.Selected = false; paramsToggle.Selected = false; + ragdollToggle.Selected = false; Wizard.Instance.SelectTab(Wizard.Tab.Character); return true; } }; + + fileEditToggle = new ToggleButton(new RectTransform(new Vector2(0.1f, 1), fileEditPanel.RectTransform, Anchor.CenterLeft, Pivot.CenterRight), Direction.Right); } #endregion + #region ToggleButtons + + private enum Direction + { + Left, + Right + } + + private class ToggleButton + { + public readonly Direction dir; + public readonly GUIButton toggleButton; + + public float OpenState { get; private set; } = 1; + + private bool isHidden; + public bool IsHidden + { + get { return isHidden; } + set + { + isHidden = value; + RefreshToggleButtonState(); + } + } + + public ToggleButton(RectTransform rectT, Direction dir) + { + toggleButton = new GUIButton(rectT, style: "UIToggleButton") + { + Color = toggleButtonColor, + OnClicked = (button, data) => + { + IsHidden = !IsHidden; + return true; + } + }; + this.dir = dir; + RefreshToggleButtonState(); + } + + public void RefreshToggleButtonState() + { + foreach (GUIComponent child in toggleButton.Children) + { + switch (dir) + { + case Direction.Right: + child.SpriteEffects = isHidden ? SpriteEffects.None : SpriteEffects.FlipHorizontally; + break; + case Direction.Left: + child.SpriteEffects = isHidden ? SpriteEffects.FlipHorizontally : SpriteEffects.None; + break; + } + } + } + + public void UpdateOpenState(float deltaTime, Vector2 hiddenPos, RectTransform panel) + { + panel.AbsoluteOffset = Vector2.SmoothStep(hiddenPos, Vector2.Zero, OpenState).ToPoint(); + OpenState = isHidden ? Math.Max(OpenState - deltaTime * 2, 0) : Math.Min(OpenState + deltaTime * 2, 1); + } + } + + #endregion + #region Params private List AnimParams => character.AnimController.AllAnimParams; private AnimationParams CurrentAnimation => character.AnimController.CurrentAnimationParams; @@ -2400,47 +2785,48 @@ namespace Barotrauma } else { - //if (selectedJoints.Any()) - //{ - // foreach (var jointParams in RagdollParams.Joints) - // { - // if (selectedJoints.Any(j => j.jointParams == jointParams)) - // { - // jointParams?.AddToEditor(ParamsEditor.Instance); - // } - // } - //} - //if (selectedLimbs.Any()) - //{ - // foreach (var limbParams in RagdollParams.Limbs) - // { - // if (limbParams == null) { continue; } - // var selectedLimb = selectedLimbs.Find(l => l.limbParams == limbParams); - // if (selectedLimb != null) - // { - // limbParams.AddToEditor(ParamsEditor.Instance); - // if (selectedLimb.attack != null) - // { - // var attackEditor = new SerializableEntityEditor(ParamsEditor.Instance.EditorBox.Content.RectTransform, selectedLimb.attack, false, true); - // } - // } - // } - //} - foreach (var joint in selectedJoints) + if (editRagdoll || !editLimbs && !editJoints) { - joint.jointParams.AddToEditor(ParamsEditor.Instance); + RagdollParams.AddToEditor(ParamsEditor.Instance, alsoChildren: false); } - foreach (var limb in selectedLimbs) + if (editJoints) { - limb.limbParams.AddToEditor(ParamsEditor.Instance); - if (limb.attack != null) + if (selectedJoints.None()) { - new SerializableEntityEditor(ParamsEditor.Instance.EditorBox.Content.RectTransform, limb.attack, inGame: false, showName: true); + RagdollParams.Joints.ForEach(jp => jp.AddToEditor(ParamsEditor.Instance)); + } + else + { + foreach (var joint in selectedJoints) + { + joint.jointParams.AddToEditor(ParamsEditor.Instance); + } } } - if (selectedJoints.None() && selectedLimbs.None()) + if (editLimbs) { - RagdollParams.AddToEditor(ParamsEditor.Instance); + if (selectedLimbs.None()) + { + foreach (var limb in character.AnimController.Limbs) + { + limb.limbParams.AddToEditor(ParamsEditor.Instance); + if (limb.attack != null) + { + new SerializableEntityEditor(ParamsEditor.Instance.EditorBox.Content.RectTransform, limb.attack, inGame: false, showName: true); + } + } + } + else + { + foreach (var limb in selectedLimbs) + { + limb.limbParams.AddToEditor(ParamsEditor.Instance); + if (limb.attack != null) + { + new SerializableEntityEditor(ParamsEditor.Instance.EditorBox.Content.RectTransform, limb.attack, inGame: false, showName: true); + } + } + } } } } @@ -2540,6 +2926,7 @@ namespace Barotrauma float closestDistance = float.MaxValue; foreach (Limb l in character.AnimController.Limbs) { + if (l == null) { continue; } if (filter == null ? true : filter(l)) { float distance = Vector2.DistanceSquared(GetLimbSpritesheetRect(l).Center.ToVector2(), targetPos); @@ -2625,7 +3012,7 @@ namespace Barotrauma private void DrawJointCreationOnSpritesheet(SpriteBatch spriteBatch, Vector2 startPos) { // Spritesheet - GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2 - 200, GameMain.GraphicsHeight - 150), GetCharacterEditorTranslation("SelectTargetLimbForJointEnd"), Color.White, Color.Black * 0.5f, 10, GUI.Font); + GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2 - 200, GameMain.GraphicsHeight - 200), GetCharacterEditorTranslation("SelectTargetLimbForJointEnd"), Color.White, Color.Black * 0.5f, 10, GUI.Font); GUI.DrawLine(spriteBatch, startPos, PlayerInput.MousePosition, Color.LightGreen, width: 3); if (targetLimb != null && targetLimb.ActiveSprite != null) { @@ -2636,7 +3023,7 @@ namespace Barotrauma private void DrawJointCreationOnRagdoll(SpriteBatch spriteBatch, Vector2 startPos) { // Ragdoll - GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2 - 200, GameMain.GraphicsHeight - 150), GetCharacterEditorTranslation("SelectTargetLimbForJointEnd"), Color.White, Color.Black * 0.5f, 10, GUI.Font); + GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2 - 200, GameMain.GraphicsHeight - 200), GetCharacterEditorTranslation("SelectTargetLimbForJointEnd"), Color.White, Color.Black * 0.5f, 10, GUI.Font); GUI.DrawLine(spriteBatch, startPos, PlayerInput.MousePosition, Color.LightGreen, width: 3); if (targetLimb != null && targetLimb.ActiveSprite != null) { @@ -2663,11 +3050,11 @@ namespace Barotrauma } else if (height > width) { - spriteSheetMaxZoom = (centerPanel.Rect.Bottom - spriteSheetOffsetY - margin) / height; + spriteSheetMaxZoom = (centerArea.Rect.Bottom - spriteSheetOffsetY - margin) / height; } else { - spriteSheetMaxZoom = (centerPanel.Rect.Left - spriteSheetOffsetX - margin) / width; + spriteSheetMaxZoom = (centerArea.Rect.Left - spriteSheetOffsetX - margin) / width; } spriteSheetMinZoom = spriteSheetMinZoom > spriteSheetMaxZoom ? spriteSheetMaxZoom : 0.25f; spriteSheetZoom = MathHelper.Clamp(1, spriteSheetMinZoom, spriteSheetMaxZoom); @@ -2740,23 +3127,12 @@ namespace Barotrauma var foot = character.AnimController.GetLimb(LimbType.RightFoot) ?? character.AnimController.GetLimb(LimbType.LeftFoot); var hand = character.AnimController.GetLimb(LimbType.RightHand) ?? character.AnimController.GetLimb(LimbType.LeftHand); var arm = character.AnimController.GetLimb(LimbType.RightArm) ?? character.AnimController.GetLimb(LimbType.LeftArm); - int widgetDefaultSize = 10; // Note: the main collider rotates only when swimming float dir = character.AnimController.Dir; - Vector2 colliderBottom = character.AnimController.GetColliderBottom(); - //Vector2 centerOfMass = character.AnimController.GetCenterOfMass(); - Vector2 simSpaceForward = Vector2.Transform(Vector2.UnitY, Matrix.CreateRotationZ(collider.Rotation)); - //Vector2 simSpaceLeft = Vector2.Transform(-Vector2.UnitX, Matrix.CreateRotationZ(collider.Rotation)); - Vector2 screenSpaceForward = VectorExtensions.BackwardFlipped(collider.Rotation, 1); - Vector2 screenSpaceLeft = screenSpaceForward.Right(); - // The forward vector is left or right in screen space when the unit is not swimming. Cannot rely on the collider here, because the rotation may vary on ground. - Vector2 forward = animParams.IsSwimAnimation ? screenSpaceForward : Vector2.UnitX * dir; Vector2 GetSimSpaceForward() => animParams.IsSwimAnimation ? Vector2.Transform(Vector2.UnitY, Matrix.CreateRotationZ(collider.Rotation)) : Vector2.UnitX * character.AnimController.Dir; Vector2 GetScreenSpaceForward() => animParams.IsSwimAnimation ? VectorExtensions.BackwardFlipped(collider.Rotation, 1) : Vector2.UnitX * character.AnimController.Dir; bool ShowCycleWidget() => PlayerInput.KeyDown(Keys.LeftAlt) && (CurrentAnimation is IHumanAnimation || CurrentAnimation is GroundedMovementParams); - - bool altDown = PlayerInput.KeyDown(Keys.LeftAlt); - if (!altDown && (animParams is IHumanAnimation || animParams is GroundedMovementParams)) + if (!PlayerInput.KeyDown(Keys.LeftAlt) && (animParams is IHumanAnimation || animParams is GroundedMovementParams)) { GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2 - 120, 100), GetCharacterEditorTranslation("HoldLeftAltToAdjustCycleSpeed"), Color.White, Color.Black * 0.5f, 10, GUI.Font); } @@ -2887,14 +3263,26 @@ namespace Barotrauma isDirectionSet = true; } var scaledInput = ConvertUnits.ToSimUnits(PlayerInput.MouseSpeed) / Cam.Zoom; - if (isHorizontal) + if (PlayerInput.KeyDown(Keys.LeftAlt)) + { + if (isHorizontal) + { + TryUpdateAnimParam("headleanamount", humanGroundedParams.HeadLeanAmount + scaledInput.X * character.AnimController.Dir); + w.refresh(); + w.DrawPos = new Vector2(PlayerInput.MousePosition.X, w.DrawPos.Y); + } + else + { + TryUpdateAnimParam("headposition", humanGroundedParams.HeadPosition - scaledInput.Y / RagdollParams.JointScale); + w.refresh(); + w.DrawPos = new Vector2(w.DrawPos.X, PlayerInput.MousePosition.Y); + } + } + else { TryUpdateAnimParam("headleanamount", humanGroundedParams.HeadLeanAmount + scaledInput.X * character.AnimController.Dir); w.refresh(); w.DrawPos = new Vector2(PlayerInput.MousePosition.X, w.DrawPos.Y); - } - else - { TryUpdateAnimParam("headposition", humanGroundedParams.HeadPosition - scaledInput.Y / RagdollParams.JointScale); w.refresh(); w.DrawPos = new Vector2(w.DrawPos.X, PlayerInput.MousePosition.Y); @@ -2904,12 +3292,20 @@ namespace Barotrauma { if (w.IsControlled && isDirectionSet) { - if (isHorizontal) + if (PlayerInput.KeyDown(Keys.LeftAlt)) { - GUI.DrawLine(spriteBatch, new Vector2(0, w.DrawPos.Y), new Vector2(GameMain.GraphicsWidth, w.DrawPos.Y), Color.Red); + if (isHorizontal) + { + GUI.DrawLine(spriteBatch, new Vector2(0, w.DrawPos.Y), new Vector2(GameMain.GraphicsWidth, w.DrawPos.Y), Color.Red); + } + else + { + GUI.DrawLine(spriteBatch, new Vector2(w.DrawPos.X, 0), new Vector2(w.DrawPos.X, GameMain.GraphicsHeight), Color.Red); + } } else { + GUI.DrawLine(spriteBatch, new Vector2(0, w.DrawPos.Y), new Vector2(GameMain.GraphicsWidth, w.DrawPos.Y), Color.Red); GUI.DrawLine(spriteBatch, new Vector2(w.DrawPos.X, 0), new Vector2(w.DrawPos.X, GameMain.GraphicsHeight), Color.Red); } } @@ -2948,7 +3344,8 @@ namespace Barotrauma referencePoint = torso.SimPosition; if (animParams is HumanGroundedParams || animParams is HumanSwimParams) { - referencePoint -= simSpaceForward * 0.25f; + var f = Vector2.Transform(Vector2.UnitY, Matrix.CreateRotationZ(collider.Rotation)); + referencePoint -= f * 0.25f; } // Torso angle DrawRadialWidget(spriteBatch, SimToScreen(referencePoint), animParams.TorsoAngle, GetCharacterEditorTranslation("TorsoAngle"), Color.White, @@ -2975,14 +3372,26 @@ namespace Barotrauma isDirectionSet = true; } var scaledInput = ConvertUnits.ToSimUnits(PlayerInput.MouseSpeed) / Cam.Zoom; - if (isHorizontal) + if (PlayerInput.KeyDown(Keys.LeftAlt)) + { + if (isHorizontal) + { + TryUpdateAnimParam("torsoleanamount", humanGroundedParams.TorsoLeanAmount + scaledInput.X * character.AnimController.Dir); + w.refresh(); + w.DrawPos = new Vector2(PlayerInput.MousePosition.X, w.DrawPos.Y); + } + else + { + TryUpdateAnimParam("torsoposition", humanGroundedParams.TorsoPosition - scaledInput.Y / RagdollParams.JointScale); + w.refresh(); + w.DrawPos = new Vector2(w.DrawPos.X, PlayerInput.MousePosition.Y); + } + } + else { TryUpdateAnimParam("torsoleanamount", humanGroundedParams.TorsoLeanAmount + scaledInput.X * character.AnimController.Dir); w.refresh(); w.DrawPos = new Vector2(PlayerInput.MousePosition.X, w.DrawPos.Y); - } - else - { TryUpdateAnimParam("torsoposition", humanGroundedParams.TorsoPosition - scaledInput.Y / RagdollParams.JointScale); w.refresh(); w.DrawPos = new Vector2(w.DrawPos.X, PlayerInput.MousePosition.Y); @@ -2992,12 +3401,20 @@ namespace Barotrauma { if (w.IsControlled && isDirectionSet) { - if (isHorizontal) + if (PlayerInput.KeyDown(Keys.LeftAlt)) { - GUI.DrawLine(spriteBatch, new Vector2(0, w.DrawPos.Y), new Vector2(GameMain.GraphicsWidth, w.DrawPos.Y), Color.DarkRed); + if (isHorizontal) + { + GUI.DrawLine(spriteBatch, new Vector2(0, w.DrawPos.Y), new Vector2(GameMain.GraphicsWidth, w.DrawPos.Y), Color.DarkRed); + } + else + { + GUI.DrawLine(spriteBatch, new Vector2(w.DrawPos.X, 0), new Vector2(w.DrawPos.X, GameMain.GraphicsHeight), Color.DarkRed); + } } else { + GUI.DrawLine(spriteBatch, new Vector2(0, w.DrawPos.Y), new Vector2(GameMain.GraphicsWidth, w.DrawPos.Y), Color.DarkRed); GUI.DrawLine(spriteBatch, new Vector2(w.DrawPos.X, 0), new Vector2(w.DrawPos.X, GameMain.GraphicsHeight), Color.DarkRed); } } @@ -3042,6 +3459,7 @@ namespace Barotrauma { if (fishParams != null) { + Vector2 colliderBottom = character.AnimController.GetColliderBottom(); foreach (Limb limb in character.AnimController.Limbs) { if (limb.type != LimbType.LeftFoot && limb.type != LimbType.RightFoot) continue; @@ -3359,7 +3777,7 @@ namespace Barotrauma bool altDown = PlayerInput.KeyDown(Keys.LeftAlt); if (!altDown && editJoints && selectedJoints.Any()) { - GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2 - 200, 100), GetCharacterEditorTranslation("HoldLeftAltToManipulateJoint"), Color.White, Color.Black * 0.5f, 10, GUI.Font); + GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2 - 200, 250), GetCharacterEditorTranslation("HoldLeftAltToManipulateJoint"), Color.White, Color.Black * 0.5f, 10, GUI.Font); } foreach (Limb limb in character.AnimController.Limbs) { @@ -3407,7 +3825,7 @@ namespace Barotrauma var f = Vector2.Transform(jointPos, Matrix.CreateRotationZ(limb.Rotation)); f.Y = -f.Y; Vector2 tformedJointPos = limbScreenPos + f * Cam.Zoom; - if (showRagdoll) + if (editRagdoll) { ShapeExtensions.DrawPoint(spriteBatch, limbScreenPos, Color.Black, size: 5); ShapeExtensions.DrawPoint(spriteBatch, limbScreenPos, Color.White, size: 1); @@ -3684,7 +4102,6 @@ namespace Barotrauma color: Color.White, layerDepth: 0); } - GUI.DrawRectangle(spriteBatch, rect, selectedLimbs.Contains(limb) ? Color.Yellow : Color.Red); // The origin is manipulated when the character is flipped. We have to undo it here. if (character.AnimController.Dir < 0) { @@ -3696,6 +4113,7 @@ namespace Barotrauma } if (editLimbs) { + GUI.DrawRectangle(spriteBatch, rect, selectedLimbs.Contains(limb) ? Color.Yellow : Color.Red); int widgetSize = 8; int halfSize = widgetSize / 2; Vector2 stringOffset = new Vector2(5, 14); @@ -4370,6 +4788,7 @@ namespace Barotrauma private bool canEnterSubmarine = true; private string texturePath; private string xmlPath; + private ContentPackage contentPackage; private Dictionary limbXElements = new Dictionary(); private List limbGUIElements = new List(); private List jointXElements = new List(); @@ -4388,6 +4807,13 @@ namespace Barotrauma } } + public void Reset() + { + CharacterView.Get().Release(); + RagdollView.Get().Release(); + instance = null; + } + public enum Tab { None, Character, Ragdoll } private View activeView; private Tab currentTab; @@ -4406,7 +4832,7 @@ namespace Barotrauma break; case Tab.None: default: - instance = null; + Reset(); break; } } @@ -4419,28 +4845,37 @@ namespace Barotrauma private class CharacterView : View { private static CharacterView instance; - public static CharacterView Get() => Get(instance); + public static CharacterView Get() => Get(ref instance); + + public override void Release() => instance = null; protected override GUIMessageBox Create() { var box = new GUIMessageBox(GetCharacterEditorTranslation("CreateNewCharacter"), string.Empty, new string[] { TextManager.Get("Cancel"), TextManager.Get("Next") }, new Vector2(0.5f, 1.0f)); + box.Header.Font = GUI.LargeFont; box.Content.ChildAnchor = Anchor.TopCenter; box.Content.AbsoluteSpacing = 20; int elementSize = 30; var listBox = new GUIListBox(new RectTransform(new Vector2(1, 0.9f), box.Content.RectTransform)); - var topGroup = new GUILayoutGroup(new RectTransform(new Point(listBox.Content.Rect.Width, elementSize * 6 + 20), listBox.Content.RectTransform)) { AbsoluteSpacing = 2 }; + var topGroup = new GUILayoutGroup(new RectTransform(Vector2.One, listBox.Content.RectTransform)) { AbsoluteSpacing = 2 }; var fields = new List(); GUITextBox texturePathElement = null; GUITextBox xmlPathElement = null; + GUIDropDown contentPackageDropDown = null; + bool updateTexturePath = true; void UpdatePaths() { - string pathBase = $"Mods/Characters/{Name}/{Name}"; + string pathBase = ContentPackage == GameMain.VanillaContent ? $"Content/Characters/{Name}/{Name}" + : $"Mods/{(ContentPackage != null ? ContentPackage.Name + "/" : string.Empty)}Characters/{Name}/{Name}"; XMLPath = $"{pathBase}.xml"; - TexturePath = $"{pathBase}.png"; - texturePathElement.Text = TexturePath; xmlPathElement.Text = XMLPath; + if (updateTexturePath) + { + TexturePath = $"{pathBase}.png"; + texturePathElement.Text = TexturePath; + } } - for (int i = 0; i < 5; i++) + for (int i = 0; i < 6; i++) { var mainElement = new GUIFrame(new RectTransform(new Point(topGroup.RectTransform.Rect.Width, elementSize), topGroup.RectTransform), style: null, color: Color.Gray * 0.25f); fields.Add(mainElement); @@ -4500,10 +4935,77 @@ namespace Barotrauma }; texturePathElement.OnTextChanged += (tb, text) => { + updateTexturePath = false; TexturePath = text; return true; }; break; + case 5: + mainElement.RectTransform.NonScaledSize = new Point( + mainElement.RectTransform.NonScaledSize.X, + mainElement.RectTransform.NonScaledSize.Y * 2); + new GUITextBlock(leftElement, TextManager.Get("ContentPackage")); + var rightContainer = new GUIFrame(rightElement, style: null); + contentPackageDropDown = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.5f), rightContainer.RectTransform, Anchor.TopRight)); + foreach (ContentPackage cp in ContentPackage.List) + { +#if !DEBUG + if (cp == GameMain.VanillaContent) { continue; } +#endif + contentPackageDropDown.AddItem(cp.Name, userData: cp, toolTip: cp.Path); + } + contentPackageDropDown.OnSelected = (obj, userdata) => + { + ContentPackage = userdata as ContentPackage; + updateTexturePath = true; + UpdatePaths(); + return true; + }; + var contentPackageNameElement = new GUITextBox(new RectTransform(new Vector2(0.7f, 0.5f), rightContainer.RectTransform, Anchor.BottomLeft), + TextManager.Get("name")) + { + CaretColor = Color.White, + }; + var createNewPackageButton = new GUIButton(new RectTransform(new Vector2(0.3f, 0.5f), rightContainer.RectTransform, Anchor.BottomRight), TextManager.Get("CreateNew")) + { + OnClicked = (btn, userdata) => + { + if (string.IsNullOrEmpty(contentPackageNameElement.Text)) + { + contentPackageNameElement.Flash(); + return false; + } + if (ContentPackage.List.Any(cp => cp.Name.ToLower() == contentPackageNameElement.Text.ToLower())) + { + new GUIMessageBox("", TextManager.Get("charactereditor.contentpackagenameinuse", fallBackTag: "leveleditorlevelobjnametaken")); + return false; + } + string fileName = ToolBox.RemoveInvalidFileNameChars(contentPackageNameElement.Text); + ContentPackage = ContentPackage.CreatePackage( + contentPackageNameElement.Text, + Path.Combine(ContentPackage.Folder, $"{fileName}.xml"), false); + ContentPackage.List.Add(ContentPackage); + GameMain.Config.SelectedContentPackages.Add(ContentPackage); + contentPackageDropDown.AddItem(ContentPackage.Name, ContentPackage, ContentPackage.Path); + contentPackageDropDown.SelectItem(ContentPackage); + contentPackageNameElement.Text = ""; + return true; + }, + Enabled = false + }; + Color textColor = contentPackageNameElement.TextColor; + contentPackageNameElement.TextColor *= 0.6f; + contentPackageNameElement.OnSelected += (sender, key) => + { + contentPackageNameElement.Text = ""; + }; + contentPackageNameElement.OnTextChanged += (textBox, text) => + { + textBox.TextColor = textColor; + createNewPackageButton.Enabled = !string.IsNullOrWhiteSpace(text); + return true; + }; + break; } } UpdatePaths(); @@ -4519,12 +5021,24 @@ namespace Barotrauma // Next box.Buttons[1].OnClicked += (b, d) => { + if (ContentPackage == null) + { + contentPackageDropDown.Flash(); + return false; + } if (!File.Exists(TexturePath)) { GUI.AddMessage(GetCharacterEditorTranslation("TextureDoesNotExist"), Color.Red); texturePathElement.Flash(Color.Red); return false; } + var path = Path.GetFileName(TexturePath); + if (!path.EndsWith(".png", StringComparison.InvariantCultureIgnoreCase)) + { + GUI.AddMessage(TextManager.Get("WrongFileType"), Color.Red); + texturePathElement.Flash(Color.Red); + return false; + } Wizard.Instance.SelectTab(Tab.Ragdoll); return true; }; @@ -4535,11 +5049,14 @@ namespace Barotrauma private class RagdollView : View { private static RagdollView instance; - public static RagdollView Get() => Get(instance); + public static RagdollView Get() => Get(ref instance); + + public override void Release() => instance = null; protected override GUIMessageBox Create() { var box = new GUIMessageBox(GetCharacterEditorTranslation("DefineRagdoll"), string.Empty, new string[] { TextManager.Get("Previous"), TextManager.Get("Create") }, new Vector2(0.5f, 1.0f)); + box.Header.Font = GUI.LargeFont; box.Content.ChildAnchor = Anchor.TopCenter; box.Content.AbsoluteSpacing = 20; int elementSize = 30; @@ -4551,10 +5068,11 @@ namespace Barotrauma // Limbs var limbsElement = new GUIFrame(new RectTransform(new Vector2(1, 0.05f), bottomGroup.RectTransform), style: null) { CanBeFocused = false }; new GUITextBlock(new RectTransform(new Vector2(0.2f, 1f), limbsElement.RectTransform), $"{GetCharacterEditorTranslation("Limbs")}: "); - var limbButtonElement = new GUIFrame(new RectTransform(new Vector2(0.5f, 1f), limbsElement.RectTransform) - { RelativeOffset = new Vector2(0.25f, 0) }, style: null) { CanBeFocused = false }; + var limbButtonElement = new GUIFrame(new RectTransform(new Vector2(0.8f, 1f), limbsElement.RectTransform) + { RelativeOffset = new Vector2(0.1f, 0) }, style: null) { CanBeFocused = false }; + var limbEditLayout = new GUILayoutGroup(new RectTransform(Vector2.One, limbButtonElement.RectTransform), isHorizontal: true) { AbsoluteSpacing = 10 }; var limbsList = new GUIListBox(new RectTransform(new Vector2(1, 0.45f), bottomGroup.RectTransform)); - var removeLimbButton = new GUIButton(new RectTransform(new Point(limbButtonElement.Rect.Height, limbButtonElement.Rect.Height), limbButtonElement.RectTransform), "-") + var removeLimbButton = new GUIButton(new RectTransform(new Point(limbButtonElement.Rect.Height, limbButtonElement.Rect.Height), limbEditLayout.RectTransform), "-") { OnClicked = (b, d) => { @@ -4565,10 +5083,7 @@ namespace Barotrauma return true; } }; - var addLimbButton = new GUIButton(new RectTransform(new Point(limbButtonElement.Rect.Height, limbButtonElement.Rect.Height), limbButtonElement.RectTransform) - { - AbsoluteOffset = new Point(removeLimbButton.Rect.Width + 10, 0) - }, "+") + var addLimbButton = new GUIButton(new RectTransform(new Point(limbButtonElement.Rect.Height, limbButtonElement.Rect.Height), limbEditLayout.RectTransform), "+") { OnClicked = (b, d) => { @@ -4576,22 +5091,101 @@ namespace Barotrauma switch (LimbGUIElements.Count) { case 0: - limbType = LimbType.Head; + limbType = LimbType.Torso; break; case 1: - limbType = LimbType.Torso; + limbType = LimbType.Head; break; } CreateLimbGUIElement(limbsList.Content.RectTransform, elementSize, id: LimbGUIElements.Count, limbType: limbType); return true; } }; + + int x = 1, y = 1, w = 100, h = 100; + int otherElements = limbButtonElement.Rect.Width / 4 + 10 + limbButtonElement.Rect.Height * 2 + 10 + limbButtonElement.RectTransform.AbsoluteOffset.X; + var frame = new GUIFrame(new RectTransform(new Point(limbEditLayout.Rect.Width - otherElements, limbButtonElement.Rect.Height), limbEditLayout.RectTransform), color: Color.Transparent); + var inputArea = new GUILayoutGroup(new RectTransform(Vector2.One, frame.RectTransform, Anchor.TopRight), isHorizontal: true, childAnchor: Anchor.CenterRight) + { + Stretch = true, + RelativeSpacing = 0.01f + }; + for (int i = 3; i >= 0; i--) + { + var element = new GUIFrame(new RectTransform(new Vector2(0.22f, 1), inputArea.RectTransform) { MinSize = new Point(50, 0), MaxSize = new Point(150, 50) }, style: null); + new GUITextBlock(new RectTransform(new Vector2(0.3f, 1), element.RectTransform, Anchor.CenterLeft), GUI.rectComponentLabels[i], font: GUI.SmallFont, textAlignment: Alignment.CenterLeft); + GUINumberInput numberInput = new GUINumberInput(new RectTransform(new Vector2(0.7f, 1), element.RectTransform, Anchor.CenterRight), GUINumberInput.NumberType.Int) + { + Font = GUI.SmallFont + }; + switch (i) + { + case 0: + case 1: + numberInput.IntValue = 1; + numberInput.MinValueInt = 1; + numberInput.MaxValueInt = 100; + break; + case 2: + case 3: + numberInput.IntValue = 100; + numberInput.MinValueInt = 0; + numberInput.MaxValueInt = 999; + break; + + } + int comp = i; + numberInput.OnValueChanged += (numInput) => + { + switch (comp) + { + case 0: + x = numInput.IntValue; + break; + case 1: + y = numInput.IntValue; + break; + case 2: + w = numInput.IntValue; + break; + case 3: + h = numInput.IntValue; + break; + } + }; + } + + new GUIButton(new RectTransform(new Point(limbButtonElement.Rect.Width / 4, limbButtonElement.Rect.Height), limbEditLayout.RectTransform) + , GetCharacterEditorTranslation("AddMultipleLimbsButton")) + { + OnClicked = (b, d) => + { + for (int i = 0; i < x; i++) + { + for (int j = 0; j < y; j++) + { + LimbType limbType = LimbType.None; + switch (LimbGUIElements.Count) + { + case 0: + limbType = LimbType.Torso; + break; + case 1: + limbType = LimbType.Head; + break; + } + CreateLimbGUIElement(limbsList.Content.RectTransform, elementSize, id: LimbGUIElements.Count, limbType: limbType, sourceRect: new Rectangle(i * w, j * h, w, h)); + } + } + return true; + } + }; // Joints new GUIFrame(new RectTransform(new Vector2(1, 0.05f), bottomGroup.RectTransform), style: null) { CanBeFocused = false }; var jointsElement = new GUIFrame(new RectTransform(new Vector2(1, 0.05f), bottomGroup.RectTransform), style: null) { CanBeFocused = false }; new GUITextBlock(new RectTransform(new Vector2(0.2f, 1f), jointsElement.RectTransform), $"{GetCharacterEditorTranslation("Joints")}: "); var jointButtonElement = new GUIFrame(new RectTransform(new Vector2(0.5f, 1f), jointsElement.RectTransform) - { RelativeOffset = new Vector2(0.25f, 0) }, style: null) { CanBeFocused = false }; + { RelativeOffset = new Vector2(0.1f, 0) }, style: null) { CanBeFocused = false }; var jointsList = new GUIListBox(new RectTransform(new Vector2(1, 0.45f), bottomGroup.RectTransform)); var removeJointButton = new GUIButton(new RectTransform(new Point(jointButtonElement.Rect.Height, jointButtonElement.Rect.Height), jointButtonElement.RectTransform), "-") { @@ -4620,6 +5214,7 @@ namespace Barotrauma if (htmlBox == null) { htmlBox = new GUIMessageBox(GetCharacterEditorTranslation("LoadHTML"), string.Empty, new string[] { TextManager.Get("Close"), TextManager.Get("Load") }, new Vector2(0.5f, 1.0f)); + htmlBox.Header.Font = GUI.LargeFont; var element = new GUIFrame(new RectTransform(new Vector2(0.8f, 0.05f), htmlBox.Content.RectTransform), style: null, color: Color.Gray * 0.25f); new GUITextBlock(new RectTransform(new Vector2(0.5f, 1), element.RectTransform), GetCharacterEditorTranslation("HTMLPath")); var htmlPathElement = new GUITextBox(new RectTransform(new Vector2(0.5f, 1), element.RectTransform, Anchor.TopRight), $"Content/Characters/{Name}/{Name}.html"); @@ -4674,7 +5269,7 @@ namespace Barotrauma { ParseLimbsFromGUIElements(); ParseJointsFromGUIElements(); - var torsoAttributes = LimbXElements.Values.Select(x => x.Attribute("type")).Where(a => a.Value.ToLowerInvariant() == "torso"); + var torsoAttributes = LimbXElements.Values.Select(xe => xe.Attribute("type")).Where(a => a.Value.ToLowerInvariant() == "torso"); if (torsoAttributes.Count() != 1) { GUI.AddMessage(GetCharacterEditorTranslation("MultipleTorsosDefined"), Color.Red); @@ -4753,7 +5348,7 @@ namespace Barotrauma LimbXElements.Values, JointXElements }; - if (CharacterEditorScreen.instance.CreateCharacter(Name, Path.GetDirectoryName(XMLPath), IsHumanoid, ragdollParams)) + if (CharacterEditorScreen.instance.CreateCharacter(Name, Path.GetDirectoryName(XMLPath), IsHumanoid, ContentPackage, ragdollParams)) { GUI.AddMessage(GetCharacterEditorTranslation("CharacterCreated").Replace("[name]", Name), Color.Green, font: GUI.Font); } @@ -4774,7 +5369,7 @@ namespace Barotrauma var idField = new GUIFrame(new RectTransform(new Point(group.Rect.Width, elementSize), group.RectTransform), style: null); var nameField = new GUIFrame(new RectTransform(new Point(group.Rect.Width, elementSize), group.RectTransform), style: null); var limbTypeField = GUI.CreateEnumField(limbType, elementSize, GetCharacterEditorTranslation("LimbType"), group.RectTransform, font: GUI.Font); - var sourceRectField = GUI.CreateRectangleField(sourceRect ?? new Rectangle(0, 0, 2, 2), elementSize, GetCharacterEditorTranslation("SourceRectangle"), group.RectTransform, font: GUI.Font); + var sourceRectField = GUI.CreateRectangleField(sourceRect ?? new Rectangle(0, 100 * LimbGUIElements.Count, 100, 100), elementSize, GetCharacterEditorTranslation("SourceRectangle"), group.RectTransform, font: GUI.Font); new GUITextBlock(new RectTransform(new Vector2(0.5f, 1), idField.RectTransform, Anchor.TopLeft), GetCharacterEditorTranslation("ID")); new GUINumberInput(new RectTransform(new Vector2(0.5f, 1), idField.RectTransform, Anchor.TopRight), GUINumberInput.NumberType.Int) { @@ -4867,6 +5462,11 @@ namespace Barotrauma get => Instance.canEnterSubmarine; set => Instance.canEnterSubmarine = value; } + public ContentPackage ContentPackage + { + get => Instance.contentPackage; + set => Instance.contentPackage = value; + } public string TexturePath { get => Instance.texturePath; @@ -4912,7 +5512,7 @@ namespace Barotrauma } protected abstract GUIMessageBox Create(); - protected static T Get(T instance) where T : View, new() + protected static T Get(ref T instance) where T : View, new() { if (instance == null) { @@ -4921,6 +5521,8 @@ namespace Barotrauma return instance; } + public abstract void Release(); + protected void ParseLimbsFromGUIElements() { LimbXElements.Clear(); @@ -5073,10 +5675,8 @@ namespace Barotrauma { if (hierarchy != "0") { - // OLD LOGIC: If the bone is at the root hierarchy, parent the bone to the last sibling (1 is parented to 0, 2 to 1 etc) // NEW LOGIC: if hierarchy length == 1, parent to 0 // Else parent to the last bone in the current hierarchy (11 is parented to 1, 212 is parented to 21 etc) - //string parent = hierarchy.Length > 1 ? hierarchy.Remove(hierarchy.Length - 1, 1) : (int.Parse(hierarchy) - 1).ToString(); string parent = hierarchy.Length > 1 ? hierarchy.Remove(hierarchy.Length - 1, 1) : "0"; if (hierarchyToID.TryGetValue(parent, out int parentID)) { diff --git a/Barotrauma/BarotraumaClient/Source/Screens/MainMenuScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/MainMenuScreen.cs index 15874f04f..62b74fa4b 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/MainMenuScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/MainMenuScreen.cs @@ -9,6 +9,7 @@ using System.Diagnostics; using System.IO; using System.Linq; using System.Threading; +using System.Xml.Linq; namespace Barotrauma { @@ -736,6 +737,7 @@ namespace Barotrauma string arguments = "-name \"" + name.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"" + " -port " + port.ToString() + " -queryport " + queryPort.ToString() + + " -public " + isPublicBox.Selected.ToString() + " -password \"" + passwordBox.Text.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"" + " -upnp " + useUpnpBox.Selected + " -maxplayers " + maxPlayersBox.Text + @@ -940,6 +942,20 @@ namespace Barotrauma #region UI Methods private void CreateHostServerFields() { + int port = NetConfig.DefaultPort; + int queryPort = NetConfig.DefaultQueryPort; + int maxPlayers = 8; + if (File.Exists(ServerSettings.SettingsFile)) + { + XDocument settingsDoc = XMLExtensions.TryLoadXml(ServerSettings.SettingsFile); + if (settingsDoc?.Root != null) + { + port = settingsDoc.Root.GetAttributeInt("port", port); + queryPort = settingsDoc.Root.GetAttributeInt("queryport", queryPort); + maxPlayers = settingsDoc.Root.GetAttributeInt("maxplayers", maxPlayers); + } + } + Vector2 textLabelSize = new Vector2(1.0f, 0.1f); Alignment textAlignment = Alignment.CenterLeft; Vector2 textFieldSize = new Vector2(0.5f, 1.0f); @@ -954,12 +970,16 @@ namespace Barotrauma new GUITextBlock(new RectTransform(textLabelSize, parent.RectTransform), TextManager.Get("HostServerButton"), textAlignment: Alignment.Center, font: GUI.LargeFont) { ForceUpperCase = true }; var label = new GUITextBlock(new RectTransform(textLabelSize, parent.RectTransform), TextManager.Get("ServerName"), textAlignment: textAlignment); - serverNameBox = new GUITextBox(new RectTransform(textFieldSize, label.RectTransform, Anchor.CenterRight), textAlignment: textAlignment); + serverNameBox = new GUITextBox(new RectTransform(textFieldSize, label.RectTransform, Anchor.CenterRight), textAlignment: textAlignment) + { + MaxTextLength = NetConfig.ServerNameMaxLength, + OverflowClip = true + }; label = new GUITextBlock(new RectTransform(textLabelSize, parent.RectTransform), TextManager.Get("ServerPort"), textAlignment: textAlignment); portBox = new GUITextBox(new RectTransform(textFieldSize, label.RectTransform, Anchor.CenterRight), textAlignment: textAlignment) { - Text = NetConfig.DefaultPort.ToString(), + Text = port.ToString(), ToolTip = TextManager.Get("ServerPortToolTip") }; @@ -968,7 +988,7 @@ namespace Barotrauma label = new GUITextBlock(new RectTransform(textLabelSize, parent.RectTransform), TextManager.Get("ServerQueryPort"), textAlignment: textAlignment); queryPortBox = new GUITextBox(new RectTransform(textFieldSize, label.RectTransform, Anchor.CenterRight), textAlignment: textAlignment) { - Text = NetConfig.DefaultQueryPort.ToString(), + Text = queryPort.ToString(), ToolTip = TextManager.Get("ServerQueryPortToolTip") }; } @@ -987,7 +1007,7 @@ namespace Barotrauma maxPlayersBox = new GUITextBox(new RectTransform(new Vector2(0.6f, 1.0f), buttonContainer.RectTransform), textAlignment: Alignment.Center) { - Text = "8", + Text = maxPlayers.ToString(), Enabled = false }; new GUIButton(new RectTransform(new Vector2(0.2f, 1.0f), buttonContainer.RectTransform), "+", textAlignment: Alignment.Center) diff --git a/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs index c989c73cf..038ee390d 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs @@ -335,8 +335,12 @@ namespace Barotrauma new GUIFrame(new RectTransform(new Vector2(1.0f, 0.03f), rightInfoColumn.RectTransform), style: null); //server info ------------------------------------------------------------------ - - ServerName = new GUITextBox(new RectTransform(new Vector2(0.3f, 0.05f), infoFrameContent.RectTransform)); + + ServerName = new GUITextBox(new RectTransform(new Vector2(infoColumnContainer.RectTransform.RelativeSize.X, 0.05f), infoFrameContent.RectTransform)) + { + MaxTextLength = NetConfig.ServerNameMaxLength, + OverflowClip = true + }; ServerName.OnDeselected += (textBox, key) => { GameMain.Client.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Name); @@ -1608,13 +1612,13 @@ namespace Barotrauma public void BanPlayer(Client client) { if (GameMain.NetworkMember == null || client == null) { return; } - GameMain.Client.CreateKickReasonPrompt(client.Name, true); + GameMain.Client.CreateKickReasonPrompt(client.Name, ban: true, rangeBan: false); } public void BanPlayerRange(Client client) { if (GameMain.NetworkMember == null || client == null) { return; } - GameMain.Client.CreateKickReasonPrompt(client.Name, true, true); + GameMain.Client.CreateKickReasonPrompt(client.Name, ban: true, rangeBan: true); } public override void AddToGUIUpdateList() diff --git a/Barotrauma/BarotraumaClient/Source/Screens/OldCharacterEditorScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/OldCharacterEditorScreen.cs deleted file mode 100644 index 26e813e63..000000000 --- a/Barotrauma/BarotraumaClient/Source/Screens/OldCharacterEditorScreen.cs +++ /dev/null @@ -1,333 +0,0 @@ -using FarseerPhysics; -using FarseerPhysics.Dynamics.Joints; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Barotrauma -{ - class OldCharacterEditorScreen : Screen - { - private Camera cam; - - private GUIComponent GUIpanel; - private GUIButton physicsButton; - - private GUIListBox limbList, jointList; - - private GUIFrame limbPanel; - - private Character editingCharacter; - private Limb editingLimb; - - private List textures; - private List texturePaths; - - private bool physicsEnabled; - - public override Camera Cam - { - get { return cam; } - } - - public override void Select() - { - base.Select(); - - GameMain.DebugDraw = true; - - cam = new Camera(); - - /*GUIpanel = new GUIFrame(new Rectangle(0, 0, 300, GameMain.GraphicsHeight), ""); - //GUIpanel.Padding = new Vector4(10.0f, 10.0f, 10.0f, 10.0f); - - physicsButton = new GUIButton(new Rectangle(0, 50, 200, 25), "Physics", Alignment.Left, "", GUIpanel); - physicsButton.OnClicked += TogglePhysics; - - new GUITextBlock(new Rectangle(0, 80, 0, 25), "Limbs:", "", GUIpanel); - limbList = new GUIListBox(new Rectangle(0, 110, 0, 250), Color.White * 0.7f, "", GUIpanel); - limbList.OnSelected = SelectLimb; - - new GUITextBlock(new Rectangle(0, 360, 0, 25), "Joints:", "", GUIpanel); - jointList = new GUIListBox(new Rectangle(0, 390, 0, 250), Color.White * 0.7f, "", GUIpanel);*/ - - while (Character.CharacterList.Count > 1) - { - Character.CharacterList.First().Remove(); - } - - if (Character.CharacterList.Count == 1) - { - if (editingCharacter != Character.CharacterList[0]) UpdateLimbLists(Character.CharacterList[0]); - editingCharacter = Character.CharacterList[0]; - - Vector2 camPos = editingCharacter.AnimController.Limbs[0].body.SimPosition; - camPos = ConvertUnits.ToDisplayUnits(camPos); - camPos.Y = -camPos.Y; - cam.TargetPos = camPos; - - if (physicsEnabled) - { - editingCharacter.Control(1.0f, cam); - } - else - { - cam.TargetPos = Vector2.Zero; - } - } - - textures = new List(); - texturePaths = new List(); - foreach (Limb limb in editingCharacter.AnimController.Limbs) - { - if (limb.ActiveSprite==null || texturePaths.Contains(limb.ActiveSprite.FilePath)) continue; - textures.Add(limb.ActiveSprite.Texture); - texturePaths.Add(limb.ActiveSprite.FilePath); - } - } - - /// - /// Allows the game to run logic such as updating the world, - /// checking for collisions, gathering input, and playing audio. - /// - public override void Update(double deltaTime) - { - cam.MoveCamera((float)deltaTime); - - GUIpanel.UpdateManually((float)deltaTime); - - if (physicsEnabled) - { - Character.UpdateAnimAll((float)deltaTime); - - Ragdoll.UpdateAll((float)deltaTime, cam); - - GameMain.World.Step((float)deltaTime); - } - } - - public override void AddToGUIUpdateList() - { - GUIpanel.AddToGUIUpdateList(); - } - - /// - /// This is called when the game should draw itself. - /// - public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch) - { - //cam.UpdateTransform(); - - graphics.Clear(Color.CornflowerBlue); - - spriteBatch.Begin(SpriteSortMode.BackToFront, - BlendState.AlphaBlend, - null, null, null, null, - cam.Transform); - - Submarine.Draw(spriteBatch, true); - - spriteBatch.End(); - - spriteBatch.Begin(SpriteSortMode.BackToFront, - BlendState.AlphaBlend, - null, null, null, null, - cam.Transform); - - //if (EntityPrefab.Selected != null) EntityPrefab.Selected.UpdatePlacing(spriteBatch, cam); - - //Entity.DrawSelecting(spriteBatch, cam); - - if (editingCharacter!=null) - editingCharacter.Draw(spriteBatch, Cam); - - spriteBatch.End(); - - //-------------------- HUD ----------------------------- - - spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, GameMain.ScissorTestEnable); - - GUIpanel.DrawManually(spriteBatch); - - EditLimb(spriteBatch); - - - int y = 0; - for (int i = 0; i < textures.Count; i++ ) - { - int x = GameMain.GraphicsWidth - textures[i].Width; - spriteBatch.Draw(textures[i], new Vector2(x, y), Color.White); - - foreach (Limb limb in editingCharacter.AnimController.Limbs) - { - if (limb.ActiveSprite == null || limb.ActiveSprite.FilePath != texturePaths[i]) continue; - Rectangle rect = limb.ActiveSprite.SourceRect; - rect.X += x; - rect.Y += y; - - GUI.DrawRectangle(spriteBatch, rect, Color.Red); - - Vector2 limbBodyPos = new Vector2( - rect.X + limb.ActiveSprite.Origin.X, - rect.Y + limb.ActiveSprite.Origin.Y); - - DrawJoints(spriteBatch, limb, limbBodyPos); - - //if (limb.BodyShapeTexture == null) continue; - - //spriteBatch.Draw(limb.BodyShapeTexture, limbBodyPos, - // null, Color.White, 0.0f, - // new Vector2(limb.BodyShapeTexture.Width, limb.BodyShapeTexture.Height) / 2, - // 1.0f, SpriteEffects.None, 0.0f); - - GUI.DrawLine(spriteBatch, limbBodyPos + Vector2.UnitY * 5.0f, limbBodyPos - Vector2.UnitY * 5.0f, Color.White); - GUI.DrawLine(spriteBatch, limbBodyPos + Vector2.UnitX * 5.0f, limbBodyPos - Vector2.UnitX * 5.0f, Color.White); - - if (Vector2.Distance(PlayerInput.MousePosition, limbBodyPos)<5.0f && PlayerInput.LeftButtonHeld()) - { - limb.ActiveSprite.Origin += PlayerInput.MouseSpeed; - } - } - - y += textures[i].Height; - } - - - GUI.Draw(Cam, spriteBatch); - - //EntityPrefab.DrawList(spriteBatch, new Vector2(20,50)); - - //Entity.Edit(spriteBatch, cam); - - spriteBatch.End(); - } - - private void UpdateLimbLists(Character character) - { - limbList.ClearChildren(); - /*foreach (Limb limb in character.AnimController.Limbs) - { - GUITextBlock textBlock = new GUITextBlock( - new Rectangle(0,0,0,25), - limb.type.ToString(), - Color.Transparent, - Color.White, - Alignment.Left, null, - limbList); - textBlock.Padding = new Vector4(10.0f, 0.0f, 0.0f, 0.0f); - textBlock.UserData = limb; - } - - jointList.ClearChildren(); - foreach (RevoluteJoint joint in character.AnimController.LimbJoints) - { - Limb limb1 = (Limb)(joint.BodyA.UserData); - Limb limb2 = (Limb)(joint.BodyB.UserData); - - GUITextBlock textBlock = new GUITextBlock( - new Rectangle(0, 0, 0, 25), - limb1.type.ToString() + " - " + limb2.type.ToString(), - Color.Transparent, - Color.White, - Alignment.Left, null, - jointList); - textBlock.Padding = new Vector4(10.0f, 0.0f, 0.0f, 0.0f); - textBlock.UserData = joint; - }*/ - } - - private void DrawJoints(SpriteBatch spriteBatch, Limb limb, Vector2 limbBodyPos) - { - foreach (var joint in editingCharacter.AnimController.LimbJoints) - { - Vector2 jointPos = Vector2.Zero; - - if (joint.BodyA == limb.body.FarseerBody) - { - jointPos = ConvertUnits.ToDisplayUnits(joint.LocalAnchorA); - - } - else if (joint.BodyB == limb.body.FarseerBody) - { - jointPos = ConvertUnits.ToDisplayUnits(joint.LocalAnchorB); - } - else - { - continue; - } - - Vector2 tformedJointPos = jointPos /= limb.Scale; - tformedJointPos.Y = -tformedJointPos.Y; - tformedJointPos += limbBodyPos; - - if (joint.BodyA == limb.body.FarseerBody) - { - float a1 = joint.UpperLimit - MathHelper.PiOver2; - float a2 = joint.LowerLimit - MathHelper.PiOver2; - float a3 = (a1 + a2) / 2.0f; - GUI.DrawLine(spriteBatch, tformedJointPos, tformedJointPos + new Vector2((float)Math.Cos(a1), -(float)Math.Sin(a1)) * 30.0f, Color.Green); - GUI.DrawLine(spriteBatch, tformedJointPos, tformedJointPos + new Vector2((float)Math.Cos(a2), -(float)Math.Sin(a2)) * 30.0f, Color.DarkGreen); - - GUI.DrawLine(spriteBatch, tformedJointPos, tformedJointPos + new Vector2((float)Math.Cos(a3), -(float)Math.Sin(a3)) * 30.0f, Color.LightGray); - } - - GUI.DrawRectangle(spriteBatch, tformedJointPos, new Vector2(5.0f, 5.0f), Color.Red, true); - if (Vector2.Distance(PlayerInput.MousePosition, tformedJointPos) < 10.0f) - { - GUI.DrawString(spriteBatch, tformedJointPos + Vector2.One*10.0f, jointPos.ToString(), Color.White, Color.Black * 0.5f); - GUI.DrawRectangle(spriteBatch, tformedJointPos - new Vector2(3.0f, 3.0f), new Vector2(11.0f, 11.0f), Color.Red, false); - if (PlayerInput.LeftButtonHeld()) - { - Vector2 speed = ConvertUnits.ToSimUnits(PlayerInput.MouseSpeed); - speed.Y = -speed.Y; - if (joint.BodyA == limb.body.FarseerBody) - { - joint.LocalAnchorA += speed; - } - else - { - joint.LocalAnchorB += speed; - } - } - } - } - } - - private bool SelectLimb(GUIComponent component, object selection) - { - /*try - { - editingLimb = (Limb)selection; - limbPanel = new GUIFrame(new Rectangle(300, 0, 500, 100), Color.Gray*0.8f); - //limbPanel.Padding = new Vector4(10.0f,10.0f,10.0f,10.0f); - new GUITextBlock(new Rectangle(0, 0, 200, 25), editingLimb.type.ToString(), Color.Transparent, Color.Black, Alignment.Left, null, limbPanel); - - //spriteOrigin = new GUITextBlock(new Rectangle(0, 25, 200, 25), "Sprite origin: ", Color.White, Color.Black, Alignment.Left, limbPanel); - - } - catch - { - return false; - }*/ - return true; - } - - private void EditLimb(SpriteBatch spriteBatch) - { - if (editingLimb == null) return; - - limbPanel.DrawManually(spriteBatch); - } - - private bool TogglePhysics(GUIButton button, object selection) - { - physicsEnabled = !physicsEnabled; - - physicsButton.Text = (physicsEnabled) ? "Disable physics" : "Enable physics"; - - return false; - } - } -} diff --git a/Barotrauma/BarotraumaClient/Source/Screens/ServerListScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/ServerListScreen.cs index b109e3f51..b0688089c 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/ServerListScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/ServerListScreen.cs @@ -19,73 +19,87 @@ namespace Barotrauma //how often the client is allowed to refresh servers private TimeSpan AllowedRefreshInterval = new TimeSpan(0, 0, 3); - private GUIFrame menu; + private readonly GUIFrame menu; - private GUIListBox serverList; - private GUIListBox serverPreview; + private readonly GUIListBox serverList; + private readonly GUIFrame serverPreview; - private GUIButton joinButton; + private readonly GUIButton joinButton; - private GUITextBox clientNameBox, ipBox; + private readonly GUITextBox clientNameBox, ipBox; private bool masterServerResponded; private IRestResponse masterServerResponse; - - private GUIButton refreshButton; - - private float[] columnRelativeWidth; + + private readonly float[] columnRelativeWidth = new float[] { 0.1f, 0.1f, 0.7f, 0.12f, 0.08f, 0.08f }; + private readonly string[] columnLabel = new string[] { "ServerListCompatible", "ServerListHasPassword", "ServerListName", "ServerListRoundStarted", "ServerListPlayers", "ServerListPing" }; + + private readonly GUILayoutGroup labelHolder; + private readonly List labelTexts = new List(); //filters - private GUITextBox searchBox; - private GUITickBox filterPassword; - private GUITickBox filterIncompatible; - private GUITickBox filterFull; - private GUITickBox filterEmpty; + private readonly GUITextBox searchBox; + private readonly GUITickBox filterPassword; + private readonly GUITickBox filterIncompatible; + private readonly GUITickBox filterFull; + private readonly GUITickBox filterEmpty; - //a timer for + private string sortedBy; + + private readonly GUIButton serverPreviewToggleButton; + + //a timer for preventing the client from spamming the refresh button faster than AllowedRefreshInterval private DateTime refreshDisableTimer; private bool waitingForRefresh; - + public ServerListScreen() { GameMain.Instance.OnResolutionChanged += OnResolutionChanged; - menu = new GUIFrame(new RectTransform(new Vector2(0.7f, 0.8f), GUI.Canvas, Anchor.Center) { MinSize = new Point(GameMain.GraphicsHeight, 0) }); + menu = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.85f), GUI.Canvas, Anchor.Center) { MinSize = new Point(GameMain.GraphicsHeight, 0) }); - var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.97f, 0.95f), menu.RectTransform, Anchor.Center), isHorizontal: true) - { Stretch = true, RelativeSpacing = 0.02f }; - - //------------------------------------------------------------------------------------- - //left column - //------------------------------------------------------------------------------------- - - var leftColumn = new GUILayoutGroup(new RectTransform(new Vector2(0.25f, 1.0f), paddedFrame.RectTransform, Anchor.CenterLeft)) { Stretch = true, RelativeSpacing = 0.5f }; - - var infoHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), leftColumn.RectTransform)) { RelativeSpacing = 0.05f }; - - new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), infoHolder.RectTransform, Anchor.Center), TextManager.Get("JoinServer"), font: GUI.LargeFont) + var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.97f, 0.95f), menu.RectTransform, Anchor.Center)) { + RelativeSpacing = 0.02f, + Stretch = true + }; + + //------------------------------------------------------------------------------------- + //Top row + //------------------------------------------------------------------------------------- + + var topRow = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), paddedFrame.RectTransform)) { Stretch = true }; + + var title = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.33f), topRow.RectTransform), TextManager.Get("JoinServer"), font: GUI.LargeFont) + { + Padding = Vector4.Zero, ForceUpperCase = true, AutoScale = true }; - new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoHolder.RectTransform), TextManager.Get("YourName")); - clientNameBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.13f), infoHolder.RectTransform), "") + var infoHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.33f), topRow.RectTransform), isHorizontal: true) { RelativeSpacing = 0.05f, Stretch = true }; + + var clientNameHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), infoHolder.RectTransform)) { RelativeSpacing = 0.05f }; + + new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), clientNameHolder.RectTransform), TextManager.Get("YourName")); + clientNameBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.5f), clientNameHolder.RectTransform), "") { Text = GameMain.Config.DefaultPlayerName, MaxTextLength = Client.MaxNameLength, OverflowClip = true }; + if (string.IsNullOrEmpty(clientNameBox.Text)) { clientNameBox.Text = SteamManager.GetUsername(); } - clientNameBox.OnTextChanged += RefreshJoinButtonState; - new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoHolder.RectTransform), TextManager.Get("ServerIP")); - ipBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.13f), infoHolder.RectTransform), ""); - ipBox.OnTextChanged += RefreshJoinButtonState; - ipBox.OnSelected += (sender, key) => + var ipBoxHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), infoHolder.RectTransform)) { RelativeSpacing = 0.05f }; + + new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), ipBoxHolder.RectTransform), TextManager.Get("ServerIP")); + ipBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.5f), ipBoxHolder.RectTransform), ""); + ipBox.OnTextChanged += (textBox, text) => { joinButton.Enabled = !string.IsNullOrEmpty(text); return true; }; + ipBox.OnSelected += (sender, key) => { if (sender.UserData is ServerInfo) { @@ -94,93 +108,357 @@ namespace Barotrauma } }; - var filterHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), leftColumn.RectTransform)) { RelativeSpacing = 0.05f }; - - new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), filterHolder.RectTransform), TextManager.Get("FilterServers")); - searchBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.13f), filterHolder.RectTransform), ""); - - var tickBoxHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), filterHolder.RectTransform)); - - searchBox.OnTextChanged += (txtBox, txt) => { FilterServers(); return true; }; - filterPassword = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.27f), tickBoxHolder.RectTransform), TextManager.Get("FilterPassword")); - filterPassword.OnSelected += (tickBox) => { FilterServers(); return true; }; - filterIncompatible = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.27f), tickBoxHolder.RectTransform), TextManager.Get("FilterIncompatibleServers")); - filterIncompatible.OnSelected += (tickBox) => { FilterServers(); return true; }; - - filterFull = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.27f), tickBoxHolder.RectTransform), TextManager.Get("FilterFullServers")); - filterFull.OnSelected += (tickBox) => { FilterServers(); return true; }; - filterEmpty = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.27f), tickBoxHolder.RectTransform), TextManager.Get("FilterEmptyServers")); - filterEmpty.OnSelected += (tickBox) => { FilterServers(); return true; }; - //------------------------------------------------------------------------------------- - //right column + // Bottom row //------------------------------------------------------------------------------------- - var rightColumn = new GUILayoutGroup(new RectTransform(new Vector2(1.0f - leftColumn.RectTransform.RelativeSize.X - 0.017f, 1.0f), + var bottomRow = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f - topRow.RectTransform.RelativeSize.Y), paddedFrame.RectTransform, Anchor.CenterRight)) { - RelativeSpacing = 0.02f, Stretch = true }; - var serverListHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), rightColumn.RectTransform)) { Stretch = true, RelativeSpacing = 0.02f }; - - serverList = new GUIListBox(new RectTransform(new Vector2(1.0f, 1.0f), serverListHolder.RectTransform, Anchor.Center)) + var serverListHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), bottomRow.RectTransform), isHorizontal: true) { - OnSelected = (btn, obj) => { - if (obj is ServerInfo) + Stretch = true + }; + + // filters ------------------------------------------- + + var filters = new GUIFrame(new RectTransform(new Vector2(0.25f, 1.0f), serverListHolder.RectTransform, Anchor.Center), style: null) + { + Color = new Color(12, 14, 15, 255) * 0.5f, + OutlineColor = Color.Black + }; + var filterToggle = new GUIButton(new RectTransform(new Vector2(0.02f, 1.0f), serverListHolder.RectTransform, Anchor.CenterRight) { MinSize = new Point(20, 0) }, style: "UIToggleButton") + { + OnClicked = (btn, userdata) => + { + filters.RectTransform.RelativeSize = new Vector2(0.25f, 1.0f); + filters.Visible = !filters.Visible; + filters.IgnoreLayoutGroups = !filters.Visible; + serverListHolder.Recalculate(); + btn.Children.ForEach(c => c.SpriteEffects = !filters.Visible ? SpriteEffects.None : SpriteEffects.FlipHorizontally); + return true; + } + }; + filterToggle.Children.ForEach(c => c.SpriteEffects = SpriteEffects.FlipHorizontally); + + var filterContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.99f), filters.RectTransform, Anchor.Center)) + { + Stretch = true, + RelativeSpacing = 0.015f + }; + + var filterTitle = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), filterContainer.RectTransform), TextManager.Get("FilterServers"), font: GUI.LargeFont) + { + Padding = Vector4.Zero, + AutoScale = true + }; + + float elementHeight = 0.05f; + + var searchHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, elementHeight), filterContainer.RectTransform), isHorizontal: true) { Stretch = true }; + + var searchTitle = new GUITextBlock(new RectTransform(new Vector2(0.001f, 1.0f), searchHolder.RectTransform), TextManager.Get("Search") + "..."); + searchBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 1.0f), searchHolder.RectTransform), ""); + searchBox.OnSelected += (sender, userdata) => { searchTitle.Visible = false; }; + searchBox.OnDeselected += (sender, userdata) => { searchTitle.Visible = true; }; + searchBox.OnTextChanged += (txtBox, txt) => { FilterServers(); return true; }; + + var filterHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), filterContainer.RectTransform)) { RelativeSpacing = 0.005f }; + + List filterTextList = new List(); + filterPassword = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filterHolder.RectTransform), TextManager.Get("FilterPassword")) + { + ToolTip = TextManager.Get("FilterPassword"), + OnSelected = (tickBox) => { FilterServers(); return true; } + }; + filterTextList.Add(filterPassword.TextBlock); + filterIncompatible = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filterHolder.RectTransform), TextManager.Get("FilterIncompatibleServers")) + { + ToolTip = TextManager.Get("FilterIncompatibleServers"), + OnSelected = (tickBox) => { FilterServers(); return true; } + }; + filterTextList.Add(filterIncompatible.TextBlock); + filterFull = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filterHolder.RectTransform), TextManager.Get("FilterFullServers")) + { + ToolTip = TextManager.Get("FilterFullServers"), + OnSelected = (tickBox) => { FilterServers(); return true; } + }; + filterTextList.Add(filterFull.TextBlock); + filterEmpty = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filterHolder.RectTransform), TextManager.Get("FilterEmptyServers")) + { + ToolTip = TextManager.Get("FilterEmptyServers"), + OnSelected = (tickBox) => { FilterServers(); return true; } + }; + filterTextList.Add(filterEmpty.TextBlock); + + filterContainer.RectTransform.SizeChanged += () => + { + filterContainer.RectTransform.RecalculateChildren(true, true); + filterTextList.ForEach(t => t.Text = t.ToolTip); + GUITextBlock.AutoScaleAndNormalize(filterTextList); + if (filterTextList[0].TextScale < 0.8f) + { + filterTextList.ForEach(t => t.TextScale = 1.0f); + filterTextList.ForEach(t => t.Text = ToolBox.LimitString(t.Text, t.Font, (int)(filterContainer.Rect.Width * 0.8f))); + } + }; + + // server list --------------------------------------------------------------------- + + var serverListContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), serverListHolder.RectTransform)) { Stretch = true }; + + labelHolder = new GUILayoutGroup(new RectTransform(new Vector2(0.99f, 0.05f), serverListContainer.RectTransform) { MinSize = new Point(0, 15) }, + isHorizontal: true) + { + Stretch = true + }; + + for (int i = 0; i < columnRelativeWidth.Length; i++) + { + var btn = new GUIButton(new RectTransform(new Vector2(columnRelativeWidth[i], 1.0f), labelHolder.RectTransform), + text: TextManager.Get(columnLabel[i]), textAlignment: Alignment.Center, style: null) + { + Color = new Color(12, 14, 15, 255) * 0.5f, + HoverColor = new Color(12, 14, 15, 255) * 2.5f, + SelectedColor = Color.Gray * 0.7f, + PressedColor = Color.Gray * 0.7f, + OutlineColor = Color.Black, + ToolTip = TextManager.Get(columnLabel[i]), + ForceUpperCase = true, + UserData = columnLabel[i], + OnClicked = SortList + }; + labelTexts.Add(btn.TextBlock); + + new GUIImage(new RectTransform(new Vector2(0.5f, 0.3f), btn.RectTransform, Anchor.BottomCenter, scaleBasis: ScaleBasis.BothHeight), style: "GUIButtonVerticalArrow", scaleToFit: true) + { + CanBeFocused = false, + UserData = "arrowup", + Visible = false + }; + new GUIImage(new RectTransform(new Vector2(0.5f, 0.3f), btn.RectTransform, Anchor.BottomCenter, scaleBasis: ScaleBasis.BothHeight), style: "GUIButtonVerticalArrow", scaleToFit: true) + { + CanBeFocused = false, + UserData = "arrowdown", + SpriteEffects = SpriteEffects.FlipVertically, + Visible = false + }; + } + + serverList = new GUIListBox(new RectTransform(new Vector2(1.0f, 1.0f), serverListContainer.RectTransform, Anchor.Center)) + { + ScrollBarVisible = true, + OnSelected = (btn, obj) => + { + if (obj is ServerInfo serverInfo) { - ServerInfo serverInfo = (ServerInfo)obj; + joinButton.Enabled = true; + ipBox.UserData = serverInfo; + ipBox.Text = serverInfo.ServerName; + if (!serverPreview.Visible) + { + serverPreview.RectTransform.RelativeSize = new Vector2(0.3f, 1.0f); + serverPreviewToggleButton.Visible = true; + serverPreviewToggleButton.IgnoreLayoutGroups = false; + serverPreview.Visible = true; + serverPreview.IgnoreLayoutGroups = false; + serverListHolder.Recalculate(); + } serverInfo.CreatePreviewWindow(serverPreview); + btn.Children.ForEach(c => c.SpriteEffects = serverPreview.Visible ? SpriteEffects.None : SpriteEffects.FlipHorizontally); } return true; } }; - serverList.OnSelected += SelectServer; + //server preview panel -------------------------------------------------- - serverPreview = new GUIListBox(new RectTransform(new Vector2(1.0f, 1.0f), serverListHolder.RectTransform, Anchor.Center)); + serverPreviewToggleButton = new GUIButton(new RectTransform(new Vector2(0.02f, 1.0f), serverListHolder.RectTransform, Anchor.CenterRight) { MinSize = new Point(20, 0) }, style: "UIToggleButton") + { + Visible = false, + OnClicked = (btn, userdata) => + { + serverPreview.RectTransform.RelativeSize = new Vector2(0.25f, 1.0f); + serverPreview.Visible = !serverPreview.Visible; + serverPreview.IgnoreLayoutGroups = !serverPreview.Visible; + serverListHolder.Recalculate(); + btn.Children.ForEach(c => c.SpriteEffects = serverPreview.Visible ? SpriteEffects.None : SpriteEffects.FlipHorizontally); + return true; + } + }; - columnRelativeWidth = new float[] { 0.04f, 0.02f, 0.044f, 0.77f, 0.02f, 0.075f, 0.06f }; + serverPreview = new GUIFrame(new RectTransform(new Vector2(0.3f, 1.0f), serverListHolder.RectTransform, Anchor.Center), style: null) + { + Color = new Color(12, 14, 15, 255) * 0.5f, + OutlineColor = Color.Black, + IgnoreLayoutGroups = true, + Visible = false + }; - var buttonContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.075f), rightColumn.RectTransform), style: null); + // Spacing + new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), bottomRow.RectTransform), style: null); - GUIButton button = new GUIButton(new RectTransform(new Vector2(0.25f, 0.9f), buttonContainer.RectTransform, Anchor.TopLeft), + var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.075f), bottomRow.RectTransform, Anchor.Center), isHorizontal: true) + { + RelativeSpacing = 0.02f, + Stretch = true + }; + + GUIButton button = new GUIButton(new RectTransform(new Vector2(0.25f, 0.9f), buttonContainer.RectTransform), TextManager.Get("Back"), style: "GUIButtonLarge") { OnClicked = GameMain.MainMenuScreen.ReturnToMainMenu }; - refreshButton = new GUIButton(new RectTransform(new Vector2(buttonContainer.Rect.Height / (float)buttonContainer.Rect.Width, 0.9f), buttonContainer.RectTransform, Anchor.Center), - "", style: "GUIButtonRefresh") { - - ToolTip = TextManager.Get("ServerListRefresh"), - OnClicked = RefreshServers + new GUIButton(new RectTransform(new Vector2(0.25f, 0.9f), buttonContainer.RectTransform), + TextManager.Get("ServerListRefresh"), style: "GUIButtonLarge") + { + OnClicked = (btn, userdata) => { RefreshServers(); return true; } }; - joinButton = new GUIButton(new RectTransform(new Vector2(0.25f, 0.9f), buttonContainer.RectTransform, Anchor.TopRight), + /*var directJoinButton = new GUIButton(new RectTransform(new Vector2(0.25f, 0.9f), buttonContainer.RectTransform), + TextManager.Get("serverlistdirectjoin"), style: "GUIButtonLarge") + { + OnClicked = (btn, userdata) => { ShowDirectJoinPrompt(); return true; } + };*/ + + joinButton = new GUIButton(new RectTransform(new Vector2(0.25f, 0.9f), buttonContainer.RectTransform), TextManager.Get("ServerListJoin"), style: "GUIButtonLarge") { - OnClicked = JoinServer, + OnClicked = (btn, userdata) => + { + if (ipBox.UserData is ServerInfo selectedServer) + { + JoinServer(selectedServer.IP + ":" + selectedServer.Port, selectedServer.ServerName); + } + else if (!string.IsNullOrEmpty(ipBox.Text)) + { + JoinServer(ipBox.Text, ""); + } + return true; + }, Enabled = false }; //-------------------------------------------------------- - button.SelectedColor = button.Color; + bottomRow.Recalculate(); + serverListHolder.Recalculate(); + serverListContainer.Recalculate(); + labelHolder.RectTransform.MaxSize = new Point(serverList.Content.Rect.Width, int.MaxValue); + labelHolder.Recalculate(); + serverList.Content.RectTransform.SizeChanged += () => + { + labelHolder.RectTransform.MaxSize = new Point(serverList.Content.Rect.Width, int.MaxValue); + labelHolder.Recalculate(); + foreach (GUITextBlock labelText in labelTexts) + { + labelText.Text = ToolBox.LimitString(labelText.ToolTip, labelText.Font, labelText.Rect.Width); + } + }; + + button.SelectedColor = button.Color; refreshDisableTimer = DateTime.Now; } private void OnResolutionChanged() { menu.RectTransform.MinSize = new Point(GameMain.GraphicsHeight, 0); + labelHolder.RectTransform.MaxSize = new Point(serverList.Content.Rect.Width, int.MaxValue); + foreach (GUITextBlock labelText in labelTexts) + { + labelText.Text = ToolBox.LimitString(labelText.ToolTip, labelText.Font, labelText.Rect.Width); + } } + private bool SortList(GUIButton button, object obj) + { + if (!(obj is string sortBy)) { return false; } + SortList(sortBy, toggle: true); + return true; + } + + private void SortList(string sortBy, bool toggle) + { + GUIButton button = labelHolder.GetChildByUserData(sortBy) as GUIButton; + if (button == null) { return; } + + sortedBy = sortBy; + + var arrowUp = button.GetChildByUserData("arrowup"); + var arrowDown = button.GetChildByUserData("arrowdown"); + + //disable arrow buttons in other labels + foreach (var child in button.Parent.Children) + { + if (child != button) + { + child.GetChildByUserData("arrowup").Visible = false; + child.GetChildByUserData("arrowdown").Visible = false; + } + } + + bool ascending = arrowUp.Visible; + if (toggle) + { + ascending = !ascending; + } + + arrowUp.Visible = ascending; + arrowDown.Visible = !ascending; + serverList.Content.RectTransform.SortChildren((c1, c2) => + { + ServerInfo s1 = c1.GUIComponent.UserData as ServerInfo; + ServerInfo s2 = c2.GUIComponent.UserData as ServerInfo; + + switch (sortBy) + { + case "ServerListCompatible": + bool? s1Compatible = NetworkMember.IsCompatible(GameMain.Version.ToString(), s1.GameVersion); + if (!s1.ContentPackageHashes.Any()) { s1Compatible = null; } + if (s1Compatible.HasValue) { s1Compatible = s1Compatible.Value && s1.ContentPackagesMatch(GameMain.SelectedPackages); }; + + bool? s2Compatible = NetworkMember.IsCompatible(GameMain.Version.ToString(), s2.GameVersion); + if (!s2.ContentPackageHashes.Any()) { s2Compatible = null; } + if (s2Compatible.HasValue) { s2Compatible = s2Compatible.Value && s2.ContentPackagesMatch(GameMain.SelectedPackages); }; + + //convert to int to make sorting easier + //1 Compatible + //0 Unknown + //-1 Incompatible + int s1CompatibleInt = s1Compatible.HasValue ? + (s1Compatible.Value ? 1 : -1) : + 0; + int s2CompatibleInt = s2Compatible.HasValue ? + (s2Compatible.Value ? 1 : -1) : + 0; + return s2CompatibleInt.CompareTo(s1CompatibleInt) * (ascending ? 1 : -1); + case "ServerListHasPassword": + if (s1.HasPassword == s2.HasPassword) { return 0; } + return (s1.HasPassword ? 1 : -1) * (ascending ? 1 : -1); + case "ServerListName": + return s1.ServerName.CompareTo(s2.ServerName) * (ascending ? 1 : -1); + case "ServerListRoundStarted": + if (s1.GameStarted == s2.GameStarted) { return 0; } + return (s1.GameStarted ? 1 : -1) * (ascending ? 1 : -1); + case "ServerListPlayers": + return s2.PlayerCount.CompareTo(s1.PlayerCount) * (ascending ? 1 : -1); + case "ServerListPing": + return s2.Ping.CompareTo(s1.Ping) * (ascending ? 1 : -1); + default: + return 0; + } + }); + } + public override void Select() { base.Select(); - RefreshServers(null, null); + RefreshServers(); } private void FilterServers() @@ -192,9 +470,15 @@ namespace Barotrauma if (!(child.UserData is ServerInfo)) continue; ServerInfo serverInfo = (ServerInfo)child.UserData; + Version remoteVersion = null; + if (!string.IsNullOrEmpty(serverInfo.GameVersion)) + { + Version.TryParse(serverInfo.GameVersion, out remoteVersion); + } + bool incompatible = (!serverInfo.ContentPackageHashes.Any() && serverInfo.ContentPackagesMatch(GameMain.Config.SelectedContentPackages)) || - (!string.IsNullOrEmpty(serverInfo.GameVersion) && serverInfo.GameVersion != GameMain.Version.ToString()); + (remoteVersion != null && !NetworkMember.IsCompatible(GameMain.Version, remoteVersion)); child.Visible = serverInfo.ServerName.ToLowerInvariant().Contains(searchBox.Text.ToLowerInvariant()) && @@ -216,59 +500,48 @@ namespace Barotrauma serverList.UpdateScrollBarSize(); } - private bool RefreshJoinButtonState(GUIComponent component, object obj) + /*private void ShowDirectJoinPrompt() { - if (obj == null || waitingForRefresh) { return false; } + var msgBox = new GUIMessageBox(TextManager.Get("ServerListDirectJoin"), "", new string[] { TextManager.Get("OK"), TextManager.Get("Cancel") }, + relativeSize: new Vector2(0.25f, 0.2f), minSize: new Point(400, 150)); - if (!string.IsNullOrWhiteSpace(clientNameBox.Text) && !string.IsNullOrWhiteSpace(ipBox.Text)) + var content = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 0.3f), msgBox.InnerFrame.RectTransform, Anchor.Center) { MinSize = new Point(0, 50) }) { - joinButton.Enabled = true; - } - else + IgnoreLayoutGroups = true, + Stretch = true, + RelativeSpacing = 0.05f + }; + + new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), content.RectTransform), TextManager.Get("ServerIP")); + var ipBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.5f), content.RectTransform)); + + var okButton = msgBox.Buttons[0]; + okButton.Enabled = false; + okButton.OnClicked = (btn, userdata) => { - joinButton.Enabled = false; - } + JoinServer(ipBox.Text, ""); + msgBox.Close(); + return true; + }; - return true; - } + var cancelButton = msgBox.Buttons[1]; + cancelButton.OnClicked = msgBox.Close; - private bool SelectServer(GUIComponent component, object obj) + ipBox.OnTextChanged += (textBox, text) => + { + okButton.Enabled = !string.IsNullOrEmpty(text); + return true; + }; + }*/ + + private void RefreshServers() { - if (obj == null || waitingForRefresh || (!(obj is ServerInfo))) { return false; } - - if (!string.IsNullOrWhiteSpace(clientNameBox.Text)) - { - joinButton.Enabled = true; - } - else - { - clientNameBox.Flash(); - joinButton.Enabled = false; - } - - ServerInfo serverInfo; - try - { - serverInfo = (ServerInfo)obj; - ipBox.UserData = serverInfo; - ipBox.Text = ToolBox.LimitString(serverInfo.ServerName, ipBox.Font, ipBox.Rect.Width); - } - catch (InvalidCastException) - { - return false; - } - - return true; - } - - private bool RefreshServers(GUIButton button, object obj) - { - if (waitingForRefresh) { return false; } + if (waitingForRefresh) { return; } serverList.ClearChildren(); serverPreview.ClearChildren(); - - ipBox.Text = null; joinButton.Enabled = false; + ipBox.UserData = null; + ipBox.Text = ""; new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), serverList.Content.RectTransform), TextManager.Get("RefreshingServerList"), textAlignment: Alignment.Center) @@ -277,8 +550,6 @@ namespace Barotrauma }; CoroutineManager.StartCoroutine(WaitForRefresh()); - - return true; } private IEnumerable WaitForRefresh() @@ -392,12 +663,15 @@ namespace Barotrauma { UserData = serverInfo }; - var serverContent = new GUILayoutGroup(new RectTransform(new Vector2(0.98f, 1.0f), serverFrame.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) + new GUILayoutGroup(new RectTransform(new Vector2(0.98f, 1.0f), serverFrame.RectTransform, Anchor.Center), isHorizontal: true, childAnchor: Anchor.CenterLeft) { Stretch = true, - RelativeSpacing = 0.02f + //RelativeSpacing = 0.02f }; UpdateServerInfo(serverInfo); + + SortList(sortedBy, toggle: false); + FilterServers(); } private void UpdateServerInfo(ServerInfo serverInfo) @@ -405,7 +679,7 @@ namespace Barotrauma var serverFrame = serverList.Content.FindChild(serverInfo); if (serverFrame == null) return; - var serverContent = serverFrame.Children.First(); + var serverContent = serverFrame.Children.First() as GUILayoutGroup; serverContent.ClearChildren(); var compatibleBox = new GUITickBox(new RectTransform(new Vector2(columnRelativeWidth[0], 0.9f), serverContent.RectTransform, Anchor.Center), label: "") @@ -425,21 +699,22 @@ namespace Barotrauma UserData = "password" }; - var serverName = new GUITextBlock(new RectTransform(new Vector2(columnRelativeWidth[3], 1.0f), serverContent.RectTransform), serverInfo.ServerName, style: "GUIServerListTextBox"); - var gameStartedBox = new GUITickBox(new RectTransform(new Vector2(columnRelativeWidth[4], 0.4f), serverContent.RectTransform, Anchor.Center), - label: "", style: "GUIServerListRoundStartedTickBox") { + var serverName = new GUITextBlock(new RectTransform(new Vector2(columnRelativeWidth[2] * 1.1f, 1.0f), serverContent.RectTransform), serverInfo.ServerName, style: "GUIServerListTextBox"); + + new GUITickBox(new RectTransform(new Vector2(columnRelativeWidth[3], 0.9f), serverContent.RectTransform, Anchor.Center), label: "") + { ToolTip = TextManager.Get((serverInfo.GameStarted) ? "ServerListRoundStarted" : "ServerListRoundNotStarted"), Selected = serverInfo.GameStarted, Enabled = false }; - var serverPlayers = new GUITextBlock(new RectTransform(new Vector2(columnRelativeWidth[5], 1.0f), serverContent.RectTransform), + var serverPlayers = new GUITextBlock(new RectTransform(new Vector2(columnRelativeWidth[4], 1.0f), serverContent.RectTransform), serverInfo.PlayerCount + "/" + serverInfo.MaxPlayers, style: "GUIServerListTextBox", textAlignment: Alignment.Right) { ToolTip = TextManager.Get("ServerListPlayers") }; - var serverPingText = new GUITextBlock(new RectTransform(new Vector2(columnRelativeWidth[6], 1.0f), serverContent.RectTransform), "?", + var serverPingText = new GUITextBlock(new RectTransform(new Vector2(columnRelativeWidth[5], 1.0f), serverContent.RectTransform), "?", style: "GUIServerListTextBox", textColor: Color.White * 0.5f, textAlignment: Alignment.Right) { ToolTip = TextManager.Get("ServerListPing") @@ -448,6 +723,7 @@ namespace Barotrauma if (serverInfo.PingChecked) { serverPingText.Text = serverInfo.Ping > -1 ? serverInfo.Ping.ToString() : "?"; + serverPingText.TextColor = GetPingTextColor(serverInfo.Ping); } else if (!string.IsNullOrEmpty(serverInfo.IP)) { @@ -501,6 +777,8 @@ namespace Barotrauma serverPlayers.TextColor *= 0.5f; } + serverContent.Recalculate(); + SortList(sortedBy, toggle: false); FilterServers(); } @@ -594,37 +872,17 @@ namespace Barotrauma masterServerResponded = true; } - private bool JoinServer(GUIButton button, object obj) + private bool JoinServer(string ip, string serverName) { if (string.IsNullOrWhiteSpace(clientNameBox.Text)) { clientNameBox.Flash(); - joinButton.Enabled = false; return false; } GameMain.Config.DefaultPlayerName = clientNameBox.Text; GameMain.Config.SaveNewPlayerConfig(); - string ip = null; - string serverName = null; - if (ipBox.UserData is ServerInfo serverInfo) - { - ip = serverInfo.IP + ":" + serverInfo.Port; - serverName = serverInfo.ServerName; - } - else if (!string.IsNullOrWhiteSpace(ipBox.Text)) - { - ip = ipBox.Text; - } - - if (string.IsNullOrWhiteSpace(ip)) - { - ipBox.Flash(); - joinButton.Enabled = false; - return false; - } - CoroutineManager.StartCoroutine(ConnectToServer(ip, serverName)); return true; @@ -671,18 +929,7 @@ namespace Barotrauma { if (serverInfo.Ping != -1) { - if (serverInfo.Ping < 50) - { - serverPingText.TextColor = Color.Green * 1.75f; - } - else if (serverInfo.Ping < 150) - { - serverPingText.TextColor = Color.Yellow * 0.85f; - } - else - { - serverPingText.TextColor = Color.Red * 0.75f; - } + serverPingText.TextColor = GetPingTextColor(serverInfo.Ping); } serverPingText.Text = serverInfo.Ping > -1 ? serverInfo.Ping.ToString() : "?"; yield return CoroutineStatus.Success; @@ -693,6 +940,12 @@ namespace Barotrauma yield return CoroutineStatus.Success; } + private Color GetPingTextColor(int ping) + { + if (ping < 0) { return Color.DarkRed; } + return ToolBox.GradientLerp(ping / 200.0f, Color.LightGreen, Color.Yellow * 0.8f, Color.Red * 0.75f); + } + public void PingServer(ServerInfo serverInfo, int timeOut) { if (serverInfo?.IP == null) diff --git a/Barotrauma/BarotraumaClient/Source/Screens/SteamWorkshopScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/SteamWorkshopScreen.cs index f417913a3..905f0dea1 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/SteamWorkshopScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/SteamWorkshopScreen.cs @@ -755,7 +755,7 @@ namespace Barotrauma for (int i = 0; i < item.Tags.Length && i < 5; i++) { if (string.IsNullOrEmpty(item.Tags[i])) { continue; } - string tag = TextManager.Get("Workshop.ContentTag." + item.Tags[i], true); + string tag = TextManager.Get("Workshop.ContentTag." + item.Tags[i].Replace(" ", ""), true); if (string.IsNullOrEmpty(tag)) { tag = item.Tags[i].CapitaliseFirstInvariant(); } tags.Add(tag); } @@ -1393,9 +1393,20 @@ namespace Barotrauma } else { - new GUIMessageBox( - TextManager.Get("Error"), - TextManager.GetWithVariable("WorkshopItemPublishFailed", "[itemname]", TextManager.EnsureUTF8(item.Title)) + item.Error); + string errorMsg = item.ErrorCode.HasValue ? + TextManager.Get("WorkshopPublishError." + item.ErrorCode.Value.ToString(), returnNull: true) : + null; + + if (errorMsg == null) + { + new GUIMessageBox( + TextManager.Get("Error"), + TextManager.GetWithVariable("WorkshopItemPublishFailed", "[itemname]", TextManager.EnsureUTF8(item.Title)) + item.Error); + } + else + { + new GUIMessageBox(TextManager.Get("Error"), errorMsg); + } } createItemFrame.ClearChildren(); diff --git a/Barotrauma/BarotraumaClient/osx/libSDL2-2.0.0.dylib b/Barotrauma/BarotraumaClient/libSDL2-2.0.0.dylib old mode 100755 new mode 100644 similarity index 100% rename from Barotrauma/BarotraumaClient/osx/libSDL2-2.0.0.dylib rename to Barotrauma/BarotraumaClient/libSDL2-2.0.0.dylib diff --git a/Barotrauma/BarotraumaClient/oalinst.exe b/Barotrauma/BarotraumaClient/oalinst.exe deleted file mode 100644 index 0b61822d6..000000000 Binary files a/Barotrauma/BarotraumaClient/oalinst.exe and /dev/null differ diff --git a/Barotrauma/BarotraumaClient/soft_oal_x64.dll b/Barotrauma/BarotraumaClient/soft_oal_x64.dll index f903a0c63..1368f8380 100644 Binary files a/Barotrauma/BarotraumaClient/soft_oal_x64.dll and b/Barotrauma/BarotraumaClient/soft_oal_x64.dll differ diff --git a/Barotrauma/BarotraumaServer/Launch_BarotraumaServer b/Barotrauma/BarotraumaServer/Launch_BarotraumaServer deleted file mode 100644 index 4c7906d0e..000000000 --- a/Barotrauma/BarotraumaServer/Launch_BarotraumaServer +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -exec mono "./DedicatedServer.exe" "$@" diff --git a/Barotrauma/BarotraumaServer/Properties/AssemblyInfo.cs b/Barotrauma/BarotraumaServer/Properties/AssemblyInfo.cs index bd7511b71..a366a224d 100644 --- a/Barotrauma/BarotraumaServer/Properties/AssemblyInfo.cs +++ b/Barotrauma/BarotraumaServer/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.9.0.7")] -[assembly: AssemblyFileVersion("0.9.0.7")] +[assembly: AssemblyVersion("0.9.1.0")] +[assembly: AssemblyFileVersion("0.9.1.0")] diff --git a/Barotrauma/BarotraumaServer/Source/GameMain.cs b/Barotrauma/BarotraumaServer/Source/GameMain.cs index 4ae2894b6..740581cda 100644 --- a/Barotrauma/BarotraumaServer/Source/GameMain.cs +++ b/Barotrauma/BarotraumaServer/Source/GameMain.cs @@ -295,6 +295,8 @@ namespace Barotrauma CloseServer(); SteamManager.ShutDown(); + + if (GameSettings.SaveDebugConsoleLogs) DebugConsole.SaveLogs(); if (GameSettings.SendUserStatistics) GameAnalytics.OnQuit(); } diff --git a/Barotrauma/BarotraumaServer/Source/Items/Components/Machines/Deconstructor.cs b/Barotrauma/BarotraumaServer/Source/Items/Components/Machines/Deconstructor.cs index 2af3e5506..3d3aa6f4b 100644 --- a/Barotrauma/BarotraumaServer/Source/Items/Components/Machines/Deconstructor.cs +++ b/Barotrauma/BarotraumaServer/Source/Items/Components/Machines/Deconstructor.cs @@ -22,6 +22,7 @@ namespace Barotrauma.Items.Components public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null) { msg.Write(IsActive); + msg.Write(progressTimer); } } } diff --git a/Barotrauma/BarotraumaServer/Source/Items/Components/Signal/ConnectionPanel.cs b/Barotrauma/BarotraumaServer/Source/Items/Components/Signal/ConnectionPanel.cs index 1eddbd62f..789bf4eaf 100644 --- a/Barotrauma/BarotraumaServer/Source/Items/Components/Signal/ConnectionPanel.cs +++ b/Barotrauma/BarotraumaServer/Source/Items/Components/Signal/ConnectionPanel.cs @@ -23,8 +23,7 @@ namespace Barotrauma.Items.Components { ushort wireId = msg.ReadUInt16(); - Item wireItem = Entity.FindEntityByID(wireId) as Item; - if (wireItem == null) continue; + if (!(Entity.FindEntityByID(wireId) is Item wireItem)) { continue; } Wire wireComponent = wireItem.GetComponent(); if (wireComponent != null) @@ -35,13 +34,13 @@ namespace Barotrauma.Items.Components } //don't allow rewiring locked panels - if (Locked) return; + if (Locked || !GameMain.NetworkMember.ServerSettings.AllowRewiring) { return; } item.CreateServerEvent(this); //check if the character can access this connectionpanel //and all the wires they're trying to connect - if (!item.CanClientAccess(c)) return; + if (!item.CanClientAccess(c)) { return; } for (int i = 0; i < Connections.Count; i++) { foreach (Wire wire in wires[i]) @@ -50,7 +49,7 @@ namespace Barotrauma.Items.Components // -> we need to check if the client has access to it if (!Connections.Any(connection => connection.Wires.Contains(wire))) { - if (!wire.Item.CanClientAccess(c)) return; + if (!wire.Item.CanClientAccess(c)) { return; } } } } @@ -62,7 +61,7 @@ namespace Barotrauma.Items.Components foreach (Wire existingWire in Connections[i].Wires) { j++; - if (existingWire == null) continue; + if (existingWire == null) { continue; } //existing wire not in the list of new wires -> disconnect it if (!wires[i].Contains(existingWire)) @@ -121,7 +120,7 @@ namespace Barotrauma.Items.Components foreach (Wire newWire in wires[i]) { //already connected, no need to do anything - if (Connections[i].Wires.Contains(newWire)) continue; + if (Connections[i].Wires.Contains(newWire)) { continue; } Connections[i].TryAddLink(newWire); newWire.Connect(Connections[i], true, true); diff --git a/Barotrauma/BarotraumaServer/Source/Networking/BanList.cs b/Barotrauma/BarotraumaServer/Source/Networking/BanList.cs index 58a1aa5b5..65551cb8a 100644 --- a/Barotrauma/BarotraumaServer/Source/Networking/BanList.cs +++ b/Barotrauma/BarotraumaServer/Source/Networking/BanList.cs @@ -35,7 +35,7 @@ namespace Barotrauma.Networking public bool CompareTo(string ipCompare) { - if (string.IsNullOrEmpty(IP)) { return false; } + if (string.IsNullOrEmpty(IP) || string.IsNullOrEmpty(IP)) { return false; } if (!IsRangeBan) { return ipCompare == IP; @@ -50,7 +50,7 @@ namespace Barotrauma.Networking public bool CompareTo(IPAddress ipCompare) { - if (string.IsNullOrEmpty(IP)) { return false; } + if (string.IsNullOrEmpty(IP) || ipCompare == null) { return false; } if (ipCompare.IsIPv4MappedToIPv6 && CompareTo(ipCompare.MapToIPv4().ToString())) { return true; diff --git a/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs b/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs index 448f4711a..b3e1b9209 100644 --- a/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs +++ b/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs @@ -97,6 +97,10 @@ namespace Barotrauma.Networking { name = name.Replace(":", ""); name = name.Replace(";", ""); + if (name.Length > NetConfig.ServerNameMaxLength) + { + name = name.Substring(0, NetConfig.ServerNameMaxLength); + } this.name = name; @@ -185,11 +189,7 @@ namespace Barotrauma.Networking if (SteamManager.USE_STEAM) { - SteamManager.CreateServer(this, isPublic); - if (isPublic) - { - registeredToMaster = true; - } + registeredToMaster = SteamManager.CreateServer(this, isPublic); } if (isPublic && !GameMain.Config.UseSteamMatchmaking) { @@ -1084,6 +1084,10 @@ namespace Barotrauma.Networking Log("Client \"" + sender.Name + "\" kicked \"" + kickedClient.Name + "\".", ServerLog.MessageType.ServerMessage); KickClient(kickedClient, string.IsNullOrEmpty(kickReason) ? $"ServerMessage.KickedBy~[initiator]={sender.Name}" : kickReason); } + else + { + SendDirectChatMessage(TextManager.GetServerMessage($"ServerMessage.PlayerNotFound~[player]={kickedName}"), sender, ChatMessageType.Console); + } break; case ClientPermissions.Ban: string bannedName = inc.ReadString().ToLowerInvariant(); @@ -1104,11 +1108,15 @@ namespace Barotrauma.Networking BanClient(bannedClient, string.IsNullOrEmpty(banReason) ? $"ServerMessage.BannedBy~[initiator]={sender.Name}" : banReason, range); } } + else + { + SendDirectChatMessage(TextManager.GetServerMessage($"ServerMessage.PlayerNotFound~[player]={bannedName}"), sender, ChatMessageType.Console); + } break; case ClientPermissions.Unban: - string unbannedName = inc.ReadString().ToLowerInvariant(); + string unbannedName = inc.ReadString(); string unbannedIP = inc.ReadString(); - UnbanPlayer(unbannedIP, unbannedIP); + UnbanPlayer(unbannedName, unbannedIP); break; case ClientPermissions.ManageRound: bool end = inc.ReadBoolean(); @@ -1784,6 +1792,8 @@ namespace Barotrauma.Networking if (serverSettings.AllowRespawn && missionAllowRespawn) respawnManager = new RespawnManager(this, usingShuttle ? selectedShuttle : null); + entityEventManager.RefreshEntityIDs(); + //assign jobs and spawnpoints separately for each team for (int n = 0; n < teamCount; n++) { @@ -1969,6 +1979,7 @@ namespace Barotrauma.Networking msg.Write(Submarine.MainSubs[1] != null); //loadSecondSub msg.Write(serverSettings.AllowDisguises); + msg.Write(serverSettings.AllowRewiring); Traitor traitor = null; if (TraitorManager != null && TraitorManager.TraitorList.Count > 0) diff --git a/Barotrauma/BarotraumaServer/Source/Networking/GameServerLogin.cs b/Barotrauma/BarotraumaServer/Source/Networking/GameServerLogin.cs index c9c105ba8..78c9ec2b8 100644 --- a/Barotrauma/BarotraumaServer/Source/Networking/GameServerLogin.cs +++ b/Barotrauma/BarotraumaServer/Source/Networking/GameServerLogin.cs @@ -339,13 +339,14 @@ namespace Barotrauma.Networking return; } - if (clVersion != GameMain.Version.ToString()) + bool? isCompatibleVersion = IsCompatible(clVersion, GameMain.Version.ToString()); + if (isCompatibleVersion.HasValue && !isCompatibleVersion.Value) { DisconnectUnauthClient(inc, unauthClient, DisconnectReason.InvalidVersion, $"DisconnectMessage.InvalidVersion~[version]={GameMain.Version.ToString()}~[clientversion]={clVersion}"); - Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (wrong game version)", ServerLog.MessageType.Error); - DebugConsole.NewMessage(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (wrong game version)", Color.Red); + Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (incompatible game version)", ServerLog.MessageType.Error); + DebugConsole.NewMessage(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (incompatible game version)", Color.Red); return; } diff --git a/Barotrauma/BarotraumaServer/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs b/Barotrauma/BarotraumaServer/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs index dc2fdfce9..9c22f1d2e 100644 --- a/Barotrauma/BarotraumaServer/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs +++ b/Barotrauma/BarotraumaServer/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs @@ -10,7 +10,7 @@ namespace Barotrauma.Networking class ServerEntityEvent : NetEntityEvent { private IServerSerializable serializable; - + #if DEBUG public string StackTrace; #endif @@ -21,11 +21,10 @@ namespace Barotrauma.Networking get { return createTime; } } - public ServerEntityEvent(IServerSerializable entity, UInt16 id) - : base(entity, id) + public ServerEntityEvent(IServerSerializable serializableEntity, UInt16 id) + : base(serializableEntity, id) { - serializable = entity; - + serializable = serializableEntity; createTime = Timing.TotalTime; #if DEBUG @@ -292,6 +291,11 @@ namespace Barotrauma.Networking bufferedEvents.Add(bufferedEvent); } + public void RefreshEntityIDs() + { + events.ForEach(e => e.RefreshEntityID()); + uniqueEvents.ForEach(e => e.RefreshEntityID()); + } /// /// Writes all the events that the client hasn't received yet into the outgoing message diff --git a/Barotrauma/BarotraumaServer/Source/Networking/ServerSettings.cs b/Barotrauma/BarotraumaServer/Source/Networking/ServerSettings.cs index 178889ce4..fa0472297 100644 --- a/Barotrauma/BarotraumaServer/Source/Networking/ServerSettings.cs +++ b/Barotrauma/BarotraumaServer/Source/Networking/ServerSettings.cs @@ -12,7 +12,6 @@ namespace Barotrauma.Networking { partial class ServerSettings { - public const string SettingsFile = "serversettings.xml"; public static readonly string ClientPermissionsFile = "Data" + Path.DirectorySeparatorChar + "clientpermissions.xml"; partial void InitProjSpecific() diff --git a/Barotrauma/BarotraumaShared/SharedContent.projitems b/Barotrauma/BarotraumaShared/SharedContent.projitems index 8987fe9ea..b54752cf9 100644 --- a/Barotrauma/BarotraumaShared/SharedContent.projitems +++ b/Barotrauma/BarotraumaShared/SharedContent.projitems @@ -1295,15 +1295,6 @@ PreserveNewest - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - PreserveNewest @@ -2174,6 +2165,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/HumanAIController.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/HumanAIController.cs index 8cfa6da08..479abd337 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/HumanAIController.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/HumanAIController.cs @@ -218,6 +218,35 @@ namespace Barotrauma if (run || speedMultiplier <= 0.0f) targetMovement *= speedMultiplier; Character.ResetSpeedMultiplier(); // Reset, items will set the value before the next update Character.AnimController.TargetMovement = targetMovement; + + if (!Character.LockHands) + { + DropUnnecessaryItems(); + } + + if (Character.IsKeyDown(InputType.Aim)) + { + var cursorDiffX = Character.CursorPosition.X - Character.Position.X; + if (cursorDiffX > 10.0f) + { + Character.AnimController.TargetDir = Direction.Right; + } + else if (cursorDiffX < -10.0f) + { + Character.AnimController.TargetDir = Direction.Left; + } + + if (Character.SelectedConstruction != null) Character.SelectedConstruction.SecondaryUse(deltaTime, Character); + + } + else if (Math.Abs(Character.AnimController.TargetMovement.X) > 0.1f && !Character.AnimController.InWater) + { + Character.AnimController.TargetDir = Character.AnimController.TargetMovement.X > 0.0f ? Direction.Right : Direction.Left; + } + } + + private void DropUnnecessaryItems() + { if (!NeedsDivingGear(Character.CurrentHull)) { bool oxygenLow = Character.OxygenAvailable < CharacterHealth.LowOxygenThreshold; @@ -278,26 +307,6 @@ namespace Barotrauma } } } - - if (Character.IsKeyDown(InputType.Aim)) - { - var cursorDiffX = Character.CursorPosition.X - Character.Position.X; - if (cursorDiffX > 10.0f) - { - Character.AnimController.TargetDir = Direction.Right; - } - else if (cursorDiffX < -10.0f) - { - Character.AnimController.TargetDir = Direction.Left; - } - - if (Character.SelectedConstruction != null) Character.SelectedConstruction.SecondaryUse(deltaTime, Character); - - } - else if (Math.Abs(Character.AnimController.TargetMovement.X) > 0.1f && !Character.AnimController.InWater) - { - Character.AnimController.TargetDir = Character.AnimController.TargetMovement.X > 0.0f ? Direction.Right : Direction.Left; - } } protected void ReportProblems() @@ -428,16 +437,24 @@ namespace Barotrauma } else { - float currentVitality = Character.CharacterHealth.Vitality; - float dmgPercentage = damage / currentVitality * 100; - if (dmgPercentage < currentVitality / 10) + // If not on the same team, always stay defensive + if (attacker.TeamID != Character.TeamID) { - // Don't retaliate on minor (accidental) dmg done by friendly characters - AddCombatObjective(AIObjectiveCombat.CombatMode.Retreat, Rand.Range(0.5f, 1f, Rand.RandSync.Unsynced)); + AddCombatObjective(AIObjectiveCombat.CombatMode.Defensive, Rand.Range(0.5f, 1f, Rand.RandSync.Unsynced)); } else { - AddCombatObjective(AIObjectiveCombat.CombatMode.Defensive, Rand.Range(0.5f, 1f, Rand.RandSync.Unsynced)); + float currentVitality = Character.CharacterHealth.Vitality; + float dmgPercentage = damage / currentVitality * 100; + if (dmgPercentage < currentVitality / 10) + { + // Don't retaliate on minor (accidental) dmg done by characters that are in the same team + AddCombatObjective(AIObjectiveCombat.CombatMode.Retreat, Rand.Range(0.5f, 1f, Rand.RandSync.Unsynced)); + } + else + { + AddCombatObjective(AIObjectiveCombat.CombatMode.Defensive, Rand.Range(0.5f, 1f, Rand.RandSync.Unsynced)); + } } } } @@ -448,24 +465,25 @@ namespace Barotrauma void AddCombatObjective(AIObjectiveCombat.CombatMode mode, float delay = 0) { + bool holdPosition = Character.Info?.Job?.Prefab.Identifier == "watchman"; if (ObjectiveManager.CurrentObjective is AIObjectiveCombat combatObjective) { if (combatObjective.Enemy != attacker || (combatObjective.Enemy == null && attacker == null)) { // Replace the old objective with the new. ObjectiveManager.Objectives.Remove(combatObjective); - objectiveManager.AddObjective(new AIObjectiveCombat(Character, attacker, mode, objectiveManager)); + objectiveManager.AddObjective(new AIObjectiveCombat(Character, attacker, mode, objectiveManager) { HoldPosition = holdPosition}); } } else { if (delay > 0) { - objectiveManager.AddObjective(new AIObjectiveCombat(Character, attacker, mode, objectiveManager), delay); + objectiveManager.AddObjective(new AIObjectiveCombat(Character, attacker, mode, objectiveManager) { HoldPosition = holdPosition }, delay); } else { - objectiveManager.AddObjective(new AIObjectiveCombat(Character, attacker, mode, objectiveManager)); + objectiveManager.AddObjective(new AIObjectiveCombat(Character, attacker, mode, objectiveManager) { HoldPosition = holdPosition }); } } } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveCombat.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveCombat.cs index 2daca7e13..85ebb08a8 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveCombat.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveCombat.cs @@ -17,7 +17,8 @@ namespace Barotrauma const float coolDown = 10.0f; public Character Enemy { get; private set; } - + public bool HoldPosition { get; set; } + private Item _weapon; private Item Weapon { @@ -125,7 +126,10 @@ namespace Barotrauma TryArm(); if (seekAmmunition == null || !subObjectives.Contains(seekAmmunition)) { - Move(); + if (!HoldPosition) + { + Move(); + } if (WeaponComponent != null) { OperateWeapon(deltaTime); @@ -151,6 +155,8 @@ namespace Barotrauma private bool TryArm() { + if (character.LockHands) { return false; } + if (Weapon != null) { if (!character.Inventory.Items.Contains(Weapon) || WeaponComponent == null) @@ -160,7 +166,7 @@ namespace Barotrauma else if (!WeaponComponent.HasRequiredContainedItems(false)) { // Seek ammunition only if cannot find a new weapon - if (!Reload(true, () => GetWeapon(out _) == null)) + if (!Reload(!HoldPosition, () => GetWeapon(out _) == null)) { if (seekAmmunition != null && subObjectives.Contains(seekAmmunition)) { @@ -266,7 +272,7 @@ namespace Barotrauma private void Unequip() { - if (character.SelectedItems.Contains(Weapon)) + if (!character.LockHands && character.SelectedItems.Contains(Weapon)) { if (!Weapon.AllowedSlots.Contains(InvSlotType.Any) || !character.Inventory.TryPutItem(Weapon, character, new List() { InvSlotType.Any })) { @@ -277,6 +283,7 @@ namespace Barotrauma private bool Equip() { + if (character.LockHands) { return false; } if (!WeaponComponent.HasRequiredContainedItems(false)) { Mode = CombatMode.Retreat; @@ -322,6 +329,13 @@ namespace Barotrauma private void Engage() { + if (character.LockHands) + { + Mode = CombatMode.Retreat; + SteeringManager.Reset(); + return; + } + retreatTarget = null; RemoveSubObjective(ref retreatObjective); RemoveSubObjective(ref seekAmmunition); diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs index 80bfc0544..af186326e 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs @@ -186,13 +186,14 @@ namespace Barotrauma } } - public Hull FindBestHull(IEnumerable ignoredHulls = null) + public Hull FindBestHull(IEnumerable ignoredHulls = null, bool allowChangingTheSubmarine = true) { Hull bestHull = null; float bestValue = 0; foreach (Hull hull in Hull.hullList) { if (hull.Submarine == null) { continue; } + if (!allowChangingTheSubmarine && hull.Submarine != character.Submarine) { continue; } if (ignoredHulls != null && ignoredHulls.Contains(hull)) { continue; } if (unreachable.Contains(hull)) { continue; } float hullSafety = 0; diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveGetItem.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveGetItem.cs index 6f4e556b8..7a752c48d 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveGetItem.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveGetItem.cs @@ -96,6 +96,12 @@ namespace Barotrauma protected override void Act(float deltaTime) { + if (character.LockHands) + { + abandon = true; + return; + } + FindTargetItem(); if (targetItem == null || moveToTarget == null) { diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRepairItem.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRepairItem.cs index 8ddb26d26..38bfff732 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRepairItem.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRepairItem.cs @@ -37,14 +37,21 @@ namespace Barotrauma float isSelected = character.SelectedConstruction == Item ? 50 : 0; float devotion = (Math.Min(Priority, 10) + isSelected) / 100; float max = MathHelper.Min(AIObjectiveManager.OrderPriority - 1, 90); + + bool isCompleted = Item.IsFullCondition; + if (isCompleted && character.SelectedConstruction == Item) + { + character?.Speak(TextManager.GetWithVariable("DialogItemRepaired", "[itemname]", Item.Name, true), null, 0.0f, "itemrepaired", 10.0f); + } + return MathHelper.Lerp(0, max, MathHelper.Clamp(devotion + damagePriority * distanceFactor * successFactor * PriorityModifier, 0, 1)); } public override bool IsCompleted() { bool isCompleted = Item.IsFullCondition; - if (isCompleted) - { + if (isCompleted && character.SelectedConstruction == Item) + { character?.Speak(TextManager.GetWithVariable("DialogItemRepaired", "[itemname]", Item.Name, true), null, 0.0f, "itemrepaired", 10.0f); } return isCompleted; diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Order.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Order.cs index c481eaa39..5c7d6641c 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Order.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Order.cs @@ -97,7 +97,7 @@ namespace Barotrauma } else { - string[] splitOptionNames = translatedOptionNames.Split(','); + string[] splitOptionNames = translatedOptionNames.Split(',', ','); OptionNames = new string[Options.Length]; for (int i = 0; i < Options.Length && i < splitOptionNames.Length; i++) { diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Animation/Params/Ragdoll/RagdollParams.cs b/Barotrauma/BarotraumaShared/Source/Characters/Animation/Params/Ragdoll/RagdollParams.cs index cec8bfc44..4477deb76 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Animation/Params/Ragdoll/RagdollParams.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Animation/Params/Ragdoll/RagdollParams.cs @@ -30,11 +30,13 @@ namespace Barotrauma [Serialize(0f, true), Editable(-360, 360, ToolTip = "Rotation offset (in degrees) used for animations and widgets. If the sprites in the sheet are in different orientations, use the orientation of the torso for the final version of your character (while editing the character in the editor, you can change the orientation freely).")] public float SpritesheetOrientation { get; set; } + private float limbScale; [Serialize(1.0f, true), Editable(MIN_SCALE, MAX_SCALE, DecimalCount = 3)] - public float LimbScale { get; set; } + public float LimbScale { get { return limbScale; } set { limbScale = MathHelper.Clamp(value, MIN_SCALE, MAX_SCALE); } } + private float jointScale; [Serialize(1.0f, true), Editable(MIN_SCALE, MAX_SCALE, DecimalCount = 3)] - public float JointScale { get; set; } + public float JointScale { get { return jointScale; } set { jointScale = MathHelper.Clamp(value, MIN_SCALE, MAX_SCALE); } } // Don't show in the editor, because shouldn't be edited in runtime. Requires that the limb scale and the collider sizes are adjusted. TODO: automatize. [Serialize(1f, false)] @@ -368,16 +370,18 @@ namespace Barotrauma #endregion #if CLIENT - public override void AddToEditor(ParamsEditor editor) + public void AddToEditor(ParamsEditor editor, bool alsoChildren = true) { base.AddToEditor(editor); - var subParams = GetAllSubParams(); - foreach (var subParam in subParams) + if (alsoChildren) { - subParam.AddToEditor(editor); - //TODO: divider sprite - new GUIFrame(new RectTransform(new Point(editor.EditorBox.Rect.Width, 10), editor.EditorBox.Content.RectTransform), - style: "ConnectionPanelWire"); + var subParams = GetAllSubParams(); + foreach (var subParam in subParams) + { + subParam.AddToEditor(editor); + new GUIFrame(new RectTransform(new Point(editor.EditorBox.Rect.Width, 10), editor.EditorBox.Content.RectTransform), + style: null, color: Color.Black); + } } } #endif diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs b/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs index 52d84e829..43d64ee53 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs @@ -1427,7 +1427,7 @@ namespace Barotrauma Vector2 rayEnd = rayStart - new Vector2(0.0f, height); - var lowestLimb = FindLowestLimb(); + //var lowestLimb = FindLowestLimb(); float closestFraction = 1; GameMain.World.RayCast((fixture, point, normal, fraction) => @@ -1440,7 +1440,7 @@ namespace Barotrauma break; case Physics.CollisionPlatform: Structure platform = fixture.Body.UserData as Structure; - if (IgnorePlatforms || lowestLimb.Position.Y < platform.Rect.Y) return -1; + if (IgnorePlatforms && TargetMovement.Y < -0.5f || Collider.Position.Y < platform.Rect.Y) return -1; break; case Physics.CollisionWall: case Physics.CollisionLevel: diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs index c3a326556..dba7e62e3 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs @@ -130,16 +130,20 @@ namespace Barotrauma { get { - if (ViewTarget == null) return AnimController.AimSourcePos; + if (ViewTarget == null) { return AnimController.AimSourcePos; } + + Vector2 viewTargetWorldPos = ViewTarget.WorldPosition; if (ViewTarget is Item targetItem) { Turret turret = targetItem.GetComponent(); if (turret != null) { - return new Vector2(targetItem.Rect.X + turret.TransformedBarrelPos.X, targetItem.Rect.Y - turret.TransformedBarrelPos.Y); + viewTargetWorldPos = new Vector2( + targetItem.WorldRect.X + turret.TransformedBarrelPos.X, + targetItem.WorldRect.Y - turret.TransformedBarrelPos.Y); } } - return ViewTarget.Position; + return Position + (viewTargetWorldPos - WorldPosition); } } @@ -1517,7 +1521,7 @@ namespace Barotrauma public bool CanAccessInventory(Inventory inventory) { - if (!CanInteract) return false; + if (!CanInteract || inventory.Locked) { return false; } //the inventory belongs to some other character if (inventory.Owner is Character && inventory.Owner != this) @@ -1558,7 +1562,11 @@ namespace Barotrauma { distanceToItem = -1.0f; - if (!CanInteract || item.HiddenInGame) return false; + bool hidden = item.HiddenInGame; +#if CLIENT + if (Screen.Selected == GameMain.SubEditorScreen) { hidden = false; } +#endif + if (!CanInteract || hidden) return false; if (item.ParentInventory != null) { @@ -1802,7 +1810,7 @@ namespace Barotrauma { SelectCharacter(focusedCharacter); } - else if (focusedCharacter != null && IsKeyHit(InputType.Health) && focusedCharacter.CharacterHealth.UseHealthWindow) + else if (focusedCharacter != null && IsKeyHit(InputType.Health) && focusedCharacter.CharacterHealth.UseHealthWindow && CanInteractWith(focusedCharacter, 160f, false)) { if (focusedCharacter == SelectedCharacter) { diff --git a/Barotrauma/BarotraumaShared/Source/DebugConsole.cs b/Barotrauma/BarotraumaShared/Source/DebugConsole.cs index bc79f5f24..85f68ba26 100644 --- a/Barotrauma/BarotraumaShared/Source/DebugConsole.cs +++ b/Barotrauma/BarotraumaShared/Source/DebugConsole.cs @@ -105,7 +105,7 @@ namespace Barotrauma public static bool CheatsEnabled; private static List unsavedMessages = new List(); - private static int messagesPerFile = 800; + private static int messagesPerFile = 5000; public const string SavePath = "ConsoleLogs"; private static void AssignOnExecute(string names, Action onExecute) @@ -648,6 +648,20 @@ namespace Barotrauma },null)); #if DEBUG + commands.Add(new Command("teleportsub", "teleportsub [start/end]: Teleport the submarine to the start or end of the level. WARNING: does not take outposts into account, so often leads to physics glitches. Only use for debugging.", (string[] args) => + { + if (Submarine.MainSub == null || Level.Loaded == null) return; + + if (args.Length > 0 && args[0].ToLowerInvariant() == "start") + { + Submarine.MainSub.SetPosition(Level.Loaded.StartPosition); + } + else + { + Submarine.MainSub.SetPosition(Level.Loaded.EndPosition); + } + }, isCheat: true)); + commands.Add(new Command("waterphysicsparams", "waterphysicsparams [stiffness] [spread] [damping]: defaults 0.02, 0.05, 0.05", (string[] args) => { Vector2 explosionPos = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition); @@ -1582,7 +1596,14 @@ namespace Barotrauma } } - string fileName = "DebugConsoleLog_" + DateTime.Now.ToShortDateString() + "_" + DateTime.Now.ToShortTimeString(); + string fileName = "DebugConsoleLog_"; +#if SERVER + fileName += "Server_"; +#else + fileName += "Client_"; +#endif + + fileName += DateTime.Now.ToShortDateString() + "_" + DateTime.Now.ToShortTimeString(); var invalidChars = Path.GetInvalidFileNameChars(); foreach (char invalidChar in invalidChars) { diff --git a/Barotrauma/BarotraumaShared/Source/GameSession/CargoManager.cs b/Barotrauma/BarotraumaShared/Source/GameSession/CargoManager.cs index 03c92e52e..8cd5fe59e 100644 --- a/Barotrauma/BarotraumaShared/Source/GameSession/CargoManager.cs +++ b/Barotrauma/BarotraumaShared/Source/GameSession/CargoManager.cs @@ -79,6 +79,7 @@ namespace Barotrauma public int GetTotalItemCost() { + if (purchasedItems == null) return 0; return purchasedItems.Sum(i => i.ItemPrefab.GetPrice(campaign.Map.CurrentLocation).BuyPrice * i.Quantity); } diff --git a/Barotrauma/BarotraumaShared/Source/GameSettings.cs b/Barotrauma/BarotraumaShared/Source/GameSettings.cs index cbb2a250c..8783df978 100644 --- a/Barotrauma/BarotraumaShared/Source/GameSettings.cs +++ b/Barotrauma/BarotraumaShared/Source/GameSettings.cs @@ -267,7 +267,13 @@ namespace Barotrauma private static bool sendUserStatistics = true; public static bool SendUserStatistics { - get { return sendUserStatistics; } + get + { +#if DEBUG + return false; +#endif + return sendUserStatistics; + } set { sendUserStatistics = value; diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/MeleeWeapon.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/MeleeWeapon.cs index c9210a753..1b7e9af30 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/MeleeWeapon.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/MeleeWeapon.cs @@ -178,29 +178,35 @@ namespace Barotrauma.Items.Components private void SetUser(Character character) { - if (user == character) return; - if (user != null && user.Removed) user = null; + if (user == character) { return; } + if (user != null && user.Removed) { user = null; } + + user = character; + + if (item.body?.FarseerBody == null || item.Removed || + !GameMain.World.BodyList.Contains(item.body.FarseerBody)) + { + return; + } if (user != null) { foreach (Limb limb in user.AnimController.Limbs) { - if (limb.body.FarseerBody != null) + if (limb.body.FarseerBody != null && GameMain.World.BodyList.Contains(limb.body.FarseerBody)) { - if (GameMain.World.BodyList.Contains(limb.body.FarseerBody)) - { - item.body.FarseerBody.RestoreCollisionWith(limb.body.FarseerBody); - } + item.body.FarseerBody.RestoreCollisionWith(limb.body.FarseerBody); } } } foreach (Limb limb in character.AnimController.Limbs) { - item.body.FarseerBody.IgnoreCollisionWith(limb.body.FarseerBody); + if (limb.body.FarseerBody != null && GameMain.World.BodyList.Contains(limb.body.FarseerBody)) + { + item.body.FarseerBody.IgnoreCollisionWith(limb.body.FarseerBody); + } } - - user = character; } private void RestoreCollision() diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Propulsion.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Propulsion.cs index 4ebd92380..088e335cd 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Propulsion.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Propulsion.cs @@ -21,7 +21,7 @@ namespace Barotrauma.Items.Components private UsableIn usableIn; - [Serialize(0.0f, false), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f)] + [Serialize(0.0f, false), Editable(MinValueFloat = -1000.0f, MaxValueFloat = 1000.0f)] public float Force { get { return force; } diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Deconstructor.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Deconstructor.cs index fb4392e3c..a59edf726 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Deconstructor.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Deconstructor.cs @@ -11,6 +11,8 @@ namespace Barotrauma.Items.Components private float progressTimer; private float progressState; + private bool hasPower; + private ItemContainer inputContainer, outputContainer; public ItemContainer InputContainer @@ -58,7 +60,8 @@ namespace Barotrauma.Items.Components return; } - if (voltage < minVoltage) { return; } + hasPower = voltage >= minVoltage; + if (!hasPower) { return; } var repairable = item.GetComponent(); if (repairable != null) @@ -68,7 +71,7 @@ namespace Barotrauma.Items.Components ApplyStatusEffects(ActionType.OnActive, deltaTime, null); - if (powerConsumption == 0.0f) voltage = 1.0f; + if (powerConsumption == 0.0f) { voltage = 1.0f; } progressTimer += deltaTime * voltage; Voltage -= deltaTime * 10.0f; @@ -107,28 +110,41 @@ namespace Barotrauma.Items.Components } } - if (targetItem.Prefab.DeconstructItems.Any()) + if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer) { - inputContainer.Inventory.RemoveItem(targetItem); - Entity.Spawner.AddToRemoveQueue(targetItem); - MoveInputQueue(); - PutItemsToLinkedContainer(); - } - else - { - if (outputContainer.Inventory.Items.All(i => i != null)) + if (targetItem.Prefab.DeconstructItems.Any()) { - targetItem.Drop(dropper: null); + //drop all items that are inside the deconstructed item + foreach (ItemContainer ic in targetItem.GetComponents()) + { + if (ic?.Inventory?.Items == null) { continue; } + foreach (Item containedItem in ic.Inventory.Items) + { + containedItem?.Drop(dropper: null, createNetworkEvent: true); + } + } + + inputContainer.Inventory.RemoveItem(targetItem); + Entity.Spawner.AddToRemoveQueue(targetItem); + MoveInputQueue(); + PutItemsToLinkedContainer(); } else { - outputContainer.Inventory.TryPutItem(targetItem, user: null, createNetworkEvent: true); + if (outputContainer.Inventory.Items.All(i => i != null)) + { + targetItem.Drop(dropper: null); + } + else + { + outputContainer.Inventory.TryPutItem(targetItem, user: null, createNetworkEvent: true); + } } - } - - if (inputContainer.Inventory.Items.Any(i => i != null)) - { +#if SERVER + item.CreateServerEvent(this); +#endif progressTimer = 0.0f; + progressState = 0.0f; } } @@ -188,21 +204,17 @@ namespace Barotrauma.Items.Components } #endif - if (!IsActive) { progressState = 0.0f; } - -#if CLIENT if (!IsActive) { progressTimer = 0.0f; - activateButton.Text = TextManager.Get("DeconstructorDeconstruct"); - } - else - { - activateButton.Text = TextManager.Get("DeconstructorCancel"); + progressState = 0.0f; } + +#if CLIENT + activateButton.Text = TextManager.Get(IsActive ? "DeconstructorCancel" : "DeconstructorDeconstruct"); #endif - inputContainer.Inventory.Locked = IsActive; + inputContainer.Inventory.Locked = IsActive; } } } diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Fabricator.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Fabricator.cs index cd1b0e081..59d850056 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Fabricator.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Fabricator.cs @@ -19,6 +19,8 @@ namespace Barotrauma.Items.Components private float timeUntilReady; private float requiredTime; + private bool hasPower; + private Character user; private ItemContainer inputContainer, outputContainer; @@ -198,8 +200,9 @@ namespace Barotrauma.Items.Components progressState = fabricatedItem == null ? 0.0f : (requiredTime - timeUntilReady) / requiredTime; - if (voltage < minVoltage) { return; } - + hasPower = voltage >= minVoltage; + if (!hasPower) { return; } + var repairable = item.GetComponent(); if (repairable != null) { @@ -215,43 +218,50 @@ namespace Barotrauma.Items.Components if (timeUntilReady > 0.0f) { return; } - var availableIngredients = GetAvailableIngredients(); - foreach (FabricationRecipe.RequiredItem ingredient in fabricatedItem.RequiredItems) + if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer) { - for (int i = 0; i < ingredient.Amount; i++) + var availableIngredients = GetAvailableIngredients(); + foreach (FabricationRecipe.RequiredItem ingredient in fabricatedItem.RequiredItems) { - var requiredItem = inputContainer.Inventory.Items.FirstOrDefault(it => it != null && it.Prefab == ingredient.ItemPrefab && it.Condition >= ingredient.ItemPrefab.Health * ingredient.MinCondition); - if (requiredItem == null) continue; - - //Item4 = use condition bool - if (ingredient.UseCondition && requiredItem.Condition - ingredient.ItemPrefab.Health * ingredient.MinCondition > 0.0f) //Leave it behind with reduced condition if it has enough to stay above 0 + for (int i = 0; i < ingredient.Amount; i++) { - requiredItem.Condition -= ingredient.ItemPrefab.Health * ingredient.MinCondition; - continue; + var availableItem = availableIngredients.FirstOrDefault(it => it != null && it.Prefab == ingredient.ItemPrefab && it.Condition >= ingredient.ItemPrefab.Health * ingredient.MinCondition); + if (availableItem == null) { continue; } + + //Item4 = use condition bool + if (ingredient.UseCondition && availableItem.Condition - ingredient.ItemPrefab.Health * ingredient.MinCondition > 0.0f) //Leave it behind with reduced condition if it has enough to stay above 0 + { + availableItem.Condition -= ingredient.ItemPrefab.Health * ingredient.MinCondition; + continue; + } + availableIngredients.Remove(availableItem); + Entity.Spawner.AddToRemoveQueue(availableItem); + inputContainer.Inventory.RemoveItem(availableItem); } - Entity.Spawner.AddToRemoveQueue(requiredItem); - inputContainer.Inventory.RemoveItem(requiredItem); } - } - if (outputContainer.Inventory.Items.All(i => i != null)) - { - Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, item.Position, item.Submarine, fabricatedItem.TargetItem.Health * fabricatedItem.OutCondition); - } - else - { - Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, outputContainer.Inventory, fabricatedItem.TargetItem.Health * fabricatedItem.OutCondition); - } - - if ((GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer) && user != null && !user.Removed) - { - foreach (Skill skill in fabricatedItem.RequiredSkills) + if (outputContainer.Inventory.Items.All(i => i != null)) { - user.Info.IncreaseSkillLevel(skill.Identifier, skill.Level / 100.0f * SkillIncreaseMultiplier, user.WorldPosition + Vector2.UnitY * 150.0f); + Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, item.Position, item.Submarine, fabricatedItem.TargetItem.Health * fabricatedItem.OutCondition); + } + else + { + Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, outputContainer.Inventory, fabricatedItem.TargetItem.Health * fabricatedItem.OutCondition); + } + + if (user != null && !user.Removed) + { + foreach (Skill skill in fabricatedItem.RequiredSkills) + { + user.Info.IncreaseSkillLevel(skill.Identifier, skill.Level / 100.0f * SkillIncreaseMultiplier, user.WorldPosition + Vector2.UnitY * 150.0f); + } } - } - CancelFabricating(null); + CancelFabricating(null); +#if SERVER + item.CreateServerEvent(this); +#endif + } } private bool CanBeFabricated(FabricationRecipe fabricableItem) @@ -328,6 +338,8 @@ namespace Barotrauma.Items.Components { var matchingItem = availableIngredients.Find(it => !usedItems.Contains(it) && IsItemValidIngredient(it, requiredItem)); if (matchingItem == null) { continue; } + + availableIngredients.Remove(matchingItem); if (matchingItem.ParentInventory == inputContainer.Inventory) { diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Reactor.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Reactor.cs index 8b2647587..e38964d4a 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Reactor.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Reactor.cs @@ -254,15 +254,19 @@ namespace Barotrauma.Items.Components { foreach (Connection connection in connections) { - if (!connection.IsPower) continue; + if (!connection.IsPower) { continue; } foreach (Connection recipient in connection.Recipients) { - if (!(recipient.Item is Item it)) continue; + if (!(recipient.Item is Item it)) { continue; } PowerTransfer pt = it.GetComponent(); - if (pt == null) continue; + if (pt == null) { continue; } - load = Math.Max(load, pt.PowerLoad); + //calculate how much external power there is in the grid + //(power coming from somewhere else than this reactor, e.g. batteries) + float externalPower = CurrPowerConsumption - pt.CurrPowerConsumption; + //reduce the external power from the load to prevent overloading the grid + load = Math.Max(load, pt.PowerLoad - externalPower); } } } diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Repairable.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Repairable.cs index f731e087f..07af6777c 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Repairable.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Repairable.cs @@ -176,7 +176,7 @@ namespace Barotrauma.Items.Components float successFactor = requiredSkills.Count == 0 ? 1.0f : 0.0f; - //item must have been below 50% condition for the player to get an achievement or XP for repairing it + //item must have been below the repair threshold for the player to get an achievement or XP for repairing it if (item.Condition < ShowRepairUIThreshold) { wasBroken = true; diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/Wire.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/Wire.cs index a969fa28d..e67851762 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/Wire.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/Wire.cs @@ -61,6 +61,7 @@ namespace Barotrauma.Items.Components { get { + if (GameMain.NetworkMember?.ServerSettings != null && !GameMain.NetworkMember.ServerSettings.AllowRewiring) { return false; } return locked || connections.Any(c => c != null && c.ConnectionPanel.Locked); } set { locked = value; } diff --git a/Barotrauma/BarotraumaShared/Source/Items/Item.cs b/Barotrauma/BarotraumaShared/Source/Items/Item.cs index 4a0479ef6..b990d98df 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Item.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Item.cs @@ -152,13 +152,12 @@ namespace Barotrauma get { return description ?? prefab.Description; } set { description = value; } } - - private bool hiddenInGame; + [Editable, Serialize(false, true)] public bool HiddenInGame { - get { return hiddenInGame; } - set { hiddenInGame = value; } + get; + set; } public float ImpactTolerance @@ -262,7 +261,26 @@ namespace Barotrauma /// public string ContainerIdentifier { - get { return Container?.prefab.Identifier ?? ""; } + get + { + return + Container?.prefab.Identifier ?? + ParentInventory?.Owner?.ToString() ?? + ""; + } + set { /*do nothing*/ } + } + + [Serialize(false, false)] + /// + /// Can be used by status effects or conditionals to check if the physics body of the item is active + /// + public bool PhysicsBodyActive + { + get + { + return body != null && body.Enabled; + } set { /*do nothing*/ } } @@ -315,7 +333,7 @@ namespace Barotrauma get { return spriteColor; } } - public bool IsFullCondition => Condition >= MaxCondition; + public bool IsFullCondition => MathUtils.NearlyEqual(Condition, MaxCondition); public float MaxCondition => Prefab.Health; public float ConditionPercentage => MathUtils.Percentage(Condition, MaxCondition); @@ -334,6 +352,8 @@ namespace Barotrauma if (Indestructible) return; float prev = condition; + bool wasInFullCondition = IsFullCondition; + condition = MathHelper.Clamp(value, 0.0f, Prefab.Health); if (condition == 0.0f && prev > 0.0f) { @@ -349,9 +369,17 @@ namespace Barotrauma SetActiveSprite(); - if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer && !MathUtils.NearlyEqual(lastSentCondition, condition)) + if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer) { - if (Math.Abs(lastSentCondition - condition) > 1.0f || condition == 0.0f || condition == Prefab.Health) + if (Math.Abs(lastSentCondition - condition) > 1.0f) + { + conditionUpdatePending = true; + } + else if (wasInFullCondition != IsFullCondition) + { + conditionUpdatePending = true; + } + else if (!MathUtils.NearlyEqual(lastSentCondition, condition) && (condition <= 0.0f || condition >= Prefab.Health)) { conditionUpdatePending = true; } @@ -2086,11 +2114,13 @@ namespace Barotrauma System.Diagnostics.Debug.Assert(Submarine != null || rootContainer.ParentInventory?.Owner is Character); Vector2 subPosition = Submarine == null ? Vector2.Zero : Submarine.HiddenSubPosition; - + + int width = ResizeHorizontal ? rect.Width : defaultRect.Width; + int height = ResizeVertical ? rect.Height : defaultRect.Height; element.Add(new XAttribute("rect", (int)(rect.X - subPosition.X) + "," + (int)(rect.Y - subPosition.Y) + "," + - defaultRect.Width + "," + defaultRect.Height)); + width + "," + height)); if (linkedTo != null && linkedTo.Count > 0) { diff --git a/Barotrauma/BarotraumaShared/Source/Items/RelatedItem.cs b/Barotrauma/BarotraumaShared/Source/Items/RelatedItem.cs index c6db98cbc..7c907cd87 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/RelatedItem.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/RelatedItem.cs @@ -17,7 +17,8 @@ namespace Barotrauma } public bool IsOptional { get; set; } - + public bool MatchOnEmpty { get; set; } + public bool IgnoreInEditor { get; set; } private string[] excludedIdentifiers; @@ -99,6 +100,11 @@ namespace Barotrauma var containedItems = parentItem.ContainedItems; if (containedItems == null) return false; + if (MatchOnEmpty && !containedItems.Any(ci => ci != null)) + { + return true; + } + foreach (Item contained in containedItems) { if (contained.Condition > 0.0f && MatchesItem(contained)) return true; @@ -222,6 +228,7 @@ namespace Barotrauma ri.IsOptional = element.GetAttributeBool("optional", false); ri.IgnoreInEditor = element.GetAttributeBool("ignoreineditor", false); + ri.MatchOnEmpty = element.GetAttributeBool("matchonempty", false); return ri; } } diff --git a/Barotrauma/BarotraumaShared/Source/Map/Gap.cs b/Barotrauma/BarotraumaShared/Source/Map/Gap.cs index fa136413a..d9a073f30 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Gap.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Gap.cs @@ -627,7 +627,8 @@ namespace Barotrauma if (IsHorizontal) { - if (Math.Max(hull1.Surface + hull1.WaveY[hull1.WaveY.Length - 1], hull2.Surface + hull2.WaveY[0]) > rect.Y) return; + //if the water level is above the gap, oxygen doesn't circulate + if (Math.Max(hull1.WorldSurface + hull1.WaveY[hull1.WaveY.Length - 1], hull2.WorldSurface + hull2.WaveY[0]) > WorldRect.Y) { return; } } float totalOxygen = hull1.Oxygen + hull2.Oxygen; diff --git a/Barotrauma/BarotraumaShared/Source/Map/Levels/Level.cs b/Barotrauma/BarotraumaShared/Source/Map/Levels/Level.cs index 6b469c300..3f6d04810 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Levels/Level.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Levels/Level.cs @@ -1548,13 +1548,13 @@ namespace Barotrauma Point? minSize = null; DockingPort subPort = null; + float closestDistance = float.MaxValue; if (Submarine.MainSub != null) { Point subSize = Submarine.MainSub.GetDockedBorders().Size; Point outpostSize = outpost.GetDockedBorders().Size; minSize = new Point(Math.Max(subSize.X, outpostSize.X), subSize.Y + outpostSize.Y); - float closestDistance = float.MaxValue; foreach (DockingPort port in DockingPort.List) { if (port.IsHorizontal || port.Docked) { continue; } @@ -1570,17 +1570,43 @@ namespace Barotrauma } } + DockingPort outpostPort = null; + closestDistance = float.MaxValue; + foreach (DockingPort port in DockingPort.List) + { + if (port.IsHorizontal || port.Docked) { continue; } + if (port.Item.Submarine != outpost) { continue; } + //the outpost port has to be at the bottom of the outpost + if (port.Item.WorldPosition.Y > outpost.WorldPosition.Y) { continue; } + float dist = Math.Abs(port.Item.WorldPosition.X - outpost.WorldPosition.X); + if (dist < closestDistance) + { + outpostPort = port; + closestDistance = dist; + } + } + float subDockingPortOffset = subPort == null ? 0.0f : subPort.Item.WorldPosition.X - Submarine.MainSub.WorldPosition.X; //don't try to compensate if the port is very far from the sub's center of mass - if (Math.Abs(subDockingPortOffset) > 2000.0f) + if (Math.Abs(subDockingPortOffset) > 5000.0f) { - subDockingPortOffset = MathHelper.Clamp(subDockingPortOffset, -2000.0f, 2000.0f); + subDockingPortOffset = MathHelper.Clamp(subDockingPortOffset, -5000.0f, 5000.0f); string warningMsg = "Docking port very far from the sub's center of mass (submarine: " + Submarine.MainSub.Name + ", dist: " + subDockingPortOffset + "). The level generator may not be able to place the outpost so that docking is possible."; DebugConsole.NewMessage(warningMsg, Color.Orange); GameAnalyticsManager.AddErrorEventOnce("Lever.CreateOutposts:DockingPortVeryFar" + Submarine.MainSub.Name, GameAnalyticsSDK.Net.EGAErrorSeverity.Warning, warningMsg); } - outpost.SetPosition(outpost.FindSpawnPos(i == 0 ? StartPosition : EndPosition, minSize, subDockingPortOffset)); + float outpostDockingPortOffset = subPort == null ? 0.0f : outpostPort.Item.WorldPosition.X - outpost.WorldPosition.X; + //don't try to compensate if the port is very far from the outpost's center of mass + if (Math.Abs(outpostDockingPortOffset) > 5000.0f) + { + outpostDockingPortOffset = MathHelper.Clamp(outpostDockingPortOffset, -5000.0f, 5000.0f); + string warningMsg = "Docking port very far from the outpost's center of mass (outpost: " + outpost.Name + ", dist: " + outpostDockingPortOffset + "). The level generator may not be able to place the outpost so that docking is possible."; + DebugConsole.NewMessage(warningMsg, Color.Orange); + GameAnalyticsManager.AddErrorEventOnce("Lever.CreateOutposts:OutpostDockingPortVeryFar" + outpost.Name, GameAnalyticsSDK.Net.EGAErrorSeverity.Warning, warningMsg); + } + + outpost.SetPosition(outpost.FindSpawnPos(i == 0 ? StartPosition : EndPosition, minSize, subDockingPortOffset - outpostDockingPortOffset)); if ((i == 0) == !Mirrored) { StartOutpost = outpost; diff --git a/Barotrauma/BarotraumaShared/Source/Map/StructurePrefab.cs b/Barotrauma/BarotraumaShared/Source/Map/StructurePrefab.cs index b43be342f..34c1170b5 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/StructurePrefab.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/StructurePrefab.cs @@ -308,18 +308,24 @@ namespace Barotrauma if (ResizeHorizontal) placeSize.X = position.X - placePosition.X; if (ResizeVertical) placeSize.Y = placePosition.Y - position.Y; - newRect = Submarine.AbsRect(placePosition, placeSize); - - if (PlayerInput.LeftButtonReleased()) + //don't allow resizing width/height to less than the grid size + if (ResizeHorizontal && Math.Abs(placeSize.X) < Submarine.GridSize.X) { - //don't allow resizing width/height to zero - if ((!ResizeHorizontal || placeSize.X != 0.0f) && (!ResizeVertical || placeSize.Y != 0.0f)) - { - newRect.Location -= MathUtils.ToPoint(Submarine.MainSub.Position); + placeSize.X = Submarine.GridSize.X; + } + if (ResizeVertical && Math.Abs(placeSize.Y) < Submarine.GridSize.Y) + { + placeSize.X = Submarine.GridSize.Y; + } - var structure = new Structure(newRect, this, Submarine.MainSub); - structure.Submarine = Submarine.MainSub; - } + newRect = Submarine.AbsRect(placePosition, placeSize); + if (PlayerInput.LeftButtonReleased()) + { + newRect.Location -= MathUtils.ToPoint(Submarine.MainSub.Position); + var structure = new Structure(newRect, this, Submarine.MainSub) + { + Submarine = Submarine.MainSub + }; selected = null; return; diff --git a/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs b/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs index b218c5757..e653d0603 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs @@ -297,6 +297,12 @@ namespace Barotrauma } } + public bool IsFileCorrupted + { + get; + private set; + } + //constructors & generation ---------------------------------------------------- public Submarine(string filePath, string hash = "", bool tryLoad = true) : base(null) @@ -322,12 +328,17 @@ namespace Barotrauma int maxLoadRetries = 4; for (int i = 0; i <= maxLoadRetries; i++) { - doc = OpenFile(filePath); + doc = OpenFile(filePath, out Exception e); + if (e != null && !(e is IOException)) { break; } if (doc != null || i == maxLoadRetries || !File.Exists(filePath)) { break; } DebugConsole.NewMessage("Opening submarine file \"" + filePath + "\" failed, retrying in 250 ms..."); Thread.Sleep(250); } - if (doc == null || doc.Root == null) { return; } + if (doc == null || doc.Root == null) + { + IsFileCorrupted = true; + return; + } if (doc != null && doc.Root != null) { @@ -1139,7 +1150,11 @@ namespace Barotrauma savedSubmarines[i].Dispose(); } } - savedSubmarines.Add(new Submarine(filePath)); + var sub = new Submarine(filePath); + if (!sub.IsFileCorrupted) + { + savedSubmarines.Add(sub); + } savedSubmarines = savedSubmarines.OrderBy(s => s.filePath ?? "").ToList(); } @@ -1193,16 +1208,26 @@ namespace Barotrauma foreach (string path in filePaths) { - savedSubmarines.Add(new Submarine(path)); + var sub = new Submarine(path); + if (!sub.IsFileCorrupted) + { + savedSubmarines.Add(sub); + } } } static readonly string TempFolder = Path.Combine("Submarine", "Temp"); public static XDocument OpenFile(string file) + { + return OpenFile(file, out _); + } + + public static XDocument OpenFile(string file, out Exception exception) { XDocument doc = null; string extension = ""; + exception = null; try { @@ -1229,6 +1254,7 @@ namespace Barotrauma } catch (Exception e) { + exception = e; DebugConsole.ThrowError("Loading submarine \"" + file + "\" failed!", e); return null; } @@ -1243,6 +1269,7 @@ namespace Barotrauma catch (Exception e) { + exception = e; DebugConsole.ThrowError("Loading submarine \"" + file + "\" failed! (" + e.Message + ")"); return null; } @@ -1257,6 +1284,7 @@ namespace Barotrauma catch (Exception e) { + exception = e; DebugConsole.ThrowError("Loading submarine \"" + file + "\" failed! (" + e.Message + ")"); return null; } diff --git a/Barotrauma/BarotraumaShared/Source/Networking/NetConfig.cs b/Barotrauma/BarotraumaShared/Source/Networking/NetConfig.cs index 5c7bfa0f0..6d47913a4 100644 --- a/Barotrauma/BarotraumaShared/Source/Networking/NetConfig.cs +++ b/Barotrauma/BarotraumaShared/Source/Networking/NetConfig.cs @@ -10,6 +10,8 @@ namespace Barotrauma.Networking public const int MaxPlayers = 16; + public const int ServerNameMaxLength = 60; + public static string MasterServerUrl = GameMain.Config.MasterServerUrl; //if a Character is further than this from the sub and the players, the server will disable it diff --git a/Barotrauma/BarotraumaShared/Source/Networking/NetEntityEvent/NetEntityEvent.cs b/Barotrauma/BarotraumaShared/Source/Networking/NetEntityEvent/NetEntityEvent.cs index e1be53f6c..d5cd0904e 100644 --- a/Barotrauma/BarotraumaShared/Source/Networking/NetEntityEvent/NetEntityEvent.cs +++ b/Barotrauma/BarotraumaShared/Source/Networking/NetEntityEvent/NetEntityEvent.cs @@ -21,16 +21,28 @@ namespace Barotrauma.Networking public readonly Entity Entity; public readonly UInt16 ID; + public UInt16 EntityID + { + get; + private set; + } + //arbitrary extra data that will be passed to the Write method of the serializable entity //(the index of an itemcomponent for example) protected object[] Data; public bool Sent; - protected NetEntityEvent(INetSerializable entity, UInt16 id) + protected NetEntityEvent(INetSerializable serializableEntity, UInt16 id) { this.ID = id; - this.Entity = entity as Entity; + this.Entity = serializableEntity as Entity; + RefreshEntityID(); + } + + public void RefreshEntityID() + { + this.EntityID = this.Entity is Entity entity ? entity.ID : Entity.NullEntityID; } public void SetData(object[] data) diff --git a/Barotrauma/BarotraumaShared/Source/Networking/NetEntityEvent/NetEntityEventManager.cs b/Barotrauma/BarotraumaShared/Source/Networking/NetEntityEvent/NetEntityEventManager.cs index 20cb9b39a..ed4b325f2 100644 --- a/Barotrauma/BarotraumaShared/Source/Networking/NetEntityEvent/NetEntityEventManager.cs +++ b/Barotrauma/BarotraumaShared/Source/Networking/NetEntityEvent/NetEntityEventManager.cs @@ -65,7 +65,7 @@ namespace Barotrauma.Networking break; } - tempBuffer.Write((UInt16)e.Entity.ID); + tempBuffer.Write(e.EntityID); tempBuffer.Write((byte)tempEventBuffer.LengthBytes); tempBuffer.Write(tempEventBuffer); tempBuffer.WritePadBits(); diff --git a/Barotrauma/BarotraumaShared/Source/Networking/NetworkMember.cs b/Barotrauma/BarotraumaShared/Source/Networking/NetworkMember.cs index 2730bacb3..923077535 100644 --- a/Barotrauma/BarotraumaShared/Source/Networking/NetworkMember.cs +++ b/Barotrauma/BarotraumaShared/Source/Networking/NetworkMember.cs @@ -241,6 +241,32 @@ namespace Barotrauma.Networking public virtual void Update(float deltaTime) { } public virtual void Disconnect() { } - } + /// + /// Check if the two version are compatible (= if they can play together in multiplayer). + /// Returns null if compatibility could not be determined (invalid/unknown version number). + /// + public static bool? IsCompatible(string myVersion, string remoteVersion) + { + if (string.IsNullOrEmpty(myVersion) || string.IsNullOrEmpty(remoteVersion)) { return null; } + + if (!Version.TryParse(myVersion, out Version myVersionNumber)) { return null; } + if (!Version.TryParse(remoteVersion, out Version remoteVersionNumber)) { return null; } + + return IsCompatible(myVersionNumber, remoteVersionNumber); + } + + /// + /// Check if the two version are compatible (= if they can play together in multiplayer). + /// + public static bool IsCompatible(Version myVersion, Version remoteVersion) + { + //major.minor.build.revision + //revision number is ignored, other values have to match + return + myVersion.Major == remoteVersion.Major && + myVersion.Minor == remoteVersion.Minor && + myVersion.Build == remoteVersion.Build; + } + } } diff --git a/Barotrauma/BarotraumaShared/Source/Networking/ServerSettings.cs b/Barotrauma/BarotraumaShared/Source/Networking/ServerSettings.cs index 1875c3bcf..abbed98a0 100644 --- a/Barotrauma/BarotraumaShared/Source/Networking/ServerSettings.cs +++ b/Barotrauma/BarotraumaShared/Source/Networking/ServerSettings.cs @@ -31,6 +31,8 @@ namespace Barotrauma.Networking partial class ServerSettings : ISerializableEntity { + public const string SettingsFile = "serversettings.xml"; + [Flags] public enum NetFlags : byte { @@ -539,7 +541,14 @@ namespace Barotrauma.Networking get; set; } - + + [Serialize(true, true)] + public bool AllowRewiring + { + get; + set; + } + private YesNoMaybe traitorsEnabled; public YesNoMaybe TraitorsEnabled { diff --git a/Barotrauma/BarotraumaShared/Source/Networking/SteamManager.cs b/Barotrauma/BarotraumaShared/Source/Networking/SteamManager.cs index f72c8354b..937048f9c 100644 --- a/Barotrauma/BarotraumaShared/Source/Networking/SteamManager.cs +++ b/Barotrauma/BarotraumaShared/Source/Networking/SteamManager.cs @@ -21,7 +21,11 @@ namespace Barotrauma.Steam { "monster", 8 }, { "art", 8 }, { "mission", 8 }, - { "environment", 5 } + { "event set", 8 }, + { "total conversion", 5 }, + { "environment", 5 }, + { "item assembly", 5 }, + { "language", 5 } }; private List popularTags = new List(); diff --git a/Barotrauma/BarotraumaShared/Source/Physics/PhysicsBody.cs b/Barotrauma/BarotraumaShared/Source/Physics/PhysicsBody.cs index 17d762f18..5484e3ffe 100644 --- a/Barotrauma/BarotraumaShared/Source/Physics/PhysicsBody.cs +++ b/Barotrauma/BarotraumaShared/Source/Physics/PhysicsBody.cs @@ -375,7 +375,7 @@ namespace Barotrauma //Enum.TryParse(element.GetAttributeString("bodytype", "Dynamic"), out BodyType bodyType); body.BodyType = BodyType.Dynamic; body.CollisionCategories = Physics.CollisionItem; - body.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel; + body.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionPlatform; body.Friction = element.GetAttributeFloat("friction", 0.3f); body.Restitution = element.GetAttributeFloat("restitution", 0.05f); body.UserData = this; diff --git a/Barotrauma/BarotraumaShared/Source/Serialization/SerializableProperty.cs b/Barotrauma/BarotraumaShared/Source/Serialization/SerializableProperty.cs index ed281d0bf..9771f33fb 100644 --- a/Barotrauma/BarotraumaShared/Source/Serialization/SerializableProperty.cs +++ b/Barotrauma/BarotraumaShared/Source/Serialization/SerializableProperty.cs @@ -414,9 +414,6 @@ namespace Barotrauma { switch (Name) { - case "Condition": - if (parentObject is Item item) { return item.Condition; } - break; case "Voltage": if (parentObject is Powered powered) { return powered.Voltage; } break; @@ -460,6 +457,21 @@ namespace Barotrauma case "IsOn": { if (parentObject is LightComponent lightComponent) { return lightComponent.IsOn; } } break; + case "Condition": + { + if (parentObject is Item item) { return item.Condition; } + } + break; + case "ContainerIdentifier": + { + if (parentObject is Item item) { return item.ContainerIdentifier; } + } + break; + case "PhysicsBodyActive": + { + if (parentObject is Item item) { return item.PhysicsBodyActive; } + } + break; } return null; diff --git a/Barotrauma/BarotraumaShared/Source/Serialization/XMLExtensions.cs b/Barotrauma/BarotraumaShared/Source/Serialization/XMLExtensions.cs index 10b3691c6..d1409336d 100644 --- a/Barotrauma/BarotraumaShared/Source/Serialization/XMLExtensions.cs +++ b/Barotrauma/BarotraumaShared/Source/Serialization/XMLExtensions.cs @@ -114,7 +114,7 @@ namespace Barotrauma string stringValue = element.Attribute(name).Value; if (string.IsNullOrEmpty(stringValue)) return defaultValue; - string[] splitValue = stringValue.Split(','); + string[] splitValue = stringValue.Split(',', ','); if (convertToLowerInvariant) { diff --git a/Barotrauma/BarotraumaShared/Source/StatusEffects/PropertyConditional.cs b/Barotrauma/BarotraumaShared/Source/StatusEffects/PropertyConditional.cs index e8dde617e..5239d9717 100644 --- a/Barotrauma/BarotraumaShared/Source/StatusEffects/PropertyConditional.cs +++ b/Barotrauma/BarotraumaShared/Source/StatusEffects/PropertyConditional.cs @@ -277,7 +277,7 @@ namespace Barotrauma case OperatorType.Equals: if (type == typeof(bool)) { - return ((bool)propertyValue) == (AttributeValue == "true"); + return ((bool)propertyValue) == (AttributeValue == "true" || AttributeValue == "True"); } else if (FloatValue == null) { @@ -290,7 +290,7 @@ namespace Barotrauma case OperatorType.NotEquals: if (type == typeof(bool)) { - return ((bool)propertyValue) != (AttributeValue == "true"); + return ((bool)propertyValue) != (AttributeValue == "true" || AttributeValue == "True"); } else if (FloatValue == null) { diff --git a/Barotrauma/BarotraumaShared/Source/StatusEffects/StatusEffect.cs b/Barotrauma/BarotraumaShared/Source/StatusEffects/StatusEffect.cs index 5ea8b90da..550d7c11f 100644 --- a/Barotrauma/BarotraumaShared/Source/StatusEffects/StatusEffect.cs +++ b/Barotrauma/BarotraumaShared/Source/StatusEffects/StatusEffect.cs @@ -424,6 +424,7 @@ namespace Barotrauma public virtual bool HasRequiredConditions(List targets) { if (!propertyConditionals.Any()) return true; + if (requiredItems.All(ri => ri.MatchOnEmpty) && targets.Count == 0) return true; switch (conditionalComparison) { case PropertyConditional.Comparison.Or: diff --git a/Barotrauma/BarotraumaShared/Source/SteamAchievementManager.cs b/Barotrauma/BarotraumaShared/Source/SteamAchievementManager.cs index bdc2a394c..ac79701f8 100644 --- a/Barotrauma/BarotraumaShared/Source/SteamAchievementManager.cs +++ b/Barotrauma/BarotraumaShared/Source/SteamAchievementManager.cs @@ -145,7 +145,7 @@ namespace Barotrauma UnlockAchievement(c, "clowncostume"); } - if (Submarine.MainSub != null && c.Submarine == null) + if (Submarine.MainSub != null && c.Submarine == null && c.ConfigPath == Character.HumanConfigFile) { float dist = 500 / Physics.DisplayToRealWorldRatio; if (Vector2.DistanceSquared(c.WorldPosition, Submarine.MainSub.WorldPosition) > diff --git a/Barotrauma/BarotraumaShared/Source/TextManager.cs b/Barotrauma/BarotraumaShared/Source/TextManager.cs index 5091040a0..a14401921 100644 --- a/Barotrauma/BarotraumaShared/Source/TextManager.cs +++ b/Barotrauma/BarotraumaShared/Source/TextManager.cs @@ -322,6 +322,8 @@ namespace Barotrauma string[] messages = serverMessage.Split('/'); + bool translationsFound = false; + try { for (int i = 0; i < messages.Length; i++) @@ -333,6 +335,7 @@ namespace Barotrauma if (msg != null) // If a translation was found, otherwise use the original { messages[i] = msg; + translationsFound = true; } } else @@ -343,6 +346,7 @@ namespace Barotrauma if (msg != null) // If a translation was found, otherwise use the original { messages[i] = msg; + translationsFound = true; } else { @@ -358,12 +362,19 @@ namespace Barotrauma } } - string translatedServerMessage = string.Empty; - for (int i = 0; i < messages.Length; i++) + if (translationsFound) { - translatedServerMessage += messages[i]; + string translatedServerMessage = string.Empty; + for (int i = 0; i < messages.Length; i++) + { + translatedServerMessage += messages[i]; + } + return translatedServerMessage; + } + else + { + return serverMessage; } - return translatedServerMessage; } catch (IndexOutOfRangeException exception) diff --git a/Barotrauma/BarotraumaShared/Submarines/Berilia.sub b/Barotrauma/BarotraumaShared/Submarines/Berilia.sub index b8903bf84..7d10e0914 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Berilia.sub and b/Barotrauma/BarotraumaShared/Submarines/Berilia.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Remora.sub b/Barotrauma/BarotraumaShared/Submarines/Remora.sub index 275d436b3..892a2fdbf 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Remora.sub and b/Barotrauma/BarotraumaShared/Submarines/Remora.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Typhon.sub b/Barotrauma/BarotraumaShared/Submarines/Typhon.sub index b67089ebc..35fd0d59d 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Typhon.sub and b/Barotrauma/BarotraumaShared/Submarines/Typhon.sub differ diff --git a/Barotrauma/BarotraumaShared/changelog.txt b/Barotrauma/BarotraumaShared/changelog.txt index 0c91224ce..529f60b6e 100644 --- a/Barotrauma/BarotraumaShared/changelog.txt +++ b/Barotrauma/BarotraumaShared/changelog.txt @@ -1,3 +1,110 @@ +--------------------------------------------------------------------------------------------------------- +v0.9.1.0 +--------------------------------------------------------------------------------------------------------- + +Additions and changes: +- Disabled riot shields for now (too much griefing potential). +- Toolboxes can be fabricated and decontructed. +- Nerfed nitroglycerin's structure damage and increased the impact tolerance from 4 to 6. +- Made alien pistols a little less underwhelming (sounds, recoil, particle effects). +- Deconstructors drop all items that are inside the deconstructed item. Previously they would just get destroyed, +making it a potential exploit to destroy items that should not be possible to deconstruct. +- Modified welding fuel tank and oxygen tank sprites a bit to differentiate them from each other a bit more. +- Modified plasma cutter and welding tool sprites a bit to differentiate them from each other a bit more. +- Diving suits play the warning beep when there's no tank in the suit (not just when the tank is running out). +- Server list can be sorted according to ping, name, compatibility or any of the other property in the list. +- Docking ports of the enemy submarine are not shown on the sonar during combat missions. +- Recharge headset batteries between rounds in single player. +- Disable status monitor displays when out of power. +- Reduce server CPU usage. +- Update ladder and gap references if a waypoint is moved around in the editor. +- More descriptive error messages when publishing a Workshop item fails. +- Allow selecting multiple files at once when adding files to a Workshop item. +- Made the autogenerated submarine preview pictures larger. +- The "Host server" menu remembers the previously used port numbers and the player count. +- Restrict server names to a maximum of 60 characters. +- Items now collide with platforms. +- The game automatically opens the generated crash report when it crashes. +- Added "setmaxplayers" command to the dedicated server. +- Clients can change the server password using the command "setpassword" if they have a permission from the host. +- Reduced the range of the sonar ping sound and added a more subtle ping sound that can be heard further away. +- Watchmen now hold position when attacked. +- Added a subtle glow to alien buttons to make them a bit more noticeable. +- Items that are set to be hidden in-game can still be interacted with in the sub editor. +- EXPERIMENTAL: option to disable rewiring items in multiplayer. At the moment the setting can only be +changed by editing a parameter called "allowrewiring" in serversettings.xml. + +Misc bugfixes: +- Fixed the position of an outpost's docking port not being taken into account when determining how to +place it in the level. Caused large submarines sometimes to collide with walls when docked to outposts +where the port is offset from the center. +- Fixed corrupted sub files causing crashes. +- Fixed item editing HUD not appearing on any other item after one item has been selected in-game +(e.g. editing the channel on a wifi component prevented editing other items). +- Fixed ladders and other resizeable items reverting to their original size if they're resized and +the sub is saved and reloaded. +- Fixed inability to resize gaps in the sub editor after they've been placed. +- Reactors take other power sources into account when calculating how much power they need to generate. +Fixes overloads on Humpback when turning on the backup batteries and operating the reactor normally. +- Fixed watchmen not retaliating when a character does very small amounts of damage to them. +- Fixed bots being able to take handcuffs off from themselves. +- Fixed waypoint and spawnpoints being selectable in the sub editor even if they're hidden. +- Fixed deconstructing coilgun ammo boxes giving out more materials than the materials required to fabricate them. +- Scrollbars can only be dragged if the mouse button is pressed down while the cursor is on the scrollbar, +not by holding the button and moving it on the scrollbar. Fixes accidentally switching from slider +to another (often happens in the reactor interface). +- Fixed crashing when setting limb or joint scale to 0 using console commands. +- Fixed dropdowns in the Workshop item publish menu being draw behind the buttons below them. +- Fixes bots being unable to exit ladders if some part of their body is below the platform. +- Ignore keyboard inputs (delete, arrow keys, copy/paste) in the sub editor when a textbox is selected. +Prevents accidentally deleting items/structures when attempting to delete text from a textbox. +- Toolboxes can't be put inside other toolboxes or in the doctor's clothes. +- Fixed items bought from the store during a singleplayer campaign session being deducted from the credits +when save & quitting from the map. +- Flares stop emitting particles when inside an inventory. +- Fixed oxygen not flowing through horizontal gaps between subs (e.g. between Remora and the drone). +- Decreased the size of the mountain in the RidgeBasic level type to prevent it from blocking the main path. +- Fixed too small collider sizes on a bunch of crafting materials. +- Fixed order options ("hold fire"/"fire at will" etc) being displayed in one button instead of +being split into separate buttons when playing the game in Chinese. +- Fixed "Text Self_CauseOfDeathDescription.Unknown not found" console error if a character gets killed +by being inside a respawn shuttle when it despawns. +- Fixed sonar not scaling properly when resolution is changed mid-round. +- Fixed fabricators & deconstructors displaying the "insufficient power" warning when power is low +(but still high enough for the devices to run). + +Networking fixes: +- Fixed another ID mismatch problem that occasionally caused clients to get kicked out in multiplayer, +usually with an error message warning about a missing item. +- Fixed players always getting range banned when banned by a client. +- Fixed turrets not being aimed correctly in multiplayer if they're in another sub (e.g. the coilgun in +Remora's drone). +- More reliable fabricator and deconstructor syncing. Should fix items disappearing when multiple players +attempt to use the device at the same time and strange timing inconsistencies when deconstructing +multiple items in succession. +- Fixed all servers showing "unknown" as the game mode in the server list. +- Fixed server not allowing forward slashes in host messages. +- Fixed repair interface sometimes getting stuck close to 100% in multiplayer. + +Character editor fixes and improvements: +- Nicer layout, made the panels hideable. +- Added buttons for creating joints, duplicating limbs, and deleting limbs/joints. +- A dropdown for selecting which content package to add the character to. +- Allow creating a new content package during character creation. +- Added hotkeys 1, 2, 3 for limb, joint, and animation modes respectively. +- Change the logic of deciding on which parameters are shown and when. In the ragdoll mode you can now +see the ragdoll, but also scale it and see the main parameters. Source rects are now longer shown on the +sprite sheet if limbs mode is disabled. +- Automatically select edit limbs mode when a new character is created. +- Option to create multiple limbs in the character creation wizard. +- Disallow deleting the main limb because it often causes crashes. +- Only allow selecting PNG files as the texture. +- The spritesheet is shown by default. +- Fixed deleted joints not being saved properly. +- Only lock the axis for torso/head position when alt is down. +- Disable test pose if the character is not a humanoid. +- Clamp camera offset so that the character is always at least partially visible. + --------------------------------------------------------------------------------------------------------- v0.9.0.7 --------------------------------------------------------------------------------------------------------- @@ -52,7 +159,6 @@ the reactor behave as if it had two rods in it. to working like buttons which send out a pulse when interacted with). - Fixed an issue that occasionally caused the main menu to look distorted when launching the game on Mac. - Fixed crash after opening the file browser from the Workshop menu more than once on Linux. -- Fixed deconstructing coilgun ammo boxes giving out more materials than the materials required to fabricate them. - Fixed combat mission achievements being impossible to unlock. - Fixed item scales not being saved when the scale is modified in the sub editor. - Fixed incorrect physicorium shell description. @@ -61,7 +167,7 @@ to working like buttons which send out a pulse when interacted with). AI characters to let go of ladders when holding RMB. - Fixed explosion damage bypassing armor (both creature shells and wearable items). - Fixed OverrideSaveFolder and OverrideMultiplayerSaveFolder settings not being saved. -- Fixed order/report icons "twitching" when the sub moves. +- Fixed order/rept icons "twitching" when the sub moves. or - Fixed players being able to repair submerged electrical items indefinitely. - Fixed a "attempted to access a removed ragdoll" console error when a character wearing the health scanner HUD is removed. diff --git a/Barotrauma/BarotraumaShared/serversettings.xml b/Barotrauma/BarotraumaShared/serversettings.xml index 8c7fa91c4..0aa7f4229 100644 --- a/Barotrauma/BarotraumaShared/serversettings.xml +++ b/Barotrauma/BarotraumaShared/serversettings.xml @@ -10,7 +10,6 @@ startwhenclientsready="False" startwhenclientsreadyratio="0.8" allowspectating="True" - voipenabled="True" endroundatlevelend="True" saveserverlogs="True" allowragdollbutton="True" @@ -22,6 +21,7 @@ botcount="0" maxbotcount="16" allowdisguises="True" + allowrewiring="True" subselectionmode="Manual" modeselectionmode="Manual" endvoterequiredratio="0.6" @@ -48,5 +48,5 @@ TraitorsEnabled="No" BotSpawnMode="Normal" AllowedRandomMissionTypes="Random,Salvage,Monster,Cargo,Combat" - AllowedClientNameChars="32-33,38-46,48-57,65-90,91,93,95-122,192-255,384-591,1024-1279" + AllowedClientNameChars="32-33,38-46,48-57,65-90,91-91,93-93,95-122,192-255,384-591,1024-1279" ServerMessage="" /> \ No newline at end of file diff --git a/Libraries/Facepunch.Steamworks/Interfaces/Workshop.Editor.cs b/Libraries/Facepunch.Steamworks/Interfaces/Workshop.Editor.cs index 36bcb330d..771768dbd 100644 --- a/Libraries/Facepunch.Steamworks/Interfaces/Workshop.Editor.cs +++ b/Libraries/Facepunch.Steamworks/Interfaces/Workshop.Editor.cs @@ -24,6 +24,7 @@ namespace Facepunch.Steamworks public bool Publishing { get; internal set; } public ItemType? Type { get; set; } public string Error { get; internal set; } = null; + public SteamNative.Result? ErrorCode { get; internal set; } = null; public string ChangeNote { get; set; } = ""; public uint WorkshopUploadAppId { get; set; } public string MetaData { get; set; } = null; @@ -99,6 +100,7 @@ namespace Facepunch.Steamworks Publishing = true; Error = null; + ErrorCode = null; if ( Id == 0 ) { @@ -129,12 +131,14 @@ namespace Facepunch.Steamworks if ( obj.Result == SteamNative.Result.OK && !Failed ) { Error = null; + ErrorCode = null; Id = obj.PublishedFileId; PublishChanges(); return; } Error = $"Error creating new file: {obj.Result} ({obj.PublishedFileId})"; + ErrorCode = obj.Result; Publishing = false; OnChangesSubmitted?.Invoke( (Result) obj.Result ); @@ -221,6 +225,7 @@ namespace Facepunch.Steamworks NeedToAgreeToWorkshopLegal = obj.UserNeedsToAcceptWorkshopLegalAgreement; Publishing = false; + ErrorCode = obj.Result; Error = obj.Result != SteamNative.Result.OK ? $"Error publishing changes: {obj.Result} ({NeedToAgreeToWorkshopLegal})" : null; diff --git a/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.Enums.cs b/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.Enums.cs index f57eb4066..5729eca44 100644 --- a/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.Enums.cs +++ b/Libraries/Facepunch.Steamworks/SteamNative/SteamNative.Enums.cs @@ -20,7 +20,7 @@ namespace SteamNative // // EResult // - internal enum Result : int + public enum Result : int { OK = 1, Fail = 2, diff --git a/Libraries/OpenAL-Soft/COPYING b/Libraries/OpenAL-Soft/COPYING new file mode 100644 index 000000000..8d5d00006 --- /dev/null +++ b/Libraries/OpenAL-Soft/COPYING @@ -0,0 +1,437 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/Libraries/OpenAL-Soft/oal_soft.diff b/Libraries/OpenAL-Soft/oal_soft.diff new file mode 100644 index 000000000..06d11de5c --- /dev/null +++ b/Libraries/OpenAL-Soft/oal_soft.diff @@ -0,0 +1,13 @@ +diff --git a/Alc/alc.cpp b/Alc/alc.cpp +index fde655be..279f400a 100644 +--- a/Alc/alc.cpp ++++ b/Alc/alc.cpp +@@ -956,7 +956,7 @@ static void alc_initconfig(void) + } + TRACE("Supported backends: %s\n", names.c_str()); + } +- ReadALConfig(); ++ //ReadALConfig(); + + str = getenv("__ALSOFT_SUSPEND_CONTEXT"); + if(str && *str)