diff --git a/Barotrauma/BarotraumaClient/Source/DebugConsole.cs b/Barotrauma/BarotraumaClient/Source/DebugConsole.cs index 18c1b7424..9fed2075c 100644 --- a/Barotrauma/BarotraumaClient/Source/DebugConsole.cs +++ b/Barotrauma/BarotraumaClient/Source/DebugConsole.cs @@ -1519,7 +1519,7 @@ namespace Barotrauma character.AnimController.ResetRagdoll(); }, isCheat: true)); - 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 => + commands.Add(new Command("reloadwearables", "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) @@ -1530,6 +1530,26 @@ namespace Barotrauma ReloadWearables(character); }, isCheat: true)); + commands.Add(new Command("loadwearable", "Force select certain variant for the selected character.", args => + { + var character = Character.Controlled; + if (character == null) + { + ThrowError("Not controlling any character."); + return; + } + if (args.Length == 0) + { + ThrowError("No arguments provided! Give an index number for the variant starting from 1."); + return; + } + if (int.TryParse(args[0], out int variant)) + { + ReloadWearables(character, variant); + } + + }, isCheat: true)); + 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) @@ -1677,7 +1697,7 @@ namespace Barotrauma }, isCheat: true)); } - private static void ReloadWearables(Character character) + private static void ReloadWearables(Character character, int variant = 0) { foreach (var limb in character.AnimController.Limbs) { @@ -1686,11 +1706,17 @@ namespace Barotrauma limb.DeformSprite?.Sprite.ReloadTexture(); foreach (var wearable in limb.WearingItems) { + if (variant > 0 && wearable.Variant > 0) + { + wearable.Variant = variant; + } + wearable.RefreshPath(); wearable.Sprite.ReloadXML(); wearable.Sprite.ReloadTexture(); } foreach (var wearable in limb.OtherWearables) { + wearable.RefreshPath(); wearable.Sprite.ReloadXML(); wearable.Sprite.ReloadTexture(); } diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Wearable.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Wearable.cs index b30c71253..1befb10a5 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Wearable.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Wearable.cs @@ -5,6 +5,7 @@ using System.IO; using System.Linq; using System.Xml.Linq; using Barotrauma.Items.Components; +using Barotrauma.Extensions; namespace Barotrauma { @@ -66,7 +67,7 @@ namespace Barotrauma public LightComponent LightComponent { get; set; } - public int Variant { get; private set; } + public int Variant { get; set; } private Gender _gender; /// @@ -114,32 +115,65 @@ namespace Barotrauma /// /// Note: this constructor cannot initialize automatically, because the gender is unknown at this point. We only know it when the item is equipped. /// - public WearableSprite(XElement subElement, Wearable wearable, int variant = 1) + public WearableSprite(XElement subElement, Wearable wearable, int variant = 0) { Type = WearableType.Item; WearableComponent = wearable; - Variant = Math.Max(variant, 1); + Variant = Math.Max(variant, 0); SpritePath = ParseSpritePath(subElement.GetAttributeString("texture", string.Empty)); SourceElement = subElement; } private string ParseSpritePath(string texturePath) => texturePath.Contains("/") ? texturePath : $"{Path.GetDirectoryName(WearableComponent.Item.Prefab.ConfigFile)}/{texturePath}"; - public bool IsInitialized { get; private set; } - public void Init(Gender gender = Gender.None) + public void RefreshPath() + { + if (Variant > 0) + { + // Restore the tag so that we can parse it again. + ReplaceNumbersWith("[VARIANT]"); + } + ParsePath(true); + } + + private void ReplaceNumbersWith(string replacement) + { + var fileName = Path.GetFileName(SpritePath); + var path = Path.GetDirectoryName(SpritePath); + fileName = fileName.Replace(replacement, c => char.IsNumber(c)); + SpritePath = Path.Combine(path, fileName); + } + + private void ParsePath(bool parseSpritePath) { - if (IsInitialized) { return; } - _gender = SpritePath.Contains("[GENDER]") ? gender : Gender.None; if (_gender != Gender.None) { SpritePath = SpritePath.Replace("[GENDER]", (_gender == Gender.Female) ? "female" : "male"); } SpritePath = SpritePath.Replace("[VARIANT]", Variant.ToString()); + if (!File.Exists(SpritePath)) + { + // If the variant does not exist, parse the path so that it uses first variant. + Variant = 1; + ReplaceNumbersWith(Variant.ToString()); + } + if (parseSpritePath) + { + Sprite.ParseTexturePath(file: SpritePath); + } + } + + public bool IsInitialized { get; private set; } + public void Init(Gender gender = Gender.None) + { + if (IsInitialized) { return; } + _gender = SpritePath.Contains("[GENDER]") ? gender : Gender.None; + ParsePath(false); if (Sprite != null) { Sprite.Remove(); } - Sprite = new Sprite(SourceElement, "", SpritePath); + Sprite = new Sprite(SourceElement, file: SpritePath); Limb = (LimbType)Enum.Parse(typeof(LimbType), SourceElement.GetAttributeString("limb", "Head"), true); HideLimb = SourceElement.GetAttributeBool("hidelimb", false); HideOtherWearables = SourceElement.GetAttributeBool("hideotherwearables", false); @@ -174,17 +208,18 @@ namespace Barotrauma.Items.Components get { return damageModifiers; } } - public Wearable (Item item, XElement element) : base(item, element) + public Wearable(Item item, XElement element) : base(item, element) { this.item = item; damageModifiers = new List(); int spriteCount = element.Elements().Count(x => x.Name.ToString() == "sprite"); + int variants = element.GetAttributeInt("variants", 0); + int variant = variants > 0 ? Rand.Range(1, variants + 1, Rand.RandSync.Server) : 1; wearableSprites = new WearableSprite[spriteCount]; limbType = new LimbType[spriteCount]; limb = new Limb[spriteCount]; - int i = 0; foreach (XElement subElement in element.Elements()) { @@ -200,7 +235,7 @@ namespace Barotrauma.Items.Components limbType[i] = (LimbType)Enum.Parse(typeof(LimbType), subElement.GetAttributeString("limb", "Head"), true); - wearableSprites[i] = new WearableSprite(subElement, this); + wearableSprites[i] = new WearableSprite(subElement, this, variant); foreach (XElement lightElement in subElement.Elements()) { diff --git a/Barotrauma/BarotraumaShared/Source/Sprite/Sprite.cs b/Barotrauma/BarotraumaShared/Source/Sprite/Sprite.cs index 7862e4e82..064b5d1e2 100644 --- a/Barotrauma/BarotraumaShared/Source/Sprite/Sprite.cs +++ b/Barotrauma/BarotraumaShared/Source/Sprite/Sprite.cs @@ -87,7 +87,6 @@ namespace Barotrauma public string FullPath { get; private set; } - public override string ToString() { return FilePath + ": " + sourceRect; @@ -107,25 +106,7 @@ namespace Barotrauma { this.lazyLoad = lazyLoad; SourceElement = element; - if (file == "") - { - file = SourceElement.GetAttributeString("texture", ""); - } - if (file == "") - { - DebugConsole.ThrowError("Sprite " + SourceElement + " doesn't have a texture specified!"); - return; - } - if (!string.IsNullOrEmpty(path)) - { - if (!path.EndsWith("/")) path += "/"; - } - FilePath = path + file; - if (!string.IsNullOrEmpty(FilePath)) - { - FullPath = Path.GetFullPath(FilePath); - } - + if (!ParseTexturePath(path, file)) { return; } Name = SourceElement.GetAttributeString("name", null); Vector4 sourceVector = SourceElement.GetAttributeVector4("sourcerect", Vector4.Zero); preMultipliedAlpha = preMultiplyAlpha ?? SourceElement.GetAttributeBool("premultiplyalpha", true); @@ -269,6 +250,29 @@ namespace Barotrauma ID = GetID(SourceElement); } } + + public bool ParseTexturePath(string path = "", string file = "") + { + if (file == "") + { + file = SourceElement.GetAttributeString("texture", ""); + } + if (file == "") + { + DebugConsole.ThrowError("Sprite " + SourceElement + " doesn't have a texture specified!"); + return false; + } + if (!string.IsNullOrEmpty(path)) + { + if (!path.EndsWith("/")) path += "/"; + } + FilePath = path + file; + if (!string.IsNullOrEmpty(FilePath)) + { + FullPath = Path.GetFullPath(FilePath); + } + return true; + } } }