Build 1.1.4.0

This commit is contained in:
Markus Isberg
2023-03-31 18:40:44 +03:00
parent efba17e0ff
commit 9470edead3
483 changed files with 17487 additions and 8548 deletions
@@ -8,44 +8,89 @@ using Barotrauma.Extensions;
namespace Barotrauma
{
/// <summary>
/// Used by various features to define different kinds of relations between items:
/// for example, which item a character must have equipped to interact with some item in some way,
/// which items can go inside a container, or which kind of item the target of a status effect must have for the effect to execute.
/// </summary>
class RelatedItem
{
public enum RelationType
{
None,
/// <summary>
/// The item must be contained inside the item this relation is defined in.
/// Can for example by used to make an item usable only when there's a specific kind of item inside it.
/// </summary>
Contained,
/// <summary>
/// The user must have equipped the item (i.e. held or worn).
/// </summary>
Equipped,
/// <summary>
/// The user must have picked up the item (i.e. the item needs to be in the user's inventory).
/// </summary>
Picked,
Container
/// <summary>
/// The item this relation is defined in must be inside a specific kind of container.
/// Can for example by used to make an item do something when it's inside some other type of item.
/// </summary>
Container,
/// <summary>
/// Signifies an error (type could not be parsed)
/// </summary>
Invalid
}
public bool IsOptional { get; set; }
/// <summary>
/// Should an empty inventory be considered valid? Can be used to, for example, make an item do something if there's a specific item, or nothing, inside it.
/// </summary>
public bool MatchOnEmpty { get; set; }
/// <summary>
/// Should only an empty inventory be considered valid? Can be used to, for example, make an item do something when there's nothing inside it.
/// </summary>
public bool RequireEmpty { get; set; }
/// <summary>
/// Only valid for the RequiredItems of an ItemComponent. Can be used to ignore the requirement in the submarine editor,
/// making it easier to for example make rewire things that require some special tool to rewire.
/// </summary>
public bool IgnoreInEditor { get; set; }
/// <summary>
/// Identifier(s) or tag(s) of the items that are NOT considered valid.
/// Can be used to, for example, exclude some specific items when using tags that apply to multiple items.
/// </summary>
public ImmutableHashSet<Identifier> ExcludedIdentifiers { get; private set; }
private RelationType type;
private readonly RelationType type;
public List<StatusEffect> statusEffects;
public List<StatusEffect> StatusEffects = new List<StatusEffect>();
/// <summary>
/// Only valid for the RequiredItems of an ItemComponent. A message displayed if the required item isn't found (e.g. a notification about lack of ammo or fuel).
/// </summary>
public LocalizedString Msg;
/// <summary>
/// Only valid for the RequiredItems of an ItemComponent. The localization tag of a message displayed if the required item isn't found (e.g. a notification about lack of ammo or fuel).
/// </summary>
public Identifier MsgTag;
/// <summary>
/// Should broken (0 condition) items be excluded
/// Should broken (0 condition) items be excluded?
/// </summary>
public bool ExcludeBroken { get; private set; }
/// <summary>
/// Should full condition (100%) items be excluded
/// Should full condition (100%) items be excluded?
/// </summary>
public bool ExcludeFullCondition { get; private set; }
/// <summary>
/// Are item variants considered valid?
/// </summary>
public bool AllowVariants { get; private set; } = true;
public RelationType Type
@@ -59,19 +104,34 @@ namespace Barotrauma
public int TargetSlot = -1;
/// <summary>
/// Overrides the position defined in ItemContainer.
/// Overrides the position defined in ItemContainer. Only valid when used in the Containable definitions of an ItemContainer.
/// </summary>
public Vector2? ItemPos;
/// <summary>
/// Only affects when ItemContainer.hideItems is false. Doesn't override the value.
/// Only valid when used in the Containable definitions of an ItemContainer.
/// Only affects when ItemContainer.hideItems is false. Doesn't override the value.
/// </summary>
public bool? Hide;
public bool Hide;
/// <summary>
/// Only valid when used in the Containable definitions of an ItemContainer.
/// Can be used to override the rotation of specific items in the container.
/// </summary>
public float Rotation;
/// <summary>
/// Only valid when used in the Containable definitions of an ItemContainer.
/// Can be used to force specific items to stay active inside the container (such as flashlights attached to a gun).
/// </summary>
public bool SetActive;
/// <summary>
/// Only valid for the RequiredItems of an ItemComponent. Can be used to make the requirement optional,
/// meaning that you don't need to have the item to interact with something, but having it may still affect what the interaction does (such as using a crowbar on a door).
/// </summary>
public bool IsOptional { get; set; }
public string JoinedIdentifiers
{
get { return string.Join(",", Identifiers); }
@@ -83,6 +143,9 @@ namespace Barotrauma
}
}
/// <summary>
/// Identifier(s) or tag(s) of the items that are considered valid.
/// </summary>
public ImmutableHashSet<Identifier> Identifiers { get; private set; }
public string JoinedExcludedIdentifiers
@@ -139,8 +202,121 @@ namespace Barotrauma
{
this.Identifiers = identifiers.Select(id => id.Value.Trim().ToIdentifier()).ToImmutableHashSet();
this.ExcludedIdentifiers = excludedIdentifiers.Select(id => id.Value.Trim().ToIdentifier()).ToImmutableHashSet();
}
public RelatedItem(ContentXElement element, string parentDebugName)
{
Identifier[] identifiers;
if (element.GetAttribute("name") != null)
{
//backwards compatibility + a console warning
DebugConsole.ThrowError($"Error in RelatedItem config (" + (string.IsNullOrEmpty(parentDebugName) ? element.ToString() : parentDebugName) + ") - use item tags or identifiers instead of names.");
Identifier[] itemNames = element.GetAttributeIdentifierArray("name", Array.Empty<Identifier>());
//attempt to convert to identifiers and tags
List<Identifier> convertedIdentifiers = new List<Identifier>();
foreach (Identifier itemName in itemNames)
{
var matchingItem = ItemPrefab.Prefabs.Find(me => me.Name == itemName.Value);
if (matchingItem != null)
{
convertedIdentifiers.Add(matchingItem.Identifier);
}
else
{
//no matching item found, this must be a tag
convertedIdentifiers.Add(itemName);
}
}
identifiers = convertedIdentifiers.ToArray();
}
else
{
identifiers = element.GetAttributeIdentifierArray("items", null) ?? element.GetAttributeIdentifierArray("item", null);
if (identifiers == null)
{
identifiers = element.GetAttributeIdentifierArray("identifiers", null) ?? element.GetAttributeIdentifierArray("tags", null);
if (identifiers == null)
{
identifiers = element.GetAttributeIdentifierArray("identifier", null) ?? element.GetAttributeIdentifierArray("tag", Array.Empty<Identifier>());
}
}
}
this.Identifiers = identifiers.ToImmutableHashSet();
Identifier[] excludedIdentifiers = element.GetAttributeIdentifierArray("excludeditems", null) ?? element.GetAttributeIdentifierArray("excludeditem", null);
if (excludedIdentifiers == null)
{
excludedIdentifiers = element.GetAttributeIdentifierArray("excludedidentifiers", null) ?? element.GetAttributeIdentifierArray("excludedtags", null);
if (excludedIdentifiers == null)
{
excludedIdentifiers = element.GetAttributeIdentifierArray("excludedidentifier", null) ?? element.GetAttributeIdentifierArray("excludedtag", Array.Empty<Identifier>());
}
}
this.ExcludedIdentifiers = excludedIdentifiers.ToImmutableHashSet();
ExcludeBroken = element.GetAttributeBool("excludebroken", true);
RequireEmpty = element.GetAttributeBool("requireempty", false);
ExcludeFullCondition = element.GetAttributeBool("excludefullcondition", false);
AllowVariants = element.GetAttributeBool("allowvariants", true);
Rotation = element.GetAttributeFloat("rotation", 0f);
SetActive = element.GetAttributeBool("setactive", false);
if (element.GetAttribute(nameof(Hide)) != null)
{
Hide = element.GetAttributeBool(nameof(Hide), false);
}
if (element.GetAttribute(nameof(ItemPos)) != null)
{
ItemPos = element.GetAttributeVector2(nameof(ItemPos), Vector2.Zero);
}
string typeStr = element.GetAttributeString("type", "");
if (string.IsNullOrEmpty(typeStr))
{
switch (element.Name.ToString().ToLowerInvariant())
{
case "containable":
typeStr = "Contained";
break;
case "suitablefertilizer":
case "suitableseed":
typeStr = "None";
break;
}
}
if (!Enum.TryParse(typeStr, true, out type))
{
DebugConsole.ThrowError("Error in RelatedItem config (" + parentDebugName + ") - \"" + typeStr + "\" is not a valid relation type.");
type = RelationType.Invalid;
}
MsgTag = element.GetAttributeIdentifier("msg", Identifier.Empty);
LocalizedString msg = TextManager.Get(MsgTag);
if (!msg.Loaded)
{
Msg = MsgTag.Value;
}
else
{
#if CLIENT
foreach (InputType inputType in Enum.GetValues(typeof(InputType)))
{
msg = msg.Replace("[" + inputType.ToString().ToLowerInvariant() + "]", GameSettings.CurrentConfig.KeyMap.KeyBindText(inputType));
}
Msg = msg;
#endif
}
foreach (var subElement in element.Elements())
{
if (!subElement.Name.ToString().Equals("statuseffect", StringComparison.OrdinalIgnoreCase)) { continue; }
StatusEffects.Add(StatusEffect.Load(subElement, parentDebugName));
}
IsOptional = element.GetAttributeBool("optional", false);
IgnoreInEditor = element.GetAttributeBool("ignoreineditor", false);
MatchOnEmpty = element.GetAttributeBool("matchonempty", false);
TargetSlot = element.GetAttributeInt("targetslot", -1);
statusEffects = new List<StatusEffect>();
}
public bool CheckRequirements(Character character, Item parentItem)
@@ -197,11 +373,14 @@ namespace Barotrauma
bool isEmpty = parentItem.OwnInventory.IsEmpty();
if (RequireEmpty && !isEmpty) { return false; }
if (MatchOnEmpty && isEmpty) { return true; }
foreach (Item contained in parentItem.ContainedItems)
foreach (var container in parentItem.GetComponents<Items.Components.ItemContainer>())
{
if (TargetSlot > -1 && parentItem.OwnInventory.FindIndex(contained) != TargetSlot) { continue; }
if ((!ExcludeBroken || contained.Condition > 0.0f) && (!ExcludeFullCondition || !contained.IsFullCondition) && MatchesItem(contained)) { return true; }
if (CheckContained(contained)) { return true; }
foreach (Item contained in container.Inventory.AllItems)
{
if (TargetSlot > -1 && parentItem.OwnInventory.FindIndex(contained) != TargetSlot) { continue; }
if ((!ExcludeBroken || contained.Condition > 0.0f) && (!ExcludeFullCondition || !contained.IsFullCondition) && MatchesItem(contained)) { return true; }
if (CheckContained(contained)) { return true; }
}
}
return false;
}
@@ -221,9 +400,9 @@ namespace Barotrauma
new XAttribute("rotation", Rotation),
new XAttribute("setactive", SetActive));
if (Hide.HasValue)
if (Hide)
{
element.Add(new XAttribute(nameof(Hide), Hide.Value));
element.Add(new XAttribute(nameof(Hide), true));
}
if (ItemPos.HasValue)
{
@@ -239,120 +418,10 @@ namespace Barotrauma
}
public static RelatedItem Load(ContentXElement element, bool returnEmpty, string parentDebugName)
{
Identifier[] identifiers;
if (element.GetAttribute("name") != null)
{
//backwards compatibility + a console warning
DebugConsole.ThrowError("Error in RelatedItem config (" + (string.IsNullOrEmpty(parentDebugName) ? element.ToString() : parentDebugName) + ") - use item tags or identifiers instead of names.");
Identifier[] itemNames = element.GetAttributeIdentifierArray("name", Array.Empty<Identifier>());
//attempt to convert to identifiers and tags
List<Identifier> convertedIdentifiers = new List<Identifier>();
foreach (Identifier itemName in itemNames)
{
var matchingItem = ItemPrefab.Prefabs.Find(me => me.Name == itemName.Value);
if (matchingItem != null)
{
convertedIdentifiers.Add(matchingItem.Identifier);
}
else
{
//no matching item found, this must be a tag
convertedIdentifiers.Add(itemName);
}
}
identifiers = convertedIdentifiers.ToArray();
}
else
{
identifiers = element.GetAttributeIdentifierArray("items", null) ?? element.GetAttributeIdentifierArray("item", null);
if (identifiers == null)
{
identifiers = element.GetAttributeIdentifierArray("identifiers", null) ?? element.GetAttributeIdentifierArray("tags", null);
if (identifiers == null)
{
identifiers = element.GetAttributeIdentifierArray("identifier", null) ?? element.GetAttributeIdentifierArray("tag", Array.Empty<Identifier>());
}
}
}
Identifier[] excludedIdentifiers = element.GetAttributeIdentifierArray("excludeditems", null) ?? element.GetAttributeIdentifierArray("excludeditem", null);
if (excludedIdentifiers == null)
{
excludedIdentifiers = element.GetAttributeIdentifierArray("excludedidentifiers", null) ?? element.GetAttributeIdentifierArray("excludedtags", null);
if (excludedIdentifiers == null)
{
excludedIdentifiers = element.GetAttributeIdentifierArray("excludedidentifier", null) ?? element.GetAttributeIdentifierArray("excludedtag", Array.Empty<Identifier>());
}
}
if (identifiers.Length == 0 && excludedIdentifiers.Length == 0 && !returnEmpty) { return null; }
RelatedItem ri = new RelatedItem(identifiers, excludedIdentifiers)
{
ExcludeBroken = element.GetAttributeBool("excludebroken", true),
RequireEmpty = element.GetAttributeBool("requireempty", false),
ExcludeFullCondition = element.GetAttributeBool("excludefullcondition", false),
AllowVariants = element.GetAttributeBool("allowvariants", true),
Rotation = element.GetAttributeFloat("rotation", 0f),
SetActive = element.GetAttributeBool("setactive", false)
};
if (element.GetAttribute(nameof(Hide)) != null)
{
ri.Hide = element.GetAttributeBool(nameof(Hide), false);
}
if (element.GetAttribute(nameof(ItemPos)) != null)
{
ri.ItemPos = element.GetAttributeVector2(nameof(ItemPos), Vector2.Zero);
}
string typeStr = element.GetAttributeString("type", "");
if (string.IsNullOrEmpty(typeStr))
{
switch (element.Name.ToString().ToLowerInvariant())
{
case "containable":
typeStr = "Contained";
break;
case "suitablefertilizer":
case "suitableseed":
typeStr = "None";
break;
}
}
if (!Enum.TryParse(typeStr, true, out ri.type))
{
DebugConsole.ThrowError("Error in RelatedItem config (" + parentDebugName + ") - \"" + typeStr + "\" is not a valid relation type.");
return null;
}
ri.MsgTag = element.GetAttributeIdentifier("msg", Identifier.Empty);
LocalizedString msg = TextManager.Get(ri.MsgTag);
if (!msg.Loaded)
{
ri.Msg = ri.MsgTag.Value;
}
else
{
#if CLIENT
foreach (InputType inputType in Enum.GetValues(typeof(InputType)))
{
msg = msg.Replace("[" + inputType.ToString().ToLowerInvariant() + "]", GameSettings.CurrentConfig.KeyMap.KeyBindText(inputType));
}
ri.Msg = msg;
#endif
}
foreach (var subElement in element.Elements())
{
if (!subElement.Name.ToString().Equals("statuseffect", StringComparison.OrdinalIgnoreCase)) { continue; }
ri.statusEffects.Add(StatusEffect.Load(subElement, parentDebugName));
}
ri.IsOptional = element.GetAttributeBool("optional", false);
ri.IgnoreInEditor = element.GetAttributeBool("ignoreineditor", false);
ri.MatchOnEmpty = element.GetAttributeBool("matchonempty", false);
ri.TargetSlot = element.GetAttributeInt("targetslot", -1);
{
RelatedItem ri = new RelatedItem(element, parentDebugName);
if (ri.Type == RelationType.Invalid) { return null; }
if (ri.Identifiers.None() && ri.ExcludedIdentifiers.None() && !returnEmpty) { return null; }
return ri;
}
}