using Barotrauma.Networking; using Lidgren.Network; 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; } } } } partial class Fabricator : Powered, IServerSerializable, IClientSerializable { private List fabricableItems; 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; 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); } InitProjSpecific(); } public override bool Select(Character character) { CheckFabricableItems(character); #if CLIENT if (itemList.Selected != null) { SelectItem(itemList.Selected, itemList.Selected.UserData); } #endif 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) { #if CLIENT 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); } #endif var itemContainer = item.GetComponent(); prevContainedItems = new Item[itemContainer.Inventory.Items.Length]; itemContainer.Inventory.Items.CopyTo(prevContainedItems, 0); } private void StartFabricating(FabricableItem selectedItem, Character user = null) { if (selectedItem == null) return; if (user != null) { GameServer.Log(user.Name + " started fabricating " + selectedItem.TargetItem.Name + " in " + item.Name, ServerLog.MessageType.ItemInteraction); } #if CLIENT itemList.Enabled = false; activateButton.Text = "Cancel"; #endif 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(Character user = null) { if (fabricatedItem != null && user != null) { GameServer.Log(user.Name + " cancelled the fabrication of " + fabricatedItem.TargetItem.Name + " in " + item.Name, ServerLog.MessageType.ItemInteraction); } IsActive = false; fabricatedItem = null; currPowerConsumption = 0.0f; #if CLIENT itemList.Enabled = true; if (activateButton != null) { activateButton.Text = "Create"; } if (progressBar != null) progressBar.BarSize = 0.0f; #endif 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 CLIENT if (progressBar!=null) { progressBar.BarSize = fabricatedItem == null ? 0.0f : (fabricatedItem.RequiredTime - timeUntilReady) / fabricatedItem.RequiredTime; } #endif 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 < ip.Item2; i++) { var requiredItem = containers[0].Inventory.Items.FirstOrDefault(it => it != null && it.Prefab == ip.Item1); if (requiredItem == null) continue; Entity.Spawner.AddToRemoveQueue(requiredItem); containers[0].Inventory.RemoveItem(requiredItem); } } if (containers[1].Inventory.Items.All(i => i != null)) { Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, item.Position, item.Submarine); } else { Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, containers[1].Inventory); } CancelFabricating(null); } 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 void ServerRead(ClientNetObject type, NetBuffer msg, Client c) { int itemIndex = msg.ReadRangedInteger(-1, fabricableItems.Count - 1); item.CreateServerEvent(this); if (!item.CanClientAccess(c)) return; if (itemIndex == -1) { CancelFabricating(c.Character); } else { //if already fabricating the selected item, return if (fabricatedItem != null && fabricableItems.IndexOf(fabricatedItem) == itemIndex) return; if (itemIndex < 0 || itemIndex >= fabricableItems.Count) return; #if CLIENT SelectItem(null, fabricableItems[itemIndex]); #endif StartFabricating(fabricableItems[itemIndex], c.Character); } } public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null) { int itemIndex = fabricatedItem == null ? -1 : fabricableItems.IndexOf(fabricatedItem); msg.WriteRangedInteger(-1, fabricableItems.Count - 1, itemIndex); } } }