Esempio n. 1
0
        protected override void Act(float deltaTime)
        {
            var weldingTool = character.Inventory.FindItemByTag("weldingequipment", true);

            if (weldingTool == null)
            {
                TryAddSubObjective(ref getWeldingTool, () => new AIObjectiveGetItem(character, "weldingequipment", objectiveManager, equip: true, spawnItemIfNotFound: character.TeamID == CharacterTeamType.FriendlyNPC),
                                   onAbandon: () =>
                {
                    if (objectiveManager.IsCurrentOrder <AIObjectiveFixLeaks>())
                    {
                        character.Speak(TextManager.Get("dialogcannotfindweldingequipment"), null, 0.0f, "dialogcannotfindweldingequipment", 10.0f);
                    }
                    Abandon = true;
                },
                                   onCompleted: () => RemoveSubObjective(ref getWeldingTool));
                return;
            }
            else
            {
                if (weldingTool.OwnInventory == null)
                {
#if DEBUG
                    DebugConsole.ThrowError($"{character.Name}: AIObjectiveFixLeak failed - the item \"" + weldingTool + "\" has no proper inventory");
#endif
                    Abandon = true;
                    return;
                }
                // Drop empty tanks
                if (weldingTool.OwnInventory.AllItems.Any(it => it.Condition <= 0.0f))
                {
                    foreach (Item containedItem in weldingTool.OwnInventory.AllItemsMod)
                    {
                        if (containedItem.Condition <= 0.0f)
                        {
                            containedItem.Drop(character);
                        }
                    }
                }

                if (weldingTool.OwnInventory.AllItems.None(i => i.HasTag("weldingfuel") && i.Condition > 0.0f))
                {
                    TryAddSubObjective(ref refuelObjective, () => new AIObjectiveContainItem(character, "weldingfuel", weldingTool.GetComponent <ItemContainer>(), objectiveManager, spawnItemIfNotFound: character.TeamID == CharacterTeamType.FriendlyNPC),
                                       onAbandon: () => Abandon = true,
                                       onCompleted: () => RemoveSubObjective(ref refuelObjective));
                    return;
                }
            }
            if (subObjectives.Any())
            {
                return;
            }
            var repairTool = weldingTool.GetComponent <RepairTool>();
            if (repairTool == null)
            {
#if DEBUG
                DebugConsole.ThrowError($"{character.Name}: AIObjectiveFixLeak failed - the item \"" + weldingTool + "\" has no RepairTool component but is tagged as a welding tool");
#endif
                Abandon = true;
                return;
            }
            Vector2 toLeak = Leak.WorldPosition - character.WorldPosition;
            // TODO: use the collider size/reach?
            if (!character.AnimController.InWater && Math.Abs(toLeak.X) < 100 && toLeak.Y < 0.0f && toLeak.Y > -150)
            {
                HumanAIController.AnimController.Crouching = true;
            }
            float reach      = CalculateReach(repairTool, character);
            bool  canOperate = toLeak.LengthSquared() < reach * reach;
            if (canOperate)
            {
                TryAddSubObjective(ref operateObjective, () => new AIObjectiveOperateItem(repairTool, character, objectiveManager, option: "", requireEquip: true, operateTarget: Leak),
                                   onAbandon: () => Abandon = true,
                                   onCompleted: () =>
                {
                    if (Check())
                    {
                        IsCompleted = true;
                    }
                    else
                    {
                        // Failed to operate. Probably too far.
                        Abandon = true;
                    }
                });
            }
            else
            {
                TryAddSubObjective(ref gotoObjective, () => new AIObjectiveGoTo(Leak, character, objectiveManager)
                {
                    CloseEnough        = reach,
                    DialogueIdentifier = Leak.FlowTargetHull != null ? "dialogcannotreachleak" : null,
                    TargetName         = Leak.FlowTargetHull?.DisplayName,
                    CheckVisibility    = false
                },
                                   onAbandon: () =>
                {
                    if (Check())
                    {
                        IsCompleted = true;
                    }
                    else if ((Leak.WorldPosition - character.WorldPosition).LengthSquared() > MathUtils.Pow(reach * 2, 2))
                    {
                        // Too far
                        Abandon = true;
                    }
                    else
                    {
                        // We are close, try again.
                        RemoveSubObjective(ref gotoObjective);
                    }
                },
                                   onCompleted: () => RemoveSubObjective(ref gotoObjective));
            }
        }
