using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Xml.Linq; namespace Barotrauma.Items.Components { class FabricableItem { public readonly ItemPrefab TargetItem; public readonly List> RequiredItems; public readonly float RequiredTime; public readonly List RequiredSkills; public FabricableItem(XElement element) { string name = ToolBox.GetAttributeString(element, "name", ""); TargetItem = ItemPrefab.list.Find(ip => ip.Name.ToLowerInvariant() == name.ToLowerInvariant()) as ItemPrefab; if (TargetItem == null) { DebugConsole.ThrowError("Error in fabricable item "+name+"! Item \"" + element.Name + "\" not found."); return; } RequiredSkills = new List(); RequiredTime = ToolBox.GetAttributeFloat(element, "requiredtime", 1.0f); RequiredItems = new List>(); string[] requiredItemNames = ToolBox.GetAttributeString(element, "requireditems", "").Split(','); foreach (string requiredItemName in requiredItemNames) { if (string.IsNullOrWhiteSpace(requiredItemName)) continue; ItemPrefab requiredItem = ItemPrefab.list.Find(ip => ip.Name.ToLowerInvariant() == requiredItemName.Trim().ToLowerInvariant()) as ItemPrefab; if (requiredItem == null) { DebugConsole.ThrowError("Error in fabricable item " + name + "! Required item \"" + requiredItemName + "\" not found."); continue; } var existing = RequiredItems.Find(r => r.Item1 == requiredItem); if (existing == null) { RequiredItems.Add(new Tuple(requiredItem, 1)); } else { RequiredItems.Remove(existing); RequiredItems.Add(new Tuple(requiredItem, existing.Item2+1)); } } foreach (XElement subElement in element.Elements()) { switch (subElement.Name.ToString().ToLowerInvariant()) { case "requiredskill": RequiredSkills.Add(new Skill( ToolBox.GetAttributeString(subElement, "name", ""), ToolBox.GetAttributeInt(subElement, "level", 0))); break; } } } } class Fabricator : Powered { private List fabricableItems; private GUIListBox itemList; private GUIFrame selectedItemFrame; private GUIProgressBar progressBar; private GUIButton activateButton; private FabricableItem fabricatedItem; private float timeUntilReady; //used for checking if contained items have changed //(in which case we need to recheck which items can be fabricated) private Item[] prevContainedItems; private float lastNetworkUpdate; public Fabricator(Item item, XElement element) : base(item, element) { fabricableItems = new List(); foreach (XElement subElement in element.Elements()) { if (subElement.Name.ToString() != "fabricableitem") continue; FabricableItem fabricableItem = new FabricableItem(subElement); if (fabricableItem.TargetItem != null) fabricableItems.Add(fabricableItem); } GuiFrame.Padding = new Vector4(20.0f, 20.0f, 20.0f, 20.0f); itemList = new GUIListBox(new Rectangle(0,0,GuiFrame.Rect.Width/2-20,0), GUI.Style, GuiFrame); itemList.OnSelected = SelectItem; foreach (FabricableItem fi in fabricableItems) { GUIFrame frame = new GUIFrame(new Rectangle(0, 0, 0, 50), Color.Transparent, null, itemList) { UserData = fi, Padding = new Vector4(5.0f, 5.0f, 5.0f, 5.0f), HoverColor = Color.Gold * 0.2f, SelectedColor = Color.Gold * 0.5f, ToolTip = fi.TargetItem.Description }; GUITextBlock textBlock = new GUITextBlock( new Rectangle(40, 0, 0, 25), fi.TargetItem.Name, Color.Transparent, Color.White, Alignment.Left, Alignment.Left, null, frame); textBlock.ToolTip = fi.TargetItem.Description; textBlock.Padding = new Vector4(5.0f, 0.0f, 5.0f, 0.0f); if (fi.TargetItem.sprite != null) { GUIImage img = new GUIImage(new Rectangle(0, 0, 40, 40), fi.TargetItem.sprite, Alignment.Left, frame); img.Scale = Math.Min(Math.Min(40.0f / img.SourceRect.Width, 40.0f / img.SourceRect.Height), 1.0f); img.Color = fi.TargetItem.SpriteColor; img.ToolTip = fi.TargetItem.Description; } } } private bool SelectItem(GUIComponent component, object obj) { FabricableItem targetItem = obj as FabricableItem; if (targetItem == null) return false; if (selectedItemFrame != null) GuiFrame.RemoveChild(selectedItemFrame); //int width = 200, height = 150; selectedItemFrame = new GUIFrame(new Rectangle(0, 0, (int)(GuiFrame.Rect.Width * 0.4f), 300), Color.Black * 0.8f, Alignment.CenterY | Alignment.Right, null, GuiFrame); selectedItemFrame.Padding = new Vector4(10.0f, 10.0f, 10.0f, 10.0f); progressBar = new GUIProgressBar(new Rectangle(0, 0, 0, 20), Color.Green, GUI.Style, 0.0f, Alignment.BottomCenter, selectedItemFrame); progressBar.IsHorizontal = true; if (targetItem.TargetItem.sprite != null) { int y = 0; GUIImage img = new GUIImage(new Rectangle(10, 0, 40, 40), targetItem.TargetItem.sprite, Alignment.TopLeft, selectedItemFrame); img.Scale = Math.Min(Math.Min(40.0f / img.SourceRect.Width, 40.0f / img.SourceRect.Height), 1.0f); img.Color = targetItem.TargetItem.SpriteColor; new GUITextBlock( new Rectangle(60, 0, 0, 25), targetItem.TargetItem.Name, Color.Transparent, Color.White, Alignment.TopLeft, Alignment.TopLeft, null, selectedItemFrame, true); y += 40; if (!string.IsNullOrWhiteSpace(targetItem.TargetItem.Description)) { var description = new GUITextBlock( new Rectangle(0, y, 0, 0), targetItem.TargetItem.Description, GUI.Style, Alignment.TopLeft, Alignment.TopLeft, selectedItemFrame, true, GUI.SmallFont); y += description.Rect.Height + 10; } List inadequateSkills = new List(); if (Character.Controlled != null) { inadequateSkills = targetItem.RequiredSkills.FindAll(skill => Character.Controlled.GetSkillLevel(skill.Name) < skill.Level); } Color textColor = Color.White; string text; if (!inadequateSkills.Any()) { text = "Required items:\n"; foreach (Tuple ip in targetItem.RequiredItems) { text += " - " + ip.Item1.Name + " x"+ip.Item2+"\n"; } text += "Required time: " + targetItem.RequiredTime + " s"; } else { text = "Skills required to calibrate:\n"; foreach (Skill skill in inadequateSkills) { text += " - " + skill.Name + " lvl " + skill.Level + "\n"; } textColor = Color.Red; } new GUITextBlock( new Rectangle(0, y, 0, 25), text, Color.Transparent, textColor, Alignment.TopLeft, Alignment.TopLeft, null, selectedItemFrame); activateButton = new GUIButton(new Rectangle(0, -30, 100, 20), "Create", Color.White, Alignment.CenterX | Alignment.Bottom, GUI.Style, selectedItemFrame); activateButton.OnClicked = StartButtonClicked; activateButton.UserData = targetItem; activateButton.Enabled = false; } return true; } public override bool Select(Character character) { CheckFabricableItems(character); if (itemList.Selected != null) { SelectItem(itemList.Selected, itemList.Selected.UserData); } return base.Select(character); } public override bool Pick(Character picker) { return (picker != null); } /// /// check which of the items can be fabricated by the character /// and update the text colors of the item list accordingly /// private void CheckFabricableItems(Character character) { foreach (GUIComponent child in itemList.children) { var itemPrefab = child.UserData as FabricableItem; if (itemPrefab == null) continue; bool canBeFabricated = CanBeFabricated(itemPrefab, character); child.GetChild().TextColor = Color.White * (canBeFabricated ? 1.0f : 0.5f); child.GetChild().Color = itemPrefab.TargetItem.SpriteColor * (canBeFabricated ? 1.0f : 0.5f); } var itemContainer = item.GetComponent(); prevContainedItems = new Item[itemContainer.Inventory.Items.Length]; itemContainer.Inventory.Items.CopyTo(prevContainedItems, 0); } private bool StartButtonClicked(GUIButton button, object obj) { if (fabricatedItem == null) { StartFabricating(obj as FabricableItem); item.NewComponentEvent(this, true, true); } else { CancelFabricating(); item.NewComponentEvent(this, true, true); } return true; } private void StartFabricating(FabricableItem selectedItem) { if (selectedItem == null) return; itemList.Enabled = false; activateButton.Text = "Cancel"; fabricatedItem = selectedItem; IsActive = true; timeUntilReady = fabricatedItem.RequiredTime; var containers = item.GetComponents(); containers[0].Inventory.Locked = true; containers[1].Inventory.Locked = true; currPowerConsumption = powerConsumption; } private void CancelFabricating() { itemList.Enabled = true; IsActive = false; fabricatedItem = null; currPowerConsumption = 0.0f; if (activateButton != null) { activateButton.Text = "Create"; } if (progressBar != null) progressBar.BarSize = 0.0f; timeUntilReady = 0.0f; var containers = item.GetComponents(); containers[0].Inventory.Locked = false; containers[1].Inventory.Locked = false; } public override void Update(float deltaTime, Camera cam) { if (progressBar!=null) { progressBar.BarSize = fabricatedItem == null ? 0.0f : (fabricatedItem.RequiredTime - timeUntilReady) / fabricatedItem.RequiredTime; } if (voltage < minVoltage) return; if (powerConsumption == 0) voltage = 1.0f; timeUntilReady -= deltaTime*voltage; voltage -= deltaTime * 10.0f; if (timeUntilReady > 0.0f) return; var containers = item.GetComponents(); if (containers.Count<2) { DebugConsole.ThrowError("Error while fabricating a new item: fabricators must have two ItemContainer components"); return; } foreach (Tuple ip in fabricatedItem.RequiredItems) { for (int i = 0; i it != null && it.Prefab == ip.Item1); if (requiredItem == null) continue; Item.Remover.QueueItem(requiredItem); containers[0].Inventory.RemoveItem(requiredItem); } } Item.Spawner.QueueItem(fabricatedItem.TargetItem, containers[1].Inventory); CancelFabricating(); } public override void DrawHUD(SpriteBatch spriteBatch, Character character) { GuiFrame.Draw(spriteBatch); } public override void AddToGUIUpdateList() { GuiFrame.AddToGUIUpdateList(); } public override void UpdateHUD(Character character) { FabricableItem targetItem = itemList.SelectedData as FabricableItem; if (targetItem != null) { activateButton.Enabled = CanBeFabricated(targetItem, character); } if (character != null) { bool itemsChanged = false; if (prevContainedItems == null) { itemsChanged = true; } else { var itemContainer = item.GetComponent(); for (int i = 0; i < itemContainer.Inventory.Items.Length; i++) { if (prevContainedItems[i] != itemContainer.Inventory.Items[i]) { itemsChanged = true; break; } } } if (itemsChanged) CheckFabricableItems(character); } GuiFrame.Update((float)Timing.Step); } private bool CanBeFabricated(FabricableItem fabricableItem, Character user) { if (fabricableItem == null) return false; if (user != null && fabricableItem.RequiredSkills.Any(skill => user.GetSkillLevel(skill.Name) < skill.Level)) { return false; } ItemContainer container = item.GetComponent(); foreach (Tuple ip in fabricableItem.RequiredItems) { if (Array.FindAll(container.Inventory.Items, it => it != null && it.Prefab == ip.Item1).Length < ip.Item2) return false; } return true; } public override bool FillNetworkData(Networking.NetworkEventType type, Lidgren.Network.NetBuffer message) { int itemIndex = fabricatedItem == null ? -1 : fabricableItems.IndexOf(fabricatedItem); message.WriteRangedInteger(-1, fabricableItems.Count-1, itemIndex); var containers = item.GetComponents(); containers[0].Inventory.FillNetworkData(type, message, null); containers[1].Inventory.FillNetworkData(type, message, null); return true; } public override void ReadNetworkData(Networking.NetworkEventType type, Lidgren.Network.NetIncomingMessage message, float sendingTime) { if (sendingTime < lastNetworkUpdate) return; int itemIndex = message.ReadRangedInteger(-1, fabricableItems.Count-1); var containers = item.GetComponents(); containers[0].Inventory.ReadNetworkData(type, message, sendingTime); containers[1].Inventory.ReadNetworkData(type, message, sendingTime); if (itemIndex == -1) { CancelFabricating(); } else { //if already fabricating the selected item, return if (fabricatedItem != null && fabricableItems.IndexOf(fabricatedItem) != itemIndex) return; if (itemIndex < 0 || itemIndex >= fabricableItems.Count) return; SelectItem(null, fabricableItems[itemIndex]); StartFabricating(fabricableItems[itemIndex]); } lastNetworkUpdate = sendingTime; } } }