using Barotrauma.Extensions; using Microsoft.Xna.Framework; using System; using System.Collections.Generic; using System.Linq; using System.Xml.Linq; namespace Barotrauma { [RequiredByCorePackage] sealed class CharacterFile : ContentFile { public CharacterFile(ContentPackage contentPackage, ContentPath path) : base(contentPackage, path) { } public override void LoadFile() { ClearCaches(); XDocument doc = XMLExtensions.TryLoadXml(Path); if (doc == null) { DebugConsole.ThrowError($"Loading character file failed: {Path}", contentPackage: ContentPackage); return; } if (CharacterPrefab.Prefabs.AllPrefabs.Any(kvp => kvp.Value.Any(cf => cf?.ContentFile == this))) { DebugConsole.ThrowError($"Duplicate path: {Path}", contentPackage: ContentPackage); return; } var mainElement = doc.Root.FromPackage(ContentPackage); bool isOverride = mainElement.IsOverride(); if (isOverride) { mainElement = mainElement.FirstElement(); } if (!CharacterPrefab.CheckSpeciesName(mainElement, this, out Identifier n)) { return; } var prefab = new CharacterPrefab(mainElement, this); CharacterPrefab.Prefabs.Add(prefab, isOverride); } public override void UnloadFile() { CharacterPrefab.Prefabs.RemoveByFile(this); ClearCaches(); } private static void ClearCaches() { // Clear the caches to get rid of any overrides. // Variants should have their own params instances, but let's keep it simple and play safe. RagdollParams.ClearCache(); AnimationParams.ClearCache(); } public override void Sort() { CharacterPrefab.Prefabs.SortAll(); } public override void Preload(Action addPreloadedSprite) { #if CLIENT CharacterPrefab characterPrefab = CharacterPrefab.FindByFilePath(Path.Value); if (characterPrefab?.ConfigElement == null) { throw new Exception($"Failed to load the character config file from {Path}!"); } var mainElement = characterPrefab.ConfigElement; mainElement.GetChildElements("sound").ForEach(e => RoundSound.Load(e)); if (!CharacterPrefab.CheckSpeciesName(mainElement, this, out Identifier speciesName)) { return; } bool humanoid = mainElement.GetAttributeBool("humanoid", false); RagdollParams ragdollParams; try { if (humanoid) { ragdollParams = RagdollParams.GetDefaultRagdollParams(speciesName, mainElement, ContentPackage); } else { ragdollParams = RagdollParams.GetDefaultRagdollParams(speciesName, mainElement, ContentPackage); } } catch (Exception e) { DebugConsole.ThrowError($"Failed to preload a ragdoll file for the character \"{characterPrefab.Name}\"", e, contentPackage: characterPrefab.ContentPackage); return; } if (ragdollParams != null) { HashSet texturePaths = new HashSet(); AddTexturePath(ragdollParams.Texture); foreach (RagdollParams.LimbParams limb in ragdollParams.Limbs) { AddTexturePath(limb.normalSpriteParams?.Texture); AddTexturePath(limb.deformSpriteParams?.Texture); AddTexturePath(limb.damagedSpriteParams?.Texture); foreach (var decorativeSprite in limb.decorativeSpriteParams) { AddTexturePath(decorativeSprite.Texture); } } foreach (ContentPath texturePath in texturePaths) { addPreloadedSprite(new Sprite(texturePath.Value, Vector2.Zero)); } void AddTexturePath(string path) { if (string.IsNullOrEmpty(path)) { return; } //if the path contains a gender variable, we can't load it yet because we don't know which gender we need if (path.Contains("[GENDER]")) { return; } texturePaths.Add(ContentPath.FromRaw(characterPrefab.ContentPackage, ragdollParams.Texture)); } } #endif } } }