Esempio n. 2
0
        public void CheckForErrors()
        {
            List <string> errorMsgs = new List <string>();
            List <SubEditorScreen.WarningType> warnings = new List <SubEditorScreen.WarningType>();

            if (!Hull.hullList.Any())
            {
                if (!IsWarningSuppressed(SubEditorScreen.WarningType.NoWaypoints))
                {
                    errorMsgs.Add(TextManager.Get("NoHullsWarning"));
                    warnings.Add(SubEditorScreen.WarningType.NoHulls);
                }
            }

            if (Info.Type != SubmarineType.OutpostModule ||
                (Info.OutpostModuleInfo?.ModuleFlags.Any(f => !f.Equals("hallwayvertical", StringComparison.OrdinalIgnoreCase) && !f.Equals("hallwayhorizontal", StringComparison.OrdinalIgnoreCase)) ?? true))
            {
                if (!WayPoint.WayPointList.Any(wp => wp.ShouldBeSaved && wp.SpawnType == SpawnType.Path))
                {
                    if (!IsWarningSuppressed(SubEditorScreen.WarningType.NoWaypoints))
                    {
                        errorMsgs.Add(TextManager.Get("NoWaypointsWarning"));
                        warnings.Add(SubEditorScreen.WarningType.NoWaypoints);
                    }
                }
            }

            if (Info.Type == SubmarineType.Player)
            {
                foreach (Item item in Item.ItemList)
                {
                    if (item.GetComponent <Items.Components.Vent>() == null)
                    {
                        continue;
                    }
                    if (!item.linkedTo.Any())
                    {
                        if (!IsWarningSuppressed(SubEditorScreen.WarningType.DisconnectedVents))
                        {
                            errorMsgs.Add(TextManager.Get("DisconnectedVentsWarning"));
                            warnings.Add(SubEditorScreen.WarningType.DisconnectedVents);
                        }
                        break;
                    }
                }

                if (!WayPoint.WayPointList.Any(wp => wp.ShouldBeSaved && wp.SpawnType == SpawnType.Human))
                {
                    if (!IsWarningSuppressed(SubEditorScreen.WarningType.NoHumanSpawnpoints))
                    {
                        errorMsgs.Add(TextManager.Get("NoHumanSpawnpointWarning"));
                        warnings.Add(SubEditorScreen.WarningType.NoHumanSpawnpoints);
                    }
                }
                if (WayPoint.WayPointList.Find(wp => wp.SpawnType == SpawnType.Cargo) == null)
                {
                    if (!IsWarningSuppressed(SubEditorScreen.WarningType.NoCargoSpawnpoints))
                    {
                        errorMsgs.Add(TextManager.Get("NoCargoSpawnpointWarning"));
                        warnings.Add(SubEditorScreen.WarningType.NoCargoSpawnpoints);
                    }
                }
                if (!Item.ItemList.Any(it => it.GetComponent <Items.Components.Pump>() != null && it.HasTag("ballast")))
                {
                    if (!IsWarningSuppressed(SubEditorScreen.WarningType.NoBallastTag))
                    {
                        errorMsgs.Add(TextManager.Get("NoBallastTagsWarning"));
                        warnings.Add(SubEditorScreen.WarningType.NoBallastTag);
                    }
                }
            }
            else if (Info.Type == SubmarineType.OutpostModule)
            {
                foreach (Item item in Item.ItemList)
                {
                    var junctionBox = item.GetComponent <PowerTransfer>();
                    if (junctionBox == null)
                    {
                        continue;
                    }
                    int doorLinks =
                        item.linkedTo.Count(lt => lt is Gap || (lt is Item it2 && it2.GetComponent <Door>() != null)) +
                        Item.ItemList.Count(it2 => it2.linkedTo.Contains(item) && !item.linkedTo.Contains(it2));
                    for (int i = 0; i < item.Connections.Count; i++)
                    {
                        int wireCount = item.Connections[i].Wires.Count(w => w != null);
                        if (doorLinks + wireCount > Connection.MaxLinked)
                        {
                            errorMsgs.Add(TextManager.GetWithVariables("InsufficientFreeConnectionsWarning",
                                                                       new string[] { "[doorcount]", "[freeconnectioncount]" },
                                                                       new string[] { doorLinks.ToString(), (Connection.MaxLinked - wireCount).ToString() }));
                            break;
                        }
                    }
                }
            }

            if (Gap.GapList.Any(g => g.linkedTo.Count == 0))
            {
                if (!IsWarningSuppressed(SubEditorScreen.WarningType.NonLinkedGaps))
                {
                    errorMsgs.Add(TextManager.Get("NonLinkedGapsWarning"));
                    warnings.Add(SubEditorScreen.WarningType.NonLinkedGaps);
                }
            }

            int disabledItemLightCount = 0;

            foreach (Item item in Item.ItemList)
            {
                if (item.ParentInventory == null)
                {
                    continue;
                }
                disabledItemLightCount += item.GetComponents <Items.Components.LightComponent>().Count();
            }
            int count = GameMain.LightManager.Lights.Count(l => l.CastShadows) - disabledItemLightCount;

            if (count > 45)
            {
                if (!IsWarningSuppressed(SubEditorScreen.WarningType.TooManyLights))
                {
                    errorMsgs.Add(TextManager.Get("subeditor.shadowcastinglightswarning"));
                    warnings.Add(SubEditorScreen.WarningType.TooManyLights);
                }
            }

            if (errorMsgs.Any())
            {
                GUIMessageBox msgBox = new GUIMessageBox(TextManager.Get("Warning"), string.Join("\n\n", errorMsgs), new Vector2(0.25f, 0.0f), new Point(400, 200));
                if (warnings.Any())
                {
                    Point      size     = msgBox.RectTransform.NonScaledSize;
                    GUITickBox suppress = new GUITickBox(new RectTransform(new Vector2(1f, 0.33f), msgBox.Content.RectTransform), TextManager.Get("editor.suppresswarnings"));
                    msgBox.RectTransform.NonScaledSize = new Point(size.X, size.Y + suppress.RectTransform.NonScaledSize.Y);

                    msgBox.Buttons[0].OnClicked += (button, obj) =>
                    {
                        if (suppress.Selected)
                        {
                            foreach (SubEditorScreen.WarningType warning in warnings.Where(warning => !SubEditorScreen.SuppressedWarnings.Contains(warning)))
                            {
                                SubEditorScreen.SuppressedWarnings.Add(warning);
                            }
                        }

                        return(true);
                    };
                }
            }

            foreach (MapEntity e in MapEntity.mapEntityList)
            {
                if (Vector2.Distance(e.Position, HiddenSubPosition) > 20000)
                {
                    //move disabled items (wires, items inside containers) inside the sub
                    if (e is Item item && item.body != null && !item.body.Enabled)
                    {
                        item.SetTransform(ConvertUnits.ToSimUnits(HiddenSubPosition), 0.0f);
                    }
                }
            }

            foreach (MapEntity e in MapEntity.mapEntityList)
            {
                if (Vector2.Distance(e.Position, HiddenSubPosition) > 20000)
                {
                    var msgBox = new GUIMessageBox(
                        TextManager.Get("Warning"),
                        TextManager.Get("FarAwayEntitiesWarning"),
                        new string[] { TextManager.Get("Yes"), TextManager.Get("No") });

                    msgBox.Buttons[0].OnClicked += (btn, obj) =>
                    {
                        GameMain.SubEditorScreen.Cam.Position = e.WorldPosition;
                        return(true);
                    };
                    msgBox.Buttons[0].OnClicked += msgBox.Close;
                    msgBox.Buttons[1].OnClicked += msgBox.Close;

                    break;
                }
            }

            bool IsWarningSuppressed(SubEditorScreen.WarningType type)
            {
                return(SubEditorScreen.SuppressedWarnings.Contains(type));
            }
        }
