Merge branch 'dev' of https://github.com/Regalis11/Barotrauma.git into unstable-tests

This commit is contained in:
Evil Factory
2022-04-08 12:52:28 -03:00
990 changed files with 44338 additions and 38589 deletions
@@ -12,6 +12,16 @@ namespace Barotrauma.Items.Components
{
partial class Holdable : Pickable, IServerSerializable, IClientSerializable
{
private readonly struct EventData : IEventData
{
public readonly Vector2 AttachPos;
public EventData(Vector2 attachPos)
{
AttachPos = attachPos;
}
}
const float MaxAttachDistance = 150.0f;
//the position(s) in the item that the Character grabs
@@ -19,7 +29,7 @@ namespace Barotrauma.Items.Components
private readonly Vector2[] scaledHandlePos;
private readonly InputType prevPickKey;
private string prevMsg;
private LocalizedString prevMsg;
private Dictionary<RelatedItem.RelationType, List<RelatedItem>> prevRequiredItems;
//the distance from the holding characters elbow to center of the physics body of the item
@@ -39,7 +49,7 @@ namespace Barotrauma.Items.Components
get;
private set;
}
[Serialize(true, true, description: "Is the item currently able to push characters around? True by default. Only valid if blocksplayers is set to true.")]
[Serialize(true, IsPropertySaveable.Yes, description: "Is the item currently able to push characters around? True by default. Only valid if blocksplayers is set to true.")]
public bool CanPush
{
get;
@@ -54,7 +64,7 @@ namespace Barotrauma.Items.Components
get { return item.body ?? body; }
}
[Serialize(false, true, description: "Is the item currently attached to a wall (only valid if Attachable is set to true).")]
[Serialize(false, IsPropertySaveable.Yes, description: "Is the item currently attached to a wall (only valid if Attachable is set to true).")]
public bool Attached
{
get { return attached && item.ParentInventory == null; }
@@ -65,56 +75,56 @@ namespace Barotrauma.Items.Components
}
}
[Serialize(true, true, description: "Can the item be pointed to a specific direction or do the characters always hold it in a static pose.")]
[Serialize(true, IsPropertySaveable.Yes, description: "Can the item be pointed to a specific direction or do the characters always hold it in a static pose.")]
public bool Aimable
{
get;
set;
}
[Serialize(false, false, description: "Should the character adjust its pose when aiming with the item. Most noticeable underwater, where the character will rotate its entire body to face the direction the item is aimed at.")]
[Serialize(false, IsPropertySaveable.No, description: "Should the character adjust its pose when aiming with the item. Most noticeable underwater, where the character will rotate its entire body to face the direction the item is aimed at.")]
public bool ControlPose
{
get;
set;
}
[Serialize(false, false, description: "Use the hand rotation instead of torso rotation for the item hold angle. Enable this if you want the item just to follow with the arm when not aiming instead of forcing the arm to a hold pose.")]
[Serialize(false, IsPropertySaveable.No, description: "Use the hand rotation instead of torso rotation for the item hold angle. Enable this if you want the item just to follow with the arm when not aiming instead of forcing the arm to a hold pose.")]
public bool UseHandRotationForHoldAngle
{
get;
set;
}
[Serialize(false, false, description: "Can the item be attached to walls.")]
[Serialize(false, IsPropertySaveable.No, description: "Can the item be attached to walls.")]
public bool Attachable
{
get { return attachable; }
set { attachable = value; }
}
[Serialize(true, false, description: "Can the item be reattached to walls after it has been deattached (only valid if Attachable is set to true).")]
[Serialize(true, IsPropertySaveable.No, description: "Can the item be reattached to walls after it has been deattached (only valid if Attachable is set to true).")]
public bool Reattachable
{
get;
set;
}
[Serialize(false, false, description: "Can the item only be attached in limited amount? Uses permanent stat values to check for legibility.")]
[Serialize(false, IsPropertySaveable.No, description: "Can the item only be attached in limited amount? Uses permanent stat values to check for legibility.")]
public bool LimitedAttachable
{
get;
set;
}
[Serialize(false, false, description: "Should the item be attached to a wall by default when it's placed in the submarine editor.")]
[Serialize(false, IsPropertySaveable.No, description: "Should the item be attached to a wall by default when it's placed in the submarine editor.")]
public bool AttachedByDefault
{
get { return attachedByDefault; }
set { attachedByDefault = value; }
}
[Editable, Serialize("0.0,0.0", false, description: "The position the character holds the item at (in pixels, as an offset from the character's shoulder)."+
[Editable, Serialize("0.0,0.0", IsPropertySaveable.No, description: "The position the character holds the item at (in pixels, as an offset from the character's shoulder)."+
" For example, a value of 10,-100 would make the character hold the item 100 pixels below the shoulder and 10 pixels forwards.")]
public Vector2 HoldPos
{
@@ -122,7 +132,7 @@ namespace Barotrauma.Items.Components
set { holdPos = ConvertUnits.ToSimUnits(value); }
}
[Serialize("0.0,0.0", false, description: "The position the character holds the item at when aiming (in pixels, as an offset from the character's shoulder)."+
[Serialize("0.0,0.0", IsPropertySaveable.No, description: "The position the character holds the item at when aiming (in pixels, as an offset from the character's shoulder)."+
" Works similarly as HoldPos, except that the position is rotated according to the direction the player is aiming at. For example, a value of 10,-100 would make the character hold the item 100 pixels below the shoulder and 10 pixels forwards when aiming directly to the right.")]
public Vector2 AimPos
{
@@ -130,7 +140,7 @@ namespace Barotrauma.Items.Components
set { aimPos = ConvertUnits.ToSimUnits(value); }
}
[Editable, Serialize(0.0f, false, description: "The rotation at which the character holds the item (in degrees, relative to the rotation of the character's hand).")]
[Editable, Serialize(0.0f, IsPropertySaveable.No, description: "The rotation at which the character holds the item (in degrees, relative to the rotation of the character's hand).")]
public float HoldAngle
{
get { return MathHelper.ToDegrees(holdAngle); }
@@ -138,24 +148,31 @@ namespace Barotrauma.Items.Components
}
private Vector2 swingAmount;
[Editable, Serialize("0.0,0.0", false, description: "How much the item swings around when aiming/holding it (in pixels, as an offset from AimPos/HoldPos).")]
[Editable, Serialize("0.0,0.0", IsPropertySaveable.No, description: "How much the item swings around when aiming/holding it (in pixels, as an offset from AimPos/HoldPos).")]
public Vector2 SwingAmount
{
get { return ConvertUnits.ToDisplayUnits(swingAmount); }
set { swingAmount = ConvertUnits.ToSimUnits(value); }
}
[Editable, Serialize(0.0f, false, description: "How fast the item swings around when aiming/holding it (only valid if SwingAmount is set).")]
[Editable, Serialize(0.0f, IsPropertySaveable.No, description: "How fast the item swings around when aiming/holding it (only valid if SwingAmount is set).")]
public float SwingSpeed { get; set; }
[Editable, Serialize(false, false, description: "Should the item swing around when it's being held.")]
[Editable, Serialize(false, IsPropertySaveable.No, description: "Should the item swing around when it's being held.")]
public bool SwingWhenHolding { get; set; }
[Editable, Serialize(false, false, description: "Should the item swing around when it's being aimed.")]
[Editable, Serialize(false, IsPropertySaveable.No, description: "Should the item swing around when it's being aimed.")]
public bool SwingWhenAiming { get; set; }
[Editable, Serialize(false, false, description: "Should the item swing around when it's being used (for example, when firing a weapon or a welding tool).")]
[Editable, Serialize(false, IsPropertySaveable.No, description: "Should the item swing around when it's being used (for example, when firing a weapon or a welding tool).")]
public bool SwingWhenUsing { get; set; }
public Holdable(Item item, XElement element)
[ConditionallyEditable(ConditionallyEditable.ConditionType.Attachable, MinValueFloat = 0.0f, MaxValueFloat = 0.999f, DecimalCount = 3), Serialize(0.55f, IsPropertySaveable.No, description: "Sprite depth that's used when the item is NOT attached to a wall.")]
public float SpriteDepthWhenDropped
{
get;
set;
}
public Holdable(Item item, ContentXElement element)
: base(item, element)
{
body = item.body;
@@ -183,7 +200,7 @@ namespace Barotrauma.Items.Components
{
int index = i - 1;
string attributeName = "handle" + i;
var attribute = element.Attribute(attributeName);
var attribute = element.GetAttribute(attributeName);
// If no value is defind for handle2, use the value of handle1.
var value = attribute != null ? ConvertUnits.ToSimUnits(XMLExtensions.ParseVector2(attribute.Value)) : previousValue;
handlePos[index] = value;
@@ -237,12 +254,12 @@ namespace Barotrauma.Items.Components
}
}
private bool loadedFromXml;
public override void Load(XElement componentElement, bool usePrefabValues, IdRemap idRemap)
private bool loadedFromInstance;
public override void Load(ContentXElement componentElement, bool usePrefabValues, IdRemap idRemap)
{
base.Load(componentElement, usePrefabValues, idRemap);
loadedFromXml = true;
loadedFromInstance = true;
if (usePrefabValues)
{
@@ -523,6 +540,11 @@ namespace Barotrauma.Items.Components
{
if (!attachable) { return; }
if (body == null)
{
throw new InvalidOperationException($"Tried to attach an item with no physics body to a wall ({item.Prefab.Identifier}).");
}
//outside hulls/subs -> we need to check if the item is being attached on a structure outside the sub
if (item.CurrentHull == null && item.Submarine == null)
{
@@ -570,6 +592,9 @@ namespace Barotrauma.Items.Components
requiredItems = new Dictionary<RelatedItem.RelationType, List<RelatedItem>>(prevRequiredItems);
Attached = true;
#if CLIENT
item.DrawDepthOffset = 0.0f;
#endif
}
public void DeattachFromWall()
@@ -578,11 +603,16 @@ namespace Barotrauma.Items.Components
Attached = false;
attachTargetCell = null;
#if CLIENT
item.DrawDepthOffset = 0.0f;
#endif
//make the item pickable with the default pick key and with no specific tools/items when it's deattached
requiredItems.Clear();
DisplayMsg = "";
PickKey = InputType.Select;
#if CLIENT
item.DrawDepthOffset = SpriteDepthWhenDropped - item.SpriteDepth;
#endif
}
public override void ParseMsg()
@@ -615,7 +645,7 @@ namespace Barotrauma.Items.Components
int maxAttachableCount = (int)character.Info.GetSavedStatValue(StatTypes.MaxAttachableCount, item.Prefab.Identifier);
int currentlyAttachedCount = Item.ItemList.Count(
i => i.Submarine == attachTarget?.Submarine && i.GetComponent<Holdable>() is Holdable holdable && holdable.Attached && i.Prefab.Identifier == item.prefab.Identifier);
i => i.Submarine == attachTarget?.Submarine && i.GetComponent<Holdable>() is Holdable holdable && holdable.Attached && i.Prefab.Identifier == item.Prefab.Identifier);
if (maxAttachableCount == 0)
{
#if CLIENT
@@ -623,7 +653,7 @@ namespace Barotrauma.Items.Components
#endif
return false;
}
else if (currentlyAttachedCount >= maxAttachableCount)
else if (currentlyAttachedCount >= maxAttachableCount)
{
#if CLIENT
GUI.AddMessage($"{TextManager.Get("itemmsgtotalnumberlimited")} ({currentlyAttachedCount}/{maxAttachableCount})", Color.Red);
@@ -646,12 +676,7 @@ namespace Barotrauma.Items.Components
{
#if CLIENT
Vector2 attachPos = ConvertUnits.ToSimUnits(GetAttachPosition(character));
GameMain.Client.CreateEntityEvent(item, new object[]
{
NetEntityEvent.Type.ComponentState,
item.GetComponentIndex(this),
attachPos
});
item.CreateClientEvent(this, new EventData(attachPos));
#endif
}
return false;
@@ -850,8 +875,8 @@ namespace Barotrauma.Items.Components
{
if (!attachable) { return; }
//a mod has overridden the item, and the base item didn't have a Holdable component = a mod made the item movable/detachable
if (item.Prefab.IsOverride && !loadedFromXml)
//the Holdable component didn't get loaded from an instance of the item, just the prefab xml = a mod or update must've made the item movable/detachable
if (!loadedFromInstance)
{
if (attachedByDefault)
{
@@ -1,64 +1,112 @@
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Collections.Immutable;
namespace Barotrauma.Items.Components
{
partial class IdCard : Pickable
{
[Serialize(CharacterTeamType.None, true, alwaysUseInstanceValues: true)]
[Serialize(CharacterTeamType.None, IsPropertySaveable.Yes, alwaysUseInstanceValues: true)]
public CharacterTeamType TeamID
{
get;
set;
}
[Serialize(0, true, alwaysUseInstanceValues: true)]
[Serialize(0, IsPropertySaveable.Yes, alwaysUseInstanceValues: true)]
public int SubmarineSpecificID
{
get;
set;
}
[Serialize("", IsPropertySaveable.Yes, alwaysUseInstanceValues: true)]
public string OwnerTags
{
get => string.Join(',', OwnerTagSet);
set => OwnerTagSet = value.Split(',').ToIdentifiers().ToImmutableHashSet();
}
[Serialize("", IsPropertySaveable.Yes, alwaysUseInstanceValues: true)]
public string Description
{
get;
set;
}
private JobPrefab cachedJobPrefab;
private string cachedName;
public IdCard(Item item, XElement element) : base(item, element)
{
public ImmutableHashSet<Identifier> OwnerTagSet { get; set; }
}
[Serialize("", IsPropertySaveable.Yes, alwaysUseInstanceValues: true)]
public string OwnerName { get; set; }
[Serialize("", IsPropertySaveable.Yes, alwaysUseInstanceValues: true)]
public Identifier OwnerJobId { get; set; }
public void Initialize(CharacterInfo info)
public JobPrefab OwnerJob => JobPrefab.Prefabs.TryGet(OwnerJobId, out var prefab) ? prefab : null;
[Serialize(-1, IsPropertySaveable.Yes, alwaysUseInstanceValues: true)]
public int OwnerHairIndex { get; set; }
[Serialize(-1, IsPropertySaveable.Yes, alwaysUseInstanceValues: true)]
public int OwnerBeardIndex { get; set; }
[Serialize(-1, IsPropertySaveable.Yes, alwaysUseInstanceValues: true)]
public int OwnerMoustacheIndex { get; set; }
[Serialize(-1, IsPropertySaveable.Yes, alwaysUseInstanceValues: true)]
public int OwnerFaceAttachmentIndex { get; set; }
[Serialize("#ffffff", IsPropertySaveable.Yes, alwaysUseInstanceValues: true)]
public Color OwnerHairColor { get; set; }
[Serialize("#ffffff", IsPropertySaveable.Yes, alwaysUseInstanceValues: true)]
public Color OwnerFacialHairColor { get; set; }
[Serialize("#ffffff", IsPropertySaveable.Yes, alwaysUseInstanceValues: true)]
public Color OwnerSkinColor { get; set; }
[Serialize("0,0", IsPropertySaveable.Yes, alwaysUseInstanceValues: true)]
public Vector2 OwnerSheetIndex { get; set; }
public IdCard(Item item, ContentXElement element) : base(item, element) { }
public void Initialize(WayPoint spawnPoint, Character character)
{
item.AddTag("name:" + character.Name);
CharacterInfo info = character.Info;
if (info == null) { return; }
if (info.Job?.Prefab != null)
if (spawnPoint != null)
{
item.AddTag("jobid:" + info.Job.Prefab.Identifier);
foreach (string s in spawnPoint.IdCardTags)
{
item.AddTag(s);
}
if (!string.IsNullOrWhiteSpace(spawnPoint.IdCardDesc))
{
item.Description = Description = spawnPoint.IdCardDesc;
}
}
TeamID = info.TeamID;
var head = info.Head;
if (head == null) { return; }
if (info.HasGenders) { item.AddTag($"gender:{head.gender.ToString().ToLowerInvariant()}"); }
if (info.HasRaces) { item.AddTag($"race:{head.race}"); }
item.AddTag($"headspriteid:{info.HeadSpriteId}");
item.AddTag($"hairindex:{head.HairIndex}");
item.AddTag($"beardindex:{head.BeardIndex}");
item.AddTag($"moustacheindex:{head.MoustacheIndex}");
item.AddTag($"faceattachmentindex:{head.FaceAttachmentIndex}");
item.AddTag($"haircolor:{head.HairColor.ToStringHex()}");
item.AddTag($"facialhaircolor:{head.FacialHairColor.ToStringHex()}");
item.AddTag($"skincolor:{head.SkinColor.ToStringHex()}");
if (head.SheetIndex != null)
{
item.AddTag($"sheetindex:{head.SheetIndex.Value.X};{head.SheetIndex.Value.Y}");
}
OwnerName = info.Name;
OwnerJobId = info.Job?.Prefab.Identifier ?? Identifier.Empty;
OwnerTagSet = info.Head.Preset.TagSet;
OwnerHairIndex = head.HairIndex;
OwnerBeardIndex = head.BeardIndex;
OwnerMoustacheIndex = head.MoustacheIndex;
OwnerFaceAttachmentIndex = head.FaceAttachmentIndex;
OwnerHairColor = head.HairColor;
OwnerFacialHairColor = head.FacialHairColor;
OwnerSkinColor = head.SkinColor;
OwnerSheetIndex = head.SheetIndex;
}
public override void Equip(Character character)
@@ -72,48 +120,12 @@ namespace Barotrauma.Items.Components
base.Unequip(character);
character.Info?.CheckDisguiseStatus(true, this);
}
public JobPrefab GetJob()
public override void OnItemLoaded()
{
if (cachedJobPrefab != null)
if (!string.IsNullOrEmpty(Description))
{
return cachedJobPrefab;
item.Description = Description;
}
foreach (string tag in item.GetTags())
{
if (tag.StartsWith("jobid:"))
{
string jobIdentifier = tag.Split(':').Last();
if (JobPrefab.Get(jobIdentifier) is { } jobPrefab)
{
cachedJobPrefab = jobPrefab;
return jobPrefab;
}
}
}
return null;
}
public string GetName()
{
if (cachedName != null)
{
return cachedName;
}
foreach (string tag in item.GetTags())
{
if (tag.StartsWith("name:"))
{
string ownerName = tag.Split(':').Last();
cachedName = ownerName;
return ownerName;
}
}
return null;
}
}
}
@@ -15,14 +15,14 @@ namespace Barotrauma.Items.Components
private float deattachTimer;
[Serialize(1.0f, false, description: "How long it takes to deattach the item from the level walls (in seconds).")]
[Serialize(1.0f, IsPropertySaveable.No, description: "How long it takes to deattach the item from the level walls (in seconds).")]
public float DeattachDuration
{
get;
set;
}
[Serialize(0.0f, false, description: "How far along the item is to being deattached. When the timer goes above DeattachDuration, the item is deattached.")]
[Serialize(0.0f, IsPropertySaveable.No, description: "How far along the item is to being deattached. When the timer goes above DeattachDuration, the item is deattached.")]
public float DeattachTimer
{
get { return deattachTimer; }
@@ -53,7 +53,7 @@ namespace Barotrauma.Items.Components
{
if (holdable.Attached)
{
GameAnalyticsManager.AddDesignEvent("ResourceCollected:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "none") + ":" + item.Prefab.Identifier);
GameAnalyticsManager.AddDesignEvent("ResourceCollected:" + (GameMain.GameSession?.GameMode?.Preset.Identifier.Value ?? "none") + ":" + item.Prefab.Identifier);
holdable.DeattachFromWall();
}
trigger.Enabled = false;
@@ -62,7 +62,7 @@ namespace Barotrauma.Items.Components
}
}
[Serialize(1.0f, false, description: "How much the position of the item can vary from the wall the item spawns on.")]
[Serialize(1.0f, IsPropertySaveable.No, description: "How much the position of the item can vary from the wall the item spawns on.")]
public float RandomOffsetFromWall
{
get;
@@ -74,7 +74,7 @@ namespace Barotrauma.Items.Components
get { return holdable != null && holdable.Attached; }
}
public LevelResource(Item item, XElement element) : base(item, element)
public LevelResource(Item item, ContentXElement element) : base(item, element)
{
IsActive = true;
}
@@ -29,34 +29,34 @@ namespace Barotrauma.Items.Components
public Character User { get; private set; }
[Serialize(0.0f, false, description: "An estimation of how close the item has to be to the target for it to hit. Used by AI characters to determine when they're close enough to hit a target.")]
[Serialize(0.0f, IsPropertySaveable.No, description: "An estimation of how close the item has to be to the target for it to hit. Used by AI characters to determine when they're close enough to hit a target.")]
public float Range
{
get { return ConvertUnits.ToDisplayUnits(range); }
set { range = ConvertUnits.ToSimUnits(value); }
}
[Serialize(0.5f, false, description: "How long the user has to wait before they can hit with the weapon again (in seconds).")]
[Serialize(0.5f, IsPropertySaveable.No, description: "How long the user has to wait before they can hit with the weapon again (in seconds).")]
public float Reload
{
get { return reload; }
set { reload = Math.Max(0.0f, value); }
}
[Serialize(false, false, description: "Can the weapon hit multiple targets per swing.")]
[Serialize(false, IsPropertySaveable.No, description: "Can the weapon hit multiple targets per swing.")]
public bool AllowHitMultiple
{
get;
set;
}
[Editable, Serialize(true, false)]
[Editable, Serialize(true, IsPropertySaveable.No)]
public bool Swing { get; set; }
[Editable, Serialize("2.0, 0.0", false)]
[Editable, Serialize("2.0, 0.0", IsPropertySaveable.No)]
public Vector2 SwingPos { get; set; }
[Editable, Serialize("3.0, -1.0", false)]
[Editable, Serialize("3.0, -1.0", IsPropertySaveable.No)]
public Vector2 SwingForce { get; set; }
public bool Hitting { get { return hitting; } }
@@ -64,12 +64,12 @@ namespace Barotrauma.Items.Components
/// <summary>
/// Defines items that boost the weapon functionality, like battery cell for stun batons.
/// </summary>
public readonly string[] PreferredContainedItems;
public readonly Identifier[] PreferredContainedItems;
public MeleeWeapon(Item item, XElement element)
public MeleeWeapon(Item item, ContentXElement element)
: base(item, element)
{
foreach (XElement subElement in element.Elements())
foreach (var subElement in element.Elements())
{
if (!subElement.Name.ToString().Equals("attack", StringComparison.OrdinalIgnoreCase)) { continue; }
Attack = new Attack(subElement, item.Name + ", MeleeWeapon", item)
@@ -79,7 +79,7 @@ namespace Barotrauma.Items.Components
}
item.IsShootable = true;
item.RequireAimToUse = element.Parent.GetAttributeBool("requireaimtouse", true);
PreferredContainedItems = element.GetAttributeStringArray("preferredcontaineditems", new string[0], convertToLowerInvariant: true);
PreferredContainedItems = element.GetAttributeIdentifierArray("preferredcontaineditems", Array.Empty<Identifier>());
}
public override void Equip(Character character)
@@ -437,13 +437,10 @@ namespace Barotrauma.Items.Components
#if SERVER
if (GameMain.Server != null && targetCharacter != null) //TODO: Log structure hits
{
GameMain.Server.CreateEntityEvent(item, new object[]
{
Networking.NetEntityEvent.Type.ApplyStatusEffect,
GameMain.Server.CreateEntityEvent(item, new Item.ApplyStatusEffectEventData(
success ? ActionType.OnUse : ActionType.OnFailure,
null, //itemcomponent
targetCharacter.ID, targetLimb
});
targetItemComponent: null,
targetCharacter, targetLimb));
string logStr = picker?.LogName + " used " + item.Name;
if (item.ContainedItems != null && item.ContainedItems.Any())
@@ -462,7 +459,7 @@ namespace Barotrauma.Items.Components
if (DeleteOnUse)
{
Entity.Spawner.AddToRemoveQueue(item);
Entity.Spawner.AddItemToRemoveQueue(item);
}
}
}
@@ -36,8 +36,8 @@ namespace Barotrauma.Items.Components
return picker;
}
}
public Pickable(Item item, XElement element)
public Pickable(Item item, ContentXElement element)
: base(item, element)
{
allowedSlots = new List<InvSlotType>();
@@ -181,7 +181,7 @@ namespace Barotrauma.Items.Components
this,
item.WorldPosition,
pickTimer / requiredTime,
GUI.Style.Red, GUI.Style.Green,
GUIStyle.Red, GUIStyle.Green,
!string.IsNullOrWhiteSpace(PickingMsg) ? PickingMsg : this is Door ? "progressbar.opening" : "progressbar.deattaching");
#endif
@@ -282,12 +282,12 @@ namespace Barotrauma.Items.Components
}
}
public virtual void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
public virtual void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
{
msg.Write(activePicker == null ? (ushort)0 : activePicker.ID);
msg.Write(activePicker?.ID ?? (ushort)0);
}
public virtual void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
public virtual void ClientEventRead(IReadMessage msg, float sendingTime)
{
ushort pickerID = msg.ReadUInt16();
if (pickerID == 0)
@@ -17,15 +17,15 @@ namespace Barotrauma.Items.Components
private float useState;
[Serialize(UseEnvironment.Both, false, description: "Can the item be used in air, underwater or both.")]
[Serialize(UseEnvironment.Both, IsPropertySaveable.No, description: "Can the item be used in air, underwater or both.")]
public UseEnvironment UsableIn { get; set; }
[Serialize(0.0f, false, description: "The force to apply to the user's body."), Editable(MinValueFloat = -1000.0f, MaxValueFloat = 1000.0f)]
[Serialize(0.0f, IsPropertySaveable.No, description: "The force to apply to the user's body."), Editable(MinValueFloat = -1000.0f, MaxValueFloat = 1000.0f)]
public float Force { get; set; }
#if CLIENT
private string particles;
[Serialize("", false, description: "The name of the particle prefab the item emits when used.")]
[Serialize("", IsPropertySaveable.No, description: "The name of the particle prefab the item emits when used.")]
public string Particles
{
get { return particles; }
@@ -33,7 +33,7 @@ namespace Barotrauma.Items.Components
}
#endif
public Propulsion(Item item, XElement element)
public Propulsion(Item item, ContentXElement element)
: base(item,element)
{
}
@@ -18,49 +18,49 @@ namespace Barotrauma.Items.Components
private Vector2 barrelPos;
[Serialize("0.0,0.0", false, description: "The position of the barrel as an offset from the item's center (in pixels). Determines where the projectiles spawn.")]
[Serialize("0.0,0.0", IsPropertySaveable.No, description: "The position of the barrel as an offset from the item's center (in pixels). Determines where the projectiles spawn.")]
public string BarrelPos
{
get { return XMLExtensions.Vector2ToString(ConvertUnits.ToDisplayUnits(barrelPos)); }
set { barrelPos = ConvertUnits.ToSimUnits(XMLExtensions.ParseVector2(value)); }
}
[Serialize(1.0f, false, description: "How long the user has to wait before they can fire the weapon again (in seconds).")]
[Serialize(1.0f, IsPropertySaveable.No, description: "How long the user has to wait before they can fire the weapon again (in seconds).")]
public float Reload
{
get { return reload; }
set { reload = Math.Max(value, 0.0f); }
}
[Serialize(false, false, description: "Tells the AI to hold the trigger down when it uses this weapon")]
[Serialize(false, IsPropertySaveable.No, description: "Tells the AI to hold the trigger down when it uses this weapon")]
public bool HoldTrigger
{
get;
set;
}
[Serialize(1, false, description: "How projectiles the weapon launches when fired once.")]
[Serialize(1, IsPropertySaveable.No, description: "How projectiles the weapon launches when fired once.")]
public int ProjectileCount
{
get;
set;
}
[Serialize(0.0f, false, description: "Random spread applied to the firing angle of the projectiles when used by a character with sufficient skills to use the weapon (in degrees).")]
[Serialize(0.0f, IsPropertySaveable.No, description: "Random spread applied to the firing angle of the projectiles when used by a character with sufficient skills to use the weapon (in degrees).")]
public float Spread
{
get;
set;
}
[Serialize(0.0f, false, description: "Random spread applied to the firing angle of the projectiles when used by a character with insufficient skills to use the weapon (in degrees).")]
[Serialize(0.0f, IsPropertySaveable.No, description: "Random spread applied to the firing angle of the projectiles when used by a character with insufficient skills to use the weapon (in degrees).")]
public float UnskilledSpread
{
get;
set;
}
[Serialize(0f, true, description: "The time required for a charge-type turret to charge up before able to fire.")]
[Serialize(0f, IsPropertySaveable.Yes, description: "The time required for a charge-type turret to charge up before able to fire.")]
public float MaxChargeTime
{
get;
@@ -92,7 +92,7 @@ namespace Barotrauma.Items.Components
private float currentChargeTime;
private bool tryingToCharge;
public RangedWeapon(Item item, XElement element)
public RangedWeapon(Item item, ContentXElement element)
: base(item, element)
{
item.IsShootable = true;
@@ -102,7 +102,7 @@ namespace Barotrauma.Items.Components
InitProjSpecific(element);
}
partial void InitProjSpecific(XElement element);
partial void InitProjSpecific(ContentXElement element);
public override void Equip(Character character)
{
@@ -17,7 +17,7 @@ namespace Barotrauma.Items.Components
Air, Water, Both, None
};
private readonly List<string> fixableEntities;
private readonly HashSet<Identifier> fixableEntities;
private Vector2 pickedPosition;
private float activeTimer;
@@ -25,88 +25,88 @@ namespace Barotrauma.Items.Components
private readonly List<Body> ignoredBodies = new List<Body>();
[Serialize("Both", false, description: "Can the item be used in air, water or both.")]
[Serialize("Both", IsPropertySaveable.No, description: "Can the item be used in air, water or both.")]
public UseEnvironment UsableIn
{
get; set;
}
[Serialize(0.0f, false, description: "The distance at which the item can repair targets.")]
[Serialize(0.0f, IsPropertySaveable.No, description: "The distance at which the item can repair targets.")]
public float Range { get; set; }
[Serialize(0.0f, false, description: "Random spread applied to the firing angle when used by a character with sufficient skills to use the tool (in degrees).")]
[Serialize(0.0f, IsPropertySaveable.No, description: "Random spread applied to the firing angle when used by a character with sufficient skills to use the tool (in degrees).")]
public float Spread
{
get;
set;
}
[Serialize(0.0f, false, description: "Random spread applied to the firing angle when used by a character with insufficient skills to use the tool (in degrees).")]
[Serialize(0.0f, IsPropertySaveable.No, description: "Random spread applied to the firing angle when used by a character with insufficient skills to use the tool (in degrees).")]
public float UnskilledSpread
{
get;
set;
}
[Serialize(0.0f, false, description: "How many units of damage the item removes from structures per second.")]
[Serialize(0.0f, IsPropertySaveable.No, description: "How many units of damage the item removes from structures per second.")]
public float StructureFixAmount
{
get; set;
}
[Serialize(0.0f, false, description: "How much damage is applied to ballast flora.")]
[Serialize(0.0f, IsPropertySaveable.No, description: "How much damage is applied to ballast flora.")]
public float FireDamage
{
get; set;
}
[Serialize(0.0f, false, description: "How many units of damage the item removes from destructible level walls per second.")]
[Serialize(0.0f, IsPropertySaveable.No, description: "How many units of damage the item removes from destructible level walls per second.")]
public float LevelWallFixAmount
{
get; set;
}
[Serialize(0.0f, false, description: "How much the item decreases the size of fires per second.")]
[Serialize(0.0f, IsPropertySaveable.No, description: "How much the item decreases the size of fires per second.")]
public float ExtinguishAmount
{
get; set;
}
[Serialize(0.0f, false, description: "How much water the item provides to planters per second.")]
[Serialize(0.0f, IsPropertySaveable.No, description: "How much water the item provides to planters per second.")]
public float WaterAmount { get; set; }
[Serialize("0.0,0.0", false, description: "The position of the barrel as an offset from the item's center (in pixels).")]
[Serialize("0.0,0.0", IsPropertySaveable.No, description: "The position of the barrel as an offset from the item's center (in pixels).")]
public Vector2 BarrelPos { get; set; }
[Serialize(false, false, description: "Can the item repair things through walls.")]
[Serialize(false, IsPropertySaveable.No, description: "Can the item repair things through walls.")]
public bool RepairThroughWalls { get; set; }
[Serialize(false, false, description: "Can the item repair multiple things at once, or will it only affect the first thing the ray from the barrel hits.")]
[Serialize(false, IsPropertySaveable.No, description: "Can the item repair multiple things at once, or will it only affect the first thing the ray from the barrel hits.")]
public bool RepairMultiple { get; set; }
[Serialize(false, false, description: "Can the item repair things through holes in walls.")]
[Serialize(false, IsPropertySaveable.No, description: "Can the item repair things through holes in walls.")]
public bool RepairThroughHoles { get; set; }
[Serialize(100.0f, false, description: "How far two walls need to not be considered overlapping and to stop the ray.")]
[Serialize(100.0f, IsPropertySaveable.No, description: "How far two walls need to not be considered overlapping and to stop the ray.")]
public float MaxOverlappingWallDist
{
get; set;
}
[Serialize(true, false, description: "Can the item hit broken doors.")]
[Serialize(true, IsPropertySaveable.No, description: "Can the item hit broken doors.")]
public bool HitItems { get; set; }
[Serialize(false, false, description: "Can the item hit broken doors.")]
[Serialize(false, IsPropertySaveable.No, description: "Can the item hit broken doors.")]
public bool HitBrokenDoors { get; set; }
[Serialize(0.0f, false, description: "The probability of starting a fire somewhere along the ray fired from the barrel (for example, 0.1 = 10% chance to start a fire during a second of use).")]
[Serialize(0.0f, IsPropertySaveable.No, description: "The probability of starting a fire somewhere along the ray fired from the barrel (for example, 0.1 = 10% chance to start a fire during a second of use).")]
public float FireProbability { get; set; }
[Serialize(0.0f, false, description: "Force applied to the entity the ray hits.")]
[Serialize(0.0f, IsPropertySaveable.No, description: "Force applied to the entity the ray hits.")]
public float TargetForce { get; set; }
[Serialize(0.0f, false, description: "Rotation of the barrel in degrees."), Editable(MinValueFloat = 0, MaxValueFloat = 360, VectorComponentLabels = new string[] { "editable.minvalue", "editable.maxvalue" })]
[Serialize(0.0f, IsPropertySaveable.No, description: "Rotation of the barrel in degrees."), Editable(MinValueFloat = 0, MaxValueFloat = 360, VectorComponentLabels = new string[] { "editable.minvalue", "editable.maxvalue" })]
public float BarrelRotation
{
get; set;
@@ -124,30 +124,30 @@ namespace Barotrauma.Items.Components
}
}
public RepairTool(Item item, XElement element)
public RepairTool(Item item, ContentXElement element)
: base(item, element)
{
this.item = item;
if (element.Attribute("limbfixamount") != null)
if (element.GetAttribute("limbfixamount") != null)
{
DebugConsole.ThrowError("Error in item \"" + item.Name + "\" - RepairTool damage should be configured using a StatusEffect with Afflictions, not the limbfixamount attribute.");
}
fixableEntities = new List<string>();
foreach (XElement subElement in element.Elements())
fixableEntities = new HashSet<Identifier>();
foreach (var subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "fixable":
if (subElement.Attribute("name") != null)
if (subElement.GetAttribute("name") != null)
{
DebugConsole.ThrowError("Error in RepairTool " + item.Name + " - use identifiers instead of names to configure fixable entities.");
fixableEntities.Add(subElement.Attribute("name").Value);
fixableEntities.Add(subElement.GetAttribute("name").Value.ToIdentifier());
}
else
{
fixableEntities.Add(subElement.GetAttributeString("identifier", ""));
fixableEntities.Add(subElement.GetAttributeIdentifier("identifier", ""));
}
break;
}
@@ -157,7 +157,7 @@ namespace Barotrauma.Items.Components
InitProjSpecific(element);
}
partial void InitProjSpecific(XElement element);
partial void InitProjSpecific(ContentXElement element);
public override void Update(float deltaTime, Camera cam)
{
@@ -228,9 +228,12 @@ namespace Barotrauma.Items.Components
{
if (MathUtils.GetLineRectangleIntersection(ConvertUnits.ToDisplayUnits(sourcePos), ConvertUnits.ToDisplayUnits(rayStart), item.CurrentHull.Rect, out Vector2 hullIntersection))
{
Vector2 rayDir = rayStart.NearlyEquals(sourcePos) ? Vector2.Zero : Vector2.Normalize(rayStart - sourcePos);
rayStartWorld = ConvertUnits.ToSimUnits(hullIntersection - rayDir * 5.0f);
if (item.Submarine != null) { rayStartWorld += item.Submarine.SimPosition; }
if (!item.CurrentHull.ConnectedGaps.Any(g => g.Open > 0.0f && Submarine.RectContains(g.Rect, hullIntersection)))
{
Vector2 rayDir = rayStart.NearlyEquals(sourcePos) ? Vector2.Zero : Vector2.Normalize(rayStart - sourcePos);
rayStartWorld = ConvertUnits.ToSimUnits(hullIntersection - rayDir * 5.0f);
if (item.Submarine != null) { rayStartWorld += item.Submarine.SimPosition; }
}
}
}
}
@@ -489,7 +492,7 @@ namespace Barotrauma.Items.Components
#if CLIENT
float barOffset = 10f * GUI.Scale;
Vector2 offset = planter.PlantSlots.ContainsKey(i) ? planter.PlantSlots[i].Offset : Vector2.Zero;
user?.UpdateHUDProgressBar(planter, planter.Item.DrawPosition + new Vector2(barOffset, 0) + offset, seed.Health / seed.MaxHealth, GUI.Style.Blue, GUI.Style.Blue, "progressbar.watering");
user?.UpdateHUDProgressBar(planter, planter.Item.DrawPosition + new Vector2(barOffset, 0) + offset, seed.Health / seed.MaxHealth, GUIStyle.Blue, GUIStyle.Blue, "progressbar.watering");
#endif
}
}
@@ -625,7 +628,7 @@ namespace Barotrauma.Items.Components
this,
targetItem.WorldPosition,
levelResource.DeattachTimer / levelResource.DeattachDuration,
GUI.Style.Red, GUI.Style.Green, "progressbar.deattaching");
GUIStyle.Red, GUIStyle.Green, "progressbar.deattaching");
#endif
FixItemProjSpecific(user, deltaTime, targetItem, showProgressBar: false);
return true;
@@ -817,12 +820,12 @@ namespace Barotrauma.Items.Components
if (leakFixed && leak.FlowTargetHull?.DisplayName != null && character.IsOnPlayerTeam)
{
if (!leak.FlowTargetHull.ConnectedGaps.Any(g => !g.IsRoomToRoom && g.Open > 0.0f))
{
character.Speak(TextManager.GetWithVariable("DialogLeaksFixed", "[roomname]", leak.FlowTargetHull.DisplayName, true), null, 0.0f, "leaksfixed", 10.0f);
{
character.Speak(TextManager.GetWithVariable("DialogLeaksFixed", "[roomname]", leak.FlowTargetHull.DisplayName, FormatCapitals.Yes).Value, null, 0.0f, "leaksfixed".ToIdentifier(), 10.0f);
}
else
{
character.Speak(TextManager.GetWithVariable("DialogLeakFixed", "[roomname]", leak.FlowTargetHull.DisplayName, true), null, 0.0f, "leakfixed", 10.0f);
character.Speak(TextManager.GetWithVariable("DialogLeakFixed", "[roomname]", leak.FlowTargetHull.DisplayName, FormatCapitals.Yes).Value, null, 0.0f, "leakfixed".ToIdentifier(), 10.0f);
}
}
@@ -882,7 +885,7 @@ namespace Barotrauma.Items.Components
if (!door.CanBeWelded || !door.Item.IsInteractable(user)) { continue; }
for (int i = 0; i < effect.propertyNames.Length; i++)
{
string propertyName = effect.propertyNames[i];
Identifier propertyName = effect.propertyNames[i];
if (propertyName != "stuck") { continue; }
if (door.SerializableProperties == null || !door.SerializableProperties.TryGetValue(propertyName, out SerializableProperty property)) { continue; }
object value = property.GetValue(target);
@@ -6,30 +6,30 @@ namespace Barotrauma.Items.Components
{
partial class Sprayer : RangedWeapon
{
[Serialize(0.0f, false, description: "The distance at which the item can spray walls.")]
[Serialize(0.0f, IsPropertySaveable.No, description: "The distance at which the item can spray walls.")]
public float Range { get; set; }
[Serialize(1.0f, false, description: "How fast the item changes the color of the walls.")]
[Serialize(1.0f, IsPropertySaveable.No, description: "How fast the item changes the color of the walls.")]
public float SprayStrength { get; set; }
private readonly Dictionary<string, Color> liquidColors;
private readonly Dictionary<Identifier, Color> liquidColors;
private ItemContainer liquidContainer;
public Sprayer(Item item, XElement element) : base(item, element)
public Sprayer(Item item, ContentXElement element) : base(item, element)
{
item.IsShootable = true;
item.RequireAimToUse = true;
foreach (XElement subElement in element.Elements())
foreach (var subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "paintcolors":
{
liquidColors = new Dictionary<string, Color>();
liquidColors = new Dictionary<Identifier, Color>();
foreach (XElement paintElement in subElement.Elements())
{
string paintName = paintElement.GetAttributeString("paintitem", string.Empty);
Identifier paintName = paintElement.GetAttributeIdentifier("paintitem", Identifier.Empty);
Color paintColor = paintElement.GetAttributeColor("color", Color.Transparent);
if (paintName != string.Empty)
@@ -49,7 +49,7 @@ namespace Barotrauma.Items.Components
liquidContainer = item.GetComponent<ItemContainer>();
}
partial void InitProjSpecific(XElement element);
partial void InitProjSpecific(ContentXElement element);
#if SERVER
public override bool Use(float deltaTime, Character character = null)
@@ -21,10 +21,10 @@ namespace Barotrauma.Items.Components
private set;
}
[Serialize(1.0f, false, description: "The impulse applied to the physics body of the item when thrown. Higher values make the item be thrown faster.")]
[Serialize(1.0f, IsPropertySaveable.No, description: "The impulse applied to the physics body of the item when thrown. Higher values make the item be thrown faster.")]
public float ThrowForce { get; set; }
public Throwable(Item item, XElement element)
public Throwable(Item item, ContentXElement element)
: base(item, element)
{
//throwForce = ToolBox.GetAttributeFloat(element, "throwforce", 1.0f);
@@ -178,11 +178,11 @@ namespace Barotrauma.Items.Components
throwDone = true;
IsActive = true;
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
if (GameMain.NetworkMember is { IsServer: true })
{
GameMain.NetworkMember.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnSecondaryUse, this, CurrentThrower.ID });
GameMain.NetworkMember.CreateEntityEvent(item, new Item.ApplyStatusEffectEventData(ActionType.OnSecondaryUse, this, CurrentThrower));
}
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
if (!(GameMain.NetworkMember is { IsClient: true }))
{
//Stun grenades, flares, etc. all have their throw-related things handled in "onSecondaryUse"
ApplyStatusEffects(ActionType.OnSecondaryUse, deltaTime, CurrentThrower, user: CurrentThrower);