Unstable 0.17.0.0

This commit is contained in:
Markus Isberg
2022-02-26 02:43:01 +09:00
parent a83f375681
commit 3974067915
913 changed files with 32472 additions and 32364 deletions
@@ -11,6 +11,7 @@ using System.Linq;
using System.Xml.Linq;
using Barotrauma.Extensions;
using Barotrauma.MapCreatures.Behavior;
using System.Collections.Immutable;
using Barotrauma.Abilities;
#if CLIENT
@@ -22,11 +23,11 @@ namespace Barotrauma
partial class Item : MapEntity, IDamageable, IIgnorable, ISerializableEntity, IServerSerializable, IClientSerializable
{
public static List<Item> ItemList = new List<Item>();
public ItemPrefab Prefab => prefab as ItemPrefab;
public new ItemPrefab Prefab => base.Prefab as ItemPrefab;
public static bool ShowLinks = true;
private readonly HashSet<string> tags;
private readonly HashSet<Identifier> tags;
private bool isWire, isLogic;
@@ -119,7 +120,7 @@ namespace Barotrauma
private readonly bool[] hasStatusEffectsOfType;
private readonly Dictionary<ActionType, List<StatusEffect>> statusEffectLists;
public Dictionary<string, SerializableProperty> SerializableProperties { get; protected set; }
public Dictionary<Identifier, SerializableProperty> SerializableProperties { get; protected set; }
private bool? hasInGameEditableProperties;
bool HasInGameEditableProperties
@@ -186,17 +187,17 @@ namespace Barotrauma
public override string Name
{
get { return prefab.Name; }
get { return base.Prefab.Name.Value; }
}
private string description;
public string Description
{
get { return description ?? prefab.Description; }
get { return description ?? base.Prefab.Description.Value; }
set { description = value; }
}
[Editable, Serialize(false, true, alwaysUseInstanceValues: true)]
[Editable, Serialize(false, IsPropertySaveable.Yes, alwaysUseInstanceValues: true)]
public bool NonInteractable
{
get;
@@ -206,21 +207,21 @@ namespace Barotrauma
/// <summary>
/// Use <see cref="IsPlayerInteractable"/> to also check <see cref="NonInteractable"/>
/// </summary>
[Editable, Serialize(false, true, description: "When enabled, item is interactable only for characters on non-player teams.", alwaysUseInstanceValues: true)]
[Editable, Serialize(false, IsPropertySaveable.Yes, description: "When enabled, item is interactable only for characters on non-player teams.", alwaysUseInstanceValues: true)]
public bool NonPlayerTeamInteractable
{
get;
set;
}
[ConditionallyEditable(ConditionallyEditable.ConditionType.IsSwappableItem), Serialize(true, true, alwaysUseInstanceValues: true)]
[ConditionallyEditable(ConditionallyEditable.ConditionType.IsSwappableItem), Serialize(true, IsPropertySaveable.Yes, alwaysUseInstanceValues: true)]
public bool AllowSwapping
{
get;
set;
}
[Serialize(false, true)]
[Serialize(false, IsPropertySaveable.Yes)]
public bool PurchasedNewSwap
{
get;
@@ -262,7 +263,7 @@ namespace Barotrauma
private float rotationRad;
[ConditionallyEditable(ConditionallyEditable.ConditionType.AllowRotating, MinValueFloat = 0.0f, MaxValueFloat = 360.0f, DecimalCount = 1, ValueStep = 1f), Serialize(0.0f, true)]
[ConditionallyEditable(ConditionallyEditable.ConditionType.AllowRotating, MinValueFloat = 0.0f, MaxValueFloat = 360.0f, DecimalCount = 1, ValueStep = 1f), Serialize(0.0f, IsPropertySaveable.Yes)]
public float Rotation
{
get
@@ -331,7 +332,7 @@ namespace Barotrauma
if (scale == value) { return; }
scale = MathHelper.Clamp(value, 0.01f, 10.0f);
float relativeScale = scale / prefab.Scale;
float relativeScale = scale / base.Prefab.Scale;
if (!ResizeHorizontal || !ResizeVertical)
{
@@ -357,21 +358,21 @@ namespace Barotrauma
} = float.PositiveInfinity;
protected Color spriteColor;
[Editable, Serialize("1.0,1.0,1.0,1.0", true)]
[Editable, Serialize("1.0,1.0,1.0,1.0", IsPropertySaveable.Yes)]
public Color SpriteColor
{
get { return spriteColor; }
set { spriteColor = value; }
}
[Serialize("1.0,1.0,1.0,1.0", true), Editable]
[Serialize("1.0,1.0,1.0,1.0", IsPropertySaveable.Yes), Editable]
public Color InventoryIconColor
{
get;
protected set;
}
[Editable, Serialize("1.0,1.0,1.0,1.0", true, description: "Changes the color of the item this item is contained inside. Only has an effect if either of the UseContainedSpriteColor or UseContainedInventoryIconColor property of the container is set to true.")]
[Editable, Serialize("1.0,1.0,1.0,1.0", IsPropertySaveable.Yes, description: "Changes the color of the item this item is contained inside. Only has an effect if either of the UseContainedSpriteColor or UseContainedInventoryIconColor property of the container is set to true.")]
public Color ContainerColor
{
get;
@@ -381,26 +382,26 @@ namespace Barotrauma
/// <summary>
/// Can be used by status effects or conditionals to check what item this item is contained inside
/// </summary>
public string ContainerIdentifier
public Identifier ContainerIdentifier
{
get
{
return
Container?.prefab.Identifier ??
ParentInventory?.Owner?.ToString() ??
"";
Container?.Prefab.Identifier ??
ParentInventory?.Owner?.ToIdentifier() ??
Identifier.Empty;
}
}
[Serialize("", true)]
[Serialize("", IsPropertySaveable.Yes)]
/// <summary>
/// Can be used to modify the AITarget's label using status effects
/// </summary>
public string SonarLabel
{
get { return AiTarget?.SonarLabel ?? ""; }
get { return AiTarget?.SonarLabel?.Value ?? ""; }
set
{
if (AiTarget != null)
@@ -421,7 +422,7 @@ namespace Barotrauma
}
}
[Serialize(0.0f, false)]
[Serialize(0.0f, IsPropertySaveable.No)]
/// <summary>
/// Can be used by status effects or conditionals to modify the sound range
/// </summary>
@@ -431,7 +432,7 @@ namespace Barotrauma
set { if (aiTarget != null) { aiTarget.SoundRange = Math.Max(0.0f, value); } }
}
[Serialize(0.0f, false)]
[Serialize(0.0f, IsPropertySaveable.No)]
/// <summary>
/// Can be used by status effects or conditionals to modify the sound range
/// </summary>
@@ -444,13 +445,13 @@ namespace Barotrauma
/// <summary>
/// Should the item's Use method be called with the "Use" or with the "Shoot" key?
/// </summary>
[Serialize(false, false)]
[Serialize(false, IsPropertySaveable.No)]
public bool IsShootable { get; set; }
/// <summary>
/// If true, the user has to hold the "aim" key before use is registered. False by default.
/// </summary>
[Serialize(false, false)]
[Serialize(false, IsPropertySaveable.No)]
public bool RequireAimToUse
{
get; set;
@@ -459,7 +460,7 @@ namespace Barotrauma
/// <summary>
/// If true, the user has to hold the "aim" key before secondary use is registered. True by default.
/// </summary>
[Serialize(true, false)]
[Serialize(true, IsPropertySaveable.No)]
public bool RequireAimToSecondaryUse
{
get; set;
@@ -475,8 +476,8 @@ namespace Barotrauma
public float ConditionPercentage => MathUtils.Percentage(Condition, MaxCondition);
private float offsetOnSelectedMultiplier = 1.0f;
[Serialize(1.0f, false)]
[Serialize(1.0f, IsPropertySaveable.No)]
public float OffsetOnSelectedMultiplier
{
get => offsetOnSelectedMultiplier;
@@ -485,7 +486,7 @@ namespace Barotrauma
private float healthMultiplier = 1.0f;
[Serialize(1.0f, true, "Multiply the maximum condition by this value")]
[Serialize(1.0f, IsPropertySaveable.Yes, "Multiply the maximum condition by this value")]
public float HealthMultiplier
{
get => healthMultiplier;
@@ -499,7 +500,7 @@ namespace Barotrauma
private float maxRepairConditionMultiplier = 1.0f;
[Serialize(1.0f, true)]
[Serialize(1.0f, IsPropertySaveable.Yes)]
public float MaxRepairConditionMultiplier
{
get => maxRepairConditionMultiplier;
@@ -508,7 +509,7 @@ namespace Barotrauma
//the default value should be Prefab.Health, but because we can't use it in the attribute,
//we'll just use NaN (which does nothing) and set the default value in the constructor/load
[Serialize(float.NaN, false), Editable]
[Serialize(float.NaN, IsPropertySaveable.No), Editable]
public float Condition
{
get { return condition; }
@@ -517,7 +518,7 @@ namespace Barotrauma
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
if (!MathUtils.IsValid(value)) { return; }
if (Indestructible) { return; }
if (InvulnerableToDamage && value <= condition) { return;}
if (InvulnerableToDamage && value <= condition) { return; }
float prev = condition;
bool wasInFullCondition = IsFullCondition;
@@ -525,6 +526,8 @@ namespace Barotrauma
condition = MathHelper.Clamp(value, 0.0f, MaxCondition);
if (condition == 0.0f && prev > 0.0f)
{
//Flag connections to be updated as device is broken
flagChangedConnections(connections);
#if CLIENT
foreach (ItemComponent ic in components)
{
@@ -534,6 +537,11 @@ namespace Barotrauma
#endif
ApplyStatusEffects(ActionType.OnBroken, 1.0f, null);
}
else if (condition > 0.0f && prev <= 0.0f)
{
//Flag connections to be updated as device is now working again
flagChangedConnections(connections);
}
SetActiveSprite();
@@ -559,6 +567,22 @@ namespace Barotrauma
LastConditionChange = condition - prev;
ConditionLastUpdated = Timing.TotalTime;
static void flagChangedConnections(Dictionary<string, Connection> connections)
{
if (connections == null) { return; }
foreach (Connection c in connections.Values)
{
if (c.IsPower && c.Grid != null)
{
Powered.ChangedConnections.Add(c);
foreach (Connection conn in c.Recipients)
{
Powered.ChangedConnections.Add(conn);
}
}
}
}
}
}
@@ -590,7 +614,7 @@ namespace Barotrauma
set;
}
[Editable, Serialize(false, isSaveable: true, "When enabled will prevent the item from taking damage from all sources")]
[Editable, Serialize(false, isSaveable: IsPropertySaveable.Yes, "When enabled will prevent the item from taking damage from all sources")]
public bool InvulnerableToDamage { get; set; }
public bool StolenDuringRound;
@@ -609,7 +633,7 @@ namespace Barotrauma
}
}
[Serialize(true, true, alwaysUseInstanceValues: true)]
[Serialize(true, IsPropertySaveable.Yes, alwaysUseInstanceValues: true)]
public bool AllowStealing
{
get;
@@ -617,7 +641,7 @@ namespace Barotrauma
}
private string originalOutpost;
[Serialize("", true, alwaysUseInstanceValues: true)]
[Serialize("", IsPropertySaveable.Yes, alwaysUseInstanceValues: true)]
public string OriginalOutpost
{
get { return originalOutpost; }
@@ -631,7 +655,7 @@ namespace Barotrauma
}
}
[Editable, Serialize("", true)]
[Editable, Serialize("", IsPropertySaveable.Yes)]
public string Tags
{
get { return string.Join(",", tags); }
@@ -639,7 +663,7 @@ namespace Barotrauma
{
tags.Clear();
// Always add prefab tags
prefab.Tags.ForEach(t => tags.Add(t));
base.Prefab.Tags.ForEach(t => tags.Add(t));
if (!string.IsNullOrWhiteSpace(value))
{
string[] splitTags = value.Split(',');
@@ -647,7 +671,7 @@ namespace Barotrauma
{
string[] splitTag = tag.Trim().Split(':');
splitTag[0] = splitTag[0].ToLowerInvariant();
tags.Add(string.Join(":", splitTag));
tags.Add(string.Join(":", splitTag).ToIdentifier());
}
}
}
@@ -705,10 +729,7 @@ namespace Barotrauma
private set;
} = new List<Connection>(20);
public string ConfigFile
{
get { return Prefab.FilePath; }
}
public ContentPath ConfigFilePath => Prefab.ContentFile.Path;
//which type of inventory slots (head, torso, any, etc) the item can be placed in
private readonly HashSet<InvSlotType> allowedSlots = new HashSet<InvSlotType>();
@@ -743,7 +764,7 @@ namespace Barotrauma
get { return ownInventory; }
}
[Editable, Serialize(false, true, description:
[Editable, Serialize(false, IsPropertySaveable.Yes, description:
"Enable if you want to display the item HUD side by side with another item's HUD, when linked together. " +
"Disclaimer: It's possible or even likely that the views block each other, if they were not designed to be viewed together!")]
public bool DisplaySideBySideWhenLinked { get; set; }
@@ -806,13 +827,13 @@ namespace Barotrauma
public bool IgnoreByAI(Character character) => HasTag("ignorebyai") || OrderedToBeIgnored && character.IsOnPlayerTeam;
public bool OrderedToBeIgnored { get; set; }
public Item(ItemPrefab itemPrefab, Vector2 position, Submarine submarine, ushort id = Entity.NullEntityID)
public Item(ItemPrefab itemPrefab, Vector2 position, Submarine submarine, ushort id = Entity.NullEntityID, bool callOnItemLoaded = true)
: this(new Rectangle(
(int)(position.X - itemPrefab.sprite.size.X / 2 * itemPrefab.Scale),
(int)(position.Y + itemPrefab.sprite.size.Y / 2 * itemPrefab.Scale),
(int)(itemPrefab.sprite.size.X * itemPrefab.Scale),
(int)(itemPrefab.sprite.size.Y * itemPrefab.Scale)),
itemPrefab, submarine, id: id)
(int)(position.X - itemPrefab.Sprite.size.X / 2 * itemPrefab.Scale),
(int)(position.Y + itemPrefab.Sprite.size.Y / 2 * itemPrefab.Scale),
(int)(itemPrefab.Sprite.size.X * itemPrefab.Scale),
(int)(itemPrefab.Sprite.size.Y * itemPrefab.Scale)),
itemPrefab, submarine, callOnItemLoaded, id: id)
{
}
@@ -824,11 +845,11 @@ namespace Barotrauma
public Item(Rectangle newRect, ItemPrefab itemPrefab, Submarine submarine, bool callOnItemLoaded = true, ushort id = Entity.NullEntityID)
: base(itemPrefab, submarine, id)
{
spriteColor = prefab.SpriteColor;
spriteColor = base.Prefab.SpriteColor;
components = new List<ItemComponent>();
drawableComponents = new List<IDrawableComponent>(); hasComponentsToDraw = false;
tags = new HashSet<string>();
tags = new HashSet<Identifier>();
repairables = new List<Repairable>();
defaultRect = newRect;
@@ -841,7 +862,7 @@ namespace Barotrauma
allPropertyObjects.Add(this);
XElement element = itemPrefab.ConfigElement;
ContentXElement element = itemPrefab.ConfigElement;
if (element == null) return;
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
@@ -850,7 +871,7 @@ namespace Barotrauma
SetActiveSprite();
foreach (XElement subElement in element.Elements())
foreach (var subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
@@ -924,7 +945,7 @@ namespace Barotrauma
aiTarget = new AITarget(this, subElement);
break;
default:
ItemComponent ic = ItemComponent.Load(subElement, this, itemPrefab.FilePath);
ItemComponent ic = ItemComponent.Load(subElement, this);
if (ic == null) break;
AddComponent(ic);
@@ -1041,7 +1062,7 @@ namespace Barotrauma
{
defaultRect = defaultRect
};
foreach (KeyValuePair<string, SerializableProperty> property in SerializableProperties)
foreach (KeyValuePair<Identifier, SerializableProperty> property in SerializableProperties)
{
if (!property.Value.Attributes.OfType<Editable>().Any()) continue;
clone.SerializableProperties[property.Key].TrySetValue(clone, property.Value.GetValue(this));
@@ -1058,7 +1079,7 @@ namespace Barotrauma
for (int i = 0; i < components.Count && i < clone.components.Count; i++)
{
foreach (KeyValuePair<string, SerializableProperty> property in components[i].SerializableProperties)
foreach (KeyValuePair<Identifier, SerializableProperty> property in components[i].SerializableProperties)
{
if (!property.Value.Attributes.OfType<Editable>().Any()) continue;
clone.components[i].SerializableProperties[property.Key].TrySetValue(clone.components[i], property.Value.GetValue(components[i]));
@@ -1269,9 +1290,9 @@ namespace Barotrauma
if (!Prefab.AllowDroppingOnSwap || otherItem == null) { return false; }
if (Prefab.AllowDroppingOnSwapWith.Any())
{
foreach (string tagOrIdentifier in Prefab.AllowDroppingOnSwapWith)
foreach (Identifier tagOrIdentifier in Prefab.AllowDroppingOnSwapWith)
{
if (otherItem.prefab.Identifier.Equals(tagOrIdentifier, StringComparison.OrdinalIgnoreCase)) { return true; }
if (otherItem.Prefab.Identifier == tagOrIdentifier) { return true; }
if (otherItem.HasTag(tagOrIdentifier)) { return true; }
}
return false;
@@ -1386,31 +1407,22 @@ namespace Barotrauma
public Item GetRootContainer()
{
if (Container == null) { return null; }
Item rootContainer = Container;
while (rootContainer.Container != null)
{
rootContainer = rootContainer.Container;
}
return rootContainer;
}
/// <summary>
/// Should this item or any of its containers be ignored by the AI?
/// </summary>
public bool IsThisOrAnyContainerIgnoredByAI(Character character)
public bool HasAccess(Character character)
{
if (IgnoreByAI(character)) { return true; }
if (Container == null) { return false; }
if (Container.IgnoreByAI(character)) { return true; }
var container = Container;
while (container.Container != null)
{
container = container.Container;
if (container.IgnoreByAI(character)) { return true; }
}
return false;
if (character.IsBot && IgnoreByAI(character)) { return false; }
if (!IsInteractable(character)) { return false; }
var itemContainer = GetComponent<ItemContainer>();
if (itemContainer != null && !itemContainer.HasAccess(character)) { return false; }
if (Container != null && !Container.HasAccess(character)) { return false; }
return true;
}
public bool IsOwnedBy(Entity entity) => FindParentInventory(i => i.Owner == entity) != null;
@@ -1449,33 +1461,49 @@ namespace Barotrauma
}
public void AddTag(string tag)
{
AddTag(tag.ToIdentifier());
}
public void AddTag(Identifier tag)
{
if (tags.Contains(tag)) { return; }
tags.Add(tag);
}
public bool HasTag(string tag)
{
return HasTag(tag.ToIdentifier());
}
public bool HasTag(Identifier tag)
{
if (tag == null) { return true; }
return tags.Contains(tag) || prefab.Tags.Contains(tag);
return tags.Contains(tag) || base.Prefab.Tags.Contains(tag);
}
public void ReplaceTag(string tag, string newTag)
{
ReplaceTag(tag.ToIdentifier(), newTag.ToIdentifier());
}
public void ReplaceTag(Identifier tag, Identifier newTag)
{
if (!tags.Contains(tag)) { return; }
tags.Remove(tag);
tags.Add(newTag);
}
public IEnumerable<string> GetTags()
public IEnumerable<Identifier> GetTags()
{
return tags;
}
public bool HasTag(IEnumerable<string> allowedTags)
public bool HasTag(IEnumerable<Identifier> allowedTags)
{
if (allowedTags == null) return true;
foreach (string tag in allowedTags)
foreach (Identifier tag in allowedTags)
{
if (tags.Contains(tag)) return true;
}
@@ -1528,7 +1556,7 @@ namespace Barotrauma
foreach (Item containedItem in ContainedItems)
{
if (effect.TargetIdentifiers != null &&
!effect.TargetIdentifiers.Contains(containedItem.prefab.Identifier) &&
!effect.TargetIdentifiers.Contains(((MapEntity)containedItem).Prefab.Identifier) &&
!effect.TargetIdentifiers.Any(id => containedItem.HasTag(id)))
{
continue;
@@ -1731,7 +1759,7 @@ namespace Barotrauma
UpdateTransform();
if (CurrentHull == null && body.SimPosition.Y < ConvertUnits.ToSimUnits(Level.MaxEntityDepth))
{
Spawner?.AddToRemoveQueue(this);
Spawner?.AddItemToRemoveQueue(this);
return;
}
}
@@ -1860,7 +1888,7 @@ namespace Barotrauma
//apply simple angular drag
body.ApplyTorque(body.AngularVelocity * volume * -0.05f);
}
}
private bool OnCollision(Fixture f1, Fixture f2, Contact contact)
@@ -1871,14 +1899,9 @@ namespace Barotrauma
if (projectile != null)
{
//ignore character colliders (a projectile only hits limbs)
if (f2.CollisionCategories == Physics.CollisionCharacter && f2.Body.UserData is Character)
{
return false;
}
if (projectile.IgnoredBodies != null)
{
if (projectile.IgnoredBodies.Contains(f2.Body)) { return false; }
}
if (f2.CollisionCategories == Physics.CollisionCharacter && f2.Body.UserData is Character) { return false; }
if (projectile.IgnoredBodies != null && projectile.IgnoredBodies.Contains(f2.Body)) { return false; }
if (projectile.ShouldIgnoreSubmarineCollision(f2, contact)) { return false; }
}
contact.GetWorldManifold(out Vector2 normal, out _);
@@ -2027,17 +2050,17 @@ namespace Barotrauma
return connectedComponents;
}
public static readonly (string input, string output)[] connectionPairs = new (string input, string output)[]
public static readonly ImmutableArray<(Identifier Input, Identifier Output)> connectionPairs = new (Identifier, Identifier)[]
{
("power_in", "power_out"),
("signal_in1", "signal_out1"),
("signal_in2", "signal_out2"),
("signal_in3", "signal_out3"),
("signal_in4", "signal_out4"),
("signal_in", "signal_out"),
("signal_in1", "signal_out"),
("signal_in2", "signal_out")
};
("power_in".ToIdentifier(), "power_out".ToIdentifier()),
("signal_in1".ToIdentifier(), "signal_out1".ToIdentifier()),
("signal_in2".ToIdentifier(), "signal_out2".ToIdentifier()),
("signal_in3".ToIdentifier(), "signal_out3".ToIdentifier()),
("signal_in4".ToIdentifier(), "signal_out4".ToIdentifier()),
("signal_in".ToIdentifier(), "signal_out".ToIdentifier()),
("signal_in1".ToIdentifier(), "signal_out".ToIdentifier()),
("signal_in2".ToIdentifier(), "signal_out".ToIdentifier())
}.ToImmutableArray();
private void GetConnectedComponentsRecursive<T>(Connection c, HashSet<Connection> alreadySearched, List<T> connectedComponents, bool ignoreInactiveRelays) where T : ItemComponent
{
@@ -2078,34 +2101,30 @@ namespace Barotrauma
if (relay != null && !relay.IsOn) { return; }
}
foreach ((string input, string output) in connectionPairs)
foreach ((Identifier input, Identifier output) in connectionPairs)
{
if (input == c.Name)
void searchFromAToB(Identifier connectionEndA, Identifier connectionEndB)
{
var pairedConnection = c.Item.Connections.FirstOrDefault(c2 => c2.Name == output);
if (pairedConnection != null)
if (connectionEndA == c.Name)
{
if (alreadySearched.Contains(pairedConnection)) { continue; }
GetConnectedComponentsRecursive(pairedConnection, alreadySearched, connectedComponents, ignoreInactiveRelays);
}
}
else if (output == c.Name)
{
var pairedConnection = c.Item.Connections.FirstOrDefault(c2 => c2.Name == input);
if (pairedConnection != null)
{
if (alreadySearched.Contains(pairedConnection)) { continue; }
GetConnectedComponentsRecursive(pairedConnection, alreadySearched, connectedComponents, ignoreInactiveRelays);
var pairedConnection = c.Item.Connections.FirstOrDefault(c2 => c2.Name == connectionEndB);
if (pairedConnection != null)
{
if (alreadySearched.Contains(pairedConnection)) { return; }
GetConnectedComponentsRecursive(pairedConnection, alreadySearched, connectedComponents, ignoreInactiveRelays);
}
}
}
searchFromAToB(input, output);
searchFromAToB(output, input);
}
}
public Controller FindController(string[] tags = null)
public Controller FindController(ImmutableArray<Identifier>? tags = null)
{
//try finding the controller with the simpler non-recursive method first
var controllers = GetConnectedComponents<Controller>();
bool needsTag = tags != null && tags.Length > 0;
bool needsTag = tags != null && tags.Value.Length > 0;
if (controllers.None() || (needsTag && controllers.None(c => c.Item.HasTag(tags))))
{
controllers = GetConnectedComponents<Controller>(recursive: true);
@@ -2119,7 +2138,7 @@ namespace Barotrauma
controllers.FirstOrDefault(c => c.GetFocusTarget() == this) ?? controllers.FirstOrDefault();
}
public bool TryFindController(out Controller controller, string[] tags = null)
public bool TryFindController(out Controller controller, ImmutableArray<Identifier>? tags = null)
{
controller = FindController(tags: tags);
return controller != null;
@@ -2291,11 +2310,12 @@ namespace Barotrauma
//to prevent accidentally selecting items when clicking UI elements
if (user == Character.Controlled && GUI.MouseOn != null)
{
if (GameMain.Config.KeyBind(ic.PickKey).MouseButton == 0)
if (GameSettings.CurrentConfig.KeyMap.Bindings[ic.PickKey].MouseButton == 0)
{
pickHit = false;
}
if (GameMain.Config.KeyBind(ic.SelectKey).MouseButton == 0)
if (GameSettings.CurrentConfig.KeyMap.Bindings[ic.SelectKey].MouseButton == 0)
{
selectHit = false;
}
@@ -2308,7 +2328,7 @@ namespace Barotrauma
//LMB is used to manipulate wires, so using E to select connection panels is much easier
if (Screen.Selected == GameMain.SubEditorScreen && GameMain.SubEditorScreen.WiringMode)
{
pickHit = selectHit = GameMain.Config.KeyBind(InputType.Use).MouseButton == MouseButton.None ?
pickHit = selectHit = GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Use].MouseButton == MouseButton.None ?
user.IsKeyHit(InputType.Use) :
user.IsKeyHit(InputType.Select);
}
@@ -2356,8 +2376,9 @@ namespace Barotrauma
{
if (requiredSkill != null)
{
GUI.AddMessage(TextManager.GetWithVariables("InsufficientSkills", new string[2] { "[requiredskill]", "[requiredlevel]" },
new string[2] { TextManager.Get("SkillName." + requiredSkill.Identifier), ((int)(requiredSkill.Level * skillMultiplier)).ToString() }, new bool[2] { true, false }), GUI.Style.Red);
GUI.AddMessage(TextManager.GetWithVariables("InsufficientSkills",
("[requiredskill]", TextManager.Get("SkillName." + requiredSkill.Identifier), FormatCapitals.Yes),
("[requiredlevel]", ((int)(requiredSkill.Level * skillMultiplier)).ToString(), FormatCapitals.No)), GUIStyle.Red);
}
}
#endif
@@ -2423,7 +2444,7 @@ namespace Barotrauma
if (remove)
{
Spawner.AddToRemoveQueue(this);
Spawner.AddItemToRemoveQueue(this);
}
}
@@ -2456,7 +2477,7 @@ namespace Barotrauma
if (remove)
{
Spawner.AddToRemoveQueue(this);
Spawner.AddItemToRemoveQueue(this);
}
}
@@ -2520,7 +2541,7 @@ namespace Barotrauma
}
if (remove) { Spawner?.AddToRemoveQueue(this); }
if (remove) { Spawner?.AddItemToRemoveQueue(this); }
}
public bool Combine(Item item, Character user)
@@ -2644,6 +2665,10 @@ namespace Barotrauma
{
msg.Write(stringVal);
}
else if (value is Identifier idValue)
{
msg.Write(idValue);
}
else if (value is float floatVal)
{
msg.Write(floatVal);
@@ -2770,6 +2795,12 @@ namespace Barotrauma
property.TrySetValue(parentObject, val);
}
}
else if (type == typeof(Identifier))
{
Identifier val = msg.ReadIdentifier();
logValue = val.Value;
if (allowEditing) { property.TrySetValue(parentObject, val); }
}
else if (type == typeof(float))
{
float val = msg.ReadSingle();
@@ -2887,7 +2918,7 @@ namespace Barotrauma
partial void UpdateNetPosition(float deltaTime);
public static Item Load(XElement element, Submarine submarine, IdRemap idRemap)
public static Item Load(ContentXElement element, Submarine submarine, IdRemap idRemap)
{
return Load(element, submarine, createNetworkEvent: false, idRemap: idRemap);
}
@@ -2899,12 +2930,12 @@ namespace Barotrauma
/// <param name="submarine">The submarine to spawn the item in (can be null)</param>
/// <param name="createNetworkEvent">Should an EntitySpawner event be created to notify clients about the item being created.</param>
/// <returns></returns>
public static Item Load(XElement element, Submarine submarine, bool createNetworkEvent, IdRemap idRemap)
public static Item Load(ContentXElement element, Submarine submarine, bool createNetworkEvent, IdRemap idRemap)
{
string name = element.Attribute("name").Value;
string identifier = element.GetAttributeString("identifier", "");
Identifier identifier = element.GetAttributeIdentifier("identifier", Identifier.Empty);
if (string.IsNullOrWhiteSpace(name) && string.IsNullOrWhiteSpace(identifier))
if (string.IsNullOrWhiteSpace(name) && identifier.IsEmpty)
{
string errorMessage = "Failed to load an item (both name and identifier were null):\n"+element.ToString();
DebugConsole.ThrowError(errorMessage);
@@ -2912,15 +2943,15 @@ namespace Barotrauma
return null;
}
string pendingSwap = element.GetAttributeString("pendingswap", "");
Identifier pendingSwap = element.GetAttributeIdentifier("pendingswap", Identifier.Empty);
ItemPrefab appliedSwap = null;
ItemPrefab oldPrefab = null;
if (!string.IsNullOrEmpty(pendingSwap) && Level.Loaded?.Type != LevelData.LevelType.Outpost)
if (!pendingSwap.IsEmpty && Level.Loaded?.Type != LevelData.LevelType.Outpost)
{
oldPrefab = ItemPrefab.Find(name, identifier);
appliedSwap = ItemPrefab.Find(string.Empty, pendingSwap);
identifier = pendingSwap;
pendingSwap = null;
pendingSwap = Identifier.Empty;
}
ItemPrefab prefab = ItemPrefab.Find(name, identifier);
@@ -2930,8 +2961,8 @@ namespace Barotrauma
Vector2 centerPos = new Vector2(rect.X + rect.Width / 2, rect.Y - rect.Height / 2);
if (appliedSwap != null)
{
rect.Width = (int)(prefab.sprite.size.X * prefab.Scale);
rect.Height = (int)(prefab.sprite.size.Y * prefab.Scale);
rect.Width = (int)(prefab.Sprite.size.X * prefab.Scale);
rect.Height = (int)(prefab.Sprite.size.Y * prefab.Scale);
}
else if (rect.Width == 0 && rect.Height == 0)
{
@@ -2943,7 +2974,7 @@ namespace Barotrauma
{
Submarine = submarine,
linkedToID = new List<ushort>(),
PendingItemSwap = string.IsNullOrEmpty(pendingSwap) ? null : MapEntityPrefab.Find(pendingSwap) as ItemPrefab
PendingItemSwap = pendingSwap.IsEmpty ? null : MapEntityPrefab.Find(pendingSwap.Value) as ItemPrefab
};
#if SERVER
@@ -2955,11 +2986,11 @@ namespace Barotrauma
foreach (XAttribute attribute in (appliedSwap?.ConfigElement ?? element).Attributes())
{
if (!item.SerializableProperties.TryGetValue(attribute.Name.ToString(), out SerializableProperty property)) { continue; }
if (!item.SerializableProperties.TryGetValue(attribute.NameAsIdentifier(), out SerializableProperty property)) { continue; }
bool shouldBeLoaded = false;
foreach (var propertyAttribute in property.Attributes.OfType<Serialize>())
{
if (propertyAttribute.isSaveable)
if (propertyAttribute.IsSaveable == IsPropertySaveable.Yes)
{
shouldBeLoaded = true;
break;
@@ -2975,19 +3006,18 @@ namespace Barotrauma
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer && property.Attributes.OfType<Editable>().Any() &&
(submarine == null || !submarine.Loading))
{
switch (property.Name)
if (property.Name == "Tags" ||
property.Name == "Condition" ||
property.Name == "Description")
{
case "Tags":
case "Condition":
case "Description":
//these can be ignored, they're always written in the spawn data
break;
default:
if (!(property.GetValue(item)?.Equals(prevValue) ?? true))
{
GameMain.NetworkMember.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ChangeProperty, property });
}
break;
//these can be ignored, they're always written in the spawn data
}
else
{
if (!(property.GetValue(item)?.Equals(prevValue) ?? true))
{
GameMain.NetworkMember.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ChangeProperty, property });
}
}
}
}
@@ -2999,15 +3029,15 @@ namespace Barotrauma
//if we're overriding a non-overridden item in a sub/assembly xml or vice versa,
//use the values from the prefab instead of loading them from the sub/assembly xml
bool usePrefabValues = thisIsOverride != prefab.IsOverride || appliedSwap != null;
bool usePrefabValues = thisIsOverride != ItemPrefab.Prefabs.IsOverride(prefab) || appliedSwap != null;
List<ItemComponent> unloadedComponents = new List<ItemComponent>(item.components);
foreach (XElement subElement in element.Elements())
foreach (var subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "upgrade":
{
var upgradeIdentifier = subElement.GetAttributeString("identifier", string.Empty);
var upgradeIdentifier = subElement.GetAttributeIdentifier("identifier", Identifier.Empty);
UpgradePrefab upgradePrefab = UpgradePrefab.Find(upgradeIdentifier);
int level = subElement.GetAttributeInt("level", 1);
if (upgradePrefab != null)
@@ -3039,8 +3069,8 @@ namespace Barotrauma
item.Upgrades.ForEach(upgrade => upgrade.ApplyUpgrade());
var availableSwapIds = element.GetAttributeStringArray("availableswaps", new string[0]);
foreach (string swapId in availableSwapIds)
var availableSwapIds = element.GetAttributeIdentifierArray("availableswaps", Array.Empty<Identifier>());
foreach (Identifier swapId in availableSwapIds)
{
ItemPrefab swapPrefab = ItemPrefab.Find(string.Empty, swapId);
if (swapPrefab != null)
@@ -3140,7 +3170,7 @@ namespace Barotrauma
if (Rotation != 0f) { element.Add(new XAttribute("rotation", Rotation)); }
if (Prefab.IsOverride) { element.Add(new XAttribute("isoverride", "true")); }
if (ItemPrefab.Prefabs.IsOverride(Prefab)) { element.Add(new XAttribute("isoverride", "true")); }
if (FlippedX) { element.Add(new XAttribute("flippedx", true)); }
if (FlippedY) { element.Add(new XAttribute("flippedy", true)); }
@@ -3332,7 +3362,7 @@ namespace Barotrauma
List<Item> list = new List<Item>(ItemList);
foreach (Item item in list)
{
if (item.prefab == prefab)
if (((MapEntity)item).Prefab == prefab)
{
item.Remove();
}