Esempio n. 3
0
        public FabricationRecipe(XElement element, ItemPrefab itemPrefab)
        {
            TargetItem = itemPrefab;
            string displayName = element.GetAttributeString("displayname", "");

            DisplayName = string.IsNullOrEmpty(displayName) ? itemPrefab.Name : TextManager.Get($"DisplayName.{displayName}");

            SuitableFabricatorIdentifiers = element.GetAttributeStringArray("suitablefabricators", new string[0]);

            RequiredSkills = new List <Skill>();
            RequiredTime   = element.GetAttributeFloat("requiredtime", 1.0f);
            OutCondition   = element.GetAttributeFloat("outcondition", 1.0f);
            RequiredItems  = new List <RequiredItem>();

            foreach (XElement subElement in element.Elements())
            {
                switch (subElement.Name.ToString().ToLowerInvariant())
                {
                case "requiredskill":
                    if (subElement.Attribute("name") != null)
                    {
                        DebugConsole.ThrowError("Error in fabricable item " + itemPrefab.Name + "! Use skill identifiers instead of names.");
                        continue;
                    }

                    RequiredSkills.Add(new Skill(
                                           subElement.GetAttributeString("identifier", ""),
                                           subElement.GetAttributeInt("level", 0)));
                    break;

                case "item":
                case "requireditem":
                    string requiredItemIdentifier = subElement.GetAttributeString("identifier", "");
                    string requiredItemTag        = subElement.GetAttributeString("tag", "");
                    if (string.IsNullOrWhiteSpace(requiredItemIdentifier) && string.IsNullOrEmpty(requiredItemTag))
                    {
                        DebugConsole.ThrowError("Error in fabricable item " + itemPrefab.Name + "! One of the required items has no identifier or tag.");
                        continue;
                    }

                    float minCondition = subElement.GetAttributeFloat("mincondition", 1.0f);
                    //Substract mincondition from required item's condition or delete it regardless?
                    bool useCondition = subElement.GetAttributeBool("usecondition", true);
                    int  count        = subElement.GetAttributeInt("count", 1);

                    if (!string.IsNullOrEmpty(requiredItemIdentifier))
                    {
                        if (!(MapEntityPrefab.Find(null, requiredItemIdentifier.Trim()) is ItemPrefab requiredItem))
                        {
                            DebugConsole.ThrowError("Error in fabricable item " + itemPrefab.Name + "! Required item \"" + requiredItemIdentifier + "\" not found.");
                            continue;
                        }

                        var existing = RequiredItems.Find(r => r.ItemPrefabs.Count == 1 && r.ItemPrefabs[0] == requiredItem);
                        if (existing == null)
                        {
                            RequiredItems.Add(new RequiredItem(requiredItem, count, minCondition, useCondition));
                        }
                        else
                        {
                            existing.Amount += count;
                        }
                    }
                    else
                    {
                        var matchingItems = ItemPrefab.Prefabs.Where(ip => ip.Tags.Any(t => t.Equals(requiredItemTag, StringComparison.OrdinalIgnoreCase)));
                        if (!matchingItems.Any())
                        {
                            DebugConsole.ThrowError("Error in fabricable item " + itemPrefab.Name + "! Could not find any items with the tag \"" + requiredItemTag + "\".");
                            continue;
                        }

                        var existing = RequiredItems.Find(r => r.ItemPrefabs.SequenceEqual(matchingItems));
                        if (existing == null)
                        {
                            RequiredItems.Add(new RequiredItem(matchingItems, count, minCondition, useCondition));
                        }
                        else
                        {
                            existing.Amount += count;
                        }
                    }
                    break;
                }
            }
        }
        protected override void Act(float deltaTime)
        {
            var extinguisherItem = character.Inventory.FindItemByIdentifier("extinguisher") ?? character.Inventory.FindItemByTag("extinguisher");

            if (extinguisherItem == null || extinguisherItem.Condition <= 0.0f || !character.HasEquippedItem(extinguisherItem))
            {
                if (getExtinguisherObjective == null)
                {
                    character.Speak(TextManager.Get("DialogFindExtinguisher"), null, 2.0f, "findextinguisher", 30.0f);
                    getExtinguisherObjective = new AIObjectiveGetItem(character, "extinguisher", true);
                }
                else
                {
                    getExtinguisherObjective.TryComplete(deltaTime);
                }

                return;
            }

            var extinguisher = extinguisherItem.GetComponent <RepairTool>();

            if (extinguisher == null)
            {
                DebugConsole.ThrowError("AIObjectiveExtinguishFire failed - the item \"" + extinguisherItem + "\" has no RepairTool component but is tagged as an extinguisher");
                return;
            }

            foreach (FireSource fs in targetHull.FireSources)
            {
                bool inRange = fs.IsInDamageRange(character, MathHelper.Clamp(fs.DamageRange * 1.5f, extinguisher.Range * 0.5f, extinguisher.Range));
                if (targetHull == character.CurrentHull && (inRange || useExtinquisherTimer > 0.0f))
                {
                    useExtinquisherTimer += deltaTime;
                    if (useExtinquisherTimer > 2.0f)
                    {
                        useExtinquisherTimer = 0.0f;
                    }

                    character.AIController.SteeringManager.Reset();
                    character.CursorPosition = fs.Position;
                    if (extinguisher.Item.RequireAimToUse)
                    {
                        character.SetInput(InputType.Aim, false, true);
                    }
                    extinguisher.Use(deltaTime, character);

                    if (!targetHull.FireSources.Contains(fs))
                    {
                        character.Speak(TextManager.Get("DialogPutOutFire").Replace("[roomname]", targetHull.Name), null, 0, "putoutfire", 10.0f);
                    }
                    return;
                }
                else
                {
                    //go to the first firesource
                    if (gotoObjective == null || !gotoObjective.CanBeCompleted || gotoObjective.IsCompleted())
                    {
                        gotoObjective = new AIObjectiveGoTo(ConvertUnits.ToSimUnits(fs.Position), character);
                    }
                    else
                    {
                        gotoObjective.TryComplete(deltaTime);
                    }
                    break;
                }
            }
        }
