diff --git a/Barotrauma/BarotraumaClient/Source/Characters/Limb.cs b/Barotrauma/BarotraumaClient/Source/Characters/Limb.cs index 4c4f87b42..6f4b916f3 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/Limb.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/Limb.cs @@ -126,7 +126,20 @@ namespace Barotrauma } } } - + + public WearableSprite HuskSprite { get; private set; } + + public void LoadHuskSprite() + { + var info = character.Info; + if (info == null) { return; } + var element = info.FilterByTypeAndHeadID(character.Info.FilterElementsByGenderAndRace(character.Info.Wearables), WearableType.Husk).FirstOrDefault(); + if (element != null) + { + HuskSprite = new WearableSprite(element.Element("sprite"), WearableType.Husk); + } + } + public float TextureScale => limbParams.Ragdoll.TextureScale; public Sprite DamagedSprite { get; private set; } @@ -266,6 +279,10 @@ namespace Barotrauma float bleedingDamage = character.CharacterHealth.DoesBleed ? afflictions.FindAll(a => a is AfflictionBleeding).Sum(a => a.GetVitalityDecrease(character.CharacterHealth)) : 0; float damage = afflictions.FindAll(a => a.Prefab.AfflictionType == "damage").Sum(a => a.GetVitalityDecrease(character.CharacterHealth)); float damageMultiplier = 1; + foreach (DamageModifier damageModifier in appliedDamageModifiers) + { + damageMultiplier *= damageModifier.DamageMultiplier; + } if (playSound) { string damageSoundType = (bleedingDamage > damage) ? "LimbSlash" : "LimbBlunt"; @@ -275,7 +292,6 @@ namespace Barotrauma { damageSoundType = damageModifier.DamageSound; SoundPlayer.PlayDamageSound(damageSoundType, Math.Max(damage, bleedingDamage), WorldPosition); - damageMultiplier *= damageModifier.DamageMultiplier; break; } } @@ -408,6 +424,11 @@ namespace Barotrauma SpriteEffects spriteEffect = (dir == Direction.Right) ? SpriteEffects.None : SpriteEffects.FlipHorizontally; if (onlyDrawable == null) { + if (HuskSprite != null && (character.SpeciesName == "Humanhusk" || (character.SpeciesName == "Human" && + character.CharacterHealth.GetAffliction("huskinfection")?.State == AfflictionHusk.InfectionState.Active))) + { + DrawWearable(HuskSprite, depthStep, spriteBatch, color, spriteEffect); + } foreach (WearableSprite wearable in OtherWearables) { DrawWearable(wearable, depthStep, spriteBatch, color, spriteEffect); @@ -568,6 +589,9 @@ namespace Barotrauma OtherWearables?.ForEach(w => w.Sprite.Remove()); OtherWearables = null; + + HuskSprite?.Sprite.Remove(); + HuskSprite = null; } } } diff --git a/Barotrauma/BarotraumaClient/Source/DebugConsole.cs b/Barotrauma/BarotraumaClient/Source/DebugConsole.cs index 495a2ff25..57c7dc1dd 100644 --- a/Barotrauma/BarotraumaClient/Source/DebugConsole.cs +++ b/Barotrauma/BarotraumaClient/Source/DebugConsole.cs @@ -1599,6 +1599,189 @@ namespace Barotrauma TextManager.WriteToCSV(); NPCConversation.WriteToCSV(); })); +#endif + + commands.Add(new Command("cleanbuild", "", (string[] args) => + { + GameMain.Config.MusicVolume = 0.5f; + GameMain.Config.SoundVolume = 0.5f; + NewMessage("Music and sound volume set to 0.5", Color.Green); + + commands.Add(new Command("camerasettings", "camerasettings [defaultzoom] [zoomsmoothness] [movesmoothness] [minzoom] [maxzoom]: debug command for testing camera settings. The values default to 1.1, 8.0, 8.0, 0.1 and 2.0.", (string[] args) => + { + float defaultZoom = Screen.Selected.Cam.DefaultZoom; + if (args.Length > 0) float.TryParse(args[0], NumberStyles.Number, CultureInfo.InvariantCulture, out defaultZoom); + + float zoomSmoothness = Screen.Selected.Cam.ZoomSmoothness; + if (args.Length > 1) float.TryParse(args[1], NumberStyles.Number, CultureInfo.InvariantCulture, out zoomSmoothness); + float moveSmoothness = Screen.Selected.Cam.MoveSmoothness; + if (args.Length > 2) float.TryParse(args[2], NumberStyles.Number, CultureInfo.InvariantCulture, out moveSmoothness); + + float minZoom = Screen.Selected.Cam.MinZoom; + if (args.Length > 3) float.TryParse(args[3], NumberStyles.Number, CultureInfo.InvariantCulture, out minZoom); + float maxZoom = Screen.Selected.Cam.MaxZoom; + if (args.Length > 4) float.TryParse(args[4], NumberStyles.Number, CultureInfo.InvariantCulture, out maxZoom); + + Screen.Selected.Cam.DefaultZoom = defaultZoom; + Screen.Selected.Cam.ZoomSmoothness = zoomSmoothness; + Screen.Selected.Cam.MoveSmoothness = moveSmoothness; + Screen.Selected.Cam.MinZoom = minZoom; + Screen.Selected.Cam.MaxZoom = maxZoom; + })); + + commands.Add(new Command("waterparams", "waterparams [distortionscalex] [distortionscaley] [distortionstrengthx] [distortionstrengthy] [bluramount]: default 0.5 0.5 0.5 0.5 1", (string[] args) => + { + float distortScaleX = 0.5f, distortScaleY = 0.5f; + float distortStrengthX = 0.5f, distortStrengthY = 0.5f; + float blurAmount = 0.0f; + if (args.Length > 0) float.TryParse(args[0], NumberStyles.Number, CultureInfo.InvariantCulture, out distortScaleX); + if (args.Length > 1) float.TryParse(args[1], NumberStyles.Number, CultureInfo.InvariantCulture, out distortScaleY); + if (args.Length > 2) float.TryParse(args[2], NumberStyles.Number, CultureInfo.InvariantCulture, out distortStrengthX); + if (args.Length > 3) float.TryParse(args[3], NumberStyles.Number, CultureInfo.InvariantCulture, out distortStrengthY); + if (args.Length > 4) float.TryParse(args[4], NumberStyles.Number, CultureInfo.InvariantCulture, out blurAmount); + WaterRenderer.DistortionScale = new Vector2(distortScaleX, distortScaleY); + WaterRenderer.DistortionStrength = new Vector2(distortStrengthX, distortStrengthY); + WaterRenderer.BlurAmount = blurAmount; + })); + + + commands.Add(new Command("refreshrect", "Updates the dimensions of the selected items to match the ones defined in the prefab. Applied only in the subeditor.", (string[] args) => + { + //TODO: maybe do this automatically during loading when possible? + if (Screen.Selected == GameMain.SubEditorScreen) + { + if (!MapEntity.SelectedAny) + { + ThrowError("You have to select item(s) first!"); + } + else + { + foreach (var mapEntity in MapEntity.SelectedList) + { + if (mapEntity is Item item) + { + item.Rect = new Rectangle(item.Rect.X, item.Rect.Y, + (int)(item.Prefab.sprite.size.X * item.Prefab.Scale), + (int)(item.Prefab.sprite.size.Y * item.Prefab.Scale)); + } + else if (mapEntity is Structure structure) + { + if (!structure.ResizeHorizontal) + { + structure.Rect = new Rectangle(structure.Rect.X, structure.Rect.Y, + (int)structure.Prefab.ScaledSize.X, + structure.Rect.Height); + } + if (!structure.ResizeVertical) + { + structure.Rect = new Rectangle(structure.Rect.X, structure.Rect.Y, + structure.Rect.Width, + (int)structure.Prefab.ScaledSize.Y); + } + } + } + } + } + }, isCheat: false)); +#endif + + GameMain.Config.SaveNewPlayerConfig(); + + commands.Add(new Command("loadtexts", "loadtexts [sourcefile] [destinationfile]: Loads all lines of text from a given .txt file and inserts them sequientially into the elements of an xml file. If the file paths are omitted, EnglishVanilla.txt and EnglishVanilla.xml are used.", (string[] args) => + { + string sourcePath = args.Length > 0 ? args[0] : "Content/Texts/EnglishVanilla.txt"; + string destinationPath = args.Length > 1 ? args[1] : "Content/Texts/EnglishVanilla.xml"; + + string[] lines; + try + { + lines = File.ReadAllLines(sourcePath); + } + catch (Exception e) + { + ThrowError("Reading the file \"" + sourcePath + "\" failed.", e); + return; + } + var doc = XMLExtensions.TryLoadXml(destinationPath); + int i = 0; + foreach (XElement element in doc.Root.Elements()) + { + if (i >= lines.Length) + { + ThrowError("Error while loading texts to the xml file. The xml has more elements than the number of lines in the text file."); + return; + } + element.Value = lines[i]; + i++; + } + doc.Save(destinationPath); + }, + () => + { + var files = TextManager.GetTextFiles().Select(f => f.Replace("\\", "/")); + return new string[][] + { + files.Where(f => Path.GetExtension(f)==".txt").ToArray(), + files.Where(f => Path.GetExtension(f)==".xml").ToArray() + }; + })); + + commands.Add(new Command("updatetextfile", "updatetextfile [sourcefile] [destinationfile]: Inserts all the xml elements that are only present in the source file into the destination file. Can be used to update outdated translation files more easily.", (string[] args) => + { + if (args.Length < 2) return; + string sourcePath = args[0]; + string destinationPath = args[1]; + + var sourceDoc = XMLExtensions.TryLoadXml(sourcePath); + var destinationDoc = XMLExtensions.TryLoadXml(destinationPath); + + XElement destinationElement = destinationDoc.Root.Elements().First(); + foreach (XElement element in sourceDoc.Root.Elements()) + { + if (destinationDoc.Root.Element(element.Name) == null) + { + element.Value = "!!!!!!!!!!!!!" + element.Value; + destinationElement.AddAfterSelf(element); + } + XNode nextNode = destinationElement.NextNode; + while ((!(nextNode is XElement) || nextNode == element) && nextNode != null) nextNode = nextNode.NextNode; + destinationElement = nextNode as XElement; + } + destinationDoc.Save(destinationPath); + }, + () => + { + var files = TextManager.GetTextFiles().Where(f => Path.GetExtension(f) == ".xml").Select(f => f.Replace("\\", "/")).ToArray(); + return new string[][] + { + files, + files + }; + })); + + commands.Add(new Command("dumpentitytexts", "dumpentitytexts [filepath]: gets the names and descriptions of all entity prefabs and writes them into a file along with xml tags that can be used in translation files. If the filepath is omitted, the file is written to Content/Texts/EntityTexts.txt", (string[] args) => + { + string filePath = args.Length > 0 ? args[0] : "Content/Texts/EntityTexts.txt"; + List lines = new List(); + foreach (MapEntityPrefab me in MapEntityPrefab.List) + { + lines.Add("" + me.Name + ""); + lines.Add("" + me.Description + ""); + } + File.WriteAllLines(filePath, lines); + })); +#if DEBUG + commands.Add(new Command("checkduplicates", "Checks the given language for duplicate translation keys and writes to file.", (string[] args) => + { + if (args.Length != 1) return; + TextManager.CheckForDuplicates(args[0]); + })); + + commands.Add(new Command("writetocsv", "Writes the default language (English) to a .csv file.", (string[] args) => + { + TextManager.WriteToCSV(); + NPCConversation.WriteToCSV(); + })); commands.Add(new Command("loadtexts", "loadtexts [sourcefile] [destinationfile]: Loads all lines of text from a given .txt file and inserts them sequientially into the elements of an xml file. If the file paths are omitted, EnglishVanilla.txt and EnglishVanilla.xml are used.", (string[] args) => { @@ -2181,87 +2364,18 @@ namespace Barotrauma character.AnimController.ResetRagdoll(); }, isCheat: true)); - commands.Add(new Command("reloadwearables|reloadlimbs", "Reloads the xml(s) where limbs and wearable sprites (clothing) of the controlled character are defined. Also reloads textures. Provide id or name if you want to target another character.", args => + commands.Add(new Command("reloadwearables|reloadlimbs", "Reloads the sprites of all limbs and wearable sprites (clothing) of the controlled character. Provide id or name if you want to target another character.", args => { var character = (args.Length == 0) ? Character.Controlled : FindMatchingCharacter(args, true); if (character == null) { - ThrowError("Not controlling any character!"); + ThrowError("Not controlling any character or no matching character found with the provided arguments."); return; } - foreach (var limb in character.AnimController.Limbs) - { - limb.Sprite?.ReloadTexture(); - limb.DamagedSprite?.ReloadTexture(); - limb.DeformSprite?.Sprite.ReloadTexture(); - foreach (var wearable in limb.WearingItems) - { - wearable.Sprite.ReloadXML(); - wearable.Sprite.ReloadTexture(); - } - foreach (var wearable in limb.OtherWearables) - { - wearable.Sprite.ReloadXML(); - wearable.Sprite.ReloadTexture(); - } - } + ReloadWearables(character); }, isCheat: true)); - commands.Add(new Command("reloadxml", "Reloads the xml definition of the selected item(s)/structure(s) (SubEditor). Can also reload sprite xmls by entity id or by the name attribute (sprite element). Example 1: reloadxml id itemid. Example 2: reloadxml name \"Sprite name\"", args => - { - if (Screen.Selected is SpriteEditorScreen) - { - return; - } - else if (args.Length > 1) - { - TryDoActionOnSprite(args[0], args[1], s => s.ReloadXML()); - } - else if (Screen.Selected is SubEditorScreen subScreen) - { - if (!MapEntity.SelectedAny) - { - ThrowError("You have to select item(s)/structure(s) first!"); - } - else - { - MapEntity.SelectedList.ForEach(e => e.Sprite?.ReloadXML()); - } - } - else - { - ThrowError("Please provide the mode (name or id) and the value so that I can find the sprite for you!"); - } - }, isCheat: true)); - - commands.Add(new Command("reloadtexture|reloadtextures", "In sub editor, reloads the xml definition of the selected item(s)/structure(s). Can also reload sprite xmls by entity id or by the name attribute (sprite element). Example 1: reloadtexture id itemid. Example 2: reloadtexture name \"Sprite name\"", args => - { - if (Screen.Selected is SpriteEditorScreen) - { - return; - } - else if (args.Length > 1) - { - TryDoActionOnSprite(args[0], args[1], s => s.ReloadTexture()); - } - else if (Screen.Selected is SubEditorScreen subScreen) - { - if (!MapEntity.SelectedAny) - { - ThrowError("You have to select item(s)/structure(s) first!"); - } - else - { - MapEntity.SelectedList.ForEach(e => e.Sprite?.ReloadTexture()); - } - } - else - { - ThrowError("Please provide the mode (name or id) and the value so that I can find the sprite for you!"); - } - }, isCheat: true)); - - commands.Add(new Command("reloadsprite|reloadsprites", "Reload xml and texture of the given sprite(s). In sub editor, reloads the xml definition of the selected item(s)/structure(s). Can also reload sprite xmls by entity id or by the name attribute (sprite element). Example 1: reloadsprite id itemid. Example 2: reloadsprite name \"Sprite name\"", args => + commands.Add(new Command("reloadsprite|reloadsprites", "Reloads the sprites of the selected item(s)/structure(s) (hovering over or selecting in the subeditor) or the controlled character. Can also reload sprites by entity id or by the name attribute (sprite element). Example 1: reloadsprite id itemid. Example 2: reloadsprite name \"Sprite name\"", args => { if (Screen.Selected is SpriteEditorScreen) { @@ -2275,31 +2389,68 @@ namespace Barotrauma s.ReloadTexture(); }); } - else if (Screen.Selected is SubEditorScreen subScreen) + else if (Screen.Selected is SubEditorScreen) { if (!MapEntity.SelectedAny) { ThrowError("You have to select item(s)/structure(s) first!"); } - else + MapEntity.SelectedList.ForEach(e => { - MapEntity.SelectedList.ForEach(e => + if (e.Sprite != null) { - if (e.Sprite != null) - { - e.Sprite.ReloadXML(); - e.Sprite.ReloadTexture(); - } - }); - } + e.Sprite.ReloadXML(); + e.Sprite.ReloadTexture(); + } + }); } else { - ThrowError("Please provide the mode (name or id) and the value so that I can find the sprite for you!"); + var character = Character.Controlled; + if (character == null) + { + ThrowError("Please provide the mode (name or id) and the value so that I can find the sprite for you!"); + return; + } + var item = character.FocusedItem; + if (item != null) + { + item.Sprite.ReloadXML(); + item.Sprite.ReloadTexture(); + } + else + { + ReloadWearables(character); + } } }, isCheat: true)); } + private static void ReloadWearables(Character character) + { + foreach (var limb in character.AnimController.Limbs) + { + limb.Sprite?.ReloadTexture(); + limb.DamagedSprite?.ReloadTexture(); + limb.DeformSprite?.Sprite.ReloadTexture(); + foreach (var wearable in limb.WearingItems) + { + wearable.Sprite.ReloadXML(); + wearable.Sprite.ReloadTexture(); + } + foreach (var wearable in limb.OtherWearables) + { + wearable.Sprite.ReloadXML(); + wearable.Sprite.ReloadTexture(); + } + if (limb.HuskSprite != null) + { + limb.HuskSprite.Sprite.ReloadXML(); + limb.HuskSprite.Sprite.ReloadTexture(); + } + } + } + private static bool TryDoActionOnSprite(string firstArg, string secondArg, Action action) { switch (firstArg) diff --git a/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs b/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs index a348da505..2e8cf1a3d 100644 --- a/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs +++ b/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs @@ -66,47 +66,6 @@ namespace Barotrauma CanBeFocused = false }; - Point scrollButtonSize = new Point((int)(200 * GUI.Scale), (int)(30 * GUI.Scale)); - - crewArea = new GUIFrame(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.CrewArea, guiFrame.RectTransform), "", Color.Transparent) - { - CanBeFocused = false - }; - toggleCrewButton = new GUIButton(new RectTransform(new Point((int)(30 * GUI.Scale), HUDLayoutSettings.CrewArea.Height), guiFrame.RectTransform) - { AbsoluteOffset = HUDLayoutSettings.CrewArea.Location }, - "", style: "UIToggleButton"); - toggleCrewButton.OnClicked += (GUIButton btn, object userdata) => - { - toggleCrewAreaOpen = !toggleCrewAreaOpen; - foreach (GUIComponent child in btn.Children) - { - child.SpriteEffects = toggleCrewAreaOpen ? SpriteEffects.None : SpriteEffects.FlipHorizontally; - } - return true; - }; - - characterListBox = new GUIListBox(new RectTransform(new Point(100, (int)(crewArea.Rect.Height - scrollButtonSize.Y * 1.6f)), crewArea.RectTransform, Anchor.CenterLeft), false, Color.Transparent, null) - { - //Spacing = (int)(3 * GUI.Scale), - ScrollBarEnabled = false, - ScrollBarVisible = false, - CanBeFocused = false - }; - - scrollButtonUp = new GUIButton(new RectTransform(scrollButtonSize, crewArea.RectTransform, Anchor.TopLeft, Pivot.TopLeft), "", Alignment.Center, "GUIButtonVerticalArrow") - { - Visible = false, - UserData = -1, - OnClicked = ScrollCharacterList - }; - scrollButtonDown = new GUIButton(new RectTransform(scrollButtonSize, crewArea.RectTransform, Anchor.BottomLeft, Pivot.BottomLeft), "", Alignment.Center, "GUIButtonVerticalArrow") - { - Visible = false, - UserData = 1, - OnClicked = ScrollCharacterList - }; - scrollButtonDown.Children.ForEach(c => c.SpriteEffects = SpriteEffects.FlipVertically); - var characterInfo = new CharacterInfo(subElement); characterInfos.Add(characterInfo); foreach (XElement invElement in subElement.Elements()) @@ -117,6 +76,49 @@ namespace Barotrauma } } + var reports = Order.PrefabList.FindAll(o => o.TargetAllCharacters && o.SymbolSprite != null); + reportButtonFrame = new GUILayoutGroup(new RectTransform( + new Point((HUDLayoutSettings.CrewArea.Height - (int)((reports.Count - 1) * 5 * GUI.Scale)) / reports.Count, HUDLayoutSettings.CrewArea.Height), guiFrame.RectTransform)) + { + AbsoluteSpacing = (int)(5 * GUI.Scale), + UserData = "reportbuttons", + CanBeFocused = false + }; + + //report buttons + foreach (Order order in reports) + { + if (!order.TargetAllCharacters || order.SymbolSprite == null) continue; + var btn = new GUIButton(new RectTransform(new Point(reportButtonFrame.Rect.Width), reportButtonFrame.RectTransform), style: null) + { + OnClicked = (GUIButton button, object userData) => + { + if (Character.Controlled == null || Character.Controlled.SpeechImpediment >= 100.0f) return false; + SetCharacterOrder(null, order, null, Character.Controlled); + return true; + }, + UserData = order, + ToolTip = order.Name + }; + + new GUIFrame(new RectTransform(new Vector2(1.5f), btn.RectTransform, Anchor.Center), "OuterGlow") + { + Color = Color.Red * 0.8f, + HoverColor = Color.Red * 1.0f, + PressedColor = Color.Red * 0.6f, + UserData = "highlighted", + CanBeFocused = false, + Visible = false + }; + + var img = new GUIImage(new RectTransform(Vector2.One, btn.RectTransform), order.Prefab.SymbolSprite, scaleToFit: true) + { + Color = order.Color, + HoverColor = Color.Lerp(order.Color, Color.White, 0.5f), + ToolTip = order.Name + }; + } + screenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight); prevUIScale = GUI.Scale; @@ -1308,6 +1310,14 @@ namespace Barotrauma if (GameMain.NetworkMember != null) GameMain.Client.SelectCrewCharacter(character, previewPlayer); + /// + /// Should report buttons be visible on the screen atm? + /// + private bool ReportButtonsVisible() + { + return CharacterHealth.OpenHealthWindow == null; + } + private bool ReportButtonClicked(GUIButton button, object userData) { //order targeted to all characters diff --git a/Barotrauma/BarotraumaClient/Source/GameSettings.cs b/Barotrauma/BarotraumaClient/Source/GameSettings.cs index 00085898f..a111ddc03 100644 --- a/Barotrauma/BarotraumaClient/Source/GameSettings.cs +++ b/Barotrauma/BarotraumaClient/Source/GameSettings.cs @@ -626,6 +626,28 @@ namespace Barotrauma new GUIMessageBox(TextManager.Get("RestartRequiredLabel"), TextManager.Get("RestartRequiredLanguage")); + return true; + }; + + //spacing + new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), generalLayoutGroup.RectTransform), style: null); + + new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), buttonArea.RectTransform, Anchor.BottomLeft), + TextManager.Get("Cancel")) + { + IgnoreLayoutGroups = true, + OnClicked = (x, y) => + { + if (UnsavedSettings) + { + LoadPlayerConfig(); + } + if (Screen.Selected == GameMain.MainMenuScreen) GameMain.MainMenuScreen.ReturnToMainMenu(null, null); + GUI.SettingsMenuOpen = false; + return true; + } + }; + //spacing new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), generalLayoutGroup.RectTransform), style: null); diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/ItemComponent.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/ItemComponent.cs index 72589ccf5..94a9eee32 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/ItemComponent.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/ItemComponent.cs @@ -184,6 +184,63 @@ namespace Barotrauma.Items.Components } } } + + public void ApplyTo(RectTransform target) + { + if (RelativeOffset.HasValue) + { + target.RelativeOffset = RelativeOffset.Value; + } + else if (AbsoluteOffset.HasValue) + { + target.AbsoluteOffset = AbsoluteOffset.Value; + } + if (RelativeSize.HasValue) + { + target.RelativeSize = RelativeSize.Value; + } + else if (AbsoluteSize.HasValue) + { + target.NonScaledSize = AbsoluteSize.Value; + } + if (Anchor.HasValue) + { + target.Anchor = Anchor.Value; + } + if (Pivot.HasValue) + { + target.Pivot = Pivot.Value; + } + else + { + target.Pivot = RectTransform.MatchPivotToAnchor(target.Anchor); + } + target.RecalculateChildren(true, true); + } + } + + public GUIFrame GuiFrame { get; protected set; } + + [Serialize(false, false)] + public bool AllowUIOverlap + { + get; + set; + } + + private ItemComponent linkToUIComponent; + [Serialize("", false)] + public string LinkUIToComponent + { + get; + set; + } + + [Serialize(0, false)] + public int HudPriority + { + get; + private set; } private bool shouldMuffleLooping; diff --git a/Barotrauma/BarotraumaClient/Source/Items/Item.cs b/Barotrauma/BarotraumaClient/Source/Items/Item.cs index c19111f10..d167d6c12 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Item.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Item.cs @@ -485,11 +485,11 @@ namespace Barotrauma } if (!inGame && Sprite != null) { - var reloadTextureButton = new GUIButton(new RectTransform(new Point(editingHUD.Rect.Width / 2, 20)), "Reload Texture"); + var reloadTextureButton = new GUIButton(new RectTransform(new Point(editingHUD.Rect.Width / 2, 20)), TextManager.Get("ReloadSprite")); reloadTextureButton.OnClicked += (button, data) => { - Sprite.ReloadTexture(); Sprite.ReloadXML(); + Sprite.ReloadTexture(); return true; }; itemEditor.AddCustomContent(reloadTextureButton, itemEditor.ContentCount); diff --git a/Barotrauma/BarotraumaClient/Source/Map/Structure.cs b/Barotrauma/BarotraumaClient/Source/Map/Structure.cs index d18ec4103..65cfd37c9 100644 --- a/Barotrauma/BarotraumaClient/Source/Map/Structure.cs +++ b/Barotrauma/BarotraumaClient/Source/Map/Structure.cs @@ -92,11 +92,11 @@ namespace Barotrauma editingHUD = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.25f), GUI.Canvas, Anchor.CenterRight) { MinSize = new Point(400, 0) }) { UserData = this }; GUIListBox listBox = new GUIListBox(new RectTransform(new Vector2(0.95f, 0.8f), editingHUD.RectTransform, Anchor.Center), style: null); var editor = new SerializableEntityEditor(listBox.Content.RectTransform, this, inGame, showName: true, elementHeight: 20); - var reloadTextureButton = new GUIButton(new RectTransform(new Point(editingHUD.Rect.Width / 2, 20)), "Reload Texture"); + var reloadTextureButton = new GUIButton(new RectTransform(new Point(editingHUD.Rect.Width / 2, 20)), TextManager.Get("ReloadSprite")); reloadTextureButton.OnClicked += (button, data) => { - Sprite.ReloadTexture(); Sprite.ReloadXML(); + Sprite.ReloadTexture(); return true; }; editor.AddCustomContent(reloadTextureButton, editor.ContentCount); diff --git a/Barotrauma/BarotraumaClient/Source/Screens/CharacterEditorScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/CharacterEditorScreen.cs index fd3e9fedc..f75d58b4d 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/CharacterEditorScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/CharacterEditorScreen.cs @@ -401,6 +401,43 @@ namespace Barotrauma UpdateSourceRect(limb, newRect); } } + UpdateJointCreation(); + if (PlayerInput.KeyHit(Keys.Left)) + { + foreach (var limb in selectedLimbs) + { + var newRect = limb.ActiveSprite.SourceRect; + newRect.X--; + UpdateSourceRect(limb, newRect); + } + } + if (PlayerInput.KeyHit(Keys.Right)) + { + foreach (var limb in selectedLimbs) + { + var newRect = limb.ActiveSprite.SourceRect; + newRect.X++; + UpdateSourceRect(limb, newRect); + } + } + if (PlayerInput.KeyHit(Keys.Down)) + { + foreach (var limb in selectedLimbs) + { + var newRect = limb.ActiveSprite.SourceRect; + newRect.Y++; + UpdateSourceRect(limb, newRect); + } + } + if (PlayerInput.KeyHit(Keys.Up)) + { + foreach (var limb in selectedLimbs) + { + var newRect = limb.ActiveSprite.SourceRect; + newRect.Y--; + UpdateSourceRect(limb, newRect); + } + } } if (!isFreezed) { diff --git a/Barotrauma/BarotraumaShared/BarotraumaShared.projitems b/Barotrauma/BarotraumaShared/BarotraumaShared.projitems index c722f33a0..29d2265d3 100644 --- a/Barotrauma/BarotraumaShared/BarotraumaShared.projitems +++ b/Barotrauma/BarotraumaShared/BarotraumaShared.projitems @@ -90,6 +90,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs index 7eb5044ed..de7b09800 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs @@ -795,15 +795,12 @@ namespace Barotrauma } partial void InitProjSpecific(XDocument doc); - public void ReloadHead(int? headId = null, int? hairIndex = null, int? beardIndex = null, int? moustacheIndex = null, int? faceAttachmentIndex = null) + public void ReloadHead(int? headId = null, int hairIndex = -1, int beardIndex = -1, int moustacheIndex = -1, int faceAttachmentIndex = -1) { if (Info == null) { return; } var head = AnimController.GetLimb(LimbType.Head); if (head == null) { return; } - if (headId.HasValue) - { - Info.RecreateHead(headId.Value, Info.Race, Info.Gender, hairIndex ?? -1, beardIndex ?? -1, moustacheIndex ?? -1, faceAttachmentIndex ?? -1); - } + Info.RecreateHead(headId ?? Info.HeadSpriteId, Info.Race, Info.Gender, hairIndex, beardIndex, moustacheIndex, faceAttachmentIndex); #if CLIENT head.RecreateSprite(); #endif @@ -828,6 +825,10 @@ namespace Barotrauma Info.MoustacheElement?.Elements("sprite").ForEach(s => head.OtherWearables.Add(new WearableSprite(s, WearableType.Moustache))); if (info.HairElement == null) { info.HairIndex = 0; } Info.HairElement?.Elements("sprite").ForEach(s => head.OtherWearables.Add(new WearableSprite(s, WearableType.Hair))); + +#if CLIENT + head.LoadHuskSprite(); +#endif } private static string humanConfigFile; diff --git a/Barotrauma/BarotraumaShared/Source/Characters/CharacterInfo.cs b/Barotrauma/BarotraumaShared/Source/Characters/CharacterInfo.cs index 77448f2a7..574e99435 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/CharacterInfo.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/CharacterInfo.cs @@ -462,7 +462,7 @@ namespace Barotrauma private List faceAttachments; private IEnumerable wearables; - private IEnumerable Wearables + public IEnumerable Wearables { get { @@ -478,7 +478,18 @@ namespace Barotrauma } } - private IEnumerable FilterElementsByGenderAndRace(IEnumerable elements) + public IEnumerable FilterByTypeAndHeadID(IEnumerable elements, WearableType targetType) + { + return elements.Where(e => + { + if (Enum.TryParse(e.GetAttributeString("type", ""), true, out WearableType type) && type != targetType) { return false; } + int headId = e.GetAttributeInt("headid", -1); + // if the head id is less than 1, the id is not valid and the condition is ignored. + return headId < 1 || headId == Head.HeadSpriteId; + }); + } + + public IEnumerable FilterElementsByGenderAndRace(IEnumerable elements) { if (elements == null) { return elements; } return elements.Where(w => @@ -669,17 +680,6 @@ namespace Barotrauma return element == null || element.Name == "Empty" ? null : element; } - IEnumerable FilterByTypeAndHeadID(IEnumerable elements, WearableType targetType) - { - return elements.Where(e => - { - if (Enum.TryParse(e.GetAttributeString("type", ""), true, out WearableType type) && type != targetType) { return false; } - int headId = e.GetAttributeInt("headid", -1); - // if the head id is less than 1, the id is not valid and the condition is ignored. - return headId < 1 || headId == Head.HeadSpriteId; - }); - } - bool IsWearableAllowed(XElement element) { string spriteName = element.Element("sprite").GetAttributeString("name", string.Empty); diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Health/CharacterHealth.cs b/Barotrauma/BarotraumaShared/Source/Characters/Health/CharacterHealth.cs index 09793541e..dad69cb1f 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Health/CharacterHealth.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Health/CharacterHealth.cs @@ -257,6 +257,11 @@ namespace Barotrauma return null; } + public T GetAffliction(string afflictionType, bool allowLimbAfflictions = true) where T : Affliction + { + return GetAffliction(afflictionType, allowLimbAfflictions) as T; + } + public Affliction GetAffliction(string afflictionType, Limb limb) { foreach (Affliction affliction in limbHealths[limb.HealthIndex].Afflictions) diff --git a/Barotrauma/BarotraumaShared/Source/DebugConsole.cs b/Barotrauma/BarotraumaShared/Source/DebugConsole.cs index aa7095827..0f0fe61b4 100644 --- a/Barotrauma/BarotraumaShared/Source/DebugConsole.cs +++ b/Barotrauma/BarotraumaShared/Source/DebugConsole.cs @@ -985,7 +985,57 @@ namespace Barotrauma Submarine.MainSub?.FlipX(); })); - commands.Add(new Command("loadhead", "Load head sprite(s). Required argument: head id. Optional arguments: hair index, beard index, moustache index, face attachment index.", args => + commands.Add(new Command("gender", "Set the gender of the controlled character. Allowed parameters: Male, Female, None.", args => + { + var character = Character.Controlled; + if (character == null) + { + ThrowError("Not controlling any character!"); + return; + } + if (args.Length == 0) + { + ThrowError("No parameters provided!"); + return; + } + if (Enum.TryParse(args[0], true, out Gender gender)) + { + character.Info.Gender = gender; + character.ReloadHead(); + foreach (var limb in character.AnimController.Limbs) + { + foreach (var wearable in limb.WearingItems) + { + if (wearable.Gender != Gender.None && wearable.Gender != gender) + { + wearable.Gender = gender; + } + } + } + } + })); + + commands.Add(new Command("race", "Set race of the controlled character. Allowed parameters: White, Black, Asian, None.", args => + { + var character = Character.Controlled; + if (character == null) + { + ThrowError("Not controlling any character!"); + return; + } + if (args.Length == 0) + { + ThrowError("No parameters provided!"); + return; + } + if (Enum.TryParse(args[0], true, out Race race)) + { + character.Info.Race = race; + character.ReloadHead(); + } + })); + + commands.Add(new Command("loadhead|head", "Load head sprite(s). Required argument: head id. Optional arguments: hair index, beard index, moustache index, face attachment index.", args => { var character = Character.Controlled; if (character == null) diff --git a/Barotrauma/BarotraumaShared/Source/GameSettings.cs b/Barotrauma/BarotraumaShared/Source/GameSettings.cs index 861034097..e9c324de7 100644 --- a/Barotrauma/BarotraumaShared/Source/GameSettings.cs +++ b/Barotrauma/BarotraumaShared/Source/GameSettings.cs @@ -67,6 +67,8 @@ namespace Barotrauma public int ParticleLimit { get; set; } + public int ParticleLimit { get; set; } + public float LightMapScale { get; set; } public bool SpecularityEnabled { get; set; } public bool ChromaticAberrationEnabled { get; set; } @@ -542,34 +544,6 @@ namespace Barotrauma TextManager.LoadTextPacks(SelectedContentPackages); - //display error messages after all content packages have been loaded - //to make sure the package that contains text files has been loaded before we attempt to use TextManager - foreach (string missingPackagePath in missingPackagePaths) - { - DebugConsole.ThrowError(TextManager.Get("ContentPackageNotFound").Replace("[packagepath]", missingPackagePath)); - } - foreach (ContentPackage incompatiblePackage in incompatiblePackages) - { - DebugConsole.ThrowError(TextManager.Get(incompatiblePackage.GameVersion <= new Version(0, 0, 0, 0) ? "IncompatibleContentPackageUnknownVersion" : "IncompatibleContentPackage") - .Replace("[packagename]", incompatiblePackage.Name) - .Replace("[packageversion]", incompatiblePackage.GameVersion.ToString()) - .Replace("[gameversion]", GameMain.Version.ToString())); - } - foreach (ContentPackage contentPackage in SelectedContentPackages) - { - foreach (ContentFile file in contentPackage.Files) - { - if (!System.IO.File.Exists(file.Path)) - { - DebugConsole.ThrowError("Error in content package \"" + contentPackage.Name + "\" - file \"" + file.Path + "\" not found."); - continue; - } - ToolBox.IsProperFilenameCase(file.Path); - } - } - - TextManager.LoadTextPacks(SelectedContentPackages); - //display error messages after all content packages have been loaded //to make sure the package that contains text files has been loaded before we attempt to use TextManager foreach (string missingPackagePath in missingPackagePaths) @@ -1226,6 +1200,32 @@ namespace Barotrauma NewLineOnAttributes = true }; +#if CLIENT + if (Tutorial.Tutorials != null) + { + foreach (Tutorial tutorial in Tutorial.Tutorials) + { + if (tutorial.Completed && !CompletedTutorialNames.Contains(tutorial.Name)) + { + CompletedTutorialNames.Add(tutorial.Name); + } + } + } +#endif + var tutorialElement = new XElement("tutorials"); + foreach (string tutorialName in CompletedTutorialNames) + { + tutorialElement.Add(new XElement("Tutorial", new XAttribute("name", tutorialName))); + } + doc.Root.Add(tutorialElement); + + XmlWriterSettings settings = new XmlWriterSettings + { + Indent = true, + OmitXmlDeclaration = true, + NewLineOnAttributes = true + }; + #if CLIENT if (Tutorial.Tutorials != null) { diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Wearable.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Wearable.cs index ad574c86a..3bd0e3545 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Wearable.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Wearable.cs @@ -15,7 +15,8 @@ namespace Barotrauma Beard, Moustache, FaceAttachment, - JobIndicator + JobIndicator, + Husk } class WearableSprite @@ -86,7 +87,7 @@ namespace Barotrauma { Type = type; SourceElement = subElement; - SpritePath = subElement.Attribute("texture").Value; + SpritePath = subElement.GetAttributeString("texture", string.Empty); Init(); switch (type) { @@ -95,6 +96,7 @@ namespace Barotrauma case WearableType.Moustache: case WearableType.FaceAttachment: case WearableType.JobIndicator: + case WearableType.Husk: Limb = LimbType.Head; HideLimb = false; HideOtherWearables = false; diff --git a/Barotrauma/BarotraumaShared/Source/Map/Map/Map.cs b/Barotrauma/BarotraumaShared/Source/Map/Map/Map.cs index c3d5d2926..5e0b5c4dc 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Map/Map.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Map/Map.cs @@ -430,6 +430,13 @@ namespace Barotrauma } CurrentLocation.SelectedMissionIndex = missionIndex; + //the destination must be the same as the destination of the mission + if (CurrentLocation.SelectedMission != null && + CurrentLocation.SelectedMission.Locations[1] != SelectedLocation) + { + SelectLocation(CurrentLocation.SelectedMission.Locations[1]); + } + SelectedLocation = location; SelectedConnection = connections.Find(c => c.Locations.Contains(CurrentLocation) && c.Locations.Contains(SelectedLocation)); OnLocationSelected?.Invoke(SelectedLocation, SelectedConnection); diff --git a/Barotrauma/BarotraumaShared/Source/PlayerInput.cs b/Barotrauma/BarotraumaShared/Source/PlayerInput.cs index dac0e7d44..fc56c8fc7 100644 --- a/Barotrauma/BarotraumaShared/Source/PlayerInput.cs +++ b/Barotrauma/BarotraumaShared/Source/PlayerInput.cs @@ -160,6 +160,16 @@ namespace Barotrauma } #endif + public void SetState() + { + hit = binding.IsHit(); + if (hit) hitQueue = true; + + held = binding.IsDown(); + if (held) heldQueue = true; + } +#endif + public bool Hit { get diff --git a/Barotrauma/BarotraumaShared/Source/Sprite/Sprite.cs b/Barotrauma/BarotraumaShared/Source/Sprite/Sprite.cs index da9b39bf5..f5b89c237 100644 --- a/Barotrauma/BarotraumaShared/Source/Sprite/Sprite.cs +++ b/Barotrauma/BarotraumaShared/Source/Sprite/Sprite.cs @@ -238,7 +238,7 @@ namespace Barotrauma } if (sourceElements.Multiple()) { - DebugConsole.NewMessage($"[Sprite] Multiple matching elements found by name ({Name}) or identifier ({EntityID})! Cannot reload the xml for sprite element \"{SourceElement.ToString()}\"!", Color.Yellow); + DebugConsole.NewMessage($"[Sprite] Multiple matching elements found by name ({Name}) or identifier ({EntityID})!: {SourceElement.ToString()}", Color.Yellow); } else if (sourceElements.None()) { @@ -247,6 +247,9 @@ namespace Barotrauma else { SourceElement = sourceElements.Single(); + } + if (SourceElement != null) + { Vector4 sourceVector = SourceElement.GetAttributeVector4("sourcerect", Vector4.Zero); sourceRect = new Rectangle((int)sourceVector.X, (int)sourceVector.Y, (int)sourceVector.Z, (int)sourceVector.W); size = SourceElement.GetAttributeVector2("size", Vector2.One);