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)); } }
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)); } }
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; } } }
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); }