Esempio n. 5
0
        public ItemPrefab(XElement element, string filePath, bool allowOverriding)
        {
            FilePath      = filePath;
            ConfigElement = element;

            originalName = element.GetAttributeString("name", "");
            name         = originalName;
            identifier   = element.GetAttributeString("identifier", "");
            if (!Enum.TryParse(element.GetAttributeString("category", "Misc"), true, out MapEntityCategory category))
            {
                category = MapEntityCategory.Misc;
            }
            Category = category;

            var parentType = element.Parent?.GetAttributeString("itemtype", "") ?? string.Empty;

            //nameidentifier can be used to make multiple items use the same names and descriptions
            string nameIdentifier = element.GetAttributeString("nameidentifier", "");

            //works the same as nameIdentifier, but just replaces the description
            string descriptionIdentifier = element.GetAttributeString("descriptionidentifier", "");

            if (string.IsNullOrEmpty(originalName))
            {
                if (string.IsNullOrEmpty(nameIdentifier))
                {
                    name = TextManager.Get("EntityName." + identifier, true) ?? string.Empty;
                }
                else
                {
                    name = TextManager.Get("EntityName." + nameIdentifier, true) ?? string.Empty;
                }
            }
            else if (Category.HasFlag(MapEntityCategory.Legacy))
            {
                // Legacy items use names as identifiers, so we have to define them in the xml. But we also want to support the translations. Therefore
                if (string.IsNullOrEmpty(nameIdentifier))
                {
                    name = TextManager.Get("EntityName." + identifier, true) ?? originalName;
                }
                else
                {
                    name = TextManager.Get("EntityName." + nameIdentifier, true) ?? originalName;
                }

                if (string.IsNullOrWhiteSpace(identifier))
                {
                    identifier = GenerateLegacyIdentifier(originalName);
                }
            }

            if (string.Equals(parentType, "wrecked", StringComparison.OrdinalIgnoreCase))
            {
                if (!string.IsNullOrEmpty(name))
                {
                    name = TextManager.GetWithVariable("wreckeditemformat", "[name]", name);
                }
            }

            if (string.IsNullOrEmpty(name))
            {
                DebugConsole.ThrowError($"Unnamed item ({identifier}) in {filePath}!");
            }

            DebugConsole.Log("    " + name);

            Aliases = new HashSet <string>
                          (element.GetAttributeStringArray("aliases", null, convertToLowerInvariant: true) ??
                          element.GetAttributeStringArray("Aliases", new string[0], convertToLowerInvariant: true));
            Aliases.Add(originalName.ToLowerInvariant());

            Triggers           = new List <Rectangle>();
            DeconstructItems   = new List <DeconstructItem>();
            FabricationRecipes = new List <FabricationRecipe>();
            DeconstructTime    = 1.0f;

            Tags = new HashSet <string>(element.GetAttributeStringArray("tags", new string[0], convertToLowerInvariant: true));
            if (!Tags.Any())
            {
                Tags = new HashSet <string>(element.GetAttributeStringArray("Tags", new string[0], convertToLowerInvariant: true));
            }

            if (element.Attribute("cargocontainername") != null)
            {
                DebugConsole.ThrowError("Error in item prefab \"" + name + "\" - cargo container should be configured using the item's identifier, not the name.");
            }

            SerializableProperty.DeserializeProperties(this, element);

            if (string.IsNullOrEmpty(Description))
            {
                if (!string.IsNullOrEmpty(descriptionIdentifier))
                {
                    Description = TextManager.Get("EntityDescription." + descriptionIdentifier, true) ?? string.Empty;
                }
                else if (string.IsNullOrEmpty(nameIdentifier))
                {
                    Description = TextManager.Get("EntityDescription." + identifier, true) ?? string.Empty;
                }
                else
                {
                    Description = TextManager.Get("EntityDescription." + nameIdentifier, true) ?? string.Empty;
                }
            }

            foreach (XElement subElement in element.Elements())
            {
                switch (subElement.Name.ToString().ToLowerInvariant())
                {
                case "sprite":
                    string spriteFolder = "";
                    if (!subElement.GetAttributeString("texture", "").Contains("/"))
                    {
                        spriteFolder = Path.GetDirectoryName(filePath);
                    }

                    CanSpriteFlipX = subElement.GetAttributeBool("canflipx", true);
                    CanSpriteFlipY = subElement.GetAttributeBool("canflipy", true);

                    sprite = new Sprite(subElement, spriteFolder, lazyLoad: true);
                    if (subElement.Attribute("sourcerect") == null)
                    {
                        DebugConsole.ThrowError("Warning - sprite sourcerect not configured for item \"" + Name + "\"!");
                    }
                    size = sprite.size;

                    if (subElement.Attribute("name") == null && !string.IsNullOrWhiteSpace(Name))
                    {
                        sprite.Name = Name;
                    }
                    sprite.EntityID = identifier;
                    break;

                case "price":
                    if (locationPrices == null)
                    {
                        locationPrices = new Dictionary <string, PriceInfo>();
                    }
                    if (subElement.Attribute("baseprice") != null)
                    {
                        foreach (Tuple <string, PriceInfo> priceInfo in PriceInfo.CreatePriceInfos(subElement, out defaultPrice))
                        {
                            if (priceInfo == null)
                            {
                                continue;
                            }
                            locationPrices.Add(priceInfo.Item1, priceInfo.Item2);
                        }
                    }
                    else if (subElement.Attribute("buyprice") != null)
                    {
                        string locationType = subElement.GetAttributeString("locationtype", "").ToLowerInvariant();
                        locationPrices.Add(locationType, new PriceInfo(subElement));
                    }
                    break;

                case "upgradeoverride":
                {
#if CLIENT
                    var sprites = new List <DecorativeSprite>();
                    foreach (XElement decorSprite in subElement.Elements())
                    {
                        if (decorSprite.Name.ToString().Equals("decorativesprite", StringComparison.OrdinalIgnoreCase))
                        {
                            sprites.Add(new DecorativeSprite(decorSprite));
                        }
                    }
                    UpgradeOverrideSprites.Add(subElement.GetAttributeString("identifier", ""), sprites);
#endif
                    break;
                }

#if CLIENT
                case "inventoryicon":
                {
                    string iconFolder = "";
                    if (!subElement.GetAttributeString("texture", "").Contains("/"))
                    {
                        iconFolder = Path.GetDirectoryName(filePath);
                    }
                    InventoryIcon = new Sprite(subElement, iconFolder, lazyLoad: true);
                }
                break;

                case "minimapicon":
                {
                    string iconFolder = "";
                    if (!subElement.GetAttributeString("texture", "").Contains("/"))
                    {
                        iconFolder = Path.GetDirectoryName(filePath);
                    }
                    MinimapIcon = new Sprite(subElement, iconFolder, lazyLoad: true);
                }
                break;

                case "infectedsprite":
                {
                    string iconFolder = "";
                    if (!subElement.GetAttributeString("texture", "").Contains("/"))
                    {
                        iconFolder = Path.GetDirectoryName(filePath);
                    }

                    InfectedSprite = new Sprite(subElement, iconFolder, lazyLoad: true);
                }
                break;

                case "damagedinfectedsprite":
                {
                    string iconFolder = "";
                    if (!subElement.GetAttributeString("texture", "").Contains("/"))
                    {
                        iconFolder = Path.GetDirectoryName(filePath);
                    }

                    DamagedInfectedSprite = new Sprite(subElement, iconFolder, lazyLoad: true);
                }
                break;

                case "brokensprite":
                    string brokenSpriteFolder = "";
                    if (!subElement.GetAttributeString("texture", "").Contains("/"))
                    {
                        brokenSpriteFolder = Path.GetDirectoryName(filePath);
                    }

                    var brokenSprite = new BrokenItemSprite(
                        new Sprite(subElement, brokenSpriteFolder, lazyLoad: true),
                        subElement.GetAttributeFloat("maxcondition", 0.0f),
                        subElement.GetAttributeBool("fadein", false),
                        subElement.GetAttributePoint("offset", Point.Zero));

                    int spriteIndex = 0;
                    for (int i = 0; i < BrokenSprites.Count && BrokenSprites[i].MaxCondition < brokenSprite.MaxCondition; i++)
                    {
                        spriteIndex = i;
                    }
                    BrokenSprites.Insert(spriteIndex, brokenSprite);
                    break;

                case "decorativesprite":
                    string decorativeSpriteFolder = "";
                    if (!subElement.GetAttributeString("texture", "").Contains("/"))
                    {
                        decorativeSpriteFolder = Path.GetDirectoryName(filePath);
                    }

                    int groupID = 0;
                    DecorativeSprite decorativeSprite = null;
                    if (subElement.Attribute("texture") == null)
                    {
                        groupID = subElement.GetAttributeInt("randomgroupid", 0);
                    }
                    else
                    {
                        decorativeSprite = new DecorativeSprite(subElement, decorativeSpriteFolder, lazyLoad: true);
                        DecorativeSprites.Add(decorativeSprite);
                        groupID = decorativeSprite.RandomGroupID;
                    }
                    if (!DecorativeSpriteGroups.ContainsKey(groupID))
                    {
                        DecorativeSpriteGroups.Add(groupID, new List <DecorativeSprite>());
                    }
                    DecorativeSpriteGroups[groupID].Add(decorativeSprite);

                    break;

                case "containedsprite":
                    string containedSpriteFolder = "";
                    if (!subElement.GetAttributeString("texture", "").Contains("/"))
                    {
                        containedSpriteFolder = Path.GetDirectoryName(filePath);
                    }
                    var containedSprite = new ContainedItemSprite(subElement, containedSpriteFolder, lazyLoad: true);
                    if (containedSprite.Sprite != null)
                    {
                        ContainedSprites.Add(containedSprite);
                    }
                    break;
#endif
                case "deconstruct":
                    DeconstructTime  = subElement.GetAttributeFloat("time", 1.0f);
                    AllowDeconstruct = true;
                    foreach (XElement deconstructItem in subElement.Elements())
                    {
                        if (deconstructItem.Attribute("name") != null)
                        {
                            DebugConsole.ThrowError("Error in item config \"" + Name + "\" - use item identifiers instead of names to configure the deconstruct items.");
                            continue;
                        }

                        DeconstructItems.Add(new DeconstructItem(deconstructItem));
                    }

                    break;

                case "fabricate":
                case "fabricable":
                case "fabricableitem":
                    fabricationRecipeElements.Add(subElement);
                    break;

                case "preferredcontainer":
                    var preferredContainer = new PreferredContainer(subElement);
                    if (preferredContainer.Primary.Count == 0 && preferredContainer.Secondary.Count == 0)
                    {
                        DebugConsole.ThrowError($"Error in item prefab {Name}: preferred container has no preferences defined ({subElement.ToString()}).");
                    }
                    else
                    {
                        PreferredContainers.Add(preferredContainer);
                    }
                    break;

                case "trigger":
                    Rectangle trigger = new Rectangle(0, 0, 10, 10)
                    {
                        X      = subElement.GetAttributeInt("x", 0),
                        Y      = subElement.GetAttributeInt("y", 0),
                        Width  = subElement.GetAttributeInt("width", 0),
                        Height = subElement.GetAttributeInt("height", 0)
                    };

                    Triggers.Add(trigger);

                    break;

                case "levelresource":
                    foreach (XElement levelCommonnessElement in subElement.GetChildElements("commonness"))
                    {
                        string levelName = levelCommonnessElement.GetAttributeString("leveltype", "").ToLowerInvariant();
                        if (!levelCommonnessElement.GetAttributeBool("fixedquantity", false))
                        {
                            if (!LevelCommonness.ContainsKey(levelName))
                            {
                                LevelCommonness.Add(levelName, levelCommonnessElement.GetAttributeFloat("commonness", 0.0f));
                            }
                        }
                        else
                        {
                            if (!LevelQuantity.ContainsKey(levelName))
                            {
                                LevelQuantity.Add(levelName, new FixedQuantityResourceInfo(
                                                      levelCommonnessElement.GetAttributeInt("clusterquantity", 0),
                                                      levelCommonnessElement.GetAttributeInt("clustersize", 0),
                                                      levelCommonnessElement.GetAttributeBool("isislandspecific", false)));
                            }
                        }
                    }
                    break;

                case "suitabletreatment":
                    if (subElement.Attribute("name") != null)
                    {
                        DebugConsole.ThrowError("Error in item prefab \"" + Name + "\" - suitable treatments should be defined using item identifiers, not item names.");
                    }

                    string treatmentIdentifier = subElement.GetAttributeString("identifier", "").ToLowerInvariant();

                    float suitability = subElement.GetAttributeFloat("suitability", 0.0f);

                    treatmentSuitability.Add(treatmentIdentifier, suitability);
                    break;
                }
            }

            // Set the default price in case the prices are defined in the old way
            // with separate Price elements and there is no default price explicitly set
            if (locationPrices != null && locationPrices.Any())
            {
                defaultPrice ??= new PriceInfo(GetMinPrice() ?? 0, false);
            }

            if (sprite == null)
            {
                DebugConsole.ThrowError("Item \"" + Name + "\" has no sprite!");
#if SERVER
                sprite            = new Sprite("", Vector2.Zero);
                sprite.SourceRect = new Rectangle(0, 0, 32, 32);
#else
                sprite = new Sprite(TextureLoader.PlaceHolderTexture, null, null)
                {
                    Origin = TextureLoader.PlaceHolderTexture.Bounds.Size.ToVector2() / 2
                };
#endif
                size            = sprite.size;
                sprite.EntityID = identifier;
            }

            if (string.IsNullOrEmpty(identifier))
            {
                DebugConsole.ThrowError(
                    "Item prefab \"" + name + "\" has no identifier. All item prefabs have a unique identifier string that's used to differentiate between items during saving and loading.");
            }

#if DEBUG
            if (!Category.HasFlag(MapEntityCategory.Legacy) && !HideInMenus)
            {
                if (!string.IsNullOrEmpty(originalName))
                {
                    DebugConsole.AddWarning($"Item \"{(string.IsNullOrEmpty(identifier) ? name : identifier)}\" has a hard-coded name, and won't be localized to other languages.");
                }
            }
#endif

            AllowedLinks = element.GetAttributeStringArray("allowedlinks", new string[0], convertToLowerInvariant: true).ToList();

            Prefabs.Add(this, allowOverriding);
        }