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 "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.Elements()) { string levelName = levelCommonnessElement.GetAttributeString("levelname", "").ToLowerInvariant(); if (!LevelCommonness.ContainsKey(levelName)) { LevelCommonness.Add(levelName, levelCommonnessElement.GetAttributeFloat("commonness", 0.0f)); } } 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."); } AllowedLinks = element.GetAttributeStringArray("allowedlinks", new string[0], convertToLowerInvariant: true).ToList(); Prefabs.Add(this, allowOverriding); }
private void CreateItemFrame(PurchasedItem pi, PriceInfo priceInfo, GUIListBox listBox, int width) { GUIFrame frame = new GUIFrame(new RectTransform(new Point(listBox.Content.Rect.Width, (int)(GUI.Scale * 50)), listBox.Content.RectTransform), style: "ListBoxElement") { UserData = pi, ToolTip = pi.ItemPrefab.Description }; var content = new GUILayoutGroup(new RectTransform(Vector2.One, frame.RectTransform), isHorizontal: true) { RelativeSpacing = 0.02f, Stretch = true }; ScalableFont font = listBox.Rect.Width < 280 ? GUI.SmallFont : GUI.Font; Sprite itemIcon = pi.ItemPrefab.InventoryIcon ?? pi.ItemPrefab.sprite; if (itemIcon != null) { GUIImage img = new GUIImage(new RectTransform(new Point((int)(content.Rect.Height * 0.8f)), content.RectTransform, Anchor.CenterLeft), itemIcon, scaleToFit: true) { Color = itemIcon == pi.ItemPrefab.InventoryIcon ? pi.ItemPrefab.InventoryIconColor : pi.ItemPrefab.SpriteColor }; img.RectTransform.MaxSize = img.Rect.Size; //img.Scale = Math.Min(Math.Min(40.0f / img.SourceRect.Width, 40.0f / img.SourceRect.Height), 1.0f); } GUITextBlock textBlock = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), content.RectTransform), pi.ItemPrefab.Name, font: font) { ToolTip = pi.ItemPrefab.Description }; new GUITextBlock(new RectTransform(new Vector2(0.2f, 1.0f), content.RectTransform), priceInfo.BuyPrice.ToString(), font: font, textAlignment: Alignment.CenterRight) { ToolTip = pi.ItemPrefab.Description }; //If its the store menu, quantity will always be 0 GUINumberInput amountInput = null; if (pi.Quantity > 0) { amountInput = new GUINumberInput(new RectTransform(new Vector2(0.3f, 1.0f), content.RectTransform), GUINumberInput.NumberType.Int) { MinValueInt = 0, MaxValueInt = 100, UserData = pi, IntValue = pi.Quantity }; amountInput.OnValueChanged += (numberInput) => { PurchasedItem purchasedItem = numberInput.UserData as PurchasedItem; //Attempting to buy if (numberInput.IntValue > purchasedItem.Quantity) { int quantity = numberInput.IntValue - purchasedItem.Quantity; //Cap the numberbox based on the amount we can afford. quantity = Campaign.Money <= 0 ? 0 : Math.Min((int)(Campaign.Money / (float)priceInfo.BuyPrice), quantity); for (int i = 0; i < quantity; i++) { BuyItem(numberInput, purchasedItem); } numberInput.IntValue = purchasedItem.Quantity; } //Attempting to sell else { int quantity = purchasedItem.Quantity - numberInput.IntValue; for (int i = 0; i < quantity; i++) { SellItem(numberInput, purchasedItem); } } }; } listBox.RecalculateChildren(); content.Recalculate(); content.RectTransform.RecalculateChildren(true, true); amountInput?.LayoutGroup.Recalculate(); textBlock.Text = ToolBox.LimitString(textBlock.Text, textBlock.Font, textBlock.Rect.Width); }
public static List <Tuple <string, PriceInfo> > CreatePriceInfos(XElement element, out PriceInfo defaultPrice) { defaultPrice = null; var basePrice = element.GetAttributeInt("baseprice", 0); var soldByDefault = element.GetAttributeBool("soldbydefault", true); var minAmount = GetMinAmount(element); var maxAmount = GetMaxAmount(element); var canBeSpecial = element.GetAttributeBool("canbespecial", true); var priceInfos = new List <Tuple <string, PriceInfo> >(); foreach (XElement childElement in element.GetChildElements("price")) { var priceMultiplier = childElement.GetAttributeFloat("multiplier", 1.0f); var sold = childElement.GetAttributeBool("sold", soldByDefault); priceInfos.Add(new Tuple <string, PriceInfo>(childElement.GetAttributeString("locationtype", "").ToLowerInvariant(), new PriceInfo(price: (int)(priceMultiplier * basePrice), canBeBought: sold, minAmount: sold ? GetMinAmount(childElement, minAmount) : 0, maxAmount: sold ? GetMaxAmount(childElement, maxAmount) : 0, canBeSpecial: canBeSpecial))); } var canBeBoughtAtOtherLocations = soldByDefault && element.GetAttributeBool("soldeverywhere", true); defaultPrice = new PriceInfo(basePrice, canBeBoughtAtOtherLocations, minAmount: canBeBoughtAtOtherLocations ? minAmount : 0, maxAmount: canBeBoughtAtOtherLocations ? maxAmount : 0, canBeSpecial: canBeSpecial); return(priceInfos); }
private void UpdateLocationView(Location location) { if (location == null) { string errorMsg = "Failed to update CampaignUI location view (location was null)\n" + Environment.StackTrace; DebugConsole.ThrowError(errorMsg); GameAnalyticsManager.AddErrorEventOnce("CampaignUI.UpdateLocationView:LocationNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); return; } if (characterPreviewFrame != null) { characterPreviewFrame.Parent.RemoveChild(characterPreviewFrame); characterPreviewFrame = null; } if (Campaign is SinglePlayerCampaign) { var hireableCharacters = location.GetHireableCharacters(); foreach (GUIComponent child in characterList.Content.Children.ToList()) { if (child.UserData is CharacterInfo character) { if (GameMain.GameSession.CrewManager.GetCharacterInfos().Contains(character)) { continue; } } else if (child.UserData as string == "mycrew" || child.UserData as string == "hire") { continue; } characterList.RemoveChild(child); } if (!hireableCharacters.Any()) { new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), characterList.Content.RectTransform), TextManager.Get("HireUnavailable"), textAlignment: Alignment.Center) { CanBeFocused = false }; } else { foreach (CharacterInfo c in hireableCharacters) { var frame = c.CreateCharacterFrame(characterList.Content, c.Name + " (" + c.Job.Name + ")", c); new GUITextBlock(new RectTransform(Vector2.One, frame.RectTransform, Anchor.TopRight), c.Salary.ToString(), textAlignment: Alignment.CenterRight); } } } characterList.UpdateScrollBarSize(); RefreshMyItems(); bool purchaseableItemsFound = false; foreach (MapEntityPrefab mapEntityPrefab in MapEntityPrefab.List) { if (!(mapEntityPrefab is ItemPrefab itemPrefab)) { continue; } PriceInfo priceInfo = itemPrefab.GetPrice(Campaign.Map.CurrentLocation); if (priceInfo != null) { purchaseableItemsFound = true; break; } } //disable store tab if there's nothing to buy tabButtons.Find(btn => (Tab)btn.UserData == Tab.Store).Enabled = purchaseableItemsFound; if (selectedTab == Tab.Store && !purchaseableItemsFound) { //switch out from store tab if there's nothing to buy SelectTab(Tab.Map); } else { //refresh store view FillStoreItemList(); FilterStoreItems(MapEntityCategory.Equipment, searchBox.Text); } }
private void CreateItemFrame(PurchasedItem pi, PriceInfo priceInfo, GUIListBox listBox, int width) { GUIFrame frame = new GUIFrame(new RectTransform(new Point(listBox.Rect.Width, 50), listBox.Content.RectTransform), style: "ListBoxElement") { UserData = pi, ToolTip = pi.ItemPrefab.Description }; ScalableFont font = listBox.Rect.Width < 280 ? GUI.SmallFont : GUI.Font; GUITextBlock textBlock = new GUITextBlock(new RectTransform(new Point(listBox.Rect.Width - (pi.Quantity > 0 ? 160 : 120), 25), frame.RectTransform, Anchor.CenterLeft) { AbsoluteOffset = new Point(40, 0), }, pi.ItemPrefab.Name, font: font) { ToolTip = pi.ItemPrefab.Description }; textBlock.Text = ToolBox.LimitString(textBlock.Text, textBlock.Font, textBlock.Rect.Width); Sprite itemIcon = pi.ItemPrefab.InventoryIcon ?? pi.ItemPrefab.sprite; if (itemIcon != null) { GUIImage img = new GUIImage(new RectTransform(new Point(40, 40), frame.RectTransform, Anchor.CenterLeft), itemIcon) { Color = itemIcon == pi.ItemPrefab.InventoryIcon ? pi.ItemPrefab.InventoryIconColor : pi.ItemPrefab.SpriteColor }; img.Scale = Math.Min(Math.Min(40.0f / img.SourceRect.Width, 40.0f / img.SourceRect.Height), 1.0f); } textBlock = new GUITextBlock(new RectTransform(new Point(60, 25), frame.RectTransform, Anchor.CenterRight) { AbsoluteOffset = new Point(pi.Quantity > 0 ? 70 : 25, 0) }, priceInfo.BuyPrice.ToString(), font: font, textAlignment: Alignment.CenterRight) { ToolTip = pi.ItemPrefab.Description }; //If its the store menu, quantity will always be 0 if (pi.Quantity > 0) { var amountInput = new GUINumberInput(new RectTransform(new Point(50, 40), frame.RectTransform, Anchor.CenterRight) { AbsoluteOffset = new Point(20, 0) }, GUINumberInput.NumberType.Int) { MinValueInt = 0, MaxValueInt = 1000, UserData = pi, IntValue = pi.Quantity }; amountInput.OnValueChanged += (numberInput) => { PurchasedItem purchasedItem = numberInput.UserData as PurchasedItem; //Attempting to buy if (numberInput.IntValue > purchasedItem.Quantity) { int quantity = numberInput.IntValue - purchasedItem.Quantity; //Cap the numberbox based on the amount we can afford. quantity = Campaign.Money <= 0 ? 0 : Math.Min((int)(Campaign.Money / (float)priceInfo.BuyPrice), quantity); for (int i = 0; i < quantity; i++) { BuyItem(numberInput, purchasedItem); } numberInput.IntValue = purchasedItem.Quantity; } //Attempting to sell else { int quantity = purchasedItem.Quantity - numberInput.IntValue; for (int i = 0; i < quantity; i++) { SellItem(numberInput, purchasedItem); } } }; } }