(b6f451292) Moved crafting recipes from the fabricator xml to the xmls of the items. Makes it possible for modders to add new craftable items without having to modify the fabricators. Closes #1001
This commit is contained in:
@@ -773,20 +773,12 @@ namespace Barotrauma
|
|||||||
|
|
||||||
commands.Add(new Command("checkcrafting", "checkcrafting: Checks item deconstruction & crafting recipes for inconsistencies.", (string[] args) =>
|
commands.Add(new Command("checkcrafting", "checkcrafting: Checks item deconstruction & crafting recipes for inconsistencies.", (string[] args) =>
|
||||||
{
|
{
|
||||||
List<FabricableItem> fabricableItems = new List<FabricableItem>();
|
List<FabricationRecipe> fabricableItems = new List<FabricationRecipe>();
|
||||||
foreach (MapEntityPrefab mapEntityPrefab in MapEntityPrefab.List)
|
foreach (MapEntityPrefab mapEntityPrefab in MapEntityPrefab.List)
|
||||||
{
|
{
|
||||||
if (mapEntityPrefab is ItemPrefab itemPrefab)
|
if (mapEntityPrefab is ItemPrefab itemPrefab)
|
||||||
{
|
{
|
||||||
var fabricatorElement = itemPrefab.ConfigElement.Element("Fabricator");
|
fabricableItems.AddRange(itemPrefab.FabricationRecipes);
|
||||||
if (fabricatorElement == null) { continue; }
|
|
||||||
|
|
||||||
foreach (XElement element in fabricatorElement.Elements())
|
|
||||||
{
|
|
||||||
if (element.Name.ToString().ToLowerInvariant() != "fabricableitem") { continue; }
|
|
||||||
fabricableItems.Add(new FabricableItem(element));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach (MapEntityPrefab mapEntityPrefab in MapEntityPrefab.List)
|
foreach (MapEntityPrefab mapEntityPrefab in MapEntityPrefab.List)
|
||||||
@@ -962,8 +954,6 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
element.Value = lines[i];
|
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
}, isCheat: false));
|
}, isCheat: false));
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -656,13 +656,6 @@ namespace Barotrauma
|
|||||||
msg.Timer -= deltaTime;
|
msg.Timer -= deltaTime;
|
||||||
msg.Pos += msg.Velocity * deltaTime;
|
msg.Pos += msg.Velocity * deltaTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (GUIMessage msg in messages)
|
|
||||||
{
|
|
||||||
if (!msg.WorldSpace) continue;
|
|
||||||
msg.Timer -= deltaTime;
|
|
||||||
msg.Pos += msg.Velocity * deltaTime;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
messages.RemoveAll(m => m.Timer <= 0.0f);
|
messages.RemoveAll(m => m.Timer <= 0.0f);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ namespace Barotrauma.Items.Components
|
|||||||
private GUIComponent inputInventoryHolder, outputInventoryHolder;
|
private GUIComponent inputInventoryHolder, outputInventoryHolder;
|
||||||
private GUICustomComponent inputInventoryOverlay, outputInventoryOverlay;
|
private GUICustomComponent inputInventoryOverlay, outputInventoryOverlay;
|
||||||
|
|
||||||
private FabricableItem selectedItem;
|
private FabricationRecipe selectedItem;
|
||||||
|
|
||||||
private GUIComponent inSufficientPowerWarning;
|
private GUIComponent inSufficientPowerWarning;
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
OnSelected = (GUIComponent component, object userdata) =>
|
OnSelected = (GUIComponent component, object userdata) =>
|
||||||
{
|
{
|
||||||
selectedItem = userdata as FabricableItem;
|
selectedItem = userdata as FabricationRecipe;
|
||||||
if (selectedItem != null) { SelectItem(Character.Controlled, selectedItem); }
|
if (selectedItem != null) { SelectItem(Character.Controlled, selectedItem); }
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -59,7 +59,7 @@ namespace Barotrauma.Items.Components
|
|||||||
CanBeFocused = false
|
CanBeFocused = false
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (FabricableItem fi in fabricableItems)
|
foreach (FabricationRecipe fi in fabricationRecipes)
|
||||||
{
|
{
|
||||||
GUIFrame frame = new GUIFrame(new RectTransform(new Point(itemList.Rect.Width, 50), itemList.Content.RectTransform), style: null)
|
GUIFrame frame = new GUIFrame(new RectTransform(new Point(itemList.Rect.Width, 50), itemList.Content.RectTransform), style: null)
|
||||||
{
|
{
|
||||||
@@ -115,13 +115,13 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
partial void SelectProjSpecific(Character character)
|
partial void SelectProjSpecific(Character character)
|
||||||
{
|
{
|
||||||
var nonItems = itemList.Content.Children.Where(c => !(c.UserData is FabricableItem)).ToList();
|
var nonItems = itemList.Content.Children.Where(c => !(c.UserData is FabricationRecipe)).ToList();
|
||||||
nonItems.ForEach(i => itemList.Content.RemoveChild(i));
|
nonItems.ForEach(i => itemList.Content.RemoveChild(i));
|
||||||
|
|
||||||
itemList.Content.RectTransform.SortChildren((c1, c2) =>
|
itemList.Content.RectTransform.SortChildren((c1, c2) =>
|
||||||
{
|
{
|
||||||
var item1 = c1.GUIComponent.UserData as FabricableItem;
|
var item1 = c1.GUIComponent.UserData as FabricationRecipe;
|
||||||
var item2 = c2.GUIComponent.UserData as FabricableItem;
|
var item2 = c2.GUIComponent.UserData as FabricationRecipe;
|
||||||
|
|
||||||
bool hasSkills1 = DegreeOfSuccess(character, item1.RequiredSkills) >= 0.5f;
|
bool hasSkills1 = DegreeOfSuccess(character, item1.RequiredSkills) >= 0.5f;
|
||||||
bool hasSkills2 = DegreeOfSuccess(character, item2.RequiredSkills) >= 0.5f;
|
bool hasSkills2 = DegreeOfSuccess(character, item2.RequiredSkills) >= 0.5f;
|
||||||
@@ -144,7 +144,7 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
CanBeFocused = false
|
CanBeFocused = false
|
||||||
};
|
};
|
||||||
var firstinSufficient = itemList.Content.Children.FirstOrDefault(c => c.UserData is FabricableItem fabricableItem && DegreeOfSuccess(character, fabricableItem.RequiredSkills) < 0.5f);
|
var firstinSufficient = itemList.Content.Children.FirstOrDefault(c => c.UserData is FabricationRecipe fabricableItem && DegreeOfSuccess(character, fabricableItem.RequiredSkills) < 0.5f);
|
||||||
if (firstinSufficient != null)
|
if (firstinSufficient != null)
|
||||||
{
|
{
|
||||||
insufficientSkillsText.RectTransform.RepositionChildInHierarchy(itemList.Content.RectTransform.GetChildIndex(firstinSufficient.RectTransform));
|
insufficientSkillsText.RectTransform.RepositionChildInHierarchy(itemList.Content.RectTransform.GetChildIndex(firstinSufficient.RectTransform));
|
||||||
@@ -155,13 +155,13 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
overlayComponent.RectTransform.SetAsLastChild();
|
overlayComponent.RectTransform.SetAsLastChild();
|
||||||
|
|
||||||
FabricableItem targetItem = fabricatedItem ?? selectedItem;
|
FabricationRecipe targetItem = fabricatedItem ?? selectedItem;
|
||||||
if (targetItem != null)
|
if (targetItem != null)
|
||||||
{
|
{
|
||||||
int slotIndex = 0;
|
int slotIndex = 0;
|
||||||
|
|
||||||
var missingItems = new List<FabricableItem.RequiredItem>();
|
var missingItems = new List<FabricationRecipe.RequiredItem>();
|
||||||
foreach (FabricableItem.RequiredItem requiredItem in targetItem.RequiredItems)
|
foreach (FabricationRecipe.RequiredItem requiredItem in targetItem.RequiredItems)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < requiredItem.Amount; i++)
|
for (int i = 0; i < requiredItem.Amount; i++)
|
||||||
{
|
{
|
||||||
@@ -176,7 +176,7 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
var availableIngredients = GetAvailableIngredients();
|
var availableIngredients = GetAvailableIngredients();
|
||||||
|
|
||||||
foreach (FabricableItem.RequiredItem requiredItem in missingItems)
|
foreach (FabricationRecipe.RequiredItem requiredItem in missingItems)
|
||||||
{
|
{
|
||||||
//highlight suitable ingredients in linked inventories
|
//highlight suitable ingredients in linked inventories
|
||||||
foreach (Item item in availableIngredients)
|
foreach (Item item in availableIngredients)
|
||||||
@@ -230,7 +230,7 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
overlayComponent.RectTransform.SetAsLastChild();
|
overlayComponent.RectTransform.SetAsLastChild();
|
||||||
|
|
||||||
FabricableItem targetItem = fabricatedItem ?? selectedItem;
|
FabricationRecipe targetItem = fabricatedItem ?? selectedItem;
|
||||||
if (targetItem != null)
|
if (targetItem != null)
|
||||||
{
|
{
|
||||||
var itemIcon = targetItem.TargetItem.InventoryIcon ?? targetItem.TargetItem.sprite;
|
var itemIcon = targetItem.TargetItem.InventoryIcon ?? targetItem.TargetItem.sprite;
|
||||||
@@ -257,7 +257,7 @@ namespace Barotrauma.Items.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool SelectItem(Character user, FabricableItem selectedItem)
|
private bool SelectItem(Character user, FabricationRecipe selectedItem)
|
||||||
{
|
{
|
||||||
selectedItemFrame.ClearChildren();
|
selectedItemFrame.ClearChildren();
|
||||||
|
|
||||||
@@ -354,7 +354,7 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
foreach (GUIComponent child in itemList.Content.Children)
|
foreach (GUIComponent child in itemList.Content.Children)
|
||||||
{
|
{
|
||||||
var itemPrefab = child.UserData as FabricableItem;
|
var itemPrefab = child.UserData as FabricationRecipe;
|
||||||
if (itemPrefab == null) continue;
|
if (itemPrefab == null) continue;
|
||||||
|
|
||||||
bool canBeFabricated = CanBeFabricated(itemPrefab, availableIngredients);
|
bool canBeFabricated = CanBeFabricated(itemPrefab, availableIngredients);
|
||||||
@@ -371,13 +371,13 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
public void ClientWrite(NetBuffer msg, object[] extraData = null)
|
public void ClientWrite(NetBuffer msg, object[] extraData = null)
|
||||||
{
|
{
|
||||||
int itemIndex = fabricatedItem == null ? -1 : fabricableItems.IndexOf(fabricatedItem);
|
int itemIndex = fabricatedItem == null ? -1 : fabricationRecipes.IndexOf(fabricatedItem);
|
||||||
msg.WriteRangedInteger(-1, fabricableItems.Count - 1, itemIndex);
|
msg.WriteRangedInteger(-1, fabricationRecipes.Count - 1, itemIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
|
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
|
||||||
{
|
{
|
||||||
int itemIndex = msg.ReadRangedInteger(-1, fabricableItems.Count - 1);
|
int itemIndex = msg.ReadRangedInteger(-1, fabricationRecipes.Count - 1);
|
||||||
UInt16 userID = msg.ReadUInt16();
|
UInt16 userID = msg.ReadUInt16();
|
||||||
Character user = Entity.FindEntityByID(userID) as Character;
|
Character user = Entity.FindEntityByID(userID) as Character;
|
||||||
|
|
||||||
@@ -388,11 +388,11 @@ namespace Barotrauma.Items.Components
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
//if already fabricating the selected item, return
|
//if already fabricating the selected item, return
|
||||||
if (fabricatedItem != null && fabricableItems.IndexOf(fabricatedItem) == itemIndex) return;
|
if (fabricatedItem != null && fabricationRecipes.IndexOf(fabricatedItem) == itemIndex) return;
|
||||||
if (itemIndex < 0 || itemIndex >= fabricableItems.Count) return;
|
if (itemIndex < 0 || itemIndex >= fabricationRecipes.Count) return;
|
||||||
|
|
||||||
SelectItem(user, fabricableItems[itemIndex]);
|
SelectItem(user, fabricationRecipes[itemIndex]);
|
||||||
StartFabricating(fabricableItems[itemIndex], user);
|
StartFabricating(fabricationRecipes[itemIndex], user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
public void ServerRead(ClientNetObject type, NetBuffer msg, Client c)
|
public void ServerRead(ClientNetObject type, NetBuffer msg, Client c)
|
||||||
{
|
{
|
||||||
int itemIndex = msg.ReadRangedInteger(-1, fabricableItems.Count - 1);
|
int itemIndex = msg.ReadRangedInteger(-1, fabricationRecipes.Count - 1);
|
||||||
|
|
||||||
item.CreateServerEvent(this);
|
item.CreateServerEvent(this);
|
||||||
|
|
||||||
@@ -25,17 +25,17 @@ namespace Barotrauma.Items.Components
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
//if already fabricating the selected item, return
|
//if already fabricating the selected item, return
|
||||||
if (fabricatedItem != null && fabricableItems.IndexOf(fabricatedItem) == itemIndex) return;
|
if (fabricatedItem != null && fabricationRecipes.IndexOf(fabricatedItem) == itemIndex) return;
|
||||||
if (itemIndex < 0 || itemIndex >= fabricableItems.Count) return;
|
if (itemIndex < 0 || itemIndex >= fabricationRecipes.Count) return;
|
||||||
|
|
||||||
StartFabricating(fabricableItems[itemIndex], c.Character);
|
StartFabricating(fabricationRecipes[itemIndex], c.Character);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
|
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
|
||||||
{
|
{
|
||||||
int itemIndex = fabricatedItem == null ? -1 : fabricableItems.IndexOf(fabricatedItem);
|
int itemIndex = fabricatedItem == null ? -1 : fabricationRecipes.IndexOf(fabricatedItem);
|
||||||
msg.WriteRangedInteger(-1, fabricableItems.Count - 1, itemIndex);
|
msg.WriteRangedInteger(-1, fabricationRecipes.Count - 1, itemIndex);
|
||||||
UInt16 userID = fabricatedItem == null || user == null ? (UInt16)0 : user.ID;
|
UInt16 userID = fabricatedItem == null || user == null ? (UInt16)0 : user.ID;
|
||||||
msg.Write(userID);
|
msg.Write(userID);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1216,6 +1216,43 @@ namespace Barotrauma
|
|||||||
targetingTag = "room";
|
targetingTag = "room";
|
||||||
}
|
}
|
||||||
if (door != null)
|
if (door != null)
|
||||||
|
{
|
||||||
|
// If there's not a more specific tag for the door
|
||||||
|
if (string.IsNullOrEmpty(targetingTag) || targetingTag == "room")
|
||||||
|
{
|
||||||
|
targetingTag = "door";
|
||||||
|
}
|
||||||
|
bool isOutdoor = door.LinkedGap?.FlowTargetHull != null && !door.LinkedGap.IsRoomToRoom;
|
||||||
|
bool isOpen = door.IsOpen || door.Item.Condition <= 0.0f;
|
||||||
|
//increase priority if the character is outside and an aggressive boarder, and the door is from outside to inside
|
||||||
|
if (aggressiveBoarding)
|
||||||
|
{
|
||||||
|
if (character.CurrentHull == null)
|
||||||
|
{
|
||||||
|
valueModifier = isOutdoor ? 1 : 0;
|
||||||
|
valueModifier *= isOpen ? 5 : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Ignore disabled walls
|
||||||
|
bool isDisabled = true;
|
||||||
|
for (int i = 0; i < s.Sections.Length; i++)
|
||||||
|
{
|
||||||
|
valueModifier = isOutdoor ? 0 : 1;
|
||||||
|
valueModifier *= isOpen ? 0 : 1;
|
||||||
|
}
|
||||||
|
if (isDisabled)
|
||||||
|
{
|
||||||
|
valueModifier = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
targetingTag = "room";
|
||||||
|
}
|
||||||
|
if (door != null)
|
||||||
{
|
{
|
||||||
// If there's not a more specific tag for the door
|
// If there's not a more specific tag for the door
|
||||||
if (string.IsNullOrEmpty(targetingTag) || targetingTag == "room")
|
if (string.IsNullOrEmpty(targetingTag) || targetingTag == "room")
|
||||||
@@ -1242,6 +1279,10 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
else if (isOpen) //ignore broken and open doors
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (target.Entity is IDamageable targetDamageable && targetDamageable.Health <= 0.0f)
|
else if (target.Entity is IDamageable targetDamageable && targetDamageable.Health <= 0.0f)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,131 +8,14 @@ using System.Xml.Linq;
|
|||||||
|
|
||||||
namespace Barotrauma.Items.Components
|
namespace Barotrauma.Items.Components
|
||||||
{
|
{
|
||||||
class FabricableItem
|
|
||||||
{
|
|
||||||
public class RequiredItem
|
|
||||||
{
|
|
||||||
public readonly ItemPrefab ItemPrefab;
|
|
||||||
public int Amount;
|
|
||||||
public readonly float MinCondition;
|
|
||||||
public readonly bool UseCondition;
|
|
||||||
|
|
||||||
public RequiredItem(ItemPrefab itemPrefab, int amount, float minCondition, bool useCondition)
|
|
||||||
{
|
|
||||||
ItemPrefab = itemPrefab;
|
|
||||||
Amount = amount;
|
|
||||||
MinCondition = minCondition;
|
|
||||||
UseCondition = useCondition;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly ItemPrefab TargetItem;
|
|
||||||
|
|
||||||
public readonly string DisplayName;
|
|
||||||
|
|
||||||
public readonly List<RequiredItem> RequiredItems;
|
|
||||||
|
|
||||||
public readonly float RequiredTime;
|
|
||||||
|
|
||||||
public readonly float OutCondition; //Percentage-based from 0 to 1
|
|
||||||
|
|
||||||
public readonly List<Skill> RequiredSkills;
|
|
||||||
|
|
||||||
public FabricableItem(XElement element)
|
|
||||||
{
|
|
||||||
if (element.Attribute("name") != null)
|
|
||||||
{
|
|
||||||
string name = element.Attribute("name").Value;
|
|
||||||
DebugConsole.ThrowError("Error in fabricable item config (" + name + ") - use item identifiers instead of names");
|
|
||||||
TargetItem = MapEntityPrefab.Find(name) as ItemPrefab;
|
|
||||||
if (TargetItem == null)
|
|
||||||
{
|
|
||||||
DebugConsole.ThrowError("Error in fabricable item config - item prefab \"" + name + "\" not found.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
string identifier = element.GetAttributeString("identifier", "");
|
|
||||||
TargetItem = MapEntityPrefab.Find(null, identifier) as ItemPrefab;
|
|
||||||
if (TargetItem == null)
|
|
||||||
{
|
|
||||||
DebugConsole.ThrowError("Error in fabricable item config - item prefab \"" + identifier + "\" not found.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
string displayName = element.GetAttributeString("displayname", "");
|
|
||||||
DisplayName = string.IsNullOrEmpty(displayName) ? TargetItem.Name : TextManager.Get($"DisplayName.{displayName}");
|
|
||||||
|
|
||||||
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 " + TargetItem.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", "");
|
|
||||||
if (string.IsNullOrWhiteSpace(requiredItemIdentifier))
|
|
||||||
{
|
|
||||||
DebugConsole.ThrowError("Error in fabricable item " + TargetItem.Name + "! One of the required items has no identifier.");
|
|
||||||
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);
|
|
||||||
|
|
||||||
|
|
||||||
ItemPrefab requiredItem = MapEntityPrefab.Find(null, requiredItemIdentifier.Trim()) as ItemPrefab;
|
|
||||||
if (requiredItem == null)
|
|
||||||
{
|
|
||||||
DebugConsole.ThrowError("Error in fabricable item " + TargetItem.Name + "! Required item \"" + requiredItemIdentifier + "\" not found.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var existing = RequiredItems.Find(r => r.ItemPrefab == requiredItem);
|
|
||||||
if (existing == null)
|
|
||||||
{
|
|
||||||
RequiredItems.Add(new RequiredItem(requiredItem, count, minCondition, useCondition));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
RequiredItems.Remove(existing);
|
|
||||||
RequiredItems.Add(new RequiredItem(requiredItem, existing.Amount + count, minCondition, useCondition));
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
partial class Fabricator : Powered, IServerSerializable, IClientSerializable
|
partial class Fabricator : Powered, IServerSerializable, IClientSerializable
|
||||||
{
|
{
|
||||||
public const float SkillIncreaseMultiplier = 0.5f;
|
public const float SkillIncreaseMultiplier = 0.5f;
|
||||||
|
|
||||||
private List<FabricableItem> fabricableItems;
|
private List<FabricationRecipe> fabricationRecipes = new List<FabricationRecipe>();
|
||||||
|
|
||||||
private FabricableItem fabricatedItem;
|
private FabricationRecipe fabricatedItem;
|
||||||
private float timeUntilReady;
|
private float timeUntilReady;
|
||||||
private float requiredTime;
|
private float requiredTime;
|
||||||
|
|
||||||
@@ -145,20 +28,29 @@ namespace Barotrauma.Items.Components
|
|||||||
public Fabricator(Item item, XElement element)
|
public Fabricator(Item item, XElement element)
|
||||||
: base(item, element)
|
: base(item, element)
|
||||||
{
|
{
|
||||||
fabricableItems = new List<FabricableItem>();
|
|
||||||
|
|
||||||
foreach (XElement subElement in element.Elements())
|
foreach (XElement subElement in element.Elements())
|
||||||
{
|
{
|
||||||
if (subElement.Name.ToString().ToLowerInvariant() != "fabricableitem") continue;
|
if (subElement.Name.ToString().ToLowerInvariant() == "fabricableitem")
|
||||||
|
|
||||||
FabricableItem fabricableItem = new FabricableItem(subElement);
|
|
||||||
if (fabricableItem.TargetItem == null)
|
|
||||||
{
|
{
|
||||||
DebugConsole.ThrowError("Error in item " + item.Name + "! Fabricable item \"" + subElement.GetAttributeString("name", "") + "\" not found.");
|
DebugConsole.ThrowError("Error in item " + item.Name + "! Fabrication recipes should be defined in the craftable item's xml, not in the fabricator.");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
|
||||||
|
foreach (MapEntityPrefab me in MapEntityPrefab.List)
|
||||||
|
{
|
||||||
|
if (!(me is ItemPrefab itemPrefab)) { continue; }
|
||||||
|
|
||||||
|
foreach (FabricationRecipe recipe in itemPrefab.FabricationRecipes)
|
||||||
{
|
{
|
||||||
fabricableItems.Add(fabricableItem);
|
if (recipe.SuitableFabricatorIdentifiers.Length > 0)
|
||||||
|
{
|
||||||
|
if (!recipe.SuitableFabricatorIdentifiers.Any(i => item.prefab.Identifier == i || item.HasTag(i)))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fabricationRecipes.Add(recipe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,12 +69,12 @@ namespace Barotrauma.Items.Components
|
|||||||
inputContainer = containers[0];
|
inputContainer = containers[0];
|
||||||
outputContainer = containers[1];
|
outputContainer = containers[1];
|
||||||
|
|
||||||
foreach (FabricableItem fabricableItem in fabricableItems)
|
foreach (var recipe in fabricationRecipes)
|
||||||
{
|
{
|
||||||
int ingredientCount = fabricableItem.RequiredItems.Sum(it => it.Amount);
|
int ingredientCount = recipe.RequiredItems.Sum(it => it.Amount);
|
||||||
if (ingredientCount > inputContainer.Capacity)
|
if (ingredientCount > inputContainer.Capacity)
|
||||||
{
|
{
|
||||||
DebugConsole.ThrowError("Error in item \"" + item.Name + "\": There's not enough room in the input inventory for the ingredients of \"" + fabricableItem.TargetItem.Name + "\"!");
|
DebugConsole.ThrowError("Error in item \"" + item.Name + "\": There's not enough room in the input inventory for the ingredients of \"" + recipe.TargetItem.Name + "\"!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,7 +99,7 @@ namespace Barotrauma.Items.Components
|
|||||||
return (picker != null);
|
return (picker != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StartFabricating(FabricableItem selectedItem, Character user)
|
private void StartFabricating(FabricationRecipe selectedItem, Character user)
|
||||||
{
|
{
|
||||||
if (selectedItem == null) return;
|
if (selectedItem == null) return;
|
||||||
if (!outputContainer.Inventory.IsEmpty()) return;
|
if (!outputContainer.Inventory.IsEmpty()) return;
|
||||||
@@ -292,7 +184,7 @@ namespace Barotrauma.Items.Components
|
|||||||
if (timeUntilReady > 0.0f) { return; }
|
if (timeUntilReady > 0.0f) { return; }
|
||||||
|
|
||||||
var availableIngredients = GetAvailableIngredients();
|
var availableIngredients = GetAvailableIngredients();
|
||||||
foreach (FabricableItem.RequiredItem ingredient in fabricatedItem.RequiredItems)
|
foreach (FabricationRecipe.RequiredItem ingredient in fabricatedItem.RequiredItems)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < ingredient.Amount; i++)
|
for (int i = 0; i < ingredient.Amount; i++)
|
||||||
{
|
{
|
||||||
@@ -335,17 +227,17 @@ namespace Barotrauma.Items.Components
|
|||||||
CancelFabricating(null);
|
CancelFabricating(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool CanBeFabricated(FabricableItem fabricableItem)
|
private bool CanBeFabricated(FabricationRecipe fabricableItem)
|
||||||
{
|
{
|
||||||
if (fabricableItem == null) { return false; }
|
if (fabricableItem == null) { return false; }
|
||||||
List<Item> availableIngredients = GetAvailableIngredients();
|
List<Item> availableIngredients = GetAvailableIngredients();
|
||||||
return CanBeFabricated(fabricableItem, availableIngredients);
|
return CanBeFabricated(fabricableItem, availableIngredients);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool CanBeFabricated(FabricableItem fabricableItem, IEnumerable<Item> availableIngredients)
|
private bool CanBeFabricated(FabricationRecipe fabricableItem, IEnumerable<Item> availableIngredients)
|
||||||
{
|
{
|
||||||
if (fabricableItem == null) { return false; }
|
if (fabricableItem == null) { return false; }
|
||||||
foreach (FabricableItem.RequiredItem requiredItem in fabricableItem.RequiredItems)
|
foreach (FabricationRecipe.RequiredItem requiredItem in fabricableItem.RequiredItems)
|
||||||
{
|
{
|
||||||
if (availableIngredients.Count(it => IsItemValidIngredient(it, requiredItem)) < requiredItem.Amount)
|
if (availableIngredients.Count(it => IsItemValidIngredient(it, requiredItem)) < requiredItem.Amount)
|
||||||
{
|
{
|
||||||
@@ -355,7 +247,7 @@ namespace Barotrauma.Items.Components
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private float GetRequiredTime(FabricableItem fabricableItem, Character user)
|
private float GetRequiredTime(FabricationRecipe fabricableItem, Character user)
|
||||||
{
|
{
|
||||||
float degreeOfSuccess = DegreeOfSuccess(user, fabricableItem.RequiredSkills);
|
float degreeOfSuccess = DegreeOfSuccess(user, fabricableItem.RequiredSkills);
|
||||||
|
|
||||||
@@ -397,7 +289,7 @@ namespace Barotrauma.Items.Components
|
|||||||
/// Move the items required for fabrication into the input container.
|
/// Move the items required for fabrication into the input container.
|
||||||
/// The method assumes that all the required ingredients are available either in the input container or linked containers.
|
/// The method assumes that all the required ingredients are available either in the input container or linked containers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void MoveIngredientsToInputContainer(FabricableItem targetItem)
|
private void MoveIngredientsToInputContainer(FabricationRecipe targetItem)
|
||||||
{
|
{
|
||||||
//required ingredients that are already present in the input container
|
//required ingredients that are already present in the input container
|
||||||
List<Item> usedItems = new List<Item>();
|
List<Item> usedItems = new List<Item>();
|
||||||
@@ -428,7 +320,7 @@ namespace Barotrauma.Items.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsItemValidIngredient(Item item, FabricableItem.RequiredItem requiredItem)
|
private bool IsItemValidIngredient(Item item, FabricationRecipe.RequiredItem requiredItem)
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
item != null &&
|
item != null &&
|
||||||
|
|||||||
@@ -491,6 +491,9 @@ namespace Barotrauma
|
|||||||
case "levelcommonness":
|
case "levelcommonness":
|
||||||
case "suitabletreatment":
|
case "suitabletreatment":
|
||||||
case "containedsprite":
|
case "containedsprite":
|
||||||
|
case "fabricate":
|
||||||
|
case "fabricable":
|
||||||
|
case "fabricableitem":
|
||||||
break;
|
break;
|
||||||
case "staticbody":
|
case "staticbody":
|
||||||
StaticBodyConfig = subElement;
|
StaticBodyConfig = subElement;
|
||||||
|
|||||||
@@ -31,6 +31,100 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FabricationRecipe
|
||||||
|
{
|
||||||
|
public class RequiredItem
|
||||||
|
{
|
||||||
|
public readonly ItemPrefab ItemPrefab;
|
||||||
|
public int Amount;
|
||||||
|
public readonly float MinCondition;
|
||||||
|
public readonly bool UseCondition;
|
||||||
|
|
||||||
|
public RequiredItem(ItemPrefab itemPrefab, int amount, float minCondition, bool useCondition)
|
||||||
|
{
|
||||||
|
ItemPrefab = itemPrefab;
|
||||||
|
Amount = amount;
|
||||||
|
MinCondition = minCondition;
|
||||||
|
UseCondition = useCondition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly ItemPrefab TargetItem;
|
||||||
|
public readonly string DisplayName;
|
||||||
|
public readonly List<RequiredItem> RequiredItems;
|
||||||
|
public readonly string[] SuitableFabricatorIdentifiers;
|
||||||
|
public readonly float RequiredTime;
|
||||||
|
public readonly float OutCondition; //Percentage-based from 0 to 1
|
||||||
|
public readonly List<Skill> RequiredSkills;
|
||||||
|
|
||||||
|
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", "");
|
||||||
|
if (string.IsNullOrWhiteSpace(requiredItemIdentifier))
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError("Error in fabricable item " + itemPrefab.Name + "! One of the required items has no identifier.");
|
||||||
|
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);
|
||||||
|
|
||||||
|
|
||||||
|
ItemPrefab requiredItem = MapEntityPrefab.Find(null, requiredItemIdentifier.Trim()) as ItemPrefab;
|
||||||
|
if (requiredItem == null)
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError("Error in fabricable item " + itemPrefab.Name + "! Required item \"" + requiredItemIdentifier + "\" not found.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var existing = RequiredItems.Find(r => r.ItemPrefab == requiredItem);
|
||||||
|
if (existing == null)
|
||||||
|
{
|
||||||
|
RequiredItems.Add(new RequiredItem(requiredItem, count, minCondition, useCondition));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
RequiredItems.Remove(existing);
|
||||||
|
RequiredItems.Add(new RequiredItem(requiredItem, existing.Amount + count, minCondition, useCondition));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
partial class ItemPrefab : MapEntityPrefab
|
partial class ItemPrefab : MapEntityPrefab
|
||||||
{
|
{
|
||||||
private readonly string configFile;
|
private readonly string configFile;
|
||||||
@@ -48,6 +142,8 @@ namespace Barotrauma
|
|||||||
//the construction can be Activated() by a Character inside the area
|
//the construction can be Activated() by a Character inside the area
|
||||||
public List<Rectangle> Triggers;
|
public List<Rectangle> Triggers;
|
||||||
|
|
||||||
|
private List<XElement> fabricationRecipeElements = new List<XElement>();
|
||||||
|
|
||||||
public string ConfigFile
|
public string ConfigFile
|
||||||
{
|
{
|
||||||
get { return configFile; }
|
get { return configFile; }
|
||||||
@@ -65,6 +161,12 @@ namespace Barotrauma
|
|||||||
private set;
|
private set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<FabricationRecipe> FabricationRecipes
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
public float DeconstructTime
|
public float DeconstructTime
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
@@ -309,6 +411,18 @@ namespace Barotrauma
|
|||||||
new ItemPrefab(doc.Root, filePath);
|
new ItemPrefab(doc.Root, filePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//initialize item requirements for fabrication recipes
|
||||||
|
//(has to be done after all the item prefabs have been loaded, because the
|
||||||
|
//recipe ingredients may not have been loaded yet when loading the prefab)
|
||||||
|
foreach (MapEntityPrefab me in List)
|
||||||
|
{
|
||||||
|
if (!(me is ItemPrefab itemPrefab)) { continue; }
|
||||||
|
foreach (XElement fabricationRecipe in itemPrefab.fabricationRecipeElements)
|
||||||
|
{
|
||||||
|
itemPrefab.FabricationRecipes.Add(new FabricationRecipe(fabricationRecipe, itemPrefab));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ItemPrefab(XElement element, string filePath)
|
public ItemPrefab(XElement element, string filePath)
|
||||||
@@ -333,6 +447,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
Triggers = new List<Rectangle>();
|
Triggers = new List<Rectangle>();
|
||||||
DeconstructItems = new List<DeconstructItem>();
|
DeconstructItems = new List<DeconstructItem>();
|
||||||
|
FabricationRecipes = new List<FabricationRecipe>();
|
||||||
DeconstructTime = 1.0f;
|
DeconstructTime = 1.0f;
|
||||||
|
|
||||||
Tags = element.GetAttributeStringArray("tags", new string[0], convertToLowerInvariant: true).ToHashSet();
|
Tags = element.GetAttributeStringArray("tags", new string[0], convertToLowerInvariant: true).ToHashSet();
|
||||||
@@ -464,6 +579,11 @@ namespace Barotrauma
|
|||||||
DeconstructItems.Add(new DeconstructItem(deconstructItem));
|
DeconstructItems.Add(new DeconstructItem(deconstructItem));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "fabricate":
|
||||||
|
case "fabricable":
|
||||||
|
case "fabricableitem":
|
||||||
|
fabricationRecipeElements.Add(subElement);
|
||||||
break;
|
break;
|
||||||
case "trigger":
|
case "trigger":
|
||||||
Rectangle trigger = new Rectangle(0, 0, 10, 10)
|
Rectangle trigger = new Rectangle(0, 0, 10, 10)
|
||||||
@@ -1148,6 +1268,39 @@ namespace Barotrauma
|
|||||||
|
|
||||||
AllowedLinks = element.GetAttributeStringArray("allowedlinks", new string[0], convertToLowerInvariant: true).ToList();
|
AllowedLinks = element.GetAttributeStringArray("allowedlinks", new string[0], convertToLowerInvariant: true).ToList();
|
||||||
|
|
||||||
|
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 (!category.HasFlag(MapEntityCategory.Legacy) && 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 (!string.IsNullOrEmpty(identifier))
|
||||||
|
{
|
||||||
|
MapEntityPrefab existingPrefab = List.Find(e => e.Identifier == identifier);
|
||||||
|
if (existingPrefab != null)
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError(
|
||||||
|
"Map entity prefabs \"" + name + "\" and \"" + existingPrefab.Name + "\" have the same identifier!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AllowedLinks = element.GetAttributeStringArray("allowedlinks", new string[0], convertToLowerInvariant: true).ToList();
|
||||||
|
|
||||||
List.Add(this);
|
List.Add(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user